2 * Copyright (c) 1998-2004 Sendmail, Inc. and its suppliers.
5 * By using this file, you agree to the terms and conditions set
6 * forth in the LICENSE file which can be found at the top level of
7 * the sendmail distribution.
13 SM_RCSID("@(#)$Id: control.c,v 8.126 2004/08/04 20:54:00 ca Exp $")
17 /* values for cmd_code */
18 #define CMDERROR 0 /* bad command */
19 #define CMDRESTART 1 /* restart daemon */
20 #define CMDSHUTDOWN 2 /* end daemon */
21 #define CMDHELP 3 /* help */
22 #define CMDSTATUS 4 /* daemon status */
23 #define CMDMEMDUMP 5 /* dump memory, to find memory leaks */
24 #if _FFR_CONTROL_MSTAT
25 # define CMDMSTAT 6 /* daemon status, more info, tagged data */
26 #endif /* _FFR_CONTROL_MSTAT */
30 char *cmd_name; /* command name */
31 int cmd_code; /* internal code, see below */
34 static struct cmd CmdTab[] =
37 { "restart", CMDRESTART },
38 { "shutdown", CMDSHUTDOWN },
39 { "status", CMDSTATUS },
40 { "memdump", CMDMEMDUMP },
41 #if _FFR_CONTROL_MSTAT
42 { "mstat", CMDMSTAT },
43 #endif /* _FFR_CONTROL_MSTAT */
49 static void controltimeout __P((int));
50 int ControlSocket = -1;
53 ** OPENCONTROLSOCKET -- create/open the daemon control named socket
55 ** Creates and opens a named socket for external control over
56 ** the sendmail daemon.
62 ** 0 if successful, -1 otherwise
71 long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN;
72 struct sockaddr_un controladdr;
74 if (ControlSocketName == NULL || *ControlSocketName == '\0')
77 if (strlen(ControlSocketName) >= sizeof controladdr.sun_path)
83 rval = safefile(ControlSocketName, RunAsUid, RunAsGid, RunAsUserName,
84 sff, S_IRUSR|S_IWUSR, NULL);
86 /* if not safe, don't create */
93 ControlSocket = socket(AF_UNIX, SOCK_STREAM, 0);
94 if (ControlSocket < 0)
96 if (SM_FD_SETSIZE > 0 && ControlSocket >= SM_FD_SETSIZE)
103 (void) unlink(ControlSocketName);
104 memset(&controladdr, '\0', sizeof controladdr);
105 controladdr.sun_family = AF_UNIX;
106 (void) sm_strlcpy(controladdr.sun_path, ControlSocketName,
107 sizeof controladdr.sun_path);
109 if (bind(ControlSocket, (struct sockaddr *) &controladdr,
110 sizeof controladdr) < 0)
124 else if (TrustedUid != 0)
128 chown(ControlSocketName, u, -1) < 0)
131 sm_syslog(LOG_ALERT, NOQID,
132 "ownership change on %s to uid %d failed: %s",
133 ControlSocketName, (int) u,
134 sm_errstring(save_errno));
135 message("050 ownership change on %s to uid %d failed: %s",
136 ControlSocketName, (int) u,
137 sm_errstring(save_errno));
138 closecontrolsocket(true);
144 if (chmod(ControlSocketName, S_IRUSR|S_IWUSR) < 0)
147 closecontrolsocket(true);
152 if (listen(ControlSocket, 8) < 0)
155 closecontrolsocket(true);
159 # endif /* NETUNIX */
163 ** CLOSECONTROLSOCKET -- close the daemon control named socket
165 ** Close a named socket.
168 ** fullclose -- if set, close the socket and remove it;
169 ** otherwise, just remove it
176 closecontrolsocket(fullclose)
180 long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN;
182 if (ControlSocket >= 0)
188 (void) close(ControlSocket);
192 rval = safefile(ControlSocketName, RunAsUid, RunAsGid,
193 RunAsUserName, sff, S_IRUSR|S_IWUSR, NULL);
195 /* if not safe, don't unlink */
199 if (unlink(ControlSocketName) < 0)
201 sm_syslog(LOG_WARNING, NOQID,
202 "Could not remove control socket: %s",
203 sm_errstring(errno));
207 # endif /* NETUNIX */
211 ** CLRCONTROL -- reset the control connection
220 ** releases any resources used by the control interface.
227 if (ControlSocket >= 0)
228 (void) close(ControlSocket);
230 # endif /* NETUNIX */
233 ** CONTROL_COMMAND -- read and process command from named socket
235 ** Read and process the command from the opened socket.
236 ** Exits when done since it is running in a forked child.
239 ** sock -- the opened socket from getrequests()
240 ** e -- the current envelope
246 static jmp_buf CtxControlTimeout;
250 controltimeout(timeout)
254 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
255 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
260 longjmp(CtxControlTimeout, 1);
264 control_command(sock, e)
268 volatile int exitstat = EX_OK;
276 char cmdbuf[MAXLINE];
279 sm_setproctitle(false, e, "control cmd read");
281 if (TimeOuts.to_control > 0)
283 /* handle possible input timeout */
284 if (setjmp(CtxControlTimeout) != 0)
287 sm_syslog(LOG_NOTICE, e->e_id,
288 "timeout waiting for input during control command");
291 ev = sm_setevent(TimeOuts.to_control, controltimeout,
292 TimeOuts.to_control);
295 s = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, (void *) &sock,
299 int save_errno = errno;
305 (void) sm_io_setvbuf(s, SM_TIME_DEFAULT, NULL,
306 SM_IO_NBF, SM_IO_BUFSIZ);
308 if (sm_io_fgets(s, SM_TIME_DEFAULT, inp, sizeof inp) == NULL)
310 (void) sm_io_close(s, SM_TIME_DEFAULT);
313 (void) sm_io_flush(s, SM_TIME_DEFAULT);
315 /* clean up end of line */
318 sm_setproctitle(false, e, "control: %s", inp);
320 /* break off command */
321 for (p = inp; isascii(*p) && isspace(*p); p++)
325 !(isascii(*p) && isspace(*p)) &&
326 cmd < &cmdbuf[sizeof cmdbuf - 2])
330 /* throw away leading whitespace */
331 while (isascii(*p) && isspace(*p))
335 for (c = CmdTab; c->cmd_name != NULL; c++)
337 if (sm_strcasecmp(c->cmd_name, cmdbuf) == 0)
343 case CMDHELP: /* get help */
344 traffic = TrafficLogFile;
345 TrafficLogFile = NULL;
349 TrafficLogFile = traffic;
353 case CMDRESTART: /* restart the daemon */
354 (void) sm_io_fprintf(s, SM_TIME_DEFAULT, "OK\r\n");
355 exitstat = EX_RESTART;
358 case CMDSHUTDOWN: /* kill the daemon */
359 (void) sm_io_fprintf(s, SM_TIME_DEFAULT, "OK\r\n");
360 exitstat = EX_SHUTDOWN;
363 case CMDSTATUS: /* daemon status */
370 /* XXX need to deal with different partitions */
372 if (!ISVALIDQGRP(qgrp))
374 free = freediskspace(Queue[qgrp]->qg_qdir, &bsize);
377 ** Prevent overflow and don't lose
378 ** precision (if bsize == 512)
382 free = (long)((double) free *
383 ((double) bsize / 1024));
385 (void) sm_io_fprintf(s, SM_TIME_DEFAULT,
387 CurChildren, MaxChildren,
390 proc_list_display(s, "");
393 # if _FFR_CONTROL_MSTAT
394 case CMDMSTAT: /* daemon status, extended, tagged format */
396 (void) sm_io_fprintf(s, SM_TIME_DEFAULT,
397 "C:%d\r\nM:%d\r\nL:%d\r\n",
398 CurChildren, MaxChildren,
401 disk_status(s, "D:");
402 proc_list_display(s, "P:");
404 # endif /* _FFR_CONTROL_MSTAT */
406 case CMDMEMDUMP: /* daemon memory dump, to find memory leaks */
408 /* dump the heap, if we are checking for memory leaks */
409 if (sm_debug_active(&SmHeapCheck, 2))
411 sm_heap_report(s, sm_debug_level(&SmHeapCheck) - 1);
415 (void) sm_io_fprintf(s, SM_TIME_DEFAULT,
416 "Memory dump unavailable.\r\n");
417 (void) sm_io_fprintf(s, SM_TIME_DEFAULT,
418 "To fix, run sendmail with -dsm_check_heap.4\r\n");
420 # else /* SM_HEAP_CHECK */
421 (void) sm_io_fprintf(s, SM_TIME_DEFAULT,
422 "Memory dump unavailable.\r\n");
423 (void) sm_io_fprintf(s, SM_TIME_DEFAULT,
424 "To fix, rebuild with -DSM_HEAP_CHECK\r\n");
425 # endif /* SM_HEAP_CHECK */
428 case CMDERROR: /* unknown command */
429 (void) sm_io_fprintf(s, SM_TIME_DEFAULT,
430 "Bad command (%s)\r\n", cmdbuf);
433 (void) sm_io_close(s, SM_TIME_DEFAULT);