📄 daemon.c
字号:
/* * Copyright (c) 1983 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. */#include <errno.h>#include "sendmail.h"#ifndef lint#ifdef DAEMONstatic char sccsid[] = "@(#)daemon.c 8.48 (Berkeley) 4/18/94 (with daemon mode)";#elsestatic char sccsid[] = "@(#)daemon.c 8.48 (Berkeley) 4/18/94 (without daemon mode)";#endif#endif /* not lint */#ifdef DAEMON# include <netdb.h># include <arpa/inet.h>#if NAMED_BIND# include <arpa/nameser.h># include <resolv.h>#endif/*** DAEMON.C -- routines to use when running as a daemon.**** This entire file is highly dependent on the 4.2 BSD** interprocess communication primitives. No attempt has** been made to make this file portable to Version 7,** Version 6, MPX files, etc. If you should try such a** thing yourself, I recommend chucking the entire file** and starting from scratch. Basic semantics are:**** getrequests()** Opens a port and initiates a connection.** Returns in a child. Must set InChannel and** OutChannel appropriately.** clrdaemon()** Close any open files associated with getting** the connection; this is used when running the queue,** etc., to avoid having extra file descriptors during** the queue run and to avoid confusing the network** code (if it cares).** makeconnection(host, port, outfile, infile, usesecureport)** Make a connection to the named host on the given** port. Set *outfile and *infile to the files** appropriate for communication. Returns zero on** success, else an exit status describing the** error.** host_map_lookup(map, hbuf, avp, pstat)** Convert the entry in hbuf into a canonical form.*//*** GETREQUESTS -- open mail IPC port and get requests.**** Parameters:** none.**** Returns:** none.**** Side Effects:** Waits until some interesting activity occurs. When** it does, a child is created to process it, and the** parent waits for completion. Return from this** routine is always in the child. The file pointers** "InChannel" and "OutChannel" should be set to point** to the communication channel.*/int DaemonSocket = -1; /* fd describing socket */SOCKADDR DaemonAddr; /* socket for incoming */int ListenQueueSize = 10; /* size of listen queue */int TcpRcvBufferSize = 0; /* size of TCP receive buffer */int TcpSndBufferSize = 0; /* size of TCP send buffer */getrequests(){ int t; bool refusingconnections = TRUE; FILE *pidf; int socksize;#ifdef XDEBUG bool j_has_dot;#endif extern void reapchild(); /* ** Set up the address for the mailer. */ if (DaemonAddr.sin.sin_family == 0) DaemonAddr.sin.sin_family = AF_INET; if (DaemonAddr.sin.sin_addr.s_addr == 0) DaemonAddr.sin.sin_addr.s_addr = INADDR_ANY; if (DaemonAddr.sin.sin_port == 0) { register struct servent *sp; sp = getservbyname("smtp", "tcp"); if (sp == NULL) { syserr("554 service \"smtp\" unknown"); DaemonAddr.sin.sin_port = htons(25); } else DaemonAddr.sin.sin_port = sp->s_port; } /* ** Try to actually open the connection. */ if (tTd(15, 1)) printf("getrequests: port 0x%x\n", DaemonAddr.sin.sin_port); /* get a socket for the SMTP connection */ socksize = opendaemonsocket(TRUE); (void) setsignal(SIGCHLD, reapchild); /* write the pid to the log file for posterity */ pidf = fopen(PidFile, "w"); if (pidf != NULL) { extern char *CommandLineArgs; /* write the process id on line 1 */ fprintf(pidf, "%d\n", getpid()); /* line 2 contains all command line flags */ fprintf(pidf, "%s\n", CommandLineArgs); /* flush and close */ fclose(pidf); }#ifdef XDEBUG { char jbuf[MAXHOSTNAMELEN]; expand("\201j", jbuf, &jbuf[sizeof jbuf - 1], CurEnv); j_has_dot = strchr(jbuf, '.') != NULL; }#endif if (tTd(15, 1)) printf("getrequests: %d\n", DaemonSocket); for (;;) { register int pid; auto int lotherend; extern bool refuseconnections(); /* see if we are rejecting connections */ CurrentLA = getla(); if (refuseconnections()) { if (DaemonSocket >= 0) { /* close socket so peer will fail quickly */ (void) close(DaemonSocket); DaemonSocket = -1; } refusingconnections = TRUE; setproctitle("rejecting connections: load average: %d", CurrentLA); sleep(15); continue; } if (refusingconnections) { /* start listening again */ (void) opendaemonsocket(FALSE); setproctitle("accepting connections"); refusingconnections = FALSE; }#ifdef XDEBUG /* check for disaster */ { register STAB *s; char jbuf[MAXHOSTNAMELEN]; expand("\201j", jbuf, &jbuf[sizeof jbuf - 1], CurEnv); if ((s = stab(jbuf, ST_CLASS, ST_FIND)) == NULL || !bitnset('w', s->s_class)) { dumpstate("daemon lost $j"); syslog(LOG_ALERT, "daemon process doesn't have $j in $=w; see syslog"); abort(); } else if (j_has_dot && strchr(jbuf, '.') == NULL) { dumpstate("daemon $j lost dot"); syslog(LOG_ALERT, "daemon process $j lost dot; see syslog"); abort(); } }#endif /* wait for a connection */ do { errno = 0; lotherend = socksize; t = accept(DaemonSocket, (struct sockaddr *)&RealHostAddr, &lotherend); } while (t < 0 && errno == EINTR); if (t < 0) { syserr("getrequests: accept"); sleep(5); continue; } /* ** Create a subprocess to process the mail. */ if (tTd(15, 2)) printf("getrequests: forking (fd = %d)\n", t); pid = fork(); if (pid < 0) { syserr("daemon: cannot fork"); sleep(10); (void) close(t); continue; } if (pid == 0) { char *p; extern char *hostnamebyanyaddr(); /* ** CHILD -- return to caller. ** Collect verified idea of sending host. ** Verify calling user id if possible here. */ (void) setsignal(SIGCHLD, SIG_DFL); DisConnected = FALSE; setproctitle("startup with %s", anynet_ntoa(&RealHostAddr)); /* determine host name */ p = hostnamebyanyaddr(&RealHostAddr); RealHostName = newstr(p); setproctitle("startup with %s", p);#ifdef LOG if (LogLevel > 11) { /* log connection information */ syslog(LOG_INFO, "connect from %s (%s)", RealHostName, anynet_ntoa(&RealHostAddr)); }#endif (void) close(DaemonSocket); if ((InChannel = fdopen(t, "r")) == NULL || (t = dup(t)) < 0 || (OutChannel = fdopen(t, "w")) == NULL) { syserr("cannot open SMTP server channel, fd=%d", t); exit(0); } /* should we check for illegal connection here? XXX */#ifdef XLA if (!xla_host_ok(RealHostName)) { message("421 Too many SMTP sessions for this host"); exit(0); }#endif if (tTd(15, 2)) printf("getreq: returning\n"); return; } /* close the port so that others will hang (for a while) */ (void) close(t); } /*NOTREACHED*/}/*** OPENDAEMONSOCKET -- open the SMTP socket**** Deals with setting all appropriate options. DaemonAddr must** be set up in advance.**** Parameters:** firsttime -- set if this is the initial open.**** Returns:** Size in bytes of the daemon socket addr.**** Side Effects:** Leaves DaemonSocket set to the open socket.** Exits if the socket cannot be created.*/#define MAXOPENTRIES 10 /* maximum number of tries to open connection */intopendaemonsocket(firsttime) bool firsttime;{ int on = 1; int socksize; int ntries = 0; int saveerrno; if (tTd(15, 2)) printf("opendaemonsocket()\n"); do { if (ntries > 0) sleep(5); if (firsttime || DaemonSocket < 0) { DaemonSocket = socket(DaemonAddr.sa.sa_family, SOCK_STREAM, 0); if (DaemonSocket < 0) { /* probably another daemon already */ saveerrno = errno; syserr("opendaemonsocket: can't create server SMTP socket"); severe:# ifdef LOG if (LogLevel > 0) syslog(LOG_ALERT, "problem creating SMTP socket");# endif /* LOG */ DaemonSocket = -1; continue; } /* turn on network debugging? */ if (tTd(15, 101)) (void) setsockopt(DaemonSocket, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof on); (void) setsockopt(DaemonSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof on); (void) setsockopt(DaemonSocket, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, sizeof on);#ifdef SO_RCVBUF if (TcpRcvBufferSize > 0) { if (setsockopt(DaemonSocket, SOL_SOCKET, SO_RCVBUF, (char *) &TcpRcvBufferSize, sizeof(TcpRcvBufferSize)) < 0) syserr("getrequests: setsockopt(SO_RCVBUF)"); }#endif switch (DaemonAddr.sa.sa_family) {# ifdef NETINET case AF_INET: socksize = sizeof DaemonAddr.sin; break;# endif# ifdef NETISO case AF_ISO: socksize = sizeof DaemonAddr.siso; break;# endif default: socksize = sizeof DaemonAddr; break; } if (bind(DaemonSocket, &DaemonAddr.sa, socksize) < 0) { saveerrno = errno; syserr("getrequests: cannot bind"); (void) close(DaemonSocket); goto severe; } } if (!firsttime && listen(DaemonSocket, ListenQueueSize) < 0) { saveerrno = errno; syserr("getrequests: cannot listen"); (void) close(DaemonSocket); goto severe; } return socksize; } while (ntries++ < MAXOPENTRIES && transienterror(saveerrno)); finis();}/*** CLRDAEMON -- reset the daemon connection**** Parameters:** none.**** Returns:** none.**** Side Effects:** releases any resources used by the passive daemon.*/clrdaemon(){ if (DaemonSocket >= 0) (void) close(DaemonSocket); DaemonSocket = -1;}/*** SETDAEMONOPTIONS -- set options for running the daemon**** Parameters:** p -- the options line.**** Returns:** none.*/setdaemonoptions(p) register char *p;{ if (DaemonAddr.sa.sa_family == AF_UNSPEC) DaemonAddr.sa.sa_family = AF_INET; while (p != NULL) { register char *f; register char *v; while (isascii(*p) && isspace(*p)) p++; if (*p == '\0') break; f = p; p = strchr(p, ','); if (p != NULL) *p++ = '\0'; v = strchr(f, '='); if (v == NULL) continue; while (isascii(*++v) && isspace(*v)) continue; switch (*f) { case 'F': /* address family */ if (isascii(*v) && isdigit(*v)) DaemonAddr.sa.sa_family = atoi(v);#ifdef NETINET else if (strcasecmp(v, "inet") == 0) DaemonAddr.sa.sa_family = AF_INET;#endif#ifdef NETISO else if (strcasecmp(v, "iso") == 0) DaemonAddr.sa.sa_family = AF_ISO;#endif#ifdef NETNS else if (strcasecmp(v, "ns") == 0) DaemonAddr.sa.sa_family = AF_NS;#endif#ifdef NETX25 else if (strcasecmp(v, "x.25") == 0) DaemonAddr.sa.sa_family = AF_CCITT;#endif else syserr("554 Unknown address family %s in Family=option", v); break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -