summaryrefslogtreecommitdiff
path: root/contrib/bind/bin/named/ns_ctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/bind/bin/named/ns_ctl.c')
-rw-r--r--contrib/bind/bin/named/ns_ctl.c866
1 files changed, 866 insertions, 0 deletions
diff --git a/contrib/bind/bin/named/ns_ctl.c b/contrib/bind/bin/named/ns_ctl.c
new file mode 100644
index 000000000000..259093b11c0d
--- /dev/null
+++ b/contrib/bind/bin/named/ns_ctl.c
@@ -0,0 +1,866 @@
+#if !defined(lint) && !defined(SABER)
+static const char rcsid[] = "$Id: ns_ctl.c,v 8.28 1999/10/13 16:39:04 vixie Exp $";
+#endif /* not lint */
+
+/*
+ * Copyright (c) 1997-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
+ * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+/* Extern. */
+
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <resolv.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <isc/eventlib.h>
+#include <isc/logging.h>
+#include <isc/memcluster.h>
+
+#include "port_after.h"
+
+#include "named.h"
+
+/* Defs. */
+
+#define CONTROL_FOUND 0x0001 /* for mark and sweep. */
+#define MAX_STR_LEN 500
+
+struct control {
+ LINK(struct control) link;
+ enum { t_dead, t_inet, t_unix } type;
+ struct ctl_sctx *sctx;
+ u_int flags;
+ union {
+ struct {
+ struct sockaddr_in in;
+ ip_match_list allow;
+ } v_inet;
+ struct {
+ struct sockaddr_un un;
+ mode_t mode;
+ uid_t owner;
+ gid_t group;
+ } v_unix;
+ } var;
+};
+
+/* Forward. */
+
+static struct ctl_sctx *mksrvr(control, const struct sockaddr *, size_t);
+static control new_control(void);
+static void free_control(controls *, control);
+static void free_controls(controls *);
+static int match_control(control, control);
+static control find_control(controls, control);
+static void propagate_changes(const control, control);
+static void install(control);
+static void install_inet(control);
+static void install_unix(control);
+static void logger(enum ctl_severity, const char *fmt, ...);
+static void verb_connect(struct ctl_sctx *, struct ctl_sess *,
+ const struct ctl_verb *,
+ const char *, u_int, void *, void *);
+static void verb_getpid(struct ctl_sctx *, struct ctl_sess *,
+ const struct ctl_verb *,
+ const char *, u_int, void *, void *);
+static void getpid_closure(struct ctl_sctx *, struct ctl_sess *,
+ void *);
+static void verb_status(struct ctl_sctx *, struct ctl_sess *,
+ const struct ctl_verb *,
+ const char *, u_int, void *, void *);
+static void status_closure(struct ctl_sctx *, struct ctl_sess *,
+ void *);
+static void verb_stop(struct ctl_sctx *, struct ctl_sess *,
+ const struct ctl_verb *,
+ const char *, u_int, void *, void *);
+static void verb_exec(struct ctl_sctx *, struct ctl_sess *,
+ const struct ctl_verb *,
+ const char *, u_int, void *, void *);
+static void verb_reload(struct ctl_sctx *, struct ctl_sess *,
+ const struct ctl_verb *,
+ const char *, u_int, void *, void *);
+static void verb_reconfig(struct ctl_sctx *, struct ctl_sess *,
+ const struct ctl_verb *,
+ const char *, u_int, void *, void *);
+static void verb_dumpdb(struct ctl_sctx *, struct ctl_sess *,
+ const struct ctl_verb *,
+ const char *, u_int, void *, void *);
+static void verb_stats(struct ctl_sctx *, struct ctl_sess *,
+ const struct ctl_verb *,
+ const char *, u_int, void *, void *);
+static void verb_trace(struct ctl_sctx *, struct ctl_sess *,
+ const struct ctl_verb *,
+ const char *, u_int, void *, void *);
+static void trace_closure(struct ctl_sctx *, struct ctl_sess *,
+ void *);
+static void verb_notrace(struct ctl_sctx *, struct ctl_sess *,
+ const struct ctl_verb *,
+ const char *, u_int, void *, void *);
+static void verb_querylog(struct ctl_sctx *, struct ctl_sess *,
+ const struct ctl_verb *,
+ const char *, u_int, void *, void *);
+static void verb_help(struct ctl_sctx *, struct ctl_sess *,
+ const struct ctl_verb *,
+ const char *, u_int, void *, void *);
+static void verb_quit(struct ctl_sctx *, struct ctl_sess *,
+ const struct ctl_verb *,
+ const char *, u_int, void *, void *);
+
+/* Private data. */
+
+static controls server_controls;
+
+static struct ctl_verb verbs[] = {
+ { "", verb_connect, ""},
+ { "getpid", verb_getpid, "getpid"},
+ { "status", verb_status, "status"},
+ { "stop", verb_stop, "stop"},
+ { "exec", verb_exec, "exec"},
+ { "reload", verb_reload, "reload [zone] ..."},
+ { "reconfig", verb_reconfig, "reconfig (just sees new/gone zones)"},
+ { "dumpdb", verb_dumpdb, "dumpdb"},
+ { "stats", verb_stats, "stats"},
+ { "trace", verb_trace, "trace [level]"},
+ { "notrace", verb_notrace, "notrace"},
+ { "querylog", verb_querylog, "querylog"},
+ { "qrylog", verb_querylog, "qrylog"},
+ { "help", verb_help, "help"},
+ { "quit", verb_quit, "quit"},
+ { NULL, NULL, NULL}
+};
+
+/* Public functions. */
+
+void
+ns_ctl_initialize(void) {
+ INIT_LIST(server_controls);
+}
+
+void
+ns_ctl_shutdown(void) {
+ if (!EMPTY(server_controls))
+ free_controls(&server_controls);
+}
+
+void
+ns_ctl_defaults(controls *list) {
+ ns_ctl_add(list, ns_ctl_new_unix(_PATH_NDCSOCK, 0600, 0, 0));
+}
+
+void
+ns_ctl_add(controls *list, control new) {
+ if (!find_control(*list, new))
+ APPEND(*list, new, link);
+}
+
+control
+ns_ctl_new_inet(struct in_addr saddr, u_int sport, ip_match_list allow) {
+ control new = new_control();
+
+ INIT_LINK(new, link);
+ new->type = t_inet;
+ memset(&new->var.v_inet.in, 0, sizeof new->var.v_inet.in);
+ new->var.v_inet.in.sin_family = AF_INET;
+ new->var.v_inet.in.sin_addr = saddr;
+ new->var.v_inet.in.sin_port = sport;
+ new->var.v_inet.allow = allow;
+ return (new);
+}
+
+control
+ns_ctl_new_unix(char *path, mode_t mode, uid_t owner, gid_t group) {
+ control new = new_control();
+
+ INIT_LINK(new, link);
+ new->type = t_unix;
+ memset(&new->var.v_unix.un, 0, sizeof new->var.v_unix.un);
+ new->var.v_unix.un.sun_family = AF_UNIX;
+ strncpy(new->var.v_unix.un.sun_path, path,
+ sizeof new->var.v_unix.un.sun_path - 1);
+ new->var.v_unix.mode = mode;
+ new->var.v_unix.owner = owner;
+ new->var.v_unix.group = group;
+ return (new);
+}
+
+void
+ns_ctl_install(controls *new) {
+ control ctl, old, next;
+
+ /* Find all the controls which aren't new or deleted. */
+ for (ctl = HEAD(server_controls); ctl != NULL; ctl = NEXT(ctl, link))
+ ctl->flags &= ~CONTROL_FOUND;
+ for (ctl = HEAD(*new); ctl != NULL; ctl = next) {
+ next = NEXT(ctl, link);
+ old = find_control(server_controls, ctl);
+ if (old != NULL) {
+ old->flags |= CONTROL_FOUND;
+ propagate_changes(ctl, old);
+ if (old->sctx == NULL)
+ free_control(&server_controls, old);
+ free_control(new, ctl);
+ }
+ }
+
+ /* Destroy any old controls which weren't found. */
+ for (ctl = HEAD(server_controls); ctl != NULL; ctl = next) {
+ next = NEXT(ctl, link);
+ if ((ctl->flags & CONTROL_FOUND) == 0)
+ free_control(&server_controls, ctl);
+ }
+
+ /* Add any new controls which were found. */
+ for (ctl = HEAD(*new); ctl != NULL; ctl = next) {
+ next = NEXT(ctl, link);
+ APPEND(server_controls, ctl, link);
+ install(ctl);
+ if (ctl->sctx == NULL)
+ free_control(&server_controls, ctl);
+ }
+}
+
+/* Private functions. */
+
+static struct ctl_sctx *
+mksrvr(control ctl, const struct sockaddr *sa, size_t salen) {
+ return (ctl_server(ev, sa, salen, verbs, 500, 222,
+ 600, 5, 10, logger, ctl));
+}
+
+static control
+new_control(void) {
+ control new = memget(sizeof *new);
+
+ if (new == NULL)
+ panic("memget failed in new_control()", NULL);
+ new->type = t_dead;
+ new->sctx = NULL;
+ return (new);
+}
+
+static void
+free_control(controls *list, control this) {
+ int was_live = 0;
+ struct stat sb;
+
+ if (this->sctx != NULL) {
+ ctl_endserver(this->sctx);
+ this->sctx = NULL;
+ was_live = 1;
+ }
+ switch (this->type) {
+ case t_inet:
+ if (this->var.v_inet.allow != NULL) {
+ free_ip_match_list(this->var.v_inet.allow);
+ this->var.v_inet.allow = NULL;
+ }
+ break;
+ case t_unix:
+ /* XXX Race condition. */
+ if (was_live &&
+ stat(this->var.v_unix.un.sun_path, &sb) == 0 &&
+ (S_ISSOCK(sb.st_mode) || S_ISFIFO(sb.st_mode))) {
+ /* XXX Race condition. */
+ unlink(this->var.v_unix.un.sun_path);
+ }
+ break;
+ default:
+ panic("impossible type in free_control", NULL);
+ /* NOTREACHED */
+ }
+ UNLINK(*list, this, link);
+ memput(this, sizeof *this);
+}
+
+static void
+free_controls(controls *list) {
+ control ctl, next;
+
+ for (ctl = HEAD(*list); ctl != NULL; ctl = next) {
+ next = NEXT(ctl, link);
+ free_control(list, ctl);
+ }
+ INIT_LIST(*list);
+}
+
+static int
+match_control(control l, control r) {
+ int match = 1;
+
+ if (l->type != r->type)
+ match = 0;
+ else
+ switch (l->type) {
+ case t_inet:
+ if (l->var.v_inet.in.sin_family !=
+ r->var.v_inet.in.sin_family ||
+ l->var.v_inet.in.sin_port !=
+ r->var.v_inet.in.sin_port ||
+ l->var.v_inet.in.sin_addr.s_addr !=
+ r->var.v_inet.in.sin_addr.s_addr)
+ match = 0;
+ break;
+ case t_unix:
+ if (l->var.v_unix.un.sun_family !=
+ r->var.v_unix.un.sun_family ||
+ strcmp(l->var.v_unix.un.sun_path,
+ r->var.v_unix.un.sun_path) != 0)
+ match = 0;
+ break;
+ default:
+ panic("impossible type in match_control", NULL);
+ /* NOTREACHED */
+ }
+ ns_debug(ns_log_config, 20, "match_control(): %d", match);
+ return (match);
+}
+
+static control
+find_control(controls list, control new) {
+ control ctl;
+
+ for (ctl = HEAD(list); ctl != NULL; ctl = NEXT(ctl, link))
+ if (match_control(ctl, new))
+ return (ctl);
+ return (NULL);
+}
+
+static void
+propagate_changes(const control diff, control base) {
+ int need_install = 0;
+
+ switch (base->type) {
+ case t_inet:
+ if (base->var.v_inet.allow != NULL)
+ free_ip_match_list(base->var.v_inet.allow);
+ base->var.v_inet.allow = diff->var.v_inet.allow;
+ diff->var.v_inet.allow = NULL;
+ need_install++;
+ break;
+ case t_unix:
+ if (base->var.v_unix.mode != diff->var.v_unix.mode) {
+ base->var.v_unix.mode = diff->var.v_unix.mode;
+ need_install++;
+ }
+ if (base->var.v_unix.owner != diff->var.v_unix.owner) {
+ base->var.v_unix.owner = diff->var.v_unix.owner;
+ need_install++;
+ }
+ if (base->var.v_unix.group != diff->var.v_unix.group) {
+ base->var.v_unix.group = diff->var.v_unix.group;
+ need_install++;
+ }
+ break;
+ default:
+ panic("impossible type in ns_ctl::propagate_changes", NULL);
+ /* NOTREACHED */
+ }
+ if (need_install)
+ install(base);
+}
+
+static void
+install(control ctl) {
+ switch (ctl->type) {
+ case t_inet:
+ install_inet(ctl);
+ break;
+ case t_unix:
+ install_unix(ctl);
+ break;
+ default:
+ panic("impossible type in ns_ctl::install", NULL);
+ /* NOTREACHED */
+ }
+}
+
+static void
+install_inet(control ctl) {
+ if (ctl->sctx == NULL) {
+ ctl->sctx = mksrvr(ctl,
+ (struct sockaddr *)&ctl->var.v_inet.in,
+ sizeof ctl->var.v_inet.in);
+ }
+}
+
+/*
+ * Unattach an old unix domain socket if it exists.
+ */
+static void
+unattach(control ctl) {
+ int s;
+ struct stat sb;
+
+ s = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (s < 0) {
+ ns_warning(ns_log_config,
+ "unix control \"%s\" socket failed: %s",
+ ctl->var.v_unix.un.sun_path,
+ strerror(errno));
+ return;
+ }
+
+ if (stat(ctl->var.v_unix.un.sun_path, &sb) < 0) {
+ switch (errno) {
+ case ENOENT: /* We exited cleanly last time */
+ break;
+ default:
+ ns_warning(ns_log_config,
+ "unix control \"%s\" stat failed: %s",
+ ctl->var.v_unix.un.sun_path,
+ strerror(errno));
+ break;
+ }
+ goto cleanup;
+ }
+
+ if (!(S_ISSOCK(sb.st_mode) || S_ISFIFO(sb.st_mode))) {
+ ns_warning(ns_log_config, "unix control \"%s\" not socket",
+ ctl->var.v_unix.un.sun_path);
+ goto cleanup;
+ }
+
+ if (connect(s, (struct sockaddr *)&ctl->var.v_unix.un,
+ sizeof ctl->var.v_unix.un) < 0) {
+ switch (errno) {
+ case ECONNREFUSED:
+ case ECONNRESET:
+ if (unlink(ctl->var.v_unix.un.sun_path) < 0)
+ ns_warning(ns_log_config,
+ "unix control \"%s\" unlink failed: %s",
+ ctl->var.v_unix.un.sun_path,
+ strerror(errno));
+ break;
+ default:
+ ns_warning(ns_log_config,
+ "unix control \"%s\" connect failed: %s",
+ ctl->var.v_unix.un.sun_path,
+ strerror(errno));
+ break;
+ }
+ }
+ cleanup:
+ close(s);
+}
+
+static void
+install_unix(control ctl) {
+ if (ctl->sctx == NULL) {
+ unattach(ctl);
+ ctl->sctx = mksrvr(ctl,
+ (struct sockaddr *)&ctl->var.v_unix.un,
+ sizeof ctl->var.v_unix.un);
+ }
+ if (ctl->sctx != NULL) {
+ /* XXX Race condition. */
+ if (chmod(ctl->var.v_unix.un.sun_path,
+ ctl->var.v_unix.mode) < 0) {
+ ns_warning(ns_log_config, "chmod(\"%s\", 0%03o): %s",
+ ctl->var.v_unix.un.sun_path,
+ ctl->var.v_unix.mode,
+ strerror(errno));
+ }
+ if (chown(ctl->var.v_unix.un.sun_path,
+ ctl->var.v_unix.owner,
+ ctl->var.v_unix.group) < 0) {
+ ns_warning(ns_log_config, "chown(\"%s\", %d, %d): %s",
+ ctl->var.v_unix.un.sun_path,
+ ctl->var.v_unix.owner,
+ ctl->var.v_unix.group,
+ strerror(errno));
+ }
+ }
+}
+
+static void
+logger(enum ctl_severity ctlsev, const char *format, ...) {
+ va_list args;
+ int logsev;
+
+ switch (ctlsev) {
+ case ctl_debug: logsev = log_debug(5); break;
+ case ctl_warning: logsev = log_warning; break;
+ case ctl_error: logsev = log_error; break;
+ default: panic("invalid ctlsev in logger", NULL);
+ }
+ if (!log_ctx_valid)
+ return;
+ va_start(args, format);
+ log_vwrite(log_ctx, ns_log_control, logsev, format, args);
+ va_end(args);
+}
+
+static void
+verb_connect(struct ctl_sctx *ctl, struct ctl_sess *sess,
+ const struct ctl_verb *verb, const char *rest,
+ u_int respflags, void *respctx, void *uctx)
+{
+ const struct sockaddr *sa = (struct sockaddr *)respctx;
+ control nsctl = (control)uctx;
+
+ if (sa->sa_family == AF_INET) {
+ const struct sockaddr_in *in = (struct sockaddr_in *)sa;
+ const ip_match_list acl = nsctl->var.v_inet.allow;
+
+ if (!ip_address_allowed(acl, in->sin_addr)) {
+ ctl_response(sess, 502, "Permission denied.",
+ CTL_EXIT, NULL, NULL, NULL, NULL, 0);
+ return;
+ }
+ }
+ ctl_response(sess, 220, server_options->version, 0, NULL, NULL, NULL,
+ NULL, 0);
+}
+
+static void
+verb_getpid(struct ctl_sctx *ctl, struct ctl_sess *sess,
+ const struct ctl_verb *verb, const char *rest,
+ u_int respflags, void *respctx, void *uctx)
+{
+ char *msg = memget(MAX_STR_LEN);
+
+ if (msg == NULL) {
+ ctl_response(sess, 503, "(out of memory)", 0,
+ NULL, NULL, NULL, NULL, 0);
+ return;
+ }
+ sprintf(msg, "my pid is <%ld>", (long)getpid());
+ ctl_response(sess, 250, msg, 0, NULL, getpid_closure, msg, NULL, 0);
+}
+
+static void
+getpid_closure(struct ctl_sctx *sctx, struct ctl_sess *sess, void *uap) {
+ char *msg = uap;
+
+ memput(msg, MAX_STR_LEN);
+}
+
+enum state {
+ e_version = 0,
+ e_nzones,
+ e_debug,
+ e_xfersrun,
+ e_xfersdfr,
+ e_qserials,
+ e_qrylog,
+ e_priming,
+ e_loading,
+ e_finito
+};
+
+struct pvt_status {
+ enum state state;
+ char text[MAX_STR_LEN];
+};
+
+static void
+verb_status(struct ctl_sctx *ctl, struct ctl_sess *sess,
+ const struct ctl_verb *verb, const char *rest,
+ u_int respflags, void *respctx, void *uctx)
+{
+ struct pvt_status *pvt = ctl_getcsctx(sess);
+
+ if (pvt == NULL) {
+ pvt = memget(sizeof *pvt);
+ if (pvt == NULL) {
+ ctl_response(sess, 505, "(out of memory)",
+ 0, NULL, NULL, NULL, NULL, 0);
+ return;
+ }
+ pvt->state = e_version;
+ (void)ctl_setcsctx(sess, pvt);
+ }
+ switch (pvt->state++) {
+ case e_version:
+ strncpy(pvt->text, Version, sizeof pvt->text);
+ pvt->text[sizeof pvt->text - 1] = '\0';
+ break;
+ case e_nzones:
+ sprintf(pvt->text, "number of zones allocated: %d", nzones);
+ break;
+ case e_debug:
+ sprintf(pvt->text, "debug level: %d", debug);
+ break;
+ case e_xfersrun:
+ sprintf(pvt->text, "xfers running: %d", xfers_running);
+ break;
+ case e_xfersdfr:
+ sprintf(pvt->text, "xfers deferred: %d", xfers_deferred);
+ break;
+ case e_qserials:
+ sprintf(pvt->text, "soa queries in progress: %d",
+ qserials_running);
+ break;
+ case e_qrylog:
+ sprintf(pvt->text, "query logging is %s",
+ qrylog ? "ON" : "OFF");
+ break;
+ case e_priming:
+ sprintf(pvt->text, "server is %s priming",
+ priming ? "STILL" : "DONE");
+ break;
+ case e_loading:
+ sprintf(pvt->text, "server %s loading its configuration",
+ loading ? "IS" : "IS NOT");
+ break;
+ case e_finito:
+ return;
+ }
+ ctl_response(sess, 250, pvt->text,
+ (pvt->state == e_finito) ? 0 : CTL_MORE,
+ NULL, status_closure, NULL, NULL, 0);
+}
+
+static void
+status_closure(struct ctl_sctx *sctx, struct ctl_sess *sess, void *uap) {
+ struct pvt_status *pvt = ctl_getcsctx(sess);
+
+ memput(pvt, sizeof *pvt);
+ ctl_setcsctx(sess, NULL);
+}
+
+static void
+verb_stop(struct ctl_sctx *ctl, struct ctl_sess *sess,
+ const struct ctl_verb *verb, const char *rest,
+ u_int respflags, void *respctx, void *uctx)
+{
+ ns_need(main_need_exit);
+ ctl_response(sess, 250, "Shutdown initiated.", 0, NULL, NULL, NULL,
+ NULL, 0);
+}
+
+static void
+verb_exec(struct ctl_sctx *ctl, struct ctl_sess *sess,
+ const struct ctl_verb *verb, const char *rest,
+ u_int respflags, void *respctx, void *uctx)
+{
+ struct stat sb;
+
+ if (rest != NULL && *rest != '\0') {
+ if (stat(rest, &sb) < 0) {
+ ctl_response(sess, 503, strerror(errno),
+ 0, NULL, NULL, NULL, NULL, 0);
+ return;
+ }
+ saved_argv[0] = savestr(rest, 1); /* Never strfreed. */
+ }
+
+ if (stat(saved_argv[0], &sb) < 0) {
+ const char *save = strerror(errno);
+
+ ns_warning(ns_log_default, "can't exec, %s: %s",
+ saved_argv[0], save);
+ ctl_response(sess, 502, save, 0, NULL, NULL, NULL,
+ NULL, 0);
+ } else {
+ ns_need(main_need_restart);
+ ctl_response(sess, 250, "Restart initiated.", 0, NULL,
+ NULL, NULL, NULL, 0);
+ }
+}
+
+static void
+verb_reload(struct ctl_sctx *ctl, struct ctl_sess *sess,
+ const struct ctl_verb *verb, const char *rest,
+ u_int respflags, void *respctx, void *uctx)
+{
+ static const char spaces[] = " \t";
+ struct zoneinfo *zp;
+ char *tmp = NULL, *x;
+ const char *msg;
+ int class, code, success;
+
+ /* If there are no args, this is a classic reload of the config. */
+ if (rest == NULL || *rest == '\0') {
+ ns_need(main_need_reload);
+ code = 250;
+ msg = "Reload initiated.";
+ goto respond;
+ }
+
+ /* Look for optional zclass argument. Default is "in". */
+ tmp = savestr(rest, 1);
+ x = tmp + strcspn(tmp, spaces);
+ if (*x != '\0') {
+ *x++ = '\0';
+ x += strspn(x, spaces);
+ }
+ if (x == NULL || *x == '\0')
+ x = "in";
+ class = sym_ston(__p_class_syms, x, &success);
+ if (!success) {
+ code = 507;
+ msg = "unrecognized class";
+ goto respond;
+ }
+
+ /* Look for the zone, and do the right thing to it. */
+ zp = find_zone(tmp, class);
+ if (zp == NULL) {
+ code = 506;
+ msg = "Zone not found.";
+ goto respond;
+ }
+ switch (zp->z_type) {
+ case z_master:
+ ns_stopxfrs(zp);
+ /*FALLTHROUGH*/
+ case z_hint:
+ block_signals();
+ code = 251;
+ msg = deferred_reload_unsafe(zp);
+ unblock_signals();
+ break;
+ case z_slave:
+ case z_stub:
+ ns_stopxfrs(zp);
+ addxfer(zp);
+ code = 251;
+ msg = "Slave transfer queued.";
+ goto respond;
+ case z_forward:
+ case z_cache:
+ default:
+ msg = "Non reloadable zone.";
+ code = 507;
+ break;
+ }
+
+ respond:
+ ctl_response(sess, code, msg, 0, NULL, NULL, NULL, NULL, 0);
+ if (tmp != NULL)
+ freestr(tmp);
+}
+
+static void
+verb_reconfig(struct ctl_sctx *ctl, struct ctl_sess *sess,
+ const struct ctl_verb *verb, const char *rest,
+ u_int respflags, void *respctx, void *uctx)
+{
+ ns_need(main_need_reconfig);
+ ctl_response(sess, 250, "Reconfig initiated.",
+ 0, NULL, NULL, NULL, NULL, 0);
+}
+
+static void
+verb_dumpdb(struct ctl_sctx *ctl, struct ctl_sess *sess,
+ const struct ctl_verb *verb, const char *rest,
+ u_int respflags, void *respctx, void *uctx)
+{
+ ns_need(main_need_dump);
+ ctl_response(sess, 250, "Database dump initiated.", 0, NULL,
+ NULL, NULL, NULL, 0);
+}
+
+static void
+verb_stats(struct ctl_sctx *ctl, struct ctl_sess *sess,
+ const struct ctl_verb *verb, const char *rest,
+ u_int respflags, void *respctx, void *uctx)
+{
+ ns_need(main_need_statsdump);
+ ctl_response(sess, 250, "Statistics dump initiated.",
+ 0, NULL, NULL, NULL, NULL, 0);
+}
+
+static void
+verb_trace(struct ctl_sctx *ctl, struct ctl_sess *sess,
+ const struct ctl_verb *verb, const char *rest,
+ u_int respflags, void *respctx, void *uctx)
+{
+ int i = atoi(rest);
+ char *msg = memget(MAX_STR_LEN);
+
+ if (msg == NULL) {
+ ctl_response(sess, 503, "(out of memory)", 0,
+ NULL, NULL, NULL, NULL, 0);
+ return;
+ }
+ if (i > 0)
+ desired_debug = i;
+ else
+ desired_debug++;
+ ns_need(main_need_debug);
+ sprintf(msg, "Debug level: %d", desired_debug);
+ ctl_response(sess, 250, msg, 0, NULL, trace_closure, msg, NULL, 0);
+}
+
+static void
+trace_closure(struct ctl_sctx *sctx, struct ctl_sess *sess, void *uap) {
+ char *msg = uap;
+
+ memput(msg, MAX_STR_LEN);
+}
+
+static void
+verb_notrace(struct ctl_sctx *ctl, struct ctl_sess *sess,
+ const struct ctl_verb *verb, const char *rest,
+ u_int respflags, void *respctx, void *uctx)
+{
+ desired_debug = 0;
+ ns_need(main_need_debug);
+ ctl_response(sess, 250, "Debugging turned off.",
+ 0, NULL, NULL, NULL, NULL, 0);
+}
+
+static void
+verb_querylog(struct ctl_sctx *ctl, struct ctl_sess *sess,
+ const struct ctl_verb *verb, const char *rest,
+ u_int respflags, void *respctx, void *uctx)
+{
+ static const char on[] = "Query logging is now on.",
+ off[] = "Query logging is now off.";
+
+ toggle_qrylog();
+ ctl_response(sess, 250, qrylog ? on : off,
+ 0, NULL, NULL, NULL, NULL, 0);
+}
+
+static void
+verb_help(struct ctl_sctx *ctl, struct ctl_sess *sess,
+ const struct ctl_verb *verb, const char *rest,
+ u_int respflags, void *respctx, void *uctx)
+{
+ ctl_sendhelp(sess, 214);
+}
+
+static void
+verb_quit(struct ctl_sctx *ctl, struct ctl_sess *sess,
+ const struct ctl_verb *verb, const char *rest,
+ u_int respflags, void *respctx, void *uctx)
+{
+ ctl_response(sess, 221, "End of control session.", CTL_EXIT, NULL,
+ NULL, NULL, NULL, 0);
+}