xref: /original-bsd/usr.sbin/sendmail/src/daemon.c (revision 47b12ae1)
17aa493c5Seric # include <errno.h>
27fa39d90Seric # include "sendmail.h"
37fa39d90Seric 
4d0a9e852Seric #ifndef DAEMON
5*47b12ae1Seric SCCSID(@(#)daemon.c	3.20		07/27/82	(w/o daemon mode));
6d0a9e852Seric #else
7d0a9e852Seric 
8d0a9e852Seric # include <sys/socket.h>
9d0a9e852Seric # include <net/in.h>
102554d49fSeric # include <wait.h>
11d0a9e852Seric 
12*47b12ae1Seric SCCSID(@(#)daemon.c	3.20		07/27/82	(with daemon mode));
137fa39d90Seric 
147fa39d90Seric /*
157fa39d90Seric **  DAEMON.C -- routines to use when running as a daemon.
16*47b12ae1Seric **
17*47b12ae1Seric **	This entire file is highly dependent on the 4.2 BSD
18*47b12ae1Seric **	interprocess communication primitives.  No attempt has
19*47b12ae1Seric **	been made to make this file portable to Version 7,
20*47b12ae1Seric **	Version 6, MPX files, etc.  If you should try such a
21*47b12ae1Seric **	thing yourself, I recommend chucking the entire file
22*47b12ae1Seric **	and starting from scratch.  Basic semantics are:
23*47b12ae1Seric **
24*47b12ae1Seric **	getrequests()
25*47b12ae1Seric **		Opens a port and initiates a connection.
26*47b12ae1Seric **		Returns in a child.  Must set InChannel and
27*47b12ae1Seric **		OutChannel appropriately.
28*47b12ae1Seric **	makeconnection(host, port, outfile, infile)
29*47b12ae1Seric **		Make a connection to the named host on the given
30*47b12ae1Seric **		port.  Set *outfile and *infile to the files
31*47b12ae1Seric **		appropriate for communication.  Returns zero on
32*47b12ae1Seric **		success, else an exit status describing the
33*47b12ae1Seric **		error.
34*47b12ae1Seric **
35*47b12ae1Seric **	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 		{
71d0a9e852Seric 			syserr("getrequests: getconnection failed");
72*47b12ae1Seric 			finis();
73d0a9e852Seric 		}
74d0a9e852Seric 
75d0a9e852Seric 		/*
76d0a9e852Seric 		**  Create a subprocess to process the mail.
77d0a9e852Seric 		*/
78d0a9e852Seric 
79d0a9e852Seric # ifdef DEBUG
80d0a9e852Seric 		if (Debug > 1)
81d0a9e852Seric 			printf("getrequests: forking (port = %d)\n", port);
82d0a9e852Seric # endif DEBUG
83eb889047Seric 
84a8268164Seric 		pid = fork();
85a8268164Seric 		if (pid < 0)
86a8268164Seric 		{
87a8268164Seric 			syserr("daemon: cannot fork");
88a8268164Seric 			sleep(10);
89b7db1904Seric 			(void) close(port);
90a8268164Seric 			continue;
91a8268164Seric 		}
92a8268164Seric 
93a8268164Seric 		if (pid == 0)
94a8268164Seric 		{
95a8268164Seric 			/*
96a8268164Seric 			**  CHILD -- return to caller.
97a8268164Seric 			**	Verify calling user id if possible here.
98a8268164Seric 			*/
99a8268164Seric 
100d0a9e852Seric 			InChannel = fdopen(port, "r");
101d0a9e852Seric 			OutChannel = fdopen(port, "w");
102d0a9e852Seric # ifdef DEBUG
103d0a9e852Seric 			if (Debug > 1)
104d0a9e852Seric 				printf("getreq: returning\n");
105d0a9e852Seric # endif DEBUG
106a8268164Seric 			return;
107a8268164Seric 		}
108a8268164Seric 
109a8268164Seric 		/*
110a8268164Seric 		**  PARENT -- wait for child to terminate.
111a8268164Seric 		**	Perhaps we should allow concurrent processing?
112a8268164Seric 		*/
113a8268164Seric 
114d0a9e852Seric # ifdef DEBUG
115d0a9e852Seric 		if (Debug > 1)
116d0a9e852Seric 		{
117d0a9e852Seric 			sleep(2);
118d0a9e852Seric 			printf("getreq: parent waiting\n");
119d0a9e852Seric 		}
120d0a9e852Seric # endif DEBUG
121d0a9e852Seric 
1222554d49fSeric 		/* close the port so that others will hang (for a while) */
123b7db1904Seric 		(void) close(port);
1242554d49fSeric 
1252554d49fSeric 		/* pick up old zombies; implement load limiting */
1262554d49fSeric 		numconnections++;
1272554d49fSeric 		while (wait3(&status, numconnections < MAXCONNS ? WNOHANG : 0, 0) > 0)
1282554d49fSeric 			numconnections--;
129a8268164Seric 	}
1307fa39d90Seric }
131d0a9e852Seric /*
132d0a9e852Seric **  GETCONNECTION -- make a connection with the outside world
133d0a9e852Seric **
134d0a9e852Seric **	Parameters:
135d0a9e852Seric **		none.
136d0a9e852Seric **
137d0a9e852Seric **	Returns:
138d0a9e852Seric **		The port for mail traffic.
139d0a9e852Seric **
140d0a9e852Seric **	Side Effects:
141d0a9e852Seric **		Waits for a connection.
142d0a9e852Seric */
143d0a9e852Seric 
1442554d49fSeric #define IPPORT_PLAYPORT	3055		/* random number */
1452554d49fSeric 
146038c4785Seric struct sockaddr_in SendmailAddress = { AF_INET, IPPORT_SMTP };
147d0a9e852Seric 
148d0a9e852Seric getconnection()
149d0a9e852Seric {
150d0a9e852Seric 	register int s;
151d0a9e852Seric 	struct sockaddr otherend;
152d0a9e852Seric 
153d0a9e852Seric 	/*
154d0a9e852Seric 	**  Set up the address for the mailer.
155d0a9e852Seric 	*/
156d0a9e852Seric 
15719cf7d27Seric 	SendmailAddress.sin_addr.s_addr = 0;
15848ff0a9dSeric 	SendmailAddress.sin_port = IPPORT_SMTP;
1592554d49fSeric # ifdef DEBUG
1602554d49fSeric 	if (Debug > 0)
1612554d49fSeric 		SendmailAddress.sin_port = IPPORT_PLAYPORT;
1622554d49fSeric # endif DEBUG
1632554d49fSeric 	SendmailAddress.sin_port = htons(SendmailAddress.sin_port);
164d0a9e852Seric 
165d0a9e852Seric 	/*
166d0a9e852Seric 	**  Try to actually open the connection.
167d0a9e852Seric 	*/
168d0a9e852Seric 
169d0a9e852Seric # ifdef DEBUG
170d0a9e852Seric 	if (Debug)
17119cf7d27Seric 		printf("getconnection\n");
172d0a9e852Seric # endif DEBUG
173d0a9e852Seric 
174*47b12ae1Seric 	for (;;)
175*47b12ae1Seric 	{
176*47b12ae1Seric 		/* get a socket for the SMTP connection */
177d0a9e852Seric 		s = socket(SOCK_STREAM, 0, &SendmailAddress, SO_ACCEPTCONN);
1782554d49fSeric 		if (s < 0)
1792554d49fSeric 		{
180*47b12ae1Seric 			/* probably another daemon already */
181*47b12ae1Seric 			syserr("getconnection: can't create socket");
182*47b12ae1Seric 			break;
1832554d49fSeric 		}
184d0a9e852Seric 
185d0a9e852Seric # ifdef DEBUG
186d0a9e852Seric 		if (Debug)
187d0a9e852Seric 			printf("getconnection: %d\n", s);
188d0a9e852Seric # endif DEBUG
189*47b12ae1Seric 
190*47b12ae1Seric 		/* wait for a connection */
191*47b12ae1Seric 		if (accept(s, &otherend) >= 0)
192*47b12ae1Seric 			break;
193*47b12ae1Seric 
194*47b12ae1Seric 		/* probably innocuous -- retry */
195*47b12ae1Seric 		syserr("getconnection: accept");
196210215eaSeric 		(void) close(s);
197*47b12ae1Seric 		sleep(20);
1982554d49fSeric 	}
199d0a9e852Seric 
200d0a9e852Seric 	return (s);
201d0a9e852Seric }
2027aa493c5Seric /*
2037aa493c5Seric **  MAKECONNECTION -- make a connection to an SMTP socket on another machine.
2047aa493c5Seric **
2057aa493c5Seric **	Parameters:
2067aa493c5Seric **		host -- the name of the host.
20748ff0a9dSeric **		port -- the port number to connect to.
2087aa493c5Seric **		outfile -- a pointer to a place to put the outfile
2097aa493c5Seric **			descriptor.
2107aa493c5Seric **		infile -- ditto for infile.
2117aa493c5Seric **
2127aa493c5Seric **	Returns:
2137aa493c5Seric **		An exit code telling whether the connection could be
2147aa493c5Seric **			made and if not why not.
2157aa493c5Seric **
2167aa493c5Seric **	Side Effects:
2177aa493c5Seric **		none.
2187aa493c5Seric */
2197aa493c5Seric 
22048ff0a9dSeric makeconnection(host, port, outfile, infile)
2217aa493c5Seric 	char *host;
222210215eaSeric 	u_short port;
2237aa493c5Seric 	FILE **outfile;
2247aa493c5Seric 	FILE **infile;
2257aa493c5Seric {
2267aa493c5Seric 	register int s;
2277aa493c5Seric 
2287aa493c5Seric 	/*
2297aa493c5Seric 	**  Set up the address for the mailer.
2307aa493c5Seric 	*/
2317aa493c5Seric 
2327aa493c5Seric 	if ((SendmailAddress.sin_addr.s_addr = rhost(&host)) == -1)
2337aa493c5Seric 		return (EX_NOHOST);
23448ff0a9dSeric 	if (port == 0)
23548ff0a9dSeric 		port = IPPORT_SMTP;
2362554d49fSeric 	SendmailAddress.sin_port = htons(port);
2377aa493c5Seric 
2387aa493c5Seric 	/*
2397aa493c5Seric 	**  Try to actually open the connection.
2407aa493c5Seric 	*/
2417aa493c5Seric 
2427aa493c5Seric # ifdef DEBUG
2437aa493c5Seric 	if (Debug)
2447aa493c5Seric 		printf("makeconnection (%s)\n", host);
2457aa493c5Seric # endif DEBUG
2467aa493c5Seric 
247b7db1904Seric 	s = socket(SOCK_STREAM, 0, (struct sockaddr_in *) 0, 0);
2487aa493c5Seric 	if (s < 0)
2497aa493c5Seric 	{
2507aa493c5Seric 		syserr("makeconnection: no socket");
2517aa493c5Seric 		goto failure;
2527aa493c5Seric 	}
2537aa493c5Seric 
2547aa493c5Seric # ifdef DEBUG
2557aa493c5Seric 	if (Debug)
2567aa493c5Seric 		printf("makeconnection: %d\n", s);
2577aa493c5Seric # endif DEBUG
258e0551291Seric 	fflush(Xscript);				/* for debugging */
2597aa493c5Seric 	if (connect(s, &SendmailAddress) < 0)
2607aa493c5Seric 	{
2617aa493c5Seric 		/* failure, decide if temporary or not */
2627aa493c5Seric 	failure:
2637aa493c5Seric 		switch (errno)
2647aa493c5Seric 		{
2657aa493c5Seric 		  case EISCONN:
2667aa493c5Seric 		  case ETIMEDOUT:
267292668b9Seric 		  case EINPROGRESS:
268292668b9Seric 		  case EALREADY:
269292668b9Seric 		  case EADDRINUSE:
270292668b9Seric 		  case ENETDOWN:
271292668b9Seric 		  case ENETRESET:
272292668b9Seric 		  case ENOBUFS:
273a1645df8Seric 		  case ECONNREFUSED:
2747aa493c5Seric 			/* there are others, I'm sure..... */
2757aa493c5Seric 			return (EX_TEMPFAIL);
2767aa493c5Seric 
2777aa493c5Seric 		  default:
2787aa493c5Seric 			return (EX_UNAVAILABLE);
2797aa493c5Seric 		}
2807aa493c5Seric 	}
2817aa493c5Seric 
2827aa493c5Seric 	/* connection ok, put it into canonical form */
2837aa493c5Seric 	*outfile = fdopen(s, "w");
2847aa493c5Seric 	*infile = fdopen(s, "r");
2857aa493c5Seric 
2867aa493c5Seric 	return (0);
2877aa493c5Seric }
288d0a9e852Seric 
289d0a9e852Seric #endif DAEMON
290