/***************************************************************************** POPular -- A POP3 server and proxy for large mail systems $Id: ctrl.c,v 1.18 2001/05/24 17:41:07 sqrt Exp $ http://www.remote.org/jochen/mail/popular/ ****************************************************************************** Copyright (C) 1999-2001 Jochen Topf This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA *****************************************************************************/ #if HAVE_CONFIG_H # include #endif #include "popular.h" #include "ctrl.h" extern volatile int got_shutdown; extern struct configdesc cd[]; extern struct ctrl_cmd_dispatch_table ccdt[]; /***************************************************************************** init_config_vars() *****************************************************************************/ void init_config_vars() { struct configdesc *c; for (c=cd; c->tag; c++) { switch (c->type) { case ctTime: /* fall through */ case ctBool: /* fall through */ case ctInt: *((int *)c->ptr) = c->def; break; case ctStr: /* fall through */ case ctId: /* fall through */ case ctFile: /* fall through */ case ctDir: strlcpy(c->ptr, c->defstr, c->max); break; case ctIgn: /* fall through */ case ctNone: /* ignored */ break; } } } /***************************************************************************** ctrl_cmd_show() *****************************************************************************/ void ctrl_cmd_show(char *argv[], int argc, char *answer) { struct configdesc *c; /* if there is no argument print list of all config variables */ if (argc == 1) { (void) strlcpy(answer, "00 vars:", MAXBUF); for (c=cd; c->tag; c++) { if (c->type == ctIgn) continue; (void) strlcat(answer, " ", MAXBUF); (void) strlcat(answer, c->tag, MAXBUF); if (! c->readwrite) (void) strlcat(answer, "(ro)", MAXBUF); } return; } for (c=cd; c->tag; c++) { if (strcasecmp(argv[1], c->tag)) continue; switch (c->type) { case ctTime: snprintf(answer, MAXBUF, "00 set %s \"%s\"", c->tag, sec2timedesc(*((int *)c->ptr))); break; case ctBool: /* fall through */ case ctInt: snprintf(answer, MAXBUF, "00 set %s \"%d\"", c->tag, *((int *)c->ptr)); break; case ctDir: /* fall through */ case ctFile: /* fall through */ case ctId: /* fall through */ case ctStr: snprintf(answer, MAXBUF, "00 set %s \"%s\"", c->tag, (char *)c->ptr); break; case ctIgn: snprintf(answer, MAXBUF, "11 variable not supported: '%s'", argv[1]); break; case ctNone: /* fall through */ default: snprintf(answer, MAXBUF, "10 unknown variable: '%s'", argv[1]); break; } return; } snprintf(answer, MAXBUF, "10 unknown variable: '%s'", argv[1]); } /***************************************************************************** ctrl_cmd_set() *****************************************************************************/ void ctrl_cmd_set(char *argv[], int argc, char *answer) { struct configdesc *c; int len; for (c=cd; c->tag; c++) { if (strcasecmp(argv[1], c->tag)) continue; if (!c->readwrite) { snprintf(answer, MAXBUF, "12 variable is read-only: '%s'", argv[1]); return; } switch (c->type) { case ctBool: if ( (!strcmp(argv[2], "0")) || (!strcmp(argv[2], "off")) || (!strcmp(argv[2], "false")) || (!strcmp(argv[2], "no")) ) { *((int *)c->ptr) = 0; } else if ( (!strcmp(argv[2], "1")) || (!strcmp(argv[2], "on")) || (!strcmp(argv[2], "true")) || (!strcmp(argv[2], "yes")) ) { *((int *)c->ptr) = 1; } else { ANSWER1("13 value for '%s' must be one of 0=no=off=false or 1=yes=on=true", argv[1]); } snprintf(answer, MAXBUF, "00 set %s \"%d\"", c->tag, *((int *)c->ptr)); /* XLOG-DOC:ADM:0160:config_set * A boolean config variable was set to a new value. * (0=no=off=false, 1=yes=on=true) */ xlog_printf(xlog_adm, 0x0160, "config_set var=%s value='%d'", c->tag, *((int *)c->ptr)); break; case ctTime: { int v = timedesc2sec(argv[2], c->min, c->max); if (v < 0) { snprintf(answer, MAXBUF, "13 value for '%s' must be between %d and %d", argv[1], c->min, c->max); return; } *((int *)c->ptr) = v; snprintf(answer, MAXBUF, "00 set %s \"%s\"", c->tag, sec2timedesc(*((int *)c->ptr))); /* XLOG-DOC:ADM:017a:config_set * A config variable was set to a new value. */ xlog_printf(xlog_adm, 0x017a, "config_set var=%s value='%d'", c->tag, *((int *)c->ptr)); break; } case ctInt: { int v = get_int(argv[2], c->min, c->max, -1, -1, -1); if (v < 0) { snprintf(answer, MAXBUF, "13 value for '%s' must be between %d and %d", argv[1], c->min, c->max); return; } if (!strcasecmp("maxsession", c->tag)) { if (v <= *((int *)c->ptr)) ANSWER0("13 value for 'maxsession' can only increase"); } *((int *)c->ptr) = v; snprintf(answer, MAXBUF, "00 set %s \"%d\"", c->tag, *((int *)c->ptr)); /* XLOG-DOC:ADM:014b:config_set * A config variable was set to a new value. */ xlog_printf(xlog_adm, 0x014b, "config_set var=%s value='%d'", c->tag, *((int *)c->ptr)); break; } case ctDir: { struct stat sbuf; if (stat(argv[2], &sbuf) != 0) ANSWER2("13 can't stat '%s': %s", argv[2], strerror(errno)); if (! S_ISDIR(sbuf.st_mode)) ANSWER1("13 not a directory '%s'", argv[2]); } if (access(argv[2], R_OK|X_OK) != 0) ANSWER2("13 dir '%s' is not accessible: %s", argv[2], strerror(errno)); len = strlcpy(c->ptr, argv[2], c->max); if (len >= c->max) ANSWER1("13 value too long for '%s' (was truncated!)", c->tag); snprintf(answer, MAXBUF, "00 set %s \"%s\"", c->tag, (char *)c->ptr); /* XLOG-DOC:ADM:014d:config_set * A config variable was set to a new value. */ xlog_printf(xlog_adm, 0x014d, "config_set var=%s value='%s'", c->tag, (char *)c->ptr); break; case ctId: if (!valid_id(argv[2])) ANSWER0("13 invalid chars in id (must be [a-zA-Z0-9._-]*)"); /* fall through */ case ctFile: /* fall through */ case ctStr: len = strlcpy(c->ptr, argv[2], c->max); if (len >= c->max) ANSWER1("13 value too long for '%s' (was truncated!)", c->tag); snprintf(answer, MAXBUF, "00 set %s \"%s\"", c->tag, (char *)c->ptr); /* XLOG-DOC:ADM:0149:config_set * A config variable was set to a new value. */ xlog_printf(xlog_adm, 0x0149, "config_set var=%s value='%s'", c->tag, (char *)c->ptr); break; case ctIgn: snprintf(answer, MAXBUF, "11 variable not supported: '%s'", argv[1]); break; case ctNone: /* fall through */ default: snprintf(answer, MAXBUF, "10 unknown variable: '%s'", argv[1]); break; } return; } snprintf(answer, MAXBUF, "10 unknown variable: '%s'", argv[1]); } /***************************************************************************** ctrl_cmd_debug() *****************************************************************************/ void ctrl_cmd_debug(char *argv[], int argc, char *answer) { int i, d=0; if (argc == 1) ANSWER1("00 debug =%s", print_debug_list(debug)); switch (argv[1][0]) { case '-': /* fallthrough */ case '+': /* fallthrough */ case '=': break; default: ANSWER0("13 first arg must be '-', '+' or '='"); } if (argv[1][1] != '\0') ANSWER0("13 first arg must be '-', '+' or '='"); for (i=2; i < argc; i++) { struct debug_info *di = find_debug_info(-1, argv[i]); if (! di) ANSWER1("13 unknown debug type: '%s'", argv[i]); d |= di->type; } switch (argv[1][0]) { case '-': debug &= ~d; break; case '+': debug |= d; break; case '=': debug = d; break; default: ANSWER0("99 internal error (+-=)"); } /* XLOG-DOC:INF:005e:debug_type * The debug options were changed through pcontrol. These are the new * options that are used from now on. */ xlog_printf(xlog_inf, 0x005e, "debug_type%s", print_debug_list(debug)); ANSWER1("00 debug =%s", print_debug_list(debug)); } /***************************************************************************** ctrl_cmd_shutdown() *****************************************************************************/ void ctrl_cmd_shutdown(char *argv[], int argc, char *answer) { if (! strcasecmp(argv[1], "all")) { /* XLOG-DOC:INF:005f:shutdown_all_received * The command 'shutdown all' was received from pcontrol. The server * will shut down all it's children and the exit itself. */ xlog_printf(xlog_inf, 0x005f, "shutdown_all_received"); got_shutdown = SHUTDOWN_CHILDREN | SHUTDOWN_SERVER; } else if (! strcasecmp(argv[1], "children")) { /* XLOG-DOC:INF:012b:shutdown_children_received * The command 'shutdown children' was received from pcontrol. The server * will shut down all it's children. */ xlog_printf(xlog_inf, 0x012b, "shutdown_children_received"); got_shutdown = SHUTDOWN_CHILDREN; } else if (! strcasecmp(argv[1], "parent")) { /* XLOG-DOC:INF:0060:shutdown_parent_received * The command 'shutdown parent' was received from pcontrol. The server * will shut down itself, but not its children. */ xlog_printf(xlog_inf, 0x0060, "shutdown_parent_received"); got_shutdown = SHUTDOWN_SERVER; } else if (! strcasecmp(argv[1], "delayed")) { /* XLOG-DOC:INF:012f:shutdown_delayed_received * The command 'shutdown delayed' was received from pcontrol. The server * will not accept new connections and shut down itself after the last * child has died. */ xlog_printf(xlog_inf, 0x012f, "shutdown_delayed_received"); got_shutdown = SHUTDOWN_DELAYED; } else { ANSWER1("22 unknown subcommand: '%s'", argv[1]); } (void) strlcpy(answer, "00 OK", MAXBUF); } /***************************************************************************** dispatch_control_request() *****************************************************************************/ void dispatch_control_request(char *request, char *answer) { int i; int argc; char *argv[MAXARGS]; argc = sep_args(request, argv, MAXARGS); if (argc == -1) ANSWER0("29 parse error"); if (argc == -2) ANSWER0("20 too many arguments"); if (argc < 0) ANSWER0("99 internal error (sep_args)"); for (i=0; ccdt[i].cmd_name; i++) { if (! strcasecmp(ccdt[i].cmd_name, argv[0])) { if (ccdt[i].minarg > argc - 1) { ANSWER0("20 not enough arguments"); } else if (argc - 1 > ccdt[i].maxarg) { ANSWER0("20 too many arguments"); } else { ccdt[i].cmd_handler(argv, argc, answer); } return; } } ANSWER1("21 unknown command: '%s'", argv[0]); } /***************************************************************************** control_command() *****************************************************************************/ void control_command(int sock) { char request[MAXBUF], answer[MAXBUF]; struct sockaddr_un from; int fromlen = sizeof(from); int len; DEBUG0(DG_CTRL, "control_command", "start"); len = recvfrom(sock, request, sizeof(request)-1, 0, (struct sockaddr *) &from, &fromlen); if (len < 0) { /* XLOG-DOC:ADM:0061:pcontrol_recvfrom_failed * The call to recvfrom() to get a command from pcontrol failed. */ xlog_printf(xlog_adm, 0x0061, "pcontrol_recvfrom_failed errno=%d errmsg='%s'", errno, strerror(errno)); return; } request[len] = '\0'; DEBUG1(DG_CTRL, "control_command", "got request: '%s'", request); dispatch_control_request(request, answer); DEBUG1(DG_CTRL, "control_command", "sending answer: '%s'", answer); /* Old linux versions don't have MSG_DONTWAIT. We work around this by * setting it to 0. This could theoretically result in a deadlock and * halt the server, but as it is really improbable, we keep going. */ #ifndef MSG_DONTWAIT #define MSG_DONTWAIT 0 #warning MSG_DONTWAIT has been set to zero. Read comment in source for details. #endif len = sendto(sock, answer, strlen(answer), MSG_DONTWAIT, (struct sockaddr *) &from, fromlen); if (len < 0) { /* XLOG-DOC:ADM:0062:pcontrol_sendto_failed * The call to sendto() to send an answer to pcontrol failed. */ xlog_printf(xlog_adm, 0x0062, "pcontrol_sendto_failed errno=%d errmsg='%s'", errno, strerror(errno)); return; } DEBUG0(DG_CTRL, "control_command", "end"); } /** THE END *****************************************************************/