xref: /illumos-gate/usr/src/cmd/sendmail/src/control.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * Copyright (c) 1998-2004 Sendmail, Inc. and its suppliers.
3*7c478bd9Sstevel@tonic-gate  *	All rights reserved.
4*7c478bd9Sstevel@tonic-gate  *
5*7c478bd9Sstevel@tonic-gate  * By using this file, you agree to the terms and conditions set
6*7c478bd9Sstevel@tonic-gate  * forth in the LICENSE file which can be found at the top level of
7*7c478bd9Sstevel@tonic-gate  * the sendmail distribution.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  */
10*7c478bd9Sstevel@tonic-gate 
11*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
12*7c478bd9Sstevel@tonic-gate 
13*7c478bd9Sstevel@tonic-gate #include <sendmail.h>
14*7c478bd9Sstevel@tonic-gate 
15*7c478bd9Sstevel@tonic-gate SM_RCSID("@(#)$Id: control.c,v 8.126 2004/08/04 20:54:00 ca Exp $")
16*7c478bd9Sstevel@tonic-gate 
17*7c478bd9Sstevel@tonic-gate #include <sm/fdset.h>
18*7c478bd9Sstevel@tonic-gate 
19*7c478bd9Sstevel@tonic-gate /* values for cmd_code */
20*7c478bd9Sstevel@tonic-gate #define CMDERROR	0	/* bad command */
21*7c478bd9Sstevel@tonic-gate #define CMDRESTART	1	/* restart daemon */
22*7c478bd9Sstevel@tonic-gate #define CMDSHUTDOWN	2	/* end daemon */
23*7c478bd9Sstevel@tonic-gate #define CMDHELP		3	/* help */
24*7c478bd9Sstevel@tonic-gate #define CMDSTATUS	4	/* daemon status */
25*7c478bd9Sstevel@tonic-gate #define CMDMEMDUMP	5	/* dump memory, to find memory leaks */
26*7c478bd9Sstevel@tonic-gate #if _FFR_CONTROL_MSTAT
27*7c478bd9Sstevel@tonic-gate # define CMDMSTAT	6	/* daemon status, more info, tagged data */
28*7c478bd9Sstevel@tonic-gate #endif /* _FFR_CONTROL_MSTAT */
29*7c478bd9Sstevel@tonic-gate 
30*7c478bd9Sstevel@tonic-gate struct cmd
31*7c478bd9Sstevel@tonic-gate {
32*7c478bd9Sstevel@tonic-gate 	char	*cmd_name;	/* command name */
33*7c478bd9Sstevel@tonic-gate 	int	cmd_code;	/* internal code, see below */
34*7c478bd9Sstevel@tonic-gate };
35*7c478bd9Sstevel@tonic-gate 
36*7c478bd9Sstevel@tonic-gate static struct cmd	CmdTab[] =
37*7c478bd9Sstevel@tonic-gate {
38*7c478bd9Sstevel@tonic-gate 	{ "help",	CMDHELP		},
39*7c478bd9Sstevel@tonic-gate 	{ "restart",	CMDRESTART	},
40*7c478bd9Sstevel@tonic-gate 	{ "shutdown",	CMDSHUTDOWN	},
41*7c478bd9Sstevel@tonic-gate 	{ "status",	CMDSTATUS	},
42*7c478bd9Sstevel@tonic-gate 	{ "memdump",	CMDMEMDUMP	},
43*7c478bd9Sstevel@tonic-gate #if _FFR_CONTROL_MSTAT
44*7c478bd9Sstevel@tonic-gate 	{ "mstat",	CMDMSTAT	},
45*7c478bd9Sstevel@tonic-gate #endif /* _FFR_CONTROL_MSTAT */
46*7c478bd9Sstevel@tonic-gate 	{ NULL,		CMDERROR	}
47*7c478bd9Sstevel@tonic-gate };
48*7c478bd9Sstevel@tonic-gate 
49*7c478bd9Sstevel@tonic-gate 
50*7c478bd9Sstevel@tonic-gate 
51*7c478bd9Sstevel@tonic-gate static void	controltimeout __P((int));
52*7c478bd9Sstevel@tonic-gate int ControlSocket = -1;
53*7c478bd9Sstevel@tonic-gate 
54*7c478bd9Sstevel@tonic-gate /*
55*7c478bd9Sstevel@tonic-gate **  OPENCONTROLSOCKET -- create/open the daemon control named socket
56*7c478bd9Sstevel@tonic-gate **
57*7c478bd9Sstevel@tonic-gate **	Creates and opens a named socket for external control over
58*7c478bd9Sstevel@tonic-gate **	the sendmail daemon.
59*7c478bd9Sstevel@tonic-gate **
60*7c478bd9Sstevel@tonic-gate **	Parameters:
61*7c478bd9Sstevel@tonic-gate **		none.
62*7c478bd9Sstevel@tonic-gate **
63*7c478bd9Sstevel@tonic-gate **	Returns:
64*7c478bd9Sstevel@tonic-gate **		0 if successful, -1 otherwise
65*7c478bd9Sstevel@tonic-gate */
66*7c478bd9Sstevel@tonic-gate 
67*7c478bd9Sstevel@tonic-gate int
68*7c478bd9Sstevel@tonic-gate opencontrolsocket()
69*7c478bd9Sstevel@tonic-gate {
70*7c478bd9Sstevel@tonic-gate # if NETUNIX
71*7c478bd9Sstevel@tonic-gate 	int save_errno;
72*7c478bd9Sstevel@tonic-gate 	int rval;
73*7c478bd9Sstevel@tonic-gate 	long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN;
74*7c478bd9Sstevel@tonic-gate 	struct sockaddr_un controladdr;
75*7c478bd9Sstevel@tonic-gate 
76*7c478bd9Sstevel@tonic-gate 	if (ControlSocketName == NULL || *ControlSocketName == '\0')
77*7c478bd9Sstevel@tonic-gate 		return 0;
78*7c478bd9Sstevel@tonic-gate 
79*7c478bd9Sstevel@tonic-gate 	if (strlen(ControlSocketName) >= sizeof controladdr.sun_path)
80*7c478bd9Sstevel@tonic-gate 	{
81*7c478bd9Sstevel@tonic-gate 		errno = ENAMETOOLONG;
82*7c478bd9Sstevel@tonic-gate 		return -1;
83*7c478bd9Sstevel@tonic-gate 	}
84*7c478bd9Sstevel@tonic-gate 
85*7c478bd9Sstevel@tonic-gate 	rval = safefile(ControlSocketName, RunAsUid, RunAsGid, RunAsUserName,
86*7c478bd9Sstevel@tonic-gate 			sff, S_IRUSR|S_IWUSR, NULL);
87*7c478bd9Sstevel@tonic-gate 
88*7c478bd9Sstevel@tonic-gate 	/* if not safe, don't create */
89*7c478bd9Sstevel@tonic-gate 	if (rval != 0)
90*7c478bd9Sstevel@tonic-gate 	{
91*7c478bd9Sstevel@tonic-gate 		errno = rval;
92*7c478bd9Sstevel@tonic-gate 		return -1;
93*7c478bd9Sstevel@tonic-gate 	}
94*7c478bd9Sstevel@tonic-gate 
95*7c478bd9Sstevel@tonic-gate 	ControlSocket = socket(AF_UNIX, SOCK_STREAM, 0);
96*7c478bd9Sstevel@tonic-gate 	if (ControlSocket < 0)
97*7c478bd9Sstevel@tonic-gate 		return -1;
98*7c478bd9Sstevel@tonic-gate 	if (SM_FD_SETSIZE > 0 && ControlSocket >= SM_FD_SETSIZE)
99*7c478bd9Sstevel@tonic-gate 	{
100*7c478bd9Sstevel@tonic-gate 		clrcontrol();
101*7c478bd9Sstevel@tonic-gate 		errno = EINVAL;
102*7c478bd9Sstevel@tonic-gate 		return -1;
103*7c478bd9Sstevel@tonic-gate 	}
104*7c478bd9Sstevel@tonic-gate 
105*7c478bd9Sstevel@tonic-gate 	(void) unlink(ControlSocketName);
106*7c478bd9Sstevel@tonic-gate 	memset(&controladdr, '\0', sizeof controladdr);
107*7c478bd9Sstevel@tonic-gate 	controladdr.sun_family = AF_UNIX;
108*7c478bd9Sstevel@tonic-gate 	(void) sm_strlcpy(controladdr.sun_path, ControlSocketName,
109*7c478bd9Sstevel@tonic-gate 			  sizeof controladdr.sun_path);
110*7c478bd9Sstevel@tonic-gate 
111*7c478bd9Sstevel@tonic-gate 	if (bind(ControlSocket, (struct sockaddr *) &controladdr,
112*7c478bd9Sstevel@tonic-gate 		 sizeof controladdr) < 0)
113*7c478bd9Sstevel@tonic-gate 	{
114*7c478bd9Sstevel@tonic-gate 		save_errno = errno;
115*7c478bd9Sstevel@tonic-gate 		clrcontrol();
116*7c478bd9Sstevel@tonic-gate 		errno = save_errno;
117*7c478bd9Sstevel@tonic-gate 		return -1;
118*7c478bd9Sstevel@tonic-gate 	}
119*7c478bd9Sstevel@tonic-gate 
120*7c478bd9Sstevel@tonic-gate 	if (geteuid() == 0)
121*7c478bd9Sstevel@tonic-gate 	{
122*7c478bd9Sstevel@tonic-gate 		uid_t u = 0;
123*7c478bd9Sstevel@tonic-gate 
124*7c478bd9Sstevel@tonic-gate 		if (RunAsUid != 0)
125*7c478bd9Sstevel@tonic-gate 			u = RunAsUid;
126*7c478bd9Sstevel@tonic-gate 		else if (TrustedUid != 0)
127*7c478bd9Sstevel@tonic-gate 			u = TrustedUid;
128*7c478bd9Sstevel@tonic-gate 
129*7c478bd9Sstevel@tonic-gate 		if (u != 0 &&
130*7c478bd9Sstevel@tonic-gate 		    chown(ControlSocketName, u, -1) < 0)
131*7c478bd9Sstevel@tonic-gate 		{
132*7c478bd9Sstevel@tonic-gate 			save_errno = errno;
133*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ALERT, NOQID,
134*7c478bd9Sstevel@tonic-gate 				  "ownership change on %s to uid %d failed: %s",
135*7c478bd9Sstevel@tonic-gate 				  ControlSocketName, (int) u,
136*7c478bd9Sstevel@tonic-gate 				  sm_errstring(save_errno));
137*7c478bd9Sstevel@tonic-gate 			message("050 ownership change on %s to uid %d failed: %s",
138*7c478bd9Sstevel@tonic-gate 				ControlSocketName, (int) u,
139*7c478bd9Sstevel@tonic-gate 				sm_errstring(save_errno));
140*7c478bd9Sstevel@tonic-gate 			closecontrolsocket(true);
141*7c478bd9Sstevel@tonic-gate 			errno = save_errno;
142*7c478bd9Sstevel@tonic-gate 			return -1;
143*7c478bd9Sstevel@tonic-gate 		}
144*7c478bd9Sstevel@tonic-gate 	}
145*7c478bd9Sstevel@tonic-gate 
146*7c478bd9Sstevel@tonic-gate 	if (chmod(ControlSocketName, S_IRUSR|S_IWUSR) < 0)
147*7c478bd9Sstevel@tonic-gate 	{
148*7c478bd9Sstevel@tonic-gate 		save_errno = errno;
149*7c478bd9Sstevel@tonic-gate 		closecontrolsocket(true);
150*7c478bd9Sstevel@tonic-gate 		errno = save_errno;
151*7c478bd9Sstevel@tonic-gate 		return -1;
152*7c478bd9Sstevel@tonic-gate 	}
153*7c478bd9Sstevel@tonic-gate 
154*7c478bd9Sstevel@tonic-gate 	if (listen(ControlSocket, 8) < 0)
155*7c478bd9Sstevel@tonic-gate 	{
156*7c478bd9Sstevel@tonic-gate 		save_errno = errno;
157*7c478bd9Sstevel@tonic-gate 		closecontrolsocket(true);
158*7c478bd9Sstevel@tonic-gate 		errno = save_errno;
159*7c478bd9Sstevel@tonic-gate 		return -1;
160*7c478bd9Sstevel@tonic-gate 	}
161*7c478bd9Sstevel@tonic-gate # endif /* NETUNIX */
162*7c478bd9Sstevel@tonic-gate 	return 0;
163*7c478bd9Sstevel@tonic-gate }
164*7c478bd9Sstevel@tonic-gate /*
165*7c478bd9Sstevel@tonic-gate **  CLOSECONTROLSOCKET -- close the daemon control named socket
166*7c478bd9Sstevel@tonic-gate **
167*7c478bd9Sstevel@tonic-gate **	Close a named socket.
168*7c478bd9Sstevel@tonic-gate **
169*7c478bd9Sstevel@tonic-gate **	Parameters:
170*7c478bd9Sstevel@tonic-gate **		fullclose -- if set, close the socket and remove it;
171*7c478bd9Sstevel@tonic-gate **			     otherwise, just remove it
172*7c478bd9Sstevel@tonic-gate **
173*7c478bd9Sstevel@tonic-gate **	Returns:
174*7c478bd9Sstevel@tonic-gate **		none.
175*7c478bd9Sstevel@tonic-gate */
176*7c478bd9Sstevel@tonic-gate 
177*7c478bd9Sstevel@tonic-gate void
178*7c478bd9Sstevel@tonic-gate closecontrolsocket(fullclose)
179*7c478bd9Sstevel@tonic-gate 	bool fullclose;
180*7c478bd9Sstevel@tonic-gate {
181*7c478bd9Sstevel@tonic-gate # if NETUNIX
182*7c478bd9Sstevel@tonic-gate 	long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN;
183*7c478bd9Sstevel@tonic-gate 
184*7c478bd9Sstevel@tonic-gate 	if (ControlSocket >= 0)
185*7c478bd9Sstevel@tonic-gate 	{
186*7c478bd9Sstevel@tonic-gate 		int rval;
187*7c478bd9Sstevel@tonic-gate 
188*7c478bd9Sstevel@tonic-gate 		if (fullclose)
189*7c478bd9Sstevel@tonic-gate 		{
190*7c478bd9Sstevel@tonic-gate 			(void) close(ControlSocket);
191*7c478bd9Sstevel@tonic-gate 			ControlSocket = -1;
192*7c478bd9Sstevel@tonic-gate 		}
193*7c478bd9Sstevel@tonic-gate 
194*7c478bd9Sstevel@tonic-gate 		rval = safefile(ControlSocketName, RunAsUid, RunAsGid,
195*7c478bd9Sstevel@tonic-gate 				RunAsUserName, sff, S_IRUSR|S_IWUSR, NULL);
196*7c478bd9Sstevel@tonic-gate 
197*7c478bd9Sstevel@tonic-gate 		/* if not safe, don't unlink */
198*7c478bd9Sstevel@tonic-gate 		if (rval != 0)
199*7c478bd9Sstevel@tonic-gate 			return;
200*7c478bd9Sstevel@tonic-gate 
201*7c478bd9Sstevel@tonic-gate 		if (unlink(ControlSocketName) < 0)
202*7c478bd9Sstevel@tonic-gate 		{
203*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_WARNING, NOQID,
204*7c478bd9Sstevel@tonic-gate 				  "Could not remove control socket: %s",
205*7c478bd9Sstevel@tonic-gate 				  sm_errstring(errno));
206*7c478bd9Sstevel@tonic-gate 			return;
207*7c478bd9Sstevel@tonic-gate 		}
208*7c478bd9Sstevel@tonic-gate 	}
209*7c478bd9Sstevel@tonic-gate # endif /* NETUNIX */
210*7c478bd9Sstevel@tonic-gate 	return;
211*7c478bd9Sstevel@tonic-gate }
212*7c478bd9Sstevel@tonic-gate /*
213*7c478bd9Sstevel@tonic-gate **  CLRCONTROL -- reset the control connection
214*7c478bd9Sstevel@tonic-gate **
215*7c478bd9Sstevel@tonic-gate **	Parameters:
216*7c478bd9Sstevel@tonic-gate **		none.
217*7c478bd9Sstevel@tonic-gate **
218*7c478bd9Sstevel@tonic-gate **	Returns:
219*7c478bd9Sstevel@tonic-gate **		none.
220*7c478bd9Sstevel@tonic-gate **
221*7c478bd9Sstevel@tonic-gate **	Side Effects:
222*7c478bd9Sstevel@tonic-gate **		releases any resources used by the control interface.
223*7c478bd9Sstevel@tonic-gate */
224*7c478bd9Sstevel@tonic-gate 
225*7c478bd9Sstevel@tonic-gate void
226*7c478bd9Sstevel@tonic-gate clrcontrol()
227*7c478bd9Sstevel@tonic-gate {
228*7c478bd9Sstevel@tonic-gate # if NETUNIX
229*7c478bd9Sstevel@tonic-gate 	if (ControlSocket >= 0)
230*7c478bd9Sstevel@tonic-gate 		(void) close(ControlSocket);
231*7c478bd9Sstevel@tonic-gate 	ControlSocket = -1;
232*7c478bd9Sstevel@tonic-gate # endif /* NETUNIX */
233*7c478bd9Sstevel@tonic-gate }
234*7c478bd9Sstevel@tonic-gate /*
235*7c478bd9Sstevel@tonic-gate **  CONTROL_COMMAND -- read and process command from named socket
236*7c478bd9Sstevel@tonic-gate **
237*7c478bd9Sstevel@tonic-gate **	Read and process the command from the opened socket.
238*7c478bd9Sstevel@tonic-gate **	Exits when done since it is running in a forked child.
239*7c478bd9Sstevel@tonic-gate **
240*7c478bd9Sstevel@tonic-gate **	Parameters:
241*7c478bd9Sstevel@tonic-gate **		sock -- the opened socket from getrequests()
242*7c478bd9Sstevel@tonic-gate **		e -- the current envelope
243*7c478bd9Sstevel@tonic-gate **
244*7c478bd9Sstevel@tonic-gate **	Returns:
245*7c478bd9Sstevel@tonic-gate **		none.
246*7c478bd9Sstevel@tonic-gate */
247*7c478bd9Sstevel@tonic-gate 
248*7c478bd9Sstevel@tonic-gate static jmp_buf	CtxControlTimeout;
249*7c478bd9Sstevel@tonic-gate 
250*7c478bd9Sstevel@tonic-gate /* ARGSUSED0 */
251*7c478bd9Sstevel@tonic-gate static void
252*7c478bd9Sstevel@tonic-gate controltimeout(timeout)
253*7c478bd9Sstevel@tonic-gate 	int timeout;
254*7c478bd9Sstevel@tonic-gate {
255*7c478bd9Sstevel@tonic-gate 	/*
256*7c478bd9Sstevel@tonic-gate 	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
257*7c478bd9Sstevel@tonic-gate 	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
258*7c478bd9Sstevel@tonic-gate 	**	DOING.
259*7c478bd9Sstevel@tonic-gate 	*/
260*7c478bd9Sstevel@tonic-gate 
261*7c478bd9Sstevel@tonic-gate 	errno = ETIMEDOUT;
262*7c478bd9Sstevel@tonic-gate 	longjmp(CtxControlTimeout, 1);
263*7c478bd9Sstevel@tonic-gate }
264*7c478bd9Sstevel@tonic-gate 
265*7c478bd9Sstevel@tonic-gate void
266*7c478bd9Sstevel@tonic-gate control_command(sock, e)
267*7c478bd9Sstevel@tonic-gate 	int sock;
268*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
269*7c478bd9Sstevel@tonic-gate {
270*7c478bd9Sstevel@tonic-gate 	volatile int exitstat = EX_OK;
271*7c478bd9Sstevel@tonic-gate 	SM_FILE_T *s = NULL;
272*7c478bd9Sstevel@tonic-gate 	SM_EVENT *ev = NULL;
273*7c478bd9Sstevel@tonic-gate 	SM_FILE_T *traffic;
274*7c478bd9Sstevel@tonic-gate 	SM_FILE_T *oldout;
275*7c478bd9Sstevel@tonic-gate 	char *cmd;
276*7c478bd9Sstevel@tonic-gate 	char *p;
277*7c478bd9Sstevel@tonic-gate 	struct cmd *c;
278*7c478bd9Sstevel@tonic-gate 	char cmdbuf[MAXLINE];
279*7c478bd9Sstevel@tonic-gate 	char inp[MAXLINE];
280*7c478bd9Sstevel@tonic-gate 
281*7c478bd9Sstevel@tonic-gate 	sm_setproctitle(false, e, "control cmd read");
282*7c478bd9Sstevel@tonic-gate 
283*7c478bd9Sstevel@tonic-gate 	if (TimeOuts.to_control > 0)
284*7c478bd9Sstevel@tonic-gate 	{
285*7c478bd9Sstevel@tonic-gate 		/* handle possible input timeout */
286*7c478bd9Sstevel@tonic-gate 		if (setjmp(CtxControlTimeout) != 0)
287*7c478bd9Sstevel@tonic-gate 		{
288*7c478bd9Sstevel@tonic-gate 			if (LogLevel > 2)
289*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_NOTICE, e->e_id,
290*7c478bd9Sstevel@tonic-gate 					  "timeout waiting for input during control command");
291*7c478bd9Sstevel@tonic-gate 			exit(EX_IOERR);
292*7c478bd9Sstevel@tonic-gate 		}
293*7c478bd9Sstevel@tonic-gate 		ev = sm_setevent(TimeOuts.to_control, controltimeout,
294*7c478bd9Sstevel@tonic-gate 				 TimeOuts.to_control);
295*7c478bd9Sstevel@tonic-gate 	}
296*7c478bd9Sstevel@tonic-gate 
297*7c478bd9Sstevel@tonic-gate 	s = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, (void *) &sock,
298*7c478bd9Sstevel@tonic-gate 		       SM_IO_RDWR, NULL);
299*7c478bd9Sstevel@tonic-gate 	if (s == NULL)
300*7c478bd9Sstevel@tonic-gate 	{
301*7c478bd9Sstevel@tonic-gate 		int save_errno = errno;
302*7c478bd9Sstevel@tonic-gate 
303*7c478bd9Sstevel@tonic-gate 		(void) close(sock);
304*7c478bd9Sstevel@tonic-gate 		errno = save_errno;
305*7c478bd9Sstevel@tonic-gate 		exit(EX_IOERR);
306*7c478bd9Sstevel@tonic-gate 	}
307*7c478bd9Sstevel@tonic-gate 	(void) sm_io_setvbuf(s, SM_TIME_DEFAULT, NULL,
308*7c478bd9Sstevel@tonic-gate 			     SM_IO_NBF, SM_IO_BUFSIZ);
309*7c478bd9Sstevel@tonic-gate 
310*7c478bd9Sstevel@tonic-gate 	if (sm_io_fgets(s, SM_TIME_DEFAULT, inp, sizeof inp) == NULL)
311*7c478bd9Sstevel@tonic-gate 	{
312*7c478bd9Sstevel@tonic-gate 		(void) sm_io_close(s, SM_TIME_DEFAULT);
313*7c478bd9Sstevel@tonic-gate 		exit(EX_IOERR);
314*7c478bd9Sstevel@tonic-gate 	}
315*7c478bd9Sstevel@tonic-gate 	(void) sm_io_flush(s, SM_TIME_DEFAULT);
316*7c478bd9Sstevel@tonic-gate 
317*7c478bd9Sstevel@tonic-gate 	/* clean up end of line */
318*7c478bd9Sstevel@tonic-gate 	fixcrlf(inp, true);
319*7c478bd9Sstevel@tonic-gate 
320*7c478bd9Sstevel@tonic-gate 	sm_setproctitle(false, e, "control: %s", inp);
321*7c478bd9Sstevel@tonic-gate 
322*7c478bd9Sstevel@tonic-gate 	/* break off command */
323*7c478bd9Sstevel@tonic-gate 	for (p = inp; isascii(*p) && isspace(*p); p++)
324*7c478bd9Sstevel@tonic-gate 		continue;
325*7c478bd9Sstevel@tonic-gate 	cmd = cmdbuf;
326*7c478bd9Sstevel@tonic-gate 	while (*p != '\0' &&
327*7c478bd9Sstevel@tonic-gate 	       !(isascii(*p) && isspace(*p)) &&
328*7c478bd9Sstevel@tonic-gate 	       cmd < &cmdbuf[sizeof cmdbuf - 2])
329*7c478bd9Sstevel@tonic-gate 		*cmd++ = *p++;
330*7c478bd9Sstevel@tonic-gate 	*cmd = '\0';
331*7c478bd9Sstevel@tonic-gate 
332*7c478bd9Sstevel@tonic-gate 	/* throw away leading whitespace */
333*7c478bd9Sstevel@tonic-gate 	while (isascii(*p) && isspace(*p))
334*7c478bd9Sstevel@tonic-gate 		p++;
335*7c478bd9Sstevel@tonic-gate 
336*7c478bd9Sstevel@tonic-gate 	/* decode command */
337*7c478bd9Sstevel@tonic-gate 	for (c = CmdTab; c->cmd_name != NULL; c++)
338*7c478bd9Sstevel@tonic-gate 	{
339*7c478bd9Sstevel@tonic-gate 		if (sm_strcasecmp(c->cmd_name, cmdbuf) == 0)
340*7c478bd9Sstevel@tonic-gate 			break;
341*7c478bd9Sstevel@tonic-gate 	}
342*7c478bd9Sstevel@tonic-gate 
343*7c478bd9Sstevel@tonic-gate 	switch (c->cmd_code)
344*7c478bd9Sstevel@tonic-gate 	{
345*7c478bd9Sstevel@tonic-gate 	  case CMDHELP:		/* get help */
346*7c478bd9Sstevel@tonic-gate 		traffic = TrafficLogFile;
347*7c478bd9Sstevel@tonic-gate 		TrafficLogFile = NULL;
348*7c478bd9Sstevel@tonic-gate 		oldout = OutChannel;
349*7c478bd9Sstevel@tonic-gate 		OutChannel = s;
350*7c478bd9Sstevel@tonic-gate 		help("control", e);
351*7c478bd9Sstevel@tonic-gate 		TrafficLogFile = traffic;
352*7c478bd9Sstevel@tonic-gate 		OutChannel = oldout;
353*7c478bd9Sstevel@tonic-gate 		break;
354*7c478bd9Sstevel@tonic-gate 
355*7c478bd9Sstevel@tonic-gate 	  case CMDRESTART:	/* restart the daemon */
356*7c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(s, SM_TIME_DEFAULT, "OK\r\n");
357*7c478bd9Sstevel@tonic-gate 		exitstat = EX_RESTART;
358*7c478bd9Sstevel@tonic-gate 		break;
359*7c478bd9Sstevel@tonic-gate 
360*7c478bd9Sstevel@tonic-gate 	  case CMDSHUTDOWN:	/* kill the daemon */
361*7c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(s, SM_TIME_DEFAULT, "OK\r\n");
362*7c478bd9Sstevel@tonic-gate 		exitstat = EX_SHUTDOWN;
363*7c478bd9Sstevel@tonic-gate 		break;
364*7c478bd9Sstevel@tonic-gate 
365*7c478bd9Sstevel@tonic-gate 	  case CMDSTATUS:	/* daemon status */
366*7c478bd9Sstevel@tonic-gate 		proc_list_probe();
367*7c478bd9Sstevel@tonic-gate 		{
368*7c478bd9Sstevel@tonic-gate 			int qgrp;
369*7c478bd9Sstevel@tonic-gate 			long bsize;
370*7c478bd9Sstevel@tonic-gate 			long free;
371*7c478bd9Sstevel@tonic-gate 
372*7c478bd9Sstevel@tonic-gate 			/* XXX need to deal with different partitions */
373*7c478bd9Sstevel@tonic-gate 			qgrp = e->e_qgrp;
374*7c478bd9Sstevel@tonic-gate 			if (!ISVALIDQGRP(qgrp))
375*7c478bd9Sstevel@tonic-gate 				qgrp = 0;
376*7c478bd9Sstevel@tonic-gate 			free = freediskspace(Queue[qgrp]->qg_qdir, &bsize);
377*7c478bd9Sstevel@tonic-gate 
378*7c478bd9Sstevel@tonic-gate 			/*
379*7c478bd9Sstevel@tonic-gate 			**  Prevent overflow and don't lose
380*7c478bd9Sstevel@tonic-gate 			**  precision (if bsize == 512)
381*7c478bd9Sstevel@tonic-gate 			*/
382*7c478bd9Sstevel@tonic-gate 
383*7c478bd9Sstevel@tonic-gate 			if (free > 0)
384*7c478bd9Sstevel@tonic-gate 				free = (long)((double) free *
385*7c478bd9Sstevel@tonic-gate 					      ((double) bsize / 1024));
386*7c478bd9Sstevel@tonic-gate 
387*7c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(s, SM_TIME_DEFAULT,
388*7c478bd9Sstevel@tonic-gate 					     "%d/%d/%ld/%d\r\n",
389*7c478bd9Sstevel@tonic-gate 					     CurChildren, MaxChildren,
390*7c478bd9Sstevel@tonic-gate 					     free, getla());
391*7c478bd9Sstevel@tonic-gate 		}
392*7c478bd9Sstevel@tonic-gate 		proc_list_display(s, "");
393*7c478bd9Sstevel@tonic-gate 		break;
394*7c478bd9Sstevel@tonic-gate 
395*7c478bd9Sstevel@tonic-gate # if _FFR_CONTROL_MSTAT
396*7c478bd9Sstevel@tonic-gate 	  case CMDMSTAT:	/* daemon status, extended, tagged format */
397*7c478bd9Sstevel@tonic-gate 		proc_list_probe();
398*7c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(s, SM_TIME_DEFAULT,
399*7c478bd9Sstevel@tonic-gate 				     "C:%d\r\nM:%d\r\nL:%d\r\n",
400*7c478bd9Sstevel@tonic-gate 				     CurChildren, MaxChildren,
401*7c478bd9Sstevel@tonic-gate 				     getla());
402*7c478bd9Sstevel@tonic-gate 		printnqe(s, "Q:");
403*7c478bd9Sstevel@tonic-gate 		disk_status(s, "D:");
404*7c478bd9Sstevel@tonic-gate 		proc_list_display(s, "P:");
405*7c478bd9Sstevel@tonic-gate 		break;
406*7c478bd9Sstevel@tonic-gate # endif /* _FFR_CONTROL_MSTAT */
407*7c478bd9Sstevel@tonic-gate 
408*7c478bd9Sstevel@tonic-gate 	  case CMDMEMDUMP:	/* daemon memory dump, to find memory leaks */
409*7c478bd9Sstevel@tonic-gate # if SM_HEAP_CHECK
410*7c478bd9Sstevel@tonic-gate 		/* dump the heap, if we are checking for memory leaks */
411*7c478bd9Sstevel@tonic-gate 		if (sm_debug_active(&SmHeapCheck, 2))
412*7c478bd9Sstevel@tonic-gate 		{
413*7c478bd9Sstevel@tonic-gate 			sm_heap_report(s, sm_debug_level(&SmHeapCheck) - 1);
414*7c478bd9Sstevel@tonic-gate 		}
415*7c478bd9Sstevel@tonic-gate 		else
416*7c478bd9Sstevel@tonic-gate 		{
417*7c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(s, SM_TIME_DEFAULT,
418*7c478bd9Sstevel@tonic-gate 					     "Memory dump unavailable.\r\n");
419*7c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(s, SM_TIME_DEFAULT,
420*7c478bd9Sstevel@tonic-gate 					     "To fix, run sendmail with -dsm_check_heap.4\r\n");
421*7c478bd9Sstevel@tonic-gate 		}
422*7c478bd9Sstevel@tonic-gate # else /* SM_HEAP_CHECK */
423*7c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(s, SM_TIME_DEFAULT,
424*7c478bd9Sstevel@tonic-gate 				     "Memory dump unavailable.\r\n");
425*7c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(s, SM_TIME_DEFAULT,
426*7c478bd9Sstevel@tonic-gate 				     "To fix, rebuild with -DSM_HEAP_CHECK\r\n");
427*7c478bd9Sstevel@tonic-gate # endif /* SM_HEAP_CHECK */
428*7c478bd9Sstevel@tonic-gate 		break;
429*7c478bd9Sstevel@tonic-gate 
430*7c478bd9Sstevel@tonic-gate 	  case CMDERROR:	/* unknown command */
431*7c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(s, SM_TIME_DEFAULT,
432*7c478bd9Sstevel@tonic-gate 				     "Bad command (%s)\r\n", cmdbuf);
433*7c478bd9Sstevel@tonic-gate 		break;
434*7c478bd9Sstevel@tonic-gate 	}
435*7c478bd9Sstevel@tonic-gate 	(void) sm_io_close(s, SM_TIME_DEFAULT);
436*7c478bd9Sstevel@tonic-gate 	if (ev != NULL)
437*7c478bd9Sstevel@tonic-gate 		sm_clrevent(ev);
438*7c478bd9Sstevel@tonic-gate 	exit(exitstat);
439*7c478bd9Sstevel@tonic-gate }
440