summaryrefslogtreecommitdiffstats
path: root/contrib/bind/bin/ndc/ndc.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/bind/bin/ndc/ndc.c')
-rw-r--r--contrib/bind/bin/ndc/ndc.c698
1 files changed, 698 insertions, 0 deletions
diff --git a/contrib/bind/bin/ndc/ndc.c b/contrib/bind/bin/ndc/ndc.c
new file mode 100644
index 0000000..764d05b
--- /dev/null
+++ b/contrib/bind/bin/ndc/ndc.c
@@ -0,0 +1,698 @@
+#if !defined(lint) && !defined(SABER)
+static const char rcsid[] = "$Id: ndc.c,v 1.13 1999/10/13 16:39:16 vixie Exp $";
+#endif /* not lint */
+
+/*
+ * Portions Copyright (c) 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.
+ */
+
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include <isc/eventlib.h>
+#include <isc/ctl.h>
+
+#include "port_after.h"
+#include "pathnames.h"
+
+typedef union {
+ struct sockaddr_in in;
+ struct sockaddr_un un;
+} sockaddr_t;
+
+typedef void (*closure)(void *, const char *, int);
+
+static const char * program = "amnesia";
+static enum { e_channel, e_signals } mode = e_channel;
+static char * channel = _PATH_NDCSOCK;
+static const char helpfmt[] = "\t%-16s\t%s\n";
+static const char * pidfile = _PATH_PIDFILE;
+static sockaddr_t client, server;
+static int quiet = 0, tracing = 0, silent = 0, client_set = 0;
+static int debug = 0, errors = 0, doneflag, exitflag;
+static int logger_show = 1;
+static evContext ev;
+static char cmd[1000];
+static const char * named_path = _PATH_NAMED;
+
+static int slashcmd(void);
+static void slashhelp(void);
+static int builtincmd(void);
+static void command(void);
+static int running(int, pid_t *);
+static void command_channel(void);
+static void channel_loop(char *, int, closure, void *);
+static void getpid_closure(void *, const char *, int);
+static void banner(struct ctl_cctx *, void *, const char *, u_int);
+static void done(struct ctl_cctx *, void *, const char *, u_int);
+static void logger(enum ctl_severity, const char *fmt, ...);
+static void command_signals(void);
+static void stop_named(pid_t);
+static void start_named(const char *, int);
+static int fgetpid(const char *, pid_t *);
+static int get_sockaddr(char *, sockaddr_t *);
+static size_t impute_addrlen(const struct sockaddr *);
+static void vtrace(const char *, va_list);
+static void trace(const char *, ...);
+static void result(const char *, ...);
+static void fatal(const char *, ...);
+static void verror(const char *, va_list);
+static void error(const char *, ...);
+
+static void
+usage(const char *fmt, ...) {
+ va_list args;
+
+ va_start(args, fmt);
+ fprintf(stderr, "%s: usage error: ", program);
+ vfprintf(stderr, fmt, args);
+ fputc('\n', stderr);
+ va_end(args);
+ fatal("usage: %s \
+[-l localsock] [-c channel] [-p pidfile] [-n namedpath] \
+[-dqst] [command [args]]\n\
+",
+ program);
+}
+
+/* Public. */
+
+int
+main(int argc, char *argv[], char *envp[]) {
+ char *p;
+ int ch;
+
+ if ((program = strrchr(argv[0], '/')) != NULL)
+ program++;
+ else
+ program = argv[0];
+ while ((ch = getopt(argc, argv, "c:p:l:n:dqst")) != -1) {
+ switch (ch) {
+ case 'c':
+ channel = optarg;
+ mode = e_channel;
+ break;
+ case 'p':
+ pidfile = optarg;
+ mode = e_signals;
+ break;
+ case 'l':
+ if (!get_sockaddr(optarg, &client))
+ usage("bad local socket (%s)", optarg);
+ client_set++;
+ break;
+ case 'n':
+ named_path = optarg;
+ break;
+ case 'd':
+ tracing++;
+ debug++;
+ break;
+ case 'q':
+ quiet++;
+ break;
+ case 's':
+ silent++;
+ break;
+ case 't':
+ tracing++;
+ break;
+ default:
+ usage("unrecognized command option (%c)", ch);
+ /* NOTREACHED */
+ }
+ }
+ if (mode != e_channel && client_set)
+ usage("the -l flag is only valid for control channels");
+ if (mode == e_channel) {
+ if (!get_sockaddr(channel, &server))
+ usage("bad channel name (%s)", channel);
+ if (evCreate(&ev) < 0)
+ fatal("evCreate - %s", strerror(errno));
+ }
+ *(p = cmd) = '\0';
+ for (argc -= optind, argv += optind;
+ argc > 0;
+ argc--, argv++) {
+ size_t t = strlen(*argv);
+
+ if ((p - cmd) + t + 2 > sizeof cmd)
+ usage("command too long");
+ strcpy(p, *argv);
+ p += t;
+ if (argv[1] != NULL)
+ *p++ = ' ';
+ *p = '\0';
+ }
+ if (cmd[0] != '\0') {
+ command();
+ } else {
+ if (!quiet)
+ result("Type help -or- /h if you need help.");
+ for (exitflag = 0; !exitflag; (void)NULL) {
+ if (!quiet) {
+ printf("%s> ", program);
+ fflush(stdout);
+ }
+ if (!fgets(cmd, sizeof cmd, stdin)) {
+ if (!quiet)
+ result("EOF");
+ exitflag++;
+ continue;
+ }
+ if (cmd[strlen(cmd) - 1] == '\n')
+ cmd[strlen(cmd) - 1] = '\0';
+ if (cmd[0] == '\0')
+ continue;
+ if (slashcmd())
+ continue;
+ command();
+ }
+ }
+ if (mode == e_channel)
+ evDestroy(ev);
+ exit(errors != 0);
+}
+
+/* Private. */
+
+static int
+slashcmd(void) {
+ if (strncasecmp(cmd, "/help", strlen(cmd)) == 0)
+ slashhelp();
+ else if (strncasecmp(cmd, "/exit", strlen(cmd)) == 0)
+ exitflag++;
+ else if (strncasecmp(cmd, "/trace", strlen(cmd)) == 0)
+ result("tracing now %s",
+ (tracing = !tracing) ? "on" : "off");
+ else if (strncasecmp(cmd, "/debug", strlen(cmd)) == 0)
+ result("debugging now %s",
+ (debug = !debug) ? "on" : "off");
+ else if (strncasecmp(cmd, "/quiet", strlen(cmd)) == 0)
+ result("%s is now %s", program,
+ (quiet = !quiet) ? "quiet" : "noisy");
+ else if (strncasecmp(cmd, "/silent", strlen(cmd)) == 0)
+ result("%s is now %s", program,
+ (silent = !silent)
+ ? "silent" : "gregarious");
+ else
+ return (0);
+ return (1);
+}
+
+static void
+slashhelp(void) {
+ printf(helpfmt, "/h(elp)", "this text");
+ printf(helpfmt, "/e(xit)", "leave this program");
+ printf(helpfmt, "/t(race)",
+ "toggle tracing (protocol and system events)");
+ printf(helpfmt, "/d(ebug)",
+ "toggle debugging (internal program events)");
+ printf(helpfmt, "/q(uiet)",
+ "toggle quietude (prompts and results)");
+ printf(helpfmt, "/s(ilent)",
+ "toggle silence (suppresses nonfatal errors)");
+}
+
+static int
+builtincmd(void) {
+ static const char spaces[] = " \t";
+ char *rest, *syscmd;
+ pid_t pid;
+ int save_quiet = quiet;
+ int len;
+
+ quiet = 1;
+
+ len = strcspn(cmd, spaces);
+ rest = cmd + len;
+ if (*rest != '\0') {
+ rest++;
+ rest += strspn(rest, spaces);
+ }
+ syscmd = malloc(strlen(named_path) + sizeof " " + strlen(rest));
+ if (syscmd == NULL)
+ fatal("malloc() failed - %s", strerror(errno));
+ strcpy(syscmd, named_path);
+ if (*rest != '\0') {
+ strcat(syscmd, " ");
+ strcat(syscmd, rest);
+ }
+ if (strncasecmp(cmd, "start", len) == 0) {
+ if (running(debug, &pid))
+ error("name server already running? (pid %ld)",
+ (long)pid);
+ else
+ start_named(syscmd, save_quiet);
+ quiet = save_quiet;
+ free(syscmd);
+ return (1);
+ } else if (strncasecmp(cmd, "restart", len) == 0) {
+ if (!running(debug, &pid))
+ error("name server was not running (warning only)");
+ else
+ stop_named(pid);
+ start_named(syscmd, save_quiet);
+ quiet = save_quiet;
+ free(syscmd);
+ return (1);
+ }
+ quiet = save_quiet;
+ free(syscmd);
+ return (0);
+}
+
+static void
+builtinhelp(void) {
+ printf(helpfmt, "start", "start the server");
+ printf(helpfmt, "restart", "stop server if any, start a new one");
+}
+
+static void
+command(void) {
+ if (builtincmd())
+ return;
+ switch (mode) {
+ case e_channel:
+ command_channel();
+ break;
+ case e_signals:
+ command_signals();
+ break;
+ default:
+ abort();
+ }
+}
+
+static int
+running(int show, pid_t *pidp) {
+ pid_t pid;
+
+ switch (mode) {
+ case e_channel:
+ pid = 0;
+ channel_loop("getpid", show, getpid_closure, &pid);
+ if (pid != 0) {
+ if (tracing)
+ result("pid %ld is running", (long)pid);
+ *pidp = pid;
+ return (1);
+ }
+ break;
+ case e_signals:
+ if (fgetpid(pidfile, pidp)) {
+ if (tracing)
+ result("pid %ld is running", (long)pid);
+ return (1);
+ }
+ break;
+ default:
+ abort();
+ }
+ if (show)
+ error("pid not valid or server not running");
+ return (0);
+}
+
+static void
+getpid_closure(void *uap, const char *text, int flags) {
+ pid_t *pidp = uap;
+ const char *cp;
+
+ flags = flags;
+ if ((cp = strchr(text, '<')) != NULL) {
+ long l = 0;
+ char ch;
+
+ while ((ch = *++cp) != '\0' && ch != '>' && isdigit(ch))
+ l *= 10, l += (ch - '0');
+ if (ch == '>') {
+ *pidp = (pid_t)l;
+ return;
+ }
+ }
+ error("response does not contain pid (%s)", text);
+}
+
+static void
+command_channel(void) {
+ int helping = (strcasecmp(cmd, "help") == 0);
+ int save_quiet = quiet;
+
+ if (helping)
+ quiet = 0;
+ channel_loop(cmd, !quiet, NULL, NULL);
+ quiet = save_quiet;
+}
+
+struct args {
+ const char *cmd;
+ closure cl;
+ void *ua;
+};
+
+static void
+channel_loop(char *cmdtext, int show, closure cl, void *ua) {
+ struct ctl_cctx *ctl;
+ struct sockaddr *client_addr;
+ struct args a;
+ evEvent e;
+ int save_logger_show = logger_show;
+
+ if (!client_set)
+ client_addr = NULL;
+ else
+ client_addr = (struct sockaddr *)&client;
+ a.cmd = cmdtext;
+ a.cl = cl;
+ a.ua = ua;
+ logger_show = show;
+ ctl = ctl_client(ev, client_addr, impute_addrlen(client_addr),
+ (struct sockaddr *)&server,
+ impute_addrlen((struct sockaddr *)&server),
+ banner, &a, 15, logger);
+ if (ctl == NULL) {
+ if (show)
+ error("cannot connect to command channel (%s)",
+ channel);
+ } else {
+ doneflag = 0;
+ while (evGetNext(ev, &e, EV_WAIT) == 0)
+ if (evDispatch(ev, e) < 0 || doneflag)
+ break;
+ ctl_endclient(ctl);
+ }
+ logger_show = save_logger_show;
+}
+
+static void
+banner(struct ctl_cctx *ctl, void *uap, const char *msg, u_int flags) {
+ struct args *a = uap;
+
+ if (msg == NULL) {
+ trace("EOF");
+ doneflag = 1;
+ return;
+ }
+ trace("%s", msg);
+ if ((flags & CTL_MORE) != 0)
+ return;
+ if (ctl_command(ctl, a->cmd, strlen(a->cmd), done, a) < 0) {
+ error("ctl_command failed - %s", strerror(errno));
+ doneflag = 1;
+ }
+}
+
+static void
+done(struct ctl_cctx *ctl, void *uap, const char *msg, u_int flags) {
+ struct args *a = uap;
+
+ if (msg == NULL) {
+ trace("EOF");
+ doneflag = 1;
+ return;
+ }
+ if (!tracing && !quiet && strlen(msg) > 4)
+ result("%s", msg + 4);
+ trace("%s", msg);
+ if (a->cl)
+ (a->cl)(a->ua, msg, flags);
+ if ((flags & CTL_MORE) == 0)
+ doneflag = 1;
+}
+
+static void
+logger(enum ctl_severity ctlsev, const char *format, ...) {
+ va_list args;
+
+ va_start(args, format);
+ switch (ctlsev) {
+ case ctl_debug:
+ /* FALLTHROUGH */
+ case ctl_warning:
+ if (debug)
+ vtrace(format, args);
+ break;
+ case ctl_error:
+ if (logger_show)
+ verror(format, args);
+ break;
+ default:
+ abort();
+ }
+ va_end(args);
+}
+
+static struct cmdsig {
+ const char * cmd;
+ int sig;
+ const char * help;
+} cmdsigs[] = {
+ { "dumpdb", SIGINT, "dump cache database to a file" },
+ { "reload", SIGHUP, "reload configuration file" },
+ { "stats", SIGILL, "dump statistics to a file" },
+ { "trace", SIGUSR1, "increment trace level" },
+ { "notrace", SIGUSR2, "turn off tracing" },
+#ifdef SIGWINCH
+ { "querylog", SIGWINCH, "toggle query logging" },
+ { "qrylog", SIGWINCH, "alias for querylog" },
+#endif
+ { NULL, 0 }
+};
+
+static void
+command_signals(void) {
+ struct cmdsig *cmdsig;
+ pid_t pid;
+ int sig;
+
+ if (strcasecmp(cmd, "help") == 0) {
+ printf(helpfmt, "help", "this output");
+ printf(helpfmt, "status", "check for running server");
+ printf(helpfmt, "stop", "stop the server");
+ builtinhelp();
+ for (cmdsig = cmdsigs; cmdsig->cmd != NULL; cmdsig++)
+ printf(helpfmt, cmdsig->cmd, cmdsig->help);
+ } else if (strcasecmp(cmd, "status") == 0) {
+ if (!fgetpid(pidfile, &pid))
+ error("pid not valid or server not running");
+ else
+ result("pid %ld is running", (long)pid);
+ } else if (strcasecmp(cmd, "stop") == 0) {
+ if (!fgetpid(pidfile, &pid))
+ error("name server not running");
+ else
+ stop_named(pid);
+ } else {
+ for (cmdsig = cmdsigs; cmdsig->cmd != NULL; cmdsig++)
+ if (strcasecmp(cmd, cmdsig->cmd) == 0)
+ break;
+ if (cmdsig->cmd == NULL)
+ error("unrecognized command (%s)", cmd);
+ else if (!fgetpid(pidfile, &pid))
+ error("can't get pid (%s)", pidfile);
+ else if (kill(pid, cmdsig->sig) < 0)
+ error("kill() failed - %s", strerror(errno));
+ else
+ trace("pid %ld sig %d OK", (long)pid, cmdsig->sig);
+ }
+}
+
+static void
+stop_named(pid_t pid) {
+ int n;
+
+ trace("stopping named (pid %ld)", (long)pid);
+ switch (mode) {
+ case e_signals:
+ if (kill(pid, SIGTERM) < 0) {
+ error("kill(%ld, SIGTERM) failed - %s",
+ (long)pid, strerror(errno));
+ return;
+ }
+ trace("SIGTERM ok, waiting for death");
+ break;
+ case e_channel:
+ channel_loop("stop", tracing, NULL, NULL);
+ break;
+ default:
+ abort();
+ }
+ for (n = 0; n < 10; n++) {
+ if (kill(pid, 0) != 0) {
+ trace("named (pid %ld) is dead", (long)pid);
+ return;
+ }
+ sleep(1);
+ }
+ error("named (pid %ld) didn't die", (long)pid);
+}
+
+static void
+start_named(const char *syscmd, int local_quiet) {
+ pid_t pid;
+
+ if (system(syscmd) != 0)
+ error("could not start new name server (%s)", syscmd);
+ else {
+ sleep(3);
+ if (!running(0, &pid))
+ error("name server has not started (yet?)");
+ else if (!local_quiet)
+ result("new pid is %ld", (long)pid);
+ }
+}
+
+static int
+fgetpid(const char *f, pid_t *pid) {
+ FILE *fp;
+ int try;
+ long t;
+
+ for (try = 0; try < 5; try++) {
+ trace("pidfile is \"%s\" (try #%d)", pidfile, try + 1);
+ if ((fp = fopen(pidfile, "r")) == NULL)
+ trace("pid file (%s) unavailable - %s",
+ pidfile, strerror(errno));
+ else if (fscanf(fp, "%ld\n", &t) != 1)
+ trace("pid file (%s) format is bad", pidfile);
+ else if (*pid = (pid_t)t, fclose(fp), kill(*pid, 0) < 0)
+ trace("pid file (%s) contains unusable pid (%d) - %s",
+ pidfile, *pid, strerror(errno));
+ else {
+ trace("pid is %ld", (long)*pid);
+ return (1);
+ }
+ sleep(1);
+ }
+ trace("pid not found");
+ return (0);
+}
+
+static int
+get_sockaddr(char *name, sockaddr_t *addr) {
+ char *slash;
+
+ if (name[0] == '/') {
+ memset(&addr->un, '\0', sizeof addr->un);
+ addr->un.sun_family = AF_UNIX;
+ strncpy(addr->un.sun_path, name, sizeof addr->un.sun_path - 1);
+ addr->un.sun_path[sizeof addr->un.sun_path - 1] = '\0';
+ } else if ((slash = strrchr(name, '/')) != NULL) {
+ *slash = '\0';
+ memset(&addr->in, '\0', sizeof addr->in);
+ if (!inet_pton(AF_INET, name, &addr->in.sin_addr))
+ usage("bad ip address (%s)", name);
+ if ((addr->in.sin_port = htons(atoi(slash+1))) == 0)
+ usage("bad ip port (%s)", slash+1);
+ addr->in.sin_family = AF_INET;
+ *slash = ':';
+ } else {
+ return (0);
+ }
+ return (1);
+}
+
+static size_t
+impute_addrlen(const struct sockaddr *sa) {
+ if (sa == 0)
+ return (0);
+ switch (sa->sa_family) {
+ case AF_INET:
+ return (sizeof(struct sockaddr_in));
+ case AF_UNIX:
+ return (sizeof(struct sockaddr_un));
+ default:
+ abort();
+ }
+}
+
+static void
+vtrace(const char *fmt, va_list ap) {
+ if (tracing) {
+ fprintf(stdout, "%s: [", program);
+ vfprintf(stdout, fmt, ap);
+ fputs("]\n", stdout);
+ }
+}
+
+static void
+trace(const char *fmt, ...) {
+ va_list args;
+
+ va_start(args, fmt);
+ vtrace(fmt, args);
+ va_end(args);
+}
+
+static void
+result(const char *fmt, ...) {
+ va_list args;
+
+ va_start(args, fmt);
+ vfprintf(stdout, fmt, args);
+ fputc('\n', stdout);
+ va_end(args);
+}
+
+static void
+fatal(const char *fmt, ...) {
+ va_list args;
+
+ va_start(args, fmt);
+ fprintf(stderr, "%s: fatal error: ", program);
+ vfprintf(stderr, fmt, args);
+ fputc('\n', stderr);
+ va_end(args);
+ exit(1);
+}
+
+static void
+verror(const char *fmt, va_list ap) {
+ fprintf(stderr, "%s: error: ", program);
+ vfprintf(stderr, fmt, ap);
+ fputc('\n', stderr);
+ errors++;
+}
+
+static void
+error(const char *fmt, ...) {
+ va_list args;
+
+ va_start(args, fmt);
+ if (silent)
+ vtrace(fmt, args);
+ else
+ verror(fmt, args);
+ va_end(args);
+}
OpenPOWER on IntegriCloud