1 /*
2 * Copyright (c) 1998-2004, 2006 Proofpoint, Inc. and its suppliers.
3 * All rights reserved.
4 *
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.
8 *
9 */
10
11 #include <sendmail.h>
12
13 SM_RCSID("@(#)$Id: control.c,v 8.130 2013-11-22 20:51:55 ca Exp $")
14
15 #include <sm/sendmail.h>
16 #include <sm/fdset.h>
17
18 /* values for cmd_code */
19 #define CMDERROR 0 /* bad command */
20 #define CMDRESTART 1 /* restart daemon */
21 #define CMDSHUTDOWN 2 /* end daemon */
22 #define CMDHELP 3 /* help */
23 #define CMDSTATUS 4 /* daemon status */
24 #define CMDMEMDUMP 5 /* dump memory, to find memory leaks */
25 #define CMDMSTAT 6 /* daemon status, more info, tagged data */
26
27 struct cmd
28 {
29 char *cmd_name; /* command name */
30 int cmd_code; /* internal code, see below */
31 };
32
33 static struct cmd CmdTab[] =
34 {
35 { "help", CMDHELP },
36 { "restart", CMDRESTART },
37 { "shutdown", CMDSHUTDOWN },
38 { "status", CMDSTATUS },
39 { "memdump", CMDMEMDUMP },
40 { "mstat", CMDMSTAT },
41 { NULL, CMDERROR }
42 };
43
44 static void controltimeout __P((int));
45 int ControlSocket = -1;
46
47 /*
48 ** OPENCONTROLSOCKET -- create/open the daemon control named socket
49 **
50 ** Creates and opens a named socket for external control over
51 ** the sendmail daemon.
52 **
53 ** Parameters:
54 ** none.
55 **
56 ** Returns:
57 ** 0 if successful, -1 otherwise
58 */
59
60 int
opencontrolsocket()61 opencontrolsocket()
62 {
63 #if NETUNIX
64 int save_errno;
65 int rval;
66 long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN;
67 struct sockaddr_un controladdr;
68
69 if (SM_IS_EMPTY(ControlSocketName))
70 return 0;
71
72 if (strlen(ControlSocketName) >= sizeof(controladdr.sun_path))
73 {
74 errno = ENAMETOOLONG;
75 return -1;
76 }
77
78 rval = safefile(ControlSocketName, RunAsUid, RunAsGid, RunAsUserName,
79 sff, S_IRUSR|S_IWUSR, NULL);
80
81 /* if not safe, don't create */
82 if (rval != 0)
83 {
84 errno = rval;
85 return -1;
86 }
87
88 ControlSocket = socket(AF_UNIX, SOCK_STREAM, 0);
89 if (ControlSocket < 0)
90 return -1;
91 if (SM_FD_SETSIZE > 0 && ControlSocket >= SM_FD_SETSIZE)
92 {
93 clrcontrol();
94 errno = EINVAL;
95 return -1;
96 }
97
98 (void) unlink(ControlSocketName);
99 memset(&controladdr, '\0', sizeof(controladdr));
100 controladdr.sun_family = AF_UNIX;
101 (void) sm_strlcpy(controladdr.sun_path, ControlSocketName,
102 sizeof(controladdr.sun_path));
103
104 if (bind(ControlSocket, (struct sockaddr *) &controladdr,
105 sizeof(controladdr)) < 0)
106 {
107 save_errno = errno;
108 clrcontrol();
109 errno = save_errno;
110 return -1;
111 }
112
113 if (geteuid() == 0)
114 {
115 uid_t u = 0;
116
117 if (RunAsUid != 0)
118 u = RunAsUid;
119 else if (TrustedUid != 0)
120 u = TrustedUid;
121
122 if (u != 0 &&
123 chown(ControlSocketName, u, -1) < 0)
124 {
125 save_errno = errno;
126 sm_syslog(LOG_ALERT, NOQID,
127 "ownership change on %s to uid %d failed: %s",
128 ControlSocketName, (int) u,
129 sm_errstring(save_errno));
130 message("050 ownership change on %s to uid %d failed: %s",
131 ControlSocketName, (int) u,
132 sm_errstring(save_errno));
133 closecontrolsocket(true);
134 errno = save_errno;
135 return -1;
136 }
137 }
138
139 if (chmod(ControlSocketName, S_IRUSR|S_IWUSR) < 0)
140 {
141 save_errno = errno;
142 closecontrolsocket(true);
143 errno = save_errno;
144 return -1;
145 }
146
147 if (listen(ControlSocket, 8) < 0)
148 {
149 save_errno = errno;
150 closecontrolsocket(true);
151 errno = save_errno;
152 return -1;
153 }
154 #endif /* NETUNIX */
155 return 0;
156 }
157 /*
158 ** CLOSECONTROLSOCKET -- close the daemon control named socket
159 **
160 ** Close a named socket.
161 **
162 ** Parameters:
163 ** fullclose -- if set, close the socket and remove it;
164 ** otherwise, just remove it
165 **
166 ** Returns:
167 ** none.
168 */
169
170 void
closecontrolsocket(fullclose)171 closecontrolsocket(fullclose)
172 bool fullclose;
173 {
174 #if NETUNIX
175 long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN;
176
177 if (ControlSocket >= 0)
178 {
179 int rval;
180
181 if (fullclose)
182 {
183 (void) close(ControlSocket);
184 ControlSocket = -1;
185 }
186
187 rval = safefile(ControlSocketName, RunAsUid, RunAsGid,
188 RunAsUserName, sff, S_IRUSR|S_IWUSR, NULL);
189
190 /* if not safe, don't unlink */
191 if (rval != 0)
192 return;
193
194 if (unlink(ControlSocketName) < 0)
195 {
196 sm_syslog(LOG_WARNING, NOQID,
197 "Could not remove control socket: %s",
198 sm_errstring(errno));
199 return;
200 }
201 }
202 #endif /* NETUNIX */
203 return;
204 }
205 /*
206 ** CLRCONTROL -- reset the control connection
207 **
208 ** Parameters:
209 ** none.
210 **
211 ** Returns:
212 ** none.
213 **
214 ** Side Effects:
215 ** releases any resources used by the control interface.
216 */
217
218 void
clrcontrol()219 clrcontrol()
220 {
221 #if NETUNIX
222 if (ControlSocket >= 0)
223 (void) close(ControlSocket);
224 ControlSocket = -1;
225 #endif /* NETUNIX */
226 }
227 /*
228 ** CONTROL_COMMAND -- read and process command from named socket
229 **
230 ** Read and process the command from the opened socket.
231 ** Exits when done since it is running in a forked child.
232 **
233 ** Parameters:
234 ** sock -- the opened socket from getrequests()
235 ** e -- the current envelope
236 **
237 ** Returns:
238 ** none.
239 */
240
241 static jmp_buf CtxControlTimeout;
242
243 /* ARGSUSED0 */
244 static void
controltimeout(timeout)245 controltimeout(timeout)
246 int timeout;
247 {
248 /*
249 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
250 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
251 ** DOING.
252 */
253
254 errno = ETIMEDOUT;
255 longjmp(CtxControlTimeout, 1);
256 }
257
258 void
control_command(sock,e)259 control_command(sock, e)
260 int sock;
261 ENVELOPE *e;
262 {
263 volatile int exitstat = EX_OK;
264 SM_FILE_T *s = NULL;
265 SM_EVENT *ev = NULL;
266 SM_FILE_T *traffic;
267 SM_FILE_T *oldout;
268 char *cmd;
269 char *p;
270 struct cmd *c;
271 char cmdbuf[MAXLINE];
272 char inp[MAXLINE];
273
274 sm_setproctitle(false, e, "control cmd read");
275
276 if (TimeOuts.to_control > 0)
277 {
278 /* handle possible input timeout */
279 if (setjmp(CtxControlTimeout) != 0)
280 {
281 if (LogLevel > 2)
282 sm_syslog(LOG_NOTICE, e->e_id,
283 "timeout waiting for input during control command");
284 exit(EX_IOERR);
285 }
286 ev = sm_setevent(TimeOuts.to_control, controltimeout,
287 TimeOuts.to_control);
288 }
289
290 s = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, (void *) &sock,
291 SM_IO_RDWR, NULL);
292 if (s == NULL)
293 {
294 int save_errno = errno;
295
296 (void) close(sock);
297 errno = save_errno;
298 exit(EX_IOERR);
299 }
300 (void) sm_io_setvbuf(s, SM_TIME_DEFAULT, NULL,
301 SM_IO_NBF, SM_IO_BUFSIZ);
302
303 if (sm_io_fgets(s, SM_TIME_DEFAULT, inp, sizeof(inp)) < 0)
304 {
305 (void) sm_io_close(s, SM_TIME_DEFAULT);
306 exit(EX_IOERR);
307 }
308 (void) sm_io_flush(s, SM_TIME_DEFAULT);
309
310 /* clean up end of line */
311 fixcrlf(inp, true);
312
313 sm_setproctitle(false, e, "control: %s", inp);
314
315 /* break off command */
316 for (p = inp; SM_ISSPACE(*p); p++)
317 continue;
318 cmd = cmdbuf;
319 while (*p != '\0' &&
320 !(SM_ISSPACE(*p)) && cmd < &cmdbuf[sizeof(cmdbuf) - 2])
321 *cmd++ = *p++;
322 *cmd = '\0';
323
324 /* throw away leading whitespace */
325 while (SM_ISSPACE(*p))
326 p++;
327
328 /* decode command */
329 for (c = CmdTab; c->cmd_name != NULL; c++)
330 {
331 if (SM_STRCASEEQ(c->cmd_name, cmdbuf))
332 break;
333 }
334
335 switch (c->cmd_code)
336 {
337 case CMDHELP: /* get help */
338 traffic = TrafficLogFile;
339 TrafficLogFile = NULL;
340 oldout = OutChannel;
341 OutChannel = s;
342 help("control", e);
343 TrafficLogFile = traffic;
344 OutChannel = oldout;
345 break;
346
347 case CMDRESTART: /* restart the daemon */
348 (void) sm_io_fprintf(s, SM_TIME_DEFAULT, "OK\r\n");
349 exitstat = EX_RESTART;
350 break;
351
352 case CMDSHUTDOWN: /* kill the daemon */
353 (void) sm_io_fprintf(s, SM_TIME_DEFAULT, "OK\r\n");
354 exitstat = EX_SHUTDOWN;
355 break;
356
357 case CMDSTATUS: /* daemon status */
358 proc_list_probe();
359 {
360 int qgrp;
361 long bsize;
362 long free;
363
364 /* XXX need to deal with different partitions */
365 qgrp = e->e_qgrp;
366 if (!ISVALIDQGRP(qgrp))
367 qgrp = 0;
368 free = freediskspace(Queue[qgrp]->qg_qdir, &bsize);
369
370 /*
371 ** Prevent overflow and don't lose
372 ** precision (if bsize == 512)
373 */
374
375 if (free > 0)
376 free = (long)((double) free *
377 ((double) bsize / 1024));
378
379 (void) sm_io_fprintf(s, SM_TIME_DEFAULT,
380 "%d/%d/%ld/%d\r\n",
381 CurChildren, MaxChildren,
382 free, getla());
383 }
384 proc_list_display(s, "");
385 break;
386
387 case CMDMSTAT: /* daemon status, extended, tagged format */
388 proc_list_probe();
389 (void) sm_io_fprintf(s, SM_TIME_DEFAULT,
390 "C:%d\r\nM:%d\r\nL:%d\r\n",
391 CurChildren, MaxChildren,
392 getla());
393 printnqe(s, "Q:");
394 disk_status(s, "D:");
395 proc_list_display(s, "P:");
396 break;
397
398 case CMDMEMDUMP: /* daemon memory dump, to find memory leaks */
399 #if SM_HEAP_CHECK
400 /* dump the heap, if we are checking for memory leaks */
401 if (sm_debug_active(&SmHeapCheck, 2))
402 {
403 sm_heap_report(s, sm_debug_level(&SmHeapCheck) - 1);
404 }
405 else
406 {
407 (void) sm_io_fprintf(s, SM_TIME_DEFAULT,
408 "Memory dump unavailable.\r\n");
409 (void) sm_io_fprintf(s, SM_TIME_DEFAULT,
410 "To fix, run sendmail with -dsm_check_heap.4\r\n");
411 }
412 #else /* SM_HEAP_CHECK */
413 (void) sm_io_fprintf(s, SM_TIME_DEFAULT,
414 "Memory dump unavailable.\r\n");
415 (void) sm_io_fprintf(s, SM_TIME_DEFAULT,
416 "To fix, rebuild with -DSM_HEAP_CHECK\r\n");
417 #endif /* SM_HEAP_CHECK */
418 break;
419
420 case CMDERROR: /* unknown command */
421 (void) sm_io_fprintf(s, SM_TIME_DEFAULT,
422 "Bad command (%s)\r\n", cmdbuf);
423 break;
424 }
425 (void) sm_io_close(s, SM_TIME_DEFAULT);
426 if (ev != NULL)
427 sm_clrevent(ev);
428 exit(exitstat);
429 }
430