source: trunk/third/sendmail/sendmail/control.c @ 19204

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