IMPORTANT: Per accedir als fitxer de subversion: http://acacha.org/svn (sense password). Poc a poc s'aniran migrant els enllaços. Encara però funciona el subversion de la farga però no se sap fins quan... (usuari: prova i la paraula de pas 123456)

Dimonis Linux

Un dimoni és una aplicació informàtica o procés que s'executa en segon pla. Un dimoni té les següents característiques:

  • És un procés no interactiu (no disposa d'interfície amb l'usuari).
  • Típicament el nom dels dimonis en Linux acaben en -d (httpd, sshd, etc.)
  • Origen del nom: terme grec (daimon) una mena “d'àngel de la guarda”. També es parla de l'abreviació de “Disk And Environment MONitor".
  • No són interactius. No disposen d'interfície directa amb l'usuari, ni gràfica ni textual.
  • No utilitzen les entrades i sortides estàndard per comunicar errors o registrar el seu funcionament.
  • Utilitzen fitxers de log (/var/log) per enregistrar els errors o esdeveniments del dimoni.


Nota sobre els exemples

Podeu descarregar tots els exemples de cop si executeu:

svn checkout https://svn.lafarga.cpl.upc.edu/dades/svn/plinux/sessio5
  • Usuari: anonymous
  • Paraula de pas: anonymous

En cas que no tingueu subversion instal·lat podeu executar:

$ sudo apt-get install subversion


Guia de bones pràctiques

  • Passar el dimoni a executar-se en segon pla (background) un cop s'executa utilitzant fork().

Exemple:

  //El primer que fem és executar un fork per executar el procés 
  //en segon pla
  status = fork();
  switch (status)
  {
  case -1:
      perror(fork());
      exit(1);
  case 0: /* El procés fill continua amb l'execució del programa */
      break;
  default: /* El procés pare finalitza */
      exit(0);
  }
  //resta del dimoni...
  • El fill hereta els descriptors de fitxers del pare. Per evitar cap relació amb la terminal des de la qual hem executat el dimoni, hem de tancar els descriptors de fitxers que tingués obert el pare. Si no ens deslliguem de la terminal des de la qual s'executa el dimoni, tancar la terminal, implicaria també el tancament del dimoni:

Exemple:

resourceLimit.rlim_max = 0;
status = getrlimit(RLIMIT_NOFILE, &resourceLimit);
if (-1 == status) /* shouldn't happen */    {
 perror("Error executant getrlimit()");
 exit(1);
}
if (0 == resourceLimit.rlim_max)    {
 //fprintf("El nombre màxim de fitxers oberts és 0!!\n");
 exit(1);
}
for (i = 0; i < resourceLimit.rlim_max; i++)  {
 (void) close(i);
}
  • També es necessari canviar el grup de procés del dimoni i evitar que el dimoni pugui rebre senyals del seu pare. Per això el procés ha de crear una nova sessió .

Exemple:

  status = setsid();
  if (-1 == status)    {
      perror(setsid());
      exit(1);
  }
  • Readquirir un terminal de control. Una segona crida a fork fa que el dimoni sigui fill de procés init, ja que el seu pare ha finalitzat l'execució prèviament amb un exit.

Exemple:

  status = fork();
  switch (status)
  {
  case -1:
      perror("Error a l'executar fork()");
      exit(1);
  case 0: /* segon procés fill */
      break;
  default: /* Parem el pare */
      exit(0);
  }
  • Ara el dimoni no té descriptors de fitxers per a les sortides i entrada estàndard ni per la sortida estàndard d'error. Com moltes llibreries suposen de l'existència dels tres descriptors de fitxers estàndard; és una bona idea crear aquests descriptors i connectar-los a un dispositiu com dev/null peer evitar problemes:

Exemple:

fileDesc = open("/dev/null", O_RDWR);/* stdin */
(void) dup(fileDesc);	/* stdout */
(void) dup(fileDesc);	/* stderr */


  • Per evitar que un dimoni bloquegi l'ús d'una part del sistema de fitxers cal utilitzar l'arrel del sistema com la carpeta de treball del dimoni. D'aquesta manera evitem que per exemple, no es pugui desmuntar un punt de muntatge per què està sent utilitzat per un dimoni.
chdir("/");
  • El dimoni hereta umask del procés que l'ha creat. Per evitar problemes amb la manipulació de fitxers per part del dimoni és convenient utilitzar umask:
umask(0);
  • Evitar l'execució de més d'un servidor al mateix temps mitjançant un fitxer PIDFILE. L'existència del fitxer de PID indica que el servidor s'està executant.
#define PID_FILE "/var/run/echod.pid"

int pid_file;
char str[30]; 

pid_file=open(PID_FILE,O_RDWR|O_CREAT,0640);
sprintf(str,"%d\n",getpid());
write(pid_file,str,strlen(str)); 

Recursos:

  • Linux network programming a Linux journal. Part 1
  • Linux network programming a Linux journal. Part 2
  • Linux network programming a Linux journal. Part 3


Exemple. Dimoni d'echos

  • SVN:
svn checkout https://svn.lafarga.cpl.upc.edu/dades/svn/plinux/sessio5/echoDimoni
  • Usuari: anonymous
  • Paraula de pas: anonymous

Si no teniu el subversion instal·lat executeu:

$ sudo apt-get install subversion

El següent exemple converteix el servidor d'echos de l'article Programació en xarxes en un dimoni Linux, tot seguint la guia de bones pràctiques de la secció anterior:

#include <sys/time.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>       

int port = 8000;
#define MAXBUFLEN 2048
 
