|
|
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) 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: #else
305: char *p;
306: if((p=index(from, '!')) != NULL) {
307: *p = NULL;
308: (void) sprintf(buf, RFROM(p+1, nows, from));
309: size += strlen(buf);
310: (void) fputs(buf, out);
311: *p = '!';
312: }
313: #endif
314: } else {
315: (void) sprintf(buf, RFROM(from, nows, hostname));
316: size += strlen(buf);
317: (void) fputs(buf, out);
318: }
319:
320: /* END OF THIS LOOKS LIKE THE PLACE */
321: #ifdef SENDMAIL
322: /*
323: ** If using sendmail, insert a Received: line only for mail
324: ** that is being passed to uux. If not using sendmail, always
325: ** insert the received line, since sendmail isn't there to do it.
326: */
327: if(command == rcommand && handle != ALL)
328: #endif
329: {
330: #ifdef GATEWAY_NAME
331: (void) sprintf(buf,
332: "Received: by %s (%s - %s) id AA%05d; %s\n",
333: hostdomain, VERSION,GATEWAY_NAME,
334: getpid(), arpanows);
335: #else
336: (void) sprintf(buf,
337: "Received: by %s (%s) id AA%05d; %s\n",
338: hostdomain, VERSION,
339: getpid(), arpanows);
340: #endif
341: size += strlen(buf);
342: (void) fputs(buf, out);
343: }
344:
345: /*
346: ** Copy input.
347: */
348: while(fgets(buf, sizeof(buf), spoolfp) != NULL) {
349: (void) fputs(buf, out);
350: }
351: /*
352: ** Get exit status and if non-zero, set global exitstat so when we exit
353: ** we can indicate an error.
354: */
355: form_error:
356: if (debug != YES) {
357: if(form == ERROR) {
358: exitstat = EX_NOHOST;
359: } else {
360: status = pclose(out);
361: exitstat = status >> 8;
362: }
363: /*
364: * The 'retrying' check prevents a smail loop.
365: */
366: if(exitstat != 0) {
367: /*
368: ** the mail failed, probably because the host
369: ** being uux'ed isn't in L.sys or local user
370: ** is unknown.
371: */
372:
373: if((retrying == 0) /* first pass */
374: && (routing != REROUTE) /* have higher level */
375: && (form != LOCAL)) { /* can't route local */
376: /*
377: ** Try again using a higher
378: ** level of routing.
379: */
380: ADVISE("%s failed (%d)\ntrying %s\n",
381: command, exitstat, scommand);
382: exitstat = 0;
383: retrying = 1;
384: form = SENT;
385: goto retry;
386: }
387:
388: /*
389: ** if we have no other routing possibilities
390: ** see that the mail is returned to sender.
391: */
392:
393: /* Important NOTE: A very nasty race condition exists here. If
394: * multiple child processes are trying to send mail to the same
395: * user simultaneously, then 1 process will get the mailbox and
396: * lock it, causing the other processes to fail. If a user invokes
397: * several processes to send mail to himself locally, then each
398: * process that fails once a mailbox is locked will force smail to
399: * return the message back to the original sender, which will fail,
400: * because the mailbox is already locked. This will force another
401: * smail process to fire up to return the returned message, and we
402: * will enter a recursive spawning of smail processes that will
403: * eventually choke the kernel.
404: *
405: * I have added code to check the following:
406: * if a local mail send fails, we check to see if the person
407: * who sent the message exists in the local send-to-user
408: * list (userlist). If this is true, we do NOT return the
409: * the mail, but write a message to the log file to indicate
410: * that this occured.
411: *
412: * Bob H. 10/20/92
413: */
414: if((routing == REROUTE)
415: || ((form == LOCAL) && (!strstr(userlist,from)))) {
416:
417: /*
418: ** if this was our last chance,
419: ** return the mail to the sender.
420: */
421:
422: ADVISE("%s failed (%d)\n",
423: command, exitstat);
424:
425: (void) fseek(spoolfp, message, 0);
426: #ifdef SENDMAIL
427: /* if we have sendmail, then it
428: ** was handed the mail, which failed.
429: ** sendmail returns the failed mail
430: ** for us, so we need not do it again.
431: */
432: if(form != LOCAL)
433: #endif
434: {
435: return_mail(from, command, form);
436: }
437: exitstat = 0;
438: }
439: if((form == LOCAL) && (strstr(userlist,from))){
440: sprintf(err_msg,"Local mail to user %s failed. Because", from);
441: error_log(err_msg);
442: sprintf(err_msg,"this was the same person to send the message,");
443: error_log(err_msg);
444: sprintf(err_msg,"it will NOT be returned to avoid a race condition.");
445: error_log(err_msg);
446: }
447: }
448: # ifdef LOG
449: else {
450: if(retrying == 0) log(command, from, size);
451: }
452: # endif
453: }
454: }
455: /*
456: ** Update logs and records.
457: */
458: # ifdef RECORD
459: (void) fseek(spoolfp, message, 0);
460: record(command, from, size);
461: # endif
462:
463: /*
464: ** close spool file pointer.
465: ** if we created it, then unlink file.
466: */
467: (void) fclose(spoolfp);
468: if(spoolmaster) {
469: (void) unlink(spoolfile);
470: }
471: (void) unlink(stderrfile);
472: }
473:
474: /*
475: ** return mail to sender, as determined by From_ line.
476: */
477: return_mail(from, fcommand, form)
478: char *from, *fcommand;
479: enum eform form;
480: {
481: char buf[SMLBUF];
482: char domain[SMLBUF], user[SMLBUF];
483: char *r;
484: FILE *fp, *out, *popen();
485: int i = 0;
486:
487: r = buf;
488:
489: (void) sprintf(r, "%s %s", SMAIL, VFLAG);
490: r += strlen(r);
491:
492: if(islocal(from, domain, user)) {
493: (void) sprintf(r, LARG(user));
494: } else {
495: (void) sprintf(r, RLARG(domain, user));
496: }
497:
498: i = 0;
499:
500: do {
501: out = popen(buf, "w");
502: if (out) break;
503: /*
504: * Fork failed. System probably overloaded.
505: * Wait awhile and try again 10 times.
506: * If it keeps failing, probably some
507: * other problem, like no uux or smail.
508: */
509: (void) sleep(60);
510: } while (++i < 10);
511:
512: if(out == NULL) {
513: (void) printf("couldn't execute %s.\n", buf);
514: return;
515: }
516:
517: (void) fprintf(out, "Date: %s\n", arpanows);
518: (void) fprintf(out, "From: MAILER-DAEMON@%s\n", hostdomain);
519: (void) fprintf(out, "Subject: failed mail\n");
520: (void) fprintf(out, "To: %s\n", from);
521: (void) fprintf(out, "\n");
522: (void) fprintf(out, "Form: %s\n", sform(form));
523: (void) fprintf(out, "Exit status: %d\n", exitstat);
524: (void) fprintf(out, "======= command failed =======\n\n");
525: (void) fprintf(out, " COMMAND: %s\n\n", fcommand);
526:
527: (void) fprintf(out, "======= standard error follows =======\n");
528: (void) fflush(stderr);
529: if((fp = fopen(stderrfile, "r")) != NULL) {
530: while(fgets(buf, sizeof(buf), fp) != NULL) {
531: (void) fputs(buf, out);
532: }
533: }
534: (void) fclose(fp);
535: (void) fprintf(out, "======= text of message follows =======\n");
536: /*
537: ** Copy input.
538: */
539: (void) fprintf(out, "From %s\n", from);
540: while(fgets(buf, sizeof(buf), spoolfp) != NULL) {
541: (void) fputs(buf, out);
542: }
543: (void) pclose(out);
544: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.