3.5. Controlling a daemon

Besides exposing an API that allows reading and changing the configuration of a given service, a module's backend is in charge of making that service work. Typically, the service will be some sort of daemon that offers some functionality across the network after reading a configuration file. So your module needs to generate the configuration file and start/stop/restart the daemon at the right times.

3.5.1. Configuration file generation

The easiest way to generate configuration files is using the mason templating engine which is also used in the web front-end. Using mason templates is documented in Section 5.2 so we won't repeat it here.

Mason templates for configuration files are installed in the stubs directory under the shared directory for eBox. In the module source tree they are usually placed in directory of their own called stubs too. The Makefile.am in the stubs directory for the dns-cache module looks like this:

Stubdir = @STUBSPATH@/dns-cache

nobase_Stub_DATA = named.conf.mas named.conf.options.mas \
	named.conf.local.mas

EXTRA_DIST = $(nobase_Stub_DATA)

MAINTAINERCLEANFILES = Makefile.in

The autoconf macro included in ebox.m4 automatically exports the stubs directory path as STUBSPATH, so you just need to put create a directory for your module in it and put your mason templates there.

There is a method in EBox::Module that helps with file permissions and other details. It is called writeConfFile and it takes three arguments:

  • The path of the configuration file that's going to be generated.

  • The path of the mason template relative to the stubs directory.

  • A reference to the arguments that you want to pass to the mason template.

writeConfFile will generate the configuration file in a temporary location and then copy it on top of the desired destination, keeping its original permissions and ownership.

Example 3.7. Generating a configuration file

This is the code that generates the configuration file in the NTP module:


my $self = shift;
my @array = ();
my @servers = $self->servers;
my $synch = 'no';
my $active = 'no';

($self->synchronized) and $synch = 'yes';
($self->service) and $active = 'yes';

push(@array, 'active'   => $active);
push(@array, 'synchronized'  => $synch);
push(@array, 'servers'  => \@servers);

$self->writeConfFile(NTPCONFFILE, "ntp/ntp.conf.mas", \@array);

				

And this is the template the generates the ntp.conf file:


<%args>
	$active
	$synchronized
	@servers
</%args>	
# /etc/ntp.conf, configuration for ntpd
# Generated by EBox

driftfile /var/lib/ntp/ntp.drift
statsdir /var/log/ntpstats/

% if ($synchronized eq 'yes') {
%	if ($servers[0]) {
server <% $servers[0] %>
%	}
%	if ($servers[1]) {
server <% $servers[1] %>
%	}
%	if ($servers[2]) {
server <% $servers[2] %>
%	}
% }
% if ($active eq 'yes') {
server 127.127.1.0
% }
fudge 127.127.1.0 stratum 13

restrict default kod notrap nomodify nopeer noquery

restrict 127.0.0.1 nomodify

				

3.5.2. Controlling execution

The first thing you need to know is when to start and stop the daemon you are controlling. Services are started by calling the restartService or save methods on a module instance, however those methods are implemented by EBox::Module and should normally not be overridden, since they takes care of logging and/or saving the configuration changes. Both of these methods call an abstract method when they need to actually start/restart the service. This method is _regenConfig, and it's the one that you need to implement.

_regenConfig should generate the configuration files for the daemon and start or restart it. A sample implementation of this method can be found in Example 3.8. If you need to know whether the call is caused by a service start/restart or by saving the configuration changes, you can check the parameters passed to _regenConfig. When it's called because the configuration was saved (save()) the named argument save will be set to 1, if it is just a regular service restart (restartService()), the restart argument will be set to 1. In most situations you won't need this, since you can easily know whether the daemon is running, this is only useful for special cases like the network module.

When _regenConfig gets called, you'll probably need to know whether to start or restart the daemon, because choosing the wrong operation may produce an error. To make that decision you need to know if the daemon is currently running. EBox::Module has two methods to make this task easier. If you know the process ID of the process you can use pidRunning, it receives a process ID as an argument. If you just know the name of the file where the daemon stored its process ID, you'll want to call pidFileRunning, which takes a file name, checks for a process ID in it and calls pidRunning. Both methods return true if the process is running and false if it's not.

Example 3.8. Sample _regenConfig implementation

sub _regenConfig
{
	my $self = shift;
	$self->_setBindConf;
	$self->_doDaemon();
}

sub _doDaemon
{
	my $self = shift;

	if ($self->service and $self->pidFileRunning(PIDFILE)) {
		$self->_daemon('reload');
	} elsif ($self->service) {
		$self->_daemon('start');
	} elsif ($self->pidFileRunning(PIDFILE)) {
		$self->_daemon('stop');
	}
}

sub _daemon # (action)
{
	my ($self, $action) = @_;
	my $command = BIND9INIT . " " . $action . " 2>&1";

	if ( $action eq 'start') {
		root($command);
	} elsif ( $action eq 'stop') {
		root($command);
	} elsif ( $action eq 'reload') {
		root($command);
	} else {
		throw EBox::Exceptions::Internal(
			"Bad argument: $action");
	}
}

Stopping the service is similar, you just check if it is running and if it is, then run the command that stops it. As with restartService, EBox::Module has a template implementation of the stopService method, which calls an abstract method when it's time to actually stop the service. The abstract method, _stopService is the one you need to implement.

Example 3.9. Sample _stopService implementation

sub _stopService
{
	my $self = shift;

	if ($self->pidFileRunning(PIDFILE)) {
		$self->_daemon('stop');
	}
}