Home | History | Annotate | Line # | Download | only in dhcpctl
omshell.c revision 1.2.2.2
      1 /*	$NetBSD: omshell.c,v 1.2.2.2 2018/04/16 01:59:47 pgoyette Exp $	*/
      2 
      3 /* omshell.c
      4 
      5    Examine and modify omapi objects. */
      6 
      7 /*
      8  * Copyright (c) 2004-2017 by 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  *   950 Charter Street
     25  *   Redwood City, CA 94063
     26  *   <info (at) isc.org>
     27  *   https://www.isc.org/
     28  *
     29  */
     30 
     31 #include <sys/cdefs.h>
     32 __RCSID("$NetBSD: omshell.c,v 1.2.2.2 2018/04/16 01:59:47 pgoyette 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 uint16_t local_port = 0;
     49 uint16_t remote_port = 0;
     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 */
     66 isc_result_t find_class (struct class **c, const char *n, const char *f, int l)
     67 {
     68 	return 0;
     69 }
     70 int parse_allow_deny (struct option_cache **oc, struct parse *cfile, int flag)
     71 {
     72 	return 0;
     73 }
     74 void dhcp (struct packet *packet) { }
     75 void bootp (struct packet *packet) { }
     76 
     77 #ifdef DHCPv6
     78 /* XXX: should we warn or something here? */
     79 void dhcpv6(struct packet *packet) { }
     80 #ifdef DHCP4o6
     81 isc_result_t dhcpv4o6_handler(omapi_object_t *h)
     82 {
     83 	return ISC_R_NOTIMPLEMENTED;
     84 }
     85 #endif /* DHCP4o6 */
     86 #endif /* DHCPv6 */
     87 
     88 int check_collection (struct packet *p, struct lease *l, struct collection *c)
     89 {
     90 	return 0;
     91 }
     92 void classify (struct packet *packet, struct class *class) { }
     93 
     94 static void usage (const char *s) {
     95 	fprintf (stderr, "Usage: %s\n", s);
     96 	exit (1);
     97 }
     98 
     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
    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 ("  new <object-type>\n");
    236 		    printf ("  set <name> = <value>\n");
    237 		    printf ("  create\n");
    238 		    printf ("  open\n");
    239 		    printf ("  update\n");
    240 		    printf ("  unset <name>\n");
    241 		    printf ("  refresh\n");
    242 		    printf ("  remove\n");
    243 		    skip_to_semi (cfile);
    244 		    break;
    245 
    246 		  case PORT:
    247 		    token = next_token (&val, (unsigned *)0, cfile);
    248 		    if (is_identifier (token)) {
    249 			    struct servent *se;
    250 			    se = getservbyname (val, "tcp");
    251 			    if (se)
    252 				    port = ntohs (se -> s_port);
    253 			    else {
    254 				    printf ("unknown service name: %s\n", val);
    255 				    break;
    256 			    }
    257 		    } else if (token == NUMBER) {
    258 			    port = atoi (val);
    259 		    } else {
    260 			    skip_to_semi (cfile);
    261 			    printf ("usage: port <port>\n");
    262 			    break;
    263 		    }
    264 		    token = next_token (&val, (unsigned *)0, cfile);
    265 		    if (token != END_OF_FILE && token != EOL) {
    266 			    printf ("usage: port <server>\n");
    267 			    skip_to_semi (cfile);
    268 			    break;
    269 		    }
    270 		    break;
    271 
    272 		  case TOKEN_SERVER:
    273 		    token = next_token (&val, (unsigned *)0, cfile);
    274 		    if (token == NUMBER) {
    275 			    int alen = (sizeof buf) - 1;
    276 			    int len;
    277 
    278 			    s = &buf [0];
    279 			    len = strlen (val);
    280 			    if (len + 1 > alen) {
    281 			      baddq:
    282 				printf ("usage: server <server>\n");
    283 				skip_to_semi (cfile);
    284 				break;
    285 			    }			    strcpy (buf, val);
    286 			    s += len;
    287 			    token = next_token (&val, (unsigned *)0, cfile);
    288 			    if (token != DOT)
    289 				    goto baddq;
    290 			    *s++ = '.';
    291 			    token = next_token (&val, (unsigned *)0, cfile);
    292 			    if (token != NUMBER)
    293 				    goto baddq;
    294 			    len = strlen (val);
    295 			    if (len + 1 > alen)
    296 				    goto baddq;
    297 			    strcpy (s, val);
    298 			    s += len;
    299 			    token = next_token (&val, (unsigned *)0, cfile);
    300 			    if (token != DOT)
    301 				    goto baddq;
    302 			    *s++ = '.';
    303 			    token = next_token (&val, (unsigned *)0, cfile);
    304 			    if (token != NUMBER)
    305 				    goto baddq;
    306 			    len = strlen (val);
    307 			    if (len + 1 > alen)
    308 				    goto baddq;
    309 			    strcpy (s, val);
    310 			    s += len;
    311 			    token = next_token (&val, (unsigned *)0, cfile);
    312 			    if (token != DOT)
    313 				    goto baddq;
    314 			    *s++ = '.';
    315 			    token = next_token (&val, (unsigned *)0, cfile);
    316 			    if (token != NUMBER)
    317 				    goto baddq;
    318 			    len = strlen (val);
    319 			    if (len + 1 > alen)
    320 				    goto baddq;
    321 			    strcpy (s, val);
    322 			    val = &buf [0];
    323 		    } else if (is_identifier (token)) {
    324 			    /* Use val directly. */
    325 		    } else {
    326 			    printf ("usage: server <server>\n");
    327 			    skip_to_semi (cfile);
    328 			    break;
    329 		    }
    330 
    331 		    s = dmalloc (strlen (val) + 1, MDL);
    332 		    if (!server) {
    333 			    printf ("no memory to store server name.\n");
    334 			    skip_to_semi (cfile);
    335 			    break;
    336 		    }
    337 		    strcpy (s, val);
    338 		    server = s;
    339 
    340 		    token = next_token (&val, (unsigned *)0, cfile);
    341 		    if (token != END_OF_FILE && token != EOL) {
    342 			    printf ("usage: server <server>\n");
    343 			    skip_to_semi (cfile);
    344 			    break;
    345 		    }
    346 		    break;
    347 
    348 		  case KEY_ALGORITHM:
    349 		    /* Algorithm is optional */
    350 		    token = next_token (&val, (unsigned *)0, cfile);
    351 		    if (token != NAME || !is_identifier(token)) {
    352 			printf ("missing or invalid algorithm name\n");
    353 			printf ("usage: key-algoritm <algorithm name>\n");
    354 			skip_to_semi (cfile);
    355 			break;
    356 		    }
    357 
    358 		    s = dmalloc (strlen (val) + 1, MDL);
    359 		    if (!s) {
    360 			printf ("no memory for algorithm name.\n");
    361 			skip_to_semi (cfile);
    362 			break;
    363 		    }
    364 
    365 		    strcpy (s, val);
    366 		    algorithm = s;
    367 
    368 		    token = next_token (&val, (unsigned *)0, cfile);
    369 		    if (token != END_OF_FILE && token != EOL) {
    370 			    printf ("extra information after %s\n", algorithm);
    371 			    printf ("usage: key-algorithm <algorithm name>\n");
    372 			    skip_to_semi (cfile);
    373 			    break;
    374 		    }
    375 
    376 		    break;
    377 
    378 		  case KEY:
    379 		    token = peek_token(&val, (unsigned *)0, cfile);
    380 		    if (token == STRING) {
    381 			    token = next_token (&val, (unsigned *)0, cfile);
    382 			    if (!is_identifier (token)) {
    383 			            printf ("usage: key <name> <value>\n");
    384 				    skip_to_semi (cfile);
    385 				    break;
    386 			    }
    387 			    s = dmalloc (strlen (val) + 1, MDL);
    388 			    if (!s) {
    389 				    printf ("no memory for key name.\n");
    390 				    skip_to_semi (cfile);
    391 				    break;
    392 			    }
    393 			    strcpy (s, val);
    394 		    } else {
    395 			    s = parse_host_name(cfile);
    396 			    if (s == NULL) {
    397 			            printf ("usage: key <name> <value>\n");
    398 				    skip_to_semi(cfile);
    399 				    break;
    400 			    }
    401 		    }
    402 		    name = s;
    403 
    404 		    memset (&secret, 0, sizeof secret);
    405 		    if (!parse_base64 (&secret, cfile)) {
    406 			    skip_to_semi (cfile);
    407 			    break;
    408 		    }
    409 
    410 		    token = next_token (&val, (unsigned *)0, cfile);
    411 		    if (token != END_OF_FILE && token != EOL) {
    412 			    printf ("usage: key <name> <value>\n");
    413 			    skip_to_semi (cfile);
    414 			    break;
    415 		    }
    416 
    417 		    break;
    418 
    419 		  case CONNECT:
    420 		    token = next_token (&val, (unsigned *)0, cfile);
    421 		    if (token != END_OF_FILE && token != EOL) {
    422 			    printf ("usage: connect\n");
    423 			    skip_to_semi (cfile);
    424 			    break;
    425 		    }
    426 
    427 		    authenticator = dhcpctl_null_handle;
    428 
    429 		    if (name) {
    430 			status = dhcpctl_new_authenticator (&authenticator,
    431 							    name, algorithm,
    432 							    secret.data,
    433 							    secret.len);
    434 
    435 			if (status != ISC_R_SUCCESS) {
    436 			    fprintf (stderr,
    437 				     "Cannot create authenticator: %s\n",
    438 				     isc_result_totext (status));
    439 			    break;
    440 			}
    441 		    }
    442 
    443 		    memset (&connection, 0, sizeof connection);
    444 		    status = dhcpctl_connect (&connection,
    445 					      server, port, authenticator);
    446 		    if (status != ISC_R_SUCCESS) {
    447 			    fprintf (stderr, "dhcpctl_connect: %s\n",
    448 				     isc_result_totext (status));
    449 			    break;
    450 		    }
    451 		    connected = 1;
    452 		    break;
    453 
    454 		  case TOKEN_NEW:
    455 		    token = next_token (&val, (unsigned *)0, cfile);
    456 		    if ((!is_identifier (token) && token != STRING)) {
    457 			    printf ("usage: new <object-type>\n");
    458 			    break;
    459 		    }
    460 
    461 		    if (oh) {
    462 			    printf ("an object is already open.\n");
    463 			    skip_to_semi (cfile);
    464 			    break;
    465 		    }
    466 
    467 		    if (!connected) {
    468 			    printf ("not connected.\n");
    469 			    skip_to_semi (cfile);
    470 			    break;
    471 		    }
    472 
    473 		    status = dhcpctl_new_object (&oh, connection, val);
    474 		    if (status != ISC_R_SUCCESS) {
    475 			    printf ("can't create object: %s\n",
    476 				    isc_result_totext (status));
    477 			    break;
    478 		    }
    479 
    480 		    token = next_token (&val, (unsigned *)0, cfile);
    481 		    if (token != END_OF_FILE && token != EOL) {
    482 			    printf ("usage: new <object-type>\n");
    483 			    skip_to_semi (cfile);
    484 			    break;
    485 		    }
    486 		    break;
    487 
    488 		  case TOKEN_CLOSE:
    489 		    token = next_token (&val, (unsigned *)0, cfile);
    490 		    if (token != END_OF_FILE && token != EOL) {
    491 			    printf ("usage: close\n");
    492 			    skip_to_semi (cfile);
    493 			    break;
    494 		    }
    495 
    496 		    if (!connected) {
    497 			    printf ("not connected.\n");
    498 			    skip_to_semi (cfile);
    499 			    break;
    500 		    }
    501 
    502 		    if (!oh) {
    503 			    printf ("not open.\n");
    504 			    skip_to_semi (cfile);
    505 			    break;
    506 		    }
    507 		    omapi_object_dereference (&oh, MDL);
    508 
    509 		    break;
    510 
    511 		  case TOKEN_SET:
    512 		    token = next_token (&val, (unsigned *)0, cfile);
    513 
    514 		    if ((!is_identifier (token) && token != STRING)) {
    515 			  set_usage:
    516 			    printf ("usage: set <name> = <value>\n");
    517 			    skip_to_semi (cfile);
    518 			    break;
    519 		    }
    520 
    521 		    if (oh == NULL) {
    522 			    printf ("no open object.\n");
    523 			    skip_to_semi (cfile);
    524 			    break;
    525 		    }
    526 
    527 		    if (!connected) {
    528 			    printf ("not connected.\n");
    529 			    skip_to_semi (cfile);
    530 			    break;
    531 		    }
    532 
    533 #ifdef HAVE_STRLCPY
    534 		    strlcpy (s1, val, sizeof(s1));
    535 #else
    536 		    s1[0] = 0;
    537 		    strncat (s1, val, sizeof(s1)-strlen(s1)-1);
    538 #endif
    539 
    540 		    token = next_token (&val, (unsigned *)0, cfile);
    541 		    if (token != EQUAL)
    542 			    goto set_usage;
    543 
    544 		    token = next_token (&val, (unsigned *)0, cfile);
    545 		    switch (token) {
    546 			  case STRING:
    547 			    dhcpctl_set_string_value (oh, val, s1);
    548 			    token = next_token (&val, (unsigned *)0, cfile);
    549 			    break;
    550 
    551 			  case NUMBER:
    552 			    strcpy (buf, val);
    553 			    token = peek_token (&val, (unsigned *)0, cfile);
    554 			    /* Colon-separated hex list? */
    555 			    if (token == COLON)
    556 				goto cshl;
    557 			    else if (token == DOT) {
    558 				s = buf;
    559 				val = buf;
    560 				do {
    561 				    int intval = atoi (val);
    562 				    if (intval > 255) {
    563 					parse_warn (cfile,
    564 						    "dotted octet > 255: %s",
    565 						    val);
    566 					skip_to_semi (cfile);
    567 					goto badnum;
    568 				    }
    569 				    *s++ = intval;
    570 				    token = next_token (&val,
    571 							(unsigned *)0, cfile);
    572 				    if (token != DOT)
    573 					    break;
    574 				    /* DOT is zero. */
    575 				    while ((token = next_token (&val,
    576 					(unsigned *)0, cfile)) == DOT)
    577 					*s++ = 0;
    578 				} while (token == NUMBER);
    579 				dhcpctl_set_data_value (oh, buf,
    580 							(unsigned)(s - buf),
    581 							s1);
    582 				break;
    583 			    }
    584 			    dhcpctl_set_int_value (oh, atoi (buf), s1);
    585 			    token = next_token (&val, (unsigned *)0, cfile);
    586 			  badnum:
    587 			    break;
    588 
    589 			  case NUMBER_OR_NAME:
    590 			    strcpy (buf, val);
    591 			  cshl:
    592 			    s = buf;
    593 			    val = buf;
    594 			    do {
    595 				convert_num (cfile, (unsigned char *)s,
    596 					     val, 16, 8);
    597 				++s;
    598 				token = next_token (&val,
    599 						    (unsigned *)0, cfile);
    600 				if (token != COLON)
    601 				    break;
    602 				token = next_token (&val,
    603 						    (unsigned *)0, cfile);
    604 			    } while (token == NUMBER ||
    605 				     token == NUMBER_OR_NAME);
    606 			    dhcpctl_set_data_value (oh, buf,
    607 						    (unsigned)(s - buf), s1);
    608 			    break;
    609 
    610 			  default:
    611 			    printf ("invalid value.\n");
    612 			    skip_to_semi (cfile);
    613 		    }
    614 
    615 		    if (token != END_OF_FILE && token != EOL)
    616 			    goto set_usage;
    617 		    break;
    618 
    619 		  case UNSET:
    620 		    token = next_token (&val, (unsigned *)0, cfile);
    621 
    622 		    if ((!is_identifier (token) && token != STRING)) {
    623 			  unset_usage:
    624 			    printf ("usage: unset <name>\n");
    625 			    skip_to_semi (cfile);
    626 			    break;
    627 		    }
    628 
    629 		    if (!oh) {
    630 			    printf ("no open object.\n");
    631 			    skip_to_semi (cfile);
    632 			    break;
    633 		    }
    634 
    635 		    if (!connected) {
    636 			    printf ("not connected.\n");
    637 			    skip_to_semi (cfile);
    638 			    break;
    639 		    }
    640 
    641 #if HAVE_STRLCPY
    642 		    strlcpy (s1, val, sizeof(s1));
    643 #else
    644 		    s1[0] = 0;
    645 		    strncat (s1, val, sizeof(s1)-strlen(s1)-1);
    646 #endif
    647 
    648 		    token = next_token (&val, (unsigned *)0, cfile);
    649 		    if (token != END_OF_FILE && token != EOL)
    650 			    goto unset_usage;
    651 
    652 		    dhcpctl_set_null_value (oh, s1);
    653 		    break;
    654 
    655 
    656 		  case TOKEN_CREATE:
    657 		  case TOKEN_OPEN:
    658 		    i = token;
    659 		    token = next_token (&val, (unsigned *)0, cfile);
    660 		    if (token != END_OF_FILE && token != EOL) {
    661 			    printf ("usage: %s\n", val);
    662 			    skip_to_semi (cfile);
    663 			    break;
    664 		    }
    665 
    666 		    if (!connected) {
    667 			    printf ("not connected.\n");
    668 			    skip_to_semi (cfile);
    669 			    break;
    670 		    }
    671 
    672 		    if (!oh) {
    673 			    printf ("you must make a new object first!\n");
    674 			    skip_to_semi (cfile);
    675 			    break;
    676 		    }
    677 
    678 		    if (i == TOKEN_CREATE)
    679 			    i = DHCPCTL_CREATE | DHCPCTL_EXCL;
    680 		    else
    681 			    i = 0;
    682 
    683 		    status = dhcpctl_open_object (oh, connection, i);
    684 		    if (status == ISC_R_SUCCESS)
    685 			    status = dhcpctl_wait_for_completion
    686 				    (oh, &waitstatus);
    687 		    if (status == ISC_R_SUCCESS)
    688 			    status = waitstatus;
    689 		    if (status != ISC_R_SUCCESS) {
    690 			    printf ("can't open object: %s\n",
    691 				    isc_result_totext (status));
    692 			    break;
    693 		    }
    694 
    695 		    break;
    696 
    697 		  case UPDATE:
    698 		    token = next_token (&val, (unsigned *)0, cfile);
    699 		    if (token != END_OF_FILE && token != EOL) {
    700 			    printf ("usage: %s\n", val);
    701 			    skip_to_semi (cfile);
    702 			    break;
    703 		    }
    704 
    705 		    if (!connected) {
    706 			    printf ("not connected.\n");
    707 			    skip_to_semi (cfile);
    708 			    break;
    709 		    }
    710 
    711 		    if (!oh) {
    712 			    printf ("you haven't opened an object yet!\n");
    713 			    skip_to_semi (cfile);
    714 			    break;
    715 		    }
    716 
    717 		    status = dhcpctl_object_update(connection, oh);
    718 		    if (status == ISC_R_SUCCESS)
    719 			    status = dhcpctl_wait_for_completion
    720 				    (oh, &waitstatus);
    721 		    if (status == ISC_R_SUCCESS)
    722 			    status = waitstatus;
    723 		    if (status != ISC_R_SUCCESS) {
    724 			    printf ("can't update object: %s\n",
    725 				    isc_result_totext (status));
    726 			    break;
    727 		    }
    728 
    729 		    break;
    730 
    731 		  case REMOVE:
    732 		    token = next_token (&val, (unsigned *)0, cfile);
    733 		    if (token != END_OF_FILE && token != EOL) {
    734 			    printf ("usage: remove\n");
    735 			    skip_to_semi (cfile);
    736 			    break;
    737 		    }
    738 
    739 		    if (!connected) {
    740 			    printf ("not connected.\n");
    741 			    break;
    742 		    }
    743 
    744 		    if (!oh) {
    745 			    printf ("no object.\n");
    746 			    break;
    747 		    }
    748 
    749 		    status = dhcpctl_object_remove(connection, oh);
    750 		    if (status == ISC_R_SUCCESS)
    751 			    status = dhcpctl_wait_for_completion
    752 				    (oh, &waitstatus);
    753 		    if (status == ISC_R_SUCCESS)
    754 			    status = waitstatus;
    755 		    if (status != ISC_R_SUCCESS) {
    756 			    printf ("can't destroy object: %s\n",
    757 				    isc_result_totext (status));
    758 			    break;
    759 		    }
    760 		    omapi_object_dereference (&oh, MDL);
    761 		    break;
    762 
    763 		  case REFRESH:
    764 		    token = next_token (&val, (unsigned *)0, cfile);
    765 		    if (token != END_OF_FILE && token != EOL) {
    766 			    printf ("usage: refresh\n");
    767 			    skip_to_semi (cfile);
    768 			    break;
    769 		    }
    770 
    771 		    if (!connected) {
    772 			    printf ("not connected.\n");
    773 			    break;
    774 		    }
    775 
    776 		    if (!oh) {
    777 			    printf ("no object.\n");
    778 			    break;
    779 		    }
    780 
    781 		    status = dhcpctl_object_refresh(connection, oh);
    782 		    if (status == ISC_R_SUCCESS)
    783 			    status = dhcpctl_wait_for_completion
    784 				    (oh, &waitstatus);
    785 		    if (status == ISC_R_SUCCESS)
    786 			    status = waitstatus;
    787 		    if (status != ISC_R_SUCCESS) {
    788 			    printf ("can't refresh object: %s\n",
    789 				    isc_result_totext (status));
    790 			    break;
    791 		    }
    792 
    793 		    break;
    794 	    }
    795 	    end_parse (&cfile);
    796 	} while (1);
    797 
    798 	exit (0);
    799 }
    800 
    801 /* Sigh */
    802 isc_result_t dhcp_set_control_state (control_object_state_t oldstate,
    803 				     control_object_state_t newstate)
    804 {
    805 	if (newstate != server_shutdown)
    806 		return ISC_R_SUCCESS;
    807 	exit (0);
    808 }
    809