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