summaryrefslogtreecommitdiffstats
path: root/sendmail/src/control.c
diff options
context:
space:
mode:
Diffstat (limited to 'sendmail/src/control.c')
-rw-r--r--sendmail/src/control.c431
1 files changed, 431 insertions, 0 deletions
diff --git a/sendmail/src/control.c b/sendmail/src/control.c
new file mode 100644
index 0000000..0b525f7
--- /dev/null
+++ b/sendmail/src/control.c
@@ -0,0 +1,431 @@
+/*
+ * Copyright (c) 1998-2004, 2006 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#include <sendmail.h>
+
+SM_RCSID("@(#)$Id: control.c,v 8.128 2006/08/15 23:24:56 ca Exp $")
+
+#include <sm/fdset.h>
+
+/* values for cmd_code */
+#define CMDERROR 0 /* bad command */
+#define CMDRESTART 1 /* restart daemon */
+#define CMDSHUTDOWN 2 /* end daemon */
+#define CMDHELP 3 /* help */
+#define CMDSTATUS 4 /* daemon status */
+#define CMDMEMDUMP 5 /* dump memory, to find memory leaks */
+#define CMDMSTAT 6 /* daemon status, more info, tagged data */
+
+struct cmd
+{
+ char *cmd_name; /* command name */
+ int cmd_code; /* internal code, see below */
+};
+
+static struct cmd CmdTab[] =
+{
+ { "help", CMDHELP },
+ { "restart", CMDRESTART },
+ { "shutdown", CMDSHUTDOWN },
+ { "status", CMDSTATUS },
+ { "memdump", CMDMEMDUMP },
+ { "mstat", CMDMSTAT },
+ { NULL, CMDERROR }
+};
+
+
+
+static void controltimeout __P((int));
+int ControlSocket = -1;
+
+/*
+** OPENCONTROLSOCKET -- create/open the daemon control named socket
+**
+** Creates and opens a named socket for external control over
+** the sendmail daemon.
+**
+** Parameters:
+** none.
+**
+** Returns:
+** 0 if successful, -1 otherwise
+*/
+
+int
+opencontrolsocket()
+{
+# if NETUNIX
+ int save_errno;
+ int rval;
+ long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN;
+ struct sockaddr_un controladdr;
+
+ if (ControlSocketName == NULL || *ControlSocketName == '\0')
+ return 0;
+
+ if (strlen(ControlSocketName) >= sizeof(controladdr.sun_path))
+ {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+
+ rval = safefile(ControlSocketName, RunAsUid, RunAsGid, RunAsUserName,
+ sff, S_IRUSR|S_IWUSR, NULL);
+
+ /* if not safe, don't create */
+ if (rval != 0)
+ {
+ errno = rval;
+ return -1;
+ }
+
+ ControlSocket = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (ControlSocket < 0)
+ return -1;
+ if (SM_FD_SETSIZE > 0 && ControlSocket >= SM_FD_SETSIZE)
+ {
+ clrcontrol();
+ errno = EINVAL;
+ return -1;
+ }
+
+ (void) unlink(ControlSocketName);
+ memset(&controladdr, '\0', sizeof(controladdr));
+ controladdr.sun_family = AF_UNIX;
+ (void) sm_strlcpy(controladdr.sun_path, ControlSocketName,
+ sizeof(controladdr.sun_path));
+
+ if (bind(ControlSocket, (struct sockaddr *) &controladdr,
+ sizeof(controladdr)) < 0)
+ {
+ save_errno = errno;
+ clrcontrol();
+ errno = save_errno;
+ return -1;
+ }
+
+ if (geteuid() == 0)
+ {
+ uid_t u = 0;
+
+ if (RunAsUid != 0)
+ u = RunAsUid;
+ else if (TrustedUid != 0)
+ u = TrustedUid;
+
+ if (u != 0 &&
+ chown(ControlSocketName, u, -1) < 0)
+ {
+ save_errno = errno;
+ sm_syslog(LOG_ALERT, NOQID,
+ "ownership change on %s to uid %d failed: %s",
+ ControlSocketName, (int) u,
+ sm_errstring(save_errno));
+ message("050 ownership change on %s to uid %d failed: %s",
+ ControlSocketName, (int) u,
+ sm_errstring(save_errno));
+ closecontrolsocket(true);
+ errno = save_errno;
+ return -1;
+ }
+ }
+
+ if (chmod(ControlSocketName, S_IRUSR|S_IWUSR) < 0)
+ {
+ save_errno = errno;
+ closecontrolsocket(true);
+ errno = save_errno;
+ return -1;
+ }
+
+ if (listen(ControlSocket, 8) < 0)
+ {
+ save_errno = errno;
+ closecontrolsocket(true);
+ errno = save_errno;
+ return -1;
+ }
+# endif /* NETUNIX */
+ return 0;
+}
+/*
+** CLOSECONTROLSOCKET -- close the daemon control named socket
+**
+** Close a named socket.
+**
+** Parameters:
+** fullclose -- if set, close the socket and remove it;
+** otherwise, just remove it
+**
+** Returns:
+** none.
+*/
+
+void
+closecontrolsocket(fullclose)
+ bool fullclose;
+{
+# if NETUNIX
+ long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN;
+
+ if (ControlSocket >= 0)
+ {
+ int rval;
+
+ if (fullclose)
+ {
+ (void) close(ControlSocket);
+ ControlSocket = -1;
+ }
+
+ rval = safefile(ControlSocketName, RunAsUid, RunAsGid,
+ RunAsUserName, sff, S_IRUSR|S_IWUSR, NULL);
+
+ /* if not safe, don't unlink */
+ if (rval != 0)
+ return;
+
+ if (unlink(ControlSocketName) < 0)
+ {
+ sm_syslog(LOG_WARNING, NOQID,
+ "Could not remove control socket: %s",
+ sm_errstring(errno));
+ return;
+ }
+ }
+# endif /* NETUNIX */
+ return;
+}
+/*
+** CLRCONTROL -- reset the control connection
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** releases any resources used by the control interface.
+*/
+
+void
+clrcontrol()
+{
+# if NETUNIX
+ if (ControlSocket >= 0)
+ (void) close(ControlSocket);
+ ControlSocket = -1;
+# endif /* NETUNIX */
+}
+/*
+** CONTROL_COMMAND -- read and process command from named socket
+**
+** Read and process the command from the opened socket.
+** Exits when done since it is running in a forked child.
+**
+** Parameters:
+** sock -- the opened socket from getrequests()
+** e -- the current envelope
+**
+** Returns:
+** none.
+*/
+
+static jmp_buf CtxControlTimeout;
+
+/* ARGSUSED0 */
+static void
+controltimeout(timeout)
+ int timeout;
+{
+ /*
+ ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
+ ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
+ ** DOING.
+ */
+
+ errno = ETIMEDOUT;
+ longjmp(CtxControlTimeout, 1);
+}
+
+void
+control_command(sock, e)
+ int sock;
+ ENVELOPE *e;
+{
+ volatile int exitstat = EX_OK;
+ SM_FILE_T *s = NULL;
+ SM_EVENT *ev = NULL;
+ SM_FILE_T *traffic;
+ SM_FILE_T *oldout;
+ char *cmd;
+ char *p;
+ struct cmd *c;
+ char cmdbuf[MAXLINE];
+ char inp[MAXLINE];
+
+ sm_setproctitle(false, e, "control cmd read");
+
+ if (TimeOuts.to_control > 0)
+ {
+ /* handle possible input timeout */
+ if (setjmp(CtxControlTimeout) != 0)
+ {
+ if (LogLevel > 2)
+ sm_syslog(LOG_NOTICE, e->e_id,
+ "timeout waiting for input during control command");
+ exit(EX_IOERR);
+ }
+ ev = sm_setevent(TimeOuts.to_control, controltimeout,
+ TimeOuts.to_control);
+ }
+
+ s = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, (void *) &sock,
+ SM_IO_RDWR, NULL);
+ if (s == NULL)
+ {
+ int save_errno = errno;
+
+ (void) close(sock);
+ errno = save_errno;
+ exit(EX_IOERR);
+ }
+ (void) sm_io_setvbuf(s, SM_TIME_DEFAULT, NULL,
+ SM_IO_NBF, SM_IO_BUFSIZ);
+
+ if (sm_io_fgets(s, SM_TIME_DEFAULT, inp, sizeof(inp)) == NULL)
+ {
+ (void) sm_io_close(s, SM_TIME_DEFAULT);
+ exit(EX_IOERR);
+ }
+ (void) sm_io_flush(s, SM_TIME_DEFAULT);
+
+ /* clean up end of line */
+ fixcrlf(inp, true);
+
+ sm_setproctitle(false, e, "control: %s", inp);
+
+ /* break off command */
+ for (p = inp; isascii(*p) && isspace(*p); p++)
+ continue;
+ cmd = cmdbuf;
+ while (*p != '\0' &&
+ !(isascii(*p) && isspace(*p)) &&
+ cmd < &cmdbuf[sizeof(cmdbuf) - 2])
+ *cmd++ = *p++;
+ *cmd = '\0';
+
+ /* throw away leading whitespace */
+ while (isascii(*p) && isspace(*p))
+ p++;
+
+ /* decode command */
+ for (c = CmdTab; c->cmd_name != NULL; c++)
+ {
+ if (sm_strcasecmp(c->cmd_name, cmdbuf) == 0)
+ break;
+ }
+
+ switch (c->cmd_code)
+ {
+ case CMDHELP: /* get help */
+ traffic = TrafficLogFile;
+ TrafficLogFile = NULL;
+ oldout = OutChannel;
+ OutChannel = s;
+ help("control", e);
+ TrafficLogFile = traffic;
+ OutChannel = oldout;
+ break;
+
+ case CMDRESTART: /* restart the daemon */
+ (void) sm_io_fprintf(s, SM_TIME_DEFAULT, "OK\r\n");
+ exitstat = EX_RESTART;
+ break;
+
+ case CMDSHUTDOWN: /* kill the daemon */
+ (void) sm_io_fprintf(s, SM_TIME_DEFAULT, "OK\r\n");
+ exitstat = EX_SHUTDOWN;
+ break;
+
+ case CMDSTATUS: /* daemon status */
+ proc_list_probe();
+ {
+ int qgrp;
+ long bsize;
+ long free;
+
+ /* XXX need to deal with different partitions */
+ qgrp = e->e_qgrp;
+ if (!ISVALIDQGRP(qgrp))
+ qgrp = 0;
+ free = freediskspace(Queue[qgrp]->qg_qdir, &bsize);
+
+ /*
+ ** Prevent overflow and don't lose
+ ** precision (if bsize == 512)
+ */
+
+ if (free > 0)
+ free = (long)((double) free *
+ ((double) bsize / 1024));
+
+ (void) sm_io_fprintf(s, SM_TIME_DEFAULT,
+ "%d/%d/%ld/%d\r\n",
+ CurChildren, MaxChildren,
+ free, getla());
+ }
+ proc_list_display(s, "");
+ break;
+
+ case CMDMSTAT: /* daemon status, extended, tagged format */
+ proc_list_probe();
+ (void) sm_io_fprintf(s, SM_TIME_DEFAULT,
+ "C:%d\r\nM:%d\r\nL:%d\r\n",
+ CurChildren, MaxChildren,
+ getla());
+ printnqe(s, "Q:");
+ disk_status(s, "D:");
+ proc_list_display(s, "P:");
+ break;
+
+ case CMDMEMDUMP: /* daemon memory dump, to find memory leaks */
+# if SM_HEAP_CHECK
+ /* dump the heap, if we are checking for memory leaks */
+ if (sm_debug_active(&SmHeapCheck, 2))
+ {
+ sm_heap_report(s, sm_debug_level(&SmHeapCheck) - 1);
+ }
+ else
+ {
+ (void) sm_io_fprintf(s, SM_TIME_DEFAULT,
+ "Memory dump unavailable.\r\n");
+ (void) sm_io_fprintf(s, SM_TIME_DEFAULT,
+ "To fix, run sendmail with -dsm_check_heap.4\r\n");
+ }
+# else /* SM_HEAP_CHECK */
+ (void) sm_io_fprintf(s, SM_TIME_DEFAULT,
+ "Memory dump unavailable.\r\n");
+ (void) sm_io_fprintf(s, SM_TIME_DEFAULT,
+ "To fix, rebuild with -DSM_HEAP_CHECK\r\n");
+# endif /* SM_HEAP_CHECK */
+ break;
+
+ case CMDERROR: /* unknown command */
+ (void) sm_io_fprintf(s, SM_TIME_DEFAULT,
+ "Bad command (%s)\r\n", cmdbuf);
+ break;
+ }
+ (void) sm_io_close(s, SM_TIME_DEFAULT);
+ if (ev != NULL)
+ sm_clrevent(ev);
+ exit(exitstat);
+}
OpenPOWER on IntegriCloud