2 * Copyright (c) 1998-2004, 2006 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.128 2006/08/15 23:24:56 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 #define CMDMSTAT 6 /* daemon status, more info, tagged data */
28 char *cmd_name
; /* command name */
29 int cmd_code
; /* internal code, see below */
32 static struct cmd CmdTab
[] =
35 { "restart", CMDRESTART
},
36 { "shutdown", CMDSHUTDOWN
},
37 { "status", CMDSTATUS
},
38 { "memdump", CMDMEMDUMP
},
39 { "mstat", CMDMSTAT
},
45 static void controltimeout
__P((int));
46 int ControlSocket
= -1;
49 ** OPENCONTROLSOCKET -- create/open the daemon control named socket
51 ** Creates and opens a named socket for external control over
52 ** the sendmail daemon.
58 ** 0 if successful, -1 otherwise
67 long sff
= SFF_SAFEDIRPATH
|SFF_OPENASROOT
|SFF_NOLINK
|SFF_CREAT
|SFF_MUSTOWN
;
68 struct sockaddr_un controladdr
;
70 if (ControlSocketName
== NULL
|| *ControlSocketName
== '\0')
73 if (strlen(ControlSocketName
) >= sizeof(controladdr
.sun_path
))
79 rval
= safefile(ControlSocketName
, RunAsUid
, RunAsGid
, RunAsUserName
,
80 sff
, S_IRUSR
|S_IWUSR
, NULL
);
82 /* if not safe, don't create */
89 ControlSocket
= socket(AF_UNIX
, SOCK_STREAM
, 0);
90 if (ControlSocket
< 0)
92 if (SM_FD_SETSIZE
> 0 && ControlSocket
>= SM_FD_SETSIZE
)
99 (void) unlink(ControlSocketName
);
100 memset(&controladdr
, '\0', sizeof(controladdr
));
101 controladdr
.sun_family
= AF_UNIX
;
102 (void) sm_strlcpy(controladdr
.sun_path
, ControlSocketName
,
103 sizeof(controladdr
.sun_path
));
105 if (bind(ControlSocket
, (struct sockaddr
*) &controladdr
,
106 sizeof(controladdr
)) < 0)
120 else if (TrustedUid
!= 0)
124 chown(ControlSocketName
, u
, -1) < 0)
127 sm_syslog(LOG_ALERT
, NOQID
,
128 "ownership change on %s to uid %d failed: %s",
129 ControlSocketName
, (int) u
,
130 sm_errstring(save_errno
));
131 message("050 ownership change on %s to uid %d failed: %s",
132 ControlSocketName
, (int) u
,
133 sm_errstring(save_errno
));
134 closecontrolsocket(true);
140 if (chmod(ControlSocketName
, S_IRUSR
|S_IWUSR
) < 0)
143 closecontrolsocket(true);
148 if (listen(ControlSocket
, 8) < 0)
151 closecontrolsocket(true);
155 # endif /* NETUNIX */
159 ** CLOSECONTROLSOCKET -- close the daemon control named socket
161 ** Close a named socket.
164 ** fullclose -- if set, close the socket and remove it;
165 ** otherwise, just remove it
172 closecontrolsocket(fullclose
)
176 long sff
= SFF_SAFEDIRPATH
|SFF_OPENASROOT
|SFF_NOLINK
|SFF_CREAT
|SFF_MUSTOWN
;
178 if (ControlSocket
>= 0)
184 (void) close(ControlSocket
);
188 rval
= safefile(ControlSocketName
, RunAsUid
, RunAsGid
,
189 RunAsUserName
, sff
, S_IRUSR
|S_IWUSR
, NULL
);
191 /* if not safe, don't unlink */
195 if (unlink(ControlSocketName
) < 0)
197 sm_syslog(LOG_WARNING
, NOQID
,
198 "Could not remove control socket: %s",
199 sm_errstring(errno
));
203 # endif /* NETUNIX */
207 ** CLRCONTROL -- reset the control connection
216 ** releases any resources used by the control interface.
223 if (ControlSocket
>= 0)
224 (void) close(ControlSocket
);
226 # endif /* NETUNIX */
229 ** CONTROL_COMMAND -- read and process command from named socket
231 ** Read and process the command from the opened socket.
232 ** Exits when done since it is running in a forked child.
235 ** sock -- the opened socket from getrequests()
236 ** e -- the current envelope
242 static jmp_buf CtxControlTimeout
;
246 controltimeout(timeout
)
250 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
251 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
256 longjmp(CtxControlTimeout
, 1);
260 control_command(sock
, e
)
264 volatile int exitstat
= EX_OK
;
272 char cmdbuf
[MAXLINE
];
275 sm_setproctitle(false, e
, "control cmd read");
277 if (TimeOuts
.to_control
> 0)
279 /* handle possible input timeout */
280 if (setjmp(CtxControlTimeout
) != 0)
283 sm_syslog(LOG_NOTICE
, e
->e_id
,
284 "timeout waiting for input during control command");
287 ev
= sm_setevent(TimeOuts
.to_control
, controltimeout
,
288 TimeOuts
.to_control
);
291 s
= sm_io_open(SmFtStdiofd
, SM_TIME_DEFAULT
, (void *) &sock
,
295 int save_errno
= errno
;
301 (void) sm_io_setvbuf(s
, SM_TIME_DEFAULT
, NULL
,
302 SM_IO_NBF
, SM_IO_BUFSIZ
);
304 if (sm_io_fgets(s
, SM_TIME_DEFAULT
, inp
, sizeof(inp
)) == NULL
)
306 (void) sm_io_close(s
, SM_TIME_DEFAULT
);
309 (void) sm_io_flush(s
, SM_TIME_DEFAULT
);
311 /* clean up end of line */
314 sm_setproctitle(false, e
, "control: %s", inp
);
316 /* break off command */
317 for (p
= inp
; isascii(*p
) && isspace(*p
); p
++)
321 !(isascii(*p
) && isspace(*p
)) &&
322 cmd
< &cmdbuf
[sizeof(cmdbuf
) - 2])
326 /* throw away leading whitespace */
327 while (isascii(*p
) && isspace(*p
))
331 for (c
= CmdTab
; c
->cmd_name
!= NULL
; c
++)
333 if (sm_strcasecmp(c
->cmd_name
, cmdbuf
) == 0)
339 case CMDHELP
: /* get help */
340 traffic
= TrafficLogFile
;
341 TrafficLogFile
= NULL
;
345 TrafficLogFile
= traffic
;
349 case CMDRESTART
: /* restart the daemon */
350 (void) sm_io_fprintf(s
, SM_TIME_DEFAULT
, "OK\r\n");
351 exitstat
= EX_RESTART
;
354 case CMDSHUTDOWN
: /* kill the daemon */
355 (void) sm_io_fprintf(s
, SM_TIME_DEFAULT
, "OK\r\n");
356 exitstat
= EX_SHUTDOWN
;
359 case CMDSTATUS
: /* daemon status */
366 /* XXX need to deal with different partitions */
368 if (!ISVALIDQGRP(qgrp
))
370 free
= freediskspace(Queue
[qgrp
]->qg_qdir
, &bsize
);
373 ** Prevent overflow and don't lose
374 ** precision (if bsize == 512)
378 free
= (long)((double) free
*
379 ((double) bsize
/ 1024));
381 (void) sm_io_fprintf(s
, SM_TIME_DEFAULT
,
383 CurChildren
, MaxChildren
,
386 proc_list_display(s
, "");
389 case CMDMSTAT
: /* daemon status, extended, tagged format */
391 (void) sm_io_fprintf(s
, SM_TIME_DEFAULT
,
392 "C:%d\r\nM:%d\r\nL:%d\r\n",
393 CurChildren
, MaxChildren
,
396 disk_status(s
, "D:");
397 proc_list_display(s
, "P:");
400 case CMDMEMDUMP
: /* daemon memory dump, to find memory leaks */
402 /* dump the heap, if we are checking for memory leaks */
403 if (sm_debug_active(&SmHeapCheck
, 2))
405 sm_heap_report(s
, sm_debug_level(&SmHeapCheck
) - 1);
409 (void) sm_io_fprintf(s
, SM_TIME_DEFAULT
,
410 "Memory dump unavailable.\r\n");
411 (void) sm_io_fprintf(s
, SM_TIME_DEFAULT
,
412 "To fix, run sendmail with -dsm_check_heap.4\r\n");
414 # else /* SM_HEAP_CHECK */
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, rebuild with -DSM_HEAP_CHECK\r\n");
419 # endif /* SM_HEAP_CHECK */
422 case CMDERROR
: /* unknown command */
423 (void) sm_io_fprintf(s
, SM_TIME_DEFAULT
,
424 "Bad command (%s)\r\n", cmdbuf
);
427 (void) sm_io_close(s
, SM_TIME_DEFAULT
);