xref: /onnv-gate/usr/src/cmd/sendmail/src/control.c (revision 3544:8dfb1c11a5d7)
10Sstevel@tonic-gate /*
2*3544Sjbeck  * Copyright (c) 1998-2004, 2006 Sendmail, Inc. and its suppliers.
30Sstevel@tonic-gate  *	All rights reserved.
40Sstevel@tonic-gate  *
50Sstevel@tonic-gate  * By using this file, you agree to the terms and conditions set
60Sstevel@tonic-gate  * forth in the LICENSE file which can be found at the top level of
70Sstevel@tonic-gate  * the sendmail distribution.
80Sstevel@tonic-gate  *
90Sstevel@tonic-gate  */
100Sstevel@tonic-gate 
110Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
120Sstevel@tonic-gate 
130Sstevel@tonic-gate #include <sendmail.h>
140Sstevel@tonic-gate 
15*3544Sjbeck SM_RCSID("@(#)$Id: control.c,v 8.128 2006/08/15 23:24:56 ca Exp $")
160Sstevel@tonic-gate 
170Sstevel@tonic-gate #include <sm/fdset.h>
180Sstevel@tonic-gate 
190Sstevel@tonic-gate /* values for cmd_code */
200Sstevel@tonic-gate #define CMDERROR	0	/* bad command */
210Sstevel@tonic-gate #define CMDRESTART	1	/* restart daemon */
220Sstevel@tonic-gate #define CMDSHUTDOWN	2	/* end daemon */
230Sstevel@tonic-gate #define CMDHELP		3	/* help */
240Sstevel@tonic-gate #define CMDSTATUS	4	/* daemon status */
250Sstevel@tonic-gate #define CMDMEMDUMP	5	/* dump memory, to find memory leaks */
26*3544Sjbeck #define CMDMSTAT	6	/* daemon status, more info, tagged data */
270Sstevel@tonic-gate 
280Sstevel@tonic-gate struct cmd
290Sstevel@tonic-gate {
300Sstevel@tonic-gate 	char	*cmd_name;	/* command name */
310Sstevel@tonic-gate 	int	cmd_code;	/* internal code, see below */
320Sstevel@tonic-gate };
330Sstevel@tonic-gate 
340Sstevel@tonic-gate static struct cmd	CmdTab[] =
350Sstevel@tonic-gate {
360Sstevel@tonic-gate 	{ "help",	CMDHELP		},
370Sstevel@tonic-gate 	{ "restart",	CMDRESTART	},
380Sstevel@tonic-gate 	{ "shutdown",	CMDSHUTDOWN	},
390Sstevel@tonic-gate 	{ "status",	CMDSTATUS	},
400Sstevel@tonic-gate 	{ "memdump",	CMDMEMDUMP	},
410Sstevel@tonic-gate 	{ "mstat",	CMDMSTAT	},
420Sstevel@tonic-gate 	{ NULL,		CMDERROR	}
430Sstevel@tonic-gate };
440Sstevel@tonic-gate 
450Sstevel@tonic-gate 
460Sstevel@tonic-gate 
470Sstevel@tonic-gate static void	controltimeout __P((int));
480Sstevel@tonic-gate int ControlSocket = -1;
490Sstevel@tonic-gate 
500Sstevel@tonic-gate /*
510Sstevel@tonic-gate **  OPENCONTROLSOCKET -- create/open the daemon control named socket
520Sstevel@tonic-gate **
530Sstevel@tonic-gate **	Creates and opens a named socket for external control over
540Sstevel@tonic-gate **	the sendmail daemon.
550Sstevel@tonic-gate **
560Sstevel@tonic-gate **	Parameters:
570Sstevel@tonic-gate **		none.
580Sstevel@tonic-gate **
590Sstevel@tonic-gate **	Returns:
600Sstevel@tonic-gate **		0 if successful, -1 otherwise
610Sstevel@tonic-gate */
620Sstevel@tonic-gate 
630Sstevel@tonic-gate int
opencontrolsocket()640Sstevel@tonic-gate opencontrolsocket()
650Sstevel@tonic-gate {
660Sstevel@tonic-gate # if NETUNIX
670Sstevel@tonic-gate 	int save_errno;
680Sstevel@tonic-gate 	int rval;
690Sstevel@tonic-gate 	long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN;
700Sstevel@tonic-gate 	struct sockaddr_un controladdr;
710Sstevel@tonic-gate 
720Sstevel@tonic-gate 	if (ControlSocketName == NULL || *ControlSocketName == '\0')
730Sstevel@tonic-gate 		return 0;
740Sstevel@tonic-gate 
75*3544Sjbeck 	if (strlen(ControlSocketName) >= sizeof(controladdr.sun_path))
760Sstevel@tonic-gate 	{
770Sstevel@tonic-gate 		errno = ENAMETOOLONG;
780Sstevel@tonic-gate 		return -1;
790Sstevel@tonic-gate 	}
800Sstevel@tonic-gate 
810Sstevel@tonic-gate 	rval = safefile(ControlSocketName, RunAsUid, RunAsGid, RunAsUserName,
820Sstevel@tonic-gate 			sff, S_IRUSR|S_IWUSR, NULL);
830Sstevel@tonic-gate 
840Sstevel@tonic-gate 	/* if not safe, don't create */
850Sstevel@tonic-gate 	if (rval != 0)
860Sstevel@tonic-gate 	{
870Sstevel@tonic-gate 		errno = rval;
880Sstevel@tonic-gate 		return -1;
890Sstevel@tonic-gate 	}
900Sstevel@tonic-gate 
910Sstevel@tonic-gate 	ControlSocket = socket(AF_UNIX, SOCK_STREAM, 0);
920Sstevel@tonic-gate 	if (ControlSocket < 0)
930Sstevel@tonic-gate 		return -1;
940Sstevel@tonic-gate 	if (SM_FD_SETSIZE > 0 && ControlSocket >= SM_FD_SETSIZE)
950Sstevel@tonic-gate 	{
960Sstevel@tonic-gate 		clrcontrol();
970Sstevel@tonic-gate 		errno = EINVAL;
980Sstevel@tonic-gate 		return -1;
990Sstevel@tonic-gate 	}
1000Sstevel@tonic-gate 
1010Sstevel@tonic-gate 	(void) unlink(ControlSocketName);
102*3544Sjbeck 	memset(&controladdr, '\0', sizeof(controladdr));
1030Sstevel@tonic-gate 	controladdr.sun_family = AF_UNIX;
1040Sstevel@tonic-gate 	(void) sm_strlcpy(controladdr.sun_path, ControlSocketName,
105*3544Sjbeck 			  sizeof(controladdr.sun_path));
1060Sstevel@tonic-gate 
1070Sstevel@tonic-gate 	if (bind(ControlSocket, (struct sockaddr *) &controladdr,
108*3544Sjbeck 		 sizeof(controladdr)) < 0)
1090Sstevel@tonic-gate 	{
1100Sstevel@tonic-gate 		save_errno = errno;
1110Sstevel@tonic-gate 		clrcontrol();
1120Sstevel@tonic-gate 		errno = save_errno;
1130Sstevel@tonic-gate 		return -1;
1140Sstevel@tonic-gate 	}
1150Sstevel@tonic-gate 
1160Sstevel@tonic-gate 	if (geteuid() == 0)
1170Sstevel@tonic-gate 	{
1180Sstevel@tonic-gate 		uid_t u = 0;
1190Sstevel@tonic-gate 
1200Sstevel@tonic-gate 		if (RunAsUid != 0)
1210Sstevel@tonic-gate 			u = RunAsUid;
1220Sstevel@tonic-gate 		else if (TrustedUid != 0)
1230Sstevel@tonic-gate 			u = TrustedUid;
1240Sstevel@tonic-gate 
1250Sstevel@tonic-gate 		if (u != 0 &&
1260Sstevel@tonic-gate 		    chown(ControlSocketName, u, -1) < 0)
1270Sstevel@tonic-gate 		{
1280Sstevel@tonic-gate 			save_errno = errno;
1290Sstevel@tonic-gate 			sm_syslog(LOG_ALERT, NOQID,
1300Sstevel@tonic-gate 				  "ownership change on %s to uid %d failed: %s",
1310Sstevel@tonic-gate 				  ControlSocketName, (int) u,
1320Sstevel@tonic-gate 				  sm_errstring(save_errno));
1330Sstevel@tonic-gate 			message("050 ownership change on %s to uid %d failed: %s",
1340Sstevel@tonic-gate 				ControlSocketName, (int) u,
1350Sstevel@tonic-gate 				sm_errstring(save_errno));
1360Sstevel@tonic-gate 			closecontrolsocket(true);
1370Sstevel@tonic-gate 			errno = save_errno;
1380Sstevel@tonic-gate 			return -1;
1390Sstevel@tonic-gate 		}
1400Sstevel@tonic-gate 	}
1410Sstevel@tonic-gate 
1420Sstevel@tonic-gate 	if (chmod(ControlSocketName, S_IRUSR|S_IWUSR) < 0)
1430Sstevel@tonic-gate 	{
1440Sstevel@tonic-gate 		save_errno = errno;
1450Sstevel@tonic-gate 		closecontrolsocket(true);
1460Sstevel@tonic-gate 		errno = save_errno;
1470Sstevel@tonic-gate 		return -1;
1480Sstevel@tonic-gate 	}
1490Sstevel@tonic-gate 
1500Sstevel@tonic-gate 	if (listen(ControlSocket, 8) < 0)
1510Sstevel@tonic-gate 	{
1520Sstevel@tonic-gate 		save_errno = errno;
1530Sstevel@tonic-gate 		closecontrolsocket(true);
1540Sstevel@tonic-gate 		errno = save_errno;
1550Sstevel@tonic-gate 		return -1;
1560Sstevel@tonic-gate 	}
1570Sstevel@tonic-gate # endif /* NETUNIX */
1580Sstevel@tonic-gate 	return 0;
1590Sstevel@tonic-gate }
1600Sstevel@tonic-gate /*
1610Sstevel@tonic-gate **  CLOSECONTROLSOCKET -- close the daemon control named socket
1620Sstevel@tonic-gate **
1630Sstevel@tonic-gate **	Close a named socket.
1640Sstevel@tonic-gate **
1650Sstevel@tonic-gate **	Parameters:
1660Sstevel@tonic-gate **		fullclose -- if set, close the socket and remove it;
1670Sstevel@tonic-gate **			     otherwise, just remove it
1680Sstevel@tonic-gate **
1690Sstevel@tonic-gate **	Returns:
1700Sstevel@tonic-gate **		none.
1710Sstevel@tonic-gate */
1720Sstevel@tonic-gate 
1730Sstevel@tonic-gate void
closecontrolsocket(fullclose)1740Sstevel@tonic-gate closecontrolsocket(fullclose)
1750Sstevel@tonic-gate 	bool fullclose;
1760Sstevel@tonic-gate {
1770Sstevel@tonic-gate # if NETUNIX
1780Sstevel@tonic-gate 	long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN;
1790Sstevel@tonic-gate 
1800Sstevel@tonic-gate 	if (ControlSocket >= 0)
1810Sstevel@tonic-gate 	{
1820Sstevel@tonic-gate 		int rval;
1830Sstevel@tonic-gate 
1840Sstevel@tonic-gate 		if (fullclose)
1850Sstevel@tonic-gate 		{
1860Sstevel@tonic-gate 			(void) close(ControlSocket);
1870Sstevel@tonic-gate 			ControlSocket = -1;
1880Sstevel@tonic-gate 		}
1890Sstevel@tonic-gate 
1900Sstevel@tonic-gate 		rval = safefile(ControlSocketName, RunAsUid, RunAsGid,
1910Sstevel@tonic-gate 				RunAsUserName, sff, S_IRUSR|S_IWUSR, NULL);
1920Sstevel@tonic-gate 
1930Sstevel@tonic-gate 		/* if not safe, don't unlink */
1940Sstevel@tonic-gate 		if (rval != 0)
1950Sstevel@tonic-gate 			return;
1960Sstevel@tonic-gate 
1970Sstevel@tonic-gate 		if (unlink(ControlSocketName) < 0)
1980Sstevel@tonic-gate 		{
1990Sstevel@tonic-gate 			sm_syslog(LOG_WARNING, NOQID,
2000Sstevel@tonic-gate 				  "Could not remove control socket: %s",
2010Sstevel@tonic-gate 				  sm_errstring(errno));
2020Sstevel@tonic-gate 			return;
2030Sstevel@tonic-gate 		}
2040Sstevel@tonic-gate 	}
2050Sstevel@tonic-gate # endif /* NETUNIX */
2060Sstevel@tonic-gate 	return;
2070Sstevel@tonic-gate }
2080Sstevel@tonic-gate /*
2090Sstevel@tonic-gate **  CLRCONTROL -- reset the control connection
2100Sstevel@tonic-gate **
2110Sstevel@tonic-gate **	Parameters:
2120Sstevel@tonic-gate **		none.
2130Sstevel@tonic-gate **
2140Sstevel@tonic-gate **	Returns:
2150Sstevel@tonic-gate **		none.
2160Sstevel@tonic-gate **
2170Sstevel@tonic-gate **	Side Effects:
2180Sstevel@tonic-gate **		releases any resources used by the control interface.
2190Sstevel@tonic-gate */
2200Sstevel@tonic-gate 
2210Sstevel@tonic-gate void
clrcontrol()2220Sstevel@tonic-gate clrcontrol()
2230Sstevel@tonic-gate {
2240Sstevel@tonic-gate # if NETUNIX
2250Sstevel@tonic-gate 	if (ControlSocket >= 0)
2260Sstevel@tonic-gate 		(void) close(ControlSocket);
2270Sstevel@tonic-gate 	ControlSocket = -1;
2280Sstevel@tonic-gate # endif /* NETUNIX */
2290Sstevel@tonic-gate }
2300Sstevel@tonic-gate /*
2310Sstevel@tonic-gate **  CONTROL_COMMAND -- read and process command from named socket
2320Sstevel@tonic-gate **
2330Sstevel@tonic-gate **	Read and process the command from the opened socket.
2340Sstevel@tonic-gate **	Exits when done since it is running in a forked child.
2350Sstevel@tonic-gate **
2360Sstevel@tonic-gate **	Parameters:
2370Sstevel@tonic-gate **		sock -- the opened socket from getrequests()
2380Sstevel@tonic-gate **		e -- the current envelope
2390Sstevel@tonic-gate **
2400Sstevel@tonic-gate **	Returns:
2410Sstevel@tonic-gate **		none.
2420Sstevel@tonic-gate */
2430Sstevel@tonic-gate 
2440Sstevel@tonic-gate static jmp_buf	CtxControlTimeout;
2450Sstevel@tonic-gate 
2460Sstevel@tonic-gate /* ARGSUSED0 */
2470Sstevel@tonic-gate static void
controltimeout(timeout)2480Sstevel@tonic-gate controltimeout(timeout)
2490Sstevel@tonic-gate 	int timeout;
2500Sstevel@tonic-gate {
2510Sstevel@tonic-gate 	/*
2520Sstevel@tonic-gate 	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
2530Sstevel@tonic-gate 	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
2540Sstevel@tonic-gate 	**	DOING.
2550Sstevel@tonic-gate 	*/
2560Sstevel@tonic-gate 
2570Sstevel@tonic-gate 	errno = ETIMEDOUT;
2580Sstevel@tonic-gate 	longjmp(CtxControlTimeout, 1);
2590Sstevel@tonic-gate }
2600Sstevel@tonic-gate 
2610Sstevel@tonic-gate void
control_command(sock,e)2620Sstevel@tonic-gate control_command(sock, e)
2630Sstevel@tonic-gate 	int sock;
2640Sstevel@tonic-gate 	ENVELOPE *e;
2650Sstevel@tonic-gate {
2660Sstevel@tonic-gate 	volatile int exitstat = EX_OK;
2670Sstevel@tonic-gate 	SM_FILE_T *s = NULL;
2680Sstevel@tonic-gate 	SM_EVENT *ev = NULL;
2690Sstevel@tonic-gate 	SM_FILE_T *traffic;
2700Sstevel@tonic-gate 	SM_FILE_T *oldout;
2710Sstevel@tonic-gate 	char *cmd;
2720Sstevel@tonic-gate 	char *p;
2730Sstevel@tonic-gate 	struct cmd *c;
2740Sstevel@tonic-gate 	char cmdbuf[MAXLINE];
2750Sstevel@tonic-gate 	char inp[MAXLINE];
2760Sstevel@tonic-gate 
2770Sstevel@tonic-gate 	sm_setproctitle(false, e, "control cmd read");
2780Sstevel@tonic-gate 
2790Sstevel@tonic-gate 	if (TimeOuts.to_control > 0)
2800Sstevel@tonic-gate 	{
2810Sstevel@tonic-gate 		/* handle possible input timeout */
2820Sstevel@tonic-gate 		if (setjmp(CtxControlTimeout) != 0)
2830Sstevel@tonic-gate 		{
2840Sstevel@tonic-gate 			if (LogLevel > 2)
2850Sstevel@tonic-gate 				sm_syslog(LOG_NOTICE, e->e_id,
2860Sstevel@tonic-gate 					  "timeout waiting for input during control command");
2870Sstevel@tonic-gate 			exit(EX_IOERR);
2880Sstevel@tonic-gate 		}
2890Sstevel@tonic-gate 		ev = sm_setevent(TimeOuts.to_control, controltimeout,
2900Sstevel@tonic-gate 				 TimeOuts.to_control);
2910Sstevel@tonic-gate 	}
2920Sstevel@tonic-gate 
2930Sstevel@tonic-gate 	s = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, (void *) &sock,
2940Sstevel@tonic-gate 		       SM_IO_RDWR, NULL);
2950Sstevel@tonic-gate 	if (s == NULL)
2960Sstevel@tonic-gate 	{
2970Sstevel@tonic-gate 		int save_errno = errno;
2980Sstevel@tonic-gate 
2990Sstevel@tonic-gate 		(void) close(sock);
3000Sstevel@tonic-gate 		errno = save_errno;
3010Sstevel@tonic-gate 		exit(EX_IOERR);
3020Sstevel@tonic-gate 	}
3030Sstevel@tonic-gate 	(void) sm_io_setvbuf(s, SM_TIME_DEFAULT, NULL,
3040Sstevel@tonic-gate 			     SM_IO_NBF, SM_IO_BUFSIZ);
3050Sstevel@tonic-gate 
306*3544Sjbeck 	if (sm_io_fgets(s, SM_TIME_DEFAULT, inp, sizeof(inp)) == NULL)
3070Sstevel@tonic-gate 	{
3080Sstevel@tonic-gate 		(void) sm_io_close(s, SM_TIME_DEFAULT);
3090Sstevel@tonic-gate 		exit(EX_IOERR);
3100Sstevel@tonic-gate 	}
3110Sstevel@tonic-gate 	(void) sm_io_flush(s, SM_TIME_DEFAULT);
3120Sstevel@tonic-gate 
3130Sstevel@tonic-gate 	/* clean up end of line */
3140Sstevel@tonic-gate 	fixcrlf(inp, true);
3150Sstevel@tonic-gate 
3160Sstevel@tonic-gate 	sm_setproctitle(false, e, "control: %s", inp);
3170Sstevel@tonic-gate 
3180Sstevel@tonic-gate 	/* break off command */
3190Sstevel@tonic-gate 	for (p = inp; isascii(*p) && isspace(*p); p++)
3200Sstevel@tonic-gate 		continue;
3210Sstevel@tonic-gate 	cmd = cmdbuf;
3220Sstevel@tonic-gate 	while (*p != '\0' &&
3230Sstevel@tonic-gate 	       !(isascii(*p) && isspace(*p)) &&
324*3544Sjbeck 	       cmd < &cmdbuf[sizeof(cmdbuf) - 2])
3250Sstevel@tonic-gate 		*cmd++ = *p++;
3260Sstevel@tonic-gate 	*cmd = '\0';
3270Sstevel@tonic-gate 
3280Sstevel@tonic-gate 	/* throw away leading whitespace */
3290Sstevel@tonic-gate 	while (isascii(*p) && isspace(*p))
3300Sstevel@tonic-gate 		p++;
3310Sstevel@tonic-gate 
3320Sstevel@tonic-gate 	/* decode command */
3330Sstevel@tonic-gate 	for (c = CmdTab; c->cmd_name != NULL; c++)
3340Sstevel@tonic-gate 	{
3350Sstevel@tonic-gate 		if (sm_strcasecmp(c->cmd_name, cmdbuf) == 0)
3360Sstevel@tonic-gate 			break;
3370Sstevel@tonic-gate 	}
3380Sstevel@tonic-gate 
3390Sstevel@tonic-gate 	switch (c->cmd_code)
3400Sstevel@tonic-gate 	{
3410Sstevel@tonic-gate 	  case CMDHELP:		/* get help */
3420Sstevel@tonic-gate 		traffic = TrafficLogFile;
3430Sstevel@tonic-gate 		TrafficLogFile = NULL;
3440Sstevel@tonic-gate 		oldout = OutChannel;
3450Sstevel@tonic-gate 		OutChannel = s;
3460Sstevel@tonic-gate 		help("control", e);
3470Sstevel@tonic-gate 		TrafficLogFile = traffic;
3480Sstevel@tonic-gate 		OutChannel = oldout;
3490Sstevel@tonic-gate 		break;
3500Sstevel@tonic-gate 
3510Sstevel@tonic-gate 	  case CMDRESTART:	/* restart the daemon */
3520Sstevel@tonic-gate 		(void) sm_io_fprintf(s, SM_TIME_DEFAULT, "OK\r\n");
3530Sstevel@tonic-gate 		exitstat = EX_RESTART;
3540Sstevel@tonic-gate 		break;
3550Sstevel@tonic-gate 
3560Sstevel@tonic-gate 	  case CMDSHUTDOWN:	/* kill the daemon */
3570Sstevel@tonic-gate 		(void) sm_io_fprintf(s, SM_TIME_DEFAULT, "OK\r\n");
3580Sstevel@tonic-gate 		exitstat = EX_SHUTDOWN;
3590Sstevel@tonic-gate 		break;
3600Sstevel@tonic-gate 
3610Sstevel@tonic-gate 	  case CMDSTATUS:	/* daemon status */
3620Sstevel@tonic-gate 		proc_list_probe();
3630Sstevel@tonic-gate 		{
3640Sstevel@tonic-gate 			int qgrp;
3650Sstevel@tonic-gate 			long bsize;
3660Sstevel@tonic-gate 			long free;
3670Sstevel@tonic-gate 
3680Sstevel@tonic-gate 			/* XXX need to deal with different partitions */
3690Sstevel@tonic-gate 			qgrp = e->e_qgrp;
3700Sstevel@tonic-gate 			if (!ISVALIDQGRP(qgrp))
3710Sstevel@tonic-gate 				qgrp = 0;
3720Sstevel@tonic-gate 			free = freediskspace(Queue[qgrp]->qg_qdir, &bsize);
3730Sstevel@tonic-gate 
3740Sstevel@tonic-gate 			/*
3750Sstevel@tonic-gate 			**  Prevent overflow and don't lose
3760Sstevel@tonic-gate 			**  precision (if bsize == 512)
3770Sstevel@tonic-gate 			*/
3780Sstevel@tonic-gate 
3790Sstevel@tonic-gate 			if (free > 0)
3800Sstevel@tonic-gate 				free = (long)((double) free *
3810Sstevel@tonic-gate 					      ((double) bsize / 1024));
3820Sstevel@tonic-gate 
3830Sstevel@tonic-gate 			(void) sm_io_fprintf(s, SM_TIME_DEFAULT,
3840Sstevel@tonic-gate 					     "%d/%d/%ld/%d\r\n",
3850Sstevel@tonic-gate 					     CurChildren, MaxChildren,
3860Sstevel@tonic-gate 					     free, getla());
3870Sstevel@tonic-gate 		}
3880Sstevel@tonic-gate 		proc_list_display(s, "");
3890Sstevel@tonic-gate 		break;
3900Sstevel@tonic-gate 
3910Sstevel@tonic-gate 	  case CMDMSTAT:	/* daemon status, extended, tagged format */
3920Sstevel@tonic-gate 		proc_list_probe();
3930Sstevel@tonic-gate 		(void) sm_io_fprintf(s, SM_TIME_DEFAULT,
3940Sstevel@tonic-gate 				     "C:%d\r\nM:%d\r\nL:%d\r\n",
3950Sstevel@tonic-gate 				     CurChildren, MaxChildren,
3960Sstevel@tonic-gate 				     getla());
3970Sstevel@tonic-gate 		printnqe(s, "Q:");
3980Sstevel@tonic-gate 		disk_status(s, "D:");
3990Sstevel@tonic-gate 		proc_list_display(s, "P:");
4000Sstevel@tonic-gate 		break;
4010Sstevel@tonic-gate 
4020Sstevel@tonic-gate 	  case CMDMEMDUMP:	/* daemon memory dump, to find memory leaks */
4030Sstevel@tonic-gate # if SM_HEAP_CHECK
4040Sstevel@tonic-gate 		/* dump the heap, if we are checking for memory leaks */
4050Sstevel@tonic-gate 		if (sm_debug_active(&SmHeapCheck, 2))
4060Sstevel@tonic-gate 		{
4070Sstevel@tonic-gate 			sm_heap_report(s, sm_debug_level(&SmHeapCheck) - 1);
4080Sstevel@tonic-gate 		}
4090Sstevel@tonic-gate 		else
4100Sstevel@tonic-gate 		{
4110Sstevel@tonic-gate 			(void) sm_io_fprintf(s, SM_TIME_DEFAULT,
4120Sstevel@tonic-gate 					     "Memory dump unavailable.\r\n");
4130Sstevel@tonic-gate 			(void) sm_io_fprintf(s, SM_TIME_DEFAULT,
4140Sstevel@tonic-gate 					     "To fix, run sendmail with -dsm_check_heap.4\r\n");
4150Sstevel@tonic-gate 		}
4160Sstevel@tonic-gate # else /* SM_HEAP_CHECK */
4170Sstevel@tonic-gate 		(void) sm_io_fprintf(s, SM_TIME_DEFAULT,
4180Sstevel@tonic-gate 				     "Memory dump unavailable.\r\n");
4190Sstevel@tonic-gate 		(void) sm_io_fprintf(s, SM_TIME_DEFAULT,
4200Sstevel@tonic-gate 				     "To fix, rebuild with -DSM_HEAP_CHECK\r\n");
4210Sstevel@tonic-gate # endif /* SM_HEAP_CHECK */
4220Sstevel@tonic-gate 		break;
4230Sstevel@tonic-gate 
4240Sstevel@tonic-gate 	  case CMDERROR:	/* unknown command */
4250Sstevel@tonic-gate 		(void) sm_io_fprintf(s, SM_TIME_DEFAULT,
4260Sstevel@tonic-gate 				     "Bad command (%s)\r\n", cmdbuf);
4270Sstevel@tonic-gate 		break;
4280Sstevel@tonic-gate 	}
4290Sstevel@tonic-gate 	(void) sm_io_close(s, SM_TIME_DEFAULT);
4300Sstevel@tonic-gate 	if (ev != NULL)
4310Sstevel@tonic-gate 		sm_clrevent(ev);
4320Sstevel@tonic-gate 	exit(exitstat);
4330Sstevel@tonic-gate }
434