exit => _exit in child
[unix-history] / usr / src / usr.sbin / sendmail / src / deliver.c
CommitLineData
b3cbe40f
EA
1# include <stdio.h>
2# include <pwd.h>
3# include <signal.h>
4# include "dlvrmail.h"
5# ifdef LOG
6# include <log.h>
7# endif LOG
8
a16e99bc 9static char SccsId[] = "@(#)deliver.c 1.9 %G%";
916b3375 10
b3cbe40f
EA
11/*
12** DELIVER -- Deliver a message to a particular address.
13**
14** Algorithm:
15** Compute receiving network (i.e., mailer), host, & user.
16** If local, see if this is really a program name.
17** Build argument for the mailer.
18** Create pipe through edit fcn if appropriate.
19** Fork.
20** Child: call mailer
21** Parent: call editfcn if specified.
22** Wait for mailer to finish.
23** Interpret exit status.
24**
25** Parameters:
26** to -- the address to deliver the message to.
27** editfcn -- if non-NULL, we want to call this function
28** to output the letter (instead of just out-
29** putting it raw).
30**
31** Returns:
32** zero -- successfully delivered.
33** else -- some failure, see ExitStat for more info.
34**
35** Side Effects:
36** The standard input is passed off to someone.
37**
38** WARNING:
39** The standard input is shared amongst all children,
40** including the file pointer. It is critical that the
41** parent waits for the child to finish before forking
42** another child.
43**
b3cbe40f
EA
44** Called By:
45** main
46** savemail
47**
48** Files:
f895a45d 49** standard input -- must be opened to the message to
b3cbe40f 50** deliver.
b3cbe40f
EA
51*/
52
53deliver(to, editfcn)
54 addrq *to;
55 int (*editfcn)();
56{
57 register struct mailer *m;
58 char *host;
59 char *user;
60 extern struct passwd *getpwnam();
61 char **pvp;
62 extern char **buildargv();
63 auto int st;
64 register int i;
65 register char *p;
66 int pid;
67 int pvect[2];
68 extern FILE *fdopen();
69 extern int errno;
70 FILE *mfile;
71 extern putheader();
72 extern pipesig();
73
74 /*
75 ** Compute receiving mailer, host, and to addreses.
76 ** Do some initialization first. To is the to address
77 ** for error messages.
78 */
79
80 To = to->q_paddr;
81 m = to->q_mailer;
82 user = to->q_user;
83 host = to->q_host;
d916f0ca 84 Errors = 0;
b3cbe40f
EA
85 errno = 0;
86# ifdef DEBUG
87 if (Debug)
88 printf("deliver(%s [%d, `%s', `%s'])\n", To, m, host, user);
89# endif DEBUG
90
91 /*
92 ** Remove quote bits from user/host.
93 */
94
95 for (p = user; (*p++ &= 0177) != '\0'; )
96 continue;
97 if (host != NULL)
98 for (p = host; (*p++ &= 0177) != '\0'; )
99 continue;
100
101 /*
102 ** Strip quote bits from names if the mailer wants it.
103 */
104
105 if (flagset(M_STRIPQ, m->m_flags))
106 {
107 stripquotes(user);
108 stripquotes(host);
109 }
110
111 /*
112 ** See if this user name is "special".
113 ** If the user is a program, diddle with the mailer spec.
114 ** If the user name has a slash in it, assume that this
115 ** is a file -- send it off without further ado.
116 ** Note that this means that editfcn's will not
117 ** be applied to the message.
118 */
119
120 if (m == &Mailer[0])
121 {
122 if (*user == '|')
123 {
124 user++;
125 m = &Mailer[1];
126 }
127 else
128 {
129 if (index(user, '/') != NULL)
130 {
131 i = mailfile(user);
132 giveresponse(i, TRUE, m);
133 return (i);
134 }
135 }
136 }
137
b3cbe40f 138 /*
cb590f52
EA
139 ** See if the user exists.
140 ** Strictly, this is only needed to print a pretty
141 ** error message.
142 **
143 ** >>>>>>>>>> This clause assumes that the local mailer
144 ** >> NOTE >> cannot do any further aliasing; that
145 ** >>>>>>>>>> function is subsumed by delivermail.
b3cbe40f
EA
146 */
147
148 if (m == &Mailer[0])
149 {
150 if (getpwnam(user) == NULL)
151 {
152 giveresponse(EX_NOUSER, TRUE, m);
153 return (EX_NOUSER);
154 }
155 }
b3cbe40f
EA
156
157 /*
158 ** If the mailer wants a From line, insert a new editfcn.
159 */
160
161 if (flagset(M_HDR, m->m_flags) && editfcn == NULL)
162 editfcn = putheader;
163
164 /*
165 ** Call the mailer.
166 ** The argument vector gets built, pipes through 'editfcn'
167 ** are created as necessary, and we fork & exec as
168 ** appropriate. In the parent, we call 'editfcn'.
169 */
170
171 pvp = buildargv(m->m_argv, m->m_flags, host, user, From.q_paddr);
172 if (pvp == NULL)
173 {
174 usrerr("name too long");
175 return (-1);
176 }
177 rewind(stdin);
178
179 /* create a pipe if we will need one */
180 if (editfcn != NULL && pipe(pvect) < 0)
181 {
182 syserr("pipe");
183 return (-1);
184 }
c9b9c7a2
MH
185# ifdef VFORK
186 pid = vfork();
187# else
b3cbe40f 188 pid = fork();
c9b9c7a2 189# endif
b3cbe40f
EA
190 if (pid < 0)
191 {
192 syserr("Cannot fork");
193 if (editfcn != NULL)
194 {
195 close(pvect[0]);
196 close(pvect[1]);
197 }
198 return (-1);
199 }
200 else if (pid == 0)
201 {
202 /* child -- set up input & exec mailer */
203 signal(SIGINT, SIG_IGN);
204 if (editfcn != NULL)
205 {
206 close(0);
207 if (dup(pvect[0]) < 0)
208 {
209 syserr("Cannot dup to zero!");
a16e99bc 210 _exit(EX_OSERR);
b3cbe40f
EA
211 }
212 close(pvect[0]);
213 close(pvect[1]);
214 }
215 if (!flagset(M_RESTR, m->m_flags))
216 setuid(getuid());
217# ifdef LOG
218 initlog(NULL, 0, LOG_CLOSE);
219# endif LOG
c9b9c7a2
MH
220# ifndef VFORK
221 /*
222 * We have to be careful with vfork - we can't mung up the
223 * memory but we don't want the mailer to inherit any extra
224 * open files. Chances are the mailer won't
225 * care about an extra file, but then again you never know.
226 * Actually, we would like to close(fileno(pwf)), but it's
227 * declared static so we can't. But if we fclose(pwf), which
228 * is what endpwent does, it closes it in the parent too and
229 * the next getpwnam will be slower. If you have a weird mailer
230 * that chokes on the extra file you should do the endpwent().
231 */
b3cbe40f 232 endpwent();
c9b9c7a2 233# endif
b3cbe40f
EA
234 execv(m->m_mailer, pvp);
235 /* syserr fails because log is closed */
236 /* syserr("Cannot exec %s", m->m_mailer); */
a16e99bc 237 _exit(EX_UNAVAILABLE);
b3cbe40f
EA
238 }
239
240 /* arrange to write out header message if error */
241 if (editfcn != NULL)
242 {
243 close(pvect[0]);
244 signal(SIGPIPE, pipesig);
245 mfile = fdopen(pvect[1], "w");
246 (*editfcn)(mfile);
247 fclose(mfile);
248 }
249
250 /*
251 ** Wait for child to die and report status.
252 ** We should never get fatal errors (e.g., segmentation
253 ** violation), so we report those specially. For other
254 ** errors, we choose a status message (into statmsg),
255 ** and if it represents an error, we print it.
256 */
257
258 while ((i = wait(&st)) > 0 && i != pid)
259 continue;
260 if (i < 0)
261 {
262 syserr("wait");
263 return (-1);
264 }
265 if ((st & 0377) != 0)
266 {
267 syserr("%s: stat %o", pvp[0], st);
21ec078e 268 ExitStat = EX_UNAVAILABLE;
b3cbe40f
EA
269 return (-1);
270 }
271 i = (st >> 8) & 0377;
272 giveresponse(i, FALSE, m);
273 return (i);
274}
275\f/*
276** GIVERESPONSE -- Interpret an error response from a mailer
277**
278** Parameters:
279** stat -- the status code from the mailer (high byte
280** only; core dumps must have been taken care of
281** already).
282** force -- if set, force an error message output, even
283** if the mailer seems to like to print its own
284** messages.
285** m -- the mailer descriptor for this mailer.
286**
287** Returns:
288** none.
289**
290** Side Effects:
d916f0ca 291** Errors may be incremented.
b3cbe40f
EA
292** ExitStat may be set.
293**
b3cbe40f
EA
294** Called By:
295** deliver
b3cbe40f
EA
296*/
297
298giveresponse(stat, force, m)
299 int stat;
300 int force;
301 register struct mailer *m;
302{
303 register char *statmsg;
304 extern char *SysExMsg[];
305 register int i;
306 extern int N_SysEx;
307
308 i = stat - EX__BASE;
309 if (i < 0 || i > N_SysEx)
310 statmsg = NULL;
311 else
312 statmsg = SysExMsg[i];
313 if (stat == 0)
314 statmsg = "ok";
315 else
316 {
d916f0ca 317 Errors++;
b3cbe40f
EA
318 if (statmsg == NULL && m->m_badstat != 0)
319 {
320 stat = m->m_badstat;
321 i = stat - EX__BASE;
322# ifdef DEBUG
323 if (i < 0 || i >= N_SysEx)
324 syserr("Bad m_badstat %d", stat);
325 else
326# endif DEBUG
327 statmsg = SysExMsg[i];
328 }
329 if (statmsg == NULL)
330 usrerr("unknown mailer response %d", stat);
331 else if (force || !flagset(M_QUIET, m->m_flags))
332 usrerr("%s", statmsg);
333 }
334
335 /*
336 ** Final cleanup.
337 ** Log a record of the transaction. Compute the new
338 ** ExitStat -- if we already had an error, stick with
339 ** that.
340 */
341
342# ifdef LOG
343 if (statmsg == NULL)
344 logmsg(LOG_INFO, "%s->%s: error %d", From.q_paddr, To, stat);
345 else
346 logmsg(LOG_INFO, "%s->%s: %s", From.q_paddr, To, statmsg);
347# endif LOG
cb590f52 348 setstat(stat);
b3cbe40f
EA
349 return (stat);
350}
351\f/*
352** PUTHEADER -- insert the From header into some mail
353**
354** For mailers such as 'msgs' that want the header inserted
355** into the mail, this edit filter inserts the From line and
356** then passes the rest of the message through.
357**
358** Parameters:
359** fp -- the file pointer for the output.
360**
361** Returns:
362** none
363**
364** Side Effects:
365** Puts a "From" line in UNIX format, and then
366** outputs the rest of the message.
367**
b3cbe40f
EA
368** Called By:
369** deliver
b3cbe40f
EA
370*/
371
372putheader(fp)
373 register FILE *fp;
374{
375 char buf[MAXLINE + 1];
376 long tim;
377 extern char *ctime();
378
379 time(&tim);
380 fprintf(fp, "From %s %s", From.q_paddr, ctime(&tim));
381 while (fgets(buf, sizeof buf, stdin) != NULL && !ferror(fp))
382 fputs(buf, fp);
383 if (ferror(fp))
384 {
385 syserr("putheader: write error");
386 setstat(EX_IOERR);
387 }
388}
389\f/*
390** PIPESIG -- Handle broken pipe signals
391**
392** This just logs an error.
393**
394** Parameters:
395** none
396**
397** Returns:
398** none
399**
400** Side Effects:
401** logs an error message.
b3cbe40f
EA
402*/
403
404pipesig()
405{
406 syserr("Broken pipe");
407}
408\f/*
409** SENDTO -- Designate a send list.
410**
411** The parameter is a comma-separated list of people to send to.
412** This routine arranges to send to all of them.
413**
414** Parameters:
415** list -- the send list.
416** copyf -- the copy flag; passed to parse.
417**
418** Returns:
419** none
420**
421** Side Effects:
422** none.
423**
b3cbe40f
EA
424** Called By:
425** main
426** alias
b3cbe40f
EA
427*/
428
429sendto(list, copyf)
430 char *list;
431 int copyf;
432{
433 register char *p;
434 register char *q;
435 register char c;
436 addrq *a;
437 extern addrq *parse();
438 bool more;
439
440 /* more keeps track of what the previous delimiter was */
441 more = TRUE;
442 for (p = list; more; )
443 {
444 /* find the end of this address */
445 q = p;
446 while ((c = *p++) != '\0' && c != ',' && c != '\n')
447 continue;
448 more = c != '\0';
449 *--p = '\0';
450 if (more)
451 p++;
452
453 /* parse the address */
454 if ((a = parse(q, (addrq *) NULL, copyf)) == NULL)
455 continue;
456
457 /* arrange to send to this person */
458 recipient(a, &SendQ);
459 }
460 To = NULL;
461}
462\f/*
463** RECIPIENT -- Designate a message recipient
464**
465** Saves the named person for future mailing.
466**
467** Designates a person as a recipient. This routine
468** does the initial parsing, and checks to see if
469** this person has already received the mail.
470** It also supresses local network names and turns them into
471** local names.
472**
473** Parameters:
474** a -- the (preparsed) address header for the recipient.
475** targetq -- the queue to add the name to.
476**
477** Returns:
478** none.
479**
480** Side Effects:
481** none.
482**
b3cbe40f
EA
483** Called By:
484** sendto
485** main
b3cbe40f
EA
486*/
487
488recipient(a, targetq)
489 register addrq *a;
490 addrq *targetq;
491{
492 register addrq *q;
493 register struct mailer *m;
494 register char **pvp;
495 extern char *xalloc();
496 extern bool forward();
497 extern int errno;
498 extern bool sameaddr();
499
500 To = a->q_paddr;
501 m = a->q_mailer;
502 errno = 0;
503# ifdef DEBUG
504 if (Debug)
505 printf("recipient(%s)\n", To);
506# endif DEBUG
507
b3cbe40f
EA
508 /*
509 ** Look up this person in the recipient list. If they
510 ** are there already, return, otherwise continue.
511 */
512
513 if (!ForceMail)
514 {
515 for (q = &SendQ; (q = nxtinq(q)) != NULL; )
516 if (sameaddr(q, a, FALSE))
517 {
518# ifdef DEBUG
519 if (Debug)
520 printf("(%s in SendQ)\n", a->q_paddr);
521# endif DEBUG
522 return;
523 }
524 for (q = &AliasQ; (q = nxtinq(q)) != NULL; )
525 if (sameaddr(q, a, FALSE))
526 {
527# ifdef DEBUG
528 if (Debug)
529 printf("(%s in AliasQ)\n", a->q_paddr);
530# endif DEBUG
531 return;
532 }
533 }
534
535 /*
536 ** See if the user wants hir mail forwarded.
537 ** `Forward' must do the forwarding recursively.
538 */
539
540 if (m == &Mailer[0] && !NoAlias && targetq == &SendQ && forward(a))
541 return;
542
543 /*
544 ** Put the user onto the target queue.
545 */
546
547 if (targetq != NULL)
548 {
549 putonq(a, targetq);
550 }
551
552 return;
553}
554\f/*
555** BUILDARGV -- Build an argument vector for a mail server.
556**
557** Using a template defined in config.c, an argv is built.
558** The format of the template is already a vector. The
559** items of this vector are copied, unless a dollar sign
560** is encountered. In this case, the next character
561** specifies something else to copy in. These can be
562** $f The from address.
563** $h The host.
564** $u The user.
565** $c The hop count.
566** The vector is built in a local buffer. A pointer to
567** the static argv is returned.
568**
569** Parameters:
570** tmplt -- a template for an argument vector.
571** flags -- the flags for this server.
572** host -- the host name to send to.
573** user -- the user name to send to.
574** from -- the person this mail is from.
575**
576** Returns:
577** A pointer to an argv.
578**
579** Side Effects:
580** none
581**
582** WARNING:
583** Since the argv is staticly allocated, any subsequent
584** calls will clobber the old argv.
585**
b3cbe40f
EA
586** Called By:
587** deliver
b3cbe40f
EA
588*/
589
590char **
591buildargv(tmplt, flags, host, user, from)
592 char **tmplt;
593 int flags;
594 char *host;
595 char *user;
596 char *from;
597{
598 register char *p;
599 register char *q;
600 static char *pv[MAXPV+1];
601 char **pvp;
602 char **mvp;
603 static char buf[512];
604 register char *bp;
605 char pbuf[30];
606
607 /*
608 ** Do initial argv setup.
609 ** Insert the mailer name. Notice that $x expansion is
610 ** NOT done on the mailer name. Then, if the mailer has
611 ** a picky -f flag, we insert it as appropriate. This
612 ** code does not check for 'pv' overflow; this places a
613 ** manifest lower limit of 4 for MAXPV.
614 */
615
616 pvp = pv;
617 bp = buf;
618
619 *pvp++ = tmplt[0];
620
621 /* insert -f or -r flag as appropriate */
622 if (flagset(M_FOPT|M_ROPT, flags) && FromFlag)
623 {
624 if (flagset(M_FOPT, flags))
625 *pvp++ = "-f";
626 else
627 *pvp++ = "-r";
628 *pvp++ = From.q_paddr;
629 }
630
631 /*
632 ** Build the rest of argv.
633 ** For each prototype parameter, the prototype is
634 ** scanned character at a time. If a dollar-sign is
635 ** found, 'q' is set to the appropriate expansion,
636 ** otherwise it is null. Then either the string
637 ** pointed to by q, or the original character, is
638 ** interpolated into the buffer. Buffer overflow is
639 ** checked.
640 */
641
642 for (mvp = tmplt; (p = *++mvp) != NULL; )
643 {
644 if (pvp >= &pv[MAXPV])
645 {
646 syserr("Too many parameters to %s", pv[0]);
647 return (NULL);
648 }
649 *pvp++ = bp;
650 for (; *p != '\0'; p++)
651 {
652 /* q will be the interpolated quantity */
653 q = NULL;
654 if (*p == '$')
655 {
656 switch (*++p)
657 {
658 case 'f': /* from person */
659 q = from;
660 break;
661
662 case 'u': /* user */
663 q = user;
664 break;
665
666 case 'h': /* host */
667 q = host;
668 break;
669
670 case 'c': /* hop count */
671 sprintf(pbuf, "%d", HopCount);
672 q = pbuf;
673 break;
674 }
675 }
676
677 /*
678 ** Interpolate q or output one character
679 ** Strip quote bits as we proceed.....
680 */
681
682 if (q != NULL)
683 {
684 while (bp < &buf[sizeof buf - 1] && (*bp++ = *q++) != '\0')
685 continue;
686 bp--;
b3cbe40f
EA
687 }
688 else if (bp < &buf[sizeof buf - 1])
689 *bp++ = *p;
690 }
691 *bp++ = '\0';
692 if (bp >= &buf[sizeof buf - 1])
693 return (NULL);
694 }
695 *pvp = NULL;
696
697# ifdef DEBUG
698 if (Debug)
699 {
700 printf("Interpolated argv is:\n");
701 for (mvp = pv; *mvp != NULL; mvp++)
702 printf("\t%s\n", *mvp);
703 }
704# endif DEBUG
705
706 return (pv);
707}
708\f/*
709** MAILFILE -- Send a message to a file.
710**
711** Parameters:
712** filename -- the name of the file to send to.
713**
714** Returns:
715** The exit code associated with the operation.
716**
717** Side Effects:
718** none.
719**
b3cbe40f
EA
720** Called By:
721** deliver
b3cbe40f
EA
722*/
723
724mailfile(filename)
725 char *filename;
726{
727 char buf[MAXLINE];
728 register FILE *f;
729 auto long tim;
730 extern char *ctime();
731
732 f = fopen(filename, "a");
733 if (f == NULL)
734 return (EX_CANTCREAT);
735
736 /* output the timestamp */
737 time(&tim);
738 fprintf(f, "From %s %s", From.q_paddr, ctime(&tim));
739 rewind(stdin);
740 while (fgets(buf, sizeof buf, stdin) != NULL)
741 {
742 fputs(buf, f);
743 if (ferror(f))
744 {
745 fclose(f);
746 return (EX_IOERR);
747 }
748 }
749 fputs("\n", f);
750 fclose(f);
751 return (EX_OK);
752}