ACC SHELL

Path : /usr/share/YaST2/modules/
File Upload :
Current File : //usr/share/YaST2/modules/Service.ycp

/**
 * File:	modules/Service.ycp
 * Package:	yast2
 * Summary:	Service manipulation
 * Authors:	Martin Vidner <mvidner@suse.cz>
 *		Petr Blahos <pblahos@suse.cz>
 *		Michal Svec <msvec@suse.cz>
 *		Lukas Ocilka <locilka@suse.cz>
 * Flags:	Stable
 *
 * $Id: Service.ycp 58399 2009-08-20 13:39:06Z jsuchome $
 *
 * Functions for service (init script) handling used by other modules.
 */

{

module "Service";
textdomain "base";

/***
 * Services Manipulation
 */

/**
 * @struct service
 * One service is described by such map: <pre>
  "servicename" : $[
    "defstart" : [ "2", "3", "5", ], // Default-Start comment
    "defstop"  : [ "0", "1", "6", ], // Default-Stop  comment

    "reqstart" : [ "$network", "portmap" ], // Required-Start comment
    "reqstop"  : [ "$network", "portmap" ], // Required-Stop  comment

    "description" : "text...",       // Description comment

    "start" : [ "3", "5", ], // which runlevels service is really started/stopped in
    "stop"  : [ "3", "5", ], // read from /etc/init.d/rc?.d/* links

    "started" : 0, // return from rcservice status (integer)

    "dirty" : false, // was the entry changed?
  ]</pre>
 */

/**
 * Script location
 */
string init_d = "/etc/init.d";

/**
 * After a function returns an error, this holds an error message,
 * including insserv stderr and possibly containing newlines.
 *
 * Set by
 * checkExists: [Full]Info, Status, Enabled, Adjust, Finetune
 *
 * Never cleared.
 */
string error_msg = "";

/**
 * Check that a service exists.
 * If not, set error_msg.
 * @param name service name without a path, eg. nfsserver
 * @return Return true if the service exists.
 */
define boolean checkExists (string name) {
    if(name == nil || name == "") {
	// Error message.
	// %1 is a name of an init script in /etc/init.d,
	// eg. nfsserver
	error_msg = sformat (_("Empty service name: %1."), name);
	y2error(1, error_msg);
	return false;
    }
    if(! (boolean) SCR::Read(.init.scripts.exists, name)) {
	// Error message.
	// %1 is a name of an init script in /etc/init.d,
	// eg. nfsserver
	error_msg = sformat (_("Service %1 does not exist."), name);
	y2milestone (1, error_msg);
	return false;
    }
    return true;
}

/**
 * Get service info without peeking if service runs.
 * @param name name of the service
 * @return Service information or empty map ($[])
 */
global define map Info (string name) {
    if(!checkExists (name)) return $[];
    map read = (map) SCR::Read (.init.scripts.runlevel, name);
    map detail = read[name]:$[];
    read = (map) SCR::Read (.init.scripts.comment, name);
    map service = read[name]:$[];
    return add (
	add (service, "start", detail["start"]:[]),
	"stop", detail["stop"]:[]);
}

/**
 * Get service status.
 *
 * The status is the output from "service status".
 * It should conform to LSB. 0 means the service is running.
 * @param name name of the service
 * @return init script exit status or -1 if it does not exist
 */
global define integer Status (string name) {
    if(!checkExists (name)) return -1;
    return (integer) SCR::Execute (.target.bash, sformat ("%2/%1 status", name, init_d), $["TERM":"raw"]);
}

/**
 * Get service info and find out whether service is running.
 * @param name name of the service
 * @return service map or empty map ($[])
 */
global define map FullInfo (string name) {
    if(!checkExists (name)) return $[];
    return add (Info (name), "started", Status (name));
}

/**
 * Call insserv -r and record errors.
 * Does not check if it exists
 * @param name service name
 * @param force pass "-f" to insserv (workaround for #17608, #27370)
 * @return success state
 */
define boolean serviceDisable (string name, boolean force) {
    map ret = (map)SCR::Execute (.target.bash_output,
			    sformat ("/sbin/insserv -r%3 %2/%1",
				     name, init_d, force? "f": ""));
    if (0 != ret["exit"]:-1)
    {
	// Error message.
	// %1 is a name of an init script in /etc/init.d,
	// Disabling means that the service should not start
	// in appropriate runlevels, eg. at boot time.
	// %2 is the stderr output of insserv(8)
	error_msg = sformat(_("Unable to disable service %1:\n%2"),
			    name, ret["stderr"]:"");
	// the user is two levels up
	y2error (2, error_msg);
	return false;
    }
    return true;
}

/**
 * Adjust runlevels in which the service runs.
 * @param name service name
 * @param action "disable" -- remove links, "enable" -- if there are
 *    no links, set default, otherwise do nothing, "default" -- set
 *    defaults.
 * @return success state
 */
global define boolean Adjust (string name, string action) {
    if (! checkExists (name))
    {
	y2error( "Service %1 does not exist.", name);
	return false;
    }
    map service = Info (name);
    if ("disable" == action)
    {
	if (size (service["start"]:[]) != 0)
	{
	    return serviceDisable (name, false);
	}
	return true;
    }
    if (("default" == action) || ("enable" == action))
    {
	if ("enable" == action && size (service["start"]:[]) != 0)
	{
	    // nothing to do
	    return true;
	}
	else
	{
	    map ret = (map)SCR::Execute (.target.bash_output,
				    sformat ("/sbin/insserv -d %2/%1",
					     name, init_d));
	    if (0 != ret["exit"]:-1)
	    {
		// Error message.
		// %1 is a name of an init script in /etc/init.d,
		// Enabling means that the service should start
		// in appropriate runlevels, eg. at boot time.
		// %2 is the stderr output of insserv(8)
		error_msg = sformat(_("Unable to enable service %1:\n%2"),
				    name, ret["stderr"]:"");
		y2error (1, error_msg);
		return false;
	    }
	}
	return true;
    }
    // not translated
    error_msg = sformat ("Invalid parameter: %1", action);
    y2internal (1, error_msg);
    return false;
}

/**
 * Set service to run in selected runlevels.
 * @param name name of service to adjust
 * @param rl list of runlevels in which service should start
 * @return success state
 */
global define boolean Finetune (string name, list rl) {
    if (! checkExists (name))
    {
	y2error ("Unknown service: %1", name);
	return false;
    }

    // Force because of #27370:
    // RunlevelEd::Write has already solved the dependencies
    // and calls us only once for each modified service.
    // In general we cannot do it with dependencies in a single pass.

    string rls = mergestring ((list<string>)rl, ",");
    // we must remove it first because insserv start=... adds
    // runlevels, not replace runlevels!!
    if (! serviceDisable (name, true))
    {
	y2error ("Cannot disable %1", name);
	return false;
    }

    // script has already been completely disabled
    // enable in required runlevels if needed
    if (rls != "") {
	string command = sformat ("/sbin/insserv -f %2/%1,start=%3", name, init_d, rls);
	y2milestone ("Calling %1", command);

	map ret = (map)SCR::Execute (.target.bash_output, command);

	if (0 != ret["exit"]:-1) {
	    // Error message.
	    // %1 is a name of an init script in /etc/init.d,
	    // Enabling means that the service should start
	    // in appropriate runlevels, eg. at boot time.
	    // %2 is the stderr output of insserv(8)
	    // %3 is a comma separated list of runlevels
	    error_msg = sformat(_("Unable to enable service %1 in runlevels %2:\n%3"),
				name, rls, ret["stderr"]:"");
	    y2error (1, error_msg);
	    return false;
	}
    } else {
	y2milestone ("No runlevels to start %1 in", name);
    }

    return true;
}

/**
 * Check if service is enabled
 *
 * Returns true if any link in /etc/init.d/rc?.d/ exists for this
 * script. If service does not exist, logs an error.
 *
 * @param name service name
 * @return true if service is set to run in any runlevel
 */
global define boolean Enabled (string name) {
    if(!checkExists (name)) return false;
    map<string, any> details = (map<string, any>) SCR::Read (.init.scripts.runlevel, name);
    map<string, any> detail  = (map<string, any>) details[name]:$[];
    return size(detail["start"]:[]) != 0;
}

/**
 * Run init script.
 * @param name init service name
 * @param param init script argument
 * @return integer exit value
 */
global define integer RunInitScript (string name, string param) {
    y2milestone("Running service initscript %1 %2", name, param);
    string command = sformat ("%2/%1 %3", name, init_d, param);
    map<string, any> output=(map<string, any>)SCR::Execute (.target.bash_output,
	    command, $[ "TERM" : "raw"]);

    if (output["exit"]:-1!=0)
	y2error("Error while running initscript %1 :\n%2", command, output);
 return output["exit"]:-1;
}


/* Time out for background agent - init script run */
integer script_time_out = 60000;
integer sleep_step = 20;

/**
 * Run init script with a time-out.
 * @param name init service name
 * @param param init script argument
 * @return integer exit value
 */
global define integer RunInitScriptWithTimeOut (string name, string param) {
    y2milestone("Running service initscript %1 %2", name, param);
    string command = sformat ("TERM=dumb %2/%1 %3", name, init_d, param);

    // default return code
    integer return_code = nil;

    // starting the process
    integer process_pid = (integer) SCR::Execute(.process.start_shell, command);
    if (process_pid == nil || process_pid <= 0) {
	y2error("Cannot run '%1' -> %2", command, process_pid);
	return return_code;
    }
    y2debug("Running: %1", command);

    list<string> script_out = [];
    integer time_spent = 0;
    boolean cont_loop = true;

    // while continuing is needed and while it is possible
    while (cont_loop && ((boolean) SCR::Read (.process.running, process_pid))) {
	if (time_spent >= script_time_out) {
	    y2error("Command '%1' timed-out after %2 mces", command, time_spent);
	    cont_loop = false;
	}

	// sleep a little while
	time_spent = time_spent + sleep_step;
	sleep(sleep_step);
    }
    
    // fetching the return code if not timed-out
    if (cont_loop) {
	return_code = (integer) SCR::Read(.process.status, process_pid);
    }

    y2milestone("Time spent: %1 msecs, retcode: %2", time_spent, return_code);

    // killing the process (if it still runs)
    if ((boolean) SCR::Read (.process.running, process_pid)) {
	SCR::Execute(.process.kill, process_pid);
    }

    return return_code;
}

string lang = nil;

/**
 * Run init script and return output
 */
/**
 * Run init script and also return its output (stdout and stderr merged).
 * @param name init service name
 * @param param init script argument
 * @return A map of $[ "stdout" : "...", "stderr" : "...", "exit" : int]
 */
global define map RunInitScriptOutput (string name, string param) {
    map env = $["TERM": "raw"];

    // encoding problems - append .UTF-8 to LANG
    if (lang == nil)
    {
	map<string, any> ex = (map<string, any>) SCR::Execute (.target.bash_output, "echo -n $LANG");
	lang = ex["stdout"]:"";
	list ll = splitstring (lang, ".");
	lang = ll[0]:"";
	if (lang != "")
	{
	    lang = lang + ".UTF-8";
	}
	y2debug ("LANG: %1", lang);
    }
    if (lang != "")
    {
	env = add (env, "LANG", lang);
    }

    // looks like a bug in bash...
    string locale_debug = "";
    // locale_debug = "; ls /nono 2>&1; /usr/bin/locale; /usr/bin/env";

    return (map)SCR::Execute (.target.bash_output,
			 sformat ("%2/%1 %3 2>&1", name, init_d, param)
			 + locale_debug,
			 env);
}

/**
 * Enable service
 * @param service service to be enabled
 * @return true if operation is  successful
 */
global define boolean Enable(string service) {
    if(!checkExists(service)) return false;
    y2milestone("Enabling service %1", service);
    return Adjust(service, "enable");
}

/**
 * Disable service
 * @param service service to be disabled
 * @return true if operation is  successful
 */
global define boolean Disable(string service) {
    if(!checkExists(service)) return false;
    y2milestone("Disabling service %1", service);
    return Adjust(service, "disable");
}

/**
 * Start service
 * @param service service to be started
 * @return true if operation is  successful
 */
global define boolean Start(string service) {
    if(!checkExists(service)) return false;
    integer ret = nil;
    y2milestone("Starting service %1", service);
    ret = RunInitScript(service, "start");
    y2debug("ret=%1", ret);
    return ret == 0;
}

/**
 * Restart service
 * @param service service to be restarted
 * @return true if operation is  successful
 */
global define boolean Restart(string service) {
    if(!checkExists(service)) return false;
    integer ret = nil;
    y2milestone("Restarting service %1", service);
    ret = RunInitScript(service, "restart");
    y2debug("ret=%1", ret);
    return ret == 0;
}

/**
 * Reload service
 * @param service service to be reloaded
 * @return true if operation is  successful
 */
global define boolean Reload(string service) {
    if(!checkExists(service)) return false;
    integer ret = nil;
    y2milestone("Reloading service %1", service);
    ret = RunInitScript(service, "reload");
    y2debug("ret=%1", ret);
    return ret == 0;
}

/**
 * Stop service
 * @param service service to be stopped
 * @return true if operation is  successful
 */
global define boolean Stop(string service) {
    if(!checkExists(service)) return false;
    integer ret = nil;
    y2milestone("Stopping service %1", service);
    ret = RunInitScript(service, "stop");
    y2debug("ret=%1", ret);
    return ret == 0;
}

/**
 * Error Message
 *
 * If a Service function returns an error, this function would return
 * an error message, including insserv stderr and possibly containing
 * newlines.
 * @return error message from the last operation
 */
global define string Error() {
    return error_msg;
}

/**
 * Get list of enabled services in a runlevel
 * @param runlevel requested runlevel number (0-6, -1 = Single)
 * @return list<string> enabled services
 */
global list<string> EnabledServices(integer runlevel)
{
    if (runlevel < -1 || runlevel > 6)
    {
	y2error("ERROR: Invalid runlevel: %1", runlevel);
	return nil;
    }

    // convert the integer to a string (-1 = S)
    string runlevel_str = (runlevel == -1) ? "S" : sformat("%1", runlevel);

    list<string> ret = [];

    string command = sformat("ls -1 /etc/init.d/rc%1.d/", runlevel_str);
    y2milestone("Executing: %1", command);

    map out = (map)SCR::Execute(.target.bash_output, command);
    y2debug("Result: %1", out);

    if (out["exit"]:-1 != 0)
    {
	y2error("ERROR: %1", out);
	return nil;
    }

    foreach(string s, splitstring(out["stdout"]:"", "\n"),
	{
	    string service = regexpsub(s, "^S[0-9]+([^0-9]+.*)", "\\1");

	    if (service != nil)
	    {
		ret = add(ret, service);
	    }
	}
    );

    y2milestone("Enabled services in runlevel %1: %2", runlevel, ret);

    return ret;
}

/**
 * Return the first of the list of services which is available
 * (has init script) or "" if none is.
 * @param list<string> list of service names
 * @return string the first found service
 */
global string Find( list <string> services )
{
    string found_service = "";

    foreach ( string service, services, {
	if ( checkExists( service ) )
	{
	   found_service = service;
	   break;
	}
    });

    return found_service;
}

/* EOF */
}

ACC SHELL 2018