⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 daemon.c

📁 < linux网络编程工具>>配套源码
💻 C
📖 第 1 页 / 共 5 页
字号:
/*
 * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
 *	All rights reserved.
 * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
 * Copyright (c) 1988, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * By using this file, you agree to the terms and conditions set
 * forth in the LICENSE file which can be found at the top level of
 * the sendmail distribution.
 *
 */

#include <sendmail.h>


#ifndef lint
# ifdef DAEMON
static char id[] = "@(#)$Id: daemon.c,v 8.401.4.18 2000/09/21 21:52:16 ca Exp $ (with daemon mode)";
# else /* DAEMON */
static char id[] = "@(#)$Id: daemon.c,v 8.401.4.18 2000/09/21 21:52:16 ca Exp $ (without daemon mode)";
# endif /* DAEMON */
#endif /* ! lint */

#if defined(SOCK_STREAM) || defined(__GNU_LIBRARY__)
# define USE_SOCK_STREAM	1
#endif /* defined(SOCK_STREAM) || defined(__GNU_LIBRARY__) */

#if DAEMON || defined(USE_SOCK_STREAM)
# if NETINET || NETINET6
#  include <arpa/inet.h>
# endif /* NETINET || NETINET6 */
# if NAMED_BIND
#  ifndef NO_DATA
#   define NO_DATA	NO_ADDRESS
#  endif /* ! NO_DATA */
# endif /* NAMED_BIND */
#endif /* DAEMON || defined(USE_SOCK_STREAM) */

#if DAEMON

# if STARTTLS
#    include <openssl/rand.h>
# endif /* STARTTLS */

# include <sys/time.h>

# if IP_SRCROUTE && NETINET
#  include <netinet/in_systm.h>
#  include <netinet/ip.h>
#  if HAS_IN_H
#   include <netinet/in.h>
#   ifndef IPOPTION
#    define IPOPTION	ip_opts
#    define IP_LIST	ip_opts
#    define IP_DST	ip_dst
#   endif /* ! IPOPTION */
#  else /* HAS_IN_H */
#   include <netinet/ip_var.h>
#   ifndef IPOPTION
#    define IPOPTION	ipoption
#    define IP_LIST	ipopt_list
#    define IP_DST	ipopt_dst
#   endif /* ! IPOPTION */
#  endif /* HAS_IN_H */
# endif /* IP_SRCROUTE && NETINET */

/* structure to describe a daemon */
struct daemon
{
	int		d_socket;	/* fd for socket */
	SOCKADDR	d_addr;		/* socket for incoming */
	u_short		d_port;		/* port number */
	int		d_listenqueue;	/* size of listen queue */
	int		d_tcprcvbufsize;	/* size of TCP receive buffer */
	int		d_tcpsndbufsize;	/* size of TCP send buffer */
	time_t		d_refuse_connections_until;
	bool		d_firsttime;
	int		d_socksize;
	BITMAP256	d_flags;	/* flags; see sendmail.h */
	char		*d_mflags;	/* flags for use in macro */
	char		*d_name;	/* user-supplied name */
};

typedef struct daemon DAEMON_T;

static void	connecttimeout __P((void));
static int	opendaemonsocket __P((struct daemon *, bool));
static u_short	setupdaemon __P((SOCKADDR *));

/*
**  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(e)
**		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, e)
**		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.
*/

static DAEMON_T	Daemons[MAXDAEMONS];
static int	ndaemons = 0;			/* actual number of daemons */

/* options for client */
static int	TcpRcvBufferSize = 0;	/* size of TCP receive buffer */
static int	TcpSndBufferSize = 0;	/* size of TCP send buffer */

/*
**  GETREQUESTS -- open mail IPC port and get requests.
**
**	Parameters:
**		e -- the current envelope.
**
**	Returns:
**		pointer to flags.
**
**	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.
*/