int main(int argc, char *argv) {
    struct rlimit resourceLimit = { 0 };
    int status = -1;
    int fileDesc = -1;
    int i;
    struct sockaddr_in sin;
    struct sockaddr_in pin;
    int sock_descriptor;
    int temp_sock_descriptor;
    int address_size;
    char buf[MAXBUFLEN];
    int len;  

   /*
    * El primer que fem és executar un fork per executar el procés 
    * en segon pla
    */
   status = fork();
   switch (status)
   {
   case -1:
       perror(fork());
       exit(1);
   case 0: /* El procés fill continua amb l'execució del programa */
       break;
   default: /* El procés pare finalitza */
       exit(0);
   }
   //continuació del procés fill.
   resourceLimit.rlim_max = 0;
   status = getrlimit(RLIMIT_NOFILE, 
	&resourceLimit);
   if (-1 == status) /* shouldn't happen */
   {
       perror("Error executant getrlimit()");
       exit(1);
   }
   if (0 == resourceLimit.rlim_max)     {
       //fprintf("El nombre màxim de fitxers oberts és 0!!\n");
       exit(1);
   }

   for (i = 0; i < resourceLimit.rlim_max; i++)     {
       (void) close(i);
   }

   status = setsid();
   if (-1 == status)     {
      perror(setsid());
      exit(1);
  }
  status = fork();
  switch (status)     {
  case -1:
       perror("Error a l'executar fork()");
       exit(1);
   case 0: /* segon procés fill */
       break;
   default: /* Parem el pare */
       exit(0);
   }

    /*
     * Ara estem a una nova sessió i a un nou grup de processos
     * que els que hi havien a l'inici de l'execució de dimoni.
     * Tampoc no tenim un terminal controlador 
     */

    chdir("/");
    umask(0);
    fileDesc = open("/dev/null", O_RDWR);/* stdin */ 
    (void) dup(fileDesc);	/* stdout */
    (void) dup(fileDesc);	/* stderr */ 

    // A partir d'aquí executem el codi del dimoni

    sock_descriptor = socket(AF_INET, SOCK_STREAM, 0);
    if (sock_descriptor == -1) {
     perror("Error al cridar socket...");
     exit(1);
    }
  
   memset(&sin,0 , sizeof(sin));
   sin.sin_family = AF_INET;
   sin.sin_addr.s_addr = INADDR_ANY;
   sin.sin_port = htons(port);

   if (bind(sock_descriptor, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
     perror("Error al cridar bind...");
     exit(1);
   }

   if (listen(sock_descriptor, 20) == -1) {
     perror("Error al cridar listen...");
     exit(1);
   }
   
   printf("El servidor esta a l'espera de rebre connexions ...\n"); 
  
   // El bucle infinit fa que el servidor es quedi escoltant noves peticions 
   // permanentment. Cada nova petició, s'obre un nou socket per fer la 
   // la comunicació entre el client i el servidor.
   
   while(1) {
      address_size=sizeof(pin);
      temp_sock_descriptor =
      accept(sock_descriptor, (struct sockaddr *)&pin,
	      &address_size);
      if (temp_sock_descriptor == -1) {
       perror("Error al cridar accept...");
      exit(1);
      }

      if (recv(temp_sock_descriptor, buf, MAXBUFLEN, 0) == -1) {
       perror("Error al cridar recv...");
       exit(1);
      }

      printf("S'ha rebut del client el següent String:%s\n", buf); 

      if (send(temp_sock_descriptor, buf, strlen(buf), 0) == -1) {
       perror("Error al cridar send");
      exit(1);
      }
      close(temp_sock_descriptor);
   }
   return 0;
}

El més útil per veure com evoluciona el procés servidor (creació de fills en fork, canvis en descriptors de fitxers, etc.) és utilitzar el monitor de sistema de Gnome:

EchoDMonitorSistema.png

Un cop tingueu l'applet, aneu al menú visualitza i activeu tots els processos i les dependències. Aneu també a Edita/Preferències i activeu totes les columnes de visualització.

Si descarregueu [aquesta versió] modificada (amb sleeps als punts clau) del servidor d'ecos, podreu observar els canvis pel quals passa el procés servidor. Obriu una terminal, i identifique-la al monitor del sistema (si cal executeu una comanda com sleep 5):

Ara executeu el servidor:

./echod_estats

Anoteu l'identificador de procés inicial per comparar-lo amb el final. Aneu veient com evoluciona el procés, des de fill del bash, a procés orfe que és adoptat pel procés init, a com es torna a fer un fill del fill:

EchoDMonitorSistema4.png

EchoDMonitorSistema5.png

EchoDMonitorSistema6.png

EchoDMonitorSistema7.png

Veureu com el procés final ja no és el mateix que el procés pare:

$ ps aux | grep echod_estats
sergi    14407  0.0  0.0   1504   244 ?        S    13:55   0:00 ./echod_estats 

Per comprovar com realment s'apunten els descriptors de fitxers al dispositiu /dev/nulll podeu utilitzar la comanda lsof:

$ lsof | grep echod
...................
echod     12544       sergi    0u      CHR        1,3                8226 /dev/null
echod     12544       sergi    1u      CHR        1,3                8226 /dev/null
echod     12544       sergi    2u      CHR        1,3                8226 /dev/null
echod     12544       sergi    3u     IPv4      43182                TCP *:8000 (LISTEN) 

També es poden consultar, seleccionant el procés al monitor de sistemes i amb el menú contextual (botó dret) escollir obrir fitxers.

Dimonis i senyals

Per defecte s'ignoren totes les senyals excepte:

  • Procesos zombies: Si el pare no recull correctament la senyal del procés fill mort, aleshores el procés pot quedar en estat zombi. Per tant hem de recollir els senyals SIGCHLD dels fills del dimoni (si en tenim).
  • Reload: És comú que els dimonis responguin a la senyal SIGHUP, tornant a llegir la seva configuració sense aturar l'execució del dimoni. Aquest procés s'anomena reload
  • PID FILE: Molt dimonis guarden el seu identificador de procés (PID) en un fitxer de log, típicament a la carpeta /var/run amb el nom nomdimoni.pid. Aquest fitxer de log pot ser d'ajuda per a realitzar tasques relacionades amb el dimoni (per exemple aturar-lo o comprovar si s'està executant)
  • Apagar el servei correctament: Quan el sistema inicia una aturada (shutdown), la senyal SIGTERM s'envia a tots els processos. Normalment la configuració del sistema espera una quantitat de temps abans d'enviar una senyal SIGKILL a aquells processos que encara no s'han autrat. Si el nostre dimoni no captura la senyal SIGTERM, i la processa en menys del temps d'espera del sistema, aleshores el dimoni s'aturarà sobtadament, sense opció a fer una aturada organitzada del dimoni (alliberar recursos, eliminar els fitxers de log PID, etc.).
