xref: /netbsd-src/external/mpl/dhcp/dist/dhcpctl/omshell.c (revision f407d9293b6650aa8c33d6a995f797bb6aaefd90)
1 /*	$NetBSD: omshell.c,v 1.3 2022/04/03 01:10:58 christos Exp $	*/
2 
3 /* omshell.c
4 
5    Examine and modify omapi objects. */
6 
7 /*
8  * Copyright (C) 2004-2022 Internet Systems Consortium, Inc. ("ISC")
9  * Copyright (c) 2001-2003 by Internet Software Consortium
10  *
11  * This Source Code Form is subject to the terms of the Mozilla Public
12  * License, v. 2.0. If a copy of the MPL was not distributed with this
13  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
16  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
17  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
18  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
21  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  *   Internet Systems Consortium, Inc.
24  *   PO Box 360
25  *   Newmarket, NH 03857 USA
26  *   <info@isc.org>
27  *   https://www.isc.org/
28  *
29  */
30 
31 #include <sys/cdefs.h>
32 __RCSID("$NetBSD: omshell.c,v 1.3 2022/04/03 01:10:58 christos Exp $");
33 
34 #include "config.h"
35 
36 #include <time.h>
37 #include <sys/time.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <stdarg.h>
41 #include <string.h>
42 //#include "result.h"
43 #include <syslog.h>
44 #include "dhcpctl.h"
45 #include "dhcpd.h"
46 #include <isc/file.h>
47 
48 extern uint16_t local_port;
49 extern uint16_t remote_port;
50 libdhcp_callbacks_t omshell_callbacks = {
51 	&local_port,
52 	&remote_port,
53 	classify,
54 	check_collection,
55 	dhcp,
56 #ifdef DHCPv6
57 	dhcpv6,
58 #endif /* DHCPv6 */
59 	bootp,
60 	find_class,
61 	parse_allow_deny,
62 	dhcp_set_control_state,
63 };
64 
65 /* Fixups */
find_class(struct class ** c,const char * n,const char * f,int l)66 isc_result_t find_class (struct class **c, const char *n, const char *f, int l)
67 {
68 	return 0;
69 }
parse_allow_deny(struct option_cache ** oc,struct parse * cfile,int flag)70 int parse_allow_deny (struct option_cache **oc, struct parse *cfile, int flag)
71 {
72 	return 0;
73 }
dhcp(struct packet * packet)74 void dhcp (struct packet *packet) { }
bootp(struct packet * packet)75 void bootp (struct packet *packet) { }
76 
77 #ifdef DHCPv6
78 /* XXX: should we warn or something here? */
dhcpv6(struct packet * packet)79 void dhcpv6(struct packet *packet) { }
80 #ifdef DHCP4o6
dhcpv4o6_handler(omapi_object_t * h)81 isc_result_t dhcpv4o6_handler(omapi_object_t *h)
82 {
83 	return ISC_R_NOTIMPLEMENTED;
84 }
85 #endif /* DHCP4o6 */
86 #endif /* DHCPv6 */
87 
check_collection(struct packet * p,struct lease * l,struct collection * c)88 int check_collection (struct packet *p, struct lease *l, struct collection *c)
89 {
90 	return 0;
91 }
classify(struct packet * packet,struct class * class)92 void classify (struct packet *packet, struct class *class) { }
93 
usage(const char * s)94 static void usage (const char *s) {
95 	fprintf (stderr, "Usage: %s\n", s);
96 	exit (1);
97 }
98 
check(isc_result_t status,const char * func)99 static void check (isc_result_t status, const char *func) {
100 	if (status != ISC_R_SUCCESS) {
101 		fprintf (stderr, "%s: %s\n", func, isc_result_totext (status));
102 		exit (1);
103 	}
104 }
105 
106 int
main(int argc,char ** argv)107 main(int argc, char **argv) {
108 	isc_result_t status, waitstatus;
109 	dhcpctl_handle connection;
110 	dhcpctl_handle authenticator;
111 	dhcpctl_handle oh;
112 	struct data_string secret;
113 	const char *name = 0, *algorithm = "hmac-md5";
114 	int i;
115 	int port = 7911;
116 	const char *server = "127.0.0.1";
117 	struct parse *cfile;
118 	enum dhcp_token token;
119 	const char *val;
120 	char *s;
121 	char buf[1024];
122 	char s1[1024];
123 	int connected = 0;
124 	char hex_buf[1025];
125 	char *progname;
126 
127 #ifdef OLD_LOG_NAME
128 	progname = "omshell";
129 #else
130 	progname = argv[0];
131 #endif
132 
133 	libdhcp_callbacks_register(&omshell_callbacks);
134 
135 	for (i = 1; i < argc; i++) {
136 		usage(isc_file_basename(progname));
137 	}
138 
139 	/* Initially, log errors to stderr as well as to syslogd. */
140 	openlog (isc_file_basename(progname),
141 		 DHCP_LOG_OPTIONS, DHCPD_LOG_FACILITY);
142 	status = dhcpctl_initialize ();
143 	if (status != ISC_R_SUCCESS) {
144 		fprintf (stderr, "dhcpctl_initialize: %s\n",
145 			 isc_result_totext (status));
146 		exit (1);
147 	}
148 
149 	memset (&oh, 0, sizeof oh);
150 
151 	do {
152 	    if (!connected) {
153 	    } else if (oh == NULL) {
154 		printf ("obj: <null>\n");
155 	    } else {
156 		dhcpctl_remote_object_t *r = (dhcpctl_remote_object_t *)oh;
157 		omapi_generic_object_t *g =
158 			(omapi_generic_object_t *)(r -> inner);
159 
160 		printf ("obj: ");
161 
162 		if (r -> rtype -> type != omapi_datatype_string) {
163 			printf ("?\n");
164 		} else {
165 			printf ("%.*s\n",
166 				(int)(r -> rtype -> u . buffer . len),
167 				r -> rtype -> u . buffer . value);
168 		}
169 
170 		for (i = 0; i < g -> nvalues; i++) {
171 		    omapi_value_t *v = g -> values [i];
172 
173 		    if (!g -> values [i])
174 			    continue;
175 
176 		    printf ("%.*s = ", (int)v -> name -> len,
177 			    v -> name -> value);
178 
179 		    if (!v -> value) {
180 			printf ("<null>\n");
181 			continue;
182 		    }
183 		    switch (v -> value -> type) {
184 			  case omapi_datatype_int:
185 			    printf ("%d\n",
186 				    v -> value -> u . integer);
187 			    break;
188 
189 			  case omapi_datatype_string:
190 			    printf ("\"%.*s\"\n",
191 				    (int) v -> value -> u.buffer.len,
192 				    v -> value -> u.buffer.value);
193 			    break;
194 
195 			  case omapi_datatype_data:
196 			    print_hex_or_string(v->value->u.buffer.len,
197 						v->value->u.buffer.value,
198 						sizeof(hex_buf), hex_buf);
199 			    printf("%s\n", hex_buf);
200 			    break;
201 
202 			  case omapi_datatype_object:
203 			    printf ("<obj>\n");
204 			    break;
205 		    }
206 		}
207 	    }
208 
209 	    fputs ("> ", stdout);
210 	    fflush (stdout);
211 	    if (fgets (buf, sizeof(buf), stdin) == NULL)
212 		break;
213 
214 	    status = new_parse (&cfile, -1, buf, strlen(buf), "<STDIN>", 1);
215 	    check(status, "new_parse()");
216 
217 	    token = next_token (&val, (unsigned *)0, cfile);
218 	    switch (token) {
219 		  default:
220 		    parse_warn (cfile, "unknown token: %s", val);
221 		    skip_to_semi (cfile);
222 		    break;
223 
224 		  case END_OF_FILE:
225 		  case ENDOFLINE: /* EOL: */
226 		    break;
227 
228 		  case TOKEN_HELP:
229 	          case QUESTIONMARK: /* '?': */
230 		    printf ("Commands:\n");
231 		    printf ("  port <server omapi port>\n");
232 		    printf ("  server <server address>\n");
233 		    printf ("  key <key name> <key value>\n");
234 		    printf ("  connect\n");
235 		    printf ("  disconnect\n");
236 		    printf ("  new <object-type>\n");
237 		    printf ("  set <name> = <value>\n");
238 		    printf ("  create\n");
239 		    printf ("  open\n");
240 		    printf ("  update\n");
241 		    printf ("  unset <name>\n");
242 		    printf ("  refresh\n");
243 		    printf ("  remove\n");
244 		    skip_to_semi (cfile);
245 		    break;
246 
247 		  case PORT:
248 		    token = next_token (&val, (unsigned *)0, cfile);
249 		    if (is_identifier (token)) {
250 			    struct servent *se;
251 			    se = getservbyname (val, "tcp");
252 			    if (se)
253 				    port = ntohs (se -> s_port);
254 			    else {
255 				    printf ("unknown service name: %s\n", val);
256 				    break;
257 			    }
258 		    } else if (token == NUMBER) {
259 			    port = atoi (val);
260 		    } else {
261 			    skip_to_semi (cfile);
262 			    printf ("usage: port <port>\n");
263 			    break;
264 		    }
265 		    token = next_token (&val, (unsigned *)0, cfile);
266 		    if (token != END_OF_FILE && token != EOL) {
267 			    printf ("usage: port <server>\n");
268 			    skip_to_semi (cfile);
269 			    break;
270 		    }
271 		    break;
272 
273 		  case TOKEN_SERVER:
274 		    token = next_token (&val, (unsigned *)0, cfile);
275 		    if (token == NUMBER) {
276 			    int alen = (sizeof buf) - 1;
277 			    int len;
278 
279 			    s = &buf [0];
280 			    len = strlen (val);
281 			    if (len + 1 > alen) {
282 			      baddq:
283 				printf ("usage: server <server>\n");
284 				skip_to_semi (cfile);
285 				break;
286 			    }			    strcpy (buf, val);
287 			    s += len;
288 			    token = next_token (&val, (unsigned *)0, cfile);
289 			    if (token != DOT)
290 				    goto baddq;
291 			    *s++ = '.';
292 			    token = next_token (&val, (unsigned *)0, cfile);
293 			    if (token != NUMBER)
294 				    goto baddq;
295 			    len = strlen (val);
296 			    if (len + 1 > alen)
297 				    goto baddq;
298 			    strcpy (s, val);
299 			    s += len;
300 			    token = next_token (&val, (unsigned *)0, cfile);
301 			    if (token != DOT)
302 				    goto baddq;
303 			    *s++ = '.';
304 			    token = next_token (&val, (unsigned *)0, cfile);
305 			    if (token != NUMBER)
306 				    goto baddq;
307 			    len = strlen (val);
308 			    if (len + 1 > alen)
309 				    goto baddq;
310 			    strcpy (s, val);
311 			    s += len;
312 			    token = next_token (&val, (unsigned *)0, cfile);
313 			    if (token != DOT)
314 				    goto baddq;
315 			    *s++ = '.';
316 			    token = next_token (&val, (unsigned *)0, cfile);
317 			    if (token != NUMBER)
318 				    goto baddq;
319 			    len = strlen (val);
320 			    if (len + 1 > alen)
321 				    goto baddq;
322 			    strcpy (s, val);
323 			    val = &buf [0];
324 		    } else if (is_identifier (token)) {
325 			    /* Use val directly. */
326 		    } else {
327 			    printf ("usage: server <server>\n");
328 			    skip_to_semi (cfile);
329 			    break;
330 		    }
331 
332 		    s = dmalloc (strlen (val) + 1, MDL);
333 		    if (!server) {
334 			    printf ("no memory to store server name.\n");
335 			    skip_to_semi (cfile);
336 			    break;
337 		    }
338 		    strcpy (s, val);
339 		    server = s;
340 
341 		    token = next_token (&val, (unsigned *)0, cfile);
342 		    if (token != END_OF_FILE && token != EOL) {
343 			    printf ("usage: server <server>\n");
344 			    skip_to_semi (cfile);
345 			    break;
346 		    }
347 		    break;
348 
349 		  case KEY_ALGORITHM:
350 		    /* Algorithm is optional */
351 		    token = next_token (&val, (unsigned *)0, cfile);
352 		    if (token != NAME || !is_identifier(token)) {
353 			printf ("missing or invalid algorithm name\n");
354 			printf ("usage: key-algoritm <algorithm name>\n");
355 			skip_to_semi (cfile);
356 			break;
357 		    }
358 
359 		    s = dmalloc (strlen (val) + 1, MDL);
360 		    if (!s) {
361 			printf ("no memory for algorithm name.\n");
362 			skip_to_semi (cfile);
363 			break;
364 		    }
365 
366 		    strcpy (s, val);
367 		    algorithm = s;
368 
369 		    token = next_token (&val, (unsigned *)0, cfile);
370 		    if (token != END_OF_FILE && token != EOL) {
371 			    printf ("extra information after %s\n", algorithm);
372 			    printf ("usage: key-algorithm <algorithm name>\n");
373 			    skip_to_semi (cfile);
374 			    break;
375 		    }
376 
377 		    break;
378 
379 		  case KEY:
380 		    token = peek_token(&val, (unsigned *)0, cfile);
381 		    if (token == STRING) {
382 			    token = next_token (&val, (unsigned *)0, cfile);
383 			    if (!is_identifier (token)) {
384 			            printf ("usage: key <name> <value>\n");
385 				    skip_to_semi (cfile);
386 				    break;
387 			    }
388 			    s = dmalloc (strlen (val) + 1, MDL);
389 			    if (!s) {
390 				    printf ("no memory for key name.\n");
391 				    skip_to_semi (cfile);
392 				    break;
393 			    }
394 			    strcpy (s, val);
395 		    } else {
396 			    s = parse_host_name(cfile);
397 			    if (s == NULL) {
398 			            printf ("usage: key <name> <value>\n");
399 				    skip_to_semi(cfile);
400 				    break;
401 			    }
402 		    }
403 		    name = s;
404 
405 		    memset (&secret, 0, sizeof secret);
406 		    if (!parse_base64 (&secret, cfile)) {
407 			    skip_to_semi (cfile);
408 			    break;
409 		    }
410 
411 		    token = next_token (&val, (unsigned *)0, cfile);
412 		    if (token != END_OF_FILE && token != EOL) {
413 			    printf ("usage: key <name> <value>\n");
414 			    skip_to_semi (cfile);
415 			    break;
416 		    }
417 
418 		    break;
419 
420 		  case CONNECT:
421 		    token = next_token (&val, (unsigned *)0, cfile);
422 		    if (token != END_OF_FILE && token != EOL) {
423 			    printf ("usage: connect\n");
424 			    skip_to_semi (cfile);
425 			    break;
426 		    }
427 
428 		    authenticator = dhcpctl_null_handle;
429 
430 		    if (name) {
431 			status = dhcpctl_new_authenticator (&authenticator,
432 							    name, algorithm,
433 							    secret.data,
434 							    secret.len);
435 
436 			if (status != ISC_R_SUCCESS) {
437 			    fprintf (stderr,
438 				     "Cannot create authenticator: %s\n",
439 				     isc_result_totext (status));
440 			    break;
441 			}
442 		    }
443 
444 		    memset (&connection, 0, sizeof connection);
445 		    status = dhcpctl_connect (&connection,
446 					      server, port, authenticator);
447 		    if (status != ISC_R_SUCCESS) {
448 			    fprintf (stderr, "dhcpctl_connect: %s\n",
449 				     isc_result_totext (status));
450 			    break;
451 		    }
452 		    connected = 1;
453 		    break;
454 
455 		  case DISCONNECT:
456 		    token = next_token (&val, (unsigned *)0, cfile);
457 		    if (token != END_OF_FILE && token != EOL) {
458 			    printf ("usage: disconnect\n");
459 			    skip_to_semi (cfile);
460 			    break;
461 		    }
462 
463 		    if (!connected || !connection) {
464 			fprintf (stderr, "not connected\n");
465 			break;
466 		    }
467 
468 		    status = dhcpctl_disconnect (&connection, 0);
469 		    if (status != ISC_R_SUCCESS) {
470 			    fprintf (stderr, "dhcpctl_disconnect: %s\n",
471 				     isc_result_totext (status));
472 			    break;
473 		    }
474 		    connected = 0;
475 		    break;
476 
477 		  case TOKEN_NEW:
478 		    token = next_token (&val, (unsigned *)0, cfile);
479 		    if ((!is_identifier (token) && token != STRING)) {
480 			    printf ("usage: new <object-type>\n");
481 			    break;
482 		    }
483 
484 		    if (oh) {
485 			    printf ("an object is already open.\n");
486 			    skip_to_semi (cfile);
487 			    break;
488 		    }
489 
490 		    if (!connected) {
491 			    printf ("not connected.\n");
492 			    skip_to_semi (cfile);
493 			    break;
494 		    }
495 
496 		    status = dhcpctl_new_object (&oh, connection, val);
497 		    if (status != ISC_R_SUCCESS) {
498 			    printf ("can't create object: %s\n",
499 				    isc_result_totext (status));
500 			    break;
501 		    }
502 
503 		    token = next_token (&val, (unsigned *)0, cfile);
504 		    if (token != END_OF_FILE && token != EOL) {
505 			    printf ("usage: new <object-type>\n");
506 			    skip_to_semi (cfile);
507 			    break;
508 		    }
509 		    break;
510 
511 		  case TOKEN_CLOSE:
512 		    token = next_token (&val, (unsigned *)0, cfile);
513 		    if (token != END_OF_FILE && token != EOL) {
514 			    printf ("usage: close\n");
515 			    skip_to_semi (cfile);
516 			    break;
517 		    }
518 
519 		    if (!connected) {
520 			    printf ("not connected.\n");
521 			    skip_to_semi (cfile);
522 			    break;
523 		    }
524 
525 		    if (!oh) {
526 			    printf ("not open.\n");
527 			    skip_to_semi (cfile);
528 			    break;
529 		    }
530 		    omapi_object_dereference (&oh, MDL);
531 
532 		    break;
533 
534 		  case TOKEN_SET:
535 		    token = next_token (&val, (unsigned *)0, cfile);
536 
537 		    if ((!is_identifier (token) && token != STRING)) {
538 			  set_usage:
539 			    printf ("usage: set <name> = <value>\n");
540 			    skip_to_semi (cfile);
541 			    break;
542 		    }
543 
544 		    if (oh == NULL) {
545 			    printf ("no open object.\n");
546 			    skip_to_semi (cfile);
547 			    break;
548 		    }
549 
550 		    if (!connected) {
551 			    printf ("not connected.\n");
552 			    skip_to_semi (cfile);
553 			    break;
554 		    }
555 
556 #ifdef HAVE_STRLCPY
557 		    strlcpy (s1, val, sizeof(s1));
558 #else
559 		    s1[0] = 0;
560 		    strncat (s1, val, sizeof(s1)-strlen(s1)-1);
561 #endif
562 
563 		    token = next_token (&val, (unsigned *)0, cfile);
564 		    if (token != EQUAL)
565 			    goto set_usage;
566 
567 		    token = next_token (&val, (unsigned *)0, cfile);
568 		    switch (token) {
569 			  case STRING:
570 			    dhcpctl_set_string_value (oh, val, s1);
571 			    token = next_token (&val, (unsigned *)0, cfile);
572 			    break;
573 
574 			  case NUMBER:
575 			    strcpy (buf, val);
576 			    token = peek_token (&val, (unsigned *)0, cfile);
577 			    /* Colon-separated hex list? */
578 			    if (token == COLON)
579 				goto cshl;
580 			    else if (token == DOT) {
581 				s = buf;
582 				val = buf;
583 				do {
584 				    int intval = atoi (val);
585 				    if (intval > 255) {
586 					parse_warn (cfile,
587 						    "dotted octet > 255: %s",
588 						    val);
589 					skip_to_semi (cfile);
590 					goto badnum;
591 				    }
592 				    *s++ = intval;
593 				    token = next_token (&val,
594 							(unsigned *)0, cfile);
595 				    if (token != DOT)
596 					    break;
597 				    /* DOT is zero. */
598 				    while ((token = next_token (&val,
599 					(unsigned *)0, cfile)) == DOT)
600 					*s++ = 0;
601 				} while (token == NUMBER);
602 				dhcpctl_set_data_value (oh, buf,
603 							(unsigned)(s - buf),
604 							s1);
605 				break;
606 			    }
607 			    dhcpctl_set_int_value (oh, atoi (buf), s1);
608 			    token = next_token (&val, (unsigned *)0, cfile);
609 			  badnum:
610 			    break;
611 
612 			  case NUMBER_OR_NAME:
613 			    strcpy (buf, val);
614 			  cshl:
615 			    s = buf;
616 			    val = buf;
617 			    do {
618 				convert_num (cfile, (unsigned char *)s,
619 					     val, 16, 8);
620 				++s;
621 				token = next_token (&val,
622 						    (unsigned *)0, cfile);
623 				if (token != COLON)
624 				    break;
625 				token = next_token (&val,
626 						    (unsigned *)0, cfile);
627 			    } while (token == NUMBER ||
628 				     token == NUMBER_OR_NAME);
629 			    dhcpctl_set_data_value (oh, buf,
630 						    (unsigned)(s - buf), s1);
631 			    break;
632 
633 			  default:
634 			    printf ("invalid value.\n");
635 			    skip_to_semi (cfile);
636 		    }
637 
638 		    if (token != END_OF_FILE && token != EOL)
639 			    goto set_usage;
640 		    break;
641 
642 		  case UNSET:
643 		    token = next_token (&val, (unsigned *)0, cfile);
644 
645 		    if ((!is_identifier (token) && token != STRING)) {
646 			  unset_usage:
647 			    printf ("usage: unset <name>\n");
648 			    skip_to_semi (cfile);
649 			    break;
650 		    }
651 
652 		    if (!oh) {
653 			    printf ("no open object.\n");
654 			    skip_to_semi (cfile);
655 			    break;
656 		    }
657 
658 		    if (!connected) {
659 			    printf ("not connected.\n");
660 			    skip_to_semi (cfile);
661 			    break;
662 		    }
663 
664 #if HAVE_STRLCPY
665 		    strlcpy (s1, val, sizeof(s1));
666 #else
667 		    s1[0] = 0;
668 		    strncat (s1, val, sizeof(s1)-strlen(s1)-1);
669 #endif
670 
671 		    token = next_token (&val, (unsigned *)0, cfile);
672 		    if (token != END_OF_FILE && token != EOL)
673 			    goto unset_usage;
674 
675 		    dhcpctl_set_null_value (oh, s1);
676 		    break;
677 
678 
679 		  case TOKEN_CREATE:
680 		  case TOKEN_OPEN:
681 		    i = token;
682 		    token = next_token (&val, (unsigned *)0, cfile);
683 		    if (token != END_OF_FILE && token != EOL) {
684 			    printf ("usage: %s\n", val);
685 			    skip_to_semi (cfile);
686 			    break;
687 		    }
688 
689 		    if (!connected) {
690 			    printf ("not connected.\n");
691 			    skip_to_semi (cfile);
692 			    break;
693 		    }
694 
695 		    if (!oh) {
696 			    printf ("you must make a new object first!\n");
697 			    skip_to_semi (cfile);
698 			    break;
699 		    }
700 
701 		    if (i == TOKEN_CREATE)
702 			    i = DHCPCTL_CREATE | DHCPCTL_EXCL;
703 		    else
704 			    i = 0;
705 
706 		    status = dhcpctl_open_object (oh, connection, i);
707 		    if (status == ISC_R_SUCCESS)
708 			    status = dhcpctl_wait_for_completion
709 				    (oh, &waitstatus);
710 		    if (status == ISC_R_SUCCESS)
711 			    status = waitstatus;
712 		    if (status != ISC_R_SUCCESS) {
713 			    printf ("can't open object: %s\n",
714 				    isc_result_totext (status));
715 			    break;
716 		    }
717 
718 		    break;
719 
720 		  case UPDATE:
721 		    token = next_token (&val, (unsigned *)0, cfile);
722 		    if (token != END_OF_FILE && token != EOL) {
723 			    printf ("usage: %s\n", val);
724 			    skip_to_semi (cfile);
725 			    break;
726 		    }
727 
728 		    if (!connected) {
729 			    printf ("not connected.\n");
730 			    skip_to_semi (cfile);
731 			    break;
732 		    }
733 
734 		    if (!oh) {
735 			    printf ("you haven't opened an object yet!\n");
736 			    skip_to_semi (cfile);
737 			    break;
738 		    }
739 
740 		    status = dhcpctl_object_update(connection, oh);
741 		    if (status == ISC_R_SUCCESS)
742 			    status = dhcpctl_wait_for_completion
743 				    (oh, &waitstatus);
744 		    if (status == ISC_R_SUCCESS)
745 			    status = waitstatus;
746 		    if (status != ISC_R_SUCCESS) {
747 			    printf ("can't update object: %s\n",
748 				    isc_result_totext (status));
749 			    break;
750 		    }
751 
752 		    break;
753 
754 		  case REMOVE:
755 		    token = next_token (&val, (unsigned *)0, cfile);
756 		    if (token != END_OF_FILE && token != EOL) {
757 			    printf ("usage: remove\n");
758 			    skip_to_semi (cfile);
759 			    break;
760 		    }
761 
762 		    if (!connected) {
763 			    printf ("not connected.\n");
764 			    break;
765 		    }
766 
767 		    if (!oh) {
768 			    printf ("no object.\n");
769 			    break;
770 		    }
771 
772 		    status = dhcpctl_object_remove(connection, oh);
773 		    if (status == ISC_R_SUCCESS)
774 			    status = dhcpctl_wait_for_completion
775 				    (oh, &waitstatus);
776 		    if (status == ISC_R_SUCCESS)
777 			    status = waitstatus;
778 		    if (status != ISC_R_SUCCESS) {
779 			    printf ("can't destroy object: %s\n",
780 				    isc_result_totext (status));
781 			    break;
782 		    }
783 		    omapi_object_dereference (&oh, MDL);
784 		    break;
785 
786 		  case REFRESH:
787 		    token = next_token (&val, (unsigned *)0, cfile);
788 		    if (token != END_OF_FILE && token != EOL) {
789 			    printf ("usage: refresh\n");
790 			    skip_to_semi (cfile);
791 			    break;
792 		    }
793 
794 		    if (!connected) {
795 			    printf ("not connected.\n");
796 			    break;
797 		    }
798 
799 		    if (!oh) {
800 			    printf ("no object.\n");
801 			    break;
802 		    }
803 
804 		    status = dhcpctl_object_refresh(connection, oh);
805 		    if (status == ISC_R_SUCCESS)
806 			    status = dhcpctl_wait_for_completion
807 				    (oh, &waitstatus);
808 		    if (status == ISC_R_SUCCESS)
809 			    status = waitstatus;
810 		    if (status != ISC_R_SUCCESS) {
811 			    printf ("can't refresh object: %s\n",
812 				    isc_result_totext (status));
813 			    break;
814 		    }
815 
816 		    break;
817 	    }
818 	    end_parse (&cfile);
819 	} while (1);
820 
821 	exit (0);
822 }
823 
824 /* Sigh */
dhcp_set_control_state(control_object_state_t oldstate,control_object_state_t newstate)825 isc_result_t dhcp_set_control_state (control_object_state_t oldstate,
826 				     control_object_state_t newstate)
827 {
828 	if (newstate != server_shutdown)
829 		return ISC_R_SUCCESS;
830 	exit (0);
831 }
832