Deleted Added
full compact
1/*
2 * Copyright (c) 2001-2003
3 * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * Author: Hartmut Brandt <harti@freebsd.org>
28 */
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD: head/sbin/atm/atmconfig/main.c 119172 2003-08-20 08:25:36Z harti $");
30__FBSDID("$FreeBSD: head/sbin/atm/atmconfig/main.c 132493 2004-07-21 09:42:56Z harti $");
31
32#include <sys/types.h>
33#include <sys/sysctl.h>
34#include <netdb.h>
35#include <stdarg.h>
36#include <ctype.h>
37#include <limits.h>
38#include <inttypes.h>
38#include <stdint.h>
39#include <fnmatch.h>
40#include <dirent.h>
41#include "atmconfig.h"
42#include "private.h"
43
44/* verbosity level */
45int verbose;
46
47/* notitle option */
48static int notitle;
49
50/* need to put heading before next output */
51static int need_heading;
52
53/*
54 * TOP LEVEL commands
55 */
56static void help_func(int argc, char *argv[]) __dead2;
57
58static const struct cmdtab main_tab[] = {
59 { "help", NULL, help_func },
60 { "options", NULL, NULL },
61 { "commands", NULL, NULL },
62 { "diag", diag_tab, NULL },
63 { "natm", natm_tab, NULL },
64 { NULL, NULL, NULL }
65};
66
67static int
68substr(const char *s1, const char *s2)
69{
70 return (strlen(s1) <= strlen(s2) && strncmp(s1, s2, strlen(s1)) == 0);
71}
72
73/*
74 * Current help file state
75 */
76struct help_file {
77 int file_state; /* 0:looking for main file, 1:found, 2:other */
78 const char *p_start; /* current path pointer */
79 const char *p_end; /* end of current path in path */
80 char *dirname; /* directory name */
81 DIR *dir; /* open directory */
82 char *fname; /* current filename */
83 FILE *fp; /* open file */
84 char line[LINE_MAX]; /* current line */
85 u_int fcnt; /* count of files found */
86};
87
88struct help_pos {
89 off_t pos; /* file position */
90 u_int fcnt; /* number of file */
91 char *fname; /* name of file */
92 const char *p_start; /* current path pointer */
93 const char *p_end; /* end of current path in path */
94};
95
96static int
97help_next_file(struct help_file *hp)
98{
99 const char *fpat;
100 struct dirent *ent;
101
102 if (hp->file_state == 3)
103 return (-1);
104
105 if (hp->file_state == 0)
106 fpat = FILE_HELP;
107 else
108 fpat = FILE_HELP_OTHERS;
109
110 if (hp->file_state == 0 || hp->file_state == 1) {
111 /* start from beginning */
112 hp->p_start = PATH_HELP;
113 hp->file_state++;
114 }
115
116 try_file:
117 if (hp->dir != NULL) {
118 /* directory open (must be state 2) */
119 while ((ent = readdir(hp->dir)) != NULL) {
120 if (fnmatch(fpat, ent->d_name, FNM_NOESCAPE) != 0)
121 continue;
122 if (asprintf(&hp->fname, "%s/%s", hp->dirname,
123 ent->d_name) == -1)
124 err(1, NULL);
125 if ((hp->fp = fopen(hp->fname, "r")) != NULL) {
126 hp->fcnt++;
127 return (0);
128 }
129 free(hp->fname);
130 }
131 /* end of directory */
132 closedir(hp->dir);
133 hp->dir = NULL;
134 free(hp->dirname);
135 goto next_path;
136 }
137
138 /* nothing open - advanc to new path element */
139 try_path:
140 for (hp->p_end = hp->p_start; *hp->p_end != '\0' &&
141 *hp->p_end != ':'; hp->p_end++)
142 ;
143
144 if (asprintf(&hp->dirname, "%.*s", (int)(hp->p_end - hp->p_start),
145 hp->p_start) == -1)
146 err(1, NULL);
147
148 if (hp->file_state == 1) {
149 /* just try to open */
150 if (asprintf(&hp->fname, "%s/%s", hp->dirname, fpat) == -1)
151 err(1, NULL);
152 if ((hp->fp = fopen(hp->fname, "r")) != NULL) {
153 hp->fcnt++;
154 return (0);
155 }
156 free(hp->fname);
157
158 goto next_path;
159 }
160
161 /* open directory */
162 if ((hp->dir = opendir(hp->dirname)) != NULL)
163 goto try_file;
164
165 free(hp->dirname);
166
167 next_path:
168 hp->p_start = hp->p_end;
169 if (*hp->p_start == '\0') {
170 /* end of path */
171 if (hp->file_state == 1)
172 errx(1, "help file not found");
173 return (-1);
174 }
175 hp->p_start++;
176 goto try_path;
177
178}
179
180/*
181 * Save current file position
182 */
183static void
184help_file_tell(struct help_file *hp, struct help_pos *pos)
185{
186 if (pos->fname != NULL)
187 free(pos->fname);
188 if ((pos->fname = strdup(hp->fname)) == NULL)
189 err(1, NULL);
190 pos->fcnt = hp->fcnt;
191 pos->p_start = hp->p_start;
192 pos->p_end = hp->p_end;
193 if ((pos->pos = ftello(hp->fp)) == -1)
194 err(1, "%s", pos->fname);
195}
196
197/*
198 * Go to that position
199 *
200 * We can go either to the original help file or back in the current file.
201 */
202static void
203help_file_seek(struct help_file *hp, struct help_pos *pos)
204{
205 hp->p_start = pos->p_start;
206 hp->p_end = pos->p_end;
207 hp->fcnt = pos->fcnt;
208
209 if (hp->dir != NULL) {
210 free(hp->dirname);
211 closedir(hp->dir);
212 hp->dir = NULL;
213 }
214
215 if (hp->fp != NULL &&strcmp(hp->fname, pos->fname) != 0) {
216 free(hp->fname);
217 fclose(hp->fp);
218 hp->fp = NULL;
219 }
220 if (hp->fp == NULL) {
221 if ((hp->fname = strdup(pos->fname)) == NULL)
222 err(1, NULL);
223 if ((hp->fp = fopen(hp->fname, "r")) == NULL)
224 err(1, "reopen %s", hp->fname);
225 }
226 if (fseeko(hp->fp, pos->pos, SEEK_SET) == -1)
227 err(1, "seek %s", hp->fname);
228
229 if (pos->fcnt == 1)
230 /* go back to state 1 */
231 hp->file_state = 1;
232 else
233 /* lock */
234 hp->file_state = 3;
235}
236
237/*
238 * Rewind to position 0
239 */
240static void
241help_file_rewind(struct help_file *hp)
242{
243
244 if (hp->file_state == 1) {
245 if (fseeko(hp->fp, (off_t)0, SEEK_SET) == -1)
246 err(1, "rewind help file");
247 return;
248 }
249
250 if (hp->dir != NULL) {
251 free(hp->dirname);
252 closedir(hp->dir);
253 hp->dir = NULL;
254 }
255
256 if (hp->fp != NULL) {
257 free(hp->fname);
258 fclose(hp->fp);
259 hp->fp = NULL;
260 }
261 memset(hp, 0, sizeof(*hp));
262}
263
264/*
265 * Get next line from a help file
266 */
267static const char *
268help_next_line(struct help_file *hp)
269{
270 for (;;) {
271 if (hp->fp != NULL) {
272 if (fgets(hp->line, sizeof(hp->line), hp->fp) != NULL)
273 return (hp->line);
274 if (ferror(hp->fp))
275 err(1, "%s", hp->fname);
276 free(hp->fname);
277
278 fclose(hp->fp);
279 hp->fp = NULL;
280 }
281 if (help_next_file(hp) == -1)
282 return (NULL);
283 }
284
285}
286
287/*
288 * This function prints the available 0-level help topics from all
289 * other help files by scanning the files. It assumes, that this is called
290 * only from the main help file.
291 */
292static void
293help_get_0topics(struct help_file *hp)
294{
295 struct help_pos save;
296 const char *line;
297
298 memset(&save, 0, sizeof(save));
299 help_file_tell(hp, &save);
300
301 help_file_rewind(hp);
302 while ((line = help_next_line(hp)) != NULL) {
303 if (line[0] == '^' && line[1] == '^')
304 printf("%s", line + 2);
305 }
306 help_file_seek(hp, &save);
307}
308
309/*
310 * Function to print help. The help argument is in argv[0] here.
311 */
312static void
313help_func(int argc, char *argv[])
314{
77 FILE *hp;
78 const char *start, *end;
79 char *fname;
80 off_t match, last_match;
81 char line[LINE_MAX];
315 struct help_file hfile;
316 struct help_pos match, last_match;
317 const char *line;
318 char key[100];
83 int level, i;
319 int level;
320 int i, has_sub_topics;
321
85 /*
86 * Find the help file
87 */
88 hp = NULL;
89 for (start = PATH_HELP; *start != '\0'; start = end + 1) {
90 for (end = start; *end != ':' && *end != '\0'; end++)
91 ;
92 if (start == end) {
93 if (asprintf(&fname, "%s", FILE_HELP) == -1)
94 err(1, NULL);
95 } else {
96 if (asprintf(&fname, "%.*s/%s", (int)(end - start),
97 start, FILE_HELP) == -1)
98 err(1, NULL);
99 }
100 if ((hp = fopen(fname, "r")) != NULL)
101 break;
102 free(fname);
103 }
104 if (hp == NULL)
105 errx(1, "help file not found");
322 memset(&hfile, 0, sizeof(hfile));
323 memset(&match, 0, sizeof(match));
324 memset(&last_match, 0, sizeof(last_match));
325
326 if (argc == 0) {
327 /* only 'help' - show intro */
328 if ((argv[0] = strdup("intro")) == NULL)
329 err(1, NULL);
330 argc = 1;
331 }
332
333 optind = 0;
114 match = -1;
115 last_match = -1;
334 match.pos = -1;
335 last_match.pos = -1;
336 for (;;) {
337 /* read next line */
118 if (fgets(line, sizeof(line), hp) == NULL) {
119 if (ferror(hp))
120 err(1, fname);
338 if ((line = help_next_line(&hfile)) == NULL) {
339 /* EOF */
122 clearerr(hp);
340 level = 999;
341 goto stop;
342 }
126 if (line[0] != '^')
343 if (line[0] != '^' || line[1] == '^')
344 continue;
345
346 if (sscanf(line + 1, "%d%99s", &level, key) != 2)
347 errx(1, "error in help file '%s'", line);
348
349 if (level < optind) {
350 stop:
351 /* next higher level entry - stop this level */
135 if (match == -1) {
352 if (match.pos == -1) {
353 /* not found */
354 goto not_found;
355 }
356 /* go back to the match */
140 if (fseeko(hp, match, SEEK_SET) == -1)
141 err(1, fname);
357 help_file_seek(&hfile, &match);
358 last_match = match;
143 match = -1;
359 memset(&match, 0, sizeof(match));
360 match.pos = -1;
361
362 /* go to next key */
363 if (++optind >= argc)
364 break;
365 }
366 if (level == optind) {
367 if (substr(argv[optind], key)) {
151 if (match != -1) {
368 if (match.pos != -1) {
369 printf("Ambiguous topic.");
370 goto list_topics;
371 }
155 if ((match = ftello(hp)) == -1)
156 err(1, fname);
372 help_file_tell(&hfile, &match);
373 }
374 }
375 }
160 if (last_match == -1) {
161 if (fseek(hp, 0L, SEEK_SET) == -1)
162 err(1, fname);
163 } else {
164 if (fseeko(hp, last_match, SEEK_SET) == -1)
165 err(1, fname);
166 }
376
377 /* before breaking above we have seeked back to the matching point */
378 for (;;) {
169 if (fgets(line, sizeof(line), hp) == NULL) {
170 if (ferror(hp))
171 err(1, fname);
379 if ((line = help_next_line(&hfile)) == NULL)
380 break;
173 }
381
382 if (line[0] == '#')
383 continue;
176 if (line[0] == '^')
384 if (line[0] == '^') {
385 if (line[1] == '^')
386 continue;
387 break;
388 }
389 if (strncmp(line, "$MAIN", 5) == 0) {
390 help_get_0topics(&hfile);
391 continue;
392 }
393 printf("%s", line);
394 }
395
396 exit(0);
397
398 not_found:
399 printf("Topic not found.");
400
401 list_topics:
187 printf(" Use one of:\natmconfig");
402 printf(" Use one of:\natmconfig help");
403 for (i = 0; i < optind; i++)
404 printf(" %s", argv[i]);
405
406 printf(" [");
407
408 /* list all the keys at this level */
192 if (last_match == -1) {
193 if (fseek(hp, 0L, SEEK_SET) == -1)
194 err(1, fname);
195 } else {
196 if (fseeko(hp, last_match, SEEK_SET) == -1)
197 err(1, fname);
198 }
409 if (last_match.pos == -1)
410 /* go back to start of help */
411 help_file_rewind(&hfile);
412 else
413 help_file_seek(&hfile, &last_match);
414
200 for (;;) {
201 /* read next line */
202 if (fgets(line, sizeof(line), hp) == NULL) {
203 if (ferror(hp))
204 err(1, fname);
205 break;
206 }
207 if (line[0] == '#' || line[0] != '^')
415 has_sub_topics = 0;
416 while ((line = help_next_line(&hfile)) != NULL) {
417 if (line[0] == '#' || line[0] != '^' || line[1] == '^')
418 continue;
419
420 if (sscanf(line + 1, "%d%99s", &level, key) != 2)
421 errx(1, "error in help file '%s'", line);
422
423 if (level < optind)
424 break;
215 if (level == optind)
425 if (level == optind) {
426 has_sub_topics = 1;
427 printf(" %s", key);
428 }
429 }
218 printf(" ]\n");
430 printf(" ].");
431 if (!has_sub_topics)
432 printf(" No sub-topics found.");
433 printf("\n");
434 exit(1);
435}
436
437int
438main(int argc, char *argv[])
439{
440 int opt, i;
441 const struct cmdtab *match, *cc, *tab;
442
443 while ((opt = getopt(argc, argv, "htv")) != -1)
444 switch (opt) {
445
446 case 'h':
447 help_func(0, argv);
448
449 case 'v':
450 verbose++;
451 break;
452
453 case 't':
454 notitle = 1;
455 break;
456 }
457
458 if (argv[optind] == NULL)
459 help_func(0, argv);
460
461 argc -= optind;
462 argv += optind;
463
464 cc = main_tab;
465 i = 0;
466 for (;;) {
467 /*
468 * Scan the table for a match
469 */
470 tab = cc;
471 match = NULL;
472 while (cc->string != NULL) {
473 if (substr(argv[i], cc->string)) {
474 if (match != NULL) {
475 printf("Ambiguous option '%s'",
476 argv[i]);
477 cc = tab;
478 goto subopts;
479 }
480 match = cc;
481 }
482 cc++;
483 }
484 if ((cc = match) == NULL) {
485 printf("Unknown option '%s'", argv[i]);
486 cc = tab;
487 goto subopts;
488 }
489
490 /*
491 * Have a match. If there is no subtable, there must
492 * be either a handler or the command is only a help entry.
493 */
494 if (cc->sub == NULL) {
495 if (cc->func != NULL)
496 break;
497 printf("Unknown option '%s'", argv[i]);
498 cc = tab;
499 goto subopts;
500 }
501
502 /*
503 * Look at the next argument. If it doesn't exist or it
504 * looks like a switch, terminate the scan here.
505 */
506 if (argv[i + 1] == NULL || argv[i + 1][0] == '-') {
507 if (cc->func != NULL)
508 break;
509 printf("Need sub-option for '%s'", argv[i]);
510 cc = cc->sub;
511 goto subopts;
512 }
513
514 cc = cc->sub;
515 i++;
516 }
517
518 argc -= i + 1;
519 argv += i + 1;
520
521 (*cc->func)(argc, argv);
522
523 return (0);
524
525 subopts:
526 printf(". Select one of:\n");
527 while (cc->string != NULL) {
528 if (cc->func != NULL || cc->sub != NULL)
529 printf("%s ", cc->string);
530 cc++;
531 }
532 printf("\n");
533
534 return (1);
535}
536
537void
538verb(const char *fmt, ...)
539{
540 va_list ap;
541
542 if (verbose) {
543 va_start(ap, fmt);
544 vfprintf(stderr, fmt, ap);
545 fprintf(stderr, "\n");
546 va_end(ap);
547 }
548}
549
550void
551heading(const char *fmt, ...)
552{
553 va_list ap;
554
555 if (need_heading) {
556 need_heading = 0;
557 if (!notitle) {
558 va_start(ap, fmt);
559 fprintf(stdout, fmt, ap);
560 va_end(ap);
561 }
562 }
563}
564
565void
566heading_init(void)
567{
568 need_heading = 1;
569}
570
571/*
572 * stringify an enumerated value
573 */
574const char *
575penum(int32_t value, const struct penum *strtab, char *buf)
576{
577 while (strtab->str != NULL) {
578 if (strtab->value == value) {
579 strcpy(buf, strtab->str);
580 return (buf);
581 }
582 strtab++;
583 }
584 warnx("illegal value for enumerated variable '%d'", value);
585 strcpy(buf, "?");
586 return (buf);
587}
588
589/*
590 * Parse command line options
591 */
592int
593parse_options(int *pargc, char ***pargv, const struct option *opts)
594{
595 const struct option *o, *m;
596 char *arg;
597 u_long ularg, ularg1;
598 long larg;
599 char *end;
600
601 if (*pargc == 0)
602 return (-1);
603 arg = (*pargv)[0];
604 if (arg[0] != '-' || arg[1] == '\0')
605 return (-1);
606 if (arg[1] == '-' && arg[2] == '\0') {
607 (*pargv)++;
608 (*pargc)--;
609 return (-1);
610 }
611
612 m = NULL;
613 for (o = opts; o->optstr != NULL; o++) {
614 if (strlen(arg + 1) <= strlen(o->optstr) &&
615 strncmp(arg + 1, o->optstr, strlen(arg + 1)) == 0) {
616 if (m != NULL)
617 errx(1, "ambiguous option '%s'", arg);
618 m = o;
619 }
620 }
621 if (m == NULL)
622 errx(1, "unknown option '%s'", arg);
623
624 (*pargv)++;
625 (*pargc)--;
626
627 if (m->opttype == OPT_NONE)
628 return (m - opts);
629
630 if (m->opttype == OPT_SIMPLE) {
631 *(int *)m->optarg = 1;
632 return (m - opts);
633 }
634
635 if (*pargc == 0)
636 errx(1, "option requires argument '%s'", arg);
637 optarg = *(*pargv)++;
638 (*pargc)--;
639
640 switch (m->opttype) {
641
642 case OPT_UINT:
643 ularg = strtoul(optarg, &end, 0);
644 if (*end != '\0')
645 errx(1, "bad unsigned integer argument for '%s'", arg);
646 if (ularg > UINT_MAX)
647 errx(1, "argument to large for option '%s'", arg);
648 *(u_int *)m->optarg = (u_int)ularg;
649 break;
650
651 case OPT_INT:
652 larg = strtol(optarg, &end, 0);
653 if (*end != '\0')
654 errx(1, "bad integer argument for '%s'", arg);
655 if (larg > INT_MAX || larg < INT_MIN)
656 errx(1, "argument out of range for option '%s'", arg);
657 *(int *)m->optarg = (int)larg;
658 break;
659
660 case OPT_UINT32:
661 ularg = strtoul(optarg, &end, 0);
662 if (*end != '\0')
663 errx(1, "bad unsigned integer argument for '%s'", arg);
664 if (ularg > UINT32_MAX)
665 errx(1, "argument to large for option '%s'", arg);
666 *(uint32_t *)m->optarg = (uint32_t)ularg;
667 break;
668
669 case OPT_INT32:
670 larg = strtol(optarg, &end, 0);
671 if (*end != '\0')
672 errx(1, "bad integer argument for '%s'", arg);
673 if (larg > INT32_MAX || larg < INT32_MIN)
674 errx(1, "argument out of range for option '%s'", arg);
675 *(int32_t *)m->optarg = (int32_t)larg;
676 break;
677
678 case OPT_UINT64:
679 *(uint64_t *)m->optarg = strtoull(optarg, &end, 0);
680 if (*end != '\0')
681 errx(1, "bad unsigned integer argument for '%s'", arg);
682 break;
683
684 case OPT_INT64:
685 *(int64_t *)m->optarg = strtoll(optarg, &end, 0);
686 if (*end != '\0')
687 errx(1, "bad integer argument for '%s'", arg);
688 break;
689
690 case OPT_FLAG:
691 if (strcasecmp(optarg, "enable") == 0 ||
692 strcasecmp(optarg, "yes") == 0 ||
693 strcasecmp(optarg, "true") == 0 ||
694 strcasecmp(optarg, "on") == 0 ||
695 strcmp(optarg, "1") == 0)
696 *(int *)m->optarg = 1;
697 else if (strcasecmp(optarg, "disable") == 0 ||
698 strcasecmp(optarg, "no") == 0 ||
699 strcasecmp(optarg, "false") == 0 ||
700 strcasecmp(optarg, "off") == 0 ||
701 strcmp(optarg, "0") == 0)
702 *(int *)m->optarg = 0;
703 else
704 errx(1, "bad boolean argument to '%s'", arg);
705 break;
706
707 case OPT_VCI:
708 ularg = strtoul(optarg, &end, 0);
709 if (*end == '.') {
710 ularg1 = strtoul(end + 1, &end, 0);
711 } else {
712 ularg1 = ularg;
713 ularg = 0;
714 }
715 if (*end != '\0')
716 errx(1, "bad VCI value for option '%s'", arg);
717 if (ularg > 0xff)
718 errx(1, "VPI value too large for option '%s'", arg);
719 if (ularg1 > 0xffff)
720 errx(1, "VCI value too large for option '%s'", arg);
721 ((u_int *)m->optarg)[0] = ularg;
722 ((u_int *)m->optarg)[1] = ularg1;
723 break;
724
725 case OPT_STRING:
726 if (m->optarg != NULL)
727 *(const char **)m->optarg = optarg;
728 break;
729
730 default:
731 errx(1, "(internal) bad option type %u for '%s'",
732 m->opttype, arg);
733 }
734 return (m - opts);
735}