void ReapChild(int pid);
void sortir(int pid);
void reload(int pid); 

struct sigaction reapAction =
{
    ReapChild, 0, SA_RESTART,NULL
};

void ReapChild(int pid)
{
   int status;
   wait(&status);
}  

struct sigaction sigtermAction =
{
    sortir, 0, SA_RESTART,NULL
};   

void sortir(int pid)
{
   openlog("echod", 0, LOG_USER);
   syslog (LOG_INFO, "Rebuda la senyal SIGTERM. Finalitzant l'execució...");
   exit (0);
}

struct sigaction sighupAction =
{
   reload, 0, SA_RESTART,NULL
};  

void reload(int pid)
{
   openlog("echod", 0, LOG_USER);
   syslog (LOG_INFO, "Reconfigurant el servidor...");
}  

int main(int argc, char *argv)
{
   ............
   //Control de  senyals
   sigaction(SIGCHLD, &reapAction, NULL);
   sigaction(SIGTERM, &sigtermAction, NULL);
   sigaction(SIGHUP, &sighupAction, NULL);
  ............. 
}


Exemple de dimoni amb senyals. Servidor d'eco

El següent exemple mostra el Programació_de_Dimonis_Linux#Exemple._Dimoni_d.27echosdimoni servidor d'ecos d'apartats anteriors amb control de senyals. També hem aprofitat per afegir l'única recomanació de l'apartat bones pràctiques que encara no havíem implementat, el fitxer PIDFILE.

  • SVN:
svn checkout https://svn.lafarga.cpl.upc.edu/dades/svn/plinux/sessio5/echoDimoniSenyals
  • Usuari: anonymous
  • Paraula de pas: anonymous

Per provar les senyals podeu executar:

  • Senyal SIGHUP (reload):
$ ps aux | grep echod
root     14167  0.0  0.0   1508   280 ?        S    10:38   0:00 /usr/sbin/echod
root     14175  0.0  0.0   2900   764 pts/4    R+   10:38   0:00 grep echod
$ sudo kill -1 14167 
  • Senyal SIGTERM (reload):
$ ps aux | grep echod
root     14167  0.0  0.0   1508   280 ?        S    10:38   0:00 /usr/sbin/echod
root     14175  0.0  0.0   2900   764 pts/4    R+   10:38   0:00 grep echod
$ sudo kill -15 14167  

Per la resta de senyals consulteu el manual de kill.

Comproveu també que es crea un fitxer PIDFILE:

$ ps aux | grep echod
root     14167  0.0  0.0   1508   280 ?        S    10:38   0:00 /usr/sbin/echod
sergi    14263  0.0  0.0   2896   760 pts/2    R+   10:40   0:00 grep echod
$ sudo cat /var/run/echod.pid 
14167

Registre de dimonis. Logging. Syslog

Com hem comentat anteriorment, una de les caracterítiques principals dels dimonis és que no tenen una interfície directa amb els usuaris. Què passa doncs si volem notificar errors o enregistrar esdeveniments del dimoni?

En aquests casos és quan entra en joc el registre del sistema Syslog. Els sistemes Unix tenen un servei centralitzat de registre (logging en angles) on podem notificar els errors o esdeveniments del nostres dimonis. Syslog és una utilitat client-servidor que permet a les aplicacions (clients) enregistrar els seus esdeveniments en un fitxer centralitzat (servidor). Els missatges són guardats al servidor en diferents fitxers segons la configuració del fitxer /etc/syslog.conf.

El fitxer include:

/usr/include/syslog.h

Ens proporciona les següents funcions:

