Además de tener un API que permite leer y cambiar la configuración de un servicio dado, el backend de un módulo está a cargo de hacer que el servicio funcione. Típicamente, el servicio será algún tipo de demonio que ofrezca algunas funcionalidades a través de la red después de leer su fichero de configuración. Por tanto, el módulo creado necesitará generar el fichero de configuración y parar/arrancar/reiniciar el demonio las veces que sea necesario.
La forma más sencilla de generar el fichero de configuración es usar el sistema de plantillas mason el cual es usado también en el front-end web. El uso de las plantillas de mason está documentado en Sección 5.2 así que no lo repetiremos aquí.
Las plantillas de mason para los ficheros de
configuración son instaladas en el directorio
stubs baje el directorio compartido para
eBox. En el árbol de directorios del código fuente, cada
módulo también tiene normalmente un directorio llamado
stubs. El fichero
Makefile.am del directorio
stubs para el módulo dnscache tiene esta
forma:
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
La macro de autoconf, llamada
ebox.m4, incluida automáticamente exporta
la ruta del directorio stubs como STUBPATH,
por lo que sólo se necesita crear un directorio para el módulo
en dicha ruta y colocar las plantilla de mason en su
interior.
Hay un método en EBox::Module que
ofrece ayuda con los permisos de los ficheros y otros
detalles. Se llama writeConfFile y
necesita tres argumentos:
La ruta del fichero de configuración que va a ser generado.
La ruta de la plantilla de mason relativa al directorio stubs.
Una referencia a los argumentos que se le quieren pasar a la plantilla de mason.
writeConfFilegenerará el
fichero de configuración en un directorio temporal y después
lo copiará encima del fichero que exista en el directorio
deseado, manteniendo sus propietarios y permisos originales.
Ejemplo 3.7. Generando un fichero de configuración
Este es el código que genera el fichero de configuración en el módulo NTP:
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);
Y esta es la plantilla que genera el fichero ntp.conf:
<%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
La primera cosa que necesita saber es cuando iniciar y
para el demonio que se está controlando. Los servicios son
arrancados llamando a las funciones
restartService o
save en la instancia de un módulo,
sin embargo estos métodos son implementados por
EBox::Module y no deberían ser
reimplementados normalmente, siendo que controlan los
registros del sistema y/o guardan los cambios de
configuración. Ambas funciones llaman a una función abstracta
cuando necesitan iniciar/reiniciar el servicio. Este método es
_regenConfig, y es el que necesita implementar.
_regenConfig debería generar
los ficheros de configuración para el demonio e iniciarlo o
reiniciarlo. Un ejemplo de implementación de este módulo se
encuentra en Ejemplo 3.8. Si necesita saber
cuando la llamada es realizada por el inicio/reinicio del
servicio o por la petición de guardar los cambios en la
configuración, puede comprobar los parámetros pasados a
_regenConfig. Cuando se llama porque
la configuración fue guardada
(save()) el argumento llamado
save será puesto a
1. En la mayoría de situaciones no será
esto necesario, ya que se puede saber fácilmente si el demonio
está ejecutándose, y sólo es útil para casos especiales como
el módulo network.
Cuando se llama a _regenConfig,
probablemente necesite saber cuando iniciar o reiniciar el
demonio, porque elegir la operación incorrecta podría producir
un error. Para realizar tal decisión necesita saber si el
demonio se encuentra funcionando en ese
momento. EBox::Module tiene dos
funciones para hacer esta tarea más sencilla. Si sabe cual es
el ID del proceso, puede usar
pidRunning. Recibe el ID de un
proceso como argumento. Si sólo sabe el nombre del fichero
donde el demonio almacenó su ID de proceso, querrá llamar a la
función pidFileRunning, el cual toma
el nombre de un fichero, comprueba el ID del proceso y llama a
pidRunning. Ambas funciones devuelven
true si el proceso está funcionando y false si no lo está.
Ejemplo 3.8. Ejemplo de implementación de _regenConfig
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");
}
}Parar el servicio es similar. Se puede comprobar si está
ejecutándose, y si es así, entonces lanzar el comando que lo
pare. Como con restartService,
EBox::Module tiene una implementación
básica de la función stopService, la
cual llama a una función abstracta cuando es momento de parar
el servicio. La función abstracta,
_stopService es la que se necesita
implementar.
Ejemplo 3.9. Implementación de
_stopService de ejemplo
sub _stopService
{
my $self = shift;
if ($self->pidFileRunning(PIDFILE)) {
$self->_daemon('stop');
}
}