]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/bind/bin/named/ns_ctl.c
This commit was generated by cvs2svn to compensate for changes in r54359,
[FreeBSD/FreeBSD.git] / contrib / bind / bin / named / ns_ctl.c
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 $";
3 #endif /* not lint */
4
5 /*
6  * Copyright (c) 1997-1999 by Internet Software Consortium.
7  *
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.
11  *
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
19  * SOFTWARE.
20  */
21
22 /* Extern. */
23
24 #include "port_before.h"
25
26 #include <sys/types.h>
27 #include <sys/param.h>
28 #include <sys/stat.h>
29 #include <sys/socket.h>
30 #include <sys/un.h>
31
32 #include <netinet/in.h>
33 #include <arpa/nameser.h>
34 #include <arpa/inet.h>
35
36 #include <ctype.h>
37 #include <errno.h>
38 #include <limits.h>
39 #include <resolv.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <syslog.h>
44 #include <time.h>
45 #include <unistd.h>
46 #include <fcntl.h>
47
48 #include <isc/eventlib.h>
49 #include <isc/logging.h>
50 #include <isc/memcluster.h>
51
52 #include "port_after.h"
53
54 #include "named.h"
55
56 /* Defs. */
57
58 #define CONTROL_FOUND   0x0001  /* for mark and sweep. */
59 #define MAX_STR_LEN     500
60
61 struct control {
62         LINK(struct control) link;
63         enum { t_dead, t_inet, t_unix } type;
64         struct ctl_sctx *sctx;
65         u_int flags;
66         union {
67                 struct {
68                         struct sockaddr_in in;
69                         ip_match_list allow;
70                 } v_inet;
71                 struct {
72                         struct sockaddr_un un;
73                         mode_t mode;
74                         uid_t owner;
75                         gid_t group;
76                 } v_unix;
77         } var;
78 };
79
80 /* Forward. */
81
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 *,
100                                        void *);
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 *,
105                                        void *);
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 *,
128                                        void *);
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 *);
141
142 /* Private data. */
143
144 static controls server_controls;
145
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"},
162         { NULL,         NULL,           NULL}
163 };
164
165 /* Public functions. */
166
167 void
168 ns_ctl_initialize(void) {
169         INIT_LIST(server_controls);
170 }
171
172 void
173 ns_ctl_shutdown(void) {
174         if (!EMPTY(server_controls))
175                 free_controls(&server_controls);
176 }
177
178 void
179 ns_ctl_defaults(controls *list) {
180         ns_ctl_add(list, ns_ctl_new_unix(_PATH_NDCSOCK, 0600, 0, 0));
181 }
182
183 void
184 ns_ctl_add(controls *list, control new) {
185         if (!find_control(*list, new))
186                 APPEND(*list, new, link);
187 }
188
189 control
190 ns_ctl_new_inet(struct in_addr saddr, u_int sport, ip_match_list allow) {
191         control new = new_control();
192
193         INIT_LINK(new, link);
194         new->type = t_inet;
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;
200         return (new);
201 }
202
203 control
204 ns_ctl_new_unix(char *path, mode_t mode, uid_t owner, gid_t group) {
205         control new = new_control();
206
207         INIT_LINK(new, link);
208         new->type = t_unix;
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;
216         return (new);
217 }
218
219 void
220 ns_ctl_install(controls *new) {
221         control ctl, old, next;
222
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);
229                 if (old != NULL) {
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);
235                 }
236         }
237
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);
243         }
244
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);
249                 install(ctl);
250                 if (ctl->sctx == NULL)
251                         free_control(&server_controls, ctl);
252         }
253 }
254
255 /* Private functions. */
256
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));
261 }
262
263 static control
264 new_control(void) {
265         control new = memget(sizeof *new);
266
267         if (new == NULL)
268                 panic("memget failed in new_control()", NULL);
269         new->type = t_dead;
270         new->sctx = NULL;
271         return (new);
272 }
273
274 static void
275 free_control(controls *list, control this) {
276         int was_live = 0;
277         struct stat sb;
278
279         if (this->sctx != NULL) {
280                 ctl_endserver(this->sctx);
281                 this->sctx = NULL;
282                 was_live = 1;
283         }
284         switch (this->type) {
285         case t_inet:
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;
289                 }
290                 break;
291         case t_unix:
292                 /* XXX Race condition. */
293                 if (was_live &&
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);
298                 }
299                 break;
300         default:
301                 panic("impossible type in free_control", NULL);
302                 /* NOTREACHED */
303         }
304         UNLINK(*list, this, link);
305         memput(this, sizeof *this);
306 }
307
308 static void
309 free_controls(controls *list) {
310         control ctl, next;
311
312         for (ctl = HEAD(*list); ctl != NULL; ctl = next) {
313                 next = NEXT(ctl, link);
314                 free_control(list, ctl);
315         }
316         INIT_LIST(*list);
317 }
318
319 static int
320 match_control(control l, control r) {
321         int match = 1;
322
323         if (l->type != r->type)
324                 match = 0;
325         else
326                 switch (l->type) {
327                 case t_inet:
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)
334                                 match = 0;
335                         break;
336                 case t_unix:
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)
341                                 match = 0;
342                         break;
343                 default:
344                         panic("impossible type in match_control", NULL);
345                         /* NOTREACHED */
346                 }
347         ns_debug(ns_log_config, 20, "match_control(): %d", match);
348         return (match);
349 }
350
351 static control
352 find_control(controls list, control new) {
353         control ctl;
354
355         for (ctl = HEAD(list); ctl != NULL; ctl = NEXT(ctl, link))
356                 if (match_control(ctl, new))
357                         return (ctl);
358         return (NULL);
359 }
360
361 static void
362 propagate_changes(const control diff, control base) {
363         int need_install = 0;
364
365         switch (base->type) {
366         case t_inet:
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;
371                 need_install++;
372                 break;
373         case t_unix:
374                 if (base->var.v_unix.mode != diff->var.v_unix.mode) {
375                         base->var.v_unix.mode = diff->var.v_unix.mode;
376                         need_install++;
377                 }
378                 if (base->var.v_unix.owner != diff->var.v_unix.owner) {
379                         base->var.v_unix.owner = diff->var.v_unix.owner;
380                         need_install++;
381                 }
382                 if (base->var.v_unix.group != diff->var.v_unix.group) {
383                         base->var.v_unix.group = diff->var.v_unix.group;
384                         need_install++;
385                 }
386                 break;
387         default:
388                 panic("impossible type in ns_ctl::propagate_changes", NULL);
389                 /* NOTREACHED */
390         }
391         if (need_install)
392                 install(base);
393 }
394
395 static void
396 install(control ctl) {
397         switch (ctl->type) {
398         case t_inet:
399                 install_inet(ctl);
400                 break;
401         case t_unix:
402                 install_unix(ctl);
403                 break;
404         default:
405                 panic("impossible type in ns_ctl::install", NULL);
406                 /* NOTREACHED */
407         }
408 }
409
410 static void
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);
416         }
417 }
418
419 /*
420  * Unattach an old unix domain socket if it exists.
421  */
422 static void
423 unattach(control ctl) {
424         int s;
425         struct stat sb;
426
427         s = socket(AF_UNIX, SOCK_STREAM, 0);
428         if (s < 0) {
429                 ns_warning(ns_log_config,
430                            "unix control \"%s\" socket failed: %s",
431                            ctl->var.v_unix.un.sun_path,
432                            strerror(errno));
433                 return;
434         }
435
436         if (stat(ctl->var.v_unix.un.sun_path, &sb) < 0) {
437                 switch (errno) {
438                 case ENOENT:    /* We exited cleanly last time */
439                         break;
440                 default:
441                         ns_warning(ns_log_config,
442                                    "unix control \"%s\" stat failed: %s",
443                                    ctl->var.v_unix.un.sun_path,
444                                    strerror(errno));
445                         break;
446                 }
447                 goto cleanup;
448         }
449
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);
453                 goto cleanup;
454         }
455
456         if (connect(s, (struct sockaddr *)&ctl->var.v_unix.un,
457                     sizeof ctl->var.v_unix.un) < 0) {
458                 switch (errno) {
459                 case ECONNREFUSED:
460                 case ECONNRESET:
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,
465                                            strerror(errno));
466                         break;
467                 default:
468                         ns_warning(ns_log_config,
469                                    "unix control \"%s\" connect failed: %s",
470                                    ctl->var.v_unix.un.sun_path,
471                                    strerror(errno));
472                         break;
473                 }
474         }
475   cleanup:
476         close(s);
477 }
478
479 static void
480 install_unix(control ctl) {
481         if (ctl->sctx == NULL) {
482                 unattach(ctl);
483                 ctl->sctx = mksrvr(ctl,
484                                    (struct sockaddr *)&ctl->var.v_unix.un,
485                                    sizeof ctl->var.v_unix.un);
486         }
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,
494                                    strerror(errno));
495                 }
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,
503                                    strerror(errno));
504                 }
505         }
506 }
507
508 static void
509 logger(enum ctl_severity ctlsev, const char *format, ...) {
510         va_list args;
511         int logsev;
512
513         switch (ctlsev) {
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);
518         }
519         if (!log_ctx_valid)
520                 return;
521         va_start(args, format);
522         log_vwrite(log_ctx, ns_log_control, logsev, format, args);
523         va_end(args);
524 }
525
526 static void
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)
530 {
531         const struct sockaddr *sa = (struct sockaddr *)respctx;
532         control nsctl = (control)uctx;
533
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;
537
538                 if (!ip_address_allowed(acl, in->sin_addr)) {
539                         ctl_response(sess, 502, "Permission denied.",
540                                      CTL_EXIT, NULL, NULL, NULL, NULL, 0);
541                         return;
542                 }
543         }
544         ctl_response(sess, 220, server_options->version, 0, NULL, NULL, NULL,
545                      NULL, 0);
546 }
547
548 static void
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)
552 {
553         char *msg = memget(MAX_STR_LEN);
554
555         if (msg == NULL) {
556                 ctl_response(sess, 503, "(out of memory)", 0,
557                              NULL, NULL, NULL, NULL, 0);
558                 return;
559         }
560         sprintf(msg, "my pid is <%ld>", (long)getpid());
561         ctl_response(sess, 250, msg, 0, NULL, getpid_closure, msg, NULL, 0);
562 }
563
564 static void
565 getpid_closure(struct ctl_sctx *sctx, struct ctl_sess *sess, void *uap) {
566         char *msg = uap;
567
568         memput(msg, MAX_STR_LEN);
569 }
570
571 enum state {
572         e_version = 0,
573         e_nzones,
574         e_debug,
575         e_xfersrun,
576         e_xfersdfr,
577         e_qserials,
578         e_qrylog,
579         e_priming,
580         e_loading,
581         e_finito
582 };
583
584 struct pvt_status {
585         enum state      state;
586         char            text[MAX_STR_LEN];
587 };
588
589 static void
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)
593 {
594         struct pvt_status *pvt = ctl_getcsctx(sess);
595
596         if (pvt == NULL) {
597                 pvt = memget(sizeof *pvt);
598                 if (pvt == NULL) {
599                         ctl_response(sess, 505, "(out of memory)",
600                                      0, NULL, NULL, NULL, NULL, 0);
601                         return;
602                 }
603                 pvt->state = e_version;
604                 (void)ctl_setcsctx(sess, pvt);
605         }
606         switch (pvt->state++) {
607         case e_version:
608                 strncpy(pvt->text, Version, sizeof pvt->text);
609                 pvt->text[sizeof pvt->text - 1] = '\0';
610                 break;
611         case e_nzones:
612                 sprintf(pvt->text, "number of zones allocated: %d", nzones);
613                 break;
614         case e_debug:
615                 sprintf(pvt->text, "debug level: %d", debug);
616                 break;
617         case e_xfersrun:
618                 sprintf(pvt->text, "xfers running: %d", xfers_running);
619                 break;
620         case e_xfersdfr:
621                 sprintf(pvt->text, "xfers deferred: %d", xfers_deferred);
622                 break;
623         case e_qserials:
624                 sprintf(pvt->text, "soa queries in progress: %d",
625                         qserials_running);
626                 break;
627         case e_qrylog:
628                 sprintf(pvt->text, "query logging is %s",
629                         qrylog ? "ON" : "OFF");
630                 break;
631         case e_priming:
632                 sprintf(pvt->text, "server is %s priming",
633                         priming ? "STILL" : "DONE");
634                 break;
635         case e_loading:
636                 sprintf(pvt->text, "server %s loading its configuration",
637                         loading ? "IS" : "IS NOT");
638                 break;
639         case e_finito:
640                 return;
641         }
642         ctl_response(sess, 250, pvt->text,
643                      (pvt->state == e_finito) ? 0 : CTL_MORE,
644                      NULL, status_closure, NULL, NULL, 0);
645 }
646
647 static void
648 status_closure(struct ctl_sctx *sctx, struct ctl_sess *sess, void *uap) {
649         struct pvt_status *pvt = ctl_getcsctx(sess);
650
651         memput(pvt, sizeof *pvt);
652         ctl_setcsctx(sess, NULL);
653 }
654
655 static void
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)
659 {
660         ns_need(main_need_exit);
661         ctl_response(sess, 250, "Shutdown initiated.", 0, NULL, NULL, NULL,
662                      NULL, 0);
663 }
664
665 static void
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)
669 {
670         struct stat sb;
671
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);
676                         return;
677                 }
678                 saved_argv[0] = savestr(rest, 1);       /* Never strfreed. */
679         }
680
681         if (stat(saved_argv[0], &sb) < 0) {
682                 const char *save = strerror(errno);
683
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,
687                              NULL, 0);
688         } else {
689                 ns_need(main_need_restart);
690                 ctl_response(sess, 250, "Restart initiated.", 0, NULL,
691                              NULL, NULL, NULL, 0);
692         }
693 }
694
695 static void
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)
699 {
700         static const char spaces[] = " \t";
701         struct zoneinfo *zp;
702         char *tmp = NULL, *x;
703         const char *msg;
704         int class, code, success;
705
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);
709                 code = 250;
710                 msg = "Reload initiated.";
711                 goto respond;
712         }
713
714         /* Look for optional zclass argument.  Default is "in". */
715         tmp = savestr(rest, 1);
716         x = tmp + strcspn(tmp, spaces);
717         if (*x != '\0') {
718                 *x++ = '\0';
719                 x += strspn(x, spaces);
720         }
721         if (x == NULL || *x == '\0')
722                 x = "in";
723         class = sym_ston(__p_class_syms, x, &success);
724         if (!success) {
725                 code = 507;
726                 msg = "unrecognized class";
727                 goto respond;
728         }
729
730         /* Look for the zone, and do the right thing to it. */
731         zp = find_zone(tmp, class);
732         if (zp == NULL) {
733                 code = 506;
734                 msg = "Zone not found.";
735                 goto respond;
736         }
737         switch (zp->z_type) {
738         case z_master:
739                 ns_stopxfrs(zp);
740                 /*FALLTHROUGH*/
741         case z_hint:
742                 block_signals();
743                 code = 251;
744                 msg = deferred_reload_unsafe(zp);
745                 unblock_signals();
746                 break;
747         case z_slave:
748         case z_stub:
749                 ns_stopxfrs(zp);
750                 addxfer(zp);
751                 code = 251;
752                 msg = "Slave transfer queued.";
753                 goto respond;
754         case z_forward:
755         case z_cache:
756         default:
757                 msg = "Non reloadable zone.";
758                 code = 507;
759                 break;
760         }
761
762  respond:
763         ctl_response(sess, code, msg, 0, NULL, NULL, NULL, NULL, 0);
764         if (tmp != NULL)
765                 freestr(tmp);
766 }
767
768 static void
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)
772 {
773         ns_need(main_need_reconfig);
774         ctl_response(sess, 250, "Reconfig initiated.",
775                      0, NULL, NULL, NULL, NULL, 0);
776 }
777
778 static void
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)
782 {
783         ns_need(main_need_dump);
784         ctl_response(sess, 250, "Database dump initiated.", 0, NULL,
785                      NULL, NULL, NULL, 0);
786 }
787
788 static void
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)
792 {
793         ns_need(main_need_statsdump);
794         ctl_response(sess, 250, "Statistics dump initiated.",
795                      0, NULL, NULL, NULL, NULL, 0);
796 }
797
798 static void
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)
802 {
803         int i = atoi(rest);
804         char *msg = memget(MAX_STR_LEN);
805
806         if (msg == NULL) {
807                 ctl_response(sess, 503, "(out of memory)", 0,
808                              NULL, NULL, NULL, NULL, 0);
809                 return;
810         }
811         if (i > 0) 
812                 desired_debug = i;
813         else
814                 desired_debug++;
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);
818 }
819
820 static void
821 trace_closure(struct ctl_sctx *sctx, struct ctl_sess *sess, void *uap) {
822         char *msg = uap;
823
824         memput(msg, MAX_STR_LEN);
825 }
826
827 static void
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)
831 {
832         desired_debug = 0;
833         ns_need(main_need_debug);
834         ctl_response(sess, 250, "Debugging turned off.",
835                      0, NULL, NULL, NULL, NULL, 0);
836 }
837
838 static void
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)
842 {
843         static const char       on[] = "Query logging is now on.",
844                                 off[] = "Query logging is now off.";
845
846         toggle_qrylog();
847         ctl_response(sess, 250, qrylog ? on : off,
848                      0, NULL, NULL, NULL, NULL, 0);
849 }
850
851 static void
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)
855 {
856         ctl_sendhelp(sess, 214);
857 }
858
859 static void
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)
863 {
864         ctl_response(sess, 221, "End of control session.", CTL_EXIT, NULL,
865                      NULL, NULL, NULL, 0);
866 }