void openlog(char *ident, int option, int facility); 

void syslog(int priority, char *format, ...); 

void closelog(void);

La funció openlog() crea una connexió amb syslog. La cadena de caràcters ident és afegida a cada missatge i correspon normalment al nom del dimoni. Això permet identificar quines línies del registre pertanyen al nostre dimoni en el cas que compartim el registre amb altres aplicacions. El paràmetre option permet fer el log a la consola en cas d'error. L'argument facility classifica el tipus de programa o dimoni que estem executant. Per defecte LOG_USER.

La funció syslog() és l'encarregada d'afegir un missatge al registre. Els arguments són similars als de la funció printf() amb excepció de %m que serà reemplaçat pel missatge d'error per al valor actual de errno. El paràmetre priority indica la importància del missatge.

La funció closelog tanca la connexió amb syslog. És opcional.

Les utilitats de syslog estan incorporades al paquet libc6-dev:

$ dpkg -S /usr/include/syslog.h
libc6-dev: /usr/include/syslog.h

Recursos:

Exemple dimoni d'ecos amb Syslog

  • SVN:
svn checkout https://svn.lafarga.cpl.upc.edu/dades/svn/plinux/sessio5/echoDimoniSysLog
  • Usuari: anonymous
  • Paraula de pas: anonymous

Programació d'scripts d'inicialització System V per a dimonis

SVN:

$ svn chechout https://svn.lafarga.cpl.upc.edu/dades/svn/plinux/sessio5/echoDimoni
  • Usuari: anonymous
  • Paraula de pas: anonymous

Els dimonis són processos que s'executen en segon pla i que normalment no són cridats explícitament per l'usuari. És el sistema qui s'encarrega de controlar l'execució de l'script. Molts sistemes Linux utilitzen el sistema d'arrancada de SystemV (entre ells Debian i Ubuntu).

Si voleu conèixer amb més detall com funciona aquest sistema consulteu l'article Configuració de serveis en Linux. Daemons. Des de la perspectiva del programador ens interessa conèixer les particularitats dels scripts d'inicialització System V.

Tal i com podem veure executant:

$ dpkg -S /etc/init.d/skeleton
initscripts: /etc/init.d/skeleton

El paquet initscripts ens proporciona un esquelet de com haurien de ser aquests scripts en Debian. La primera secció conté tags informatius sobre l'script:

### BEGIN INIT INFO
# Provides:          skeleton
# Required-Start:    $local_fs $remote_fs
# Required-Stop:     $local_fs $remote_fs
# Default-Start:     2 3 4 5
# Default-Stop:      S 0 1 6
# Short-Description: Example initscript
# Description:       This file should be used to construct scripts to be
#                    placed in /etc/init.d.
### END INIT INFO   

Després comença la declaració de variables que permet fer que l'script sigui més robust i fàcil d'utilitzar:

# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/usr/sbin:/usr/bin:/sbin:/bin
DESC="Description of the service"
NAME=daemonexecutablename
DAEMON=/usr/sbin/$NAME
DAEMON_ARGS="--options args"
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME 

La gràcia del fitxer skeleton és que configurant correctament aquests paràmetres podem tenir un script d'inicialització System V per als nostres serveis sense necessitat de tocar garirabé res més de la resta del fitxer.

Les següents línies controlen que existeix el dimoni abans de continuar.

# Exit if the package is not installed
 [ -x "$DAEMON" ] || exit 0

NOTA: Recordeu que una desinstal·lació (P. ex. apt-get remove) sense l'opció purge (--purge) mantindrà els fitxers de configuració del paquet localitzats a la carpeta o subcarpetes /etc.

Per conveni s'estableix que el fitxer /etc/default/$NAME, si existeix, contindrà els paràmetres per defecte que configuren el dimoni.

# Read configuration variable file if it is present
[ -r /etc/default/$NAME ] && . /etc/default/$NAME 

Tot seguit incloem, si existeix, el fitxer /etc/default/rcS.

# Load the VERBOSE setting and other rcS variables

