Import bind-9.3.4
[dragonfly.git] / contrib / bind-9.3 / bin / rndc / rndc.c
1 /*
2  * Copyright (C) 2004-2006  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 2000-2003  Internet Software Consortium.
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15  * PERFORMANCE OF THIS SOFTWARE.
16  */
17
18 /* $Id: rndc.c,v 1.77.2.5.2.19 2006/08/04 03:03:08 marka Exp $ */
19
20 /*
21  * Principal Author: DCL
22  */
23
24 #include <config.h>
25
26 #include <stdlib.h>
27
28 #include <isc/app.h>
29 #include <isc/buffer.h>
30 #include <isc/commandline.h>
31 #include <isc/file.h>
32 #include <isc/log.h>
33 #include <isc/mem.h>
34 #include <isc/random.h>
35 #include <isc/socket.h>
36 #include <isc/stdtime.h>
37 #include <isc/string.h>
38 #include <isc/task.h>
39 #include <isc/thread.h>
40 #include <isc/util.h>
41
42 #include <isccfg/namedconf.h>
43
44 #include <isccc/alist.h>
45 #include <isccc/base64.h>
46 #include <isccc/cc.h>
47 #include <isccc/ccmsg.h>
48 #include <isccc/result.h>
49 #include <isccc/sexpr.h>
50 #include <isccc/types.h>
51 #include <isccc/util.h>
52
53 #include <bind9/getaddresses.h>
54
55 #include "util.h"
56
57 #define SERVERADDRS 10
58
59 char *progname;
60 isc_boolean_t verbose;
61
62 static const char *admin_conffile;
63 static const char *admin_keyfile;
64 static const char *version = VERSION;
65 static const char *servername = NULL;
66 static isc_sockaddr_t serveraddrs[SERVERADDRS];
67 static int nserveraddrs;
68 static int currentaddr = 0;
69 static unsigned int remoteport = 0;
70 static isc_socketmgr_t *socketmgr = NULL;
71 static unsigned char databuf[2048];
72 static isccc_ccmsg_t ccmsg;
73 static isccc_region_t secret;
74 static isc_boolean_t failed = ISC_FALSE;
75 static isc_mem_t *mctx;
76 static int sends, recvs, connects;
77 static char *command;
78 static char *args;
79 static char program[256];
80 static isc_socket_t *sock = NULL;
81 static isc_uint32_t serial;
82
83 static void rndc_startconnect(isc_sockaddr_t *addr, isc_task_t *task);
84
85 static void
86 usage(int status) {
87         fprintf(stderr, "\
88 Usage: %s [-c config] [-s server] [-p port]\n\
89         [-k key-file ] [-y key] [-V] command\n\
90 \n\
91 command is one of the following:\n\
92 \n\
93   reload        Reload configuration file and zones.\n\
94   reload zone [class [view]]\n\
95                 Reload a single zone.\n\
96   refresh zone [class [view]]\n\
97                 Schedule immediate maintenance for a zone.\n\
98   retransfer zone [class [view]]\n\
99                 Retransfer a single zone without checking serial number.\n\
100   freeze zone [class [view]]\n\
101                 Suspend updates to a dynamic zone.\n\
102   thaw zone [class [view]]\n\
103                 Enable updates to a frozen dynamic zone and reload it.\n\
104   reconfig      Reload configuration file and new zones only.\n\
105   stats         Write server statistics to the statistics file.\n\
106   querylog      Toggle query logging.\n\
107   dumpdb [-all|-cache|-zones] [view ...]\n\
108                 Dump cache(s) to the dump file (named_dump.db).\n\
109   stop          Save pending updates to master files and stop the server.\n\
110   stop -p       Save pending updates to master files and stop the server\n\
111                 reporting process id.\n\
112   halt          Stop the server without saving pending updates.\n\
113   halt -p       Stop the server without saving pending updates reporting\n\
114                 process id.\n\
115   trace         Increment debugging level by one.\n\
116   trace level   Change the debugging level.\n\
117   notrace       Set debugging level to 0.\n\
118   flush         Flushes all of the server's caches.\n\
119   flush [view]  Flushes the server's cache for a view.\n\
120   flushname name [view]\n\
121                 Flush the given name from the server's cache(s)\n\
122   status        Display status of the server.\n\
123   recursing     Dump the queries that are currently recursing (named.recursing)\n\
124   *restart      Restart the server.\n\
125 \n\
126 * == not yet implemented\n\
127 Version: %s\n",
128                 progname, version);
129
130         exit(status);
131 }
132
133 static void
134 get_addresses(const char *host, in_port_t port) {
135         isc_result_t result;
136
137         isc_app_block();
138         result = bind9_getaddresses(servername, port,
139                                     serveraddrs, SERVERADDRS, &nserveraddrs);
140         isc_app_unblock();
141         if (result != ISC_R_SUCCESS)
142                 fatal("couldn't get address for '%s': %s",
143                       host, isc_result_totext(result));
144         INSIST(nserveraddrs > 0);
145 }
146
147 static void
148 rndc_senddone(isc_task_t *task, isc_event_t *event) {
149         isc_socketevent_t *sevent = (isc_socketevent_t *)event;
150
151         UNUSED(task);
152
153         sends--;
154         if (sevent->result != ISC_R_SUCCESS)
155                 fatal("send failed: %s", isc_result_totext(sevent->result));
156         isc_event_free(&event);
157         if (sends == 0 && recvs == 0) {
158                 isc_socket_detach(&sock);
159                 isc_task_shutdown(task);
160                 RUNTIME_CHECK(isc_app_shutdown() == ISC_R_SUCCESS);
161         }
162 }
163
164 static void
165 rndc_recvdone(isc_task_t *task, isc_event_t *event) {
166         isccc_sexpr_t *response = NULL;
167         isccc_sexpr_t *data;
168         isccc_region_t source;
169         char *errormsg = NULL;
170         char *textmsg = NULL;
171         isc_result_t result;
172
173         recvs--;
174
175         if (ccmsg.result == ISC_R_EOF)
176                 fatal("connection to remote host closed\n"
177                       "This may indicate that the remote server is using "
178                       "an older version of \n"
179                       "the command protocol, this host is not authorized "
180                       "to connect,\nor the key is invalid.");
181
182         if (ccmsg.result != ISC_R_SUCCESS)
183                 fatal("recv failed: %s", isc_result_totext(ccmsg.result));
184
185         source.rstart = isc_buffer_base(&ccmsg.buffer);
186         source.rend = isc_buffer_used(&ccmsg.buffer);
187
188         DO("parse message", isccc_cc_fromwire(&source, &response, &secret));
189
190         data = isccc_alist_lookup(response, "_data");
191         if (data == NULL)
192                 fatal("no data section in response");
193         result = isccc_cc_lookupstring(data, "err", &errormsg);
194         if (result == ISC_R_SUCCESS) {
195                 failed = ISC_TRUE;
196                 fprintf(stderr, "%s: '%s' failed: %s\n",
197                         progname, command, errormsg);
198         }
199         else if (result != ISC_R_NOTFOUND)
200                 fprintf(stderr, "%s: parsing response failed: %s\n",
201                         progname, isc_result_totext(result));
202
203         result = isccc_cc_lookupstring(data, "text", &textmsg);
204         if (result == ISC_R_SUCCESS)
205                 printf("%s\n", textmsg);
206         else if (result != ISC_R_NOTFOUND)
207                 fprintf(stderr, "%s: parsing response failed: %s\n",
208                         progname, isc_result_totext(result));
209
210         isc_event_free(&event);
211         isccc_sexpr_free(&response);
212         if (sends == 0 && recvs == 0) {
213                 isc_socket_detach(&sock);
214                 isc_task_shutdown(task);
215                 RUNTIME_CHECK(isc_app_shutdown() == ISC_R_SUCCESS);
216         }
217 }
218
219 static void
220 rndc_recvnonce(isc_task_t *task, isc_event_t *event) {
221         isccc_sexpr_t *response = NULL;
222         isccc_sexpr_t *_ctrl;
223         isccc_region_t source;
224         isc_result_t result;
225         isc_uint32_t nonce;
226         isccc_sexpr_t *request = NULL;
227         isccc_time_t now;
228         isc_region_t r;
229         isccc_sexpr_t *data;
230         isccc_region_t message;
231         isc_uint32_t len;
232         isc_buffer_t b;
233
234         recvs--;
235
236         if (ccmsg.result == ISC_R_EOF)
237                 fatal("connection to remote host closed\n"
238                       "This may indicate that the remote server is using "
239                       "an older version of \n"
240                       "the command protocol, this host is not authorized "
241                       "to connect,\nor the key is invalid.");
242
243         if (ccmsg.result != ISC_R_SUCCESS)
244                 fatal("recv failed: %s", isc_result_totext(ccmsg.result));
245
246         source.rstart = isc_buffer_base(&ccmsg.buffer);
247         source.rend = isc_buffer_used(&ccmsg.buffer);
248
249         DO("parse message", isccc_cc_fromwire(&source, &response, &secret));
250
251         _ctrl = isccc_alist_lookup(response, "_ctrl");
252         if (_ctrl == NULL)
253                 fatal("_ctrl section missing");
254         nonce = 0;
255         if (isccc_cc_lookupuint32(_ctrl, "_nonce", &nonce) != ISC_R_SUCCESS)
256                 nonce = 0;
257
258         isc_stdtime_get(&now);
259
260         DO("create message", isccc_cc_createmessage(1, NULL, NULL, ++serial,
261                                                     now, now + 60, &request));
262         data = isccc_alist_lookup(request, "_data");
263         if (data == NULL)
264                 fatal("_data section missing");
265         if (isccc_cc_definestring(data, "type", args) == NULL)
266                 fatal("out of memory");
267         if (nonce != 0) {
268                 _ctrl = isccc_alist_lookup(request, "_ctrl");
269                 if (_ctrl == NULL)
270                         fatal("_ctrl section missing");
271                 if (isccc_cc_defineuint32(_ctrl, "_nonce", nonce) == NULL)
272                         fatal("out of memory");
273         }
274         message.rstart = databuf + 4;
275         message.rend = databuf + sizeof(databuf);
276         DO("render message", isccc_cc_towire(request, &message, &secret));
277         len = sizeof(databuf) - REGION_SIZE(message);
278         isc_buffer_init(&b, databuf, 4);
279         isc_buffer_putuint32(&b, len - 4);
280         r.length = len;
281         r.base = databuf;
282
283         isccc_ccmsg_cancelread(&ccmsg);
284         DO("schedule recv", isccc_ccmsg_readmessage(&ccmsg, task,
285                                                     rndc_recvdone, NULL));
286         recvs++;
287         DO("send message", isc_socket_send(sock, &r, task, rndc_senddone,
288                                            NULL));
289         sends++;
290
291         isc_event_free(&event);
292         isccc_sexpr_free(&response);
293         return;
294 }
295
296 static void
297 rndc_connected(isc_task_t *task, isc_event_t *event) {
298         char socktext[ISC_SOCKADDR_FORMATSIZE];
299         isc_socketevent_t *sevent = (isc_socketevent_t *)event;
300         isccc_sexpr_t *request = NULL;
301         isccc_sexpr_t *data;
302         isccc_time_t now;
303         isccc_region_t message;
304         isc_region_t r;
305         isc_uint32_t len;
306         isc_buffer_t b;
307         isc_result_t result;
308
309         connects--;
310
311         if (sevent->result != ISC_R_SUCCESS) {
312                 isc_sockaddr_format(&serveraddrs[currentaddr], socktext,
313                                     sizeof(socktext));
314                 if (sevent->result != ISC_R_CANCELED &&
315                     ++currentaddr < nserveraddrs)
316                 {
317                         notify("connection failed: %s: %s", socktext,
318                                isc_result_totext(sevent->result));
319                         isc_socket_detach(&sock);
320                         isc_event_free(&event);
321                         rndc_startconnect(&serveraddrs[currentaddr], task);
322                         return;
323                 } else
324                         fatal("connect failed: %s: %s", socktext,
325                               isc_result_totext(sevent->result));
326         }
327
328         isc_stdtime_get(&now);
329         DO("create message", isccc_cc_createmessage(1, NULL, NULL, ++serial,
330                                                     now, now + 60, &request));
331         data = isccc_alist_lookup(request, "_data");
332         if (data == NULL)
333                 fatal("_data section missing");
334         if (isccc_cc_definestring(data, "type", "null") == NULL)
335                 fatal("out of memory");
336         message.rstart = databuf + 4;
337         message.rend = databuf + sizeof(databuf);
338         DO("render message", isccc_cc_towire(request, &message, &secret));
339         len = sizeof(databuf) - REGION_SIZE(message);
340         isc_buffer_init(&b, databuf, 4);
341         isc_buffer_putuint32(&b, len - 4);
342         r.length = len;
343         r.base = databuf;
344
345         isccc_ccmsg_init(mctx, sock, &ccmsg);
346         isccc_ccmsg_setmaxsize(&ccmsg, 1024);
347
348         DO("schedule recv", isccc_ccmsg_readmessage(&ccmsg, task,
349                                                     rndc_recvnonce, NULL));
350         recvs++;
351         DO("send message", isc_socket_send(sock, &r, task, rndc_senddone,
352                                            NULL));
353         sends++;
354         isc_event_free(&event);
355 }
356
357 static void
358 rndc_startconnect(isc_sockaddr_t *addr, isc_task_t *task) {
359         isc_result_t result;
360
361         char socktext[ISC_SOCKADDR_FORMATSIZE];
362
363         isc_sockaddr_format(addr, socktext, sizeof(socktext));
364
365         notify("using server %s (%s)", servername, socktext);
366
367         DO("create socket", isc_socket_create(socketmgr,
368                                               isc_sockaddr_pf(addr),
369                                               isc_sockettype_tcp, &sock));
370         DO("connect", isc_socket_connect(sock, addr, task, rndc_connected,
371                                          NULL));
372         connects++;
373 }
374
375 static void
376 rndc_start(isc_task_t *task, isc_event_t *event) {
377         isc_event_free(&event);
378
379         get_addresses(servername, (in_port_t) remoteport);
380
381         currentaddr = 0;
382         rndc_startconnect(&serveraddrs[currentaddr], task);
383 }
384
385 static void
386 parse_config(isc_mem_t *mctx, isc_log_t *log, const char *keyname,
387              cfg_parser_t **pctxp, cfg_obj_t **configp)
388 {
389         isc_result_t result;
390         const char *conffile = admin_conffile;
391         const cfg_obj_t *defkey = NULL;
392         const cfg_obj_t *options = NULL;
393         const cfg_obj_t *servers = NULL;
394         const cfg_obj_t *server = NULL;
395         const cfg_obj_t *keys = NULL;
396         const cfg_obj_t *key = NULL;
397         const cfg_obj_t *defport = NULL;
398         const cfg_obj_t *secretobj = NULL;
399         const cfg_obj_t *algorithmobj = NULL;
400         cfg_obj_t *config = NULL;
401         const cfg_listelt_t *elt;
402         const char *secretstr;
403         const char *algorithm;
404         static char secretarray[1024];
405         const cfg_type_t *conftype = &cfg_type_rndcconf;
406         isc_boolean_t key_only = ISC_FALSE;
407
408         if (! isc_file_exists(conffile)) {
409                 conffile = admin_keyfile;
410                 conftype = &cfg_type_rndckey;
411
412                 if (! isc_file_exists(conffile))
413                         fatal("neither %s nor %s was found",
414                               admin_conffile, admin_keyfile);
415                 key_only = ISC_TRUE;
416         }
417
418         DO("create parser", cfg_parser_create(mctx, log, pctxp));
419
420         /*
421          * The parser will output its own errors, so DO() is not used.
422          */
423         result = cfg_parse_file(*pctxp, conffile, conftype, &config);
424         if (result != ISC_R_SUCCESS)
425                 fatal("could not load rndc configuration");
426
427         if (!key_only)
428                 (void)cfg_map_get(config, "options", &options);
429
430         if (key_only && servername == NULL)
431                 servername = "127.0.0.1";
432         else if (servername == NULL && options != NULL) {
433                 const cfg_obj_t *defserverobj = NULL;
434                 (void)cfg_map_get(options, "default-server", &defserverobj);
435                 if (defserverobj != NULL)
436                         servername = cfg_obj_asstring(defserverobj);
437         }
438
439         if (servername == NULL)
440                 fatal("no server specified and no default");
441
442         if (!key_only) {
443                 (void)cfg_map_get(config, "server", &servers);
444                 if (servers != NULL) {
445                         for (elt = cfg_list_first(servers);
446                              elt != NULL; 
447                              elt = cfg_list_next(elt))
448                         {
449                                 const char *name;
450                                 server = cfg_listelt_value(elt);
451                                 name = cfg_obj_asstring(cfg_map_getname(server));
452                                 if (strcasecmp(name, servername) == 0)
453                                         break;
454                                 server = NULL;
455                         }
456                 }
457         }
458
459         /*
460          * Look for the name of the key to use.
461          */
462         if (keyname != NULL)
463                 ;               /* Was set on command line, do nothing. */
464         else if (server != NULL) {
465                 DO("get key for server", cfg_map_get(server, "key", &defkey));
466                 keyname = cfg_obj_asstring(defkey);
467         } else if (options != NULL) {
468                 DO("get default key", cfg_map_get(options, "default-key",
469                                                   &defkey));
470                 keyname = cfg_obj_asstring(defkey);
471         } else if (!key_only)
472                 fatal("no key for server and no default");
473
474         /*
475          * Get the key's definition.
476          */
477         if (key_only)
478                 DO("get key", cfg_map_get(config, "key", &key));
479         else {
480                 DO("get config key list", cfg_map_get(config, "key", &keys));
481                 for (elt = cfg_list_first(keys);
482                      elt != NULL; 
483                      elt = cfg_list_next(elt))
484                 {
485                         key = cfg_listelt_value(elt);
486                         if (strcasecmp(cfg_obj_asstring(cfg_map_getname(key)),
487                                        keyname) == 0)
488                                 break;
489                 }
490                 if (elt == NULL)
491                         fatal("no key definition for name %s", keyname);
492         }
493         (void)cfg_map_get(key, "secret", &secretobj);
494         (void)cfg_map_get(key, "algorithm", &algorithmobj);
495         if (secretobj == NULL || algorithmobj == NULL)
496                 fatal("key must have algorithm and secret");
497
498         secretstr = cfg_obj_asstring(secretobj);
499         algorithm = cfg_obj_asstring(algorithmobj);
500
501         if (strcasecmp(algorithm, "hmac-md5") != 0)
502                 fatal("unsupported algorithm: %s", algorithm);
503
504         secret.rstart = (unsigned char *)secretarray;
505         secret.rend = (unsigned char *)secretarray + sizeof(secretarray);
506         DO("decode base64 secret", isccc_base64_decode(secretstr, &secret));
507         secret.rend = secret.rstart;
508         secret.rstart = (unsigned char *)secretarray;
509
510         /*
511          * Find the port to connect to.
512          */
513         if (remoteport != 0)
514                 ;               /* Was set on command line, do nothing. */
515         else {
516                 if (server != NULL)
517                         (void)cfg_map_get(server, "port", &defport);
518                 if (defport == NULL && options != NULL)
519                         (void)cfg_map_get(options, "default-port", &defport);
520         }
521         if (defport != NULL) {
522                 remoteport = cfg_obj_asuint32(defport);
523                 if (remoteport > 65535 || remoteport == 0)
524                         fatal("port %d out of range", remoteport);
525         } else if (remoteport == 0)
526                 remoteport = NS_CONTROL_PORT;
527
528         *configp = config;
529 }
530
531 int
532 main(int argc, char **argv) {
533         isc_boolean_t show_final_mem = ISC_FALSE;
534         isc_result_t result = ISC_R_SUCCESS;
535         isc_taskmgr_t *taskmgr = NULL;
536         isc_task_t *task = NULL;
537         isc_log_t *log = NULL;
538         isc_logconfig_t *logconfig = NULL;
539         isc_logdestination_t logdest;
540         cfg_parser_t *pctx = NULL;
541         cfg_obj_t *config = NULL;
542         const char *keyname = NULL;
543         char *p;
544         size_t argslen;
545         int ch;
546         int i;
547
548         result = isc_file_progname(*argv, program, sizeof(program));
549         if (result != ISC_R_SUCCESS)
550                 memcpy(program, "rndc", 5);
551         progname = program;
552
553         admin_conffile = RNDC_CONFFILE;
554         admin_keyfile = RNDC_KEYFILE;
555
556         result = isc_app_start();
557         if (result != ISC_R_SUCCESS)
558                 fatal("isc_app_start() failed: %s", isc_result_totext(result));
559
560         while ((ch = isc_commandline_parse(argc, argv, "c:k:Mmp:s:Vy:"))
561                != -1) {
562                 switch (ch) {
563                 case 'c':
564                         admin_conffile = isc_commandline_argument;
565                         break;
566
567                 case 'k':
568                         admin_keyfile = isc_commandline_argument;
569                         break;
570
571                 case 'M':
572                         isc_mem_debugging = ISC_MEM_DEBUGTRACE;
573                         break;
574
575                 case 'm':
576                         show_final_mem = ISC_TRUE;
577                         break;
578
579                 case 'p':
580                         remoteport = atoi(isc_commandline_argument);
581                         if (remoteport > 65535 || remoteport == 0)
582                                 fatal("port '%s' out of range",
583                                       isc_commandline_argument);
584                         break;
585
586                 case 's':
587                         servername = isc_commandline_argument;
588                         break;
589                 case 'V':
590                         verbose = ISC_TRUE;
591                         break;
592                 case 'y':
593                         keyname = isc_commandline_argument;
594                         break;
595                 case '?':
596                         usage(0);
597                         break;
598                 default:
599                         fatal("unexpected error parsing command arguments: "
600                               "got %c\n", ch);
601                         break;
602                 }
603         }
604
605         argc -= isc_commandline_index;
606         argv += isc_commandline_index;
607
608         if (argc < 1)
609                 usage(1);
610
611         isc_random_get(&serial);
612
613         DO("create memory context", isc_mem_create(0, 0, &mctx));
614         DO("create socket manager", isc_socketmgr_create(mctx, &socketmgr));
615         DO("create task manager", isc_taskmgr_create(mctx, 1, 0, &taskmgr));
616         DO("create task", isc_task_create(taskmgr, 0, &task));
617
618         DO("create logging context", isc_log_create(mctx, &log, &logconfig));
619         isc_log_setcontext(log);
620         DO("setting log tag", isc_log_settag(logconfig, progname));
621         logdest.file.stream = stderr;
622         logdest.file.name = NULL;
623         logdest.file.versions = ISC_LOG_ROLLNEVER;
624         logdest.file.maximum_size = 0;
625         DO("creating log channel",
626            isc_log_createchannel(logconfig, "stderr",
627                                  ISC_LOG_TOFILEDESC, ISC_LOG_INFO, &logdest,
628                                  ISC_LOG_PRINTTAG|ISC_LOG_PRINTLEVEL));
629         DO("enabling log channel", isc_log_usechannel(logconfig, "stderr",
630                                                       NULL, NULL));
631
632         parse_config(mctx, log, keyname, &pctx, &config);
633
634         isccc_result_register();
635
636         command = *argv;
637
638         /*
639          * Convert argc/argv into a space-delimited command string
640          * similar to what the user might enter in interactive mode
641          * (if that were implemented).
642          */
643         argslen = 0;
644         for (i = 0; i < argc; i++)
645                 argslen += strlen(argv[i]) + 1;
646
647         args = isc_mem_get(mctx, argslen);
648         if (args == NULL)
649                 DO("isc_mem_get", ISC_R_NOMEMORY);
650
651         p = args;
652         for (i = 0; i < argc; i++) {
653                 size_t len = strlen(argv[i]);
654                 memcpy(p, argv[i], len);
655                 p += len;
656                 *p++ = ' ';
657         }
658
659         p--;
660         *p++ = '\0';
661         INSIST(p == args + argslen);
662
663         notify("%s", command);
664
665         if (strcmp(command, "restart") == 0)
666                 fatal("'%s' is not implemented", command);
667
668         DO("post event", isc_app_onrun(mctx, task, rndc_start, NULL));
669
670         result = isc_app_run();
671         if (result != ISC_R_SUCCESS)
672                 fatal("isc_app_run() failed: %s", isc_result_totext(result));
673
674         if (connects > 0 || sends > 0 || recvs > 0)
675                 isc_socket_cancel(sock, task, ISC_SOCKCANCEL_ALL);
676
677         isc_task_detach(&task);
678         isc_taskmgr_destroy(&taskmgr);
679         isc_socketmgr_destroy(&socketmgr);
680         isc_log_destroy(&log);
681         isc_log_setcontext(NULL);
682
683         cfg_obj_destroy(pctx, &config);
684         cfg_parser_destroy(&pctx);
685
686         isc_mem_put(mctx, args, argslen);
687         isccc_ccmsg_invalidate(&ccmsg);
688
689         if (show_final_mem)
690                 isc_mem_stats(mctx, stderr);
691
692         isc_mem_destroy(&mctx);
693
694         if (failed)
695                 return (1);
696
697         return (0);
698 }