modules/pa/spawn.c
/* [<][>][^][v][top][bottom][index][help] */
FUNCTIONS
This source file includes following functions.
- spawn_job
- nfslock
- 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