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