[ -f /etc/default/rcS ] && . /etc/default/rcS

I ara s'inclouen les funcions de log de LSB:

# Define LSB log_* functions.
# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
. /lib/lsb/init-functions

El següent són les definicions de les funcions encarregades d'executar, recarregar o parar el dimoni. La primera funció és start:

do_start()
{

# Return # 0 if daemon has been started # 1 if daemon was already running # 2 if daemon could not be started start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \ || return 1 start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \ $DAEMON_ARGS \ || return 2 # Add code here, if necessary, that waits for the process to be ready # to handle requests from services started subsequently which depend

	# on this one.  As a last resort, sleep for some time.
}

start-stop-daemon és un binari proporcionat pel paquet dpkg:

$ dpkg -S /sbin/start-stop-daemon
dpkg: /sbin/start-stop-daemon

Tal i com podem llegir al manual d'start-stop daemon la seva funció és encarregar-se de comprovar si el servei que volem executar ja s'estava executant abans de tornar-lo a executar. La primera línia:

start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null || return 1

Comprova si el procés ja existeix. Si existeix l'script s'atura tornant un 1. La segona línia:

start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- $DAEMON_ARGS || return 2

Executa el servei o retorna 2 si hi ha algun problema.

La següent part de l'script s'encarrega de la parada del servei. Aquesta funció és força equivalent a la funció anterior (start):

do_stop()
 {

# Return # 0 if daemon has been stopped # 1 if daemon was already stopped # 2 if daemon could not be stopped # other if a failure occurred start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME RETVAL="$?" [ "$RETVAL" = 2 ] && return 2 # Wait for children to finish too if this is a daemon that forks # and if the daemon is only ever run from this initscript. # If the above conditions are not satisfied then add some other code # that waits for the process to drop all resources that could be # needed by services started subsequently. A last resort is to # sleep for some time. start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON [ "$?" = 2 ] && return 2 # Many daemons don't delete their pidfiles when they exit. rm -f $PIDFILE return "$RETVAL"

}

I finalment la funció reload:

#
# Function that sends a SIGHUP to the daemon/service
#
do_reload() {

# # If the daemon can reload its configuration without # restarting (for example, when it is sent a SIGHUP), # then implement that here. # start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME return 0

}

Tal i com ja hem comentat anteriorment a la secció Dimonis i senyals, els dimonis han de capturar la senyal 'SIGHUP que serà l'encarregada de recarregar el servei (normalment tornar a llegir els seus fitxers de configuració). Com podeu veure, start-stop-daemon té un paràmetre (--signal) que permet enviar senyals al dimoni.

Les ultimes línies contenen la lògica d'execucio de l'script. Es tracta d'una estructura case que segons el valor del primer paràmetre (start, stop, reload, etc.) crida la funció previàment definida que correspongui:

case "$1" in
 start)

[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" do_start case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; esac ;;

 stop)

[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" do_stop case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; esac ;;

 #reload|force-reload)

# # If do_reload() is not implemented then leave this commented out # and leave 'force-reload' as an alias for 'restart'. # #log_daemon_msg "Reloading $DESC" "$NAME" #do_reload #log_end_msg $? #;;

 restart|force-reload)

# # If the "reload" option is implemented then remove the # 'force-reload' alias # log_daemon_msg "Restarting $DESC" "$NAME" do_stop case "$?" in 0|1) do_start case "$?" in 0) log_end_msg 0 ;; 1) log_end_msg 1 ;; # Old process is still running *) log_end_msg 1 ;; # Failed to start esac ;; *) # Failed to stop log_end_msg 1 ;; esac ;;

 *)

#echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2 echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2 exit 3 ;;

esac

Fixeu-vos que és en aquesta secció on s'utiltizen les funcions de log LSB (log_daemon_msg, log_end_msg 1 etc) i el control dels paràmetres d'execució.

Podeu trobar un altre exemple d'esquelet aquí.

Recordeu que l'usuari i la contrasenya d'accés a SVN són anonymous i anonymous respectivament.

