Tags:
create new tag
view all tags

Controlling Daemons

From lcfg-ngeneric version 1.6.0 onwards there is a new Service method interface. This makes it trivial to call an action for a service from a component, and have the action handled in a standard way, whatever init daemon is in use.

Background

The purpose of many LCFG components is to configure and control the behaviour of daemons which provide services (e.g. dns, sshd or httpd) on a machine. On Linux, the way in which daemons are controlled varies depending on which init daemon is being used. The traditional approach is that of SysVinit where each daemon has a shell script in the /etc/init.d directory. More recently the Upstart init daemon has been used. It supports old-style SysVinit scripts as well as providing a service command (this is used on SL6). The latest init daemon is Systemd which is used on Fedora and RHEL7. This uses the systemctl command to control services and does not support old-style init scripts.

Each individual component author has been left to write their own code to control daemons. This has resulted in a wide range of approaches. Some components have the paths to the init scripts hardwired into the code; some have them as resources. This is nearly all incompatible with use of Systemd. As well as not working on modern systems this approach clearly wastes developer time, as the same problems have to be solved repeatedly, and it probably leads to a lot of copy/paste of code and the opportunity for errors as a result.

The ideal solution is to have a single interface which solves the problem in a generic way that minimises the amount of code a component author has to write to control daemons.

Selection of init system

The selection of the init system is done on the client by examining LCFG Sysinfo resources. Each platform has a sysinfo.init resource with the name of the init system (e.g. sysvinit, systemd or launchd). For some init systems (e.g. upstart and Systemd) there is also a sysinfo.path_init_tool resource which gives the full path to the command (e.g. service or systemctl) which should be used to control the daemon. On platforms which support SysVinit scripts there is also a sysinfo.path_initd resource which gives the location of the scripts directory. Primarily these resources are designed for use in the Service method but they are intended to be more generally useful - there will be situations in which a component will need to control daemons directly or make other decisions based on the init system. These Sysinfo values can be queried in the standard way:

In Perl:

my $sysinfo = $self->GetSysInfo();
my $init = $sysinfo->get_info('init');
my $init_tool = $sysinfo->get_path('init_tool')

or in Shell:

GetSysInfo 'INIT'
GetSysPath 'INIT_TOOL'

The MacOSX platform is a special case. When the sysinfo.init resource is set to launchd the Service method will be a silent no-op. This is because it is rarely necessary or possible to signal daemons on MacOSX.

If the sysinfo.init resource is set to any value other than one of (sysv, upstart, systemd or launchd) an error will be thrown by the Service method.

Debugging and Error Logging

If the debug option has been set for a component then the Service method will print a debug message with details of the daemon, action, any arguments and the init daemon selected. It will look like this:

Service: '$service', Action: '$action', Args: '$args', Init: '$init'

Note that enabling debugging does NOT disable the calling of the action it just generates additional output.

If an error occurs when the action is called, then the Service method will log (to the component log file) any output to stdout or stderr, and print an error message describing what failed. This means that it is rarely necessary to handle the output in the component code itself. Note that the method will NOT fail - it will just log an error message. If you need to fail you should check the returned status in your component code and fail when necessary. Note that it is mostly a bad idea to fail when starting or configuring a component, because this can leave it in an unstarted state from which it is difficult to automatically recover.

Implementations

The functionality of the Service method implementations in Shell and Perl are fairly similar, with Perl having a few additional features:

Perl

The LCFG::Component object has a Service method which can be used in a method (e.g. Configure) like this:

my $ok = $self->Service( $daemon_name, $action, @args, \%options );

or

my ( $ok, $out, $err ) = $self->Service( $daemon_name, $action, @args, \%options );

The command requires the name of the daemon (e.g. sshd) and the action to be called (e.g. stop). The @args list and %options are optional arguments. The options hash can be used to specify a timeout in seconds after which the method will fail if the action has not completed.

If the method is called in scalar context then only the status will be returned. This will be a true value if the command succeeded and false if it failed.

If the method is called in array context, then along with the status, anything sent to stdout or stderr when the command is called will also be returned.

A few examples:

my $ok = $self->Service( 'sshd', 'reload' );

will reload the SSH daemon. Or

my ( $ok, $out, $err ) = $self->Service( 'httpd', 'stop', { timeout => 20 } );
if ( !$ok ) {
   $self->Fail("Failed to stop apache");
}

will stop the apache daemon, or fail if it did not stop within 20 seconds. It also captures any output sent to stdout or stderr.

The implementation of the Service method is provided by the LCFG::Utils::Service module. This can be used from any Perl script (not just LCFG components) to call service methods in a platform-independent manner.

Shell

The Shell implementation does not provide a facility to capture stdout or stderr. It is called like this:

Service sshd reload

or

Service httpd stop
if [ $? -ne 0 ] ; then
  Fail "Failed to stop apache"
fi

If necessary a list of command line arguments can also be passed into the Service method.

If a timeout is required then it can be specified using a --timeout option. For example:

Service sshd stop --timeout 10

Note that, as usual, the success of a function in Shell is indicated by an exit status of zero, and any non-zero value indicates a failure (this is the opposite of the success code returned in Perl).

When the command is executed the file-descriptors 11 and 12 (which are used in the LCFG ngeneric framework) are closed to avoid daemons keeping log files open.

The Service function is implemented using the lcfg-service script which is itself a wrapper around the LCFG::Utils::Service Perl module. That script can be used from any script or the command-line (not just LCFG components) to call service methods in a platform-independent manner.

-- Main.squinney - 2014-05-23

Topic revision: r4 - 2015-03-05 - squinney
 
This site is powered by the TWiki collaboration platform Powered by PerlCopyright © 2008-2020 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback