xref: /freebsd-src/contrib/sendmail/src/control.c (revision d39bd2c1388b520fcba9abed1932acacead60fba)
1065a643dSPeter Wemm /*
25dd76dd0SGregory Neil Shapiro  * Copyright (c) 1998-2004, 2006 Proofpoint, Inc. and its suppliers.
306f25ae9SGregory Neil Shapiro  *	All rights reserved.
4065a643dSPeter Wemm  *
5065a643dSPeter Wemm  * By using this file, you agree to the terms and conditions set
6065a643dSPeter Wemm  * forth in the LICENSE file which can be found at the top level of
7065a643dSPeter Wemm  * the sendmail distribution.
8065a643dSPeter Wemm  *
9065a643dSPeter Wemm  */
10065a643dSPeter Wemm 
1106f25ae9SGregory Neil Shapiro #include <sendmail.h>
1206f25ae9SGregory Neil Shapiro 
134313cc83SGregory Neil Shapiro SM_RCSID("@(#)$Id: control.c,v 8.130 2013-11-22 20:51:55 ca Exp $")
1413bd1963SGregory Neil Shapiro 
15*2fb4f839SGregory Neil Shapiro #include <sm/sendmail.h>
1613bd1963SGregory Neil Shapiro #include <sm/fdset.h>
1740266059SGregory Neil Shapiro 
188774250cSGregory Neil Shapiro /* values for cmd_code */
198774250cSGregory Neil Shapiro #define CMDERROR	0	/* bad command */
208774250cSGregory Neil Shapiro #define CMDRESTART	1	/* restart daemon */
218774250cSGregory Neil Shapiro #define CMDSHUTDOWN	2	/* end daemon */
228774250cSGregory Neil Shapiro #define CMDHELP		3	/* help */
238774250cSGregory Neil Shapiro #define CMDSTATUS	4	/* daemon status */
2440266059SGregory Neil Shapiro #define CMDMEMDUMP	5	/* dump memory, to find memory leaks */
2540266059SGregory Neil Shapiro #define CMDMSTAT	6	/* daemon status, more info, tagged data */
268774250cSGregory Neil Shapiro 
278774250cSGregory Neil Shapiro struct cmd
288774250cSGregory Neil Shapiro {
298774250cSGregory Neil Shapiro 	char	*cmd_name;	/* command name */
308774250cSGregory Neil Shapiro 	int	cmd_code;	/* internal code, see below */
318774250cSGregory Neil Shapiro };
328774250cSGregory Neil Shapiro 
338774250cSGregory Neil Shapiro static struct cmd	CmdTab[] =
348774250cSGregory Neil Shapiro {
358774250cSGregory Neil Shapiro 	{ "help",	CMDHELP		},
368774250cSGregory Neil Shapiro 	{ "restart",	CMDRESTART	},
378774250cSGregory Neil Shapiro 	{ "shutdown",	CMDSHUTDOWN	},
388774250cSGregory Neil Shapiro 	{ "status",	CMDSTATUS	},
3940266059SGregory Neil Shapiro 	{ "memdump",	CMDMEMDUMP	},
4040266059SGregory Neil Shapiro 	{ "mstat",	CMDMSTAT	},
418774250cSGregory Neil Shapiro 	{ NULL,		CMDERROR	}
428774250cSGregory Neil Shapiro };
438774250cSGregory Neil Shapiro 
44b6bacd31SGregory Neil Shapiro static void	controltimeout __P((int));
45065a643dSPeter Wemm int ControlSocket = -1;
46065a643dSPeter Wemm 
4740266059SGregory Neil Shapiro /*
48065a643dSPeter Wemm **  OPENCONTROLSOCKET -- create/open the daemon control named socket
49065a643dSPeter Wemm **
50065a643dSPeter Wemm **	Creates and opens a named socket for external control over
51065a643dSPeter Wemm **	the sendmail daemon.
52065a643dSPeter Wemm **
53065a643dSPeter Wemm **	Parameters:
54065a643dSPeter Wemm **		none.
55065a643dSPeter Wemm **
56065a643dSPeter Wemm **	Returns:
57065a643dSPeter Wemm **		0 if successful, -1 otherwise
58065a643dSPeter Wemm */
59065a643dSPeter Wemm 
60065a643dSPeter Wemm int
opencontrolsocket()61065a643dSPeter Wemm opencontrolsocket()
62065a643dSPeter Wemm {
6306f25ae9SGregory Neil Shapiro #if NETUNIX
6406f25ae9SGregory Neil Shapiro 	int save_errno;
65065a643dSPeter Wemm 	int rval;
6606f25ae9SGregory Neil Shapiro 	long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN;
67065a643dSPeter Wemm 	struct sockaddr_un controladdr;
68065a643dSPeter Wemm 
69*2fb4f839SGregory Neil Shapiro 	if (SM_IS_EMPTY(ControlSocketName))
70065a643dSPeter Wemm 		return 0;
71065a643dSPeter Wemm 
72d0cef73dSGregory Neil Shapiro 	if (strlen(ControlSocketName) >= sizeof(controladdr.sun_path))
73065a643dSPeter Wemm 	{
74065a643dSPeter Wemm 		errno = ENAMETOOLONG;
75065a643dSPeter Wemm 		return -1;
76065a643dSPeter Wemm 	}
77065a643dSPeter Wemm 
78065a643dSPeter Wemm 	rval = safefile(ControlSocketName, RunAsUid, RunAsGid, RunAsUserName,
79065a643dSPeter Wemm 			sff, S_IRUSR|S_IWUSR, NULL);
80065a643dSPeter Wemm 
81065a643dSPeter Wemm 	/* if not safe, don't create */
82065a643dSPeter Wemm 	if (rval != 0)
83065a643dSPeter Wemm 	{
84065a643dSPeter Wemm 		errno = rval;
85065a643dSPeter Wemm 		return -1;
86065a643dSPeter Wemm 	}
87065a643dSPeter Wemm 
88065a643dSPeter Wemm 	ControlSocket = socket(AF_UNIX, SOCK_STREAM, 0);
89065a643dSPeter Wemm 	if (ControlSocket < 0)
90065a643dSPeter Wemm 		return -1;
9113bd1963SGregory Neil Shapiro 	if (SM_FD_SETSIZE > 0 && ControlSocket >= SM_FD_SETSIZE)
9213bd1963SGregory Neil Shapiro 	{
9313bd1963SGregory Neil Shapiro 		clrcontrol();
9413bd1963SGregory Neil Shapiro 		errno = EINVAL;
9513bd1963SGregory Neil Shapiro 		return -1;
9613bd1963SGregory Neil Shapiro 	}
97065a643dSPeter Wemm 
9806f25ae9SGregory Neil Shapiro 	(void) unlink(ControlSocketName);
99d0cef73dSGregory Neil Shapiro 	memset(&controladdr, '\0', sizeof(controladdr));
100065a643dSPeter Wemm 	controladdr.sun_family = AF_UNIX;
10140266059SGregory Neil Shapiro 	(void) sm_strlcpy(controladdr.sun_path, ControlSocketName,
102d0cef73dSGregory Neil Shapiro 			  sizeof(controladdr.sun_path));
103065a643dSPeter Wemm 
104065a643dSPeter Wemm 	if (bind(ControlSocket, (struct sockaddr *) &controladdr,
105d0cef73dSGregory Neil Shapiro 		 sizeof(controladdr)) < 0)
106065a643dSPeter Wemm 	{
10706f25ae9SGregory Neil Shapiro 		save_errno = errno;
1082e43090eSPeter Wemm 		clrcontrol();
109065a643dSPeter Wemm 		errno = save_errno;
110065a643dSPeter Wemm 		return -1;
111065a643dSPeter Wemm 	}
112065a643dSPeter Wemm 
113193538b7SGregory Neil Shapiro 	if (geteuid() == 0)
114065a643dSPeter Wemm 	{
115193538b7SGregory Neil Shapiro 		uid_t u = 0;
116193538b7SGregory Neil Shapiro 
117193538b7SGregory Neil Shapiro 		if (RunAsUid != 0)
118193538b7SGregory Neil Shapiro 			u = RunAsUid;
119193538b7SGregory Neil Shapiro 		else if (TrustedUid != 0)
120193538b7SGregory Neil Shapiro 			u = TrustedUid;
121193538b7SGregory Neil Shapiro 
122193538b7SGregory Neil Shapiro 		if (u != 0 &&
123193538b7SGregory Neil Shapiro 		    chown(ControlSocketName, u, -1) < 0)
124065a643dSPeter Wemm 		{
12506f25ae9SGregory Neil Shapiro 			save_errno = errno;
126065a643dSPeter Wemm 			sm_syslog(LOG_ALERT, NOQID,
127193538b7SGregory Neil Shapiro 				  "ownership change on %s to uid %d failed: %s",
128193538b7SGregory Neil Shapiro 				  ControlSocketName, (int) u,
12940266059SGregory Neil Shapiro 				  sm_errstring(save_errno));
130193538b7SGregory Neil Shapiro 			message("050 ownership change on %s to uid %d failed: %s",
131193538b7SGregory Neil Shapiro 				ControlSocketName, (int) u,
13240266059SGregory Neil Shapiro 				sm_errstring(save_errno));
13340266059SGregory Neil Shapiro 			closecontrolsocket(true);
134065a643dSPeter Wemm 			errno = save_errno;
135065a643dSPeter Wemm 			return -1;
136065a643dSPeter Wemm 		}
137065a643dSPeter Wemm 	}
138065a643dSPeter Wemm 
139065a643dSPeter Wemm 	if (chmod(ControlSocketName, S_IRUSR|S_IWUSR) < 0)
140065a643dSPeter Wemm 	{
14106f25ae9SGregory Neil Shapiro 		save_errno = errno;
14240266059SGregory Neil Shapiro 		closecontrolsocket(true);
143065a643dSPeter Wemm 		errno = save_errno;
144065a643dSPeter Wemm 		return -1;
145065a643dSPeter Wemm 	}
146065a643dSPeter Wemm 
147065a643dSPeter Wemm 	if (listen(ControlSocket, 8) < 0)
148065a643dSPeter Wemm 	{
14906f25ae9SGregory Neil Shapiro 		save_errno = errno;
15040266059SGregory Neil Shapiro 		closecontrolsocket(true);
151065a643dSPeter Wemm 		errno = save_errno;
152065a643dSPeter Wemm 		return -1;
153065a643dSPeter Wemm 	}
15406f25ae9SGregory Neil Shapiro #endif /* NETUNIX */
155065a643dSPeter Wemm 	return 0;
156065a643dSPeter Wemm }
15740266059SGregory Neil Shapiro /*
158065a643dSPeter Wemm **  CLOSECONTROLSOCKET -- close the daemon control named socket
159065a643dSPeter Wemm **
160065a643dSPeter Wemm **	Close a named socket.
161065a643dSPeter Wemm **
162065a643dSPeter Wemm **	Parameters:
163065a643dSPeter Wemm **		fullclose -- if set, close the socket and remove it;
164065a643dSPeter Wemm **			     otherwise, just remove it
165065a643dSPeter Wemm **
166065a643dSPeter Wemm **	Returns:
167065a643dSPeter Wemm **		none.
168065a643dSPeter Wemm */
169065a643dSPeter Wemm 
170065a643dSPeter Wemm void
closecontrolsocket(fullclose)171065a643dSPeter Wemm closecontrolsocket(fullclose)
172065a643dSPeter Wemm 	bool fullclose;
173065a643dSPeter Wemm {
17406f25ae9SGregory Neil Shapiro #if NETUNIX
17506f25ae9SGregory Neil Shapiro 	long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN;
176065a643dSPeter Wemm 
177065a643dSPeter Wemm 	if (ControlSocket >= 0)
178065a643dSPeter Wemm 	{
179065a643dSPeter Wemm 		int rval;
180065a643dSPeter Wemm 
181065a643dSPeter Wemm 		if (fullclose)
182065a643dSPeter Wemm 		{
183065a643dSPeter Wemm 			(void) close(ControlSocket);
184065a643dSPeter Wemm 			ControlSocket = -1;
185065a643dSPeter Wemm 		}
186065a643dSPeter Wemm 
187193538b7SGregory Neil Shapiro 		rval = safefile(ControlSocketName, RunAsUid, RunAsGid,
188193538b7SGregory Neil Shapiro 				RunAsUserName, sff, S_IRUSR|S_IWUSR, NULL);
189065a643dSPeter Wemm 
190065a643dSPeter Wemm 		/* if not safe, don't unlink */
191065a643dSPeter Wemm 		if (rval != 0)
192065a643dSPeter Wemm 			return;
193065a643dSPeter Wemm 
194065a643dSPeter Wemm 		if (unlink(ControlSocketName) < 0)
195065a643dSPeter Wemm 		{
196065a643dSPeter Wemm 			sm_syslog(LOG_WARNING, NOQID,
197065a643dSPeter Wemm 				  "Could not remove control socket: %s",
19840266059SGregory Neil Shapiro 				  sm_errstring(errno));
199065a643dSPeter Wemm 			return;
200065a643dSPeter Wemm 		}
201065a643dSPeter Wemm 	}
20206f25ae9SGregory Neil Shapiro #endif /* NETUNIX */
203065a643dSPeter Wemm 	return;
204065a643dSPeter Wemm }
20540266059SGregory Neil Shapiro /*
206065a643dSPeter Wemm **  CLRCONTROL -- reset the control connection
207065a643dSPeter Wemm **
208065a643dSPeter Wemm **	Parameters:
209065a643dSPeter Wemm **		none.
210065a643dSPeter Wemm **
211065a643dSPeter Wemm **	Returns:
212065a643dSPeter Wemm **		none.
213065a643dSPeter Wemm **
214065a643dSPeter Wemm **	Side Effects:
215065a643dSPeter Wemm **		releases any resources used by the control interface.
216065a643dSPeter Wemm */
217065a643dSPeter Wemm 
218065a643dSPeter Wemm void
clrcontrol()219065a643dSPeter Wemm clrcontrol()
220065a643dSPeter Wemm {
22106f25ae9SGregory Neil Shapiro #if NETUNIX
222065a643dSPeter Wemm 	if (ControlSocket >= 0)
223065a643dSPeter Wemm 		(void) close(ControlSocket);
224065a643dSPeter Wemm 	ControlSocket = -1;
22506f25ae9SGregory Neil Shapiro #endif /* NETUNIX */
226065a643dSPeter Wemm }
22740266059SGregory Neil Shapiro /*
228065a643dSPeter Wemm **  CONTROL_COMMAND -- read and process command from named socket
229065a643dSPeter Wemm **
230065a643dSPeter Wemm **	Read and process the command from the opened socket.
23106f25ae9SGregory Neil Shapiro **	Exits when done since it is running in a forked child.
232065a643dSPeter Wemm **
233065a643dSPeter Wemm **	Parameters:
234065a643dSPeter Wemm **		sock -- the opened socket from getrequests()
235065a643dSPeter Wemm **		e -- the current envelope
236065a643dSPeter Wemm **
237065a643dSPeter Wemm **	Returns:
238065a643dSPeter Wemm **		none.
239065a643dSPeter Wemm */
240065a643dSPeter Wemm 
24106f25ae9SGregory Neil Shapiro static jmp_buf	CtxControlTimeout;
24206f25ae9SGregory Neil Shapiro 
24340266059SGregory Neil Shapiro /* ARGSUSED0 */
24406f25ae9SGregory Neil Shapiro static void
controltimeout(timeout)24506f25ae9SGregory Neil Shapiro controltimeout(timeout)
246b6bacd31SGregory Neil Shapiro 	int timeout;
24706f25ae9SGregory Neil Shapiro {
2488774250cSGregory Neil Shapiro 	/*
2498774250cSGregory Neil Shapiro 	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
2508774250cSGregory Neil Shapiro 	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
2518774250cSGregory Neil Shapiro 	**	DOING.
2528774250cSGregory Neil Shapiro 	*/
2538774250cSGregory Neil Shapiro 
2548774250cSGregory Neil Shapiro 	errno = ETIMEDOUT;
25506f25ae9SGregory Neil Shapiro 	longjmp(CtxControlTimeout, 1);
25606f25ae9SGregory Neil Shapiro }
25706f25ae9SGregory Neil Shapiro 
258065a643dSPeter Wemm void
control_command(sock,e)259065a643dSPeter Wemm control_command(sock, e)
260065a643dSPeter Wemm 	int sock;
261065a643dSPeter Wemm 	ENVELOPE *e;
262065a643dSPeter Wemm {
26306f25ae9SGregory Neil Shapiro 	volatile int exitstat = EX_OK;
26440266059SGregory Neil Shapiro 	SM_FILE_T *s = NULL;
26540266059SGregory Neil Shapiro 	SM_EVENT *ev = NULL;
26640266059SGregory Neil Shapiro 	SM_FILE_T *traffic;
26740266059SGregory Neil Shapiro 	SM_FILE_T *oldout;
268065a643dSPeter Wemm 	char *cmd;
269065a643dSPeter Wemm 	char *p;
270065a643dSPeter Wemm 	struct cmd *c;
271065a643dSPeter Wemm 	char cmdbuf[MAXLINE];
272065a643dSPeter Wemm 	char inp[MAXLINE];
273065a643dSPeter Wemm 
27440266059SGregory Neil Shapiro 	sm_setproctitle(false, e, "control cmd read");
27506f25ae9SGregory Neil Shapiro 
27606f25ae9SGregory Neil Shapiro 	if (TimeOuts.to_control > 0)
27706f25ae9SGregory Neil Shapiro 	{
27806f25ae9SGregory Neil Shapiro 		/* handle possible input timeout */
27906f25ae9SGregory Neil Shapiro 		if (setjmp(CtxControlTimeout) != 0)
28006f25ae9SGregory Neil Shapiro 		{
28106f25ae9SGregory Neil Shapiro 			if (LogLevel > 2)
28206f25ae9SGregory Neil Shapiro 				sm_syslog(LOG_NOTICE, e->e_id,
28306f25ae9SGregory Neil Shapiro 					  "timeout waiting for input during control command");
28406f25ae9SGregory Neil Shapiro 			exit(EX_IOERR);
28506f25ae9SGregory Neil Shapiro 		}
28640266059SGregory Neil Shapiro 		ev = sm_setevent(TimeOuts.to_control, controltimeout,
28706f25ae9SGregory Neil Shapiro 				 TimeOuts.to_control);
28806f25ae9SGregory Neil Shapiro 	}
289065a643dSPeter Wemm 
29040266059SGregory Neil Shapiro 	s = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, (void *) &sock,
29140266059SGregory Neil Shapiro 		       SM_IO_RDWR, NULL);
292065a643dSPeter Wemm 	if (s == NULL)
293065a643dSPeter Wemm 	{
294065a643dSPeter Wemm 		int save_errno = errno;
295065a643dSPeter Wemm 
29606f25ae9SGregory Neil Shapiro 		(void) close(sock);
297065a643dSPeter Wemm 		errno = save_errno;
29806f25ae9SGregory Neil Shapiro 		exit(EX_IOERR);
299065a643dSPeter Wemm 	}
30040266059SGregory Neil Shapiro 	(void) sm_io_setvbuf(s, SM_TIME_DEFAULT, NULL,
30140266059SGregory Neil Shapiro 			     SM_IO_NBF, SM_IO_BUFSIZ);
302065a643dSPeter Wemm 
303552d4955SGregory Neil Shapiro 	if (sm_io_fgets(s, SM_TIME_DEFAULT, inp, sizeof(inp)) < 0)
304065a643dSPeter Wemm 	{
30540266059SGregory Neil Shapiro 		(void) sm_io_close(s, SM_TIME_DEFAULT);
30606f25ae9SGregory Neil Shapiro 		exit(EX_IOERR);
307065a643dSPeter Wemm 	}
30840266059SGregory Neil Shapiro 	(void) sm_io_flush(s, SM_TIME_DEFAULT);
309065a643dSPeter Wemm 
310065a643dSPeter Wemm 	/* clean up end of line */
31140266059SGregory Neil Shapiro 	fixcrlf(inp, true);
312065a643dSPeter Wemm 
31340266059SGregory Neil Shapiro 	sm_setproctitle(false, e, "control: %s", inp);
314065a643dSPeter Wemm 
315065a643dSPeter Wemm 	/* break off command */
3165b0945b5SGregory Neil Shapiro 	for (p = inp; SM_ISSPACE(*p); p++)
317065a643dSPeter Wemm 		continue;
318065a643dSPeter Wemm 	cmd = cmdbuf;
319065a643dSPeter Wemm 	while (*p != '\0' &&
3205b0945b5SGregory Neil Shapiro 	       !(SM_ISSPACE(*p)) && cmd < &cmdbuf[sizeof(cmdbuf) - 2])
321065a643dSPeter Wemm 		*cmd++ = *p++;
322065a643dSPeter Wemm 	*cmd = '\0';
323065a643dSPeter Wemm 
324065a643dSPeter Wemm 	/* throw away leading whitespace */
3255b0945b5SGregory Neil Shapiro 	while (SM_ISSPACE(*p))
326065a643dSPeter Wemm 		p++;
327065a643dSPeter Wemm 
328065a643dSPeter Wemm 	/* decode command */
32906f25ae9SGregory Neil Shapiro 	for (c = CmdTab; c->cmd_name != NULL; c++)
330065a643dSPeter Wemm 	{
331*2fb4f839SGregory Neil Shapiro 		if (SM_STRCASEEQ(c->cmd_name, cmdbuf))
332065a643dSPeter Wemm 			break;
333065a643dSPeter Wemm 	}
334065a643dSPeter Wemm 
33506f25ae9SGregory Neil Shapiro 	switch (c->cmd_code)
336065a643dSPeter Wemm 	{
337065a643dSPeter Wemm 	  case CMDHELP:		/* get help */
338065a643dSPeter Wemm 		traffic = TrafficLogFile;
339065a643dSPeter Wemm 		TrafficLogFile = NULL;
340065a643dSPeter Wemm 		oldout = OutChannel;
341065a643dSPeter Wemm 		OutChannel = s;
34206f25ae9SGregory Neil Shapiro 		help("control", e);
343065a643dSPeter Wemm 		TrafficLogFile = traffic;
344065a643dSPeter Wemm 		OutChannel = oldout;
345065a643dSPeter Wemm 		break;
346065a643dSPeter Wemm 
347065a643dSPeter Wemm 	  case CMDRESTART:	/* restart the daemon */
34840266059SGregory Neil Shapiro 		(void) sm_io_fprintf(s, SM_TIME_DEFAULT, "OK\r\n");
34906f25ae9SGregory Neil Shapiro 		exitstat = EX_RESTART;
350065a643dSPeter Wemm 		break;
351065a643dSPeter Wemm 
352065a643dSPeter Wemm 	  case CMDSHUTDOWN:	/* kill the daemon */
35340266059SGregory Neil Shapiro 		(void) sm_io_fprintf(s, SM_TIME_DEFAULT, "OK\r\n");
35406f25ae9SGregory Neil Shapiro 		exitstat = EX_SHUTDOWN;
355065a643dSPeter Wemm 		break;
356065a643dSPeter Wemm 
357065a643dSPeter Wemm 	  case CMDSTATUS:	/* daemon status */
358065a643dSPeter Wemm 		proc_list_probe();
35906f25ae9SGregory Neil Shapiro 		{
36040266059SGregory Neil Shapiro 			int qgrp;
36106f25ae9SGregory Neil Shapiro 			long bsize;
36206f25ae9SGregory Neil Shapiro 			long free;
36306f25ae9SGregory Neil Shapiro 
36440266059SGregory Neil Shapiro 			/* XXX need to deal with different partitions */
36540266059SGregory Neil Shapiro 			qgrp = e->e_qgrp;
36640266059SGregory Neil Shapiro 			if (!ISVALIDQGRP(qgrp))
36740266059SGregory Neil Shapiro 				qgrp = 0;
36840266059SGregory Neil Shapiro 			free = freediskspace(Queue[qgrp]->qg_qdir, &bsize);
36906f25ae9SGregory Neil Shapiro 
37006f25ae9SGregory Neil Shapiro 			/*
37106f25ae9SGregory Neil Shapiro 			**  Prevent overflow and don't lose
37206f25ae9SGregory Neil Shapiro 			**  precision (if bsize == 512)
37306f25ae9SGregory Neil Shapiro 			*/
37406f25ae9SGregory Neil Shapiro 
37540266059SGregory Neil Shapiro 			if (free > 0)
37640266059SGregory Neil Shapiro 				free = (long)((double) free *
37740266059SGregory Neil Shapiro 					      ((double) bsize / 1024));
37806f25ae9SGregory Neil Shapiro 
37940266059SGregory Neil Shapiro 			(void) sm_io_fprintf(s, SM_TIME_DEFAULT,
38040266059SGregory Neil Shapiro 					     "%d/%d/%ld/%d\r\n",
38106f25ae9SGregory Neil Shapiro 					     CurChildren, MaxChildren,
38240266059SGregory Neil Shapiro 					     free, getla());
38306f25ae9SGregory Neil Shapiro 		}
38440266059SGregory Neil Shapiro 		proc_list_display(s, "");
38540266059SGregory Neil Shapiro 		break;
38640266059SGregory Neil Shapiro 
38740266059SGregory Neil Shapiro 	  case CMDMSTAT:	/* daemon status, extended, tagged format */
38840266059SGregory Neil Shapiro 		proc_list_probe();
38940266059SGregory Neil Shapiro 		(void) sm_io_fprintf(s, SM_TIME_DEFAULT,
39040266059SGregory Neil Shapiro 				     "C:%d\r\nM:%d\r\nL:%d\r\n",
39140266059SGregory Neil Shapiro 				     CurChildren, MaxChildren,
39240266059SGregory Neil Shapiro 				     getla());
39340266059SGregory Neil Shapiro 		printnqe(s, "Q:");
39440266059SGregory Neil Shapiro 		disk_status(s, "D:");
39540266059SGregory Neil Shapiro 		proc_list_display(s, "P:");
39640266059SGregory Neil Shapiro 		break;
39740266059SGregory Neil Shapiro 
39840266059SGregory Neil Shapiro 	  case CMDMEMDUMP:	/* daemon memory dump, to find memory leaks */
39940266059SGregory Neil Shapiro #if SM_HEAP_CHECK
40040266059SGregory Neil Shapiro 		/* dump the heap, if we are checking for memory leaks */
40140266059SGregory Neil Shapiro 		if (sm_debug_active(&SmHeapCheck, 2))
40240266059SGregory Neil Shapiro 		{
40340266059SGregory Neil Shapiro 			sm_heap_report(s, sm_debug_level(&SmHeapCheck) - 1);
40440266059SGregory Neil Shapiro 		}
40540266059SGregory Neil Shapiro 		else
40640266059SGregory Neil Shapiro 		{
40740266059SGregory Neil Shapiro 			(void) sm_io_fprintf(s, SM_TIME_DEFAULT,
40840266059SGregory Neil Shapiro 					     "Memory dump unavailable.\r\n");
40940266059SGregory Neil Shapiro 			(void) sm_io_fprintf(s, SM_TIME_DEFAULT,
41040266059SGregory Neil Shapiro 					     "To fix, run sendmail with -dsm_check_heap.4\r\n");
41140266059SGregory Neil Shapiro 		}
41240266059SGregory Neil Shapiro #else /* SM_HEAP_CHECK */
41340266059SGregory Neil Shapiro 		(void) sm_io_fprintf(s, SM_TIME_DEFAULT,
41440266059SGregory Neil Shapiro 				     "Memory dump unavailable.\r\n");
41540266059SGregory Neil Shapiro 		(void) sm_io_fprintf(s, SM_TIME_DEFAULT,
41640266059SGregory Neil Shapiro 				     "To fix, rebuild with -DSM_HEAP_CHECK\r\n");
41740266059SGregory Neil Shapiro #endif /* SM_HEAP_CHECK */
418065a643dSPeter Wemm 		break;
419065a643dSPeter Wemm 
420065a643dSPeter Wemm 	  case CMDERROR:	/* unknown command */
42140266059SGregory Neil Shapiro 		(void) sm_io_fprintf(s, SM_TIME_DEFAULT,
42240266059SGregory Neil Shapiro 				     "Bad command (%s)\r\n", cmdbuf);
423065a643dSPeter Wemm 		break;
424065a643dSPeter Wemm 	}
42540266059SGregory Neil Shapiro 	(void) sm_io_close(s, SM_TIME_DEFAULT);
42606f25ae9SGregory Neil Shapiro 	if (ev != NULL)
42740266059SGregory Neil Shapiro 		sm_clrevent(ev);
42806f25ae9SGregory Neil Shapiro 	exit(exitstat);
429065a643dSPeter Wemm }
430