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