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