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