  Process Monitor HOW-TO for Linux
  Al Dev (Alavoor Vasudevan) alavoor@yahoo.com
  v11.0, 14 June 2001

  Questo documento descrive come monitorare i processi di un sistema
  Linux/Unix e reinizializzarli automaticamente se sono arrestati senza
  un qualsiasi intervento manuale. Questo documento si trova anche
  all'URL "Unix Processes" FAQ.  La traduzione italiana  stata curata
  da Bettani Marco (cmos at inwind.it).
  ______________________________________________________________________

  Indice Generale


  1. Processi Linux o Unix
  2. Comando Unix/Linux: procautostart
  3. File procautostart.cpp
  4. File debug.cpp
  5. File debug.h
  6. Makefile
  7. Testare il programma: monitor_test
  8. Altri strumenti di monitoraggio
     8.1 Comando init di Unix
     8.2 Strumenti di monitoraggio OpenSource
     8.3 Strumenti di monitoraggio: "daemontools"
     8.4 Strumenti di monitoraggio commerciali

  9. Altri formati di questo documento
     9.1 Formato Acrobat PDF
     9.2 Convertire Linuxdoc nel formato Docbook
     9.3 Convertire nel formato MS WinHelp
     9.4 Leggere diversi formati

  10. Copyright Notice


  ______________________________________________________________________

  11..  PPrroocceessssii LLiinnuuxx oo UUnniixx

  I processi sono il "cuore" di Linux/Unix.  molto importante
  monitorare i processi delle applicazioni per assicurare al 100% la
  disponibilit e l'attendibilit del sistema.  Per esempio, i processi
  di database, server web, ecc. hanno bisogno di essere attivi 24 ore al
  giorno e 365 giorni all'anno.  Utilizzate gli strumenti descritti in
  questo documento per monitorare i processi di importanti applicazioni.

  Vedete anche i seguenti argomenti sui processi di Linux/Unix:

    Unix Programming FAQ - Capitolo 1 Processi Unix
     <http://www.erlenstar.demon.co.uk/unix/faq_toc.html>


    Altre FAQ su Unix sono alla pagina
     <http://www.erlenstar.demon.co.uk/unix/>


  22..  CCoommaannddoo UUnniixx//LLiinnuuxx:: pprrooccaauuttoossttaarrtt

  Usate il programma pprrooccaauuttoossttaarrtt (si dice "Prok-Auto-Start" o Process
  AutoStart) per monitorare e automaticamente reinizializzare qualsiasi
  processo Unix/Linux che non sia terminato. Questo piccolo programma 
  molto utile ed  comparabile con grandi prodotti commerciali i quali
  ccoossttaannoo cciirrccaa 8800..000000 $$.  Procautostart pu essere utilizzato per
  controllare le seguenti applicazioni:

    Per controlli in tempo reale di processi industriali come industrie
     chimiche, manifatturiere, generatori di potenza ed altri.
     Utilizzare nel programma _n_a_n_o_-_s_e_c_o_n_d_s per ottenere un controllo
     migliore.

    Per controllare i processi di applicazioni software come server
     web, server di database, o processi Unix critici, ecc.

    Come un sistema di allarme per quasiasi sistema di monitoraggio di
     software generale. Il programma pu dar luogo ad una pagina o
     telefonare o accendere delle lampadine rosse sullo schermo del
     computer. Affinch chiami un numero di telefono dovreste utilizzare
     un modem.

  Il listato del programma  dato nella sezione seguente di questo
  documento.

  pprrooccaauuttoossttaarrtt  --nn   _< _d_e_l_a_y___s_e_c_o_n_d_s _> --cc   "_< _c_o_m_m_a_n_d___l_i_n_e _>" nohup &

  In questo modo si inizializza il processo Unix pprrooccaauuttoossttaarrtt ed anche
  il processo ccoommmmaanndd__lliinnee.  Il processo pprrooccaauuttoossttaarrtt reinizializzer
  il processo ccoommmmaanndd__lliinnee se  terminato.  L'opzione _-_n indica ogni
  quanti secondi pprrooccaauuttoossttaarrtt deve verificare l'esecuzione di
  ccoommmmaanndd__lliinnee.  consigliabile avviare procautostart come un processo
  in background, non interrotto dalla sessione di lavoro, utilizzando
  "nohup &". Vedere "man nohup".

  Procautostart  stato scritto in "C" cos che sia molto veloce ed
  efficiente, proprio perch il programma  chiamato ogni _n secondi.  Le
  risorse totali utilizzate da procautostart sono mmoollttoo rriiddoottttee ed
  insignificanti inoltre il programma  piccolo e altamente ottimizzato
  con l'opzione di compilazione -o3.

  Per esempio:

  ______________________________________________________________________
          procautostart -n 12 -c "monitor_test -d $HOME  -a dummy_arg " nohup &
  ______________________________________________________________________


  Qui pprrooccaauuttoossttaarrtt verificher il processo monitor_test ooggnnii 12 sec
  ondi.

  Il programma fornir in uscita un file di log nella sottodirectory
  "mon" creata con l'ora e la data in cui il processo  terminato e
  ripartito.  Questi file forniscono informazioni su quanto spesso i
  processi sono terminati.

  Potete anche utilizzare l'opzione "-m" per microsecondi o "-o" per
  nanosecondi, editando il codice sorgente di pprrooccaauuttoossttaarrtt..ccpppp e non
  commentando le linee appropriate.

  33..  FFiillee pprrooccaauuttoossttaarrtt..ccpppp

  //Dal vostro browser salvate questo file come un ffiillee ddii tteessttoo e
  chiamatelo "procautostart.cpp".



  ______________________________________________________________________
  // Author: Al Dev alavoor@yahoo.com
  //
  // Program to monitor the unix processes
  // and automatically re-start them if they die
  //
  //************************************************************************
  //  NOTE: This program uses the Al Dev's String class library. Download string
  //        class from  http://linuxdoc.org/HOWTO/C++Programming-HOWTO.html
  //************************************************************************


  #include <stdio.h>
  #include <strings.h> // C strings
  #include <unistd.h> // for getopt
  #include <alloc.h> // for free

  #include <errno.h> // for kill() - error numbers command
  extern int errno;

  #ifdef Linux
  #include <asm/errno.h> // for kill() - error numbers command
  #endif

  #include <sys/types.h> // for kill() command
  #include <signal.h> // for kill() command
  #include <sys/wait.h> // for wait()
  #include <stdlib.h> // for setenv()
  #include <time.h> // for strftime()
  #include <libgen.h> // for basename()

  //#include <syslog.h> // for logging

  #include "debug.h"
  #include "String.h"
  #include "StringTokenizer.h"

  #define BUFF_HUN        100
  #define BUFF_THOU       1024
  #define PR_INIT_VAL     -10
  #define WAIT_FOR_SYS    5  // wait for process to start up
  #define DEF_SL_SECS     6  // default sleep time
  #define SAFE_MEM        10  // to avoid any possible memory leaks

  #define LOG_NO          false  // do not output to logfile
  #define LOG_YES         true  // do output to logfile
  #define STD_ERR_NO      false  // do not print to std err
  #define STD_ERR_YES     true  // do print to std err
  #define DATE_NO         false  // do not print date
  #define DATE_YES        true  // do print date

  int start_process(char *commandline, char *args[], char **envp, pid_t proc_pid);
  int fork2(pid_t parent_pid, unsigned long tsecs);
  inline void error_msg(char *mesg_out, char *lg_file, bool pr_lg, bool std_err, bool pr_dt);

  //////////////////////////////////////////////
  // To test this program use --
  // procautostart -n 5 -c 'monitor_test dummy1 -a dummy2 -b dummy3  ' &
  //////////////////////////////////////////////

  void usage(char **argv)
  {

      printf("%s:\n", argv[0]);
      printf("\ninterval specification:\n");
      printf(" -n # -- seconds\n");
      printf(" -m # -- microseconds\n");
      printf(" -o # -- nanoseconds\n");
      printf("\nprocess specification:\n");
      printf(" -c 'cmdline'\n");
      printf
          (" -p pidfile -- if specified reads process pid from this file\n");
      printf("\n");

          printf("\nUsage : %s -n <seconds> -m <microsecond> -o <nanosecond> -c '<command>'\n", argv[0]);
          printf("\nExample: procautostart -n 5 -c 'monitor_test dummy1 -a dummy2 -b dummy3  ' \n");

      exit(-1);
  }

  int main(int argc, char **argv, char **envp)
  {
          unsigned long   sleep_sec, sleep_micro, sleep_nano;
          int     ch;
          pid_t   proc_pid;
          int pr_no = PR_INIT_VAL;
          char mon_log[40];
          char *pr_name = NULL, **cmdargs = NULL;
          String cmdline;
      char *pidfile = NULL;

          // you can turn on debug by editing Makefile and put -DDEBUG_PRT in gcc
          debug_("test debug", "this line");
          debug_("argc", argc);

          // Use getpid() - man 2 getpid()
          proc_pid = getpid(); // get the Process ID of procautostart
          debug_("PID proc_pid", (int) proc_pid);

          // Create directory to hold log, temp files
          system("mkdir mon 1>/dev/null 2>/dev/null");

          sleep_sec = DEF_SL_SECS ; // default sleep time
          sleep_micro = 0; // default micro-sleep time
          sleep_nano = 0; // default nano-sleep time
          optarg = NULL;
          while ((ch = getopt(argc, argv, "n:m:o:h:c:")) != -1) // needs trailing colon :
          {
                  switch (ch)
                  {
                          case 'n':
                                  debug_("scanned option n ", optarg);
                                  sleep_sec = atoi(optarg);
                                  debug_("sleep_sec", sleep_sec);
                                  break;
                          case 'm':
                                  debug_("scanned option m ", optarg);
                                  sleep_micro = atoi(optarg);
                                  debug_("sleep_micro", sleep_micro);
                                  break;
                          case 'o':
                                  debug_("scanned option o ", optarg);
                                  sleep_nano = atoi(optarg);
                                  debug_("sleep_nano", sleep_nano);
                                  break;
                          case 'c':
                                  debug_("scanned option c ", optarg);
                                  cmdline = optarg;
                                  //cmdline = strdup(optarg); // does auto-malloc here
                                  debug_("cmdline", cmdline.val());
                                  break;
                          case 'h':
                                  debug_("scanned option h ", optarg);
                                  usage(argv);
                                  break;
              case 'p':
                  pidfile = strdup(optarg);
                  break;

                          default:
                                  debug_("ch", "default");
                                  usage(argv);
                                  break;
                  }
          }

          if (cmdline.length() == 0) //if (cmdline == NULL)
                  usage(argv);

          // detach from the main process
      if (fork())  // 0 returned in child process
                  exit(0); // exit parent process - non-zero child PID got here...

      //openlog(argv[0], LOG_PID, LOG_DAEMON);

          // trim the trailing blanks -- otherwise problem in grep command
          //cmdline.trim(true);
          //cmdline.chopall('&'); // trim trailing ampersand
          debug_("cmdline", cmdline.val());

          // Start the process
          {
                  // Find the command line args
                  StringTokenizer strtokens(cmdline.val());  // string tokenizer is borrowed from Java
                  cmdargs = (char **) calloc(strtokens.countTokens() + SAFE_MEM, sizeof(char *));
                  debug_("countTokens()", strtokens.countTokens());
                  for (int tmpii = 0; strtokens.hasMoreTokens(); tmpii++)
                  {
                          cmdargs[tmpii] = strdup(strtokens.nextToken().val());
                          debug_("tmpii", tmpii);
                          debug_("cmdargs[tmpii]", (char *) cmdargs[tmpii]);
                  }

                  // In case execve you MUST NOT have trailing ampersand & in the command line!!
                  //pr_no = start_process(cmdline, NULL, NULL, proc_pid);  // Using execlp ...
                  pr_no = start_process(cmdargs[0], & cmdargs[0], envp, proc_pid); // Using execve ....
                  //cmdpid = start_command(cmdargs, envp, pidfile);

                  // You can also use syslog if you do not like above logging
                  //syslog(LOG_NOTICE, "Started process: %s", cmdline.val());

                  debug_("The child pid", pr_no);
                  if (pr_no < 0)
                  {
                          fprintf(stderr, "\nFatal Error: Failed to start the process\n");
                          exit(-1);
                  }
                  sleep(WAIT_FOR_SYS); // wait for the process to come up

                  // Get process name - only the first word from cmdline
                  pr_name = strdup(basename(cmdargs[0])); // process name,  does auto-malloc here
          }

          // generate log file names
          {
                  char    aa[21];

                  strncpy(aa, pr_name, 20); aa[20] = '\0';
                  // Define mon file-names - make it unique with combination of
                  // process name and process id
                  sprintf(mon_log, "mon/%s%d.log", aa, (int) proc_pid);
          }

          // Print out pid to log file
          if (pr_no > 0)
          {
                  char aa[200];
                  sprintf(aa, "Process ID of %s is %d", pr_name, pr_no);
                  error_msg(aa, mon_log, LOG_YES, STD_ERR_NO, DATE_YES);
          }

          // monitors the process - restarts if process dies...
          char print_log[200];
          while (1)  // infinite loop - monitor every 6 seconds
          {
                  //debug_("Monitoring the process now...", ".");
          switch (kill(pr_no, 0))
          {
              case 0:
                  break;

              default:
              case ESRCH:    // process died !!
                                  // ERSRCH means - No process can be found corresponding to pr_no
                                  // hence process had died !!
                                  sprintf(print_log, "Error ESRCH: No process or process group can be found for %d", pr_no);
                                  error_msg(print_log, mon_log, LOG_YES, STD_ERR_YES, DATE_YES);
                                  // You can also use syslog if you do not like above logging
                  //syslog(LOG_NOTICE, "PROCESS DIED: %s", cmdline.val());
                                  //pr_no = start_process(cmdline, NULL, NULL, proc_pid);  // Using execlp ....
                                  pr_no = start_process(cmdargs[0], & cmdargs[0], envp, proc_pid); // Using execve ....
                                  // You can also use syslog if you do not like below logging
                  //syslog(LOG_NOTICE, "Started process: %s", cmdline.val());
                                  sprintf(print_log, "Starting process %s", pr_name);
                                  error_msg(print_log, mon_log, LOG_YES, STD_ERR_NO, DATE_NO);
                                  sleep(WAIT_FOR_SYS); // wait for the process to come up
                  break;
          }
                  sprintf(print_log, "Process ID of %s is %d", pr_name, pr_no);
                  error_msg(print_log, mon_log, LOG_YES, STD_ERR_NO, DATE_NO);
                  //debug_("Sleeping now ......", ".");
                  sleep(sleep_sec);

                  // Uncomment these to use micro-seconds
                  // For real-time process control use micro-seconds or nana-seconds sleep functions
                  // See 'man3 usleep', 'man 2 nanasleep'
                  // If you do not have usleep() or nanosleep() on your system, use select() or poll()
                  // specifying no file descriptors to test.
                  //usleep(sleep_micro);

                  // To sleep nano-seconds ...  Uncomment these to use nano-seconds
                  //struct timespec *req = new struct timespec;
                  //req->tv_sec = 0; // seconds
                  //req->tv_nsec = sleep_nano; // nanoseconds
                  //nanosleep( (const struct timespec *)req, NULL);

                  /* You can use select instead of sleep for portability
          struct timeval interval;
                  interval.tv_sec += tmp;
                  interval.tv_usec += (long int) 1e3 *tmp;
          select(1, NULL, NULL, NULL, & interval);
                  */
          }
          //closelog(); if using syslog
  }

  inline void error_msg(char *mesg_out, char *lg_file, bool pr_lg, bool std_err, bool pr_dt)
  {
          if (pr_lg) // (pr_lg == true) output to log file
          {
                  char tmp_msg[BUFF_THOU];
                  if (pr_dt == true) // print date and message to log file 'lg_file'
                  {
                          sprintf(tmp_msg, "date >> %s; echo '\n%s\n' >> %s\n ",
                                  lg_file, mesg_out, lg_file);
                          system(tmp_msg);
                  }
                  else
                  {
                          sprintf(tmp_msg, "echo '\n%s\n' >> %s\n ",
                                  mesg_out, lg_file);
                          system(tmp_msg);
                  }
          }

          if (std_err) // (std_err == true) output to standard error
                  fprintf(stderr, "\n%s\n", mesg_out);

          //debug_("mesg_out", mesg_out);
  }

  // start a process and returns PID or -ve value if error
  // The main() function has envp arg as in - main(int argc, char *argv[], char **envp)
  int start_process(char *commandline, char *args[], char **envp, pid_t parent_pid)
  {
          int ff;
          unsigned long  tsecs;

          tsecs = time(NULL); // time in secs since Epoch 1 Jan 1970
          debug_("Time tsecs", tsecs);

          // Use fork2() instead of fork to avoid zombie child processes
          switch (ff = fork2(parent_pid, tsecs))  // fork creates 2 process each executing the following lines
          {
          case -1:
                  fprintf(stderr, "\nFatal Error: start_process() - Unable to fork process\n");
                  _exit(errno);
                  break;
          case 0:  // child process
                  debug_("\nStarting the start child process\n", " ");
                  // For child process to ignore the interrupts (i.e. to put
                  // child process in "background" mode.
                  // Signals are sent to all processes started from a
                  // particular terminal. Accordingly, when a program is to be run non-interactively
                  // (started by &), the shell arranges that the program will ignore interrupts, so
                  // it won't be stopped by interrupts intended for foreground processes.
                  // Hence if previous value of signal is not IGN than set it to IGN.

                  // Note: Signal handlers cannot be set for SIGKILL, SIGSTOP
                  if (signal(SIGINT, SIG_IGN) == SIG_ERR)
                          fprintf(stderr, "\nSignal Error: Not able to set signal to SIGINT\n");
                  else
                  if (signal(SIGINT, SIG_IGN) != SIG_IGN) // program already run in background
                          signal(SIGINT, SIG_IGN);  // ignore interrupts

                  if (signal(SIGHUP, SIG_IGN) == SIG_ERR)
                          fprintf(stderr, "\nSignal Error: Not able to set signal to SIGHUP\n");
                  else
                  if (signal(SIGHUP, SIG_IGN) != SIG_IGN) // program already run in background
                          signal(SIGHUP, SIG_IGN);  // ignore hangups
                  if (signal(SIGQUIT, SIG_IGN) == SIG_ERR)
                          fprintf(stderr, "\nSignal Error: Not able to set signal to SIGQUIT\n");
                  else
                  if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) // program already run in background
                          signal(SIGQUIT, SIG_IGN);  // ignore Quit

                  if (signal(SIGABRT, SIG_IGN) == SIG_ERR)
                          fprintf(stderr, "\nSignal Error: Not able to set signal to SIGABRT\n");
                  else
                  if (signal(SIGABRT, SIG_IGN) != SIG_IGN) // program already run in background
                          signal(SIGABRT, SIG_IGN);  // ignore ABRT

                  if (signal(SIGTERM, SIG_IGN) == SIG_ERR)
                          fprintf(stderr, "\nSignal Error: Not able to set signal to SIGTERM\n");
                  else
                  if (signal(SIGTERM, SIG_IGN) != SIG_IGN) // program already run in background
                          signal(SIGTERM, SIG_IGN);  // ignore TERM

                  // sigtstp - Stop typed at tty. Ignore this so that parent process
                  // be put in background with CTRL+Z or with SIGSTOP
                  if (signal(SIGTSTP, SIG_IGN) == SIG_ERR)
                          fprintf(stderr, "\nSignal Error: Not able to set signal to SIGTSTP\n");
                  else
                  if (signal(SIGTSTP, SIG_IGN) != SIG_IGN) // program already run in background
                          signal(SIGTSTP, SIG_IGN);  // ignore TSTP

                  // You can use debug_ generously because they do NOT increase program size!
                  debug_("before execve commandline", commandline);
                  debug_("before execve args[0]", args[0]);
                  debug_("before execve args[1]", args[1]);
                  debug_("before execve args[2]", args[2]);
                  debug_("before execve args[3]", args[3]);
                  debug_("before execve args[4]", args[4]);
                  debug_("before execve args[5]", args[5]);
                  debug_("before execve args[6]", args[6]);
                  debug_("before execve args[7]", args[7]);
                  debug_("before execve args[8]", args[8]);
                  debug_("before execve args[9]", args[9]);
                  execve(commandline, args, envp);

                  // execlp, execvp does not provide expansion of metacharacters
                  // like <, >, *, quotes, etc., in argument list. Invoke
                  // the shell /bin/sh which then does all the work. Construct
                  // a string 'commandline' that contains the complete command
                  //execlp("/bin/sh", "sh", "-c", commandline, (char *) 0);  // if success than NEVER returns !!

                  // If execlp returns than there is some serious error !! And
                  // executes the following lines below...
                  fprintf(stderr, "\nFatal Error: Unable to start child process\n");
                  ff = -2;
                  exit(127);
                  break;
          default: // parent process
                  // child pid is ff;
                  if (ff < 0)
                          fprintf(stderr, "\nFatal Error: Problem while starting child process\n");

                  {
                          char    buff[BUFF_HUN];
                          FILE    *fp1;
                          sprintf(buff, "mon/%d%lu.out", (int) parent_pid, tsecs); // tsecs is unsigned long
                          fp1 = fopen(buff, "r");
                          if (fp1 != NULL)
                          {
                                  buff[0] = '\0';
                                  fgets(buff, BUFF_HUN, fp1);
                                  ff = atoi(buff);
                          }
                          fclose(fp1);
                          debug_("start process(): ff - ", ff);
  #ifndef DEBUG_PRT
                          sprintf(buff, "rm -f mon/%d%lu.out", (int) parent_pid, tsecs);
                          system(buff);
  #endif // DEBUG_PRT
                  }

                  // define wait() to put child process in foreground or else put in background
                  //waitpid(ff, & status, WNOHANG || WUNTRACED);
                  //waitpid(ff, & status, WUNTRACED);
                  //wait(& status);

                  break;
          }
          return ff;
  }

  /* fork2() -- like fork, but the new process is immediately orphaned
   *            (won't leave a zombie when it exits)
   * Returns 1 to the parent, not any meaningful pid.
   * The parent cannot wait() for the new process (it's unrelated).
   */
  /* This version assumes that you *haven't* caught or ignored SIGCHLD. */
  /* If you have, then you should just be using fork() instead anyway.  */

  int fork2(pid_t parent_pid, unsigned long tsecs)
  {
      pid_t mainpid, child_pid = -10;
      int status;
          char    buff[BUFF_HUN];

      if (!(mainpid = fork()))
      {
          switch (child_pid = fork())
          {
            case 0:
                          //child_pid = getpid();
                          //debug_("At case 0 fork2 child_pid : ", child_pid);
                          return 0;
            case -1:
                          _exit(errno);    /* assumes all errnos are <256 */
            default:
                          debug_("fork2 child_pid : ", (int) child_pid);
                          sprintf(buff, "echo %d > mon/%d%lu.out", (int) child_pid, (int) parent_pid, tsecs);
                          system(buff);
                          _exit(0);
          }
      }

          //debug_("fork2 pid : ", pid);
      if (mainpid < 0 || waitpid(mainpid, & status, 0) < 0)
        return -1;

      if (WIFEXITED(status))
        if (WEXITSTATUS(status) == 0)
          return 1;
        else
          errno = WEXITSTATUS(status);
      else
        errno = EINTR;  /* well, sort of :-) */

      return -1;
  }
  //
  // char respawn[1024];
  // strcpy(respawn, cmdline);
  // For "C" program use kill(pid_t process, int signal) function.
  // #include <signal.h> // See 'man 2 kill'
  // Returns 0 on success and -1 with errno set.
  //              kill -0 $pid 2>/dev/null || respawn
  // To get the exit return status do --
  //              kill -0 $pid 2>/dev/null | echo $?
  // Return value 0 is success and others mean failure
  // Sending 0 does not do anything to target process, but it tests
  // whether the process exists. The kill command will set its exit
  // status based on this process.
  //
  // Alternatively, you can use
  //              ps -p $pid >/dev/null 2>&1 || respawn
  // To get the exit return status do --
  //              ps -p $pid >/dev/null 2>&1 | echo $?
  // Return value 0 is success and others mean failure


  //***********************************************************************
  //      You can use pidfile to get the process id
  //**********************************************************************
  /*
  void poll_pidfile(char *pidfile)
  {
      struct stat buf;
      struct timeval interval =
      {
          tv_sec:0, tv_usec:(long int) 1e4
      }; // 10 miliseconds

      while (stat(pidfile, & buf) ? errno == ENOENT : buf.st_size == 0)
      {
          struct timeval i = interval;
          select(1, NULL, NULL, NULL, & i);
      }
  }

  int start_command(char **args, char **envp, char *pidfile)
  {
      pid_t cmdpid;

      if (pidfile != NULL)
      {
          switch (unlink(pidfile))
          {
              case ENOENT:
              case 0:
                  break;

              default:
                  return -1;
          }
      }

      switch (cmdpid = fork2())
      {
          case 0: // child
              execve(args[0], args, envp);
              exit(-1);

          case -1: // error
              return -1;

          default: // parent
              break;
      }

      if (pidfile != NULL)
      {
          FILE *pf;

          poll_pidfile(pidfile);

          if ((pf = fopen(pidfile, "r")) == NULL)
          {
              syslog(LOG_ERR, "failed to read pidfile, using fork2 pid");
          }
          else
          {
              char textpid[1024];
              pid_t pid;

              fgets(textpid, sizeof(textpid), pf);
              if ((pid = atoi(textpid)) != -1)
              {
                  cmdpid = pid;
              } else
              syslog(LOG_ERR,
                      "failed to find pid in pidfile, using fork2 pid");
              fclose(pf);
          }
      }

      return cmdpid;
  }
  */
  ______________________________________________________________________



  44..  FFiillee ddeebbuugg..ccpppp

  //Dal vostro browser salvate questo file come un ffiillee ddii tteessttoo e
  chiamatelo "debug.cpp".



  ______________________________________________________________________
  #ifdef DEBUG_PRT

  #include "debug.h"
  // Variable value[] can be char, string, int, unsigned long, float, etc...

  void local_dbg(char name[], char value[], char fname[], int lineno, bool logfile) {
          printf("\nDebug %s Line: %d %s is = %s\n", fname, lineno, name, value ); }

  void local_dbg(char name[], int value, char fname[], int lineno, bool logfile) {
          printf("\nDebug %s Line: %d %s is = %d\n", fname, lineno, name, value ); }

  void local_dbg(char name[], unsigned int value, char fname[], int lineno, bool logfile) {
          printf("\nDebug %s Line: %d %s is = %d\n", fname, lineno, name, value ); }

  void local_dbg(char name[], long value, char fname[], int lineno, bool logfile) {
          printf("\nDebug %s Line: %d %s is = %ld\n", fname, lineno, name, value ); }

  void local_dbg(char name[], unsigned long value, char fname[], int lineno, bool logfile) {
          printf("\nDebug %s Line: %d %s is = %ld\n", fname, lineno, name, value ); }

  void local_dbg(char name[], short value, char fname[], int lineno, bool logfile) {
          printf("\nDebug %s Line: %d %s is = %d\n", fname, lineno, name, value ); }

  void local_dbg(char name[], unsigned short value, char fname[], int lineno, bool logfile) {
          printf("\nDebug %s Line: %d %s is = %d\n", fname, lineno, name, value ); }

  void local_dbg(char name[], float value, char fname[], int lineno, bool logfile) {
          printf("\nDebug %s Line: %d %s is = %f\n", fname, lineno, name, value ); }

  void local_dbg(char name[], double value, char fname[], int lineno, bool logfile) {
          printf("\nDebug %s Line: %d %s is = %f\n", fname, lineno, name, value ); }

  // You add many more here - value can be a class, ENUM, datetime, etc...

  #endif // DEBUG_PRT
  ______________________________________________________________________



  55..  FFiillee ddeebbuugg..hh

  //Dal vostro browser salvate questo file come un ffiillee ddii tteessttoo e
  chiamatelo "degub.h".



  ______________________________________________________________________
  #ifdef DEBUG_PRT

  #include <stdio.h>
  //#include <strings.h>
  //#include <assert.h>  // assert() macro which is also used for debugging

  // Debugging code
  // Use debug2_ to output result to a log file
  #define debug_(NM, VL) (void) ( local_dbg(NM, VL, __FILE__, __LINE__) )
  #define debug2_(NM, VL, LOG_FILE) (void) ( local_dbg(NM, VL, __FILE__, __LINE__, LOG_FILE) )
  void local_dbg(char name[], char value[], char fname[], int lineno, bool logfile= false);
  void local_dbg(char name[], int value, char fname[], int lineno, bool logfile= false);
  void local_dbg(char name[], unsigned long value, char fname[], int lineno, bool logfile= false);
  void local_dbg(char name[], float value, char fname[], int lineno, bool logfile= false);

  #else

  #define debug_(NM, VL) ((void) 0)
  #define debug2_(NM, VL, LOG_FILE) ((void) 0)

  #endif // DEBUG_PRT
  ______________________________________________________________________



  66..  MMaakkeeffiillee

  #Dal vostro browser salvate questo file come un ffiillee ddii tteessttoo e
  chiamatelo "Makefile".



  ______________________________________________________________________
  #//*****************************************************************
  #// Copyright policy is GNU/GPL and it is requested that
  #// you include author's name and email on all copies
  #// Author : Al Dev Email: alavoor@yahoo.com
  #//*****************************************************************

  .SUFFIXES: .pc .cpp .c .o

  HOSTFLAG=-DLinux
  #HOSTFLAG=-DSunOS

  CC=gcc
  CXX=g++

  MAKEMAKE=mm
  #LIBRARY=libString.a
  DEST=/home/myname/lib

  # Note: You should set only ONE value of MYCFLAGS below, that is only
  # one line is uncommented and others are commented.
  # Use options -Wall (all warning msgs) -O3 (optimization)
  MYCFLAGS=-DDEBUG_PRT -g3 -Wall
  #MYCFLAGS=-O3 -Wall

  #PURIFY=purify -best-effort

  SRCS=procautostart.cpp debug.cpp
  #HDR=my_malloc.h  String.h StringTokenizer.h File.h debug.h string_multi.h
  #LIBOBJS=my_malloc.o String.o StringTokenizer.o File.o debug.o
  OBJS=procautostart.o  debug.o
  EXE=procautostart

  # For generating makefile dependencies..
  SHELL=/bin/sh

  CPPFLAGS=$(MYCFLAGS) $(OS_DEFINES)
  CFLAGS=$(MYCFLAGS) $(OS_DEFINES)

  #
  # If the libString.a is in the current
  # directory then use -L. (dash L dot)
  MYLIBDIR=-L$(MY_DIR)/libmy -L.

  ALLLDFLAGS= $(LDFLAGS)  $(MYLIBDIR)

  COMMONLIBS=-lstdc++ -lm
  MYLIBS=-lString
  LIBS=$(COMMONLIBS)  $(MYLIBS)

  all: $(LIBRARY) $(EXE)

  $(MAKEMAKE):
          @rm -f $(MAKEMAKE)
          $(PURIFY) $(CXX) -M  $(INCLUDE) $(CPPFLAGS) *.cpp > $(MAKEMAKE)

  $(EXE): $(OBJS) $(LIBRARY)
          @echo "Creating a executable "
          $(PURIFY) $(CC) -o $(EXE) $(OBJS) $(ALLLDFLAGS) $(LIBS)

  #$(LIBRARY): $(LIBOBJS)
  #       @echo "\n***********************************************"
  #       @echo "   Loading $(LIBRARY) ... to $(DEST)"
  #       @echo "***********************************************"
  #       @ar cru $(LIBRARY) $(LIBOBJS)
  #       @echo "\n "
  .cpp.o: $(SRCS) $(HDR)
  #       @echo "Creating a object files from " $*.cpp " files "
          $(PURIFY) $(CXX) -c  $(INCLUDE) $(HOSTFLAG) $(CPPFLAGS) $*.cpp

  .c.o: $(SRCS) $(HDR)
  #       @echo "Creating a object files from " $*.c " files "
          $(PURIFY) $(CC) -c $(INCLUDE) $(HOSTFLAG) $(CFLAGS) $*.c

  clean:
          rm -f *.o *.log *~ *.log.old *.pid core err a.out lib*.a afiedt.buf  *.class tags
          rm -f $(EXE)
          rm -f $(MAKEMAKE)
          ln -s ../cpphowto/libString.a .

  #%.d: %.c
  #       @echo "Generating the dependency file *.d from *.c"
  #       $(SHELL) -ec '$(CC) -M $(CPPFLAGS) $< | sed '\''s/$*.o/& $@/g'\'' > $@'
  #%.d: %.cpp
  #       @echo "Generating the dependency file *.d from *.cpp"
  #       $(SHELL) -ec '$(CC) -M $(CPPFLAGS) $< | sed '\''s/$*.o/& $@/g'\'' > $@'

  # Must include all the c flags for -M option
  #$(MAKEMAKE):
  #       @echo "Generating the dependency file *.d from *.cpp"
  #       $(CXX) -M  $(INCLUDE) $(CPPFLAGS) *.cpp > $(MAKEMAKE)

  include $(MAKEMAKE)
  #include $(SRCS:.cpp=.d)
  #include $(SRCS:.c=.d)
  ______________________________________________________________________



  77..  TTeessttaarree iill pprrooggrraammmmaa:: mmoonniittoorr__tteesstt

  Dal vostro browser salvate questo file come un ffiillee ddii tteessttoo e
  chiamatelo "monitor_test".

  Utilizzate questo programma per testare il programma "procautostart".
  Per esempio:

  ______________________________________________________________________
          procautostart -n 12 -c "monitor_test -d $HOME  -a dummy_arg " nohup &
  ______________________________________________________________________


  Qui pprrooccaauuttoossttaarrtt verificher il processo monitor_test ooggnnii 12 sec
  ondi.


  ______________________________________________________________________
  #!/bin/ksh

  # Program to test the procautostart

  echo "Started the monitor_test ...."
  date > monitor_test.log
  while :
  do
          date >> monitor_test.log
          sleep 2
  done
  ______________________________________________________________________



  Poi usate il comando tail per monitorare l'uscita e simulate gli
  insuccessi del programma monitor_test.

  ______________________________________________________________________
          bash$ tail -f monitor_test.log
          bash$ ps -ef | grep monitor_test
  Guardate il PID del monitor_test e terminatelo.
          bash$ kill -9 < PID of monitor_test >
  ______________________________________________________________________


  Dopo aver terminato il processo, vi verr comunicato immediatamente
  che  stato riattivato proprio da procautostart!

  88..  AAllttrrii ssttrruummeennttii ddii mmoonniittoorraaggggiioo


  88..11..  CCoommaannddoo iinniitt ddii UUnniixx

  Il comando iinniitt  un freddo strumento per fare dei semplici
  monitoraggi di processi.  Aggiungete :respawn: all'interno del vostro
  /etc/inittab, se necessitate di avere processi riavviabili.  Vedete le
  pagine di manuale online digitando "man init" al prompt della shell.

  88..22..  SSttrruummeennttii ddii mmoonniittoorraaggggiioo OOppeennSSoouurrccee

  In un sistema Linux potete trovare i seguenti pacchetti. Se non sono
  nel CDROM principale potete verificare nel CDROM contrib:

    Nel CDROM contrib ddaaeemmoonnttoooollss**..rrppmm


    "top" (comando) pprrooccppss**..rrppmm


    "top" (comando in modalit grafica) pprrooccppss--XX1111**..rrppmm


    "ktop" (in modalit grafica) kkttoopp**..rrppmm


    "gtop" (in modalit grafica) ggttoopp**..rrppmm


    "WMMon" (carico della CPU) wwmmmmoonn**..rrppmm


    "wmsysmon" (monitor) wwmmssyyssmmoonn**..rrppmm


    "procmeter" (rilevatore dell'attivit del sistema) pprrooccmmeetteerr**..rrppmm


     Per usare i comandi top digitare al prompt di Unix:

     ___________________________________________________________________
             $ top
             $ ktop
             $ gtop
     ___________________________________________________________________



  88..33..  SSttrruummeennttii ddii mmoonniittoorraaggggiioo:: ""ddaaeemmoonnttoooollss""

  Visitate il sito web degli strumenti daemon a
  <http://www.pobox.com/~djb/daemontools.html>

  Per installare il formato RPM di questi strumenti, fate:

  ______________________________________________________________________
          # rpm -i /mnt/cdrom/daemontools*.html
          # man supervise
  ______________________________________________________________________


  SSuuppeerrvviissee  un servizio di monitoraggio. Lancia e reinizializza il
  servizio se  terminato. Il programma svc ferma, mette in pausa, o
  reinizializza il servizio su richiesta dell'amministratore di sistema.
  Il programma svstat stampa una linea riportante gli stati. Vedete la
  pagina di manuale "man supervise".

  ssvvcc - controlla un supervisore di servizi.

  svc  modifica gli stati di un supervisore di servizi.  dir  la stessa
  directory usata per il sorvegliante.  Potete listare molte dir.  svc
  modificher gli stati di ogni servizio attivo.

  ssvvssttaatt - stampa gli stati di un supervisore di servizi.

  svstat stampa gli stati di un supervisore di servizi.  dir  la stessa
  directory usata per il supervisore.  Potete listare molte dir.
  svstat stamper gli stati di ogni servizio attivo.

  ccyycclloogg scrive un log sul disco. Sincronizza automaticamente il log
  ogni 100kB (valore predefinito) per garantire l'integrit dei dati
  dopo un arresto.  Automaticamente mantiene i log a 1MB (valore
  predefinito). Se il disco  pieno, cyclog si mette in pausa e riprova
  nuovamente, senza perdere alcun dato.  Vedete la pagina di manuale
  "man cyclog".

  aaccccuussttaammpp aggiunge l'orario ad ogni riga di ingresso. Il tempo  un
  tempo numerico TAI con precisione al microsecondo. Il programma
  tailocal converte il tempo TAI in tempo locale. Vedere "man
  accustamp".

  uussuuaallllyy esamina un file di log e copia su stderr le righe che non
  corrispondono ad un modello. Il programma errorsto redirige stderr in
  un file. Vedete "man usually".

  sseettuusseerr esegue un programma con uid e gid dell'utente specificato.
  Diversamente da su, setuser non prende privilegi, non verifica le
  password e pu essere eseguito solamente da root. Vedete "man
  setuser".

  88..44..  SSttrruummeennttii ddii mmoonniittoorraaggggiioo ccoommmmeerrcciiaallii

  Sono disponibili strumenti commerciali per il monitoraggio. Provate:

    BMC Patrol per Unix/Databases  <http://www.bmc.com>


    TIBCO corp's  Hawk per monitoraggio Unix  <http://www.tibco.com>


    Ente LandMark



    Ente Platinum

  Il sito principale per Linux  all'URL  <http://www.aldev.8m.com> Siti
  mirror si trovano ai seguenti indirizzi: <http://aldev0.webjump.com>,
  angelfire <http://www.angelfire.com/country/aldev0>, geocities
  <http://www.geocities.com/alavoor/index.html>, virtualave
  <http://aldev0.virtualave.net>, 50megs <http://aldev0.50megs.com>,
  theglobe <http://members.theglobe.com/aldev1/index.html>, NBCi
  <http://members.nbci.com/alavoor>, Terrashare
  <http://aldev.terrashare.com>, Fortunecity
  <http://members.fortunecity.com/aldev>, Freewebsites
  <http://aldev.freewebsites.com>, Tripod
  <http://members.tripod.lycos.com/aldev>, Spree
  <http://members.spree.com/technology/aldev>, Escalix
  <http://www.escalix.com/freepage/aldev>, Httpcity
  <http://www.httpcity.com/aldev/index.html>, Freeservers
  <http://aldev.freeservers.com>.

  99..  AAllttrrii ffoorrmmaattii ddii qquueessttoo ddooccuummeennttoo

  Questo documento  pubblicato in 14 differenti formati, precisamente:
  DVI, PostScript, Latex, Adobe Acrobat PDF, LyX, GNU-info, HTML,
  RTF(Rich Text Format), puro testo, pagine man di Unix, singolo file
  HTML, SGML (formato linuxdoc), SGML (formato Docbook), formato help di
  MS WinHelp.

  Questo documento si trova anche sul sito

    <http://www.linuxdoc.org>: fate clic su HOWTO e cercate il nome di
     questo howto utilizzando CTRL+f o ALT+f all'interno della pagina
     web.

  Potete anche cercare questo documento sui seguenti siti mirror:

    <http://www.caldera.com/LDP/HOWTO>

    <http://www.linux.ucla.edu/LDP>

    <http://www.cc.gatech.edu/linux/LDP>

    <http://www.redhat.com/mirrors/LDP>

    Potete trovare i siti mirror pi vicini a voi (sulla base
     dell'indirizzo) collegandovi all'URL
     <http://www.linuxdoc.org/mirrors.html>: scegliete un sito ed andate
     alla directory /LDP/HOWTO/xxxxx-HOWTO.html



    Potete prendere questo documento HOWTO come un singolo file tar nel
     formato HTML, DVI, PostScript o SGML da
     <ftp://www.linuxdoc.org/pub/Linux/docs/HOWTO/other-formats/> e
     <http://www.linuxdoc.org/docs.html#howto>


    Puro testo su  <ftp://www.linuxdoc.org/pub/Linux/docs/\HOWTO> e
     <http://www.linuxdoc.org/docs.html#howto>


    Il singolo file in formato HTML  su
     <http://www.linuxdoc.org/docs.html#howto>

     Il singolo file HTML pu essere creato con il seguente comando
     (vedete "man sgml2html"): sgml2html -split 0   xxxxhowto.sgml


    La traduzione in altri linguaggi come francese, tedesco, spagnolo,
     cinese, giapponese sono su
     <ftp://www.linuxdoc.org/pub/Linux/docs/HOWTO> e
     <http://www.linuxdoc.org/docs.html#howto>.  Qualsiasi aiuto per
     tradurre in altre lingue  ben accetto.

     Il documento  stato scritto utilizzando uno strumento chiamato
     "SGML-Tools" che pu essere prelevato da
     <http://www.sgmltools.org>.  Compilando il sorgente, troverete i
     seguenti comandi:

    sgml2html xxxxhowto.sgml     (per generare file HTML)

    sgml2html -split 0   xxxxhowto.sgml (per generare una singola
     pagina HTML)

    sgml2rtf  xxxxhowto.sgml     (per generare file RTF)

    sgml2latex xxxxhowto.sgml    (per generare file LaTeX)

  99..11..  FFoorrmmaattoo AAccrroobbaatt PPDDFF

  Il file PDF pu essere generato dal file PostScript utilizzando
  Acrobat ddiissttiillll oppure GGhhoossttssccrriipptt.  Il file PostScript  stato
  generato dal DVI il quale  stato generato dal file LaTeX.  Potete
  scaricare il software da  <http://www.adobe.com>. Segue una sessione
  di esempio:

  ______________________________________________________________________
  bash$ man sgml2latex
  bash$ sgml2latex filename.sgml
  bash$ man dvips
  bash$ dvips -o filename.ps filename.dvi
  bash$ distill filename.ps
  bash$ man ghostscript
  bash$ man ps2pdf
  bash$ ps2pdf input.ps output.pdf
  bash$ acroread output.pdf &
  ______________________________________________________________________


  Oppure potete usare il comando Ghostscript ppss22ppddff.  ps2pdf  un buon
  sostituto di Adobe Acrobat Distiller per quasi tutte le funzionalit
  del prodotto Adobe Acrobat Distiller: converte file PostScript in file
  Portable Document Format (PDF).  ppss22ppddff  implementato come un comando
  di script molto piccolo (file batch) che invoca Ghostscript, selezio
  nando uno speciale "dispositivo di uscita" chiamato ppddffwwrriittee.  Allo
  scopo di utilizzare ps2pdf, il dispositivo pdfwrite deve essere
  incluso nel makefile quando Ghostscript viene compilato; per i det
  tagli vedete la documentazione sulla compilazione di Ghostscript.

  99..22..  CCoonnvveerrttiirree LLiinnuuxxddoocc nneell ffoorrmmaattoo DDooccbbooookk

  Questo documento  stato scritto nel formato SGML linuxdoc. Il formato
  SGML Docbook ha pi caratteristiche del formato linuxdoc, mentre il
  formato linuxdoc  molto pi semplice da usare. Per convertire il file
  in formato linuxdoc SGML nel formato Docbook utilizzate il programma
  lldd22ddbb..sshh e alcuni script in Perl. L'uscita di ld2db non  al 100%
  chiara ed  necessario usare lo script perl cclleeaann__lldd22ddbb..ppll. Potete
  aver bisogno di modificare manualmente poche linee nel documento.

    Scaricate il programma ld2db da
     <http://www.dcs.gla.ac.uk/~rrt/docbook.html> o da Al Dev site
     <http://www.aldev.8m.com/cppsrc.html>


    Scaricate lo script Perl cleanup_ld2db.pl da Al Dev site
     <http://www.aldev.8m.com/cppsrc.html>

     L'ld2db.sh non  chiaro al 100%, dar molti errori quando lo
     eseguirete

     ___________________________________________________________________
             bash$ ld2db.sh file-linuxdoc.sgml db.sgml
             bash$ cleanup.pl db.sgml > db_clean.sgml
             bash$ gvim db_clean.sgml
             bash$ docbook2html db.sgml
     ___________________________________________________________________


  Potrete avere la necessit di correggere manualmente alcuni degli
  errori meno gravi dopo l'esecuzione dello script Perl. Per esempio
  potete aver bisogno di chiudere dei tag < /Para> per ogni < Listitem>

  99..33..  CCoonnvveerrttiirree nneell ffoorrmmaattoo MMSS WWiinnHHeellpp

  Potete convertire il documento HOWTO dal formato SGML al file
  Microsoft Windows Help. Prima convertite il SGML in HTML usando:

  ______________________________________________________________________
          bash$ sgml2html xxxxhowto.sgml     (per generare pi file HTML)
          bash$ sgml2html -split 0   xxxxhowto.sgml (per generare un singolo file HTML)
  ______________________________________________________________________


  Poi utilizzate lo strumento HtmlToHlp <http://javadocs.plan
  etmi\rror.com/htmltohlpe.html>.  Potete anche usare sgml2rtf e poi
  usare i file RTF per generare i file WinHelp.

  99..44..  LLeeggggeerree ddiivveerrssii ffoorrmmaattii

  Per vedere il documento nel formato DVI, usate il programma xdvi. Il
  programma xdvi  situato nel pacchetto tetex-xdvi*.rpm di Linux Redhat
  il quale pu essere localizzato attraverso i pulsanti di men
  ControlPanel | Applications | Publishing | TeX.  Per leggere il
  documento DVI date il comando:


               xdvi -geometry 80x90 howto.dvi
               man xdvi



  e ridimensionate la finestra con il mouse. Per navigare usate i tasti
  freccia, i tasti Pagina Su, Pagina Gi, potete usare anche i tasti
  lettera 'f', 'd', 'u', 'c', 'l', 'r', 'p', 'n' per muovervi su, gi,
  in centro, pagina seguente, pagina precedente, ecc. Per chiudere il
  men premere 'x'.

  Potete leggere il file PostScript usando il programma 'gv' (ghostview)
  o 'ghostscript'. Il programma ghostscript  nel pacchetto gv*.rpm in
  Redhat Linux il quale pu essere localizzato attraverso i pulsanti del
  men ControlPanel | Applications | Graphics. Il programma gv  molto
  pi intuitivo di ghostscript. Ghostscript e gv sono anche disponibili
  per altre piattaforme come OS/2, Windows 95 e NT: potete leggere
  questo documento perfino su queste piattaforme.


    Trovate ghostscript per Windows 95, OS/2 e per altri sistemi su
     <http://www.cs.wisc.edu/~ghost>.

  Per leggere il documento PostScript date il comando:


                       gv howto.ps
                       ghostscript howto.ps



  Potete leggere il documento in formato HTML usando Netscape Navigator,
  Microsoft Internet Explorer, Redhat Baron Web browser o uno qualsiasi
  degli altri dieci browser web.

  Potete leggere l'output LaTeX, utilizzando LyX, un'interfaccia X
  Window per LaTeX.

  1100..  CCooppyyrriigghhtt NNoottiiccee

  Copyright policy is GNU/GPL as per LDP (Linux Documentation project).
  LDP is a GNU/GPL project.  Additional restrictions are - you must
  retain the author's name, email address and this copyright notice on
  all the copies. If you make any changes or additions to this document
  than you should intimate all the authors of this document.



