1 #if !defined(lint) && !defined(SABER)
2 static const char rcsid[] = "$Id: ns_ctl.c,v 8.28 1999/10/13 16:39:04 vixie Exp $";
6 * Copyright (c) 1997-1999 by Internet Software Consortium.
8 * Permission to use, copy, modify, and distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
12 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
13 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
14 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
15 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
16 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
17 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
18 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
24 #include "port_before.h"
26 #include <sys/types.h>
27 #include <sys/param.h>
29 #include <sys/socket.h>
32 #include <netinet/in.h>
33 #include <arpa/nameser.h>
34 #include <arpa/inet.h>
48 #include <isc/eventlib.h>
49 #include <isc/logging.h>
50 #include <isc/memcluster.h>
52 #include "port_after.h"
58 #define CONTROL_FOUND 0x0001 /* for mark and sweep. */
59 #define MAX_STR_LEN 500
62 LINK(struct control) link;
63 enum { t_dead, t_inet, t_unix } type;
64 struct ctl_sctx *sctx;
68 struct sockaddr_in in;
72 struct sockaddr_un un;
82 static struct ctl_sctx *mksrvr(control, const struct sockaddr *, size_t);
83 static control new_control(void);
84 static void free_control(controls *, control);
85 static void free_controls(controls *);
86 static int match_control(control, control);
87 static control find_control(controls, control);
88 static void propagate_changes(const control, control);
89 static void install(control);
90 static void install_inet(control);
91 static void install_unix(control);
92 static void logger(enum ctl_severity, const char *fmt, ...);
93 static void verb_connect(struct ctl_sctx *, struct ctl_sess *,
94 const struct ctl_verb *,
95 const char *, u_int, void *, void *);
96 static void verb_getpid(struct ctl_sctx *, struct ctl_sess *,
97 const struct ctl_verb *,
98 const char *, u_int, void *, void *);
99 static void getpid_closure(struct ctl_sctx *, struct ctl_sess *,
101 static void verb_status(struct ctl_sctx *, struct ctl_sess *,
102 const struct ctl_verb *,
103 const char *, u_int, void *, void *);
104 static void status_closure(struct ctl_sctx *, struct ctl_sess *,
106 static void verb_stop(struct ctl_sctx *, struct ctl_sess *,
107 const struct ctl_verb *,
108 const char *, u_int, void *, void *);
109 static void verb_exec(struct ctl_sctx *, struct ctl_sess *,
110 const struct ctl_verb *,
111 const char *, u_int, void *, void *);
112 static void verb_reload(struct ctl_sctx *, struct ctl_sess *,
113 const struct ctl_verb *,
114 const char *, u_int, void *, void *);
115 static void verb_reconfig(struct ctl_sctx *, struct ctl_sess *,
116 const struct ctl_verb *,
117 const char *, u_int, void *, void *);
118 static void verb_dumpdb(struct ctl_sctx *, struct ctl_sess *,
119 const struct ctl_verb *,
120 const char *, u_int, void *, void *);
121 static void verb_stats(struct ctl_sctx *, struct ctl_sess *,
122 const struct ctl_verb *,
123 const char *, u_int, void *, void *);
124 static void verb_trace(struct ctl_sctx *, struct ctl_sess *,
125 const struct ctl_verb *,
126 const char *, u_int, void *, void *);
127 static void trace_closure(struct ctl_sctx *, struct ctl_sess *,
129 static void verb_notrace(struct ctl_sctx *, struct ctl_sess *,
130 const struct ctl_verb *,
131 const char *, u_int, void *, void *);
132 static void verb_querylog(struct ctl_sctx *, struct ctl_sess *,
133 const struct ctl_verb *,
134 const char *, u_int, void *, void *);
135 static void verb_help(struct ctl_sctx *, struct ctl_sess *,
136 const struct ctl_verb *,
137 const char *, u_int, void *, void *);
138 static void verb_quit(struct ctl_sctx *, struct ctl_sess *,
139 const struct ctl_verb *,
140 const char *, u_int, void *, void *);
144 static controls server_controls;
146 static struct ctl_verb verbs[] = {
147 { "", verb_connect, ""},
148 { "getpid", verb_getpid, "getpid"},
149 { "status", verb_status, "status"},
150 { "stop", verb_stop, "stop"},
151 { "exec", verb_exec, "exec"},
152 { "reload", verb_reload, "reload [zone] ..."},
153 { "reconfig", verb_reconfig, "reconfig (just sees new/gone zones)"},
154 { "dumpdb", verb_dumpdb, "dumpdb"},
155 { "stats", verb_stats, "stats"},
156 { "trace", verb_trace, "trace [level]"},
157 { "notrace", verb_notrace, "notrace"},
158 { "querylog", verb_querylog, "querylog"},
159 { "qrylog", verb_querylog, "qrylog"},
160 { "help", verb_help, "help"},
161 { "quit", verb_quit, "quit"},
165 /* Public functions. */
168 ns_ctl_initialize(void) {
169 INIT_LIST(server_controls);
173 ns_ctl_shutdown(void) {
174 if (!EMPTY(server_controls))
175 free_controls(&server_controls);
179 ns_ctl_defaults(controls *list) {
180 ns_ctl_add(list, ns_ctl_new_unix(_PATH_NDCSOCK, 0600, 0, 0));
184 ns_ctl_add(controls *list, control new) {
185 if (!find_control(*list, new))
186 APPEND(*list, new, link);
190 ns_ctl_new_inet(struct in_addr saddr, u_int sport, ip_match_list allow) {
191 control new = new_control();
193 INIT_LINK(new, link);
195 memset(&new->var.v_inet.in, 0, sizeof new->var.v_inet.in);
196 new->var.v_inet.in.sin_family = AF_INET;
197 new->var.v_inet.in.sin_addr = saddr;
198 new->var.v_inet.in.sin_port = sport;
199 new->var.v_inet.allow = allow;
204 ns_ctl_new_unix(char *path, mode_t mode, uid_t owner, gid_t group) {
205 control new = new_control();
207 INIT_LINK(new, link);
209 memset(&new->var.v_unix.un, 0, sizeof new->var.v_unix.un);
210 new->var.v_unix.un.sun_family = AF_UNIX;
211 strncpy(new->var.v_unix.un.sun_path, path,
212 sizeof new->var.v_unix.un.sun_path - 1);
213 new->var.v_unix.mode = mode;
214 new->var.v_unix.owner = owner;
215 new->var.v_unix.group = group;
220 ns_ctl_install(controls *new) {
221 control ctl, old, next;
223 /* Find all the controls which aren't new or deleted. */
224 for (ctl = HEAD(server_controls); ctl != NULL; ctl = NEXT(ctl, link))
225 ctl->flags &= ~CONTROL_FOUND;
226 for (ctl = HEAD(*new); ctl != NULL; ctl = next) {
227 next = NEXT(ctl, link);
228 old = find_control(server_controls, ctl);
230 old->flags |= CONTROL_FOUND;
231 propagate_changes(ctl, old);
232 if (old->sctx == NULL)
233 free_control(&server_controls, old);
234 free_control(new, ctl);
238 /* Destroy any old controls which weren't found. */
239 for (ctl = HEAD(server_controls); ctl != NULL; ctl = next) {
240 next = NEXT(ctl, link);
241 if ((ctl->flags & CONTROL_FOUND) == 0)
242 free_control(&server_controls, ctl);
245 /* Add any new controls which were found. */
246 for (ctl = HEAD(*new); ctl != NULL; ctl = next) {
247 next = NEXT(ctl, link);
248 APPEND(server_controls, ctl, link);
250 if (ctl->sctx == NULL)
251 free_control(&server_controls, ctl);
255 /* Private functions. */
257 static struct ctl_sctx *
258 mksrvr(control ctl, const struct sockaddr *sa, size_t salen) {
259 return (ctl_server(ev, sa, salen, verbs, 500, 222,
260 600, 5, 10, logger, ctl));
265 control new = memget(sizeof *new);
268 panic("memget failed in new_control()", NULL);
275 free_control(controls *list, control this) {
279 if (this->sctx != NULL) {
280 ctl_endserver(this->sctx);
284 switch (this->type) {
286 if (this->var.v_inet.allow != NULL) {
287 free_ip_match_list(this->var.v_inet.allow);
288 this->var.v_inet.allow = NULL;
292 /* XXX Race condition. */
294 stat(this->var.v_unix.un.sun_path, &sb) == 0 &&
295 (S_ISSOCK(sb.st_mode) || S_ISFIFO(sb.st_mode))) {
296 /* XXX Race condition. */
297 unlink(this->var.v_unix.un.sun_path);
301 panic("impossible type in free_control", NULL);
304 UNLINK(*list, this, link);
305 memput(this, sizeof *this);
309 free_controls(controls *list) {
312 for (ctl = HEAD(*list); ctl != NULL; ctl = next) {
313 next = NEXT(ctl, link);
314 free_control(list, ctl);
320 match_control(control l, control r) {
323 if (l->type != r->type)
328 if (l->var.v_inet.in.sin_family !=
329 r->var.v_inet.in.sin_family ||
330 l->var.v_inet.in.sin_port !=
331 r->var.v_inet.in.sin_port ||
332 l->var.v_inet.in.sin_addr.s_addr !=
333 r->var.v_inet.in.sin_addr.s_addr)
337 if (l->var.v_unix.un.sun_family !=
338 r->var.v_unix.un.sun_family ||
339 strcmp(l->var.v_unix.un.sun_path,
340 r->var.v_unix.un.sun_path) != 0)
344 panic("impossible type in match_control", NULL);
347 ns_debug(ns_log_config, 20, "match_control(): %d", match);
352 find_control(controls list, control new) {
355 for (ctl = HEAD(list); ctl != NULL; ctl = NEXT(ctl, link))
356 if (match_control(ctl, new))
362 propagate_changes(const control diff, control base) {
363 int need_install = 0;
365 switch (base->type) {
367 if (base->var.v_inet.allow != NULL)
368 free_ip_match_list(base->var.v_inet.allow);
369 base->var.v_inet.allow = diff->var.v_inet.allow;
370 diff->var.v_inet.allow = NULL;
374 if (base->var.v_unix.mode != diff->var.v_unix.mode) {
375 base->var.v_unix.mode = diff->var.v_unix.mode;
378 if (base->var.v_unix.owner != diff->var.v_unix.owner) {
379 base->var.v_unix.owner = diff->var.v_unix.owner;
382 if (base->var.v_unix.group != diff->var.v_unix.group) {
383 base->var.v_unix.group = diff->var.v_unix.group;
388 panic("impossible type in ns_ctl::propagate_changes", NULL);
396 install(control ctl) {
405 panic("impossible type in ns_ctl::install", NULL);
411 install_inet(control ctl) {
412 if (ctl->sctx == NULL) {
413 ctl->sctx = mksrvr(ctl,
414 (struct sockaddr *)&ctl->var.v_inet.in,
415 sizeof ctl->var.v_inet.in);
420 * Unattach an old unix domain socket if it exists.
423 unattach(control ctl) {
427 s = socket(AF_UNIX, SOCK_STREAM, 0);
429 ns_warning(ns_log_config,
430 "unix control \"%s\" socket failed: %s",
431 ctl->var.v_unix.un.sun_path,
436 if (stat(ctl->var.v_unix.un.sun_path, &sb) < 0) {
438 case ENOENT: /* We exited cleanly last time */
441 ns_warning(ns_log_config,
442 "unix control \"%s\" stat failed: %s",
443 ctl->var.v_unix.un.sun_path,
450 if (!(S_ISSOCK(sb.st_mode) || S_ISFIFO(sb.st_mode))) {
451 ns_warning(ns_log_config, "unix control \"%s\" not socket",
452 ctl->var.v_unix.un.sun_path);
456 if (connect(s, (struct sockaddr *)&ctl->var.v_unix.un,
457 sizeof ctl->var.v_unix.un) < 0) {
461 if (unlink(ctl->var.v_unix.un.sun_path) < 0)
462 ns_warning(ns_log_config,
463 "unix control \"%s\" unlink failed: %s",
464 ctl->var.v_unix.un.sun_path,
468 ns_warning(ns_log_config,
469 "unix control \"%s\" connect failed: %s",
470 ctl->var.v_unix.un.sun_path,
480 install_unix(control ctl) {
481 if (ctl->sctx == NULL) {
483 ctl->sctx = mksrvr(ctl,
484 (struct sockaddr *)&ctl->var.v_unix.un,
485 sizeof ctl->var.v_unix.un);
487 if (ctl->sctx != NULL) {
488 /* XXX Race condition. */
489 if (chmod(ctl->var.v_unix.un.sun_path,
490 ctl->var.v_unix.mode) < 0) {
491 ns_warning(ns_log_config, "chmod(\"%s\", 0%03o): %s",
492 ctl->var.v_unix.un.sun_path,
493 ctl->var.v_unix.mode,
496 if (chown(ctl->var.v_unix.un.sun_path,
497 ctl->var.v_unix.owner,
498 ctl->var.v_unix.group) < 0) {
499 ns_warning(ns_log_config, "chown(\"%s\", %d, %d): %s",
500 ctl->var.v_unix.un.sun_path,
501 ctl->var.v_unix.owner,
502 ctl->var.v_unix.group,
509 logger(enum ctl_severity ctlsev, const char *format, ...) {
514 case ctl_debug: logsev = log_debug(5); break;
515 case ctl_warning: logsev = log_warning; break;
516 case ctl_error: logsev = log_error; break;
517 default: panic("invalid ctlsev in logger", NULL);
521 va_start(args, format);
522 log_vwrite(log_ctx, ns_log_control, logsev, format, args);
527 verb_connect(struct ctl_sctx *ctl, struct ctl_sess *sess,
528 const struct ctl_verb *verb, const char *rest,
529 u_int respflags, void *respctx, void *uctx)
531 const struct sockaddr *sa = (struct sockaddr *)respctx;
532 control nsctl = (control)uctx;
534 if (sa->sa_family == AF_INET) {
535 const struct sockaddr_in *in = (struct sockaddr_in *)sa;
536 const ip_match_list acl = nsctl->var.v_inet.allow;
538 if (!ip_address_allowed(acl, in->sin_addr)) {
539 ctl_response(sess, 502, "Permission denied.",
540 CTL_EXIT, NULL, NULL, NULL, NULL, 0);
544 ctl_response(sess, 220, server_options->version, 0, NULL, NULL, NULL,
549 verb_getpid(struct ctl_sctx *ctl, struct ctl_sess *sess,
550 const struct ctl_verb *verb, const char *rest,
551 u_int respflags, void *respctx, void *uctx)
553 char *msg = memget(MAX_STR_LEN);
556 ctl_response(sess, 503, "(out of memory)", 0,
557 NULL, NULL, NULL, NULL, 0);
560 sprintf(msg, "my pid is <%ld>", (long)getpid());
561 ctl_response(sess, 250, msg, 0, NULL, getpid_closure, msg, NULL, 0);
565 getpid_closure(struct ctl_sctx *sctx, struct ctl_sess *sess, void *uap) {
568 memput(msg, MAX_STR_LEN);
586 char text[MAX_STR_LEN];
590 verb_status(struct ctl_sctx *ctl, struct ctl_sess *sess,
591 const struct ctl_verb *verb, const char *rest,
592 u_int respflags, void *respctx, void *uctx)
594 struct pvt_status *pvt = ctl_getcsctx(sess);
597 pvt = memget(sizeof *pvt);
599 ctl_response(sess, 505, "(out of memory)",
600 0, NULL, NULL, NULL, NULL, 0);
603 pvt->state = e_version;
604 (void)ctl_setcsctx(sess, pvt);
606 switch (pvt->state++) {
608 strncpy(pvt->text, Version, sizeof pvt->text);
609 pvt->text[sizeof pvt->text - 1] = '\0';
612 sprintf(pvt->text, "number of zones allocated: %d", nzones);
615 sprintf(pvt->text, "debug level: %d", debug);
618 sprintf(pvt->text, "xfers running: %d", xfers_running);
621 sprintf(pvt->text, "xfers deferred: %d", xfers_deferred);
624 sprintf(pvt->text, "soa queries in progress: %d",
628 sprintf(pvt->text, "query logging is %s",
629 qrylog ? "ON" : "OFF");
632 sprintf(pvt->text, "server is %s priming",
633 priming ? "STILL" : "DONE");
636 sprintf(pvt->text, "server %s loading its configuration",
637 loading ? "IS" : "IS NOT");
642 ctl_response(sess, 250, pvt->text,
643 (pvt->state == e_finito) ? 0 : CTL_MORE,
644 NULL, status_closure, NULL, NULL, 0);
648 status_closure(struct ctl_sctx *sctx, struct ctl_sess *sess, void *uap) {
649 struct pvt_status *pvt = ctl_getcsctx(sess);
651 memput(pvt, sizeof *pvt);
652 ctl_setcsctx(sess, NULL);
656 verb_stop(struct ctl_sctx *ctl, struct ctl_sess *sess,
657 const struct ctl_verb *verb, const char *rest,
658 u_int respflags, void *respctx, void *uctx)
660 ns_need(main_need_exit);
661 ctl_response(sess, 250, "Shutdown initiated.", 0, NULL, NULL, NULL,
666 verb_exec(struct ctl_sctx *ctl, struct ctl_sess *sess,
667 const struct ctl_verb *verb, const char *rest,
668 u_int respflags, void *respctx, void *uctx)
672 if (rest != NULL && *rest != '\0') {
673 if (stat(rest, &sb) < 0) {
674 ctl_response(sess, 503, strerror(errno),
675 0, NULL, NULL, NULL, NULL, 0);
678 saved_argv[0] = savestr(rest, 1); /* Never strfreed. */
681 if (stat(saved_argv[0], &sb) < 0) {
682 const char *save = strerror(errno);
684 ns_warning(ns_log_default, "can't exec, %s: %s",
685 saved_argv[0], save);
686 ctl_response(sess, 502, save, 0, NULL, NULL, NULL,
689 ns_need(main_need_restart);
690 ctl_response(sess, 250, "Restart initiated.", 0, NULL,
691 NULL, NULL, NULL, 0);
696 verb_reload(struct ctl_sctx *ctl, struct ctl_sess *sess,
697 const struct ctl_verb *verb, const char *rest,
698 u_int respflags, void *respctx, void *uctx)
700 static const char spaces[] = " \t";
702 char *tmp = NULL, *x;
704 int class, code, success;
706 /* If there are no args, this is a classic reload of the config. */
707 if (rest == NULL || *rest == '\0') {
708 ns_need(main_need_reload);
710 msg = "Reload initiated.";
714 /* Look for optional zclass argument. Default is "in". */
715 tmp = savestr(rest, 1);
716 x = tmp + strcspn(tmp, spaces);
719 x += strspn(x, spaces);
721 if (x == NULL || *x == '\0')
723 class = sym_ston(__p_class_syms, x, &success);
726 msg = "unrecognized class";
730 /* Look for the zone, and do the right thing to it. */
731 zp = find_zone(tmp, class);
734 msg = "Zone not found.";
737 switch (zp->z_type) {
744 msg = deferred_reload_unsafe(zp);
752 msg = "Slave transfer queued.";
757 msg = "Non reloadable zone.";
763 ctl_response(sess, code, msg, 0, NULL, NULL, NULL, NULL, 0);
769 verb_reconfig(struct ctl_sctx *ctl, struct ctl_sess *sess,
770 const struct ctl_verb *verb, const char *rest,
771 u_int respflags, void *respctx, void *uctx)
773 ns_need(main_need_reconfig);
774 ctl_response(sess, 250, "Reconfig initiated.",
775 0, NULL, NULL, NULL, NULL, 0);
779 verb_dumpdb(struct ctl_sctx *ctl, struct ctl_sess *sess,
780 const struct ctl_verb *verb, const char *rest,
781 u_int respflags, void *respctx, void *uctx)
783 ns_need(main_need_dump);
784 ctl_response(sess, 250, "Database dump initiated.", 0, NULL,
785 NULL, NULL, NULL, 0);
789 verb_stats(struct ctl_sctx *ctl, struct ctl_sess *sess,
790 const struct ctl_verb *verb, const char *rest,
791 u_int respflags, void *respctx, void *uctx)
793 ns_need(main_need_statsdump);
794 ctl_response(sess, 250, "Statistics dump initiated.",
795 0, NULL, NULL, NULL, NULL, 0);
799 verb_trace(struct ctl_sctx *ctl, struct ctl_sess *sess,
800 const struct ctl_verb *verb, const char *rest,
801 u_int respflags, void *respctx, void *uctx)
804 char *msg = memget(MAX_STR_LEN);
807 ctl_response(sess, 503, "(out of memory)", 0,
808 NULL, NULL, NULL, NULL, 0);
815 ns_need(main_need_debug);
816 sprintf(msg, "Debug level: %d", desired_debug);
817 ctl_response(sess, 250, msg, 0, NULL, trace_closure, msg, NULL, 0);
821 trace_closure(struct ctl_sctx *sctx, struct ctl_sess *sess, void *uap) {
824 memput(msg, MAX_STR_LEN);
828 verb_notrace(struct ctl_sctx *ctl, struct ctl_sess *sess,
829 const struct ctl_verb *verb, const char *rest,
830 u_int respflags, void *respctx, void *uctx)
833 ns_need(main_need_debug);
834 ctl_response(sess, 250, "Debugging turned off.",
835 0, NULL, NULL, NULL, NULL, 0);
839 verb_querylog(struct ctl_sctx *ctl, struct ctl_sess *sess,
840 const struct ctl_verb *verb, const char *rest,
841 u_int respflags, void *respctx, void *uctx)
843 static const char on[] = "Query logging is now on.",
844 off[] = "Query logging is now off.";
847 ctl_response(sess, 250, qrylog ? on : off,
848 0, NULL, NULL, NULL, NULL, 0);
852 verb_help(struct ctl_sctx *ctl, struct ctl_sess *sess,
853 const struct ctl_verb *verb, const char *rest,
854 u_int respflags, void *respctx, void *uctx)
856 ctl_sendhelp(sess, 214);
860 verb_quit(struct ctl_sctx *ctl, struct ctl_sess *sess,
861 const struct ctl_verb *verb, const char *rest,
862 u_int respflags, void *respctx, void *uctx)
864 ctl_response(sess, 221, "End of control session.", CTL_EXIT, NULL,
865 NULL, NULL, NULL, 0);