Logo Search packages:      
Sourcecode: ledcontrol version File versions  Download package

startup.c

/*
 * This file includes stuff related to handling the startup scripts and
 * process stuff.
 *
 * Previously this file contained configuration-related stuff, that has
 * been moved to config.c.
 */

#include "ledd.h"

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <errno.h>
#include <dirent.h>

#define STRWRITE(fd,str) write(fd,str,strlen(str))

static void startup_exec(options *opts,File *startup);

/*
 * Starts all the startup scripts.
 */
void startup_exec_all(options *opts) {
      GSList *list;
      File *startup;

      for (list=opts->startup; list; list=g_slist_next(list)) {
            if (list->data==NULL)
                  continue;
            startup=list->data;
            if (startup->name==NULL ||
                startup->name[0]==0)
                  continue;

            startup_exec(opts,startup);
      }
      return;
}


/*
 * Starts one startup script. Creates two pipes, connects them to stdout
 * and stderr of the child and sets them to the correct places in opts.
 */
static void startup_exec(options *opts,File *startup) {
      int out[2],err[2];
      File *p;
      pid_t pid;

      pipe_create(out);  /* Like pipe() but sets options. */
      pipe_create(err);

      pid=fork();
      if (pid<0) {
            /* Fork failed */
            G_ERROR("fork failed (%s)",STRERROR);
      }
      if (pid==0) {
            /* The child */
            char *argv[4];
            GSList *list;
            int sout,serr;
            
            /* Close all unnecessary filehandles. */
            close(out[0]);
            close(err[0]);
            for (list=opts->pipes; list; list=g_slist_next(list)) {
                  if (list->data==NULL ||
                      ((File *)list->data)->fd <0)
                        continue;
                  close(((File *)list->data)->fd);
            }
            
            /* How do I set out[1] and err[1] to 1 and 2 safely? */
            sout=out[1];
            while (sout<3 && sout!=-1)
                  sout=dup(sout);
            serr=err[1];
            while (serr<3 && sout!=-1)
                  serr=dup(serr);

            if (serr==-1 || sout==-1) {
                  STRWRITE(err[1],"unable to set stdout/stderr\n");
                  exit(127);
            }
            
            if (dup2(sout,1)<0) {   /* Set STDOUT */
                  STRWRITE(err[1],"unable to set stdout/stderr\n");
                  exit(127);
            }
            if (dup2(serr,2)<0) {   /* Set STDERR */
                  STRWRITE(err[1],"unable to set stdout/stderr\n");
                  exit(127);
            }
            
            /* EXEC */
            argv[0]="sh";
            argv[1]="-c";
            argv[2]=startup->name;
            argv[3]=0;

            execv("/bin/sh",argv);

            /* We can now use stderr for errors. */
            perror(startup->name);
            exit(127);
      }
      
      /* We're the parent */
      
      close(out[1]);
      close(err[1]);
      
      /* Startup struct */
      startup->pid=pid;
      startup->fd=err[0];
      
      /* Pipe struct */
      p=g_new0(File,1);
      p->name=g_strdup(startup->name);
      p->type=TYPE_SCRIPT;
      p->fd=out[0];
      g_slist_append(opts->pipes,p);

      return;
}


/*
 * Checks child existance and whether it has some errors to say.
 * Always returns TRUE (continue looping).
 */
gboolean startup_check_all(options *opts) {
      gchar *str;
      GSList *list;
      File *startup;
      int i;
      int status;

      for (list=opts->startup; list; list=g_slist_next(list)) {
            if (list->data==NULL)
                  continue;
            startup=list->data;
            if (startup->fd < 0 || startup->pid <= 0)
                  continue;

            i=waitpid(startup->pid,&status,WNOHANG|WUNTRACED);
            if (i<0) {
                  g_warning("waitpid(%d) of %s failed (%s)",
                          startup->pid,g_basename(startup->name),
                          STRERROR);
                  continue;
            }
            if (i>0) {
                  /* Child has exited, may it rest in peace. */
                  while (1) {
                        str=pipe_gets(startup->fd);
                        if (str[0]==0)
                              break;
                        g_warning("%s: %s",g_basename(startup->name),
                                str);
                        g_free(str);
                  }
                  g_free(str);

                  /* Log it. */
                  if (WIFEXITED(status))
                        g_message("%s: exited with status %d",
                                g_basename(startup->name),
                                WEXITSTATUS(status));
                  else if (WIFSIGNALED(status))
                        g_message("%s: exited on signal %d (%s)",
                                g_basename(startup->name),
                                WTERMSIG(status),
                                g_strsignal(WTERMSIG(status)));
                  else if (WIFSTOPPED(status))
                        continue;
                  else
                        g_warning("%s: exited for unknown reason"
                                " - bug?",g_basename(startup->name));
                        
                        
                  startup->pid = 0;
                  close(startup->fd);
                  /* TODO: stdout pipe is left open. */
                  startup->fd = -1;
                  continue;
            }

            str=pipe_gets(startup->fd);
            if (str[0])
                  g_warning("%s: %s",g_basename(startup->name),str);
            g_free(str);
      }
      return TRUE;
}


