xref: /illumos-gate/usr/src/cmd/sendmail/src/control.c (revision 058561cbaa119a6f2659bc27ef343e1b47266bb2)
17c478bd9Sstevel@tonic-gate /*
2*058561cbSjbeck  * Copyright (c) 1998-2004, 2006 Sendmail, Inc. and its suppliers.
37c478bd9Sstevel@tonic-gate  *	All rights reserved.
47c478bd9Sstevel@tonic-gate  *
57c478bd9Sstevel@tonic-gate  * By using this file, you agree to the terms and conditions set
67c478bd9Sstevel@tonic-gate  * forth in the LICENSE file which can be found at the top level of
77c478bd9Sstevel@tonic-gate  * the sendmail distribution.
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  */
107c478bd9Sstevel@tonic-gate 
117c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
127c478bd9Sstevel@tonic-gate 
137c478bd9Sstevel@tonic-gate #include <sendmail.h>
147c478bd9Sstevel@tonic-gate 
15*058561cbSjbeck SM_RCSID("@(#)$Id: control.c,v 8.128 2006/08/15 23:24:56 ca Exp $")
167c478bd9Sstevel@tonic-gate 
177c478bd9Sstevel@tonic-gate #include <sm/fdset.h>
187c478bd9Sstevel@tonic-gate 
197c478bd9Sstevel@tonic-gate /* values for cmd_code */
207c478bd9Sstevel@tonic-gate #define CMDERROR	0	/* bad command */
217c478bd9Sstevel@tonic-gate #define CMDRESTART	1	/* restart daemon */
227c478bd9Sstevel@tonic-gate #define CMDSHUTDOWN	2	/* end daemon */
237c478bd9Sstevel@tonic-gate #define CMDHELP		3	/* help */
247c478bd9Sstevel@tonic-gate #define CMDSTATUS	4	/* daemon status */
257c478bd9Sstevel@tonic-gate #define CMDMEMDUMP	5	/* dump memory, to find memory leaks */
267c478bd9Sstevel@tonic-gate #define CMDMSTAT	6	/* daemon status, more info, tagged data */
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate struct cmd
297c478bd9Sstevel@tonic-gate {
307c478bd9Sstevel@tonic-gate 	char	*cmd_name;	/* command name */
317c478bd9Sstevel@tonic-gate 	int	cmd_code;	/* internal code, see below */
327c478bd9Sstevel@tonic-gate };
337c478bd9Sstevel@tonic-gate 
347c478bd9Sstevel@tonic-gate static struct cmd	CmdTab[] =
357c478bd9Sstevel@tonic-gate {
367c478bd9Sstevel@tonic-gate 	{ "help",	CMDHELP		},
377c478bd9Sstevel@tonic-gate 	{ "restart",	CMDRESTART	},
387c478bd9Sstevel@tonic-gate 	{ "shutdown",	CMDSHUTDOWN	},
397c478bd9Sstevel@tonic-gate 	{ "status",	CMDSTATUS	},
407c478bd9Sstevel@tonic-gate 	{ "memdump",	CMDMEMDUMP	},
417c478bd9Sstevel@tonic-gate 	{ "mstat",	CMDMSTAT	},
427c478bd9Sstevel@tonic-gate 	{ NULL,		CMDERROR	}
437c478bd9Sstevel@tonic-gate };
447c478bd9Sstevel@tonic-gate 
457c478bd9Sstevel@tonic-gate 
467c478bd9Sstevel@tonic-gate 
477c478bd9Sstevel@tonic-gate static void	controltimeout __P((int));
487c478bd9Sstevel@tonic-gate int ControlSocket = -1;
497c478bd9Sstevel@tonic-gate 
507c478bd9Sstevel@tonic-gate /*
517c478bd9Sstevel@tonic-gate **  OPENCONTROLSOCKET -- create/open the daemon control named socket
527c478bd9Sstevel@tonic-gate **
537c478bd9Sstevel@tonic-gate **	Creates and opens a named socket for external control over
547c478bd9Sstevel@tonic-gate **	the sendmail daemon.
557c478bd9Sstevel@tonic-gate **
567c478bd9Sstevel@tonic-gate **	Parameters:
577c478bd9Sstevel@tonic-gate **		none.
587c478bd9Sstevel@tonic-gate **
597c478bd9Sstevel@tonic-gate **	Returns:
607c478bd9Sstevel@tonic-gate **		0 if successful, -1 otherwise
617c478bd9Sstevel@tonic-gate */
627c478bd9Sstevel@tonic-gate 
637c478bd9Sstevel@tonic-gate int
647c478bd9Sstevel@tonic-gate opencontrolsocket()
657c478bd9Sstevel@tonic-gate {
667c478bd9Sstevel@tonic-gate # if NETUNIX
677c478bd9Sstevel@tonic-gate 	int save_errno;
687c478bd9Sstevel@tonic-gate 	int rval;
697c478bd9Sstevel@tonic-gate 	long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN;
707c478bd9Sstevel@tonic-gate 	struct sockaddr_un controladdr;
717c478bd9Sstevel@tonic-gate 
727c478bd9Sstevel@tonic-gate 	if (ControlSocketName == NULL || *ControlSocketName == '\0')
737c478bd9Sstevel@tonic-gate 		return 0;
747c478bd9Sstevel@tonic-gate 
75*058561cbSjbeck 	if (strlen(ControlSocketName) >= sizeof(controladdr.sun_path))
767c478bd9Sstevel@tonic-gate 	{
777c478bd9Sstevel@tonic-gate 		errno = ENAMETOOLONG;
787c478bd9Sstevel@tonic-gate 		return -1;
797c478bd9Sstevel@tonic-gate 	}
807c478bd9Sstevel@tonic-gate 
817c478bd9Sstevel@tonic-gate 	rval = safefile(ControlSocketName, RunAsUid, RunAsGid, RunAsUserName,
827c478bd9Sstevel@tonic-gate 			sff, S_IRUSR|S_IWUSR, NULL);
837c478bd9Sstevel@tonic-gate 
847c478bd9Sstevel@tonic-gate 	/* if not safe, don't create */
857c478bd9Sstevel@tonic-gate 	if (rval != 0)
867c478bd9Sstevel@tonic-gate 	{
877c478bd9Sstevel@tonic-gate 		errno = rval;
887c478bd9Sstevel@tonic-gate 		return -1;
897c478bd9Sstevel@tonic-gate 	}
907c478bd9Sstevel@tonic-gate 
917c478bd9Sstevel@tonic-gate 	ControlSocket = socket(AF_UNIX, SOCK_STREAM, 0);
927c478bd9Sstevel@tonic-gate 	if (ControlSocket < 0)
937c478bd9Sstevel@tonic-gate 		return -1;
947c478bd9Sstevel@tonic-gate 	if (SM_FD_SETSIZE > 0 && ControlSocket >= SM_FD_SETSIZE)
957c478bd9Sstevel@tonic-gate 	{
967c478bd9Sstevel@tonic-gate 		clrcontrol();
977c478bd9Sstevel@tonic-gate 		errno = EINVAL;
987c478bd9Sstevel@tonic-gate 		return -1;
997c478bd9Sstevel@tonic-gate 	}
1007c478bd9Sstevel@tonic-gate 
1017c478bd9Sstevel@tonic-gate 	(void) unlink(ControlSocketName);
102*058561cbSjbeck 	memset(&controladdr, '\0', sizeof(controladdr));
1037c478bd9Sstevel@tonic-gate 	controladdr.sun_family = AF_UNIX;
1047c478bd9Sstevel@tonic-gate 	(void) sm_strlcpy(controladdr.sun_path, ControlSocketName,
105*058561cbSjbeck 			  sizeof(controladdr.sun_path));
1067c478bd9Sstevel@tonic-gate 
1077c478bd9Sstevel@tonic-gate 	if (bind(ControlSocket, (struct sockaddr *) &controladdr,
108*058561cbSjbeck 		 sizeof(controladdr)) < 0)
1097c478bd9Sstevel@tonic-gate 	{
1107c478bd9Sstevel@tonic-gate 		save_errno = errno;
1117c478bd9Sstevel@tonic-gate 		clrcontrol();
1127c478bd9Sstevel@tonic-gate 		errno = save_errno;
1137c478bd9Sstevel@tonic-gate 		return -1;
1147c478bd9Sstevel@tonic-gate 	}
1157c478bd9Sstevel@tonic-gate 
1167c478bd9Sstevel@tonic-gate 	if (geteuid() == 0)
1177c478bd9Sstevel@tonic-gate 	{
1187c478bd9Sstevel@tonic-gate 		uid_t u = 0;
1197c478bd9Sstevel@tonic-gate 
1207c478bd9Sstevel@tonic-gate 		if (RunAsUid != 0)
1217c478bd9Sstevel@tonic-gate 			u = RunAsUid;
1227c478bd9Sstevel@tonic-gate 		else if (TrustedUid != 0)
1237c478bd9Sstevel@tonic-gate 			u = TrustedUid;
1247c478bd9Sstevel@tonic-gate 
1257c478bd9Sstevel@tonic-gate 		if (u != 0 &&
1267c478bd9Sstevel@tonic-gate 		    chown(ControlSocketName, u, -1) < 0)
1277c478bd9Sstevel@tonic-gate 		{
1287c478bd9Sstevel@tonic-gate 			save_errno = errno;
1297c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ALERT, NOQID,
1307c478bd9Sstevel@tonic-gate 				  "ownership change on %s to uid %d failed: %s",
1317c478bd9Sstevel@tonic-gate 				  ControlSocketName, (int) u,
1327c478bd9Sstevel@tonic-gate 				  sm_errstring(save_errno));
1337c478bd9Sstevel@tonic-gate 			message("050 ownership change on %s to uid %d failed: %s",
1347c478bd9Sstevel@tonic-gate 				ControlSocketName, (int) u,
1357c478bd9Sstevel@tonic-gate 				sm_errstring(save_errno));
1367c478bd9Sstevel@tonic-gate 			closecontrolsocket(true);
1377c478bd9Sstevel@tonic-gate 			errno = save_errno;
1387c478bd9Sstevel@tonic-gate 			return -1;
1397c478bd9Sstevel@tonic-gate 		}
1407c478bd9Sstevel@tonic-gate 	}
1417c478bd9Sstevel@tonic-gate 
1427c478bd9Sstevel@tonic-gate 	if (chmod(ControlSocketName, S_IRUSR|S_IWUSR) < 0)
1437c478bd9Sstevel@tonic-gate 	{
1447c478bd9Sstevel@tonic-gate 		save_errno = errno;
1457c478bd9Sstevel@tonic-gate 		closecontrolsocket(true);
1467c478bd9Sstevel@tonic-gate 		errno = save_errno;
1477c478bd9Sstevel@tonic-gate 		return -1;
1487c478bd9Sstevel@tonic-gate 	}
1497c478bd9Sstevel@tonic-gate 
1507c478bd9Sstevel@tonic-gate 	if (listen(ControlSocket, 8) < 0)
1517c478bd9Sstevel@tonic-gate 	{
1527c478bd9Sstevel@tonic-gate 		save_errno = errno;
1537c478bd9Sstevel@tonic-gate 		closecontrolsocket(true);
1547c478bd9Sstevel@tonic-gate 		errno = save_errno;
1557c478bd9Sstevel@tonic-gate 		return -1;
1567c478bd9Sstevel@tonic-gate 	}
1577c478bd9Sstevel@tonic-gate # endif /* NETUNIX */
1587c478bd9Sstevel@tonic-gate 	return 0;
1597c478bd9Sstevel@tonic-gate }
1607c478bd9Sstevel@tonic-gate /*
1617c478bd9Sstevel@tonic-gate **  CLOSECONTROLSOCKET -- close the daemon control named socket
1627c478bd9Sstevel@tonic-gate **
1637c478bd9Sstevel@tonic-gate **	Close a named socket.
1647c478bd9Sstevel@tonic-gate **
1657c478bd9Sstevel@tonic-gate **	Parameters:
1667c478bd9Sstevel@tonic-gate **		fullclose -- if set, close the socket and remove it;
1677c478bd9Sstevel@tonic-gate **			     otherwise, just remove it
1687c478bd9Sstevel@tonic-gate **
1697c478bd9Sstevel@tonic-gate **	Returns:
1707c478bd9Sstevel@tonic-gate **		none.
1717c478bd9Sstevel@tonic-gate */
1727c478bd9Sstevel@tonic-gate 
1737c478bd9Sstevel@tonic-gate void
1747c478bd9Sstevel@tonic-gate closecontrolsocket(fullclose)
1757c478bd9Sstevel@tonic-gate 	bool fullclose;
1767c478bd9Sstevel@tonic-gate {
1777c478bd9Sstevel@tonic-gate # if NETUNIX
1787c478bd9Sstevel@tonic-gate 	long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN;
1797c478bd9Sstevel@tonic-gate 
1807c478bd9Sstevel@tonic-gate 	if (ControlSocket >= 0)
1817c478bd9Sstevel@tonic-gate 	{
1827c478bd9Sstevel@tonic-gate 		int rval;
1837c478bd9Sstevel@tonic-gate 
1847c478bd9Sstevel@tonic-gate 		if (fullclose)
1857c478bd9Sstevel@tonic-gate 		{
1867c478bd9Sstevel@tonic-gate 			(void) close(ControlSocket);
1877c478bd9Sstevel@tonic-gate 			ControlSocket = -1;
1887c478bd9Sstevel@tonic-gate 		}
1897c478bd9Sstevel@tonic-gate 
1907c478bd9Sstevel@tonic-gate 		rval = safefile(ControlSocketName, RunAsUid, RunAsGid,
1917c478bd9Sstevel@tonic-gate 				RunAsUserName, sff, S_IRUSR|S_IWUSR, NULL);
1927c478bd9Sstevel@tonic-gate 
1937c478bd9Sstevel@tonic-gate 		/* if not safe, don't unlink */
1947c478bd9Sstevel@tonic-gate 		if (rval != 0)
1957c478bd9Sstevel@tonic-gate 			return;
1967c478bd9Sstevel@tonic-gate 
1977c478bd9Sstevel@tonic-gate 		if (unlink(ControlSocketName) < 0)
1987c478bd9Sstevel@tonic-gate 		{
1997c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_WARNING, NOQID,
2007c478bd9Sstevel@tonic-gate 				  "Could not remove control socket: %s",
2017c478bd9Sstevel@tonic-gate 				  sm_errstring(errno));
2027c478bd9Sstevel@tonic-gate 			return;
2037c478bd9Sstevel@tonic-gate 		}
2047c478bd9Sstevel@tonic-gate 	}
2057c478bd9Sstevel@tonic-gate # endif /* NETUNIX */
2067c478bd9Sstevel@tonic-gate 	return;
2077c478bd9Sstevel@tonic-gate }
2087c478bd9Sstevel@tonic-gate /*
2097c478bd9Sstevel@tonic-gate **  CLRCONTROL -- reset the control connection
2107c478bd9Sstevel@tonic-gate **
2117c478bd9Sstevel@tonic-gate **	Parameters:
2127c478bd9Sstevel@tonic-gate **		none.
2137c478bd9Sstevel@tonic-gate **
2147c478bd9Sstevel@tonic-gate **	Returns:
2157c478bd9Sstevel@tonic-gate **		none.
2167c478bd9Sstevel@tonic-gate **
2177c478bd9Sstevel@tonic-gate **	Side Effects:
2187c478bd9Sstevel@tonic-gate **		releases any resources used by the control interface.
2197c478bd9Sstevel@tonic-gate */
2207c478bd9Sstevel@tonic-gate 
2217c478bd9Sstevel@tonic-gate void
2227c478bd9Sstevel@tonic-gate clrcontrol()
2237c478bd9Sstevel@tonic-gate {
2247c478bd9Sstevel@tonic-gate # if NETUNIX
2257c478bd9Sstevel@tonic-gate 	if (ControlSocket >= 0)
2267c478bd9Sstevel@tonic-gate 		(void) close(ControlSocket);
2277c478bd9Sstevel@tonic-gate 	ControlSocket = -1;
2287c478bd9Sstevel@tonic-gate # endif /* NETUNIX */
2297c478bd9Sstevel@tonic-gate }
2307c478bd9Sstevel@tonic-gate /*
2317c478bd9Sstevel@tonic-gate **  CONTROL_COMMAND -- read and process command from named socket
2327c478bd9Sstevel@tonic-gate **
2337c478bd9Sstevel@tonic-gate **	Read and process the command from the opened socket.
2347c478bd9Sstevel@tonic-gate **	Exits when done since it is running in a forked child.
2357c478bd9Sstevel@tonic-gate **
2367c478bd9Sstevel@tonic-gate **	Parameters:
2377c478bd9Sstevel@tonic-gate **		sock -- the opened socket from getrequests()
2387c478bd9Sstevel@tonic-gate **		e -- the current envelope
2397c478bd9Sstevel@tonic-gate **
2407c478bd9Sstevel@tonic-gate **	Returns:
2417c478bd9Sstevel@tonic-gate **		none.
2427c478bd9Sstevel@tonic-gate */
2437c478bd9Sstevel@tonic-gate 
2447c478bd9Sstevel@tonic-gate static jmp_buf	CtxControlTimeout;
2457c478bd9Sstevel@tonic-gate 
2467c478bd9Sstevel@tonic-gate /* ARGSUSED0 */
2477c478bd9Sstevel@tonic-gate static void
2487c478bd9Sstevel@tonic-gate controltimeout(timeout)
2497c478bd9Sstevel@tonic-gate 	int timeout;
2507c478bd9Sstevel@tonic-gate {
2517c478bd9Sstevel@tonic-gate 	/*
2527c478bd9Sstevel@tonic-gate 	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
2537c478bd9Sstevel@tonic-gate 	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
2547c478bd9Sstevel@tonic-gate 	**	DOING.
2557c478bd9Sstevel@tonic-gate 	*/
2567c478bd9Sstevel@tonic-gate 
2577c478bd9Sstevel@tonic-gate 	errno = ETIMEDOUT;
2587c478bd9Sstevel@tonic-gate 	longjmp(CtxControlTimeout, 1);
2597c478bd9Sstevel@tonic-gate }
2607c478bd9Sstevel@tonic-gate 
2617c478bd9Sstevel@tonic-gate void
2627c478bd9Sstevel@tonic-gate control_command(sock, e)
2637c478bd9Sstevel@tonic-gate 	int sock;
2647c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
2657c478bd9Sstevel@tonic-gate {
2667c478bd9Sstevel@tonic-gate 	volatile int exitstat = EX_OK;
2677c478bd9Sstevel@tonic-gate 	SM_FILE_T *s = NULL;
2687c478bd9Sstevel@tonic-gate 	SM_EVENT *ev = NULL;
2697c478bd9Sstevel@tonic-gate 	SM_FILE_T *traffic;
2707c478bd9Sstevel@tonic-gate 	SM_FILE_T *oldout;
2717c478bd9Sstevel@tonic-gate 	char *cmd;
2727c478bd9Sstevel@tonic-gate 	char *p;
2737c478bd9Sstevel@tonic-gate 	struct cmd *c;
2747c478bd9Sstevel@tonic-gate 	char cmdbuf[MAXLINE];
2757c478bd9Sstevel@tonic-gate 	char inp[MAXLINE];
2767c478bd9Sstevel@tonic-gate 
2777c478bd9Sstevel@tonic-gate 	sm_setproctitle(false, e, "control cmd read");
2787c478bd9Sstevel@tonic-gate 
2797c478bd9Sstevel@tonic-gate 	if (TimeOuts.to_control > 0)
2807c478bd9Sstevel@tonic-gate 	{
2817c478bd9Sstevel@tonic-gate 		/* handle possible input timeout */
2827c478bd9Sstevel@tonic-gate 		if (setjmp(CtxControlTimeout) != 0)
2837c478bd9Sstevel@tonic-gate 		{
2847c478bd9Sstevel@tonic-gate 			if (LogLevel > 2)
2857c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_NOTICE, e->e_id,
2867c478bd9Sstevel@tonic-gate 					  "timeout waiting for input during control command");
2877c478bd9Sstevel@tonic-gate 			exit(EX_IOERR);
2887c478bd9Sstevel@tonic-gate 		}
2897c478bd9Sstevel@tonic-gate 		ev = sm_setevent(TimeOuts.to_control, controltimeout,
2907c478bd9Sstevel@tonic-gate 				 TimeOuts.to_control);
2917c478bd9Sstevel@tonic-gate 	}
2927c478bd9Sstevel@tonic-gate 
2937c478bd9Sstevel@tonic-gate 	s = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, (void *) &sock,
2947c478bd9Sstevel@tonic-gate 		       SM_IO_RDWR, NULL);
2957c478bd9Sstevel@tonic-gate 	if (s == NULL)
2967c478bd9Sstevel@tonic-gate 	{
2977c478bd9Sstevel@tonic-gate 		int save_errno = errno;
2987c478bd9Sstevel@tonic-gate 
2997c478bd9Sstevel@tonic-gate 		(void) close(sock);
3007c478bd9Sstevel@tonic-gate 		errno = save_errno;
3017c478bd9Sstevel@tonic-gate 		exit(EX_IOERR);
3027c478bd9Sstevel@tonic-gate 	}
3037c478bd9Sstevel@tonic-gate 	(void) sm_io_setvbuf(s, SM_TIME_DEFAULT, NULL,
3047c478bd9Sstevel@tonic-gate 			     SM_IO_NBF, SM_IO_BUFSIZ);
3057c478bd9Sstevel@tonic-gate 
306*058561cbSjbeck 	if (sm_io_fgets(s, SM_TIME_DEFAULT, inp, sizeof(inp)) == NULL)
3077c478bd9Sstevel@tonic-gate 	{
3087c478bd9Sstevel@tonic-gate 		(void) sm_io_close(s, SM_TIME_DEFAULT);
3097c478bd9Sstevel@tonic-gate 		exit(EX_IOERR);
3107c478bd9Sstevel@tonic-gate 	}
3117c478bd9Sstevel@tonic-gate 	(void) sm_io_flush(s, SM_TIME_DEFAULT);
3127c478bd9Sstevel@tonic-gate 
3137c478bd9Sstevel@tonic-gate 	/* clean up end of line */
3147c478bd9Sstevel@tonic-gate 	fixcrlf(inp, true);
3157c478bd9Sstevel@tonic-gate 
3167c478bd9Sstevel@tonic-gate 	sm_setproctitle(false, e, "control: %s", inp);
3177c478bd9Sstevel@tonic-gate 
3187c478bd9Sstevel@tonic-gate 	/* break off command */
3197c478bd9Sstevel@tonic-gate 	for (p = inp; isascii(*p) && isspace(*p); p++)
3207c478bd9Sstevel@tonic-gate 		continue;
3217c478bd9Sstevel@tonic-gate 	cmd = cmdbuf;
3227c478bd9Sstevel@tonic-gate 	while (*p != '\0' &&
3237c478bd9Sstevel@tonic-gate 	       !(isascii(*p) && isspace(*p)) &&
324*058561cbSjbeck 	       cmd < &cmdbuf[sizeof(cmdbuf) - 2])
3257c478bd9Sstevel@tonic-gate 		*cmd++ = *p++;
3267c478bd9Sstevel@tonic-gate 	*cmd = '\0';
3277c478bd9Sstevel@tonic-gate 
3287c478bd9Sstevel@tonic-gate 	/* throw away leading whitespace */
3297c478bd9Sstevel@tonic-gate 	while (isascii(*p) && isspace(*p))
3307c478bd9Sstevel@tonic-gate 		p++;
3317c478bd9Sstevel@tonic-gate 
3327c478bd9Sstevel@tonic-gate 	/* decode command */
3337c478bd9Sstevel@tonic-gate 	for (c = CmdTab; c->cmd_name != NULL; c++)
3347c478bd9Sstevel@tonic-gate 	{
3357c478bd9Sstevel@tonic-gate 		if (sm_strcasecmp(c->cmd_name, cmdbuf) == 0)
3367c478bd9Sstevel@tonic-gate 			break;
3377c478bd9Sstevel@tonic-gate 	}
3387c478bd9Sstevel@tonic-gate 
3397c478bd9Sstevel@tonic-gate 	switch (c->cmd_code)
3407c478bd9Sstevel@tonic-gate 	{
3417c478bd9Sstevel@tonic-gate 	  case CMDHELP:		/* get help */
3427c478bd9Sstevel@tonic-gate 		traffic = TrafficLogFile;
3437c478bd9Sstevel@tonic-gate 		TrafficLogFile = NULL;
3447c478bd9Sstevel@tonic-gate 		oldout = OutChannel;
3457c478bd9Sstevel@tonic-gate 		OutChannel = s;
3467c478bd9Sstevel@tonic-gate 		help("control", e);
3477c478bd9Sstevel@tonic-gate 		TrafficLogFile = traffic;
3487c478bd9Sstevel@tonic-gate 		OutChannel = oldout;
3497c478bd9Sstevel@tonic-gate 		break;
3507c478bd9Sstevel@tonic-gate 
3517c478bd9Sstevel@tonic-gate 	  case CMDRESTART:	/* restart the daemon */
3527c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(s, SM_TIME_DEFAULT, "OK\r\n");
3537c478bd9Sstevel@tonic-gate 		exitstat = EX_RESTART;
3547c478bd9Sstevel@tonic-gate 		break;
3557c478bd9Sstevel@tonic-gate 
3567c478bd9Sstevel@tonic-gate 	  case CMDSHUTDOWN:	/* kill the daemon */
3577c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(s, SM_TIME_DEFAULT, "OK\r\n");
3587c478bd9Sstevel@tonic-gate 		exitstat = EX_SHUTDOWN;
3597c478bd9Sstevel@tonic-gate 		break;
3607c478bd9Sstevel@tonic-gate 
3617c478bd9Sstevel@tonic-gate 	  case CMDSTATUS:	/* daemon status */
3627c478bd9Sstevel@tonic-gate 		proc_list_probe();
3637c478bd9Sstevel@tonic-gate 		{
3647c478bd9Sstevel@tonic-gate 			int qgrp;
3657c478bd9Sstevel@tonic-gate 			long bsize;
3667c478bd9Sstevel@tonic-gate 			long free;
3677c478bd9Sstevel@tonic-gate 
3687c478bd9Sstevel@tonic-gate 			/* XXX need to deal with different partitions */
3697c478bd9Sstevel@tonic-gate 			qgrp = e->e_qgrp;
3707c478bd9Sstevel@tonic-gate 			if (!ISVALIDQGRP(qgrp))
3717c478bd9Sstevel@tonic-gate 				qgrp = 0;
3727c478bd9Sstevel@tonic-gate 			free = freediskspace(Queue[qgrp]->qg_qdir, &bsize);
3737c478bd9Sstevel@tonic-gate 
3747c478bd9Sstevel@tonic-gate 			/*
3757c478bd9Sstevel@tonic-gate 			**  Prevent overflow and don't lose
3767c478bd9Sstevel@tonic-gate 			**  precision (if bsize == 512)
3777c478bd9Sstevel@tonic-gate 			*/
3787c478bd9Sstevel@tonic-gate 
3797c478bd9Sstevel@tonic-gate 			if (free > 0)
3807c478bd9Sstevel@tonic-gate 				free = (long)((double) free *
3817c478bd9Sstevel@tonic-gate 					      ((double) bsize / 1024));
3827c478bd9Sstevel@tonic-gate 
3837c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(s, SM_TIME_DEFAULT,
3847c478bd9Sstevel@tonic-gate 					     "%d/%d/%ld/%d\r\n",
3857c478bd9Sstevel@tonic-gate 					     CurChildren, MaxChildren,
3867c478bd9Sstevel@tonic-gate 					     free, getla());
3877c478bd9Sstevel@tonic-gate 		}
3887c478bd9Sstevel@tonic-gate 		proc_list_display(s, "");
3897c478bd9Sstevel@tonic-gate 		break;
3907c478bd9Sstevel@tonic-gate 
3917c478bd9Sstevel@tonic-gate 	  case CMDMSTAT:	/* daemon status, extended, tagged format */
3927c478bd9Sstevel@tonic-gate 		proc_list_probe();
3937c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(s, SM_TIME_DEFAULT,
3947c478bd9Sstevel@tonic-gate 				     "C:%d\r\nM:%d\r\nL:%d\r\n",
3957c478bd9Sstevel@tonic-gate 				     CurChildren, MaxChildren,
3967c478bd9Sstevel@tonic-gate 				     getla());
3977c478bd9Sstevel@tonic-gate 		printnqe(s, "Q:");
3987c478bd9Sstevel@tonic-gate 		disk_status(s, "D:");
3997c478bd9Sstevel@tonic-gate 		proc_list_display(s, "P:");
4007c478bd9Sstevel@tonic-gate 		break;
4017c478bd9Sstevel@tonic-gate 
4027c478bd9Sstevel@tonic-gate 	  case CMDMEMDUMP:	/* daemon memory dump, to find memory leaks */
4037c478bd9Sstevel@tonic-gate # if SM_HEAP_CHECK
4047c478bd9Sstevel@tonic-gate 		/* dump the heap, if we are checking for memory leaks */
4057c478bd9Sstevel@tonic-gate 		if (sm_debug_active(&SmHeapCheck, 2))
4067c478bd9Sstevel@tonic-gate 		{
4077c478bd9Sstevel@tonic-gate 			sm_heap_report(s, sm_debug_level(&SmHeapCheck) - 1);
4087c478bd9Sstevel@tonic-gate 		}
4097c478bd9Sstevel@tonic-gate 		else
4107c478bd9Sstevel@tonic-gate 		{
4117c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(s, SM_TIME_DEFAULT,
4127c478bd9Sstevel@tonic-gate 					     "Memory dump unavailable.\r\n");
4137c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(s, SM_TIME_DEFAULT,
4147c478bd9Sstevel@tonic-gate 					     "To fix, run sendmail with -dsm_check_heap.4\r\n");
4157c478bd9Sstevel@tonic-gate 		}
4167c478bd9Sstevel@tonic-gate # else /* SM_HEAP_CHECK */
4177c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(s, SM_TIME_DEFAULT,
4187c478bd9Sstevel@tonic-gate 				     "Memory dump unavailable.\r\n");
4197c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(s, SM_TIME_DEFAULT,
4207c478bd9Sstevel@tonic-gate 				     "To fix, rebuild with -DSM_HEAP_CHECK\r\n");
4217c478bd9Sstevel@tonic-gate # endif /* SM_HEAP_CHECK */
4227c478bd9Sstevel@tonic-gate 		break;
4237c478bd9Sstevel@tonic-gate 
4247c478bd9Sstevel@tonic-gate 	  case CMDERROR:	/* unknown command */
4257c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(s, SM_TIME_DEFAULT,
4267c478bd9Sstevel@tonic-gate 				     "Bad command (%s)\r\n", cmdbuf);
4277c478bd9Sstevel@tonic-gate 		break;
4287c478bd9Sstevel@tonic-gate 	}
4297c478bd9Sstevel@tonic-gate 	(void) sm_io_close(s, SM_TIME_DEFAULT);
4307c478bd9Sstevel@tonic-gate 	if (ev != NULL)
4317c478bd9Sstevel@tonic-gate 		sm_clrevent(ev);
4327c478bd9Sstevel@tonic-gate 	exit(exitstat);
4337c478bd9Sstevel@tonic-gate }
434