|
|
1.1 root 1: /*
2: ** Deliver.c
3: **
4: ** Routines to effect delivery of mail for rmail/smail.
5: **
6: */
7:
8: #ifndef lint
9: static char *sccsid="@(#)deliver.c 2.5 (smail) 9/15/87";
10: #endif
11:
12: # include <stdio.h>
13: # include <sys/types.h>
14: # include <sys/stat.h>
15: /* # include <sys/utsname.h> */
16: # include <ctype.h>
17: # include <signal.h>
18: # include "defs.h"
19:
20: extern int errno; /* Error number set by certain system routines. */
21: extern int exitstat; /* set if a forked mailer fails */
22: extern enum edebug debug; /* how verbose we are */
23: extern char hostname[]; /* our uucp hostname */
24: extern char hostdomain[]; /* our host's domain */
25: extern enum ehandle handle; /* what we handle */
26: extern enum erouting routing; /* how we're routing addresses */
27: extern char *uuxargs; /* arguments given to uux */
28: extern int queuecost; /* threshold for queueing mail */
29: extern int maxnoqueue; /* max number of uucico's */
30: extern char *spoolfile; /* file name of spooled message */
31: extern FILE *spoolfp; /* file ptr to spooled message */
32: extern int spoolmaster; /* set if creator of spoolfile */
33: extern char nows[]; /* local time in ctime(3) format*/
34: extern char arpanows[]; /* local time in arpadate format*/
35: extern char _version[]; /* Version string from copyright.c */
36: char stderrfile[20]; /* error file for stderr traping*/
37: char err_msg[2*MAXCLEN]; /* handy place to build error messages */
38:
39: char userlist[2*MAXCLEN]; /* used to build a list of users mail will
40: * sent to.
41: */
42: /*
43: **
44: ** deliver(): hand the letter to the proper mail programs.
45: **
46: ** Issues one command for each different host of <hostv>,
47: ** constructing the proper command for LOCAL or UUCP mail.
48: ** Note that LOCAL mail has blank host names.
49: **
50: ** The <userv> names for each host are arguments to the command.
51: **
52: ** Prepends a "From" line to the letter just before going
53: ** out, with a "remote from <hostname>" if it is a UUCP letter.
54: **
55: */
56:
57: deliver(argc, hostv, userv, formv, costv)
58: int argc; /* number of addresses */
59: char *hostv[]; /* host names */
60: char *userv[]; /* user names */
61: enum eform formv[]; /* form for each address */
62: int costv[]; /* cost vector */
63: {
64: FILE *out; /* pipe to mailer */
65: FILE *popen(); /* to fork a mailer */
66: #ifdef RECORD
67: void record(); /* record all transactions */
68: #endif
69: #ifdef LOG
70: void log();
71: #endif
72: char *mktemp();
73: char *sform();
74: char from[SMLBUF]; /* accumulated from argument */
75: char lcommand[SMLBUF]; /* local command issued */
76: char rcommand[SMLBUF]; /* remote command issued */
77: char scommand[SMLBUF]; /* retry command issued */
78: char *command; /* actual command */
79: char buf[SMLBUF]; /* copying rest of the letter */
80: enum eform form; /* holds form[i] for speed */
81: long size; /* number of bytes of message */
82: char *flags; /* flags for uux */
83: char *sflag; /* flag for smail */
84: int i, j, status, retrying;
85: char *c, *postmaster();
86: int failcount = 0;
87: int noqcnt = 0; /* number of uucico's started */
88: char *uux_noqueue = UUX_NOQUEUE;/* uucico starts immediately */
89: char *uux_queue = UUX_QUEUE; /* uucico job gets queued */
90: off_t message;
91: struct stat st;
92:
93: /*
94: ** rewind the spool file and read the collapsed From_ line
95: */
96: (void) fseek(spoolfp, 0L, 0);
97: (void) fgets(from, sizeof(from), spoolfp);
98: if((c = index(from, '\n')) != 0) *c = '\0';
99: message = ftell(spoolfp);
100:
101: /*
102: ** We pass through the list of addresses.
103: */
104: userlist[0] = '\0'; /* initialize userlist */
105: stderrfile[0] = '\0';
106: for(i = 0; i < argc; i++) {
107: char *lend = lcommand;
108: char *rend = rcommand;
109: char *send = scommand;
110:
111: /*
112: ** Prevent errors from previous passes.
113: */
114: exitstat = 0;
115:
116:
117: /*
118: ** If we don't have sendmail, arrange to trap standard error
119: ** for inclusion in the message that is returned with failed mail.
120: */
121: (void) unlink(stderrfile);
122: (void) strcpy(stderrfile, "/tmp/stderrXXXXXX");
123: (void) mktemp(stderrfile);
124: (void) freopen(stderrfile, "w", stderr);
125: if(debug != YES) {
126: (void) freopen(stderrfile, "w", stdout);
127: }
128:
129: *lend = *rend = *send = '\0';
130:
131: /*
132: ** If form == ERROR, the address was bad
133: ** If form == SENT, it has been sent on a previous pass.
134: */
135: form = formv[i];
136: if (form == SENT) {
137: continue;
138: }
139: /*
140: ** Build the command based on whether this is local mail or uucp mail.
141: ** By default, don't allow more than 'maxnoqueue' uucico commands to
142: ** be started by a single invocation of 'smail'.
143: */
144: if(uuxargs == NULL) { /* flags not set on command line */
145: if(noqcnt < maxnoqueue && costv[i] <= queuecost) {
146: flags = uux_noqueue;
147: } else {
148: flags = uux_queue;
149: }
150: } else {
151: flags = uuxargs;
152: }
153:
154: retrying = 0;
155: if(routing == JUSTDOMAIN) {
156: sflag = "-r";
157: } else if(routing == ALWAYS) {
158: sflag = "-R";
159: } else {
160: sflag = "";
161: }
162:
163: (void) sprintf(lcommand, LMAIL(from, hostv[i]));
164: (void) sprintf(rcommand, RMAIL(flags, from, hostv[i]));
165:
166: /*
167: ** For each address with the same host name and form, append the user
168: ** name to the command line, and set form = ERROR so we skip this address
169: ** on later passes.
170: */
171: /* we initialized lend (rend) to point at the
172: * beginning of its buffer, so that at
173: * least one address will be used regardless
174: * of the length of lcommand (rcommand).
175: */
176: for (j = i; j < argc; j++) {
177: if ((formv[j] != form)
178: || (strcmpic(hostv[i], hostv[j]) != 0)
179: || ((lend - lcommand) > MAXCLEN)
180: || ((rend - rcommand) > MAXCLEN)) {
181: continue;
182: }
183:
184: /*
185: ** seek to the end of scommand
186: ** and add on a 'smail' command
187: ** multiple commands are separated by ';'
188: */
189:
190: send += strlen(send);
191: if(send != scommand) {
192: *send++ = ';' ;
193: }
194:
195: (void) sprintf(send, RETRY(sflag));
196: send += strlen(send);
197:
198: lend += strlen(lend);
199: rend += strlen(rend);
200:
201: if (form == LOCAL) {
202: (void) sprintf(lend, LARG(userv[j]));
203: (void) sprintf(send, LARG(userv[j]));
204: strcat(userlist,userv[j]);
205: } else {
206: (void) sprintf(lend, RLARG(hostv[i], userv[j]));
207: (void) sprintf(send, RLARG(hostv[i], userv[j]));
208: }
209:
210: (void) sprintf(rend, RARG(userv[j]));
211: formv[j] = SENT;
212: }
213: retry:
214: /*
215: ** rewind the spool file and read the collapsed From_ line
216: */
217: (void) fseek(spoolfp, message, 0);
218:
219: /* if the address was in a bogus form (usually DOMAIN),
220: ** then don't bother trying the uux.
221: **
222: ** Rather, go straight to the next smail routing level.
223: */
224: if(form == ERROR) {
225: static char errbuf[SMLBUF];
226: (void) sprintf(errbuf,
227: "address resolution ('%s' @ '%s') failed",
228: userv[i], hostv[i]);
229: command = errbuf;
230: size = 0;
231: goto form_error;
232: }
233:
234: if (retrying) {
235: command = scommand;
236: } else if (form == LOCAL) {
237: command = lcommand;
238: } else {
239: command = rcommand;
240: if(flags == uux_noqueue) {
241: noqcnt++;
242: }
243: }
244: ADVISE("COMMAND: %s\n", command);
245:
246: /*
247: ** Fork the mailer and set it up for writing so we can send the mail to it,
248: ** or for debugging divert the output to stdout.
249: */
250:
251: /*
252: ** We may try to write on a broken pipe, if the uux'd host
253: ** is unknown to us. Ignore this signal, since we can use the
254: ** return value of the pclose() as our indication of failure.
255: */
256: (void) signal(SIGPIPE, SIG_IGN);
257:
258: if (debug == YES) {
259: out = stdout;
260: } else {
261: failcount = 0;
262: do {
263: out = popen(command, "w");
264: if (out != NULL) break;
265: /*
266: * Fork failed. System probably overloaded.
267: * Wait awhile and try again 10 times.
268: * If it keeps failing, probably some
269: * other problem, like no uux or smail.
270: */
271: (void) sleep(60);
272: if (failcount == 1) {
273: sprintf(err_msg,
274: "Deliver can not popen %s. errno = %d",
275: command, errno);
276: error_log(err_msg);
277: } /* if failed exactly once */
278: } while (++failcount < 10);
279: if (failcount == 10) {
280: sprintf(err_msg,
281: "Deliver failed 10 times. errno = %d",
282: command, errno);
283: error_log(err_msg);
284: } /* if failed exactly once */
285: }
286: if(out == NULL) {
287: exitstat = EX_UNAVAILABLE;
288: (void) printf("couldn't execute %s.\n", command);
289: continue;
290: }
291:
292: size = 0;
293: if(fstat(fileno(spoolfp), &st) >= 0) {
294: size = st.st_size - message;
295: }
296: /*
297: ** Output our From_ line.
298: */
299: if (form == LOCAL) {
300: #ifdef SENDMAIL
301: (void) sprintf(buf, LFROM(from, nows, hostname));
302: size += strlen(buf);
303: (void) fputs(buf, out);
304: fflush(out);
305: #else
306: char *p;
307: if((p=index(from, '!')) != NULL) {
308: *p = NULL;
309: (void) sprintf(buf, RFROM(p+1, nows, from));
310: size += strlen(buf);
311: (void) fputs(buf, out);
312: fflush(out);
313: *p = '!';
314: }
315: #endif
316: } else {
317: (void) sprintf(buf, RFROM(from, nows, hostname));
318: size += strlen(buf);
319: (void) fputs(buf, out);
320: fflush(out);
321: }
322:
323: /* END OF THIS LOOKS LIKE THE PLACE */
324: #ifdef SENDMAIL
325: /*
326: ** If using sendmail, insert a Received: line only for mail
327: ** that is being passed to uux. If not using sendmail, always
328: ** insert the received line, since sendmail isn't there to do it.
329: */
330: if(command == rcommand && handle != ALL)
331: #endif
332: {
333: #ifdef GATEWAY_NAME
334: (void) sprintf(buf,
335: "Received: by %s (%s - %s) id AA%05d; %s\n",
336: hostdomain, VERSION,GATEWAY_NAME,
337: getpid(), arpanows);
338: #else
339: (void) sprintf(buf,
340: "Received: by %s (%s) id AA%05d; %s\n",
341: hostdomain, VERSION,
342: getpid(), arpanows);
343: #endif
344: size += strlen(buf);
345: (void) fputs(buf, out);
346: fflush(out);
347: }
348:
349: /*
350: ** Copy input.
351: */
352: while(fgets(buf, sizeof(buf), spoolfp) != NULL) {
353: (void) fputs(buf, out);
354: fflush(out);
355: }
356: /*
357: ** Get exit status and if non-zero, set global exitstat so when we exit
358: ** we can indicate an error.
359: */
360: form_error:
361: if (debug != YES) {
362: if(form == ERROR) {
363: exitstat = EX_NOHOST;
364: } else {
365: status = pclose(out);
366: exitstat = status >> 8;
367: }
368: /*
369: * The 'retrying' check prevents a smail loop.
370: */
371: if(exitstat != 0) {
372: /*
373: ** the mail failed, probably because the host
374: ** being uux'ed isn't in L.sys or local user
375: ** is unknown.
376: */
377:
378: if((retrying == 0) /* first pass */
379: && (routing != REROUTE) /* have higher level */
380: && (form != LOCAL)) { /* can't route local */
381: /*
382: ** Try again using a higher
383: ** level of routing.
384: */
385: ADVISE("%s failed (%d)\ntrying %s\n",
386: command, exitstat, scommand);
387: exitstat = 0;
388: retrying = 1;
389: form = SENT;
390: goto retry;
391: }
392:
393: /*
394: ** if we have no other routing possibilities
395: ** see that the mail is returned to sender.
396: */
397:
398: /* Important NOTE: A very nasty race condition exists here. If
399: * multiple child processes are trying to send mail to the same
400: * user simultaneously, then 1 process will get the mailbox and
401: * lock it, causing the other processes to fail. If a user invokes
402: * several processes to send mail to himself locally, then each
403: * process that fails once a mailbox is locked will force smail to
404: * return the message back to the original sender, which will fail,
405: * because the mailbox is already locked. This will force another
406: * smail process to fire up to return the returned message, and we
407: * will enter a recursive spawning of smail processes that will
408: * eventually choke the kernel.
409: *
410: * I have added code to check the following:
411: * if a local mail send fails, we check to see if the person
412: * who sent the message exists in the local send-to-user
413: * list (userlist). If this is true, we do NOT return the
414: * the mail, but write a message to the log file to indicate
415: * that this occured.
416: *
417: * Bob H. 10/20/92
418: */
419: if((routing == REROUTE)
420: || ((form == LOCAL) && (!strstr(userlist,from)))) {
421:
422: /*
423: ** if this was our last chance,
424: ** return the mail to the sender.
425: */
426:
427: ADVISE("%s failed (%d)\n",
428: command, exitstat);
429:
430: (void) fseek(spoolfp, message, 0);
431: #ifdef SENDMAIL
432: /* if we have sendmail, then it
433: ** was handed the mail, which failed.
434: ** sendmail returns the failed mail
435: ** for us, so we need not do it again.
436: */
437: if(form != LOCAL)
438: #endif
439: {
440: return_mail(from, command, form);
441: }
442: exitstat = 0;
443: }
444: if((form == LOCAL) && (strstr(userlist,from))){
445: sprintf(err_msg,"exit stat was %d", exitstat);
446: error_log(err_msg);
447: sprintf(err_msg,"Local mail to user %s failed. Because", from);
448: error_log(err_msg);
449: sprintf(err_msg,"this was the same person to send the message,");
450: error_log(err_msg);
451: sprintf(err_msg,"it will NOT be returned to avoid a race condition.");
452: error_log(err_msg);
453:
454: }
455: }
456: # ifdef LOG
457: else {
458: if(retrying == 0) log(command, from, size);
459: }
460: # endif
461: }
462: }
463: /*
464: ** Update logs and records.
465: */
466: # ifdef RECORD
467: (void) fseek(spoolfp, message, 0);
468: record(command, from, size);
469: # endif
470:
471: /*
472: ** close spool file pointer.
473: ** if we created it, then unlink file.
474: */
475: (void) fclose(spoolfp);
476: if(spoolmaster) {
477: (void) unlink(spoolfile);
478: }
479: (void) unlink(stderrfile);
480: }
481:
482: /*
483: ** return mail to sender, as determined by From_ line.
484: */
485: return_mail(from, fcommand, form)
486: char *from, *fcommand;
487: enum eform form;
488: {
489: char buf[SMLBUF];
490: char domain[SMLBUF], user[SMLBUF];
491: char *r;
492: FILE *fp, *out, *popen();
493: int i = 0;
494:
495: r = buf;
496:
497: (void) sprintf(r, "%s %s", SMAIL, VFLAG);
498: r += strlen(r);
499:
500: if(islocal(from, domain, user)) {
501: (void) sprintf(r, LARG(user));
502: } else {
503: (void) sprintf(r, RLARG(domain, user));
504: }
505:
506: i = 0;
507:
508: do {
509: out = popen(buf, "w");
510: if (out) break;
511: /*
512: * Fork failed. System probably overloaded.
513: * Wait awhile and try again 10 times.
514: * If it keeps failing, probably some
515: * other problem, like no uux or smail.
516: */
517: (void) sleep(60);
518: } while (++i < 10);
519:
520: if(out == NULL) {
521: (void) printf("couldn't execute %s.\n", buf);
522: return;
523: }
524:
525: (void) fprintf(out, "Date: %s\n", arpanows);
526: (void) fprintf(out, "From: MAILER-DAEMON@%s\n", hostdomain);
527: (void) fprintf(out, "Subject: failed mail\n");
528: (void) fprintf(out, "To: %s\n", from);
529: (void) fprintf(out, "\n");
530: (void) fprintf(out, "Form: %s\n", sform(form));
531: (void) fprintf(out, "Exit status: %d\n", exitstat);
532: (void) fprintf(out, "======= command failed =======\n\n");
533: (void) fprintf(out, " COMMAND: %s\n\n", fcommand);
534:
535: (void) fprintf(out, "======= standard error follows =======\n");
536: (void) fflush(stderr);
537: if((fp = fopen(stderrfile, "r")) != NULL) {
538: while(fgets(buf, sizeof(buf), fp) != NULL) {
539: (void) fputs(buf, out);
540: }
541: }
542: (void) fclose(fp);
543: (void) fprintf(out, "======= text of message follows =======\n");
544: /*
545: ** Copy input.
546: */
547: (void) fprintf(out, "From %s\n", from);
548: while(fgets(buf, sizeof(buf), spoolfp) != NULL) {
549: (void) fputs(buf, out);
550: }
551: (void) pclose(out);
552: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.