3.5. Controlando un demonio

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.

3.5.1. Generación del fichero de configuración

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

				

3.5.2. Controlando la ejecución

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