xref: /original-bsd/usr.sbin/sendmail/src/daemon.c (revision 877a6142)
17aa493c5Seric # include <errno.h>
27fa39d90Seric # include "sendmail.h"
37fa39d90Seric 
4d0a9e852Seric #ifndef DAEMON
5*877a6142Seric SCCSID(@(#)daemon.c	3.35		12/05/82	(w/o daemon mode));
6d0a9e852Seric #else
7d0a9e852Seric 
8d0a9e852Seric # include <sys/socket.h>
9d0a9e852Seric # include <net/in.h>
102554d49fSeric # include <wait.h>
11d0a9e852Seric 
12*877a6142Seric SCCSID(@(#)daemon.c	3.35		12/05/82	(with daemon mode));
137fa39d90Seric 
147fa39d90Seric /*
157fa39d90Seric **  DAEMON.C -- routines to use when running as a daemon.
1647b12ae1Seric **
1747b12ae1Seric **	This entire file is highly dependent on the 4.2 BSD
1847b12ae1Seric **	interprocess communication primitives.  No attempt has
1947b12ae1Seric **	been made to make this file portable to Version 7,
2047b12ae1Seric **	Version 6, MPX files, etc.  If you should try such a
2147b12ae1Seric **	thing yourself, I recommend chucking the entire file
2247b12ae1Seric **	and starting from scratch.  Basic semantics are:
2347b12ae1Seric **
2447b12ae1Seric **	getrequests()
2547b12ae1Seric **		Opens a port and initiates a connection.
2647b12ae1Seric **		Returns in a child.  Must set InChannel and
2747b12ae1Seric **		OutChannel appropriately.
2847b12ae1Seric **	makeconnection(host, port, outfile, infile)
2947b12ae1Seric **		Make a connection to the named host on the given
3047b12ae1Seric **		port.  Set *outfile and *infile to the files
3147b12ae1Seric **		appropriate for communication.  Returns zero on
3247b12ae1Seric **		success, else an exit status describing the
3347b12ae1Seric **		error.
3447b12ae1Seric **
3547b12ae1Seric **	The semantics of both of these should be clean.
367fa39d90Seric */
377fa39d90Seric /*
387fa39d90Seric **  GETREQUESTS -- open mail IPC port and get requests.
397fa39d90Seric **
407fa39d90Seric **	Parameters:
417fa39d90Seric **		none.
427fa39d90Seric **
437fa39d90Seric **	Returns:
447fa39d90Seric **		none.
457fa39d90Seric **
467fa39d90Seric **	Side Effects:
477fa39d90Seric **		Waits until some interesting activity occurs.  When
487fa39d90Seric **		it does, a child is created to process it, and the
497fa39d90Seric **		parent waits for completion.  Return from this
507fa39d90Seric **		routine is always in the child.
517fa39d90Seric */
527fa39d90Seric 
532554d49fSeric # define MAXCONNS	4	/* maximum simultaneous sendmails */
542554d49fSeric 
557fa39d90Seric getrequests()
567fa39d90Seric {
572554d49fSeric 	union wait status;
582554d49fSeric 	int numconnections = 0;
592554d49fSeric 
60eb889047Seric 	for (;;)
61eb889047Seric 	{
62a8268164Seric 		register int pid;
63d0a9e852Seric 		register int port;
64eb889047Seric 
65a8268164Seric 		/*
66a8268164Seric 		**  Wait for a connection.
67eb889047Seric 		*/
68eb889047Seric 
69d0a9e852Seric 		while ((port = getconnection()) < 0)
70d0a9e852Seric 		{
71b0ba8827Seric # ifdef LOG
72b0ba8827Seric 			if (LogLevel > 0)
73b0ba8827Seric 				syslog(LOG_SALERT, "cannot get connection");
74b0ba8827Seric # endif LOG
7547b12ae1Seric 			finis();
76d0a9e852Seric 		}
77d0a9e852Seric 
78d0a9e852Seric 		/*
79d0a9e852Seric 		**  Create a subprocess to process the mail.
80d0a9e852Seric 		*/
81d0a9e852Seric 
82d0a9e852Seric # ifdef DEBUG
8361e4310fSeric 		if (tTd(15, 2))
84d0a9e852Seric 			printf("getrequests: forking (port = %d)\n", port);
85d0a9e852Seric # endif DEBUG
86eb889047Seric 
87a8268164Seric 		pid = fork();
88a8268164Seric 		if (pid < 0)
89a8268164Seric 		{
90a8268164Seric 			syserr("daemon: cannot fork");
91a8268164Seric 			sleep(10);
92b7db1904Seric 			(void) close(port);
93a8268164Seric 			continue;
94a8268164Seric 		}
95a8268164Seric 
96a8268164Seric 		if (pid == 0)
97a8268164Seric 		{
98a8268164Seric 			/*
99a8268164Seric 			**  CHILD -- return to caller.
100a8268164Seric 			**	Verify calling user id if possible here.
101a8268164Seric 			*/
102a8268164Seric 
103d0a9e852Seric 			InChannel = fdopen(port, "r");
104d0a9e852Seric 			OutChannel = fdopen(port, "w");
105d0a9e852Seric # ifdef DEBUG
10661e4310fSeric 			if (tTd(15, 2))
107d0a9e852Seric 				printf("getreq: returning\n");
108d0a9e852Seric # endif DEBUG
109252c8a22Seric # ifdef LOG
110252c8a22Seric 			if (LogLevel > 11)
111252c8a22Seric 				syslog(LOG_DEBUG, "connected, pid=%d", getpid());
112252c8a22Seric # endif LOG
113a8268164Seric 			return;
114a8268164Seric 		}
115a8268164Seric 
116a8268164Seric 		/*
117a8268164Seric 		**  PARENT -- wait for child to terminate.
118a8268164Seric 		**	Perhaps we should allow concurrent processing?
119a8268164Seric 		*/
120a8268164Seric 
121d0a9e852Seric # ifdef DEBUG
12261e4310fSeric 		if (tTd(15, 2))
123d0a9e852Seric 		{
124d0a9e852Seric 			sleep(2);
125d0a9e852Seric 			printf("getreq: parent waiting\n");
126d0a9e852Seric 		}
127d0a9e852Seric # endif DEBUG
128d0a9e852Seric 
1292554d49fSeric 		/* close the port so that others will hang (for a while) */
130b7db1904Seric 		(void) close(port);
1312554d49fSeric 
1322554d49fSeric 		/* pick up old zombies; implement load limiting */
1332554d49fSeric 		numconnections++;
1342554d49fSeric 		while (wait3(&status, numconnections < MAXCONNS ? WNOHANG : 0, 0) > 0)
1352554d49fSeric 			numconnections--;
136a8268164Seric 	}
1377fa39d90Seric }
138d0a9e852Seric /*
139d0a9e852Seric **  GETCONNECTION -- make a connection with the outside world
140d0a9e852Seric **
1418aa272cfSeric **	This routine is horribly contorted to try to get around a bunch
1428aa272cfSeric **	of 4.1a IPC bugs.  There appears to be nothing we can do to make
1438aa272cfSeric **	it "right" -- the code to interrupt accepts just doesn't work
1448aa272cfSeric **	right.  However, this is an attempt to minimize the probablity
1458aa272cfSeric **	of problems.
1468aa272cfSeric **
147d0a9e852Seric **	Parameters:
148d0a9e852Seric **		none.
149d0a9e852Seric **
150d0a9e852Seric **	Returns:
151d0a9e852Seric **		The port for mail traffic.
152d0a9e852Seric **
153d0a9e852Seric **	Side Effects:
154d0a9e852Seric **		Waits for a connection.
155d0a9e852Seric */
156d0a9e852Seric 
1572554d49fSeric #define IPPORT_PLAYPORT	3055		/* random number */
1582554d49fSeric 
159038c4785Seric struct sockaddr_in SendmailAddress = { AF_INET, IPPORT_SMTP };
160d0a9e852Seric 
161d0a9e852Seric getconnection()
162d0a9e852Seric {
16310d42158Seric 	int s;
16410d42158Seric #ifdef NVMUNIX
16510d42158Seric 	int t;
16610d42158Seric #endif NVMUNIX
167d0a9e852Seric 	struct sockaddr otherend;
168d0a9e852Seric 
169d0a9e852Seric 	/*
170d0a9e852Seric 	**  Set up the address for the mailer.
171d0a9e852Seric 	*/
172d0a9e852Seric 
17319cf7d27Seric 	SendmailAddress.sin_addr.s_addr = 0;
17448ff0a9dSeric 	SendmailAddress.sin_port = IPPORT_SMTP;
1752554d49fSeric # ifdef DEBUG
17661e4310fSeric 	if (tTd(15, 15))
1772554d49fSeric 		SendmailAddress.sin_port = IPPORT_PLAYPORT;
1782554d49fSeric # endif DEBUG
1792554d49fSeric 	SendmailAddress.sin_port = htons(SendmailAddress.sin_port);
180d0a9e852Seric 
181d0a9e852Seric 	/*
182d0a9e852Seric 	**  Try to actually open the connection.
183d0a9e852Seric 	*/
184d0a9e852Seric 
185d0a9e852Seric # ifdef DEBUG
18661e4310fSeric 	if (tTd(15, 1))
18719cf7d27Seric 		printf("getconnection\n");
188d0a9e852Seric # endif DEBUG
189d0a9e852Seric 
1908aa272cfSeric 	for (;; sleep(15))
19147b12ae1Seric 	{
192d7a9bebcSeric 		int i;
193d7a9bebcSeric 
19447b12ae1Seric 		/* get a socket for the SMTP connection */
19510d42158Seric #ifdef NVMUNIX
19610d42158Seric 		s = socket(AF_INET, SOCK_STREAM, 0, 0);
19710d42158Seric 		bind(s, &SendmailAddress, sizeof SendmailAddress, 0);
19810d42158Seric 		listen(s, 10);
19910d42158Seric #else NVMUNIX
200d7a9bebcSeric 		/* do loop is to avoid 4.1b kernel bug (?) */
2018aa272cfSeric 		i = 60;
202d7a9bebcSeric 		do
203d7a9bebcSeric 		{
204d0a9e852Seric 			s = socket(SOCK_STREAM, 0, &SendmailAddress, SO_ACCEPTCONN);
2052554d49fSeric 			if (s < 0)
2068aa272cfSeric 				sleep(10);
207d7a9bebcSeric 		} while (--i > 0 && s < 0);
20810d42158Seric #endif NVMUNIX
209d7a9bebcSeric 		if (s < 0)
2102554d49fSeric 		{
21147b12ae1Seric 			/* probably another daemon already */
21247b12ae1Seric 			syserr("getconnection: can't create socket");
2130ef8369eSeric 			return (-1);
2142554d49fSeric 		}
215d0a9e852Seric 
216d0a9e852Seric # ifdef DEBUG
21761e4310fSeric 		if (tTd(15, 1))
218d0a9e852Seric 			printf("getconnection: %d\n", s);
219d0a9e852Seric # endif DEBUG
22047b12ae1Seric 
22147b12ae1Seric 		/* wait for a connection */
2228aa272cfSeric 		do
2238aa272cfSeric 		{
2240ef8369eSeric 			errno = 0;
22510d42158Seric #ifdef NVMUNIX
22610d42158Seric 			lotherend = sizeof otherend;
22710d42158Seric 			t = accept(s, &otherend, &lotherend, 0);
22810d42158Seric 			if (t >= 0)
22910d42158Seric 				return (t);
23010d42158Seric #else NVMUNIX
2319cc51a1bSeric 			if (accept(s, &otherend) >= 0)
2320ef8369eSeric 				return (s);
23310d42158Seric #endif NVMUNIX
2348aa272cfSeric 		} while (errno == EINTR);
235b7cb2f21Seric 		syserr("getconnection: accept");
236b7cb2f21Seric 		sleep(5);
2378aa272cfSeric 		(void) close(s);
238b7cb2f21Seric 	}
2392554d49fSeric }
2407aa493c5Seric /*
2417aa493c5Seric **  MAKECONNECTION -- make a connection to an SMTP socket on another machine.
2427aa493c5Seric **
2437aa493c5Seric **	Parameters:
2447aa493c5Seric **		host -- the name of the host.
24548ff0a9dSeric **		port -- the port number to connect to.
2467aa493c5Seric **		outfile -- a pointer to a place to put the outfile
2477aa493c5Seric **			descriptor.
2487aa493c5Seric **		infile -- ditto for infile.
2497aa493c5Seric **
2507aa493c5Seric **	Returns:
2517aa493c5Seric **		An exit code telling whether the connection could be
2527aa493c5Seric **			made and if not why not.
2537aa493c5Seric **
2547aa493c5Seric **	Side Effects:
2557aa493c5Seric **		none.
2567aa493c5Seric */
2577aa493c5Seric 
25848ff0a9dSeric makeconnection(host, port, outfile, infile)
2597aa493c5Seric 	char *host;
260210215eaSeric 	u_short port;
2617aa493c5Seric 	FILE **outfile;
2627aa493c5Seric 	FILE **infile;
2637aa493c5Seric {
2647aa493c5Seric 	register int s;
2657aa493c5Seric 
2667aa493c5Seric 	/*
2677aa493c5Seric 	**  Set up the address for the mailer.
26871096d12Seric 	**	Accept "[a.b.c.d]" syntax for host name.
2697aa493c5Seric 	*/
2707aa493c5Seric 
27171096d12Seric 	if (host[0] == '[')
27271096d12Seric 	{
27371096d12Seric 		long hid = 0;
27471096d12Seric 		int i, j;
27571096d12Seric 		register char *p = host;
27671096d12Seric 
27771096d12Seric 		for (i = 3; i >= 0 && *p != ']' && *p != '\0'; i--)
27871096d12Seric 		{
27971096d12Seric 			j = 0;
28071096d12Seric 			while (isdigit(*++p))
28171096d12Seric 				j = j * 10 + (*p - '0');
28271096d12Seric 			if (*p != (i == 0 ? ']' : '.') || j > 255 || j < 0)
28371096d12Seric 				break;
28471096d12Seric 			hid |= j << ((3 - i) * 8);
28571096d12Seric 		}
28671096d12Seric 		if (i >= 0 || *p != ']' || *++p != '\0')
28771096d12Seric 		{
28871096d12Seric 			usrerr("Invalid numeric domain spec \"%s\"", host);
28971096d12Seric 			return (EX_NOHOST);
29071096d12Seric 		}
29171096d12Seric 		SendmailAddress.sin_addr.s_addr = hid;
29271096d12Seric 	}
29371096d12Seric 	else if ((SendmailAddress.sin_addr.s_addr = rhost(&host)) == -1)
2947aa493c5Seric 		return (EX_NOHOST);
29548ff0a9dSeric 	if (port == 0)
29648ff0a9dSeric 		port = IPPORT_SMTP;
2972554d49fSeric 	SendmailAddress.sin_port = htons(port);
2987aa493c5Seric 
2997aa493c5Seric 	/*
3007aa493c5Seric 	**  Try to actually open the connection.
3017aa493c5Seric 	*/
3027aa493c5Seric 
3037aa493c5Seric # ifdef DEBUG
30461e4310fSeric 	if (tTd(16, 1))
3057aa493c5Seric 		printf("makeconnection (%s)\n", host);
3067aa493c5Seric # endif DEBUG
3077aa493c5Seric 
30810d42158Seric #ifdef NVMUNIX
30910d42158Seric 	s = socket(AF_INET, SOCK_STREAM, 0, 0);
31010d42158Seric #else NVMUNIX
311b7db1904Seric 	s = socket(SOCK_STREAM, 0, (struct sockaddr_in *) 0, 0);
31210d42158Seric #endif NVMUNIX
3137aa493c5Seric 	if (s < 0)
3147aa493c5Seric 	{
3157aa493c5Seric 		syserr("makeconnection: no socket");
3167aa493c5Seric 		goto failure;
3177aa493c5Seric 	}
3187aa493c5Seric 
3197aa493c5Seric # ifdef DEBUG
32061e4310fSeric 	if (tTd(16, 1))
3217aa493c5Seric 		printf("makeconnection: %d\n", s);
3227aa493c5Seric # endif DEBUG
323*877a6142Seric 	(void) fflush(CurEnv->e_xfp);			/* for debugging */
32410d42158Seric #ifdef NVMUNIX
32510d42158Seric 	bind(s, &SendmailAddress, sizeof SendmailAddress, 0);
32610d42158Seric 	if (connect(s, &SendmailAddress, sizeof SendmailAddress, 0) < 0)
32710d42158Seric #else NVMUNIX
3287aa493c5Seric 	if (connect(s, &SendmailAddress) < 0)
32910d42158Seric #endif NVMUNIX
3307aa493c5Seric 	{
3317aa493c5Seric 		/* failure, decide if temporary or not */
3327aa493c5Seric 	failure:
3337aa493c5Seric 		switch (errno)
3347aa493c5Seric 		{
3357aa493c5Seric 		  case EISCONN:
3367aa493c5Seric 		  case ETIMEDOUT:
337292668b9Seric 		  case EINPROGRESS:
338292668b9Seric 		  case EALREADY:
339292668b9Seric 		  case EADDRINUSE:
340292668b9Seric 		  case ENETDOWN:
341292668b9Seric 		  case ENETRESET:
342292668b9Seric 		  case ENOBUFS:
343a1645df8Seric 		  case ECONNREFUSED:
3447aa493c5Seric 			/* there are others, I'm sure..... */
3457aa493c5Seric 			return (EX_TEMPFAIL);
3467aa493c5Seric 
3477aa493c5Seric 		  default:
3487aa493c5Seric 			return (EX_UNAVAILABLE);
3497aa493c5Seric 		}
3507aa493c5Seric 	}
3517aa493c5Seric 
3527aa493c5Seric 	/* connection ok, put it into canonical form */
3537aa493c5Seric 	*outfile = fdopen(s, "w");
3547aa493c5Seric 	*infile = fdopen(s, "r");
3557aa493c5Seric 
3567aa493c5Seric 	return (0);
3577aa493c5Seric }
358d0a9e852Seric 
359d0a9e852Seric #endif DAEMON
360