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