/***************************************************************************** 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 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 "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; nh_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; nh_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 *****************************************************************/