Back to index

courier  0.68.2
courierfilter.c
Go to the documentation of this file.
00001 /*
00002 ** Copyright 1998 - 2004 Double Precision, Inc.
00003 ** See COPYING for distribution information.
00004 */
00005 #include      "config.h"
00006 #include      "courier.h"
00007 #include      "mydirent.h"
00008 #include      "liblock/config.h"
00009 #include      "liblock/liblock.h"
00010 #include      "numlib/numlib.h"
00011 #include      "filtersocketdir.h"
00012 #include      "filteractivedir.h"
00013 #include      "pidfile.h"
00014 
00015 #include      <stdio.h>
00016 #include      <stdlib.h>
00017 #include      <string.h>
00018 #include      <signal.h>
00019 #if    HAVE_FCNTL_H
00020 #include      <fcntl.h>
00021 #endif
00022 #if    HAVE_UNISTD_H
00023 #include      <unistd.h>
00024 #endif
00025 #include      <sys/types.h>
00026 #if    HAVE_SYS_WAIT_H
00027 #include      <sys/wait.h>
00028 #endif
00029 
00030 
00031 #define       LOCKFILE      FILTERSOCKETDIR "/.lock"
00032 
00033 static void stop()
00034 {
00035 FILE   *f;
00036 unsigned long pid;
00037 int    fd;
00038 
00039        if ((f=fopen(PIDFILE, "r")) == 0)  return;
00040        if (fscanf(f, "%lu", &pid) <= 0)
00041        {
00042               fclose(f);
00043               return;
00044        }
00045        fclose(f);
00046 
00047        kill (pid, SIGTERM);
00048 
00049        /*
00050        ** Wait until the daemon goes away by attempting to lock the lock file
00051        ** ourselves.
00052        */
00053 
00054        alarm(15);
00055        fd=open(LOCKFILE, O_RDWR);
00056        if (fd < 0)   return;
00057        ll_lock_ex(fd);
00058        close(fd);
00059 }
00060 
00061 static struct filterinfo {
00062        struct filterinfo *next;
00063        char *filtername;
00064        int    fd0;
00065        pid_t  p;
00066        } *filterlist=0;
00067 
00068 static int sighup_received=0;
00069 static int sigterm_received=0;
00070 
00071 static void kill1filter(struct filterinfo **p)
00072 {
00073 int    waitstat;
00074 pid_t  pid;
00075 struct filterinfo *pp= *p;
00076 
00077        *p=pp->next;
00078 
00079        clog_msg_start_info();
00080        clog_msg_str("Stopping ");
00081        clog_msg_str(pp->filtername);
00082        clog_msg_send();
00083        close(pp->fd0);
00084        while ((pid=wait(&waitstat)) >= 0 && pid != pp->p)
00085               ;
00086        free(pp->filtername);
00087        free(pp);
00088 }
00089 
00090 static char *activename(const char *p)
00091 {
00092 char   *s=malloc(sizeof(FILTERACTIVEDIR "/")+strlen(p));
00093 
00094        if (!s)
00095        {
00096               perror("malloc");
00097               exit(1);
00098        }
00099        return (strcat(strcpy(s, FILTERACTIVEDIR "/"), p));
00100 }
00101 
00102 /*
00103 **     sighup attempts to synchronize everything.
00104 */
00105 static void sighup()
00106 {
00107 struct filterinfo **pp;
00108 DIR    *dirp;
00109 struct dirent *de;
00110 
00111        /* Start any new filters */
00112 
00113        dirp=opendir(FILTERACTIVEDIR);
00114        while (dirp && (de=readdir(dirp)) != 0)
00115        {
00116        char   *n;
00117        int    pipefd[2];
00118        struct filterinfo *newp;
00119 
00120               if (de->d_name[0] == '.')
00121                      continue;
00122 
00123               for (pp= &filterlist; *pp; pp= &(*pp)->next)
00124                      if (strcmp( (*pp)->filtername, de->d_name) == 0)
00125                             break;
00126 
00127               if (*pp)      continue;
00128 
00129               n=malloc(sizeof(FILTERACTIVEDIR "/")+strlen(de->d_name));
00130               if (!n)
00131               {
00132                      perror("malloc");
00133                      exit(1);
00134               }
00135               strcat(strcpy(n, FILTERACTIVEDIR "/"), de->d_name);
00136 
00137               if ( (newp=(struct filterinfo *)
00138                             malloc(sizeof(struct filterinfo))) == 0 ||
00139                      (newp->filtername=malloc(strlen(de->d_name)+1)) == 0)
00140               {
00141                      perror("malloc");
00142                      exit(1);
00143               }
00144               strcpy(newp->filtername, de->d_name);
00145               newp->next=filterlist;
00146               filterlist=newp;
00147 
00148               if (pipe(pipefd) < 0)
00149               {
00150                      perror("pipe");
00151                      exit(1);
00152               }
00153 
00154               newp->fd0=pipefd[1];
00155 
00156               while ((newp->p=fork()) < 0)
00157               {
00158                      perror("fork");
00159                      sleep(3);
00160               }
00161 
00162               if ( newp->p == 0)
00163               {
00164                      dup2(pipefd[0], 0);
00165                      close(pipefd[0]);
00166                      close(pipefd[1]);
00167                      execl(n, de->d_name, (char *)0);
00168                      perror("exec");
00169                      exit(1);
00170               }
00171               free(n);
00172               close(pipefd[0]);
00173               if (fcntl(newp->fd0, F_SETFD, FD_CLOEXEC) < 0)
00174               {
00175                      perror("fcntl");
00176                      exit(1);
00177               }
00178               clog_msg_start_info();
00179               clog_msg_str("Starting ");
00180               clog_msg_str(newp->filtername);
00181               clog_msg_send();
00182        }
00183        if (dirp)     closedir(dirp);
00184 
00185        /* Then, kill any filters that should not be running any more */
00186 
00187        for (pp= &filterlist; *pp; )
00188        {
00189        char   *n=activename( (*pp)->filtername );
00190 
00191               if (access(n, 0) == 0)
00192               {
00193                      free(n);
00194                      pp= &(*pp)->next;
00195                      continue;
00196               }
00197               free(n);
00198               kill1filter(pp);
00199        }
00200 
00201        /* Finally, remove any sockets for filters that don't run any more */
00202 
00203   {
00204   static const char *dirs[2]={FILTERSOCKETDIR, ALLFILTERSOCKETDIR};
00205   int i;
00206 
00207     for (i=0; i<2; i++)
00208     {
00209        dirp=opendir(dirs[i]);
00210        while (dirp && (de=readdir(dirp)) != 0)
00211        {
00212        char   *n;
00213 
00214               if (de->d_name[0] == '.')
00215                      continue;
00216 
00217               for (pp= &filterlist; *pp; pp= &(*pp)->next)
00218                      if (strcmp( (*pp)->filtername, de->d_name) == 0)
00219                             break;
00220 
00221               if (*pp)      continue;
00222 
00223               n=malloc(strlen(dirs[i])+strlen(de->d_name)+2);
00224               if (!n)
00225               {
00226                      perror("malloc");
00227                      exit(1);
00228               }
00229               strcat(strcat(strcpy(n, dirs[i]), "/"), de->d_name);
00230               unlink(n);
00231               free(n);
00232        }
00233        if (dirp)     closedir(dirp);
00234     }
00235   }
00236 }
00237 
00238 static RETSIGTYPE sighuphandler(int signum)
00239 {
00240        sighup_received=1;
00241 #if     RETSIGTYPE != void
00242         return (0);
00243 #endif
00244 }
00245 
00246 static RETSIGTYPE sigtermhandler(int signum)
00247 {
00248        sigterm_received=1;
00249 #if     RETSIGTYPE != void
00250         return (0);
00251 #endif
00252 }
00253 
00254 /*
00255 **     Tricky synchronization when we start courierfilter.  Basically, we
00256 **     don't want to exit this process until all filters are initialized.
00257 **
00258 **     Here's what we do.  All filters will manually close file descritor
00259 **     3 when they're ready.  Therefore, we set up a pipe whose write end
00260 **     is set to file descriptor 3, and is inherited via fork and exec
00261 **     by every filter.  We do a read on the pipe, which will complete
00262 **     when all copies of the write side of the pipe are closed.
00263 **
00264 */
00265 static int reserve3(int* pipe3fd)
00266 {
00267        int devnull = -1, dupped = -1;
00268        pipe3fd[0] = pipe3fd[1] = -1;
00269        close(3);
00270        if (open("/dev/null", O_RDONLY) != 3
00271            || pipe(pipe3fd) < 0
00272            || close(3)
00273            || (dupped = dup(pipe3fd[1])) != 3)
00274        {
00275               fprintf(stderr, "Unable to reserve file descriptor 3.\n");
00276               close(devnull);
00277               close(dupped);
00278               close(pipe3fd[0]);
00279               close(pipe3fd[1]);
00280               return (1);
00281        }
00282        close(pipe3fd[1]);
00283        return (0);
00284 }
00285 
00286 static int start()
00287 {
00288        int    lockfd;
00289        int    pipe3fd[2];
00290        char   temp_buf;
00291 
00292        lockfd=ll_daemon_start(LOCKFILE);
00293        if (lockfd <= 0)
00294        {
00295               close(3);
00296               return (lockfd);
00297        }
00298 
00299        if (lockfd == 3)
00300        {
00301               lockfd=dup(lockfd);  /* Get it out of the way */
00302               if (lockfd < 0)
00303               {
00304                      perror("dup");
00305                      return (-1);
00306               }
00307        }
00308        if (reserve3(pipe3fd))
00309        {
00310               return (1);
00311        }
00312 
00313        fcntl(lockfd, F_SETFD, FD_CLOEXEC);
00314        fcntl(pipe3fd[0], F_SETFD, FD_CLOEXEC);
00315 
00316        fcntl(3, F_SETFD, FD_CLOEXEC);     /* For the logger */
00317        clog_start_logger("courierfilter");
00318        clog_open_stderr(0);
00319        fcntl(3, F_SETFD, 0);
00320 
00321        signal(SIGPIPE, SIG_IGN);
00322        dup2(2, 1);
00323        sighup();
00324        close(0);
00325        open("/dev/null", O_RDONLY);
00326 
00327        close(3);
00328        if (read(pipe3fd[0], &temp_buf, 1) != 1)
00329               ; /* ignore */
00330        close(pipe3fd[0]);
00331 
00332        signal(SIGHUP, sighuphandler);
00333        signal(SIGTERM, sigtermhandler);
00334 
00335        ll_daemon_started(PIDFILE, lockfd);
00336 
00337        while (!sigterm_received)
00338        {
00339               if (sighup_received)
00340               {
00341                      sighup_received=0;
00342                      if (reserve3(pipe3fd) == 0)
00343                      {
00344                             sighup();
00345                             close(3);
00346                             close(pipe3fd[0]);
00347                      }
00348                      signal(SIGHUP, sighuphandler);
00349               }
00350 #if    HAVE_SIGSUSPEND
00351 
00352        {
00353        sigset_t      ss;
00354 
00355               sigemptyset(&ss);
00356               sigsuspend(&ss);
00357        }
00358 #else
00359               sigpause(0);
00360 #endif
00361        }
00362        while (filterlist)
00363               kill1filter(&filterlist);
00364        unlink(PIDFILE);
00365        return (0);
00366 }
00367 
00368 static int restart()
00369 {
00370        return (ll_daemon_restart(LOCKFILE, PIDFILE));
00371 }
00372 
00373 int main(int argc, char **argv)
00374 {
00375        if (getuid() == 0)
00376               libmail_changeuidgid(MAILUID, MAILGID);
00377                      /* Drop root privileges */
00378        if (chdir(courierdir()) < 0)
00379        {
00380               perror("courierdir");
00381               return (1);
00382        }
00383 
00384        if (argc > 1)
00385        {
00386               if (strcmp(argv[1], "stop") == 0)
00387               {
00388                      stop();
00389                      return (0);
00390               }
00391               if (strcmp(argv[1], "start") == 0)
00392               {
00393                      return (start());
00394               }
00395               if (strcmp(argv[1], "restart") == 0)
00396               {
00397                      return (restart());
00398               }
00399        }
00400        fprintf(stderr, "Usage: %s (stop|restart|start)\n", argv[0]);
00401        return (1);
00402 }