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