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.
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
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'); } }