summaryrefslogtreecommitdiffstats
path: root/usr.sbin/sendmail/src/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/sendmail/src/main.c')
-rw-r--r--usr.sbin/sendmail/src/main.c2572
1 files changed, 0 insertions, 2572 deletions
diff --git a/usr.sbin/sendmail/src/main.c b/usr.sbin/sendmail/src/main.c
deleted file mode 100644
index 9dd8a6a..0000000
--- a/usr.sbin/sendmail/src/main.c
+++ /dev/null
@@ -1,2572 +0,0 @@
-/*
- * Copyright (c) 1983, 1995-1997 Eric P. Allman
- * Copyright (c) 1988, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef lint
-static char copyright[] =
-"@(#) Copyright (c) 1988, 1993\n\
- The Regents of the University of California. All rights reserved.\n";
-#endif /* not lint */
-
-#ifndef lint
-static char sccsid[] = "@(#)main.c 8.258 (Berkeley) 10/20/97";
-#endif /* not lint */
-
-#define _DEFINE
-
-#include "sendmail.h"
-#include <arpa/inet.h>
-#if NAMED_BIND
-#include <resolv.h>
-#endif
-
-/*
-** SENDMAIL -- Post mail to a set of destinations.
-**
-** This is the basic mail router. All user mail programs should
-** call this routine to actually deliver mail. Sendmail in
-** turn calls a bunch of mail servers that do the real work of
-** delivering the mail.
-**
-** Sendmail is driven by settings read in from /etc/sendmail.cf
-** (read by readcf.c).
-**
-** Usage:
-** /usr/lib/sendmail [flags] addr ...
-**
-** See the associated documentation for details.
-**
-** Author:
-** Eric Allman, UCB/INGRES (until 10/81).
-** Britton-Lee, Inc., purveyors of fine
-** database computers (11/81 - 10/88).
-** International Computer Science Institute
-** (11/88 - 9/89).
-** UCB/Mammoth Project (10/89 - 7/95).
-** InReference, Inc. (8/95 - 1/97).
-** The support of the my employers is gratefully acknowledged.
-** Few of them (Britton-Lee in particular) have had
-** anything to gain from my involvement in this project.
-*/
-
-
-int NextMailer; /* "free" index into Mailer struct */
-char *FullName; /* sender's full name */
-ENVELOPE BlankEnvelope; /* a "blank" envelope */
-ENVELOPE MainEnvelope; /* the envelope around the basic letter */
-ADDRESS NullAddress = /* a null address */
- { "", "", NULL, "" };
-char *CommandLineArgs; /* command line args for pid file */
-bool Warn_Q_option = FALSE; /* warn about Q option use */
-char **SaveArgv; /* argument vector for re-execing */
-int MissingFds = 0; /* bit map of fds missing on startup */
-
-#ifdef NGROUPS_MAX
-GIDSET_T InitialGidSet[NGROUPS_MAX];
-#endif
-
-static void obsolete();
-extern void printmailer __P((MAILER *));
-extern void tTflag __P((char *));
-
-#if DAEMON && !SMTP
-ERROR %%%% Cannot have DAEMON mode without SMTP %%%% ERROR
-#endif /* DAEMON && !SMTP */
-#if SMTP && !QUEUE
-ERROR %%%% Cannot have SMTP mode without QUEUE %%%% ERROR
-#endif /* DAEMON && !SMTP */
-
-#define MAXCONFIGLEVEL 7 /* highest config version level known */
-
-int
-main(argc, argv, envp)
- int argc;
- char **argv;
- char **envp;
-{
- register char *p;
- char **av;
- extern char Version[];
- char *ep, *from;
- STAB *st;
- register int i;
- int j;
- bool queuemode = FALSE; /* process queue requests */
- bool safecf = TRUE;
- bool warn_C_flag = FALSE;
- char warn_f_flag = '\0';
- bool run_in_foreground = FALSE; /* -bD mode */
- static bool reenter = FALSE;
- struct passwd *pw;
- struct hostent *hp;
- bool nullserver = FALSE;
- char jbuf[MAXHOSTNAMELEN]; /* holds MyHostName */
- static char rnamebuf[MAXNAME]; /* holds RealUserName */
- char *emptyenviron[1];
- extern int DtableSize;
- extern int optind;
- extern int opterr;
- extern char *optarg;
- extern char **environ;
- extern time_t convtime();
- extern SIGFUNC_DECL intsig __P((int));
- extern struct hostent *myhostname();
- extern char *getauthinfo();
- extern char *getcfname();
- extern SIGFUNC_DECL sigusr1 __P((int));
- extern SIGFUNC_DECL sighup __P((int));
- extern void initmacros __P((ENVELOPE *));
- extern void init_md __P((int, char **));
- extern int getdtsize __P((void));
- extern void tTsetup __P((u_char *, int, char *));
- extern void setdefaults __P((ENVELOPE *));
- extern void initsetproctitle __P((int, char **, char **));
- extern void init_vendor_macros __P((ENVELOPE *));
- extern void load_if_names __P((void));
- extern void vendor_pre_defaults __P((ENVELOPE *));
- extern void vendor_post_defaults __P((ENVELOPE *));
- extern void readcf __P((char *, bool, ENVELOPE *));
- extern void printqueue __P((void));
- extern void sendtoargv __P((char **, ENVELOPE *));
- extern void resetlimits __P((void));
-
- /*
- ** Check to see if we reentered.
- ** This would normally happen if e_putheader or e_putbody
- ** were NULL when invoked.
- */
-
- if (reenter)
- {
- syserr("main: reentered!");
- abort();
- }
- reenter = TRUE;
-
- /* avoid null pointer dereferences */
- TermEscape.te_rv_on = TermEscape.te_rv_off = "";
-
- /* do machine-dependent initializations */
- init_md(argc, argv);
-
- /* in 4.4BSD, the table can be huge; impose a reasonable limit */
- DtableSize = getdtsize();
- if (DtableSize > 256)
- DtableSize = 256;
-
- /*
- ** Be sure we have enough file descriptors.
- ** But also be sure that 0, 1, & 2 are open.
- */
-
- fill_fd(STDIN_FILENO, NULL);
- fill_fd(STDOUT_FILENO, NULL);
- fill_fd(STDERR_FILENO, NULL);
-
- i = DtableSize;
- while (--i > 0)
- {
- if (i != STDIN_FILENO && i != STDOUT_FILENO && i != STDERR_FILENO)
- (void) close(i);
- }
- errno = 0;
-
-#if LOG
-# ifdef LOG_MAIL
- openlog("sendmail", LOG_PID, LOG_MAIL);
-# else
- openlog("sendmail", LOG_PID);
-# endif
-#endif
-
- if (MissingFds != 0)
- {
- char mbuf[MAXLINE];
-
- mbuf[0] = '\0';
- if (bitset(1 << STDIN_FILENO, MissingFds))
- strcat(mbuf, ", stdin");
- if (bitset(1 << STDOUT_FILENO, MissingFds))
- strcat(mbuf, ", stdout");
- if (bitset(1 << STDERR_FILENO, MissingFds))
- strcat(mbuf, ", stderr");
- syserr("File descriptors missing on startup: %s", &mbuf[2]);
- }
-
- /* reset status from syserr() calls for missing file descriptors */
- Errors = 0;
- ExitStat = EX_OK;
-
-#if XDEBUG
- checkfd012("after openlog");
-#endif
-
- tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
-
-#ifdef NGROUPS_MAX
- /* save initial group set for future checks */
- i = getgroups(NGROUPS_MAX, InitialGidSet);
- if (i == 0)
- InitialGidSet[0] = (GID_T) -1;
- while (i < NGROUPS_MAX)
- InitialGidSet[i++] = InitialGidSet[0];
-#endif
-
- /* drop group id privileges (RunAsUser not yet set) */
- (void) drop_privileges(FALSE);
-
-#ifdef SIGUSR1
- /* arrange to dump state on user-1 signal */
- setsignal(SIGUSR1, sigusr1);
-#endif
-
- /* Handle any non-getoptable constructions. */
- obsolete(argv);
-
- /*
- ** Do a quick prescan of the argument list.
- */
-
-#if defined(__osf__) || defined(_AIX3)
-# define OPTIONS "B:b:C:cd:e:F:f:h:IiM:mN:nO:o:p:q:R:r:sTtUV:vX:x"
-#endif
-#if defined(sony_news)
-# define OPTIONS "B:b:C:cd:E:e:F:f:h:IiJ:M:mN:nO:o:p:q:R:r:sTtUV:vX:"
-#endif
-#ifndef OPTIONS
-# define OPTIONS "B:b:C:cd:e:F:f:h:IiM:mN:nO:o:p:q:R:r:sTtUV:vX:"
-#endif
- opterr = 0;
- while ((j = getopt(argc, argv, OPTIONS)) != -1)
- {
- switch (j)
- {
- case 'd':
- /* hack attack -- see if should use ANSI mode */
- if (strcmp(optarg, "ANSI") == 0)
- {
- TermEscape.te_rv_on = "\033[7m";
- TermEscape.te_rv_off = "\033[0m";
- break;
- }
- tTflag(optarg);
- setbuf(stdout, (char *) NULL);
- break;
- }
- }
- opterr = 1;
-
- /* set up the blank envelope */
- BlankEnvelope.e_puthdr = putheader;
- BlankEnvelope.e_putbody = putbody;
- BlankEnvelope.e_xfp = NULL;
- STRUCTCOPY(NullAddress, BlankEnvelope.e_from);
- CurEnv = &BlankEnvelope;
- STRUCTCOPY(NullAddress, MainEnvelope.e_from);
-
- /*
- ** Set default values for variables.
- ** These cannot be in initialized data space.
- */
-
- setdefaults(&BlankEnvelope);
-
- RealUid = getuid();
- RealGid = getgid();
-
- pw = sm_getpwuid(RealUid);
- if (pw != NULL)
- (void) snprintf(rnamebuf, sizeof rnamebuf, "%s", pw->pw_name);
- else
- (void) snprintf(rnamebuf, sizeof rnamebuf, "Unknown UID %d", RealUid);
- RealUserName = rnamebuf;
-
- /* if running non-setuid binary, pretend we are the RunAsUid */
- if (geteuid() == RealUid)
- {
- if (tTd(47, 1))
- printf("Non-setuid binary: RunAsUid = RealUid = %d\n",
- RealUid);
- RunAsUid = RealUid;
- }
- if (getegid() == RealGid)
- RunAsGid = RealGid;
-
- /* save command line arguments */
- i = 0;
- for (av = argv; *av != NULL; )
- i += strlen(*av++) + 1;
- SaveArgv = (char **) xalloc(sizeof (char *) * (argc + 1));
- CommandLineArgs = xalloc(i);
- p = CommandLineArgs;
- for (av = argv, i = 0; *av != NULL; )
- {
- SaveArgv[i++] = newstr(*av);
- if (av != argv)
- *p++ = ' ';
- strcpy(p, *av++);
- p += strlen(p);
- }
- SaveArgv[i] = NULL;
-
- if (tTd(0, 1))
- {
- int ll;
- extern char *CompileOptions[];
-
- printf("Version %s\n Compiled with:", Version);
- av = CompileOptions;
- ll = 7;
- while (*av != NULL)
- {
- if (ll + strlen(*av) > 63)
- {
- putchar('\n');
- ll = 0;
- }
- if (ll == 0)
- {
- putchar('\t');
- putchar('\t');
- }
- else
- putchar(' ');
- printf("%s", *av);
- ll += strlen(*av++) + 1;
- }
- putchar('\n');
- }
- if (tTd(0, 10))
- {
- int ll;
- extern char *OsCompileOptions[];
-
- printf(" OS Defines:");
- av = OsCompileOptions;
- ll = 7;
- while (*av != NULL)
- {
- if (ll + strlen(*av) > 63)
- {
- putchar('\n');
- ll = 0;
- }
- if (ll == 0)
- {
- putchar('\t');
- putchar('\t');
- }
- else
- putchar(' ');
- printf("%s", *av);
- ll += strlen(*av++) + 1;
- }
- putchar('\n');
-#ifdef _PATH_UNIX
- printf("Kernel symbols:\t%s\n", _PATH_UNIX);
-#endif
- printf(" Def Conf file:\t%s\n", getcfname());
- printf(" Pid file:\t%s\n", PidFile);
- }
-
- InChannel = stdin;
- OutChannel = stdout;
-
- /* initialize for setproctitle */
- initsetproctitle(argc, argv, envp);
-
- /* clear sendmail's environment */
- ExternalEnviron = environ;
- emptyenviron[0] = NULL;
- environ = emptyenviron;
-
- /* prime the child environment */
- setuserenv("AGENT", "sendmail");
-
- if (setsignal(SIGINT, SIG_IGN) != SIG_IGN)
- (void) setsignal(SIGINT, intsig);
- (void) setsignal(SIGTERM, intsig);
- (void) setsignal(SIGPIPE, SIG_IGN);
- OldUmask = umask(022);
- OpMode = MD_DELIVER;
- FullName = getextenv("NAME");
-
- /*
- ** Initialize name server if it is going to be used.
- */
-
-#if NAMED_BIND
- if (!bitset(RES_INIT, _res.options))
- res_init();
- if (tTd(8, 8))
- _res.options |= RES_DEBUG;
- else
- _res.options &= ~RES_DEBUG;
-# ifdef RES_NOALIASES
- _res.options |= RES_NOALIASES;
-# endif
-#endif
-
- errno = 0;
- from = NULL;
-
- /* initialize some macros, etc. */
- initmacros(CurEnv);
- init_vendor_macros(CurEnv);
-
- /* version */
- define('v', Version, CurEnv);
-
- /* hostname */
- hp = myhostname(jbuf, sizeof jbuf);
- if (jbuf[0] != '\0')
- {
- struct utsname utsname;
-
- if (tTd(0, 4))
- printf("canonical name: %s\n", jbuf);
- define('w', newstr(jbuf), CurEnv); /* must be new string */
- define('j', newstr(jbuf), CurEnv);
- setclass('w', jbuf);
-
- p = strchr(jbuf, '.');
- if (p != NULL)
- {
- if (p[1] != '\0')
- {
- define('m', newstr(&p[1]), CurEnv);
- }
- while (p != NULL && strchr(&p[1], '.') != NULL)
- {
- *p = '\0';
- if (tTd(0, 4))
- printf("\ta.k.a.: %s\n", jbuf);
- setclass('w', jbuf);
- *p++ = '.';
- p = strchr(p, '.');
- }
- }
-
- if (uname(&utsname) >= 0)
- p = utsname.nodename;
- else
- {
- if (tTd(0, 22))
- printf("uname failed (%s)\n", errstring(errno));
- makelower(jbuf);
- p = jbuf;
- }
- if (tTd(0, 4))
- printf(" UUCP nodename: %s\n", p);
- p = newstr(p);
- define('k', p, CurEnv);
- setclass('k', p);
- setclass('w', p);
- }
- if (hp != NULL)
- {
- for (av = hp->h_aliases; av != NULL && *av != NULL; av++)
- {
- if (tTd(0, 4))
- printf("\ta.k.a.: %s\n", *av);
- setclass('w', *av);
- }
-#if NETINET
- if (hp->h_addrtype == AF_INET && hp->h_length == INADDRSZ)
- {
- register int i;
-
- for (i = 0; hp->h_addr_list[i] != NULL; i++)
- {
- char ipbuf[103];
-
- snprintf(ipbuf, sizeof ipbuf, "[%.100s]",
- inet_ntoa(*((struct in_addr *) hp->h_addr_list[i])));
- if (tTd(0, 4))
- printf("\ta.k.a.: %s\n", ipbuf);
- setclass('w', ipbuf);
- }
- }
-#endif
- }
-
- /* current time */
- define('b', arpadate((char *) NULL), CurEnv);
-
- /*
- ** Crack argv.
- */
-
- av = argv;
- p = strrchr(*av, '/');
- if (p++ == NULL)
- p = *av;
- if (strcmp(p, "newaliases") == 0)
- OpMode = MD_INITALIAS;
- else if (strcmp(p, "mailq") == 0)
- OpMode = MD_PRINT;
- else if (strcmp(p, "smtpd") == 0)
- OpMode = MD_DAEMON;
- else if (strcmp(p, "hoststat") == 0)
- OpMode = MD_HOSTSTAT;
- else if (strcmp(p, "purgestat") == 0)
- OpMode = MD_PURGESTAT;
-
- optind = 1;
- while ((j = getopt(argc, argv, OPTIONS)) != -1)
- {
- switch (j)
- {
- case 'b': /* operations mode */
- switch (j = *optarg)
- {
- case MD_DAEMON:
- case MD_FGDAEMON:
-# if !DAEMON
- usrerr("Daemon mode not implemented");
- ExitStat = EX_USAGE;
- break;
-# endif /* DAEMON */
- case MD_SMTP:
-# if !SMTP
- usrerr("I don't speak SMTP");
- ExitStat = EX_USAGE;
- break;
-# endif /* SMTP */
-
- case MD_INITALIAS:
- case MD_DELIVER:
- case MD_VERIFY:
- case MD_TEST:
- case MD_PRINT:
- case MD_HOSTSTAT:
- case MD_PURGESTAT:
- case MD_ARPAFTP:
- OpMode = j;
- break;
-
- case MD_FREEZE:
- usrerr("Frozen configurations unsupported");
- ExitStat = EX_USAGE;
- break;
-
- default:
- usrerr("Invalid operation mode %c", j);
- ExitStat = EX_USAGE;
- break;
- }
- break;
-
- case 'B': /* body type */
- CurEnv->e_bodytype = optarg;
- break;
-
- case 'C': /* select configuration file (already done) */
- if (RealUid != 0)
- warn_C_flag = TRUE;
- ConfFile = optarg;
- (void) drop_privileges(TRUE);
- safecf = FALSE;
- break;
-
- case 'd': /* debugging -- already done */
- break;
-
- case 'f': /* from address */
- case 'r': /* obsolete -f flag */
- if (from != NULL)
- {
- usrerr("More than one \"from\" person");
- ExitStat = EX_USAGE;
- break;
- }
- from = newstr(denlstring(optarg, TRUE, TRUE));
- if (strcmp(RealUserName, from) != 0)
- warn_f_flag = j;
- break;
-
- case 'F': /* set full name */
- FullName = newstr(optarg);
- break;
-
- case 'h': /* hop count */
- CurEnv->e_hopcount = strtol(optarg, &ep, 10);
- if (*ep)
- {
- usrerr("Bad hop count (%s)", optarg);
- ExitStat = EX_USAGE;
- }
- break;
-
- case 'n': /* don't alias */
- NoAlias = TRUE;
- break;
-
- case 'N': /* delivery status notifications */
- DefaultNotify |= QHASNOTIFY;
- if (strcasecmp(optarg, "never") == 0)
- break;
- for (p = optarg; p != NULL; optarg = p)
- {
- p = strchr(p, ',');
- if (p != NULL)
- *p++ = '\0';
- if (strcasecmp(optarg, "success") == 0)
- DefaultNotify |= QPINGONSUCCESS;
- else if (strcasecmp(optarg, "failure") == 0)
- DefaultNotify |= QPINGONFAILURE;
- else if (strcasecmp(optarg, "delay") == 0)
- DefaultNotify |= QPINGONDELAY;
- else
- {
- usrerr("Invalid -N argument");
- ExitStat = EX_USAGE;
- }
- }
- break;
-
- case 'o': /* set option */
- setoption(*optarg, optarg + 1, FALSE, TRUE, CurEnv);
- break;
-
- case 'O': /* set option (long form) */
- setoption(' ', optarg, FALSE, TRUE, CurEnv);
- break;
-
- case 'p': /* set protocol */
- p = strchr(optarg, ':');
- if (p != NULL)
- {
- *p++ = '\0';
- if (*p != '\0')
- {
- ep = xalloc(strlen(p) + 1);
- cleanstrcpy(ep, p, MAXNAME);
- define('s', ep, CurEnv);
- }
- }
- if (*optarg != '\0')
- {
- ep = xalloc(strlen(optarg) + 1);
- cleanstrcpy(ep, optarg, MAXNAME);
- define('r', ep, CurEnv);
- }
- break;
-
- case 'q': /* run queue files at intervals */
-# if QUEUE
- FullName = NULL;
- queuemode = TRUE;
- switch (optarg[0])
- {
- case 'I':
- QueueLimitId = newstr(&optarg[1]);
- break;
-
- case 'R':
- QueueLimitRecipient = newstr(&optarg[1]);
- break;
-
- case 'S':
- QueueLimitSender = newstr(&optarg[1]);
- break;
-
- default:
- QueueIntvl = convtime(optarg, 'm');
- break;
- }
-# else /* QUEUE */
- usrerr("I don't know about queues");
- ExitStat = EX_USAGE;
-# endif /* QUEUE */
- break;
-
- case 'R': /* DSN RET: what to return */
- if (bitset(EF_RET_PARAM, CurEnv->e_flags))
- {
- usrerr("Duplicate -R flag");
- ExitStat = EX_USAGE;
- break;
- }
- CurEnv->e_flags |= EF_RET_PARAM;
- if (strcasecmp(optarg, "hdrs") == 0)
- CurEnv->e_flags |= EF_NO_BODY_RETN;
- else if (strcasecmp(optarg, "full") != 0)
- {
- usrerr("Invalid -R value");
- ExitStat = EX_USAGE;
- }
- break;
-
- case 't': /* read recipients from message */
- GrabTo = TRUE;
- break;
-
- case 'U': /* initial (user) submission */
- UserSubmission = TRUE;
- break;
-
- case 'V': /* DSN ENVID: set "original" envelope id */
- if (!xtextok(optarg))
- {
- usrerr("Invalid syntax in -V flag");
- ExitStat = EX_USAGE;
- }
- else
- CurEnv->e_envid = newstr(optarg);
- break;
-
- case 'X': /* traffic log file */
- (void) drop_privileges(TRUE);
- TrafficLogFile = fopen(optarg, "a");
- if (TrafficLogFile == NULL)
- {
- syserr("cannot open %s", optarg);
- ExitStat = EX_CANTCREAT;
- break;
- }
-#ifdef HASSETVBUF
- setvbuf(TrafficLogFile, NULL, _IOLBF, 0);
-#else
- setlinebuf(TrafficLogFile);
-#endif
- break;
-
- /* compatibility flags */
- case 'c': /* connect to non-local mailers */
- case 'i': /* don't let dot stop me */
- case 'm': /* send to me too */
- case 'T': /* set timeout interval */
- case 'v': /* give blow-by-blow description */
- setoption(j, "T", FALSE, TRUE, CurEnv);
- break;
-
- case 'e': /* error message disposition */
- case 'M': /* define macro */
- setoption(j, optarg, FALSE, TRUE, CurEnv);
- break;
-
- case 's': /* save From lines in headers */
- setoption('f', "T", FALSE, TRUE, CurEnv);
- break;
-
-# ifdef DBM
- case 'I': /* initialize alias DBM file */
- OpMode = MD_INITALIAS;
- break;
-# endif /* DBM */
-
-# if defined(__osf__) || defined(_AIX3)
- case 'x': /* random flag that OSF/1 & AIX mailx passes */
- break;
-# endif
-# if defined(sony_news)
- case 'E':
- case 'J': /* ignore flags for Japanese code conversion
- impremented on Sony NEWS */
- break;
-# endif
-
- default:
- ExitStat = EX_USAGE;
- finis();
- break;
- }
- }
- av += optind;
-
- /*
- ** Do basic initialization.
- ** Read system control file.
- ** Extract special fields for local use.
- */
-
- /* set up ${opMode} for use in config file */
- {
- char mbuf[2];
-
- mbuf[0] = OpMode;
- mbuf[1] = '\0';
- define(MID_OPMODE, newstr(mbuf), CurEnv);
- }
-
-#if XDEBUG
- checkfd012("before readcf");
-#endif
- vendor_pre_defaults(CurEnv);
- readcf(getcfname(), safecf, CurEnv);
- ConfigFileRead = TRUE;
- vendor_post_defaults(CurEnv);
-
- /* Enforce use of local time (null string overrides this) */
- if (TimeZoneSpec == NULL)
- unsetenv("TZ");
- else if (TimeZoneSpec[0] != '\0')
- setuserenv("TZ", TimeZoneSpec);
- else
- setuserenv("TZ", NULL);
- tzset();
-
- /* avoid denial-of-service attacks */
- resetlimits();
-
- if (OpMode != MD_DAEMON && OpMode != MD_FGDAEMON)
- {
- /* drop privileges -- daemon mode done after socket/bind */
- (void) drop_privileges(FALSE);
- }
-
- /*
- ** Find our real host name for future logging.
- */
-
- p = getauthinfo(STDIN_FILENO);
- define('_', p, CurEnv);
-
- /* suppress error printing if errors mailed back or whatever */
- if (CurEnv->e_errormode != EM_PRINT)
- HoldErrs = TRUE;
-
- /* set up the $=m class now, after .cf has a chance to redefine $m */
- expand("\201m", jbuf, sizeof jbuf, CurEnv);
- setclass('m', jbuf);
-
- /* probe interfaces and locate any additional names */
- if (!DontProbeInterfaces)
- load_if_names();
-
- if (tTd(0, 1))
- {
- printf("\n============ SYSTEM IDENTITY (after readcf) ============");
- printf("\n (short domain name) $w = ");
- xputs(macvalue('w', CurEnv));
- printf("\n (canonical domain name) $j = ");
- xputs(macvalue('j', CurEnv));
- printf("\n (subdomain name) $m = ");
- xputs(macvalue('m', CurEnv));
- printf("\n (node name) $k = ");
- xputs(macvalue('k', CurEnv));
- printf("\n========================================================\n\n");
- }
-
- /*
- ** Do more command line checking -- these are things that
- ** have to modify the results of reading the config file.
- */
-
- /* process authorization warnings from command line */
- if (warn_C_flag)
- auth_warning(CurEnv, "Processed by %s with -C %s",
- RealUserName, ConfFile);
- if (Warn_Q_option)
- auth_warning(CurEnv, "Processed from queue %s", QueueDir);
-
- /* check body type for legality */
- if (CurEnv->e_bodytype == NULL)
- /* nothing */ ;
- else if (strcasecmp(CurEnv->e_bodytype, "7BIT") == 0)
- SevenBitInput = TRUE;
- else if (strcasecmp(CurEnv->e_bodytype, "8BITMIME") == 0)
- SevenBitInput = FALSE;
- else
- {
- usrerr("Illegal body type %s", CurEnv->e_bodytype);
- CurEnv->e_bodytype = NULL;
- }
-
- /* tweak default DSN notifications */
- if (DefaultNotify == 0)
- DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
-
- /* be sure we don't pick up bogus HOSTALIASES environment variable */
- if (queuemode && RealUid != 0)
- (void) unsetenv("HOSTALIASES");
-
- /* check for sane configuration level */
- if (ConfigLevel > MAXCONFIGLEVEL)
- {
- syserr("Warning: .cf version level (%d) exceeds sendmail version %s functionality (%d)",
- ConfigLevel, Version, MAXCONFIGLEVEL);
- }
-
- /* need MCI cache to have persistence */
- if (HostStatDir != NULL && MaxMciCache == 0)
- {
- HostStatDir = NULL;
- printf("Warning: HostStatusDirectory disabled with ConnectionCacheSize = 0\n");
- }
-
- /* need HostStatusDir in order to have SingleThreadDelivery */
- if (SingleThreadDelivery && HostStatDir == NULL)
- {
- SingleThreadDelivery = FALSE;
- printf("Warning: HostStatusDirectory required for SingleThreadDelivery\n");
- }
-
- /* check for permissions */
- if ((OpMode == MD_DAEMON || OpMode == MD_PURGESTAT) && RealUid != 0)
- {
- if (LogLevel > 1)
- sm_syslog(LOG_ALERT, NOQID,
- "user %d attempted to %s",
- RealUid,
- OpMode == MD_DAEMON ? "run daemon"
- : "purge host status");
- usrerr("Permission denied");
- exit(EX_USAGE);
- }
-
- if (MeToo)
- BlankEnvelope.e_flags |= EF_METOO;
-
- switch (OpMode)
- {
- case MD_TEST:
- /* don't have persistent host status in test mode */
- HostStatDir = NULL;
- Verbose = 2;
- CurEnv->e_errormode = EM_PRINT;
- break;
-
- case MD_FGDAEMON:
- run_in_foreground = TRUE;
- OpMode = MD_DAEMON;
- /* fall through ... */
-
- case MD_DAEMON:
- vendor_daemon_setup(CurEnv);
-
- /* remove things that don't make sense in daemon mode */
- FullName = NULL;
- GrabTo = FALSE;
-
- /* arrange to restart on hangup signal */
- if (SaveArgv[0] == NULL || SaveArgv[0][0] != '/')
- sm_syslog(LOG_WARNING, NOQID,
- "daemon invoked without full pathname; kill -1 won't work");
- setsignal(SIGHUP, sighup);
-
- /* workaround: can't seem to release the signal in the parent */
- releasesignal(SIGHUP);
- break;
-
- case MD_INITALIAS:
- Verbose = 2;
- CurEnv->e_errormode = EM_PRINT;
- /* fall through... */
-
- case MD_PRINT:
- /* to handle sendmail -bp -qSfoobar properly */
- queuemode = FALSE;
- /* fall through... */
-
- default:
- /* arrange to exit cleanly on hangup signal */
- if (setsignal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
- setsignal(SIGHUP, intsig);
- break;
- }
-
- /* full names can't have newlines */
- if (FullName != NULL && strchr(FullName, '\n') != NULL)
- FullName = newstr(denlstring(FullName, TRUE, TRUE));
-
- /* do heuristic mode adjustment */
- if (Verbose)
- {
- /* turn off noconnect option */
- setoption('c', "F", TRUE, FALSE, CurEnv);
-
- /* turn on interactive delivery */
- setoption('d', "", TRUE, FALSE, CurEnv);
- }
-
- if (ConfigLevel < 3)
- {
- UseErrorsTo = TRUE;
- }
-
- /* set options that were previous macros */
- if (SmtpGreeting == NULL)
- {
- if (ConfigLevel < 7 && (p = macvalue('e', CurEnv)) != NULL)
- SmtpGreeting = newstr(p);
- else
- SmtpGreeting = "\201j Sendmail \201v ready at \201b";
- }
- if (UnixFromLine == NULL)
- {
- if (ConfigLevel < 7 && (p = macvalue('l', CurEnv)) != NULL)
- UnixFromLine = newstr(p);
- else
- UnixFromLine = "From \201g \201d";
- }
-
- /* our name for SMTP codes */
- expand("\201j", jbuf, sizeof jbuf, CurEnv);
- MyHostName = jbuf;
- if (strchr(jbuf, '.') == NULL)
- message("WARNING: local host name (%s) is not qualified; fix $j in config file",
- jbuf);
-
- /* make certain that this name is part of the $=w class */
- setclass('w', MyHostName);
-
- /* the indices of built-in mailers */
- st = stab("local", ST_MAILER, ST_FIND);
- if (st != NULL)
- LocalMailer = st->s_mailer;
- else if (OpMode != MD_TEST || !warn_C_flag)
- syserr("No local mailer defined");
-
- st = stab("prog", ST_MAILER, ST_FIND);
- if (st == NULL)
- syserr("No prog mailer defined");
- else
- {
- ProgMailer = st->s_mailer;
- clrbitn(M_MUSER, ProgMailer->m_flags);
- }
-
- st = stab("*file*", ST_MAILER, ST_FIND);
- if (st == NULL)
- syserr("No *file* mailer defined");
- else
- {
- FileMailer = st->s_mailer;
- clrbitn(M_MUSER, FileMailer->m_flags);
- }
-
- st = stab("*include*", ST_MAILER, ST_FIND);
- if (st == NULL)
- syserr("No *include* mailer defined");
- else
- InclMailer = st->s_mailer;
-
- if (ConfigLevel < 6)
- {
- /* heuristic tweaking of local mailer for back compat */
- if (LocalMailer != NULL)
- {
- setbitn(M_ALIASABLE, LocalMailer->m_flags);
- setbitn(M_HASPWENT, LocalMailer->m_flags);
- setbitn(M_TRYRULESET5, LocalMailer->m_flags);
- setbitn(M_CHECKINCLUDE, LocalMailer->m_flags);
- setbitn(M_CHECKPROG, LocalMailer->m_flags);
- setbitn(M_CHECKFILE, LocalMailer->m_flags);
- setbitn(M_CHECKUDB, LocalMailer->m_flags);
- }
- if (ProgMailer != NULL)
- setbitn(M_RUNASRCPT, ProgMailer->m_flags);
- if (FileMailer != NULL)
- setbitn(M_RUNASRCPT, FileMailer->m_flags);
- }
- if (ConfigLevel < 7)
- {
- if (LocalMailer != NULL)
- setbitn(M_VRFY250, LocalMailer->m_flags);
- if (ProgMailer != NULL)
- setbitn(M_VRFY250, ProgMailer->m_flags);
- if (FileMailer != NULL)
- setbitn(M_VRFY250, FileMailer->m_flags);
- }
-
- /* MIME Content-Types that cannot be transfer encoded */
- setclass('n', "multipart/signed");
-
- /* MIME message/xxx subtypes that can be treated as messages */
- setclass('s', "rfc822");
-
- /* MIME Content-Transfer-Encodings that can be encoded */
- setclass('e', "7bit");
- setclass('e', "8bit");
- setclass('e', "binary");
-
-#ifdef USE_B_CLASS
- /* MIME Content-Types that should be treated as binary */
- setclass('b', "image");
- setclass('b', "audio");
- setclass('b', "video");
- setclass('b', "application/octet-stream");
-#endif
-
- /* operate in queue directory */
- if (QueueDir == NULL)
- {
- if (OpMode != MD_TEST)
- {
- syserr("QueueDirectory (Q) option must be set");
- ExitStat = EX_CONFIG;
- }
- }
- else
- {
- /* test path to get warning messages */
- (void) safedirpath(QueueDir, (uid_t) 0, (gid_t) 0, NULL, SFF_ANYFILE);
- if (OpMode != MD_TEST && chdir(QueueDir) < 0)
- {
- syserr("cannot chdir(%s)", QueueDir);
- ExitStat = EX_CONFIG;
- }
- }
-
- /* check host status directory for validity */
- if (HostStatDir != NULL && !path_is_dir(HostStatDir, FALSE))
- {
- /* cannot use this value */
- if (tTd(0, 2))
- printf("Cannot use HostStatusDirectory = %s: %s\n",
- HostStatDir, errstring(errno));
- HostStatDir = NULL;
- }
-
-# if QUEUE
- if (queuemode && RealUid != 0 && bitset(PRIV_RESTRICTQRUN, PrivacyFlags))
- {
- struct stat stbuf;
-
- /* check to see if we own the queue directory */
- if (stat(".", &stbuf) < 0)
- syserr("main: cannot stat %s", QueueDir);
- if (stbuf.st_uid != RealUid)
- {
- /* nope, really a botch */
- usrerr("You do not have permission to process the queue");
- exit (EX_NOPERM);
- }
- }
-# endif /* QUEUE */
-
- /* if we've had errors so far, exit now */
- if (ExitStat != EX_OK && OpMode != MD_TEST)
- {
- endpwent();
- setuid(RealUid);
- exit(ExitStat);
- }
-
-#if XDEBUG
- checkfd012("before main() initmaps");
-#endif
-
- /*
- ** Do operation-mode-dependent initialization.
- */
-
- switch (OpMode)
- {
- case MD_PRINT:
- /* print the queue */
-#if QUEUE
- dropenvelope(CurEnv, TRUE);
- printqueue();
- endpwent();
- setuid(RealUid);
- exit(EX_OK);
-#else /* QUEUE */
- usrerr("No queue to print");
- finis();
-#endif /* QUEUE */
-
- case MD_HOSTSTAT:
- mci_traverse_persistent(mci_print_persistent, NULL);
- exit(EX_OK);
- break;
-
- case MD_PURGESTAT:
- mci_traverse_persistent(mci_purge_persistent, NULL);
- exit(EX_OK);
- break;
-
- case MD_INITALIAS:
- /* initialize alias database */
- initmaps(TRUE, CurEnv);
- endpwent();
- setuid(RealUid);
- exit(ExitStat);
-
- case MD_SMTP:
- nullserver = FALSE;
- /* fall through... */
-
- case MD_DAEMON:
- /* reset DSN parameters */
- DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
- CurEnv->e_envid = NULL;
- CurEnv->e_flags &= ~(EF_RET_PARAM|EF_NO_BODY_RETN);
-
- /* don't open alias database -- done in srvrsmtp */
- break;
-
- default:
- /* open the alias database */
- initmaps(FALSE, CurEnv);
- break;
- }
-
- if (tTd(0, 15))
- {
- extern void printrules __P((void));
-
- /* print configuration table (or at least part of it) */
- if (tTd(0, 90))
- printrules();
- for (i = 0; i < MAXMAILERS; i++)
- {
- if (Mailer[i] != NULL)
- printmailer(Mailer[i]);
- }
- }
-
- /*
- ** Switch to the main envelope.
- */
-
- CurEnv = newenvelope(&MainEnvelope, CurEnv);
- MainEnvelope.e_flags = BlankEnvelope.e_flags;
-
- /*
- ** If test mode, read addresses from stdin and process.
- */
-
- if (OpMode == MD_TEST)
- {
- char buf[MAXLINE];
- SIGFUNC_DECL intindebug __P((int));
-
- if (isatty(fileno(stdin)))
- Verbose = 2;
-
- if (Verbose)
- {
- printf("ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)\n");
- printf("Enter <ruleset> <address>\n");
- }
- if (setjmp(TopFrame) > 0)
- printf("\n");
- (void) setsignal(SIGINT, intindebug);
- for (;;)
- {
- extern void testmodeline __P((char *, ENVELOPE *));
-
- if (Verbose)
- printf("> ");
- (void) fflush(stdout);
- if (fgets(buf, sizeof buf, stdin) == NULL)
- finis();
- p = strchr(buf, '\n');
- if (p != NULL)
- *p = '\0';
- if (!Verbose)
- printf("> %s\n", buf);
- testmodeline(buf, CurEnv);
- }
- }
-
-# if QUEUE
- /*
- ** If collecting stuff from the queue, go start doing that.
- */
-
- if (queuemode && OpMode != MD_DAEMON && QueueIntvl == 0)
- {
- (void) runqueue(FALSE, Verbose);
- finis();
- }
-# endif /* QUEUE */
-
- /*
- ** If a daemon, wait for a request.
- ** getrequests will always return in a child.
- ** If we should also be processing the queue, start
- ** doing it in background.
- ** We check for any errors that might have happened
- ** during startup.
- */
-
- if (OpMode == MD_DAEMON || QueueIntvl != 0)
- {
- char dtype[200];
- extern void getrequests __P((ENVELOPE *));
-
- if (!run_in_foreground && !tTd(99, 100))
- {
- /* put us in background */
- i = fork();
- if (i < 0)
- syserr("daemon: cannot fork");
- if (i != 0)
- exit(0);
-
- /* disconnect from our controlling tty */
- disconnect(2, CurEnv);
- }
-
- dtype[0] = '\0';
- if (OpMode == MD_DAEMON)
- strcat(dtype, "+SMTP");
- if (QueueIntvl != 0)
- {
- strcat(dtype, "+queueing@");
- strcat(dtype, pintvl(QueueIntvl, TRUE));
- }
- if (tTd(0, 1))
- strcat(dtype, "+debugging");
-
- sm_syslog(LOG_INFO, NOQID,
- "starting daemon (%s): %s", Version, dtype + 1);
-#ifdef XLA
- xla_create_file();
-#endif
-
-# if QUEUE
- if (queuemode)
- {
- (void) runqueue(TRUE, FALSE);
- if (OpMode != MD_DAEMON)
- {
- for (;;)
- {
- pause();
- if (DoQueueRun)
- (void) runqueue(TRUE, FALSE);
- }
- }
- }
-# endif /* QUEUE */
- dropenvelope(CurEnv, TRUE);
-
-#if DAEMON
- getrequests(CurEnv);
-
- /* drop privileges */
- (void) drop_privileges(FALSE);
-
- /* at this point we are in a child: reset state */
- (void) newenvelope(CurEnv, CurEnv);
-
- /*
- ** Get authentication data
- */
-
- p = getauthinfo(fileno(InChannel));
- define('_', p, &BlankEnvelope);
-
- /* validate the connection */
- HoldErrs = TRUE;
- nullserver = !validate_connection(&RealHostAddr, RealHostName, CurEnv);
- HoldErrs = FALSE;
-#endif /* DAEMON */
- }
-
-# if SMTP
- /*
- ** If running SMTP protocol, start collecting and executing
- ** commands. This will never return.
- */
-
- if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
- {
- char pbuf[20];
- extern void smtp __P((bool, ENVELOPE *));
-
- /*
- ** Save some macros for check_* rulesets.
- */
-
- define(macid("{client_name}", NULL), RealHostName, &BlankEnvelope);
- define(macid("{client_addr}", NULL),
- newstr(anynet_ntoa(&RealHostAddr)), &BlankEnvelope);
- if (RealHostAddr.sa.sa_family == AF_INET)
- snprintf(pbuf, sizeof pbuf, "%d", RealHostAddr.sin.sin_port);
- else
- snprintf(pbuf, sizeof pbuf, "0");
- define(macid("{client_port}", NULL), newstr(pbuf), &BlankEnvelope);
-
- smtp(nullserver, CurEnv);
- }
-# endif /* SMTP */
-
- clearenvelope(CurEnv, FALSE);
- if (OpMode == MD_VERIFY)
- {
- CurEnv->e_sendmode = SM_VERIFY;
- CurEnv->e_errormode = EM_PRINT;
- PostMasterCopy = NULL;
- HoldErrs = FALSE;
- }
- else
- {
- /* interactive -- all errors are global */
- CurEnv->e_flags |= EF_GLOBALERRS|EF_LOGSENDER;
- }
-
- /*
- ** Do basic system initialization and set the sender
- */
-
- initsys(CurEnv);
- if (warn_f_flag != '\0' && !wordinclass(RealUserName, 't'))
- auth_warning(CurEnv, "%s set sender to %s using -%c",
- RealUserName, from, warn_f_flag);
- setsender(from, CurEnv, NULL, '\0', FALSE);
- if (macvalue('s', CurEnv) == NULL)
- define('s', RealHostName, CurEnv);
-
- if (*av == NULL && !GrabTo)
- {
- CurEnv->e_flags |= EF_GLOBALERRS;
- usrerr("Recipient names must be specified");
-
- /* collect body for UUCP return */
- if (OpMode != MD_VERIFY)
- collect(InChannel, FALSE, NULL, CurEnv);
- finis();
- }
-
- /*
- ** Scan argv and deliver the message to everyone.
- */
-
- sendtoargv(av, CurEnv);
-
- /* if we have had errors sofar, arrange a meaningful exit stat */
- if (Errors > 0 && ExitStat == EX_OK)
- ExitStat = EX_USAGE;
-
- /*
- ** Read the input mail.
- */
-
- CurEnv->e_to = NULL;
- if (OpMode != MD_VERIFY || GrabTo)
- {
- long savedflags = CurEnv->e_flags & EF_FATALERRS;
-
- CurEnv->e_flags |= EF_GLOBALERRS;
- CurEnv->e_flags &= ~EF_FATALERRS;
- collect(InChannel, FALSE, NULL, CurEnv);
-
- /* bail out if message too large */
- if (bitset(EF_CLRQUEUE, CurEnv->e_flags))
- {
- finis();
- /*NOTREACHED*/
- return -1;
- }
- CurEnv->e_flags |= savedflags;
- }
- errno = 0;
-
- if (tTd(1, 1))
- printf("From person = \"%s\"\n", CurEnv->e_from.q_paddr);
-
- /*
- ** Actually send everything.
- ** If verifying, just ack.
- */
-
- CurEnv->e_from.q_flags |= QDONTSEND;
- if (tTd(1, 5))
- {
- printf("main: QDONTSEND ");
- printaddr(&CurEnv->e_from, FALSE);
- }
- CurEnv->e_to = NULL;
- CurrentLA = getla();
- sendall(CurEnv, SM_DEFAULT);
-
- /*
- ** All done.
- ** Don't send return error message if in VERIFY mode.
- */
-
- finis();
- /*NOTREACHED*/
- return -1;
-}
-
-
-SIGFUNC_DECL
-intindebug(sig)
- int sig;
-{
- longjmp(TopFrame, 1);
- return SIGFUNC_RETURN;
-}
-
-
- /*
-** FINIS -- Clean up and exit.
-**
-** Parameters:
-** none
-**
-** Returns:
-** never
-**
-** Side Effects:
-** exits sendmail
-*/
-
-void
-finis()
-{
- if (tTd(2, 1))
- {
- extern void printenvflags();
-
- printf("\n====finis: stat %d e_id=%s e_flags=",
- ExitStat,
- CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id);
- printenvflags(CurEnv);
- }
- if (tTd(2, 9))
- printopenfds(FALSE);
-
- /* if we fail in finis(), just exit */
- if (setjmp(TopFrame) != 0)
- {
- /* failed -- just give it up */
- goto forceexit;
- }
-
- /* clean up temp files */
- CurEnv->e_to = NULL;
- if (CurEnv->e_id != NULL)
- dropenvelope(CurEnv, TRUE);
-
- /* flush any cached connections */
- mci_flush(TRUE, NULL);
-
-# ifdef XLA
- /* clean up extended load average stuff */
- xla_all_end();
-# endif
-
- /* and exit */
- forceexit:
- if (LogLevel > 78)
- sm_syslog(LOG_DEBUG, CurEnv->e_id,
- "finis, pid=%d",
- getpid());
- if (ExitStat == EX_TEMPFAIL || CurEnv->e_errormode == EM_BERKNET)
- ExitStat = EX_OK;
-
- /* reset uid for process accounting */
- endpwent();
- setuid(RealUid);
-
- exit(ExitStat);
-}
- /*
-** INTSIG -- clean up on interrupt
-**
-** This just arranges to exit. It pessimises in that it
-** may resend a message.
-**
-** Parameters:
-** none.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Unlocks the current job.
-*/
-
-SIGFUNC_DECL
-intsig(sig)
- int sig;
-{
- if (LogLevel > 79)
- sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt");
- FileName = NULL;
- unlockqueue(CurEnv);
-#ifdef XLA
- xla_all_end();
-#endif
-
- /* reset uid for process accounting */
- endpwent();
- setuid(RealUid);
-
- exit(EX_OK);
-}
- /*
-** INITMACROS -- initialize the macro system
-**
-** This just involves defining some macros that are actually
-** used internally as metasymbols to be themselves.
-**
-** Parameters:
-** none.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** initializes several macros to be themselves.
-*/
-
-struct metamac MetaMacros[] =
-{
- /* LHS pattern matching characters */
- { '*', MATCHZANY }, { '+', MATCHANY }, { '-', MATCHONE },
- { '=', MATCHCLASS }, { '~', MATCHNCLASS },
-
- /* these are RHS metasymbols */
- { '#', CANONNET }, { '@', CANONHOST }, { ':', CANONUSER },
- { '>', CALLSUBR },
-
- /* the conditional operations */
- { '?', CONDIF }, { '|', CONDELSE }, { '.', CONDFI },
-
- /* the hostname lookup characters */
- { '[', HOSTBEGIN }, { ']', HOSTEND },
- { '(', LOOKUPBEGIN }, { ')', LOOKUPEND },
-
- /* miscellaneous control characters */
- { '&', MACRODEXPAND },
-
- { '\0' }
-};
-
-#define MACBINDING(name, mid) \
- stab(name, ST_MACRO, ST_ENTER)->s_macro = mid; \
- MacroName[mid] = name;
-
-void
-initmacros(e)
- register ENVELOPE *e;
-{
- register struct metamac *m;
- register int c;
- char buf[5];
- extern char *MacroName[256];
-
- for (m = MetaMacros; m->metaname != '\0'; m++)
- {
- buf[0] = m->metaval;
- buf[1] = '\0';
- define(m->metaname, newstr(buf), e);
- }
- buf[0] = MATCHREPL;
- buf[2] = '\0';
- for (c = '0'; c <= '9'; c++)
- {
- buf[1] = c;
- define(c, newstr(buf), e);
- }
-
- /* set defaults for some macros sendmail will use later */
- define('n', "MAILER-DAEMON", e);
-
- /* set up external names for some internal macros */
- MACBINDING("opMode", MID_OPMODE);
- /*XXX should probably add equivalents for all short macros here XXX*/
-}
- /*
-** DISCONNECT -- remove our connection with any foreground process
-**
-** Parameters:
-** droplev -- how "deeply" we should drop the line.
-** 0 -- ignore signals, mail back errors, make sure
-** output goes to stdout.
-** 1 -- also, make stdout go to transcript.
-** 2 -- also, disconnect from controlling terminal
-** (only for daemon mode).
-** e -- the current envelope.
-**
-** Returns:
-** none
-**
-** Side Effects:
-** Trys to insure that we are immune to vagaries of
-** the controlling tty.
-*/
-
-void
-disconnect(droplev, e)
- int droplev;
- register ENVELOPE *e;
-{
- int fd;
-
- if (tTd(52, 1))
- printf("disconnect: In %d Out %d, e=%lx\n",
- fileno(InChannel), fileno(OutChannel), (u_long) e);
- if (tTd(52, 100))
- {
- printf("don't\n");
- return;
- }
- if (LogLevel > 93)
- sm_syslog(LOG_DEBUG, e->e_id,
- "disconnect level %d",
- droplev);
-
- /* be sure we don't get nasty signals */
- (void) setsignal(SIGINT, SIG_IGN);
- (void) setsignal(SIGQUIT, SIG_IGN);
-
- /* we can't communicate with our caller, so.... */
- HoldErrs = TRUE;
- CurEnv->e_errormode = EM_MAIL;
- Verbose = 0;
- DisConnected = TRUE;
-
- /* all input from /dev/null */
- if (InChannel != stdin)
- {
- (void) fclose(InChannel);
- InChannel = stdin;
- }
- (void) freopen("/dev/null", "r", stdin);
-
- /* output to the transcript */
- if (OutChannel != stdout)
- {
- (void) fclose(OutChannel);
- OutChannel = stdout;
- }
- if (droplev > 0)
- {
- if (e->e_xfp == NULL)
- fd = open("/dev/null", O_WRONLY, 0666);
- else
- fd = fileno(e->e_xfp);
- (void) fflush(stdout);
- dup2(fd, STDOUT_FILENO);
- dup2(fd, STDERR_FILENO);
- if (e->e_xfp == NULL)
- close(fd);
- }
-
- /* drop our controlling TTY completely if possible */
- if (droplev > 1)
- {
- (void) setsid();
- errno = 0;
- }
-
-#if XDEBUG
- checkfd012("disconnect");
-#endif
-
- if (LogLevel > 71)
- sm_syslog(LOG_DEBUG, e->e_id,
- "in background, pid=%d",
- getpid());
-
- errno = 0;
-}
-
-static void
-obsolete(argv)
- char *argv[];
-{
- register char *ap;
- register char *op;
-
- while ((ap = *++argv) != NULL)
- {
- /* Return if "--" or not an option of any form. */
- if (ap[0] != '-' || ap[1] == '-')
- return;
-
- /* skip over options that do have a value */
- op = strchr(OPTIONS, ap[1]);
- if (op != NULL && *++op == ':' && ap[2] == '\0' &&
- ap[1] != 'd' &&
-#if defined(sony_news)
- ap[1] != 'E' && ap[1] != 'J' &&
-#endif
- argv[1] != NULL && argv[1][0] != '-')
- {
- argv++;
- continue;
- }
-
- /* If -C doesn't have an argument, use sendmail.cf. */
-#define __DEFPATH "sendmail.cf"
- if (ap[1] == 'C' && ap[2] == '\0')
- {
- *argv = xalloc(sizeof(__DEFPATH) + 2);
- argv[0][0] = '-';
- argv[0][1] = 'C';
- (void)strcpy(&argv[0][2], __DEFPATH);
- }
-
- /* If -q doesn't have an argument, run it once. */
- if (ap[1] == 'q' && ap[2] == '\0')
- *argv = "-q0";
-
- /* if -d doesn't have an argument, use 0-99.1 */
- if (ap[1] == 'd' && ap[2] == '\0')
- *argv = "-d0-99.1";
-
-# if defined(sony_news)
- /* if -E doesn't have an argument, use -EC */
- if (ap[1] == 'E' && ap[2] == '\0')
- *argv = "-EC";
-
- /* if -J doesn't have an argument, use -JJ */
- if (ap[1] == 'J' && ap[2] == '\0')
- *argv = "-JJ";
-# endif
- }
-}
- /*
-** AUTH_WARNING -- specify authorization warning
-**
-** Parameters:
-** e -- the current envelope.
-** msg -- the text of the message.
-** args -- arguments to the message.
-**
-** Returns:
-** none.
-*/
-
-void
-#ifdef __STDC__
-auth_warning(register ENVELOPE *e, const char *msg, ...)
-#else
-auth_warning(e, msg, va_alist)
- register ENVELOPE *e;
- const char *msg;
- va_dcl
-#endif
-{
- char buf[MAXLINE];
- VA_LOCAL_DECL
-
- if (bitset(PRIV_AUTHWARNINGS, PrivacyFlags))
- {
- register char *p;
- static char hostbuf[48];
- extern struct hostent *myhostname();
-
- if (hostbuf[0] == '\0')
- (void) myhostname(hostbuf, sizeof hostbuf);
-
- (void) snprintf(buf, sizeof buf, "%s: ", hostbuf);
- p = &buf[strlen(buf)];
- VA_START(msg);
- vsnprintf(p, SPACELEFT(buf, p), msg, ap);
- VA_END;
- addheader("X-Authentication-Warning", buf, &e->e_header);
- if (LogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Authentication-Warning: %.400s",
- buf);
- }
-}
- /*
-** GETEXTENV -- get from external environment
-**
-** Parameters:
-** envar -- the name of the variable to retrieve
-**
-** Returns:
-** The value, if any.
-*/
-
-char *
-getextenv(envar)
- const char *envar;
-{
- char **envp;
- int l;
-
- l = strlen(envar);
- for (envp = ExternalEnviron; *envp != NULL; envp++)
- {
- if (strncmp(*envp, envar, l) == 0 && (*envp)[l] == '=')
- return &(*envp)[l + 1];
- }
- return NULL;
-}
- /*
-** SETUSERENV -- set an environment in the propogated environment
-**
-** Parameters:
-** envar -- the name of the environment variable.
-** value -- the value to which it should be set. If
-** null, this is extracted from the incoming
-** environment. If that is not set, the call
-** to setuserenv is ignored.
-**
-** Returns:
-** none.
-*/
-
-void
-setuserenv(envar, value)
- const char *envar;
- const char *value;
-{
- int i;
- char **evp = UserEnviron;
- char *p;
-
- if (value == NULL)
- {
- value = getextenv(envar);
- if (value == NULL)
- return;
- }
-
- i = strlen(envar);
- p = (char *) xalloc(strlen(value) + i + 2);
- strcpy(p, envar);
- p[i++] = '=';
- strcpy(&p[i], value);
-
- while (*evp != NULL && strncmp(*evp, p, i) != 0)
- evp++;
- if (*evp != NULL)
- {
- *evp++ = p;
- }
- else if (evp < &UserEnviron[MAXUSERENVIRON])
- {
- *evp++ = p;
- *evp = NULL;
- }
-
- /* make sure it is in our environment as well */
- if (putenv(p) < 0)
- syserr("setuserenv: putenv(%s) failed", p);
-}
- /*
-** DUMPSTATE -- dump state
-**
-** For debugging.
-*/
-
-void
-dumpstate(when)
- char *when;
-{
- register char *j = macvalue('j', CurEnv);
- int rs;
-
- sm_syslog(LOG_DEBUG, CurEnv->e_id,
- "--- dumping state on %s: $j = %s ---",
- when,
- j == NULL ? "<NULL>" : j);
- if (j != NULL)
- {
- if (!wordinclass(j, 'w'))
- sm_syslog(LOG_DEBUG, CurEnv->e_id,
- "*** $j not in $=w ***");
- }
- sm_syslog(LOG_DEBUG, CurEnv->e_id, "CurChildren = %d", CurChildren);
- sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- open file descriptors: ---");
- printopenfds(TRUE);
- sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- connection cache: ---");
- mci_dump_all(TRUE);
- rs = strtorwset("debug_dumpstate", NULL, ST_FIND);
- if (rs > 0)
- {
- int stat;
- register char **pvp;
- char *pv[MAXATOM + 1];
-
- pv[0] = NULL;
- stat = rewrite(pv, rs, 0, CurEnv);
- sm_syslog(LOG_DEBUG, CurEnv->e_id,
- "--- ruleset debug_dumpstate returns stat %d, pv: ---",
- stat);
- for (pvp = pv; *pvp != NULL; pvp++)
- sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s", *pvp);
- }
- sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- end of state dump ---");
-}
-
-
-SIGFUNC_DECL
-sigusr1(sig)
- int sig;
-{
- dumpstate("user signal");
- return SIGFUNC_RETURN;
-}
-
-
-SIGFUNC_DECL
-sighup(sig)
- int sig;
-{
- if (SaveArgv[0][0] != '/')
- {
- if (LogLevel > 3)
- sm_syslog(LOG_INFO, NOQID, "could not restart: need full path");
- exit(EX_OSFILE);
- }
- if (LogLevel > 3)
- sm_syslog(LOG_INFO, NOQID, "restarting %s on signal", SaveArgv[0]);
- alarm(0);
- releasesignal(SIGHUP);
- if (drop_privileges(TRUE) != EX_OK)
- {
- if (LogLevel > 0)
- sm_syslog(LOG_ALERT, NOQID, "could not set[ug]id(%d, %d): %m",
- RunAsUid, RunAsGid);
- exit(EX_OSERR);
- }
- execve(SaveArgv[0], (ARGV_T) SaveArgv, (ARGV_T) ExternalEnviron);
- if (LogLevel > 0)
- sm_syslog(LOG_ALERT, NOQID, "could not exec %s: %m", SaveArgv[0]);
- exit(EX_OSFILE);
-}
- /*
-** DROP_PRIVILEGES -- reduce privileges to those of the RunAsUser option
-**
-** Parameters:
-** to_real_uid -- if set, drop to the real uid instead
-** of the RunAsUser.
-**
-** Returns:
-** EX_OSERR if the setuid failed.
-** EX_OK otherwise.
-*/
-
-int
-drop_privileges(to_real_uid)
- bool to_real_uid;
-{
- int rval = EX_OK;
-#ifdef NGROUPS_MAX
- GIDSET_T emptygidset[NGROUPS_MAX];
-#endif
-
- if (tTd(47, 1))
- printf("drop_privileges(%d): Real[UG]id=%d:%d, RunAs[UG]id=%d:%d\n",
- to_real_uid, RealUid, RealGid, RunAsUid, RunAsGid);
-
- if (to_real_uid)
- {
- RunAsUserName = RealUserName;
- RunAsUid = RealUid;
- RunAsGid = RealGid;
- }
-
- /* make sure no one can grab open descriptors for secret files */
- endpwent();
-
-#ifdef NGROUPS_MAX
- /* reset group permissions; these can be set later */
- emptygidset[0] = (to_real_uid || RunAsGid != 0) ? RunAsGid : getegid();
- (void) setgroups(1, emptygidset);
-#endif
-
- /* reset primary group and user id */
- if ((to_real_uid || RunAsGid != 0) && setgid(RunAsGid) < 0)
- rval = EX_OSERR;
- if ((to_real_uid || RunAsUid != 0) && setuid(RunAsUid) < 0)
- rval = EX_OSERR;
- return rval;
-}
- /*
-** FILL_FD -- make sure a file descriptor has been properly allocated
-**
-** Used to make sure that stdin/out/err are allocated on startup
-**
-** Parameters:
-** fd -- the file descriptor to be filled.
-** where -- a string used for logging. If NULL, this is
-** being called on startup, and logging should
-** not be done.
-**
-** Returns:
-** none
-*/
-
-void
-fill_fd(fd, where)
- int fd;
- char *where;
-{
- int i;
- struct stat stbuf;
-
- if (fstat(fd, &stbuf) >= 0 || errno != EBADF)
- return;
-
- if (where != NULL)
- syserr("fill_fd: %s: fd %d not open", where, fd);
- else
- MissingFds |= 1 << fd;
- i = open("/dev/null", fd == 0 ? O_RDONLY : O_WRONLY, 0666);
- if (i < 0)
- {
- syserr("!fill_fd: %s: cannot open /dev/null",
- where == NULL ? "startup" : where);
- }
- if (fd != i)
- {
- (void) dup2(i, fd);
- (void) close(i);
- }
-}
- /*
-** TESTMODELINE -- process a test mode input line
-**
-** Parameters:
-** line -- the input line.
-** e -- the current environment.
-** Syntax:
-** # a comment
-** .X process X as a configuration line
-** =X dump a configuration item (such as mailers)
-** $X dump a macro or class
-** /X try an activity
-** X normal process through rule set X
-*/
-
-void
-testmodeline(line, e)
- char *line;
- ENVELOPE *e;
-{
- register char *p;
- char *q;
- auto char *delimptr;
- int mid;
- int i, rs;
- STAB *map;
- char **s;
- struct rewrite *rw;
- ADDRESS a;
- static int tryflags = RF_COPYNONE;
- char exbuf[MAXLINE];
- extern bool invalidaddr __P((char *, char *));
- extern char *crackaddr __P((char *));
- extern void dump_class __P((STAB *, int));
- extern void translate_dollars __P((char *));
- extern void help __P((char *));
-
- switch (line[0])
- {
- case '#':
- case 0:
- return;
-
- case '?':
- help("-bt");
- return;
-
- case '.': /* config-style settings */
- switch (line[1])
- {
- case 'D':
- mid = macid(&line[2], &delimptr);
- if (mid == '\0')
- return;
- translate_dollars(delimptr);
- define(mid, newstr(delimptr), e);
- break;
-
- case 'C':
- if (line[2] == '\0') /* not to call syserr() */
- return;
-
- mid = macid(&line[2], &delimptr);
- if (mid == '\0')
- return;
- translate_dollars(delimptr);
- expand(delimptr, exbuf, sizeof exbuf, e);
- p = exbuf;
- while (*p != '\0')
- {
- register char *wd;
- char delim;
-
- while (*p != '\0' && isascii(*p) && isspace(*p))
- p++;
- wd = p;
- while (*p != '\0' && !(isascii(*p) && isspace(*p)))
- p++;
- delim = *p;
- *p = '\0';
- if (wd[0] != '\0')
- setclass(mid, wd);
- *p = delim;
- }
- break;
-
- case '\0':
- printf("Usage: .[DC]macro value(s)\n");
- break;
-
- default:
- printf("Unknown \".\" command %s\n", line);
- break;
- }
- return;
-
- case '=': /* config-style settings */
- switch (line[1])
- {
- case 'S': /* dump rule set */
- rs = strtorwset(&line[2], NULL, ST_FIND);
- if (rs < 0)
- {
- printf("Undefined ruleset %s\n", &line[2]);
- return;
- }
- rw = RewriteRules[rs];
- if (rw == NULL)
- return;
- do
- {
- putchar('R');
- s = rw->r_lhs;
- while (*s != NULL)
- {
- xputs(*s++);
- putchar(' ');
- }
- putchar('\t');
- putchar('\t');
- s = rw->r_rhs;
- while (*s != NULL)
- {
- xputs(*s++);
- putchar(' ');
- }
- putchar('\n');
- } while ((rw = rw->r_next) != NULL);
- break;
-
- case 'M':
- for (i = 0; i < MAXMAILERS; i++)
- {
- if (Mailer[i] != NULL)
- printmailer(Mailer[i]);
- }
- break;
-
- case '\0':
- printf("Usage: =Sruleset or =M\n");
- break;
-
- default:
- printf("Unknown \"=\" command %s\n", line);
- break;
- }
- return;
-
- case '-': /* set command-line-like opts */
- switch (line[1])
- {
- case 'd':
- tTflag(&line[2]);
- break;
-
- case '\0':
- printf("Usage: -d{debug arguments}\n");
- break;
-
- default:
- printf("Unknown \"-\" command %s\n", line);
- break;
- }
- return;
-
- case '$':
- if (line[1] == '=')
- {
- mid = macid(&line[2], NULL);
- if (mid != '\0')
- stabapply(dump_class, mid);
- return;
- }
- mid = macid(&line[1], NULL);
- if (mid == '\0')
- return;
- p = macvalue(mid, e);
- if (p == NULL)
- printf("Undefined\n");
- else
- {
- xputs(p);
- printf("\n");
- }
- return;
-
- case '/': /* miscellaneous commands */
- p = &line[strlen(line)];
- while (--p >= line && isascii(*p) && isspace(*p))
- *p = '\0';
- p = strpbrk(line, " \t");
- if (p != NULL)
- {
- while (isascii(*p) && isspace(*p))
- *p++ = '\0';
- }
- else
- p = "";
- if (line[1] == '\0')
- {
- printf("Usage: /[canon|map|mx|parse|try|tryflags]\n");
- return;
- }
- if (strcasecmp(&line[1], "mx") == 0)
- {
-#if NAMED_BIND
- /* look up MX records */
- int nmx;
- auto int rcode;
- char *mxhosts[MAXMXHOSTS + 1];
-
- if (*p == '\0')
- {
- printf("Usage: /mx address\n");
- return;
- }
- nmx = getmxrr(p, mxhosts, FALSE, &rcode);
- printf("getmxrr(%s) returns %d value(s):\n", p, nmx);
- for (i = 0; i < nmx; i++)
- printf("\t%s\n", mxhosts[i]);
-#else
- printf("No MX code compiled in\n");
-#endif
- }
- else if (strcasecmp(&line[1], "canon") == 0)
- {
- char host[MAXHOSTNAMELEN];
-
- if (*p == '\0')
- {
- printf("Usage: /canon address\n");
- return;
- }
- else if (strlen(p) >= sizeof host)
- {
- printf("Name too long\n");
- return;
- }
- strcpy(host, p);
- (void) getcanonname(host, sizeof(host), HasWildcardMX);
- printf("getcanonname(%s) returns %s\n", p, host);
- }
- else if (strcasecmp(&line[1], "map") == 0)
- {
- auto int rcode = EX_OK;
-
- if (*p == '\0')
- {
- printf("Usage: /map mapname key\n");
- return;
- }
- for (q = p; *q != '\0' && !isspace(*q); q++)
- continue;
- if (*q == '\0')
- {
- printf("No key specified\n");
- return;
- }
- *q++ = '\0';
- map = stab(p, ST_MAP, ST_FIND);
- if (map == NULL)
- {
- printf("Map named \"%s\" not found\n", p);
- return;
- }
- if (!bitset(MF_OPEN, map->s_map.map_mflags))
- {
- printf("Map named \"%s\" not open\n", p);
- return;
- }
- printf("map_lookup: %s (%s) ", p, q);
- p = (*map->s_map.map_class->map_lookup)
- (&map->s_map, q, NULL, &rcode);
- if (p == NULL)
- printf("no match (%d)\n", rcode);
- else
- printf("returns %s (%d)\n", p, rcode);
- }
- else if (strcasecmp(&line[1], "try") == 0)
- {
- MAILER *m;
- STAB *s;
- auto int rcode = EX_OK;
-
- q = strpbrk(p, " \t");
- if (q != NULL)
- {
- while (isascii(*q) && isspace(*q))
- *q++ = '\0';
- }
- if (q == NULL || *q == '\0')
- {
- printf("Usage: /try mailer address\n");
- return;
- }
- s = stab(p, ST_MAILER, ST_FIND);
- if (s == NULL)
- {
- printf("Unknown mailer %s\n", p);
- return;
- }
- m = s->s_mailer;
- printf("Trying %s %s address %s for mailer %s\n",
- bitset(RF_HEADERADDR, tryflags) ? "header" : "envelope",
- bitset(RF_SENDERADDR, tryflags) ? "sender" : "recipient",
- q, p);
- p = remotename(q, m, tryflags, &rcode, CurEnv);
- printf("Rcode = %d, addr = %s\n",
- rcode, p == NULL ? "<NULL>" : p);
- e->e_to = NULL;
- }
- else if (strcasecmp(&line[1], "tryflags") == 0)
- {
- if (*p == '\0')
- {
- printf("Usage: /tryflags [Hh|Ee][Ss|Rr]\n");
- return;
- }
- for (; *p != '\0'; p++)
- {
- switch (*p)
- {
- case 'H':
- case 'h':
- tryflags |= RF_HEADERADDR;
- break;
-
- case 'E':
- case 'e':
- tryflags &= ~RF_HEADERADDR;
- break;
-
- case 'S':
- case 's':
- tryflags |= RF_SENDERADDR;
- break;
-
- case 'R':
- case 'r':
- tryflags &= ~RF_SENDERADDR;
- break;
- }
- }
- }
- else if (strcasecmp(&line[1], "parse") == 0)
- {
- if (*p == '\0')
- {
- printf("Usage: /parse address\n");
- return;
- }
- q = crackaddr(p);
- printf("Cracked address = ");
- xputs(q);
- printf("\nParsing %s %s address\n",
- bitset(RF_HEADERADDR, tryflags) ? "header" : "envelope",
- bitset(RF_SENDERADDR, tryflags) ? "sender" : "recipient");
- if (parseaddr(p, &a, tryflags, '\0', NULL, e) == NULL)
- printf("Cannot parse\n");
- else if (a.q_host != NULL && a.q_host[0] != '\0')
- printf("mailer %s, host %s, user %s\n",
- a.q_mailer->m_name, a.q_host, a.q_user);
- else
- printf("mailer %s, user %s\n",
- a.q_mailer->m_name, a.q_user);
- e->e_to = NULL;
- }
- else
- {
- printf("Unknown \"/\" command %s\n", line);
- }
- return;
- }
-
- for (p = line; isascii(*p) && isspace(*p); p++)
- continue;
- q = p;
- while (*p != '\0' && !(isascii(*p) && isspace(*p)))
- p++;
- if (*p == '\0')
- {
- printf("No address!\n");
- return;
- }
- *p = '\0';
- if (invalidaddr(p + 1, NULL))
- return;
- do
- {
- register char **pvp;
- char pvpbuf[PSBUFSIZE];
-
- pvp = prescan(++p, ',', pvpbuf, sizeof pvpbuf,
- &delimptr, NULL);
- if (pvp == NULL)
- continue;
- p = q;
- while (*p != '\0')
- {
- int stat;
- int rs = strtorwset(p, NULL, ST_FIND);
-
- if (rs < 0)
- {
- printf("Undefined ruleset %s\n", p);
- break;
- }
- stat = rewrite(pvp, rs, 0, e);
- if (stat != EX_OK)
- printf("== Ruleset %s (%d) status %d\n",
- p, rs, stat);
- while (*p != '\0' && *p++ != ',')
- continue;
- }
- } while (*(p = delimptr) != '\0');
-}
-
-
-void
-dump_class(s, id)
- register STAB *s;
- int id;
-{
- if (s->s_type != ST_CLASS)
- return;
- if (bitnset(id & 0xff, s->s_class))
- printf("%s\n", s->s_name);
-}
OpenPOWER on IntegriCloud