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