## modules/pa/spawn.c

/* [<][>][^][v][top][bottom][index][help] */

## FUNCTIONS

This source file includes following functions.

1. spawn_job
2. nfslock
3. nfsunlock

       1 /*  spawn.c
       2
       3     Source file for spawn operations for  PGPsendmail  (wrapper to sendmail).
       4
       5     Copyright (C) 1994-1998  Richard Gooch
       6
       7     This program is free software; you can redistribute it and/or modify
       8     it under the terms of the GNU General Public License as published by
       9     the Free Software Foundation; either version 2 of the License, or
      10     (at your option) any later version.
      11
      12     This program is distributed in the hope that it will be useful,
      13     but WITHOUT ANY WARRANTY; without even the implied warranty of
      14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15     GNU General Public License for more details.
      16
      17     You should have received a copy of the GNU General Public License
      18     along with this program; if not, write to the Free Software
      19     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
      20
      21     Richard Gooch may be reached by email at  rgooch@atnf.csiro.au
      22     The postal address is:
      23       Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia.
      24 */
      25
      26 /*  This programme intercepts messages sent by user mail agents to the
      27     sendmail daemon and checks to see if messages can be encrypted using the
      28     recipient's PGP public keys.
      29
      30
      31     Written by      Richard Gooch   31-MAY-1994
      32
      33     Updated by      Richard Gooch   31-MAY-1994: Extracted from  pgpsendmail.c
      34
      35     Updated by      Richard Gooch   18-JUN-1994: Made error messages more
      36   explicit.
      37
      38     Updated by      Richard Gooch   27-JUN-1994: Copied  set_env  from
      39   pgpdaemon.c
      40
      41     Updated by      Richard Gooch   5-JUL-1994: Changed to use of  m_copy  .
      42
      43     Updated by      Richard Gooch   14-JUL-1994: Moved  copy_data  and  set_env
      44   to  misc.c
      45
      46     Updated by      Richard Gooch   3-DEC-1994: Fixed bug for externally set
      47   error descriptor.
      48
      49     Updated by      Richard Gooch   25-SEP-1997: Used new ERRSTRING macro.
      50
      51     Last updated by Richard Gooch   10-JUL-1998: Removed definitions of system
      52   errlist array.
      53
      54
      55 */
      56 #include <stdio.h>
      57 #include <stdlib.h>
      58 #include <errno.h>
      59 #include <sys/types.h>
      60 #include <unistd.h>
      61 #include <string.h>
      62 #include <sys/stat.h>
      63 #include <fcntl.h>
      64 #include <time.h>
      65
      66 /* #include "pgpsendmail.h" */
      67
      68 #define ERRSTRING strerror(errno)
      69
      70 #define LINE_LENGTH 1024
      71 #define STRING_LENGTH 255
      72
      73 enum NFSL {
      74   NFSL_SYSF,
      75   NFSL_SECV,
      76   NFSL_LOCKED,
      77   NFSL_OK,
      78   NFSL_STOLEN,
      79   NFSL_LOST
      80 };
      81
      82
      83 int sd1[2] /*, sd2[2] */;  /* sd2 is used to have a collaborative
      84                                   dialogue between processes          */
      85 int spawn_job (char *path, char *argv[], int *in_fd, int *out_fd, int *err_fd)
         /* [<][>][^][v][top][bottom][index][help] */
      86 /*  This routine will fork(2) and execvp(2) a process.
      87     The file to execute must be pointed to by  path  .
      88     The NULL terminated list of arguments which will be passed to  main  must
      89     be pointed to by  argv  .
      90     The input file descriptor (fd = 0) for the process must be pointed to by
      91     in_fd  .If the value here is less than 0, then a pipe to the process is
      92     opened and the writeable end is written to the storage pointed to by  in_fd
      93     The standard output file descriptor (fd = 1) for the process must be
      94     pointed to by  out_fd  .If the value here is less than 0, then a pipe to
      95     the process is opened and the readable end is written to the storage
      96     pointed to by  out_fd  .
      97     The standard error output file descriptor (fd = 2) for the process must be
      98     pointed to by  err_fd  .If the value here is less than 0, then a pipe to
      99     the process is opened and the readable end is written to the storage
     100     pointed to by  err_fd  .
     101     The routine returns the child process ID on success, else it returns -1.
     102 */
     103 {
     104     int child_pid;
     105     /*     char txt[LINE_LENGTH];  */
     106
     107     if (pipe(sd1) == -1)
     108       {
     109         perror("pipe failed");
     110         return(1);
     111       }
     112
     113     /*  Fork and exec  */
     114     switch ( child_pid = fork () )
     115     {
     116       case 0:
     117         /*  Child: exec  */
     118         close(sd1[0]);
     119
     120         dup2( sd1[1], 1 );   /* stdout */
     121
     122         /*      fprintf (stderr, "dup2 1 result: %s\n", ERRSTRING); */
     123         dup2( sd1[1], 2 );    /* stderr */
     124         /*      fprintf (stderr, "dup2 2 result: %s\n", ERRSTRING); */
     125
     126         execvp (path, argv);
     127
     128         fprintf (stderr, "Could not exec: \"%s\"\t%s\n", path, ERRSTRING);
     129         exit (1);
     130         break;
     131       case -1:
     132         /*  Error  */
     133         fprintf (stderr, "Could not fork\t%s\n", ERRSTRING);
     134         return (-1);
     135         break;
     136       default:
     137         /*  Parent  */
     138         break;
     139     }
     140     /*  Parent only  */
     141
     142     close(sd1[1]);
     143
     144     dup2 (sd1[0], 0);
     145
     146     /*     fprintf (stderr, "dup2 0 result: %s\n", ERRSTRING);  */
     147     /*     fprintf(stderr, "Reading child output\n");
     148     while (read(0, txt, 1000) != 0)
     149       fprintf(stderr, "child read %s\n", txt);
     150
     151       fprintf(stderr, "Finished reading child output\n");   */
     152
     153     return (child_pid);
     154 }   /*  End Function spawn_job  */
     155
     156
     157 #define DEFAULT_LOCKTIME 300;
     158
     159 time_t nfslock(char *path, char *namelock, int max_age, int notify)
         /* [<][>][^][v][top][bottom][index][help] */
     160 {
     161   int tries = 0, oldlck = 0, tmpfd;
     162   struct stat old_stat, our_tmp;
     163   char tmp[32];
     164   char *tpath, *fullpath;
     165
     166   srandom(getpid()+time(0));
     167   max_age = max_age ? max_age : DEFAULT_LOCKTIME;
     168
     169   /*
     170    * 1. create a tmp file with a psuedo random file name. we also make
     171    *    tpath which is a buffer to store the full pathname of the tmp file.
     172    */
     173
     174   sprintf(tmp,"slock%d.%d", (int)getpid(), (int)random());
     175
     176   tpath = malloc(strlen(path) + 1 + strlen(tmp) + 1);
     177   if (tpath == NULL) return(NFSL_SYSF);
     178   sprintf(tpath,"%s/%s", path, tmp);
     179
     180   tmpfd = open(tpath, O_CREAT|O_WRONLY|O_EXCL, S_IRUSR|S_IWUSR);
     181
     182   if (tmpfd < 0) {      /* open failed */
     183     close(tmpfd);
     184     unlink(tpath);
     185     free(tpath);
     186     return(NFSL_SYSF);
     187   }
     188
     189   if (getuid()==0) {    /* we're root, so be careful! */
     190     if (fstat(tmpfd, &our_tmp) < 0) {   /* stat failed... shouldn't happen */
     191       close(tmpfd);
     192       unlink(tpath);
     193       free(tpath);
     194       return(NFSL_SYSF);
     195     }
     196     if (our_tmp.st_nlink != 1) {        /* someone's trying to mess with us */
     197       fprintf(stderr,"nfslock: bad link count on %s\n",tpath);
     198       close(tmpfd);
     199       unlink(tpath);
     200       free(tpath);
     201       return(NFSL_SECV);
     202     }
     203   }
     204
     205   /*
     206    * 2. make fullpath, a buffer for the full pathname of the lock file.
     207    *    then start looping trying to lock it
     208    */
     209
     210   fullpath = malloc(strlen(path) + 1 + strlen(namelock) + 1);
     211   if (fullpath == NULL) {
     212     close(tmpfd);
     213     unlink(tpath);
     214     free(tpath);
     215     return(NFSL_SYSF);
     216   }
     217   sprintf(fullpath,"%s/%s", path, namelock);
     218
     219   while (tries < 10) {
     220
     221     /*
     222      * 3. link tmp file to lock file.  if it goes, we win and we clean
     223      *    up and return the st_ctime of the lock file.
     224      */
     225
     226     if (link(tpath, fullpath) == 0) {
     227       unlink(tpath); /* got it! */
     228       free(tpath);
     229       close(tmpfd);
     230       if (lstat(fullpath, &our_tmp) < 0) {      /* stat failed... shouldn't happen */
     231         unlink(fullpath);
     232         free(fullpath);
     233         return (NFSL_SYSF);
     234       }
     235       free(fullpath);
     236       return(our_tmp.st_ctime);
     237     }
     238
     239     /*
     240      * 4. the lock failed.  check for a stale lock file, being mindful
     241      *    of NFS and the fact the time is set from the NFS server.  we
     242      *    do a write on the tmp file to update its time to the server's
     243      *    idea of "now."
     244      */
     245
     246     oldlck = lstat(fullpath, &old_stat);
     247
     248     if (write(tmpfd, "\0", 1) != 1 || fstat(tmpfd, &our_tmp) < 0)
     249       break; /* something bogus is going on */
     250
     251     if (oldlck != -1 && old_stat.st_ctime + max_age < our_tmp.st_ctime) {
     252       unlink(fullpath); /* break the stale lock */
     253       if (notify) fprintf(stderr,"Breaking stale lock on %s.\n",fullpath);
     254       tries++;
     255       sleep(1+(random() % 10)); /* It is CRITICAL that we sleep after breaking
     256                                  * the lock. Otherwise, we could race with
     257                                  * another process and unlink it's newly-
     258                                  * created file.
     259                                  */
     260       continue;
     261     }
     262
     263     /*
     264      * 5. try again
     265      */
     266
     267     if (notify) {
     268       if (tries==0) fprintf(stderr,"Waiting for lock on file %s.\n",fullpath);
     269       else fprintf(stderr,"Still waiting...\n");
     270     }
     271     tries++;
     272     sleep(1+(random() % 10));
     273   }
     274
     275   /*
     276    * 6. give up, failure.
     277    */
     278
     279   errno = EEXIST;
     280   unlink(tpath);
     281   free(tpath);
     282   free(fullpath);
     283   close(tmpfd);
     284   return(NFSL_LOCKED);
     285 }
     286
     287 int nfsunlock(char *path, char *namelock, int max_age, time_t birth)
         /* [<][>][^][v][top][bottom][index][help] */
     288 {
     289   int tmpfd;
     290   struct stat old_stat, our_tmp;
     291   char *tpath, *fullpath;
     292
     293   srandom(getpid()+time(0));
     294   max_age = max_age ? max_age : DEFAULT_LOCKTIME;
     295
     296   /*
     297    * 1. Build a temp file and stat that to get an idea of what the server
     298    *    thinks the current time is (our_tmp.st_ctime)..
     299    */
     300
     301   tpath = malloc(strlen(path) + 25);    /* small slop factor- 23 s/b enough */
     302   if (tpath == NULL) return(NFSL_SYSF);
     303   sprintf(tpath,"%s/slock%d.%d", path, (int)getpid(), (int)random());
     304
     305   tmpfd = open(tpath, O_CREAT|O_WRONLY|O_EXCL, S_IRUSR|S_IWUSR);
     306
     307   if ((tmpfd < 0) || (write(tmpfd, "\0", 1) != 1)
     308                   || (fstat(tmpfd, &our_tmp) < 0)) {
     309     /* The open failed, or we can't write the file, or we can't stat it */
     310     close(tmpfd);
     311     unlink(tpath);
     312     free(tpath);
     313     return(NFSL_SYSF);
     314   }
     315
     316   close(tmpfd);         /* We don't need this once we have our_tmp.st_ctime. */
     317   unlink(tpath);
     318   free(tpath);
     319
     320   /*
     321    * 2. make fullpath, a buffer for the full pathname of the lock file
     322    */
     323
     324   fullpath = malloc(strlen(path) + 1 + strlen(namelock) + 1);
     325   if (fullpath == NULL)
     326     return(NFSL_SYSF);
     327   sprintf(fullpath,"%s/%s", path, namelock);
     328
     329   /*
     330    * 3. If the ctime hasn't been modified, unlink the file and return. If the
     331    *    lock has expired, sleep the usual random interval before returning.
     332    *    If we didn't sleep, there could be a race if the caller immediately
     333    *    tries to relock the file.
     334    */
     335
     336   if ( !lstat(fullpath, &old_stat) &&   /* stat succeeds so file is there */
     337       (old_stat.st_ctime == birth)) {   /* hasn't been modified since birth */
     338     unlink(fullpath);                   /* so the lock is ours to remove */
     339     if (our_tmp.st_ctime >= birth + max_age)    /* the lock has expired */
     340       sleep(1+(random() % 10));         /* so sleep a bit */
     341     free(fullpath);
     342     return(NFSL_OK);                    /* success */
     343   }
     344
     345   free(fullpath);       /* we don't use fullpath anymore */
     346
     347   /*
     348    * 4. Either ctime has been modified, or the entire lock file is missing.
     349    *    If the lock should still be ours, based on the ctime of the temp
     350    *    file, return with NFSL_STOLEN. If not, then our lock is expired and
     351    *    someone else has grabbed the file, so return NFSL_LOST.
     352    */
     353
     354   if (our_tmp.st_ctime < birth + max_age)       /* lock was stolen */
     355     return(NFSL_STOLEN);
     356
     357   return(NFSL_LOST);    /* The lock must have expired first. */
     358 }
     359
     360
     361

/* [<][>][^][v][top][bottom][index][help] */