implement arpatounix; log more info; allow nested $?...$|...$.;
[unix-history] / usr / src / usr.sbin / sendmail / src / main.c
CommitLineData
aeb2545d 1/*
dc45ba8c 2 * Copyright (c) 1983 Eric P. Allman
bee79b64
KB
3 * Copyright (c) 1988 Regents of the University of California.
4 * All rights reserved.
5 *
417f7a11 6 * %sccs.include.redist.c%
bee79b64 7 */
aeb2545d
DF
8
9#ifndef lint
10char copyright[] =
bee79b64 11"@(#) Copyright (c) 1988 Regents of the University of California.\n\
aeb2545d 12 All rights reserved.\n";
bee79b64 13#endif /* not lint */
aeb2545d
DF
14
15#ifndef lint
1627a785 16static char sccsid[] = "@(#)main.c 6.7 (Berkeley) %G%";
bee79b64 17#endif /* not lint */
aeb2545d 18
137c2d1f
KB
19#define _DEFINE
20
1627a785 21#include <unistd.h>
5bec1980 22#include <sys/file.h>
f2e44ded 23#include <sys/stat.h>
137c2d1f
KB
24#include <signal.h>
25#include <sgtty.h>
26#include "sendmail.h"
27#include <arpa/nameser.h>
28#include <resolv.h>
b3cbe40f 29
17a67c62 30# ifdef lint
2e3062fe 31char edata, end;
17a67c62
EA
32# endif lint
33
b3cbe40f 34/*
96faada8 35** SENDMAIL -- Post mail to a set of destinations.
b3cbe40f
EA
36**
37** This is the basic mail router. All user mail programs should
96faada8 38** call this routine to actually deliver mail. Sendmail in
b3cbe40f
EA
39** turn calls a bunch of mail servers that do the real work of
40** delivering the mail.
41**
3199e4f3
EA
42** Sendmail is driven by tables read in from /usr/lib/sendmail.cf
43** (read by readcf.c). Some more static configuration info,
44** including some code that you may want to tailor for your
45** installation, is in conf.c. You may also want to touch
46** daemon.c (if you have some other IPC mechanism), acct.c
47** (to change your accounting), names.c (to adjust the name
48** server mechanism).
b3cbe40f
EA
49**
50** Usage:
7338e3d4 51** /usr/lib/sendmail [flags] addr ...
b3cbe40f 52**
7338e3d4 53** See the associated documentation for details.
b3cbe40f 54**
b3cbe40f 55** Author:
7338e3d4
EA
56** Eric Allman, UCB/INGRES (until 10/81)
57** Britton-Lee, Inc., purveyors of fine
58** database computers (from 11/81)
ce46a48a 59** Now back at UCB at the Mammoth project.
7338e3d4
EA
60** The support of the INGRES Project and Britton-Lee is
61** gratefully acknowledged. Britton-Lee in
62** particular had absolutely nothing to gain from
63** my involvement in this project.
b3cbe40f
EA
64*/
65
66
83f674d8 67int NextMailer; /* "free" index into Mailer struct */
912acb74 68char *FullName; /* sender's full name */
be2fcca9 69ENVELOPE BlankEnvelope; /* a "blank" envelope */
2654b031 70ENVELOPE MainEnvelope; /* the envelope around the basic letter */
2e3062fe 71ADDRESS NullAddress = /* a null address */
599d88d0 72 { "", "", NULL, "" };
2e3062fe
EA
73
74/*
75** Pointers for setproctitle.
76** This allows "ps" listings to give more useful information.
77** These must be kept out of BSS for frozen configuration files
78** to work.
79*/
80
81# ifdef SETPROCTITLE
82char **Argv = NULL; /* pointer to argument vector */
83char *LastArgv = NULL; /* end of argv */
f3d8f6d6 84# endif /* SETPROCTITLE */
b3cbe40f 85
ce46a48a
EA
86/*
87** The file in which to log raw recipient information.
88** This is logged before aliasing, forwarding, and so forth so we
89** can see how our addresses are being used. For example, this
90** would give us the names of aliases (instead of what they alias
91** to), the pre-MX hostnames, and so forth.
92**
93** This is specified on the command line, not in the config file,
94** and is therefore really only useful for logging SMTP RCPTs.
95*/
96
97char *RcptLogFile = NULL; /* file name */
98
f96fa7de
EA
99static void obsolete();
100
4aebfe5d
EA
101#ifdef DAEMON
102#ifndef SMTP
103ERROR %%%% Cannot have daemon mode without SMTP %%%% ERROR
f3d8f6d6
EA
104#endif /* SMTP */
105#endif /* DAEMON */
4aebfe5d 106
2281a453
EA
107#define MAXCONFIGLEVEL 3 /* highest config version level known */
108
e36705df 109main(argc, argv, envp)
b3cbe40f
EA
110 int argc;
111 char **argv;
e36705df 112 char **envp;
b3cbe40f
EA
113{
114 register char *p;
af04f6e4 115 register char *q;
acae5a9d 116 char **av;
17df0fcb 117 char *locname;
b3cbe40f 118 extern int finis();
b3cbe40f 119 extern char Version[];
b2d4b27a 120 char *ep, *from;
b3cbe40f 121 typedef int (*fnptr)();
be2fcca9 122 STAB *st;
9e3c0a28 123 register int i;
d0a69620 124 int j;
4388720d 125 bool readconfig = TRUE;
aba51985 126 bool queuemode = FALSE; /* process queue requests */
43e0af62 127 bool nothaw;
66391bf0 128 bool safecf = TRUE;
be2fcca9 129 static bool reenter = FALSE;
4b26318e 130 char *argv0 = argv[0];
b2d4b27a 131 char jbuf[MAXHOSTNAMELEN]; /* holds MyHostName */
f96fa7de
EA
132 extern int DtableSize;
133 extern int optind;
f6a0cc15 134 extern bool safefile();
ed45aae1 135 extern time_t convtime();
dd1fe05b 136 extern putheader(), putbody();
be2fcca9 137 extern ENVELOPE *newenvelope();
0df908a9 138 extern void intsig();
1dbda134 139 extern char **myhostname();
22e6d6b8 140 extern char *arpadate();
f96fa7de 141 extern char *optarg;
b72377c6 142
22659072
EA
143 /*
144 ** Check to see if we reentered.
145 ** This would normally happen if e_putheader or e_putbody
146 ** were NULL when invoked.
147 */
148
b72377c6
EA
149 if (reenter)
150 {
151 syserr("main: reentered!");
152 abort();
153 }
154 reenter = TRUE;
abae7b2d 155 extern ADDRESS *recipient();
17df0fcb 156 bool canrename;
b3cbe40f 157
07c63e56 158#ifndef SYS5TZ
140717b5
EA
159 /* enforce use of kernel-supplied time zone information */
160 unsetenv("TZ");
161#endif
60c0b7b2 162
3eb4fac4
EA
163 /* in 4.4BSD, the table can be huge; impose a reasonable limit */
164 DtableSize = getdtablesize();
165 if (DtableSize > 256)
166 DtableSize = 256;
167
d15bd559
EA
168 /*
169 ** Be sure we have enough file descriptors.
59b65d0f 170 ** But also be sure that 0, 1, & 2 are open.
d15bd559
EA
171 */
172
e1098041 173 i = open("/dev/null", O_RDWR);
59b65d0f
EA
174 while (i >= 0 && i < 2)
175 i = dup(i);
66391bf0 176
3eb4fac4 177 i = DtableSize;
1627a785
EA
178 while (--i > 0)
179 {
180 if (i != STDIN_FILENO && i != STDOUT_FILENO && i != STDERR_FILENO)
181 (void) close(i);
182 }
d15bd559
EA
183 errno = 0;
184
07adc4f5
RA
185#ifdef LOG_MAIL
186 openlog("sendmail", LOG_PID, LOG_MAIL);
187#else
188 openlog("sendmail", LOG_PID);
189#endif
190
8ff78b51
EA
191 /*
192 ** Set default values for variables.
193 ** These cannot be in initialized data space.
194 */
195
196 setdefaults();
197
bb09c502
EA
198 /* set up the blank envelope */
199 BlankEnvelope.e_puthdr = putheader;
200 BlankEnvelope.e_putbody = putbody;
201 BlankEnvelope.e_xfp = NULL;
2e3062fe 202 STRUCTCOPY(NullAddress, BlankEnvelope.e_from);
bd575ea9
EA
203 STRUCTCOPY(BlankEnvelope, MainEnvelope);
204 CurEnv = &MainEnvelope;
bb09c502 205
b2d4b27a
KB
206 /* Handle any non-getoptable constructions. */
207 obsolete(argv);
208
22659072
EA
209 /*
210 ** Do a quick prescan of the argument list.
211 ** We do this to find out if we can potentially thaw the
212 ** configuration file. If not, we do the thaw now so that
213 ** the argument processing applies to this run rather than
214 ** to the run that froze the configuration.
215 */
43e0af62 216 nothaw = FALSE;
a0431b8d
EA
217#define OPTIONS "b:C:cd:eF:f:h:Iimno:p:q:R:r:sTtv"
218 while ((j = getopt(argc, argv, OPTIONS)) != EOF)
f96fa7de
EA
219 {
220 switch (j)
560a80d9 221 {
b2d4b27a
KB
222 case 'b':
223 if (optarg[0] == 'z' && optarg[1] == '\0')
224 nothaw = TRUE;
225 break;
226
227 case 'C':
228 ConfFile = optarg;
0e306e7f
EA
229 (void) setgid(getrgid());
230 (void) setuid(getruid());
66391bf0 231 safecf = FALSE;
43e0af62 232 nothaw = TRUE;
b2d4b27a
KB
233 break;
234
235 case 'd':
43e0af62 236 tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
b2d4b27a 237 tTflag(optarg);
43e0af62
EA
238 setbuf(stdout, (char *) NULL);
239 printf("Version %s\n", Version);
b2d4b27a 240 break;
43e0af62 241 }
f96fa7de 242 }
2bc15c4e
KB
243
244 InChannel = stdin;
245 OutChannel = stdout;
246
43e0af62 247 if (!nothaw)
4b26318e 248 readconfig = !thaw(FreezeFile, argv0);
22659072 249
42bbf376
EA
250# ifdef SETPROCTITLE
251 /*
252 ** Save start and extent of argv for setproctitle.
253 */
254
255 Argv = argv;
3251a23d 256 if (--i > 0)
577bce94
EA
257 LastArgv = envp[i - 1] + strlen(envp[i - 1]);
258 else
259 LastArgv = argv[argc - 1] + strlen(argv[argc - 1]);
f3d8f6d6 260# endif /* SETPROCTITLE */
e36705df 261
b3cbe40f 262 if (signal(SIGINT, SIG_IGN) != SIG_IGN)
6e2f38be 263 (void) signal(SIGINT, intsig);
eb211e2c 264 if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
6e2f38be
EA
265 (void) signal(SIGHUP, intsig);
266 (void) signal(SIGTERM, intsig);
ed7382d3 267 (void) signal(SIGPIPE, SIG_IGN);
a0554f81 268 OldUmask = umask(0);
75f95954 269 OpMode = MD_DELIVER;
e673aad7 270 MotherPid = getpid();
561c7c50 271 FullName = getenv("NAME");
dd1fe05b 272
b3cbe40f
EA
273 errno = 0;
274 from = NULL;
378e8da7 275
4388720d 276 if (readconfig)
1dbda134 277 {
4388720d
EA
278 /* initialize some macros, etc. */
279 initmacros();
280
281 /* hostname */
282 av = myhostname(jbuf, sizeof jbuf);
283 if (jbuf[0] != '\0')
284 {
dec0d30e
EA
285 extern char *strchr();
286
43e0af62
EA
287 if (tTd(0, 4))
288 printf("canonical name: %s\n", jbuf);
4388720d
EA
289 p = newstr(jbuf);
290 define('w', p, CurEnv);
dec0d30e
EA
291
292 q = strchr(jbuf, '.');
293 if (q != NULL)
294 {
1bc035b0 295 *q++ = '\0';
dec0d30e 296 p = newstr(jbuf);
1bc035b0 297 define('m', q, CurEnv);
dec0d30e 298 }
4388720d
EA
299 setclass('w', p);
300 }
301 while (av != NULL && *av != NULL)
43e0af62 302 {
43e0af62
EA
303 if (tTd(0, 4))
304 printf("\ta.k.a.: %s\n", *av);
4388720d 305 setclass('w', *av++);
43e0af62 306 }
4388720d
EA
307
308 /* version */
309 define('v', Version, CurEnv);
1dbda134 310 }
378e8da7
EA
311
312 /* current time */
22e6d6b8 313 define('b', arpadate((char *) NULL), CurEnv);
378e8da7 314
a691a4a6 315 /*
c1e24818 316 ** Crack argv.
a691a4a6
EA
317 */
318
acae5a9d 319 av = argv;
f3d8f6d6 320 p = strrchr(*av, '/');
26a3626c
EA
321 if (p++ == NULL)
322 p = *av;
323 if (strcmp(p, "newaliases") == 0)
75f95954 324 OpMode = MD_INITALIAS;
26a3626c 325 else if (strcmp(p, "mailq") == 0)
75f95954 326 OpMode = MD_PRINT;
34fe0a9b
EA
327 else if (strcmp(p, "smtpd") == 0)
328 OpMode = MD_DAEMON;
b2d4b27a
KB
329
330 optind = 1;
a0431b8d 331 while ((j = getopt(argc, argv, OPTIONS)) != EOF)
f96fa7de
EA
332 {
333 switch (j)
a691a4a6 334 {
75f95954 335 case 'b': /* operations mode */
f96fa7de 336 switch (j = *optarg)
c1e24818 337 {
75f95954 338 case MD_DAEMON:
908dc8db
MK
339# ifdef DAEMON
340 if (getuid() != 0) {
341 usrerr("Permission denied");
342 exit (EX_USAGE);
343 }
344 (void) unsetenv("HOSTALIASES");
345# else
2e15a2d8
MK
346 usrerr("Daemon mode not implemented");
347 ExitStat = EX_USAGE;
75f95954 348 break;
f3d8f6d6 349# endif /* DAEMON */
75f95954
EA
350 case MD_SMTP:
351# ifndef SMTP
2e15a2d8
MK
352 usrerr("I don't speak SMTP");
353 ExitStat = EX_USAGE;
75f95954 354 break;
f3d8f6d6 355# endif /* SMTP */
75f95954
EA
356 case MD_DELIVER:
357 case MD_VERIFY:
358 case MD_TEST:
359 case MD_INITALIAS:
360 case MD_PRINT:
361 case MD_FREEZE:
b2d4b27a 362 OpMode = j;
75f95954
EA
363 break;
364
365 default:
b2d4b27a 366 usrerr("Invalid operation mode %c", j);
2e15a2d8 367 ExitStat = EX_USAGE;
75f95954 368 break;
c1e24818
EA
369 }
370 break;
371
560a80d9 372 case 'C': /* select configuration file (already done) */
c1e24818 373 break;
a691a4a6 374
43e0af62 375 case 'd': /* debugging -- redo in case frozen */
c1e24818 376 tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
b2d4b27a 377 tTflag(optarg);
c1e24818 378 setbuf(stdout, (char *) NULL);
c1e24818 379 break;
b3cbe40f 380
b3cbe40f 381 case 'f': /* from address */
c1e24818 382 case 'r': /* obsolete -f flag */
22659072 383 if (from != NULL)
b3cbe40f 384 {
2e15a2d8
MK
385 usrerr("More than one \"from\" person");
386 ExitStat = EX_USAGE;
b3cbe40f
EA
387 break;
388 }
b2d4b27a 389 from = newstr(optarg);
b3cbe40f
EA
390 break;
391
6da7b890 392 case 'F': /* set full name */
b2d4b27a 393 FullName = newstr(optarg);
6da7b890
EA
394 break;
395
b3cbe40f 396 case 'h': /* hop count */
b2d4b27a
KB
397 CurEnv->e_hopcount = strtol(optarg, &ep, 10);
398 if (*ep)
b3cbe40f 399 {
b2d4b27a 400 usrerr("Bad hop count (%s)", optarg);
2e15a2d8 401 ExitStat = EX_USAGE;
3110074f 402 break;
b3cbe40f 403 }
b3cbe40f 404 break;
b3cbe40f 405
c1e24818
EA
406 case 'n': /* don't alias */
407 NoAlias = TRUE;
d59b067a
EA
408 break;
409
c1e24818 410 case 'o': /* set option */
b2d4b27a 411 setoption(*optarg, optarg + 1, FALSE, TRUE);
14a39063 412 break;
cbdb7357 413
b1e36c23 414 case 'p': /* set protocol */
b2d4b27a 415 q = strchr(optarg, ':');
af04f6e4
EA
416 if (q != NULL)
417 *q++ = '\0';
b2d4b27a
KB
418 if (*optarg != '\0')
419 define('r', newstr(optarg), CurEnv);
af04f6e4
EA
420 if (*q != '\0')
421 define('s', newstr(q), CurEnv);
b1e36c23
EA
422 break;
423
ed45aae1 424 case 'q': /* run queue files at intervals */
884a20cb 425# ifdef QUEUE
f2e44ded
EA
426 if (getuid() != 0)
427 {
428 struct stat stbuf;
429
430 /* check to see if we own the queue directory */
431 if (stat(QueueDir, &stbuf) < 0)
432 syserr("main: cannot stat %s", QueueDir);
433 if (stbuf.st_uid != getuid())
434 {
435 /* nope, really a botch */
436 usrerr("Permission denied");
437 exit (EX_NOPERM);
438 }
908dc8db
MK
439 }
440 (void) unsetenv("HOSTALIASES");
d0a69620 441 FullName = NULL;
aba51985 442 queuemode = TRUE;
b2d4b27a 443 QueueIntvl = convtime(optarg);
f3d8f6d6 444# else /* QUEUE */
2e15a2d8
MK
445 usrerr("I don't know about queues");
446 ExitStat = EX_USAGE;
f3d8f6d6 447# endif /* QUEUE */
ed45aae1
EA
448 break;
449
c1e24818
EA
450 case 't': /* read recipients from message */
451 GrabTo = TRUE;
452 break;
453
454 /* compatibility flags */
c1e24818
EA
455 case 'c': /* connect to non-local mailers */
456 case 'e': /* error message disposition */
457 case 'i': /* don't let dot stop me */
458 case 'm': /* send to me too */
459 case 'T': /* set timeout interval */
460 case 'v': /* give blow-by-blow description */
a0431b8d 461 setoption(j, "T", FALSE, TRUE);
35cc3fad
EA
462 break;
463
c1e24818 464 case 's': /* save From lines in headers */
a0431b8d 465 setoption('f', "T", FALSE, TRUE);
b3cbe40f 466 break;
26a3626c
EA
467
468# ifdef DBM
469 case 'I': /* initialize alias DBM file */
75f95954 470 OpMode = MD_INITALIAS;
26a3626c 471 break;
ce46a48a
EA
472# endif /* DBM */
473
474 case 'R': /* log raw recipient info */
b2d4b27a
KB
475 RcptLogFile = newstr(optarg);
476 break;
477
478 default:
479 ExitStat = EX_USAGE;
480 finis();
ce46a48a 481 break;
b3cbe40f 482 }
f96fa7de 483 }
b2d4b27a 484 av += optind;
b3cbe40f 485
836a1487
EA
486#ifdef NAMED_BIND
487 if (tTd(8, 1))
488 _res.options |= RES_DEBUG;
489#endif
490
9e3c0a28 491 /*
2cce0c26
EA
492 ** Do basic initialization.
493 ** Read system control file.
179c1218 494 ** Extract special fields for local use.
9e3c0a28
EA
495 */
496
46f6ec52 497 if (OpMode == MD_FREEZE || readconfig)
a4076aed 498 readcf(ConfFile, safecf, CurEnv);
22659072 499
07c63e56 500#ifdef SYS5TZ
ca4d0c0b
EA
501 /* Enforce use of local time (null string overrides this) */
502 if (TimeZoneSpec == NULL)
503 unsetenv("TZ");
504 else if (TimeZoneSpec[0] != '\0')
505 {
506 p = xalloc(strlen(TimeZoneSpec) + 4);
507 (void) strcpy(p, "TZ=");
508 (void) strcat(p, TimeZoneSpec);
509 putenv(p);
510 }
511#endif
512
2281a453
EA
513 if (ConfigLevel > MAXCONFIGLEVEL)
514 {
515 syserr("Warning: .cf version level (%d) exceeds program functionality (%d)",
516 ConfigLevel, MAXCONFIGLEVEL);
517 }
75f95954 518 switch (OpMode)
acae5a9d 519 {
26a3626c 520 case MD_FREEZE:
a9621daf 521 /* this is critical to avoid forgeries of the frozen config */
0e306e7f
EA
522 (void) setgid(getgid());
523 (void) setuid(getuid());
a9621daf
EA
524
525 /* freeze the configuration */
8fe4fb9b 526 freeze(FreezeFile);
acae5a9d 527 exit(EX_OK);
26a3626c
EA
528
529 case MD_INITALIAS:
530 Verbose = TRUE;
531 break;
13dfebea
EA
532
533 case MD_DAEMON:
534 /* remove things that don't make sense in daemon mode */
535 FullName = NULL;
536 break;
acae5a9d 537 }
179c1218 538
6130649c
EA
539 /* do heuristic mode adjustment */
540 if (Verbose)
75f95954
EA
541 {
542 /* turn off noconnect option */
543 setoption('c', "F", TRUE, FALSE);
544
545 /* turn on interactive delivery */
546 setoption('d', "", TRUE, FALSE);
547 }
6130649c 548
179c1218 549 /* our name for SMTP codes */
a73ae8ac 550 expand("\001j", jbuf, &jbuf[sizeof jbuf - 1], CurEnv);
57c97d4a 551 MyHostName = jbuf;
d6a28dd8 552
98e5062b 553 /* the indices of built-in mailers */
179c1218
EA
554 st = stab("local", ST_MAILER, ST_FIND);
555 if (st == NULL)
556 syserr("No local mailer defined");
557 else
558 LocalMailer = st->s_mailer;
98e5062b 559
179c1218
EA
560 st = stab("prog", ST_MAILER, ST_FIND);
561 if (st == NULL)
562 syserr("No prog mailer defined");
563 else
564 ProgMailer = st->s_mailer;
565
98e5062b
EA
566 st = stab("*file*", ST_MAILER, ST_FIND);
567 if (st == NULL)
568 syserr("No *file* mailer defined");
569 else
570 FileMailer = st->s_mailer;
571
572 st = stab("*include*", ST_MAILER, ST_FIND);
573 if (st == NULL)
574 syserr("No *include* mailer defined");
575 else
576 InclMailer = st->s_mailer;
577
578
6bbaf971
EA
579 /* operate in queue directory */
580 if (chdir(QueueDir) < 0)
581 {
582 syserr("cannot chdir(%s)", QueueDir);
583 exit(EX_SOFTWARE);
584 }
585
64912e7e 586 /*
55f0da62 587 ** Do operation-mode-dependent initialization.
64912e7e
EA
588 */
589
55f0da62 590 switch (OpMode)
64912e7e 591 {
55f0da62
EA
592 case MD_PRINT:
593 /* print the queue */
74f37936 594#ifdef QUEUE
64912e7e
EA
595 dropenvelope(CurEnv);
596 printqueue();
597 exit(EX_OK);
f3d8f6d6 598#else /* QUEUE */
74f37936
EA
599 usrerr("No queue to print");
600 finis();
f3d8f6d6 601#endif /* QUEUE */
8acb5142 602
55f0da62
EA
603 case MD_INITALIAS:
604 /* initialize alias database */
a4076aed 605 initaliases(AliasFile, TRUE, CurEnv);
f4dbf345 606 exit(EX_OK);
cdb17311 607
55f0da62
EA
608 case MD_DAEMON:
609 /* don't open alias database -- done in srvrsmtp */
610 break;
611
612 default:
613 /* open the alias database */
a4076aed 614 initaliases(AliasFile, FALSE, CurEnv);
55f0da62
EA
615 break;
616 }
617
9678c96d 618 if (tTd(0, 15))
9c6d4c70 619 {
f6a0cc15 620 /* print configuration table (or at least part of it) */
9c6d4c70
EA
621 printrules();
622 for (i = 0; i < MAXMAILERS; i++)
623 {
624 register struct mailer *m = Mailer[i];
1dbda134 625 int j;
9c6d4c70
EA
626
627 if (m == NULL)
628 continue;
97ad25b6
EA
629 printf("mailer %d (%s): P=%s S=%d R=%d M=%ld F=", i, m->m_name,
630 m->m_mailer, m->m_s_rwset, m->m_r_rwset,
631 m->m_maxsize);
1dbda134
EA
632 for (j = '\0'; j <= '\177'; j++)
633 if (bitnset(j, m->m_flags))
0e306e7f 634 (void) putchar(j);
1dbda134 635 printf(" E=");
b3ef02a2 636 xputs(m->m_eol);
72f91c2e
EA
637 if (m->m_argv != NULL)
638 {
639 char **a = m->m_argv;
640
641 printf(" A=");
642 while (*a != NULL)
643 {
644 if (a != m->m_argv)
645 printf(" ");
646 xputs(*a++);
647 }
648 }
b3ef02a2 649 printf("\n");
9c6d4c70
EA
650 }
651 }
9c6d4c70 652
be2fcca9
EA
653 /*
654 ** Switch to the main envelope.
655 */
656
657 CurEnv = newenvelope(&MainEnvelope);
e6f08ab1 658 MainEnvelope.e_flags = BlankEnvelope.e_flags;
be2fcca9 659
cf69a203
EA
660 /*
661 ** If test mode, read addresses from stdin and process.
662 */
663
75f95954 664 if (OpMode == MD_TEST)
cf69a203 665 {
dab2b390 666 bool terminal = isatty(fileno(stdin));
cf69a203
EA
667 char buf[MAXLINE];
668
dab2b390
EA
669 if (terminal)
670 {
671 printf("ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)\n");
672 printf("Enter <ruleset> <address>\n");
673 }
cf69a203
EA
674 for (;;)
675 {
676 register char **pvp;
50435450 677 char *q;
50435450 678 extern char *DelimChar;
2ee7e9e0 679 extern bool invalidaddr();
cf69a203 680
dab2b390
EA
681 if (terminal)
682 printf("> ");
0e306e7f 683 (void) fflush(stdout);
cf69a203
EA
684 if (fgets(buf, sizeof buf, stdin) == NULL)
685 finis();
dab2b390
EA
686 if (!terminal)
687 printf("> %s", buf);
688 if (buf[0] == '#')
689 continue;
f90b4150 690 for (p = buf; isspace(*p); p++)
cf69a203 691 continue;
ecfd2c8e
EA
692 q = p;
693 while (*p != '\0' && !isspace(*p))
694 p++;
cf69a203 695 if (*p == '\0')
2ee7e9e0
EA
696 {
697 printf("No address!\n");
cf69a203 698 continue;
2ee7e9e0 699 }
50435450 700 *p = '\0';
2ee7e9e0 701 if (invalidaddr(p + 1))
2ee7e9e0 702 continue;
50435450 703 do
ecfd2c8e 704 {
217a0102
EA
705 extern char **prescan();
706 char pvpbuf[PSBUFSIZE];
707
708 pvp = prescan(++p, ',', pvpbuf);
50435450 709 if (pvp == NULL)
ecfd2c8e 710 continue;
50435450
EA
711 p = q;
712 while (*p != '\0')
713 {
714 rewrite(pvp, atoi(p));
715 while (*p != '\0' && *p++ != ',')
716 continue;
717 }
718 } while (*(p = DelimChar) != '\0');
cf69a203
EA
719 }
720 }
721
e3cd595c
EA
722# ifdef QUEUE
723 /*
724 ** If collecting stuff from the queue, go start doing that.
725 */
726
7b21425b 727 if (queuemode && OpMode != MD_DAEMON && QueueIntvl == 0)
e3cd595c 728 {
ca4d0c0b 729 runqueue(FALSE);
e3cd595c
EA
730 finis();
731 }
f3d8f6d6 732# endif /* QUEUE */
e3cd595c 733
f6a0cc15
EA
734 /*
735 ** If a daemon, wait for a request.
736 ** getrequests will always return in a child.
25b9d645 737 ** If we should also be processing the queue, start
19147b2d
EA
738 ** doing it in background.
739 ** We check for any errors that might have happened
740 ** during startup.
f6a0cc15
EA
741 */
742
75f95954 743 if (OpMode == MD_DAEMON || QueueIntvl != 0)
25b9d645 744 {
9678c96d 745 if (!tTd(0, 1))
58b27aa4 746 {
fcdf8200 747 /* put us in background */
58b27aa4
EA
748 i = fork();
749 if (i < 0)
750 syserr("daemon: cannot fork");
751 if (i != 0)
752 exit(0);
fcdf8200
EA
753
754 /* get our pid right */
d9162460 755 MotherPid = getpid();
fcdf8200
EA
756
757 /* disconnect from our controlling tty */
d188728a 758 disconnect(TRUE);
58b27aa4 759 }
7338e3d4 760
25b9d645
EA
761# ifdef QUEUE
762 if (queuemode)
f309127e 763 {
ca4d0c0b 764 runqueue(TRUE);
75f95954 765 if (OpMode != MD_DAEMON)
f309127e
EA
766 for (;;)
767 pause();
768 }
f3d8f6d6 769# endif /* QUEUE */
7338e3d4
EA
770 dropenvelope(CurEnv);
771
772#ifdef DAEMON
f6a0cc15 773 getrequests();
2a16bae3
EA
774
775 /* at this point we are in a child: reset state */
75f95954 776 OpMode = MD_SMTP;
7338e3d4 777 (void) newenvelope(CurEnv);
912acb74 778 openxscript(CurEnv);
f3d8f6d6 779#endif /* DAEMON */
7338e3d4 780 }
88039044
EA
781
782# ifdef SMTP
783 /*
784 ** If running SMTP protocol, start collecting and executing
785 ** commands. This will never return.
786 */
787
75f95954 788 if (OpMode == MD_SMTP)
a4076aed 789 smtp(CurEnv);
f3d8f6d6 790# endif /* SMTP */
88039044 791
f6a0cc15 792 /*
e6f08ab1 793 ** Do basic system initialization and set the sender
f6a0cc15
EA
794 */
795
a4076aed 796 initsys(CurEnv);
b5958391 797 setsender(from, CurEnv);
a9e0e597 798
0e1aa71e 799 if (*av == NULL && !GrabTo)
e863b1fa 800 {
2e3062fe
EA
801 usrerr("Recipient names must be specified");
802
803 /* collect body for UUCP return */
804 if (OpMode != MD_VERIFY)
a4076aed 805 collect(FALSE, CurEnv);
e863b1fa
EA
806 finis();
807 }
75f95954
EA
808 if (OpMode == MD_VERIFY)
809 SendMode = SM_VERIFY;
b3cbe40f 810
b3cbe40f 811 /*
d6b27179 812 ** Scan argv and deliver the message to everyone.
b3cbe40f
EA
813 */
814
a4076aed 815 sendtoargv(av, CurEnv);
b3cbe40f 816
72e9b3cc 817 /* if we have had errors sofar, arrange a meaningful exit stat */
d916f0ca 818 if (Errors > 0 && ExitStat == EX_OK)
a4b004a6 819 ExitStat = EX_USAGE;
a4b004a6 820
dc39c568
EA
821 /*
822 ** Read the input mail.
823 */
824
2654b031 825 CurEnv->e_to = NULL;
75f95954 826 if (OpMode != MD_VERIFY || GrabTo)
a4076aed 827 collect(FALSE, CurEnv);
d829793b 828 errno = 0;
35cc3fad 829
4e1f4d4b 830 /* collect statistics */
7338e3d4
EA
831 if (OpMode != MD_VERIFY)
832 markstats(CurEnv, (ADDRESS *) NULL);
b3cbe40f 833
9678c96d 834 if (tTd(1, 1))
2654b031 835 printf("From person = \"%s\"\n", CurEnv->e_from.q_paddr);
d6b27179 836
b3cbe40f
EA
837 /*
838 ** Actually send everything.
d6b27179 839 ** If verifying, just ack.
b3cbe40f
EA
840 */
841
7338e3d4
EA
842 CurEnv->e_from.q_flags |= QDONTSEND;
843 CurEnv->e_to = NULL;
f7e74083 844 sendall(CurEnv, SM_DEFAULT);
b3cbe40f
EA
845
846 /*
847 ** All done.
848 */
849
850 finis();
851}
852\f/*
853** FINIS -- Clean up and exit.
854**
b3cbe40f
EA
855** Parameters:
856** none
857**
858** Returns:
859** never
860**
861** Side Effects:
96faada8 862** exits sendmail
b3cbe40f
EA
863*/
864
865finis()
866{
9678c96d 867 if (tTd(2, 1))
e6f08ab1 868 printf("\n====finis: stat %d e_flags %o\n", ExitStat, CurEnv->e_flags);
aba51985 869
7338e3d4 870 /* clean up temp files */
912acb74 871 CurEnv->e_to = NULL;
e6f08ab1 872 dropenvelope(CurEnv);
b3cbe40f 873
f2e44ded
EA
874 /* flush any cached connections */
875 mci_flush();
876
7338e3d4
EA
877 /* post statistics */
878 poststats(StatFile);
68f0b54c 879
7338e3d4 880 /* and exit */
36a4e219
EA
881# ifdef LOG
882 if (LogLevel > 11)
883 syslog(LOG_DEBUG, "finis, pid=%d", getpid());
f3d8f6d6 884# endif /* LOG */
c8ec8736
EA
885 if (ExitStat == EX_TEMPFAIL)
886 ExitStat = EX_OK;
b3cbe40f
EA
887 exit(ExitStat);
888}
889\f/*
6e2f38be
EA
890** INTSIG -- clean up on interrupt
891**
7338e3d4
EA
892** This just arranges to exit. It pessimises in that it
893** may resend a message.
6e2f38be
EA
894**
895** Parameters:
896** none.
897**
898** Returns:
899** none.
900**
901** Side Effects:
7338e3d4 902** Unlocks the current job.
6e2f38be
EA
903*/
904
0df908a9 905void
6e2f38be
EA
906intsig()
907{
7338e3d4
EA
908 FileName = NULL;
909 unlockqueue(CurEnv);
910 exit(EX_OK);
6e2f38be
EA
911}
912\f/*
721fad23
EA
913** INITMACROS -- initialize the macro system
914**
915** This just involves defining some macros that are actually
916** used internally as metasymbols to be themselves.
917**
918** Parameters:
919** none.
920**
921** Returns:
922** none.
923**
924** Side Effects:
925** initializes several macros to be themselves.
926*/
927
9dbc8d99
EA
928struct metamac MetaMacros[] =
929{
eca244ca 930 /* LHS pattern matching characters */
42fa5d67
EA
931 '*', MATCHZANY, '+', MATCHANY, '-', MATCHONE,
932 '=', MATCHCLASS, '~', MATCHNCLASS,
721fad23
EA
933
934 /* these are RHS metasymbols */
42fa5d67
EA
935 '#', CANONNET, '@', CANONHOST, ':', CANONUSER,
936 '>', CALLSUBR,
41173b8f 937 '{', MATCHLOOKUP, '}', MATCHELOOKUP,
721fad23 938
eca244ca 939 /* the conditional operations */
42fa5d67 940 '?', CONDIF, '|', CONDELSE, '.', CONDFI,
9dbc8d99 941
eca244ca 942 /* and finally the hostname lookup characters */
42fa5d67
EA
943 '[', HOSTBEGIN, ']', HOSTEND,
944 '(', LOOKUPBEGIN, ')', LOOKUPEND,
eca244ca 945
9dbc8d99 946 '\0'
721fad23
EA
947};
948
949initmacros()
950{
9dbc8d99
EA
951 register struct metamac *m;
952 char buf[5];
953 register int c;
721fad23 954
9dbc8d99
EA
955 for (m = MetaMacros; m->metaname != '\0'; m++)
956 {
957 buf[0] = m->metaval;
958 buf[1] = '\0';
7338e3d4 959 define(m->metaname, newstr(buf), CurEnv);
9dbc8d99
EA
960 }
961 buf[0] = MATCHREPL;
962 buf[2] = '\0';
963 for (c = '0'; c <= '9'; c++)
964 {
965 buf[1] = c;
7338e3d4 966 define(c, newstr(buf), CurEnv);
9dbc8d99 967 }
721fad23 968}
dd1fe05b 969\f/*
acae5a9d
EA
970** FREEZE -- freeze BSS & allocated memory
971**
972** This will be used to efficiently load the configuration file.
973**
974** Parameters:
8fe4fb9b 975** freezefile -- the name of the file to freeze to.
acae5a9d
EA
976**
977** Returns:
978** none.
979**
980** Side Effects:
8fe4fb9b 981** Writes BSS and malloc'ed memory to freezefile
acae5a9d
EA
982*/
983
7338e3d4 984union frz
acae5a9d 985{
7338e3d4
EA
986 char frzpad[BUFSIZ]; /* insure we are on a BUFSIZ boundary */
987 struct
988 {
989 time_t frzstamp; /* timestamp on this freeze */
990 char *frzbrk; /* the current break */
2e3062fe
EA
991 char *frzedata; /* address of edata */
992 char *frzend; /* address of end */
7338e3d4
EA
993 char frzver[252]; /* sendmail version */
994 } frzinfo;
acae5a9d
EA
995};
996
8fe4fb9b
EA
997freeze(freezefile)
998 char *freezefile;
acae5a9d
EA
999{
1000 int f;
7338e3d4 1001 union frz fhdr;
2e3062fe 1002 extern char edata, end;
acae5a9d 1003 extern char *sbrk();
912acb74 1004 extern char Version[];
acae5a9d 1005
8fe4fb9b 1006 if (freezefile == NULL)
acae5a9d
EA
1007 return;
1008
1009 /* try to open the freeze file */
8fe4fb9b 1010 f = creat(freezefile, FileMode);
acae5a9d
EA
1011 if (f < 0)
1012 {
2e15a2d8 1013 syserr("Cannot freeze %s", freezefile);
acae5a9d
EA
1014 errno = 0;
1015 return;
1016 }
1017
1018 /* build the freeze header */
7338e3d4
EA
1019 fhdr.frzinfo.frzstamp = curtime();
1020 fhdr.frzinfo.frzbrk = sbrk(0);
2e3062fe
EA
1021 fhdr.frzinfo.frzedata = &edata;
1022 fhdr.frzinfo.frzend = &end;
0e306e7f 1023 (void) strcpy(fhdr.frzinfo.frzver, Version);
acae5a9d
EA
1024
1025 /* write out the freeze header */
611b763d 1026 if (write(f, (char *) &fhdr, sizeof fhdr) != sizeof fhdr ||
34fe0a9b
EA
1027 write(f, (char *) &edata, (int) (fhdr.frzinfo.frzbrk - &edata)) !=
1028 (int) (fhdr.frzinfo.frzbrk - &edata))
7338e3d4 1029 {
2e15a2d8 1030 syserr("Cannot freeze %s", freezefile);
7338e3d4 1031 }
acae5a9d
EA
1032
1033 /* fine, clean up */
1034 (void) close(f);
1035}
1036\f/*
1037** THAW -- read in the frozen configuration file.
1038**
1039** Parameters:
8fe4fb9b 1040** freezefile -- the name of the file to thaw from.
4b26318e 1041** binfile -- the name of the sendmail binary (ok to guess).
acae5a9d
EA
1042**
1043** Returns:
1044** TRUE if it successfully read the freeze file.
1045** FALSE otherwise.
1046**
1047** Side Effects:
8fe4fb9b 1048** reads freezefile in to BSS area.
acae5a9d
EA
1049*/
1050
4b26318e 1051thaw(freezefile, binfile)
8fe4fb9b 1052 char *freezefile;
4b26318e 1053 char *binfile;
acae5a9d
EA
1054{
1055 int f;
ccfed53c 1056 register char *p;
7338e3d4 1057 union frz fhdr;
ccfed53c 1058 char hbuf[60];
4b26318e 1059 struct stat fst, sst;
a59237a4 1060 extern char edata, end;
912acb74 1061 extern char Version[];
17a67c62 1062 extern caddr_t brk();
ccfed53c
EA
1063 extern char **myhostname();
1064 extern char *macvalue();
acae5a9d 1065
8fe4fb9b 1066 if (freezefile == NULL)
acae5a9d
EA
1067 return (FALSE);
1068
1069 /* open the freeze file */
8fe4fb9b 1070 f = open(freezefile, 0);
acae5a9d
EA
1071 if (f < 0)
1072 {
1073 errno = 0;
1074 return (FALSE);
1075 }
1076
4b26318e
EA
1077 if (fstat(f, &fst) < 0 || stat(ConfFile, &sst) < 0 ||
1078 fst.st_mtime < sst.st_mtime)
1079 {
1080 syslog(LOG_WARNING, "Freeze file older than config file");
1081 (void) close(f);
1082 return (FALSE);
1083 }
1084
1085 if (strchr(binfile, '/') != NULL && stat(binfile, &sst) == 0 &&
1086 fst.st_mtime < sst.st_mtime)
1087 {
1088 syslog(LOG_WARNING, "Freeze file older than binary file");
1089 (void) close(f);
1090 return (FALSE);
1091 }
1092
acae5a9d 1093 /* read in the header */
07adc4f5
RA
1094 if (read(f, (char *) &fhdr, sizeof fhdr) < sizeof fhdr)
1095 {
db281fbf 1096 syslog(LOG_WARNING, "Cannot read frozen config file");
07adc4f5
RA
1097 (void) close(f);
1098 return (FALSE);
1099 }
db281fbf 1100 if (fhdr.frzinfo.frzedata != &edata ||
2e3062fe 1101 fhdr.frzinfo.frzend != &end ||
7338e3d4 1102 strcmp(fhdr.frzinfo.frzver, Version) != 0)
acae5a9d 1103 {
07adc4f5 1104 syslog(LOG_WARNING, "Wrong version of frozen config file");
acae5a9d
EA
1105 (void) close(f);
1106 return (FALSE);
1107 }
1108
1109 /* arrange to have enough space */
17a67c62 1110 if (brk(fhdr.frzinfo.frzbrk) == (caddr_t) -1)
acae5a9d 1111 {
7338e3d4 1112 syserr("Cannot break to %x", fhdr.frzinfo.frzbrk);
acae5a9d
EA
1113 (void) close(f);
1114 return (FALSE);
1115 }
1116
1117 /* now read in the freeze file */
34fe0a9b
EA
1118 if (read(f, (char *) &edata, (int) (fhdr.frzinfo.frzbrk - &edata)) !=
1119 (int) (fhdr.frzinfo.frzbrk - &edata))
acae5a9d 1120 {
07adc4f5 1121 syserr("Cannot read frozen config file");
acae5a9d 1122 /* oops! we have trashed memory..... */
0e306e7f 1123 (void) write(2, "Cannot read freeze file\n", 24);
7338e3d4 1124 _exit(EX_SOFTWARE);
acae5a9d
EA
1125 }
1126
1127 (void) close(f);
ccfed53c
EA
1128
1129 /* verify that the host name was correct on the freeze */
1130 (void) myhostname(hbuf, sizeof hbuf);
1131 p = macvalue('w', CurEnv);
1132 if (p == NULL)
1133 p = "";
1134 if (strcmp(hbuf, macvalue('w', CurEnv)) == 0)
1135 return (TRUE);
1136 syslog(LOG_WARNING, "Hostname changed since freeze (%s => %s)",
1137 p, hbuf);
1138 return (FALSE);
acae5a9d 1139}
813d8709
EA
1140\f/*
1141** DISCONNECT -- remove our connection with any foreground process
1142**
1143** Parameters:
d188728a
EA
1144** fulldrop -- if set, we should also drop the controlling
1145** TTY if possible -- this should only be done when
1146** setting up the daemon since otherwise UUCP can
1147** leave us trying to open a dialin, and we will
1148** wait for the carrier.
813d8709
EA
1149**
1150** Returns:
1151** none
1152**
1153** Side Effects:
1154** Trys to insure that we are immune to vagaries of
1155** the controlling tty.
1156*/
1157
d188728a
EA
1158disconnect(fulldrop)
1159 bool fulldrop;
813d8709
EA
1160{
1161 int fd;
1162
e6f08ab1 1163 if (tTd(52, 1))
b9accadd
EA
1164 printf("disconnect: In %d Out %d\n", fileno(InChannel),
1165 fileno(OutChannel));
e6f08ab1 1166 if (tTd(52, 5))
813d8709 1167 {
e6f08ab1
EA
1168 printf("don't\n");
1169 return;
813d8709 1170 }
813d8709
EA
1171
1172 /* be sure we don't get nasty signals */
0e306e7f
EA
1173 (void) signal(SIGHUP, SIG_IGN);
1174 (void) signal(SIGINT, SIG_IGN);
1175 (void) signal(SIGQUIT, SIG_IGN);
813d8709
EA
1176
1177 /* we can't communicate with our caller, so.... */
7338e3d4
EA
1178 HoldErrs = TRUE;
1179 ErrorMode = EM_MAIL;
813d8709
EA
1180 Verbose = FALSE;
1181
1182 /* all input from /dev/null */
813d8709 1183 if (InChannel != stdin)
b9accadd
EA
1184 {
1185 (void) fclose(InChannel);
1186 InChannel = stdin;
1187 }
1188 (void) freopen("/dev/null", "r", stdin);
813d8709
EA
1189
1190 /* output to the transcript */
b9accadd 1191 if (OutChannel != stdout)
813d8709 1192 {
b9accadd
EA
1193 (void) fclose(OutChannel);
1194 OutChannel = stdout;
813d8709 1195 }
912acb74
EA
1196 if (CurEnv->e_xfp == NULL)
1197 CurEnv->e_xfp = fopen("/dev/null", "w");
b9accadd
EA
1198 (void) fflush(stdout);
1199 (void) close(1);
1200 (void) close(2);
912acb74 1201 while ((fd = dup(fileno(CurEnv->e_xfp))) < 2 && fd > 0)
b9accadd 1202 continue;
813d8709 1203
e6f08ab1 1204 /* drop our controlling TTY completely if possible */
d188728a 1205 if (fulldrop)
e6f08ab1 1206 {
5229f34d 1207 (void) setsid();
afe907a4 1208#ifdef TIOCNOTTY
d188728a
EA
1209 fd = open("/dev/tty", 2);
1210 if (fd >= 0)
1211 {
17a67c62 1212 (void) ioctl(fd, (int) TIOCNOTTY, (char *) 0);
d188728a
EA
1213 (void) close(fd);
1214 }
17a67c62 1215 (void) setpgrp(0, 0);
afe907a4 1216#endif /* TIOCNOTTY */
70faa7c8 1217 errno = 0;
e6f08ab1 1218 }
e6f08ab1 1219
813d8709
EA
1220# ifdef LOG
1221 if (LogLevel > 11)
1222 syslog(LOG_DEBUG, "in background, pid=%d", getpid());
f3d8f6d6 1223# endif /* LOG */
813d8709
EA
1224
1225 errno = 0;
1226}
b2d4b27a
KB
1227
1228static void
1229obsolete(argv)
1230 char *argv[];
1231{
1232 char *ap;
1233
1234 while (ap = *++argv)
1235 {
1236 /* Return if "--" or not an option of any form. */
1237 if (ap[0] != '-' || ap[1] == '-')
1238 return;
1239
1240 /* If -C doesn't have an argument, use sendmail.cf. */
1241#define __DEFPATH "sendmail.cf"
f96fa7de
EA
1242 if (ap[1] == 'C' && ap[2] == '\0' &&
1243 (argv[1] == NULL || argv[1][0] == '-'))
b2d4b27a
KB
1244 {
1245 *argv = xalloc(sizeof(__DEFPATH) + 2);
1246 argv[0][0] = '-';
1247 argv[0][1] = 'C';
1248 (void)strcpy(&argv[0][2], __DEFPATH);
1249 }
f96fa7de
EA
1250
1251 /* If -q doesn't have an argument, run it once. */
1252 if (ap[1] == 'q' && ap[2] == '\0' &&
1253 (argv[1] == NULL || argv[1][0] == '-'))
1254 *argv = "-q0";
a0431b8d
EA
1255
1256 /* if -d doesn't have an argument, use 0-99.1 */
1257 if (ap[1] == 'd' && ap[2] == '\0' &&
1258 (argv[1] == NULL || argv[1][0] == '-'))
1259 *argv = "-d0-99.1";
b2d4b27a
KB
1260 }
1261}