Source File pa/spawn.c

    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)
    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)
    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)
    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  |