/*****************************************************************************
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 <jochen@remote.org>
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 <config.h>
#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 *****************************************************************/
syntax highlighted by Code2HTML, v. 0.9.1