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