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