Recursos:

Linux Standard Functions (LSB)

  • Fitxer:/lib/lsb/init-functions
  • Paquet: lsb-base. Linux Standard Base 3.1 init script functionality
$ dpkg -S /lib/lsb/init-functions
lsb-base: /lib/lsb/init-functions

El fitxer /lib/lsb/init-functions proporciona d'utilitats de logging (registre) als dimonis Linux. Només comentarem les funcions:

  • log_begin_msg: Permet mostrar un missatge inicial sobre l'execució/aturada o recarrega del servei.
  • log_end_msg: Mostra un resultat final ([ ok ] o [ fail])

Aquestes funcions ens permeten mostrar els típics missatges d'execució de serveis:

Fitxer:LSF.png

Per sistemes Red Hat, les funcions equivalents es troben a: /etc/rc.d/init.d/functions.

Recursos:


Tags

### BEGIN INIT INFO
# Provides:          skeleton
# Required-Start:    $local_fs $remote_fs
# Required-Stop:     $local_fs $remote_fs
# Default-Start:     2 3 4 5
# Default-Stop:      S 0 1 6
# Short-Description: Example initscript
# Description:       This file should be used to construct scripts to be
#                    placed in /etc/init.d.
### END INIT INFO  


  1. chkconfig: <startlevellist> <startpriority> <endpriority> 
  Required. <startlevellist> is a list of levels in which the service should be started by default. <startpriority> and <endpriority> are priority numbers. For example: # chkconfig: 2345 20 80 Read 'man chkconfig' for more information.
  Unless there is a VERY GOOD, EXPLICIT reason to the contrary, the <endpriority> should be equal to 100 - <startpriority>
  • Description: Descripció de l'script.
  1. config: 
  Optional, multiple entries allowed. For each static config file used by the daemon, use a single entry. For example:
  1. config: /etc/httpd/conf/httpd.conf
  2. config: /etc/httpd/conf/srm.conf 
  Optionally, if the server will automatically reload the config file if it is changed, you can append the word "autoreload" to the line:
  1. config: /etc/foobar.conf autoreload
  2. pidfile: 
  Optional, multiple entries allowed. Use just like the config entry, except that it points at pidfiles. It is assumed that the pidfiles are only updated at process creation time, and not later. The first line of this file should be the ASCII representation of the PID; a terminating newline is optional. Any lines other than the first line are not examined.
  1. probe: true 
  Optional, used IN PLACE of processname, config, and pidfile. If it exists, then a proper reload-if-necessary cycle may be acheived by running these commands:

command=$(/etc/rd.d/init.d/SCRIPT probe) [ -n "$command" ] && /etc/rc.d/init.d/SCRIPT $command

where SCRIPT is the name of the service's sysv init script.

  Scripts that need to do complex processing could, as an example, return "run /var/tmp/<servicename.probe.$$" and implement a "run" command which would execute the named script and then remove it.
  Note that the probe command should simply "exit 0" if nothing needs to be done to bring the service into sync with its configuration files.


Fitxer /usr/share/initscripts/default.rcS

El fitxer /usr/share/initscripts/default.rcS conté una sèrie de variables que controlen l'execució dels scripts d'inicialització:

$ cat /usr/share/initscripts/default.rcS

Quantes vegades heu perdut un fitxer que havieu descarregat de la web obrint en comptes de desant, i al reiniciar la màquina s'havia esborrat de la carpeta /tmp? Això es pot evitar indicant el nombre de dies que voleu que es mantinguin els fitxers a la carpeta tmp canviant la variable TMPTIME. Per exemple 30 dies:

$ sudo joe /usr/share/initscripts/default.rcS
TMPTIME=30
SULOGIN=no
DELAYLOGIN=no
UTC=yes
VERBOSE=no
FSCKFIX=no

També hi ha el fitxer /etc/default/rcS:

$ cat /etc/default/rcS
#
#       Defaults for the boot scripts in /etc/rcS.d.
#       This file originates from package initscripts.  Because of
#       bug #213907, it is not possible to tell this to dpkg.
#

