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] */