BITMAP256 *
getrequests(e)
	ENVELOPE *e;
{
	int t;
	time_t last_disk_space_check = 0;
	int idx, curdaemon = -1;
	int i, olddaemon = 0;
# if XDEBUG
	bool j_has_dot;
# endif /* XDEBUG */
	char status[MAXLINE];
	SOCKADDR sa;
	SOCKADDR_LEN_T len = sizeof sa;
# if NETUNIX
	extern int ControlSocket;
# endif /* NETUNIX */
	extern ENVELOPE BlankEnvelope;

#define D(x,idx)	x[idx]


	for (idx = 0; idx < ndaemons; idx++)
	{
		Daemons[idx].d_port = setupdaemon(&(Daemons[idx].d_addr));
		Daemons[idx].d_firsttime = TRUE;
		Daemons[idx].d_refuse_connections_until = (time_t) 0;
	}
	/*
	**  Try to actually open the connection.
	*/

	if (tTd(15, 1))
	{
		for (idx = 0; idx < ndaemons; idx++)
			dprintf("getrequests: daemon %s: port %d\n",
				Daemons[idx].d_name,
				ntohs(Daemons[idx].d_port));
	}

	/* get a socket for the SMTP connection */
	for (idx = 0; idx < ndaemons; idx++)
		Daemons[idx].d_socksize = opendaemonsocket(&Daemons[idx], TRUE);

	if (opencontrolsocket() < 0)
		sm_syslog(LOG_WARNING, NOQID,
			  "daemon could not open control socket %s: %s",
			  ControlSocketName, errstring(errno));

	(void) setsignal(SIGCHLD, reapchild);

	/* write the pid to file */
	log_sendmail_pid(e);

# if XDEBUG
	{
		char jbuf[MAXHOSTNAMELEN];

		expand("\201j", jbuf, sizeof jbuf, e);
		j_has_dot = strchr(jbuf, '.') != NULL;
	}
# endif /* XDEBUG */

	/* Add parent process as first item */
	proc_list_add(getpid(), "Sendmail daemon", PROC_DAEMON);

	if (tTd(15, 1))
	{
		for (idx = 0; idx < ndaemons; idx++)
			dprintf("getrequests: daemon %s: %d\n",
				Daemons[idx].d_name,
				Daemons[idx].d_socket);
	}

	for (;;)
	{
		register pid_t pid;
		auto SOCKADDR_LEN_T lotherend;
		bool timedout = FALSE;
		bool control = FALSE;
		int save_errno;
		int pipefd[2];
# if STARTTLS
		long seed;
		time_t timenow;
# endif /* STARTTLS */

		/* see if we are rejecting connections */
		(void) blocksignal(SIGALRM);

		for (idx = 0; idx < ndaemons; idx++)
		{
			if (curtime() < Daemons[idx].d_refuse_connections_until)
				continue;
			if (refuseconnections(Daemons[idx].d_name, e, idx))
			{
				if (Daemons[idx].d_socket >= 0)
				{
				       /* close socket so peer fails quickly */
				       (void) close(Daemons[idx].d_socket);
				       Daemons[idx].d_socket = -1;
				}

				/* refuse connections for next 15 seconds */
				Daemons[idx].d_refuse_connections_until = curtime() + 15;
			}
			else if (Daemons[idx].d_socket < 0 ||
				 Daemons[idx].d_firsttime)
			{
			      if (!Daemons[idx].d_firsttime && LogLevel >= 9)
				sm_syslog(LOG_INFO, NOQID,
					  "accepting connections again for daemon %s",
					  Daemons[idx].d_name);

			      /* arrange to (re)open the socket if needed */
			      (void) opendaemonsocket(&Daemons[idx], FALSE);
			      Daemons[idx].d_firsttime = FALSE;
			}
		}

		if (curtime() >= last_disk_space_check)
		{
			if (!enoughdiskspace(MinBlocksFree + 1, FALSE))
			{
				if (!bitnset(D_ETRNONLY, Daemons[idx].d_flags))
				{
					/* log only if not logged before */
					if (LogLevel >= 9)
						sm_syslog(LOG_INFO, NOQID,
							  "rejecting new messages: min free: %ld",
							  MinBlocksFree);
					sm_setproctitle(TRUE, e,
							"rejecting new messages: min free: %ld",
							 MinBlocksFree);
					setbitn(D_ETRNONLY, Daemons[idx].d_flags);
				}
			}
			else if (bitnset(D_ETRNONLY, Daemons[idx].d_flags))
			{
				/* log only if not logged before */
				if (LogLevel >= 9)
					sm_syslog(LOG_INFO, NOQID,
						  "accepting new messages (again)");
				/* title will be set below */
				clrbitn(D_ETRNONLY, Daemons[idx].d_flags);
			}
			/* only check disk space once a minute */
			last_disk_space_check = curtime() + 60;
		}

# if XDEBUG
		/* check for disaster */
		{
			char jbuf[MAXHOSTNAMELEN];

			expand("\201j", jbuf, sizeof jbuf, e);
			if (!wordinclass(jbuf, 'w'))
			{
				dumpstate("daemon lost $j");
				sm_syslog(LOG_ALERT, NOQID,
					  "daemon process doesn't have $j in $=w; see syslog");
				abort();
			}
			else if (j_has_dot && strchr(jbuf, '.') == NULL)
			{
				dumpstate("daemon $j lost dot");
				sm_syslog(LOG_ALERT, NOQID,
					  "daemon process $j lost dot; see syslog");
				abort();
			}
		}
# endif /* XDEBUG */

# if 0
		/*
		**  Andrew Sun <asun@ieps-sun.ml.com> claims that this will
		**  fix the SVr4 problem.  But it seems to have gone away,
		**  so is it worth doing this?
		*/

		if (DaemonSocket >= 0 &&
		    SetNonBlocking(DaemonSocket, FALSE) < 0)
			log an error here;
# endif /* 0 */
		(void) releasesignal(SIGALRM);

		for (;;)
		{
			int highest = -1;
			fd_set readfds;
			struct timeval timeout;

			FD_ZERO(&readfds);

			for (idx = 0; idx < ndaemons; idx++)
			{
				/* wait for a connection */
				if (Daemons[idx].d_socket >= 0)
				{
					if (!bitnset(D_ETRNONLY, Daemons[idx].d_flags))
					{
						sm_setproctitle(TRUE, e,
								"accepting connections");
					}
					if (Daemons[idx].d_socket > highest)
						highest = Daemons[idx].d_socket;
					FD_SET((u_int)Daemons[idx].d_socket, &readfds);
				}
			}

# if NETUNIX
			if (ControlSocket >= 0)
			{
				if (ControlSocket > highest)
					highest = ControlSocket;
				FD_SET(ControlSocket, &readfds);
			}
# endif /* NETUNIX */

			/*
			**  if one socket is closed, set the timeout
			**  to 5 seconds (so it might get reopened soon),
			**  otherwise (all sockets open) 60.
			*/
			idx = 0;
			while (idx < ndaemons && Daemons[idx].d_socket >= 0)
				idx++;
			if (idx < ndaemons)
				timeout.tv_sec = 5;
			else
				timeout.tv_sec = 60;
			timeout.tv_usec = 0;

			t = select(highest + 1, FDSET_CAST &readfds,
				   NULL, NULL, &timeout);

			if (DoQueueRun)
				(void) runqueue(TRUE, FALSE);
			if (t <= 0)
			{
				timedout = TRUE;
				break;
			}

			control = FALSE;
			errno = 0;
			curdaemon = -1;

			/* look "round-robin" for an active socket */
			if ((idx = olddaemon + 1) >= ndaemons)
				idx = 0;
			for (i = 0; i < ndaemons; i++)
			{
				if (Daemons[idx].d_socket >= 0 &&
				    FD_ISSET(Daemons[idx].d_socket, &readfds))
				{
					lotherend = Daemons[idx].d_socksize;
					t = accept(Daemons[idx].d_socket,
						   (struct sockaddr *)&RealHostAddr,
						   &lotherend);
					olddaemon = curdaemon = idx;
					break;
				}
				if (++idx >= ndaemons)
					idx = 0;
			}
# if NETUNIX
			if (curdaemon == -1 && ControlSocket >= 0 &&
				 FD_ISSET(ControlSocket, &readfds))
			{
				struct sockaddr_un sa_un;

				lotherend = sizeof sa_un;
				t = accept(ControlSocket,
					   (struct sockaddr *)&sa_un,
					   &lotherend);
				control = TRUE;
			}
# endif /* NETUNIX */
			if (t >= 0 || errno != EINTR)
				break;
		}
		if (timedout)
		{
			timedout = FALSE;
			continue;
		}
		save_errno = errno;
		(void) blocksignal(SIGALRM);
		if (t < 0)
		{
			errno = save_errno;
			syserr("getrequests: accept");

			/* arrange to re-open the socket next time around */
			(void) close(Daemons[curdaemon].d_socket);
			Daemons[curdaemon].d_socket = -1;
# if SO_REUSEADDR_IS_BROKEN
			/*
			**  Give time for bound socket to be released.
			**  This creates a denial-of-service if you can
			**  force accept() to fail on affected systems.
			*/

			Daemons[curdaemon].d_refuse_connections_until = curtime() + 15;
# endif /* SO_REUSEADDR_IS_BROKEN */
			continue;
		}

		if (!control)
		{
			/* set some daemon related macros */
			switch (Daemons[curdaemon].d_addr.sa.sa_family)
			{
			  case AF_UNSPEC:
				define(macid("{daemon_family}", NULL),
				       "unspec", &BlankEnvelope);
				break;
# if NETINET
			  case AF_INET:
				define(macid("{daemon_family}", NULL),
				       "inet", &BlankEnvelope);
				break;
# endif /* NETINET */
# if NETINET6
			  case AF_INET6:
				define(macid("{daemon_family}", NULL),
				       "inet6", &BlankEnvelope);
				break;
# endif /* NETINET6 */
# if NETISO
			  case AF_ISO:
				define(macid("{daemon_family}", NULL),
				       "iso", &BlankEnvelope);
				break;
# endif /* NETISO */
# if NETNS
			  case AF_NS:
				define(macid("{daemon_family}", NULL),
				       "ns", &BlankEnvelope);
				break;
# endif /* NETNS */
# if NETX25
			  case AF_CCITT:
				define(macid("{daemon_family}", NULL),
				       "x.25", &BlankEnvelope);
				break;
# endif /* NETX25 */
			}
			define(macid("{daemon_name}", NULL),
			       Daemons[curdaemon].d_name, &BlankEnvelope);
			if (Daemons[curdaemon].d_mflags != NULL)
				define(macid("{daemon_flags}", NULL),
				       Daemons[curdaemon].d_mflags,
				       &BlankEnvelope);
			else
				define(macid("{daemon_flags}", NULL),
				       "", &BlankEnvelope);
		}

		/*
		**  Create a subprocess to process the mail.
		*/

		if (tTd(15, 2))
			dprintf("getrequests: forking (fd = %d)\n", t);

		/*
		**  advance state of PRNG
		**  this is necessary because otherwise all child processes
		**  will produce the same PRN sequence and hence the selection
		**  of a queue directory (and other things, e.g., MX selection)
		**  are not "really" random.
		*/
# if STARTTLS
		seed = get_random();
		RAND_seed((void *) &last_disk_space_check,
			sizeof last_disk_space_check);
		timenow = curtime();
		RAND_seed((void *) &timenow, sizeof timenow);
		RAND_seed((void *) &seed, sizeof seed);
# else /* STARTTLS */
		(void) get_random();
# endif /* STARTTLS */

		/*
		**  Create a pipe to keep the child from writing to the
		**  socket until after the parent has closed it.  Otherwise
		**  the parent may hang if the child has closed it first.
		*/

		if (pipe(pipefd) < 0)
			pipefd[0] = pipefd[1] = -1;

		(void) blocksignal(SIGCHLD);
		pid = fork();
		if (pid < 0)
		{
			syserr("daemon: cannot fork");
			if (pipefd[0] != -1)
			{
				(void) close(pipefd[0]);
				(void) close(pipefd[1]);
			}
			(void) releasesignal(SIGCHLD);
			(void) sleep(10);
			(void) close(t);
			continue;
		}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -