modules/pa/spawn.c

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

DEFINITIONS

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 
  57 #include "rip.h"
  58 
  59 #include <stdio.h>
  60 #include <stdlib.h>
  61 #include <errno.h>
  62 #include <sys/types.h> 
  63 #include <unistd.h>
  64 #include <string.h>
  65 #include <sys/stat.h>
  66 #include <fcntl.h>
  67 #include <time.h>
  68 
  69 /* #include "pgpsendmail.h" */
  70 
  71 #ifndef ERRSTRING
  72 #define ERRSTRING strerror(errno)
  73 #endif /* ERRSTRING */
  74 
  75 #define LINE_LENGTH 1024
  76 #define STRING_LENGTH 255
  77 
  78 enum NFSL {
  79   NFSL_SYSF,
  80   NFSL_SECV,
  81   NFSL_LOCKED,
  82   NFSL_OK,
  83   NFSL_STOLEN,
  84   NFSL_LOST
  85 };
  86 
  87 
  88 int sd1[2] /*, sd2[2] */;  /* sd2 is used to have a collaborative 
  89                                   dialogue between processes          */
  90 int spawn_job (char *path, char *argv[], int *in_fd, int *out_fd, int *err_fd)
     /* [<][>][^][v][top][bottom][index][help] */
  91 /*  This routine will fork(2) and execvp(2) a process.
  92     The file to execute must be pointed to by  path  .
  93     The NULL terminated list of arguments which will be passed to  main  must
  94     be pointed to by  argv  .
  95     The input file descriptor (fd = 0) for the process must be pointed to by
  96     in_fd  .If the value here is less than 0, then a pipe to the process is
  97     opened and the writeable end is written to the storage pointed to by  in_fd
  98     The standard output file descriptor (fd = 1) for the process must be
  99     pointed to by  out_fd  .If the value here is less than 0, then a pipe to
 100     the process is opened and the readable end is written to the storage
 101     pointed to by  out_fd  .
 102     The standard error output file descriptor (fd = 2) for the process must be
 103     pointed to by  err_fd  .If the value here is less than 0, then a pipe to
 104     the process is opened and the readable end is written to the storage
 105     pointed to by  err_fd  .
 106     The routine returns the child process ID on success, else it returns -1.
 107 */
 108 {
 109     int child_pid;
 110     /*     char txt[LINE_LENGTH];  */
 111 
 112     if (pipe(sd1) == -1)
 113       {
 114         perror("pipe failed");
 115         return(1);
 116       }
 117 
 118     /*  Fork and exec  */
 119     switch ( child_pid = fork () )
 120     {
 121       case 0:
 122         /*  Child: exec  */
 123         close(sd1[0]); 
 124 
 125         dup2( sd1[1], 1 );   /* stdout */
 126 
 127         /*      fprintf (stderr, "dup2 1 result: %s\n", ERRSTRING); */
 128         dup2( sd1[1], 2 );    /* stderr */
 129         /*      fprintf (stderr, "dup2 2 result: %s\n", ERRSTRING); */
 130 
 131         execvp (path, argv);
 132 
 133         fprintf (stderr, "Could not exec: \"%s\"\t%s\n", path, ERRSTRING);
 134         exit (1); 
 135         break;
 136       case -1:
 137         /*  Error  */
 138         fprintf (stderr, "Could not fork\t%s\n", ERRSTRING);
 139         return (-1);
 140         break;
 141       default:
 142         /*  Parent  */
 143         break;
 144     }
 145     /*  Parent only  */
 146 
 147     close(sd1[1]); 
 148 
 149     dup2 (sd1[0], 0); 
 150     
 151     /*     fprintf (stderr, "dup2 0 result: %s\n", ERRSTRING);  */
 152     /*     fprintf(stderr, "Reading child output\n");
 153     while (read(0, txt, 1000) != 0)
 154       fprintf(stderr, "child read %s\n", txt);
 155     
 156       fprintf(stderr, "Finished reading child output\n");   */
 157     
 158     return (child_pid);
 159 }   /*  End Function spawn_job  */
 160 
 161 
 162 #define DEFAULT_LOCKTIME 300;
 163 
 164 time_t nfslock(char *path, char *namelock, int max_age, int notify)
     /* [<][>][^][v][top][bottom][index][help] */
 165 {
 166   int tries = 0, oldlck = 0, tmpfd;
 167   struct stat old_stat, our_tmp;
 168   char tmp[32];
 169   char *tpath, *fullpath;
 170 
 171   srandom(getpid()+time(0));
 172   max_age = max_age ? max_age : DEFAULT_LOCKTIME;
 173 
 174   /*
 175    * 1. create a tmp file with a psuedo random file name. we also make
 176    *    tpath which is a buffer to store the full pathname of the tmp file.
 177    */
 178 
 179   sprintf(tmp,"slock%d.%d", (int)getpid(), (int)random());
 180 
 181   tpath = UT_malloc(strlen(path) + 1 + strlen(tmp) + 1);
 182   if (tpath == NULL) return(NFSL_SYSF);
 183   sprintf(tpath,"%s/%s", path, tmp);
 184 
 185   tmpfd = open(tpath, O_CREAT|O_WRONLY|O_EXCL, S_IRUSR|S_IWUSR);
 186 
 187   if (tmpfd < 0) {      /* open failed */
 188     close(tmpfd);
 189     unlink(tpath);
 190     UT_free(tpath);
 191     return(NFSL_SYSF);
 192   }
 193 
 194   if (getuid()==0) {    /* we're root, so be careful! */
 195     if (fstat(tmpfd, &our_tmp) < 0) {   /* stat failed... shouldn't happen */
 196       close(tmpfd);
 197       unlink(tpath);
 198       UT_free(tpath);
 199       return(NFSL_SYSF);
 200     }
 201     if (our_tmp.st_nlink != 1) {        /* someone's trying to mess with us */
 202       fprintf(stderr,"nfslock: bad link count on %s\n",tpath);
 203       close(tmpfd);
 204       unlink(tpath);
 205       UT_free(tpath);
 206       return(NFSL_SECV);
 207     }
 208   }
 209 
 210   /*
 211    * 2. make fullpath, a buffer for the full pathname of the lock file.
 212    *    then start looping trying to lock it
 213    */
 214 
 215   fullpath = UT_malloc(strlen(path) + 1 + strlen(namelock) + 1);
 216   if (fullpath == NULL) {
 217     close(tmpfd);
 218     unlink(tpath);
 219     UT_free(tpath);
 220     return(NFSL_SYSF);
 221   }
 222   sprintf(fullpath,"%s/%s", path, namelock);
 223 
 224   while (tries < 10) {
 225 
 226     /*
 227      * 3. link tmp file to lock file.  if it goes, we win and we clean
 228      *    up and return the st_ctime of the lock file.
 229      */
 230 
 231     if (link(tpath, fullpath) == 0) {
 232       unlink(tpath); /* got it! */
 233       UT_free(tpath);
 234       close(tmpfd);
 235       if (lstat(fullpath, &our_tmp) < 0) {      /* stat failed... shouldn't happen */
 236         unlink(fullpath);
 237         UT_free(fullpath);
 238         return (NFSL_SYSF);
 239       }
 240       UT_free(fullpath);
 241       return(our_tmp.st_ctime);
 242     }
 243 
 244     /*
 245      * 4. the lock failed.  check for a stale lock file, being mindful
 246      *    of NFS and the fact the time is set from the NFS server.  we
 247      *    do a write on the tmp file to update its time to the server's
 248      *    idea of "now."
 249      */
 250 
 251     oldlck = lstat(fullpath, &old_stat);
 252 
 253     if (write(tmpfd, "\0", 1) != 1 || fstat(tmpfd, &our_tmp) < 0)
 254       break; /* something bogus is going on */
 255 
 256     if (oldlck != -1 && old_stat.st_ctime + max_age < our_tmp.st_ctime) {
 257       unlink(fullpath); /* break the stale lock */
 258       if (notify) fprintf(stderr,"Breaking stale lock on %s.\n",fullpath);
 259       tries++;
 260       sleep(1+(random() % 10)); /* It is CRITICAL that we sleep after breaking
 261                                  * the lock. Otherwise, we could race with
 262                                  * another process and unlink it's newly-
 263                                  * created file.
 264                                  */
 265       continue;
 266     }
 267 
 268     /*
 269      * 5. try again
 270      */
 271 
 272     if (notify) {
 273       if (tries==0) fprintf(stderr,"Waiting for lock on file %s.\n",fullpath);
 274       else fprintf(stderr,"Still waiting...\n");
 275     }
 276     tries++;
 277     sleep(1+(random() % 10));
 278   }
 279 
 280   /*
 281    * 6. give up, failure.
 282    */
 283 
 284   errno = EEXIST;
 285   unlink(tpath);
 286   UT_free(tpath);
 287   UT_free(fullpath);
 288   close(tmpfd);
 289   return(NFSL_LOCKED);
 290 }
 291 
 292 int nfsunlock(char *path, char *namelock, int max_age, time_t birth)
     /* [<][>][^][v][top][bottom][index][help] */
 293 {
 294   int tmpfd;
 295   struct stat old_stat, our_tmp;
 296   char *tpath, *fullpath;
 297 
 298   srandom(getpid()+time(0));
 299   max_age = max_age ? max_age : DEFAULT_LOCKTIME;
 300 
 301   /*
 302    * 1. Build a temp file and stat that to get an idea of what the server
 303    *    thinks the current time is (our_tmp.st_ctime)..
 304    */
 305 
 306   tpath = UT_malloc(strlen(path) + 25);   /* small slop factor- 23 s/b enough */
 307   if (tpath == NULL) return(NFSL_SYSF);
 308   sprintf(tpath,"%s/slock%d.%d", path, (int)getpid(), (int)random());
 309 
 310   tmpfd = open(tpath, O_CREAT|O_WRONLY|O_EXCL, S_IRUSR|S_IWUSR);
 311 
 312   if ((tmpfd < 0) || (write(tmpfd, "\0", 1) != 1)
 313                   || (fstat(tmpfd, &our_tmp) < 0)) {
 314     /* The open failed, or we can't write the file, or we can't stat it */
 315     close(tmpfd);
 316     unlink(tpath);
 317     UT_free(tpath);
 318     return(NFSL_SYSF);
 319   }
 320 
 321   close(tmpfd);         /* We don't need this once we have our_tmp.st_ctime. */
 322   unlink(tpath);
 323   UT_free(tpath);
 324 
 325   /*
 326    * 2. make fullpath, a buffer for the full pathname of the lock file
 327    */
 328 
 329   fullpath = UT_malloc(strlen(path) + 1 + strlen(namelock) + 1);
 330   if (fullpath == NULL)
 331     return(NFSL_SYSF);
 332   sprintf(fullpath,"%s/%s", path, namelock);
 333 
 334   /*
 335    * 3. If the ctime hasn't been modified, unlink the file and return. If the
 336    *    lock has expired, sleep the usual random interval before returning.
 337    *    If we didn't sleep, there could be a race if the caller immediately
 338    *    tries to relock the file.
 339    */
 340 
 341   if ( !lstat(fullpath, &old_stat) &&   /* stat succeeds so file is there */
 342       (old_stat.st_ctime == birth)) {   /* hasn't been modified since birth */
 343     unlink(fullpath);                   /* so the lock is ours to remove */
 344     if (our_tmp.st_ctime >= birth + max_age)    /* the lock has expired */
 345       sleep(1+(random() % 10));         /* so sleep a bit */
 346     UT_free(fullpath);
 347     return(NFSL_OK);                    /* success */
 348   }
 349 
 350   UT_free(fullpath);       /* we don't use fullpath anymore */
 351 
 352   /*
 353    * 4. Either ctime has been modified, or the entire lock file is missing.
 354    *    If the lock should still be ours, based on the ctime of the temp
 355    *    file, return with NFSL_STOLEN. If not, then our lock is expired and
 356    *    someone else has grabbed the file, so return NFSL_LOST.
 357    */
 358 
 359   if (our_tmp.st_ctime < birth + max_age)       /* lock was stolen */
 360     return(NFSL_STOLEN);
 361 
 362   return(NFSL_LOST);    /* The lock must have expired first. */
 363 }
 364 
 365 
 366 

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