# Time files in /tmp are kept in days.
TMPTIME=0
# Set to yes if you want sulogin to be spawned on bootup
SULOGIN=no
# Set to no if you want to be able to login over telnet/rlogin
# before system startup is complete (as soon as inetd is started)
DELAYLOGIN=no
# Set UTC=yes if your system clock is set to UTC (GMT), and UTC=no if not.
UTC=no
# Set VERBOSE to "no" if you would like a more quiet bootup.
VERBOSE=yes
# Set EDITMOTD to "no" if you don't want /etc/motd to be regenerated
# automatically
EDITMOTD=yes
# Set FSCKFIX to "yes" if you want to add "-y" to the fsck at startup.
FSCKFIX=no  


Per a més informació consulteu man 5 rcS.


Exercici. Crear un init script pel dimoni d'ecos

Utilitzant el fitxer /etc/init.d/skeleton, creeu un fitxer init script pel dimoni d'ecos.


Versió final del servidor d'ecos

En aquest apartat podeu trobar una solució completa del servidor d'ecos amb les següents funcionalitats:

  • S'ha aplicat la Guia de bones pràctiques per tal de "dimonitzar" el servidor.
  • S'implementa la captura de senyals seguint la Guia de bones pràctiques. NOTA: Faltaria implementar el reload (rellegir el fitxer /etc/echod/port).
  • Utilitza Syslog.
  • Conté un script d'inicialització System V i utilitza un fitxer de PID per tal de controlar la seva execució.
  • Si existeix, llegeix el port on s'ha d'executar el servidor del fitxer /etc/echod/port.


Podeu obtenir els fitxers de la solució a:

$ svn checkout https://svn.lafarga.cpl.upc.edu/dades/svn/plinux/sessio5/echoDimoniSolucio

  • Usuari: anonymous
  • Paraula de pas: anonymous

Dimonis multithreading

2335f1.png

TODO

Dimonis Autònoms (Standalone Daemons)

Quan es vol executar un dimoni/servidor Linux, s'executa un programa que crea el socket i es posa en mode LISTENING a l'espera de rebre peticions dels clients.

Exemples de dimonis autònoms:

  • Apache (httpd)
  • Squid
  • Samba

Normalment la majoria de dimonis tenen unes característiques comunes:

  • Els seus noms acaben per d (daemon)
  • Responen a la senyal HUP (veieu man 7 signal) re-llegint els seus fitxers de configuració. La senyal HUP es pot enviar executant:
$kill -HUP comanda
  • Són inicialitzats per scripts ques troben a la carpeta /etc/init.d seguint system V. Aquests scripts accepten com a mínim els paràmetres start i stop. La majoria també accepten el paràmetre restart per tal de tornar a llegir els fitxers de configuració.
  • Quant reben una petició d'un client, normalment fan una còpia d'ells mateixos (fork()) per tal de oferir el servei. Això provoca que hi hagin múltiples instàncies del mateix dimoni executant-se al mateix temps.

Veieu l'article Configuració de serveis en Linux. Dimonis per obtenir més informació sobre l'administració i configuració de dimonis.

Inetd. xinetd

El sistema de dimonis autònoms té el següents inconvenients:

  • Si es tenen múltiples serveis en una mateixa màquina es necessiten múltiples dimonis executant-se a la màquina, inclús encara que estiguin inactius.
  • No hi ha una forma centralitzada de modificar els dimonis per proveir serveis com com l'encriptació o el control d'accés. Cada dimoni ha de programar el seu propi codi.
  • Si el dimoni mor per un error de programació, el dimoni no estarà disponible fins que es torni a executar.
  • Programar dimonis no és senzill sobretot perquè la programació és Multithreading.

Llavors es va proposar la idea de tenir un sol dimoni, configurat per tal d'escoltar peticions de múltiples serveis. Així van néixer els "super-servidors d'Internet", primer inetd i després xinetd. "Internet super-server." Xinetd és un reemplaçament més segur de inetd.

Recursos