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