/*
 * Forks, parent closes, closes fd's 0-1 and reopens them to /dev/null,
 * sets signal ignorance of SIGTTOU, SIGTTIN and SIGTTSP.
 * Almost directly copied from pidentd-3.0.4.
 */
void startup_fork(void) {
      pid_t pid;
      int i,fd;

      pid=fork();
      if (pid<0) {
            G_ERROR("fork failed: %s",STRERROR);
      } else if (pid>0) {
            exit(0);   /* Parent exists successfully. */
      }

      /* I am the child. */

#ifdef SIGTTOU
      signal(SIGTTOU, SIG_IGN);
#endif
#ifdef SIGTTIN
      signal(SIGTTIN, SIG_IGN);
#endif
#ifdef SIGTTSP
      signal(SIGTTSP, SIG_IGN);
#endif
      setsid();   /* Not exactly sure what this does, but... */

      /* stdin, stdout to /dev/null */
      /* don't redirect stderr, as that might be used for logging.
       * also glib errors probably pop up there.... */
      for (i=0; i<=1; i++) {
            close(i);
            fd=open("/dev/null",O_RDWR);
            if (fd!=i) {
                  dup2(fd,i);
                  close(fd);
            }
      }
      return;
}


/*
 * Check for existing pidfile and whether such a process exists.
 * Returns pid of process or 0 on no such process. If pidfile exists, but
 * cannot be read, returns 1.
 */
gint startup_pidfile_check(options *opts) {
      gint pid;
      FILE *fp;
      gchar name[32];
      DIR *dp;

      if (parse_eol(opts->pidfile->name))
            return 0;

      if ((fp=fopen(opts->pidfile->name,"rt"))==NULL) {
            if (errno==ENOENT)   /* Doesn't exists. */
                  return 0;
            g_warning("couldn't read %s (%s)",opts->pidfile->name,
                    STRERROR);
            return 1;
      }

      if (fscanf(fp," %d\n",&pid)<1) {   /* Bad file, OK to continue. */
            fclose(fp);
            return 0;
      }

      fclose(fp);

      /* TODO: Any nicer way to find out whether a process is running??? */

      snprintf(name,32,"/proc/%d",pid);
      dp=opendir(name);
      if (dp==NULL && errno==ENOENT)  /* No such entry. */
            return 0;
      if (dp)
            closedir(dp);

      /* TODO: We trust it's ledd. */
      return pid;
}


/*
 * Writes a pidfile for the current process.
 */
void startup_pidfile_write(options *opts) {
      pid_t pid;
      FILE *fp;

      if (parse_eol(opts->pidfile->name))
            return;

      pid=getpid();
      umask(S_IWGRP|S_IWOTH);
      if ((fp=fopen(opts->pidfile->name,"wt"))==NULL) {
            G_ERROR("couldn't write pidfile %s (%s)",
                  opts->pidfile->name,STRERROR);
      }
      fprintf(fp,"%d\n",pid);
      fclose(fp);
      return;
}


/*
 * Removes the pidfile. First checks that the pid in it is correct.
 */
void startup_pidfile_remove(options *opts) {
      gint p;
      pid_t pid;

      if (parse_eol(opts->pidfile->name))
            return;
      pid=getpid();
      p=startup_pidfile_check(opts);
      if (p!=0 && pid!=p) {
            g_warning("wrong PID (%d != %d) in pidfile, not removing",
                    p,pid);
            return;
      }
      unlink(opts->pidfile->name);
      return;
}


Generated by  Doxygen 1.6.0   Back to index