/*****************************************************************************
POPular -- A POP3 server and proxy for large mail systems
$Id: pproxy_ctrl.c,v 1.30 2002/11/28 13:56:03 sqrt Exp $
http://www.remote.org/jochen/mail/popular/
******************************************************************************
Copyright (C) 1999-2002 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 "pproxy.h"
#include "ctrl.h"
extern struct capa *first_capa;
extern struct pdm_module *first_pdm_module;
extern struct pproxyconfig conf;
struct configdesc cd[] = {
/* tag rw type addr min max default value */
{ "allowsslv2", 1, ctBool, &conf.allowsslv2, 0, 1, 0, NULL },
{ "authtimeout", 1, ctTime, &conf.authtimeout, 0, 60*60, DEFAULT_TIMEOUT_AUTH, NULL },
{ "backlog", 1, ctInt, &conf.backlog, 5, 1024, DEFAULT_LISTEN_BACKLOG, NULL },
{ "checkport", 1, ctInt, &conf.checkport, 1, 65535, DEFAULT_PORT_CHECK, NULL },
{ "checktimeout", 1, ctTime, &conf.checktimeout, 1, 60, DEFAULT_TIMEOUT_CHECK, NULL },
/* RFC1939 says the 'inactivity autologout timer' MUST be >= 10 mins */
{ "idletimeout", 1, ctTime, &conf.idletimeout, 600, 60*60*24, DEFAULT_TIMEOUT_IDLE, NULL },
{ "maxlocalload", 1, ctInt, &conf.maxlocalload, 0, 9999, 0, NULL },
{ "maxsession", 1, ctInt, &conf.maxsession, 2, MAX_SESSION, DEFAULT_MAX_SESSION, NULL },
{ "pid", 0, ctInt, &conf.pid, 0, 0, 0, NULL },
/* RFC1939 says the 'inactivity autologout timer' MUST be >= 10 mins */
{ "proxytimeout", 1, ctTime, &conf.proxytimeout, 600, 60*60*24, DEFAULT_TIMEOUT_PROXY, NULL },
{ "sessionlimit", 0, ctInt, &conf.sessionlimit, 0, 0, MAX_SESSION, NULL },
{ "sessiontimeout", 1, ctTime, &conf.sessiontimeout, 0, 60*60*24, DEFAULT_TIMEOUT_SESSION, NULL },
/* tag rw type addr strlength default value */
{ "capadir", 1, ctDir, conf.capadir, 0, sizeof(conf.capadir), 0, PPROXY_CAPA_DIR },
{ "defaultns", 1, ctId, conf.defaultns, 0, sizeof(conf.defaultns), 0, "" },
{ "fallback", 1, ctId, conf.fallback, 0, sizeof(conf.fallback), 0, "" },
{ "id", 0, ctId, conf.id, 0, sizeof(conf.id), 0, PPROXY_PRG_NAME },
{ "logfile", 1, ctFile, conf.logfile, 0, sizeof(conf.logfile), 0, PPROXY_LOG_FILE },
{ "pdmdir", 1, ctDir, conf.pdmdir, 0, sizeof(conf.pdmdir), 0, PPROXY_PDM_DIR },
{ "rundir" , 0, ctDir, conf.rundir, 0, sizeof(conf.rundir), 0, RUN_DIR },
{ "sidprefix" , 1, ctId, conf.sidprefix, 0, sizeof(conf.sidprefix), 0, "" },
{ "tlsdir", 1, ctDir, conf.tlsdir, 0, sizeof(conf.tlsdir), 0, PPROXY_TLS_DIR },
{ "version", 0, ctStr, conf.version, 0, sizeof(conf.version), 0, VERSION },
{ NULL, 0, ctNone, NULL, 0, 0, 0, NULL }
};
struct ctrl_cmd_dispatch_table ccdt[] = {
{ "backend", ctrl_cmd_backend, 1, 11 },
{ "capa", ctrl_cmd_capa, 1, 2 },
{ "debug", ctrl_cmd_debug, 0, 10 },
{ "pdm", ctrl_cmd_pdm, 1, 19 },
{ "set", ctrl_cmd_set, 2, 2 },
{ "show", ctrl_cmd_show, 0, 1 },
{ "shutdown", ctrl_cmd_shutdown, 1, 1 },
{ "vserv", ctrl_cmd_vserv, 1, 22 },
{ "prng", ctrl_cmd_prng, 1, 2 },
{ NULL, NULL, 0, 0 }
};
extern volatile int got_shutdown;
/*****************************************************************************
ctrl_cmd_backend()
*****************************************************************************/
void
ctrl_cmd_backend(char *argv[], int argc, char *answer)
{
struct backend *old;
int got_conf=0;
if (argc >= 3 && !valid_id(argv[2])) ANSWER0("13 invalid chars in id (must be [a-zA-Z0-9._-]*)");
if (!strcasecmp(argv[1], "conf")) {
struct backend new;
int n, got_host, port=-1;
if ((argc < 5) || ((argc % 2) != 1)) ANSWER0("20 wrong argument count");
old = get_backend(argv[2], 1);
if (!old) ANSWER1("15 unknown backend and max backend number already reached: '%s'", argv[2]);
/* if this backend name is new, intialize this backend with default values */
if (old->state == bstFree) {
int len;
memset(&new, 0, sizeof(new));
len = strlcpy(new.id, argv[2], sizeof(new.id));
if (len >= sizeof(new.id)) ANSWER0("13 id too long");
new.prot = ptPOP3;
new.state = bstOffline;
new.backend_addr.sin_family = AF_INET;
got_host = 0;
port = DEFAULT_PORT_POP3;
} else {
memcpy(&new, old, sizeof(new));
got_host = 1;
}
for (n=3; n<argc; n+=2) {
if (! strcasecmp(argv[n], "host")) { /* host */
struct hostent *host;
host = gethostbyname(argv[n+1]);
if (host == NULL || (host->h_addr_list)[0] == NULL)
ANSWER1("13 unknown host or IP number: '%s'", argv[n+1]);
memcpy(&(new.backend_addr.sin_addr.s_addr), host->h_addr_list[0],
sizeof(new.backend_addr.sin_addr.s_addr));
got_host = 1;
} else if (! strcasecmp(argv[n], "port")) { /* port */
port = get_port(argv[n+1]);
if (port < 0) ANSWER0("13 port must be valid TCP port");
new.backend_addr.sin_port = htons(port);
} else if (! strcasecmp(argv[n], "prot")) { /* prot */
struct protocol_info *pi = find_protocol_info(-1, argv[n+1]);
if (! pi || ! pi->support_backend_side) ANSWER1("13 not a valid protocol: '%s'", argv[n+1]);
new.prot = pi->type;
port = pi->port;
} else if (! strcasecmp(argv[n], "state")) { /* state */
struct backend_state_info *bsi = find_backend_state_info(-1, argv[n+1]);
if (! bsi || bsi->type == bstFree)
ANSWER1("13 unknown state: '%s'", argv[n+1]);
new.state = bsi->type;
} else {
ANSWER1("23 unknown keyword: '%s'", argv[n]);
}
}
if (!got_host) ANSWER0("16 missing host option");
if (!new.backend_addr.sin_port) new.backend_addr.sin_port = htons(port);
memcpy(old, &new, sizeof(new));
got_conf=1;
} else if (!strcasecmp(argv[1], "show")) {
if (argc != 3) ANSWER0("20 too many arguments");
old = get_backend(argv[2], 0);
if (! old) ANSWER1("10 unknown backend: '%s'", argv[2]);
} else if (!strcasecmp(argv[1], "del")) {
if (argc != 3) ANSWER0("20 too many arguments");
old = get_backend(argv[2], 0);
if (! old) ANSWER1("10 unknown backend: '%s'", argv[2]);
old->state = bstFree;
/* XLOG-DOC:ADM:0163:config_backend_del
* A 'backend del' command was executed. */
xlog_printf(xlog_adm, 0x0163, "config_backend_del backend=%s", old->id);
ANSWER1("00 backend '%s' deleted", old->id);
} else if (!strcasecmp(argv[1], "flush")) {
if (argc != 2) ANSWER0("20 too many arguments");
flush_backend();
/* XLOG-DOC:ADM:0164:config_backend_flush
* A 'backend flush' command was executed. */
xlog_printf(xlog_adm, 0x0164, "config_backend_flush");
ANSWER0("00 all backends flushed");
} else if (!strcasecmp(argv[1], "list")) {
ANSWER1("00 backends:%s", list_backend());
} else {
ANSWER1("22 unknown subcomand: '%s'", argv[1]);
}
{
struct protocol_info *p = find_protocol_info(old->prot, NULL);
struct backend_state_info *s = find_backend_state_info(old->state, NULL);
if (! p) ANSWER0("99 internal error (protocol_info)");
if (! s) ANSWER0("99 internal error (backend_state_info)");
snprintf(answer, MAXBUF, "00 backend conf \"%s\" host \"%s\" port \"%d\" prot \"%s\" state \"%s\"",
old->id, /* backend id */
print_ip(&(old->backend_addr), NULL), /* IP address */
ntohs(old->backend_addr.sin_port), /* TCP port */
p->name, /* protocol name */
s->name); /* state */
/* XLOG-DOC:ADM:0165:config_backend_conf
* A 'backend conf' command was executed. */
if (got_conf) xlog_printf(xlog_adm, 0x0165, "config_backend_conf id=%s host=%s port=%d prot=%s state=%s", old->id, print_ip(&(old->backend_addr), NULL), ntohs(old->backend_addr.sin_port), p->name, s->name);
}
}
/*****************************************************************************
capa_name()
*****************************************************************************/
char *
capa_name(struct virt_serv *vs)
{
switch (vs->capa_type) {
case capa_error:
return "ERROR";
case capa_none:
return "NONE";
case capa_default:
return "DEFAULT";
case capa_user:
return vs->capa_ptr->name;
default:
return "ERROR";
}
}
/*****************************************************************************
starttls_name()
*****************************************************************************/
char *
starttls_name(struct virt_serv *vs)
{
switch (vs->starttls_type) {
case starttls_off:
return "OFF";
case starttls_optional:
return "OPTIONAL";
case starttls_force:
return "FORCE";
default:
return "ERROR";
}
}
/*****************************************************************************
ctrl_cmd_vserv()
*****************************************************************************/
void
ctrl_cmd_vserv(char *argv[], int argc, char *answer)
{
struct virt_serv *vs, *sip;
int got_conf=0;
int len;
if (argc >= 3 && !valid_id(argv[2])) ANSWER0("13 invalid chars in id (must be [a-zA-Z0-9._-]*)");
if (!strcasecmp(argv[1], "conf")) {
struct virt_serv new;
int n, got_host, port=-1;
if ((argc < 5) || ((argc % 2) != 1)) ANSWER0("20 wrong argument count");
vs = get_virt_serv(argv[2], 1);
if (!vs) ANSWER1("15 unknown vserv and max vservs already reached: '%s'", argv[2]);
/* if this vserv is new, intialize it with default values */
if (vs->state == vsstFree) {
memset(&new, 0, sizeof(new));
len = strlcpy(new.id, argv[2], sizeof(new.id));
if (len >= sizeof(new.id)) ANSWER0("13 id too long");
new.prot = ptPOP3;
new.state = vsstOffline;
new.local_addr.sin_family = AF_INET;
got_host = 0;
port = DEFAULT_PORT_POP3;
new.capa_type = capa_error;
new.capa_ptr = NULL;
new.starttls_type = starttls_off;
} else {
memcpy(&new, vs, sizeof(new));
got_host = 1;
}
for (n=3; n<argc; n+=2) {
if (! strcasecmp(argv[n], "iface")) { /* iface */
struct hostent *host;
if (new.state != vsstOffline) ANSWER0("17 iface can only be changed when in state OFFLINE");
if (!strcasecmp(argv[n+1], "ANY")) {
new.local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
} else {
host = gethostbyname(argv[n+1]);
if (host == NULL || (host->h_addr_list)[0] == NULL)
ANSWER1("13 unknown host or IP number: '%s'", argv[n+1]);
memcpy(&(new.local_addr.sin_addr.s_addr), host->h_addr_list[0],
sizeof(new.local_addr.sin_addr.s_addr));
}
got_host = 1;
} else if (! strcasecmp(argv[n], "port")) { /* port */
if (new.state != vsstOffline) ANSWER0("17 port can only be changed when in state OFFLINE");
port = get_port(argv[n+1]);
if (port < 0) ANSWER0("13 port must be valid TCP port");
new.local_addr.sin_port = htons(port);
} else if (! strcasecmp(argv[n], "prot")) { /* prot */
struct protocol_info *pi = find_protocol_info(-1, argv[n+1]);
if (! pi || ! pi->support_client_side) ANSWER1("13 not a valid protocol: '%s'", argv[n+1]);
if (is_tls_protocol(pi->type) && new.starttls_type != starttls_off) {
ANSWER0("25 not allowed with STARTTLS setting");
}
new.prot = pi->type;
port = pi->port;
} else if (! strcasecmp(argv[n], "capa")) { /* capa */
if (!strcasecmp(argv[n+1], "error")) {
new.capa_type = capa_error;
new.capa_ptr = NULL;
} else if (!strcasecmp(argv[n+1], "none")) {
new.capa_type = capa_none;
new.capa_ptr = NULL;
} else if (!strcasecmp(argv[n+1], "default")) {
new.capa_type = capa_default;
new.capa_ptr = NULL;
} else {
struct capa *c;
int found=0;
for (c=first_capa; c; c=c->next) {
if (!strcasecmp(argv[n+1], c->name)) {
new.capa_type = capa_user;
new.capa_ptr = c;
found = 1;
break;
}
}
if (!found) ANSWER1("13 no capa '%s' found", argv[n+1]);
}
} else if (! strcasecmp(argv[n], "namespace")) { /* namespace */
if (!valid_id(argv[n+1])) ANSWER0("13 invalid chars in namespace (must be [a-zA-Z0-9._-]*)");
len = strlcpy(new.namespace, argv[n+1], sizeof(new.namespace));
if (len >= sizeof(new.namespace)) ANSWER0("13 namespace too long");
} else if (! strcasecmp(argv[n], "bannerok")) { /* banner */
len = strlcpy(new.banner_ok, argv[n+1], sizeof(new.banner_ok));
if (len >= sizeof(new.banner_ok)) ANSWER0("13 banner too long");
} else if (! strcasecmp(argv[n], "bannererr")) { /* banner */
len = strlcpy(new.banner_err, argv[n+1], sizeof(new.banner_err));
if (len >= sizeof(new.banner_err)) ANSWER0("13 banner too long");
} else if (! strcasecmp(argv[n], "starttls")) { /* starttls */
#if USE_TLS
if (! strcasecmp(argv[n+1], "off")) {
new.starttls_type = starttls_off;
} else if (! strcasecmp(argv[n+1], "optional")) {
if (is_tls_protocol(new.prot)) {
ANSWER0("24 not allowed with TLS protocol");
} else {
new.starttls_type = starttls_optional;
}
} else if (! strcasecmp(argv[n+1], "force")) {
if (is_tls_protocol(new.prot)) {
ANSWER0("24 not allowed with TLS protocol");
} else {
new.starttls_type = starttls_force;
}
} else {
ANSWER0("23 unknown keyword");
}
#else
ANSWER0("26 setting not allowed because TLS not compiled in");
#endif
} else if (! strcasecmp(argv[n], "state")) { /* state */
struct virt_serv_state_info *ssi = find_virt_serv_state_info(-1, argv[n+1]);
if (! ssi || ssi->type == vsstFree) ANSWER1("13 unknown state: '%s'", argv[n+1]);
new.state = ssi->type;
} else {
ANSWER1("23 unknown keyword: '%s'", argv[n]);
}
}
if (!got_host) ANSWER0("16 missing iface option");
if (!new.local_addr.sin_port) new.local_addr.sin_port = htons(port);
sip = find_virt_serv_by_ip(&new.local_addr);
if (sip && sip != vs) ANSWER0("13 ip/port combination already used for another vserv");
if (vs->state != new.state) {
struct virt_serv_state_info *o = find_virt_serv_state_info(vs->state, NULL);
struct virt_serv_state_info *n = find_virt_serv_state_info(new.state, NULL);
if (o->listen && !n->listen) { /* stop listening */
(void) close(vs->fd);
(void) close_listen_port(&(vs->local_addr));
} else if (!o->listen && n->listen) { /* start listening */
new.fd = bind_and_listen(&(new.local_addr));
if (new.fd < 0) ANSWER0("31 bind error");
}
}
/* setup or destroy SSL context if needed */
#if USE_TLS
{
int need_tls = is_tls_protocol(new.prot) |
(new.starttls_type != starttls_off);
if (need_tls && new.ssl_ctx == NULL) {
DEBUG0(DG_TLS, "ctrl_cmd_vserv", "Create TLS context because of changed config");
if (! io_tls_vserv_setup(conf.tlsdir, conf.allowsslv2, &new)) {
ANSWER0("35 TLS error, see log file");
}
}
if (!need_tls && new.ssl_ctx != NULL) {
DEBUG0(DG_TLS, "ctrl_cmd_vserv", "Destroy TLS context because of changed config");
io_tls_vserv_shutdown(&new);
}
}
#endif
memcpy(vs, &new, sizeof(new));
got_conf=1;
} else if (!strcasecmp(argv[1], "show")) {
if (argc != 3) ANSWER0("20 too many or too few arguments");
vs = get_virt_serv(argv[2], 0);
if (! vs) ANSWER1("13 unknown vserv: '%s'", argv[2]);
} else if (!strcasecmp(argv[1], "del")) {
if (argc != 3) ANSWER0("20 too many or too few arguments");
vs = get_virt_serv(argv[2], 0);
if (! vs) ANSWER1("13 unknown vserv: '%s'", argv[2]);
if (vs->state != vsstOffline) close(vs->fd);
vs->state = vsstFree;
/* XLOG-DOC:ADM:0168:config_vserv_del
* A 'vserv del' command was executed. */
xlog_printf(xlog_adm, 0x0168, "config_vserv_del id=%s", vs->id);
ANSWER1("00 virt_serv '%s' deleted", vs->id);
} else if (!strcasecmp(argv[1], "flush")) {
if (argc != 2) ANSWER0("20 too many arguments");
flush_virt_serv(vsstFree);
/* XLOG-DOC:ADM:0167:config_vserv_flush
* A 'vserv flush' command was executed. */
xlog_printf(xlog_adm, 0x0167, "config_vserv_flush");
ANSWER0("00 all virtual servers flushed");
} else if (!strcasecmp(argv[1], "list")) {
if (argc != 2) ANSWER0("20 too many arguments");
ANSWER1("00 vservs:%s", list_virt_serv());
} else {
ANSWER1("22 unknown subcomand: '%s'", argv[1]);
}
{
struct protocol_info *p = find_protocol_info(vs->prot, NULL);
struct virt_serv_state_info *s = find_virt_serv_state_info(vs->state, NULL);
if (! p) ANSWER0("99 internal error (protocol_info)");
if (! s) ANSWER0("99 internal error (virt_serv_state_info)");
snprintf(answer, MAXBUF, "00 vserv conf \"%s\" iface \"%s\" port \"%d\" prot \"%s\" capa \"%s\" starttls \"%s\" state \"%s\" namespace \"%s\" bannerok \"%s\" bannererr \"%s\"",
vs->id, /* virtual server id */
print_ip(&(vs->local_addr), NULL), /* IP address */
ntohs(vs->local_addr.sin_port), /* TCP port */
p->name, /* protocol name */
capa_name(vs), /* capability list */
starttls_name(vs), /* starttls option */
s->name, /* state */
vs->namespace, /* namespace name */
vs->banner_ok, /* banner (+OK) */
vs->banner_err); /* banner (-ERR) */
/* XLOG-DOC:ADM:0166:config_vserv_conf
* A 'vserv conf' command was executed. */
if (got_conf) xlog_printf(xlog_adm, 0x0166, "config_vserv_conf id=%s iface=%s port=%d prot=%s capa=%s starttls=%s state=%s namespace=%s bannerok='%s' bannererr='%s'",
vs->id, /* virtual server id */
print_ip(&(vs->local_addr), NULL), /* IP address */
ntohs(vs->local_addr.sin_port), /* TCP port */
p->name, /* protocol name */
capa_name(vs), /* capability list */
starttls_name(vs), /* starttls option */
s->name, /* state */
vs->namespace, /* namespace name */
vs->banner_ok, /* banner (+OK) */
vs->banner_err); /* banner (-ERR) */
}
}
/*****************************************************************************
ctrl_cmd_capa()
*****************************************************************************/
void
ctrl_cmd_capa(char *argv[], int argc, char *answer)
{
if (! strcasecmp(argv[1], "list")) {
struct capa *c;
if (argc != 2) ANSWER0("20 too many arguments");
strlcpy(answer, "00 capas: ", MAXBUF);
for (c=first_capa; c; c=c->next) {
strlcat(answer, c->name, MAXBUF);
if (c->next) strlcat(answer, ", ", MAXBUF);
}
} else if (! strcasecmp(argv[1], "load")) {
struct capa *c;
int new = 0;
if (argc != 3) ANSWER0("20 second argument (capa id) is missing");
if (!valid_id(argv[2])) ANSWER0("13 invalid chars in id (must be [a-zA-Z0-9._-]*)");
for (c=first_capa; c; c=c->next) {
if (!strcmp(c->name, argv[2])) goto addtext;
}
c = malloc(sizeof(struct capa));
if (! c) ANSWER0("32 out of memory");
c->next = first_capa;
first_capa = c;
strlcpy(c->name, argv[2], MAXLEN_ID);
new = 1;
addtext:
{
FILE *f;
char buf[MAXBUF];
char readbuf[MAXLEN_CAPA+1];
int offset = 0;
snprintf(buf, sizeof(buf), "%s/%s", conf.capadir, argv[2]);
f = fopen(buf, "r");
if (! f) {
if (new) { /* if we created a new capa entry, del it after an error */
first_capa = c->next;
free(c);
}
ANSWER1("33 error opening '%s'", buf);
}
/* read in capa file converting LF to CRLF as we go */
while (fgets(readbuf + offset, sizeof(readbuf) - offset - 2, f)) {
int len = strlen(readbuf + offset);
readbuf[offset+len-1] = '\r';
readbuf[offset+len] = '\n';
readbuf[offset+len+1] = '\0';
offset += len+1;
if (offset >= sizeof(readbuf) - 2) {
if (new) { /* if we created a new capa entry, del it */
first_capa = c->next;
free(c);
}
ANSWER1("13 capa '%s' too long to be loaded", argv[2]);
}
}
fclose(f);
strlcpy(c->text, readbuf, sizeof(c->text));
}
/* XLOG-DOC:ADM:0169:config_capa_load
* A 'capa load' command was executed. */
xlog_printf(xlog_adm, 0x0169, "config_capa_load id=%s", argv[2]);
ANSWER1("00 capa '%s' loaded", argv[2]);
} else if (! strcasecmp(argv[1], "del")) {
struct capa *c, **d;
if (argc != 3) ANSWER0("20 second argument (capa id) is missing");
if (!valid_id(argv[2])) ANSWER0("13 invalid chars in id (must be [a-zA-Z0-9._-]*)");
for (c=first_capa, d=&first_capa; c; d=&(c->next), c=c->next) {
if (!strcmp(c->name, argv[2])) {
if (find_virt_serv_by_capa(c)) ANSWER1("18 capa '%s' is in use", argv[2]);
*d = c->next;
free(c);
/* XLOG-DOC:ADM:016a:config_capa_del
* A 'capa del' command was executed. */
xlog_printf(xlog_adm, 0x016a, "config_capa_del id=%s", argv[2]);
ANSWER1("00 capa '%s' deleted", argv[2]);
}
}
ANSWER1("00 no capa '%s' found", argv[2]);
} else if (! strcasecmp(argv[1], "show")) {
struct capa *c;
if (argc != 3) ANSWER0("20 second argument (capa id) is missing");
if (!valid_id(argv[2])) ANSWER0("13 invalid chars in id (must be [a-zA-Z0-9._-]*)");
for (c=first_capa; c; c=c->next) {
if (!strcmp(c->name, argv[2])) {
char *x;
snprintf(answer, MAXBUF, "00 capa %s: %s", argv[2], c->text);
for (x=answer; *x; x++) {
if (*x == '\r') *x=',';
if (*x == '\n') *x=' ';
}
answer[strlen(answer)-2] = '\0';
return;
}
}
ANSWER1("00 no capa '%s' found", argv[2]);
} else {
ANSWER1("22 unknown subcomand: '%s'", argv[1]);
}
}
/*****************************************************************************
ctrl_cmd_prng()
*****************************************************************************/
void
ctrl_cmd_prng(char *argv[], int argc, char *answer)
{
#ifndef USE_TLS
ANSWER0("26 prng command only allowed when TLS is compiled in");
#else
int bytes = ((argc == 3) ? atoi(argv[2]) : DEFAULT_RANDOM_BYTES);
DEBUG2(DG_CTRL, "ctrl_cmd_prng", "seed prng filename=%s bytes=%d", argv[1], bytes);
if (!io_tls_seed_prng(argv[1], bytes)) {
/* XLOG-DOC:SOS:0203:tls_seed_failed
* Initialization of the TLS pseudo random number generator failed.
* This is serious because it could potentially mean that there is
* no security at all. */
xlog_printf(xlog_sos, 0x0203, "tls_seed_failed");
ANSWER0("33 tls seeding failed");
}
ANSWER0("00 ok");
#endif
}
/*****************************************************************************
pdm_find_module()
*****************************************************************************/
struct pdm_module *
pdm_find_module(char *id)
{
struct pdm_module *m;
for (m=first_pdm_module; m; m=m->next) {
if (!strcmp(id, m->id)) return m;
}
return NULL;
}
/*****************************************************************************
ctrl_cmd_pdm()
*****************************************************************************/
void
ctrl_cmd_pdm(char *argv[], int argc, char *answer)
{
if (! strcasecmp(argv[1], "list")) {
struct pdm_module *m;
if (argc != 2) ANSWER0("20 too many arguments");
strlcpy(answer, "00 modules: ", MAXBUF);
for (m=first_pdm_module; m; m=m->next) {
strlcat(answer, m->id, MAXBUF);
if (m->next) strlcat(answer, ", ", MAXBUF);
}
} else if (! strcasecmp(argv[1], "add")) {
struct pdm_module *m, *mo;
if (argc < 4) ANSWER0("20 not enough arguments");
m = pdm_find_module(argv[2]);
if (m) ANSWER1("13 module '%s' already exists", argv[2]);
m = pdm_attach(conf.pdmdir, argc-2, argv+2);
if (! m) ANSWER1("40 loading of module libpdm_%s.so failed", argv[3]);
if (first_pdm_module) {
for (mo = first_pdm_module; mo->next; mo=mo->next);
m->next = NULL;
mo->next = m;
} else {
first_pdm_module = m;
}
/* XLOG-DOC:ADM:016b:config_pdm_add
* A 'pdm add' command was executed. */
xlog_printf(xlog_adm, 0x016b, "config_pdm_add id=%s", argv[2]);
ANSWER0("00 module loaded");
} else if (! strcasecmp(argv[1], "del")) {
struct pdm_module **mp, *m;
if (argc != 3) ANSWER0("20 wrong number of arguments");
if (! first_pdm_module) {
ANSWER1("15 module '%s' doesn't exist", argv[2]);
} else if (! strcmp(first_pdm_module->id, argv[2])) {
m = first_pdm_module;
first_pdm_module = first_pdm_module->next;
} else {
for (mp = &first_pdm_module; (*mp)->next; mp=&((*mp)->next)) {
DEBUG2(DG_CTRL, "ctrl_cmd_pdm", "list: %s %s", (*mp)->id, (*mp)->next->id);
if (! strcmp((*mp)->next->id, argv[2])) {
m = (*mp)->next;
DEBUG2(DG_CTRL, "ctrl_cmd_pdm", "found '%s' (next is '%s')", m->id, m->next ? m->next->id : "NULL");
(*mp)->next = m->next;
pdm_detach(m);
ANSWER0("00 module deleted");
}
}
ANSWER1("15 module '%s' doesn't exist", argv[2]);
}
pdm_detach(m);
/* XLOG-DOC:ADM:016c:config_pdm_del
* A 'pdm del' command was executed. */
xlog_printf(xlog_adm, 0x016c, "config_pdm_del id=%s", argv[2]);
ANSWER0("00 module deleted");
} else if (! strcasecmp(argv[1], "reload")) {
struct pdm_module *m;
if (argc != 3) ANSWER0("20 wrong number of arguments");
m = pdm_find_module(argv[2]);
if (! m) ANSWER1("15 module '%s' doesn't exist", argv[2]);
if (pdm_call_reload(m)) {
/* XLOG-DOC:ADM:016d:config_pdm_reload
* A 'pdm reload' command was executed. */
xlog_printf(xlog_adm, 0x016d, "config_pdm_reload id=%s", argv[2]);
ANSWER1("00 module '%s' reloaded", argv[2]);
}
ANSWER1("34 module '%s' reload failed", argv[2]);
} else if (! strcasecmp(argv[1], "flush")) {
struct pdm_module *m, *mf;
if (argc != 2) ANSWER0("20 too many arguments");
for (m=first_pdm_module; m; m=mf) {
mf = m->next;
pdm_detach(m);
}
first_pdm_module = NULL;
/* XLOG-DOC:ADM:016e:config_pdm_flush
* A 'pdm flush' command was executed. */
xlog_printf(xlog_adm, 0x016e, "config_pdm_flush");
ANSWER0("00 all modules unloaded");
} else if (! strcasecmp(argv[1], "show")) {
struct pdm_module *m;
if (argc != 3) ANSWER0("20 wrong number of arguments");
m = pdm_find_module(argv[2]);
if (! m) ANSWER1("15 module '%s' doesn't exist", argv[2]);
ANSWER2("00 module add %s %s", argv[2], pdm_get_args(m));
} else{
ANSWER1("22 unknown subcomand: '%s'", argv[1]);
}
}
/** THE END *****************************************************************/
syntax highlighted by Code2HTML, v. 0.9.1