Merge pull request #18 from ifupdown-ng/feature/if-option-group
Feature/if option group
This commit is contained in:
		
						commit
						4e3d2dbbc3
					
				
					 8 changed files with 410 additions and 214 deletions
				
			
		
							
								
								
									
										6
									
								
								Makefile
									
										
									
									
									
								
							
							
						
						
									
										6
									
								
								Makefile
									
										
									
									
									
								
							|  | @ -39,7 +39,11 @@ LIBIFUPDOWN_SRC = \ | |||
| LIBIFUPDOWN_OBJ = ${LIBIFUPDOWN_SRC:.c=.o} | ||||
| LIBIFUPDOWN_LIB = libifupdown.a | ||||
| 
 | ||||
| MULTICALL_SRC = cmd/multicall.c | ||||
| MULTICALL_SRC = \
 | ||||
| 	cmd/multicall.c \
 | ||||
| 	cmd/multicall-options.c \
 | ||||
| 	cmd/multicall-exec-options.c \
 | ||||
| 	cmd/multicall-match-options.c | ||||
| MULTICALL_OBJ = ${MULTICALL_SRC:.c=.o} | ||||
| MULTICALL = ifupdown | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										158
									
								
								cmd/ifquery.c
									
										
									
									
									
								
							
							
						
						
									
										158
									
								
								cmd/ifquery.c
									
										
									
									
									
								
							|  | @ -21,11 +21,6 @@ | |||
| #include "libifupdown/libifupdown.h" | ||||
| #include "cmd/multicall.h" | ||||
| 
 | ||||
| static struct lif_execute_opts exec_opts = { | ||||
| 	.interfaces_file = INTERFACES_FILE, | ||||
| 	.executor_path = EXECUTOR_PATH | ||||
| }; | ||||
| 
 | ||||
| void | ||||
| print_interface(struct lif_interface *iface) | ||||
| { | ||||
|  | @ -163,38 +158,6 @@ print_interface_property(struct lif_interface *iface, const char *property) | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| void | ||||
| ifquery_usage(int status) | ||||
| { | ||||
| 	fprintf(stderr, "usage: ifquery [options] <interfaces>\n"); | ||||
| 	fprintf(stderr, "       ifquery [options] --list\n"); | ||||
| 
 | ||||
| 	fprintf(stderr, "\nOptions:\n"); | ||||
| 	fprintf(stderr, "  -h, --help                   this help\n"); | ||||
| 	fprintf(stderr, "  -V, --version                show this program's version\n"); | ||||
| 	fprintf(stderr, "  -i, --interfaces FILE        use FILE for interface definitions\n"); | ||||
| 	fprintf(stderr, "  -L, --list                   list matching interfaces\n"); | ||||
| 	fprintf(stderr, "  -a, --auto                   only match against interfaces hinted as 'auto'\n"); | ||||
| 	fprintf(stderr, "  -I, --include PATTERN        only match against interfaces matching PATTERN\n"); | ||||
| 	fprintf(stderr, "  -X, --exclude PATTERN        never match against interfaces matching PATTERN\n"); | ||||
| 	fprintf(stderr, "  -P, --pretty-print           pretty print the interfaces instead of just listing\n"); | ||||
| 	fprintf(stderr, "  -S, --state-file FILE        use FILE for state\n"); | ||||
| 	fprintf(stderr, "  -s, --state                  show configured state\n"); | ||||
| 	fprintf(stderr, "  -D, --dot                    generate a dependency graph\n"); | ||||
| 	fprintf(stderr, "  -p, --property PROPERTY      print values of properties matching PROPERTY\n"); | ||||
| 
 | ||||
| 	exit(status); | ||||
| } | ||||
| 
 | ||||
| struct match_options { | ||||
| 	bool is_auto; | ||||
| 	char *exclude_pattern; | ||||
| 	char *include_pattern; | ||||
| 	bool pretty_print; | ||||
| 	bool dot; | ||||
| 	char *property; | ||||
| }; | ||||
| 
 | ||||
| void | ||||
| list_interfaces(struct lif_dict *collection, struct match_options *opts) | ||||
| { | ||||
|  | @ -256,83 +219,56 @@ list_state(struct lif_dict *state, struct match_options *opts) | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| static bool listing = false, listing_stat = false; | ||||
| 
 | ||||
| static void | ||||
| handle_local(int short_opt, const struct if_option *opt, const char *opt_arg, const struct if_applet *applet) | ||||
| { | ||||
| 	(void) opt; | ||||
| 	(void) applet; | ||||
| 
 | ||||
| 	switch (short_opt) { | ||||
| 	case 'L': | ||||
| 		listing = true; | ||||
| 		break; | ||||
| 	case 'P': | ||||
| 		match_opts.pretty_print = true; | ||||
| 		break; | ||||
| 	case 's': | ||||
| 		listing_stat = true; | ||||
| 		break; | ||||
| 	case 'D': | ||||
| 		match_opts.dot = true; | ||||
| 		break; | ||||
| 	case 'p': | ||||
| 		match_opts.property = opt_arg; | ||||
| 		break; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static struct if_option local_options[] = { | ||||
| 	{'s', "state", NULL, "show configured state", false, handle_local}, | ||||
| 	{'p', "property", "property PROPERTY", "print values of properties matching PROPERTY", true, handle_local}, | ||||
| 	{'D', "dot", NULL, "generate a dependency graph", false, handle_local}, | ||||
| 	{'L', "list", NULL, "list matching interfaces", false, handle_local}, | ||||
| 	{'P', "pretty-print", NULL, "pretty print the interfaces instead of just listing", false, handle_local}, | ||||
| }; | ||||
| 
 | ||||
| static struct if_option_group local_option_group = { | ||||
| 	.desc = "Program-specific options", | ||||
| 	.group_size = ARRAY_SIZE(local_options), | ||||
| 	.group = local_options | ||||
| }; | ||||
| 
 | ||||
| int | ||||
| ifquery_main(int argc, char *argv[]) | ||||
| { | ||||
| 	struct lif_dict state = {}; | ||||
| 	struct lif_dict collection = {}; | ||||
| 	struct option long_options[] = { | ||||
| 		{"help", no_argument, 0, 'h'}, | ||||
| 		{"version", no_argument, 0, 'V'}, | ||||
| 		{"interfaces", required_argument, 0, 'i'}, | ||||
| 		{"list", no_argument, 0, 'L'}, | ||||
| 		{"auto", no_argument, 0, 'a'}, | ||||
| 		{"include", required_argument, 0, 'I'}, | ||||
| 		{"exclude", required_argument, 0, 'X'}, | ||||
| 		{"pretty-print", no_argument, 0, 'P'}, | ||||
| 		{"state-file", required_argument, 0, 'S'}, | ||||
| 		{"state", no_argument, 0, 's'}, | ||||
| 		{"dot", no_argument, 0, 'D'}, | ||||
| 		{"property", required_argument, 0, 'p'}, | ||||
| 		{"executor-path", required_argument, 0, 'E'}, | ||||
| 		{NULL, 0, 0, 0} | ||||
| 	}; | ||||
| 	struct match_options match_opts = {}; | ||||
| 	bool listing = false, listing_stat = false; | ||||
| 	char *state_file = STATE_FILE; | ||||
| 
 | ||||
| 	for (;;) | ||||
| 	if (!lif_state_read_path(&state, exec_opts.state_file)) | ||||
| 	{ | ||||
| 		int c = getopt_long(argc, argv, "hVi:LaI:X:PS:sDp:E:", long_options, NULL); | ||||
| 		if (c == -1) | ||||
| 			break; | ||||
| 
 | ||||
| 		switch (c) { | ||||
| 		case 'h': | ||||
| 			ifquery_usage(EXIT_SUCCESS); | ||||
| 			break; | ||||
| 		case 'V': | ||||
| 			lif_common_version(); | ||||
| 			break; | ||||
| 		case 'i': | ||||
| 			exec_opts.interfaces_file = optarg; | ||||
| 			break; | ||||
| 		case 'L': | ||||
| 			listing = true; | ||||
| 			break; | ||||
| 		case 'a': | ||||
| 			match_opts.is_auto = true; | ||||
| 			break; | ||||
| 		case 'I': | ||||
| 			match_opts.include_pattern = optarg; | ||||
| 			break; | ||||
| 		case 'X': | ||||
| 			match_opts.exclude_pattern = optarg; | ||||
| 			break; | ||||
| 		case 'P': | ||||
| 			match_opts.pretty_print = true; | ||||
| 			break; | ||||
| 		case 'S': | ||||
| 			state_file = optarg; | ||||
| 			break; | ||||
| 		case 's': | ||||
| 			listing_stat = true; | ||||
| 			break; | ||||
| 		case 'D': | ||||
| 			match_opts.dot = true; | ||||
| 			break; | ||||
| 		case 'p': | ||||
| 			match_opts.property = optarg; | ||||
| 			break; | ||||
| 		case 'E': | ||||
| 			exec_opts.executor_path = optarg; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (!lif_state_read_path(&state, state_file)) | ||||
| 	{ | ||||
| 		fprintf(stderr, "%s: could not parse %s\n", argv0, state_file); | ||||
| 		fprintf(stderr, "%s: could not parse %s\n", argv0, exec_opts.state_file); | ||||
| 		return EXIT_FAILURE; | ||||
| 	} | ||||
| 
 | ||||
|  | @ -344,7 +280,7 @@ ifquery_main(int argc, char *argv[]) | |||
| 
 | ||||
| 	/* --list --state is not allowed */ | ||||
| 	if (listing && listing_stat) | ||||
| 		ifquery_usage(EXIT_FAILURE); | ||||
| 		generic_usage(self_applet, EXIT_FAILURE); | ||||
| 
 | ||||
| 	if (listing) | ||||
| 	{ | ||||
|  | @ -358,7 +294,7 @@ ifquery_main(int argc, char *argv[]) | |||
| 	} | ||||
| 
 | ||||
| 	if (optind >= argc) | ||||
| 		ifquery_usage(EXIT_FAILURE); | ||||
| 		generic_usage(self_applet, EXIT_FAILURE); | ||||
| 
 | ||||
| 	int idx = optind; | ||||
| 	for (; idx < argc; idx++) | ||||
|  | @ -390,6 +326,8 @@ ifquery_main(int argc, char *argv[]) | |||
| 
 | ||||
| struct if_applet ifquery_applet = { | ||||
| 	.name = "ifquery", | ||||
| 	.desc = "query interface configuration", | ||||
| 	.main = ifquery_main, | ||||
| 	.usage = ifquery_usage | ||||
| 	.usage = "ifquery [options] <interfaces>\n  ifquery [options] --list", | ||||
| 	.groups = { &global_option_group, &match_option_group, &exec_option_group, &local_option_group }, | ||||
| }; | ||||
|  |  | |||
							
								
								
									
										105
									
								
								cmd/ifupdown.c
									
										
									
									
									
								
							
							
						
						
									
										105
									
								
								cmd/ifupdown.c
									
										
									
									
									
								
							|  | @ -24,40 +24,7 @@ | |||
| #include "libifupdown/libifupdown.h" | ||||
| #include "cmd/multicall.h" | ||||
| 
 | ||||
| struct match_options { | ||||
| 	bool is_auto; | ||||
| 	char *exclude_pattern; | ||||
| 	char *include_pattern; | ||||
| }; | ||||
| 
 | ||||
| static bool up; | ||||
| static struct lif_execute_opts exec_opts = { | ||||
| 	.executor_path = EXECUTOR_PATH, | ||||
| 	.interfaces_file = INTERFACES_FILE, | ||||
| 	.state_file = STATE_FILE, | ||||
| }; | ||||
| 
 | ||||
| void | ||||
| ifupdown_usage(int status) | ||||
| { | ||||
| 	fprintf(stderr, "usage: %s [options] <interfaces>\n", argv0); | ||||
| 
 | ||||
| 	fprintf(stderr, "\nOptions:\n"); | ||||
| 	fprintf(stderr, "  -h, --help                   this help\n"); | ||||
| 	fprintf(stderr, "  -V, --version                show this program's version\n"); | ||||
| 	fprintf(stderr, "  -i, --interfaces FILE        use FILE for interface definitions\n"); | ||||
| 	fprintf(stderr, "  -S, --state-file FILE        use FILE for state\n"); | ||||
| 	fprintf(stderr, "  -a, --auto                   only match against interfaces hinted as 'auto'\n"); | ||||
| 	fprintf(stderr, "  -I, --include PATTERN        only match against interfaces matching PATTERN\n"); | ||||
| 	fprintf(stderr, "  -X, --exclude PATTERN        never match against interfaces matching PATTERN\n"); | ||||
| 	fprintf(stderr, "  -n, --no-act                 do not actually run any commands\n"); | ||||
| 	fprintf(stderr, "  -v, --verbose                show what commands are being run\n"); | ||||
| 	fprintf(stderr, "  -E, --executor-path PATH     use PATH for executor directory\n"); | ||||
| 	fprintf(stderr, "  -f, --force                  force (de)configuration\n"); | ||||
| 	fprintf(stderr, "  -L, --no-lock                do not use a lockfile to serialize state changes\n"); | ||||
| 
 | ||||
| 	exit(status); | ||||
| } | ||||
| 
 | ||||
| bool | ||||
| is_ifdown() | ||||
|  | @ -195,68 +162,6 @@ ifupdown_main(int argc, char *argv[]) | |||
| 
 | ||||
| 	struct lif_dict state = {}; | ||||
| 	struct lif_dict collection = {}; | ||||
| 	struct option long_options[] = { | ||||
| 		{"help", no_argument, 0, 'h'}, | ||||
| 		{"version", no_argument, 0, 'V'}, | ||||
| 		{"interfaces", required_argument, 0, 'i'}, | ||||
| 		{"auto", no_argument, 0, 'a'}, | ||||
| 		{"include", required_argument, 0, 'I'}, | ||||
| 		{"exclude", required_argument, 0, 'X'}, | ||||
| 		{"state-file", required_argument, 0, 'S'}, | ||||
| 		{"no-act", no_argument, 0, 'n'}, | ||||
| 		{"verbose", no_argument, 0, 'v'}, | ||||
| 		{"executor-path", required_argument, 0, 'E'}, | ||||
| 		{"force", no_argument, 0, 'f'}, | ||||
| 		{"no-lock", no_argument, 0, 'L'}, | ||||
| 		{NULL, 0, 0, 0} | ||||
| 	}; | ||||
| 	struct match_options match_opts = {}; | ||||
| 
 | ||||
| 	for (;;) | ||||
| 	{ | ||||
| 		int c = getopt_long(argc, argv, "hVi:aI:X:S:nvE:fL", long_options, NULL); | ||||
| 		if (c == -1) | ||||
| 			break; | ||||
| 
 | ||||
| 		switch (c) { | ||||
| 		case 'h': | ||||
| 			ifupdown_usage(EXIT_SUCCESS); | ||||
| 			break; | ||||
| 		case 'V': | ||||
| 			lif_common_version(); | ||||
| 			break; | ||||
| 		case 'i': | ||||
| 			exec_opts.interfaces_file = optarg; | ||||
| 			break; | ||||
| 		case 'a': | ||||
| 			match_opts.is_auto = true; | ||||
| 			break; | ||||
| 		case 'I': | ||||
| 			match_opts.include_pattern = optarg; | ||||
| 			break; | ||||
| 		case 'X': | ||||
| 			match_opts.exclude_pattern = optarg; | ||||
| 			break; | ||||
| 		case 'S': | ||||
| 			exec_opts.state_file = optarg; | ||||
| 			break; | ||||
| 		case 'n': | ||||
| 			exec_opts.mock = true; | ||||
| 			exec_opts.verbose = true; | ||||
| 			break; | ||||
| 		case 'v': | ||||
| 			exec_opts.verbose = true; | ||||
| 			break; | ||||
| 		case 'E': | ||||
| 			exec_opts.executor_path = optarg; | ||||
| 			break; | ||||
| 		case 'f': | ||||
| 			break; | ||||
| 		case 'L': | ||||
| 			exec_opts.no_lock = true; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (!lif_state_read_path(&state, exec_opts.state_file)) | ||||
| 	{ | ||||
|  | @ -284,7 +189,7 @@ ifupdown_main(int argc, char *argv[]) | |||
| 		return EXIT_SUCCESS; | ||||
| 	} | ||||
| 	else if (optind >= argc) | ||||
| 		ifupdown_usage(EXIT_FAILURE); | ||||
| 		generic_usage(self_applet, EXIT_FAILURE); | ||||
| 
 | ||||
| 	int idx = optind; | ||||
| 	for (; idx < argc; idx++) | ||||
|  | @ -331,12 +236,16 @@ ifupdown_main(int argc, char *argv[]) | |||
| 
 | ||||
| struct if_applet ifup_applet = { | ||||
| 	.name = "ifup", | ||||
| 	.desc = "bring interfaces up", | ||||
| 	.main = ifupdown_main, | ||||
| 	.usage = ifupdown_usage | ||||
| 	.usage = "ifup [options] <interfaces>", | ||||
| 	.groups = { &global_option_group, &match_option_group, &exec_option_group, }, | ||||
| }; | ||||
| 
 | ||||
| struct if_applet ifdown_applet = { | ||||
| 	.name = "ifdown", | ||||
| 	.desc = "take interfaces down", | ||||
| 	.main = ifupdown_main, | ||||
| 	.usage = ifupdown_usage | ||||
| 	.usage = "ifdown [options] <interfaces>", | ||||
| 	.groups = { &global_option_group, &match_option_group, &exec_option_group, }, | ||||
| }; | ||||
|  |  | |||
							
								
								
									
										77
									
								
								cmd/multicall-exec-options.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								cmd/multicall-exec-options.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,77 @@ | |||
| /*
 | ||||
|  * cmd/multicall-exec-options.c | ||||
|  * Purpose: multi-call binary frontend -- option handling | ||||
|  * | ||||
|  * Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org> | ||||
|  * | ||||
|  * Permission to use, copy, modify, and/or distribute this software for any | ||||
|  * purpose with or without fee is hereby granted, provided that the above | ||||
|  * copyright notice and this permission notice appear in all copies. | ||||
|  * | ||||
|  * This software is provided 'as is' and without any warranty, express or | ||||
|  * implied.  In no event shall the authors be liable for any damages arising | ||||
|  * from the use of this software. | ||||
|  */ | ||||
| 
 | ||||
| #define _GNU_SOURCE | ||||
| #include <libgen.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <getopt.h> | ||||
| #include "cmd/multicall.h" | ||||
| 
 | ||||
| struct lif_execute_opts exec_opts = { | ||||
| 	.interfaces_file = INTERFACES_FILE, | ||||
| 	.executor_path = EXECUTOR_PATH, | ||||
| 	.state_file = STATE_FILE | ||||
| }; | ||||
| 
 | ||||
| static void handle_exec(int short_opt, const struct if_option *opt, const char *opt_arg, const struct if_applet *applet) | ||||
| { | ||||
| 	(void) opt; | ||||
| 	(void) applet; | ||||
| 
 | ||||
| 	switch (short_opt) | ||||
| 	{ | ||||
| 		case 'i': | ||||
| 			exec_opts.interfaces_file = opt_arg; | ||||
| 			break; | ||||
| 		case 'S': | ||||
| 			exec_opts.state_file = opt_arg; | ||||
| 			break; | ||||
| 		case 'n': | ||||
| 			exec_opts.mock = true; | ||||
| 			exec_opts.verbose = true; | ||||
| 			break; | ||||
| 		case 'v': | ||||
| 			exec_opts.verbose = true; | ||||
| 			break; | ||||
| 		case 'E': | ||||
| 			exec_opts.executor_path = opt_arg; | ||||
| 			break; | ||||
| 		case 'f': | ||||
| 			break; | ||||
| 		case 'l': | ||||
| 			exec_opts.no_lock = true; | ||||
| 			break; | ||||
| 		default: | ||||
| 			break; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static struct if_option exec_options[] = { | ||||
| 	{'f', "force", NULL, "force (de)configuration", true, handle_exec}, | ||||
| 	{'i', "interfaces", "interfaces FILE", "use FILE for interface definitions", true, handle_exec}, | ||||
| 	{'l', "no-lock", NULL, "do not use a lockfile to serialize state changes", false, handle_exec}, | ||||
| 	{'n', "no-act", NULL, "do not actually run any commands", false, handle_exec}, | ||||
| 	{'v', "verbose", NULL, "show what commands are being run", false, handle_exec}, | ||||
| 	{'E', "executor-path", "executor-path PATH", "use PATH for executor directory", true, handle_exec}, | ||||
| 	{'S', "state-file", "state-file FILE", "use FILE for state", true, handle_exec}, | ||||
| }; | ||||
| 
 | ||||
| struct if_option_group exec_option_group = { | ||||
| 	.desc = "Execution", | ||||
| 	.group_size = ARRAY_SIZE(exec_options), | ||||
| 	.group = exec_options | ||||
| }; | ||||
							
								
								
									
										57
									
								
								cmd/multicall-match-options.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								cmd/multicall-match-options.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,57 @@ | |||
| /*
 | ||||
|  * cmd/multicall-match-options.c | ||||
|  * Purpose: multi-call binary frontend -- option handling | ||||
|  * | ||||
|  * Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org> | ||||
|  * | ||||
|  * Permission to use, copy, modify, and/or distribute this software for any | ||||
|  * purpose with or without fee is hereby granted, provided that the above | ||||
|  * copyright notice and this permission notice appear in all copies. | ||||
|  * | ||||
|  * This software is provided 'as is' and without any warranty, express or | ||||
|  * implied.  In no event shall the authors be liable for any damages arising | ||||
|  * from the use of this software. | ||||
|  */ | ||||
| 
 | ||||
| #define _GNU_SOURCE | ||||
| #include <libgen.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <getopt.h> | ||||
| #include "cmd/multicall.h" | ||||
| 
 | ||||
| struct match_options match_opts = {}; | ||||
| 
 | ||||
| static void handle_match(int short_opt, const struct if_option *opt, const char *opt_arg, const struct if_applet *applet) | ||||
| { | ||||
| 	(void) opt; | ||||
| 	(void) applet; | ||||
| 
 | ||||
| 	switch (short_opt) | ||||
| 	{ | ||||
| 	case 'a': | ||||
| 		match_opts.is_auto = true; | ||||
| 		break; | ||||
| 	case 'I': | ||||
| 		match_opts.include_pattern = opt_arg; | ||||
| 		break; | ||||
| 	case 'X': | ||||
| 		match_opts.exclude_pattern = opt_arg; | ||||
| 		break; | ||||
| 	default: | ||||
| 		break; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static struct if_option match_options[] = { | ||||
| 	{'a', "auto", NULL, "only match against interfaces hinted as 'auto'", false, handle_match}, | ||||
| 	{'I', "include", "include PATTERN", "only match against interfaces matching PATTERN", true, handle_match}, | ||||
| 	{'X', "exclude", "exclude PATTERN", "never match against interfaces matching PATTERN", true, handle_match}, | ||||
| }; | ||||
| 
 | ||||
| struct if_option_group match_option_group = { | ||||
| 	.desc = "Matching interfaces", | ||||
| 	.group_size = ARRAY_SIZE(match_options), | ||||
| 	.group = match_options | ||||
| }; | ||||
							
								
								
									
										159
									
								
								cmd/multicall-options.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										159
									
								
								cmd/multicall-options.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,159 @@ | |||
| /*
 | ||||
|  * cmd/multicall-options.c | ||||
|  * Purpose: multi-call binary frontend -- option handling | ||||
|  * | ||||
|  * Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org> | ||||
|  * | ||||
|  * Permission to use, copy, modify, and/or distribute this software for any | ||||
|  * purpose with or without fee is hereby granted, provided that the above | ||||
|  * copyright notice and this permission notice appear in all copies. | ||||
|  * | ||||
|  * This software is provided 'as is' and without any warranty, express or | ||||
|  * implied.  In no event shall the authors be liable for any damages arising | ||||
|  * from the use of this software. | ||||
|  */ | ||||
| 
 | ||||
| #define _GNU_SOURCE | ||||
| #include <libgen.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <getopt.h> | ||||
| #include "cmd/multicall.h" | ||||
| 
 | ||||
| void | ||||
| generic_usage(const struct if_applet *applet, int result) | ||||
| { | ||||
| 	fprintf(stderr, "%s", applet->name); | ||||
| 	if (applet->desc != NULL) | ||||
| 		fprintf(stderr, " - %s", applet->desc); | ||||
| 
 | ||||
| 	fprintf(stderr, "\n"); | ||||
| 
 | ||||
| 	if (applet->usage != NULL) | ||||
| 		fprintf(stderr, "\nUsage:\n  %s\n", applet->usage); | ||||
| 
 | ||||
| 	size_t iter; | ||||
| 	for (iter = 0; applet->groups[iter] != NULL; iter++) | ||||
| 	{ | ||||
| 		const struct if_option_group *group = applet->groups[iter]; | ||||
| 
 | ||||
| 		fprintf(stderr, "\n%s:\n", group->desc); | ||||
| 
 | ||||
| 		size_t group_iter; | ||||
| 		for (group_iter = 0; group_iter < group->group_size; group_iter++) | ||||
| 		{ | ||||
| 			const struct if_option *opt = &group->group[group_iter]; | ||||
| 
 | ||||
| 			fprintf(stderr, "  "); | ||||
| 
 | ||||
| 			if (opt->short_opt) | ||||
| 				fprintf(stderr, "-%c", opt->short_opt); | ||||
| 			else | ||||
| 				fprintf(stderr, "  "); | ||||
| 
 | ||||
| 			if (opt->long_opt) | ||||
| 				fprintf(stderr, "%c --%-30s", opt->short_opt ? ',' : ' ', | ||||
| 					opt->long_opt_desc ? opt->long_opt_desc : opt->long_opt); | ||||
| 			else | ||||
| 				fprintf(stderr, "%34s", ""); | ||||
| 
 | ||||
| 			fprintf(stderr, "%s\n", opt->desc); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	exit(result); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| generic_usage_request(int short_opt, const struct if_option *option, const char *opt_arg, const struct if_applet *applet) | ||||
| { | ||||
| 	(void) short_opt; | ||||
| 	(void) option; | ||||
| 	(void) opt_arg; | ||||
| 
 | ||||
| 	generic_usage(applet, EXIT_SUCCESS); | ||||
| } | ||||
| 
 | ||||
| static struct if_option global_options[] = { | ||||
| 	{'h', "help", NULL, "this help", false, generic_usage_request}, | ||||
| 	{'V', "version", NULL, "show this program's version", false, (void *) lif_common_version}, | ||||
| }; | ||||
| 
 | ||||
| struct if_option_group global_option_group = { | ||||
| 	.desc = "Global options", | ||||
| 	.group_size = ARRAY_SIZE(global_options), | ||||
| 	.group = global_options | ||||
| }; | ||||
| 
 | ||||
| const struct if_option * | ||||
| lookup_option(const struct if_applet *applet, int opt) | ||||
| { | ||||
| 	size_t iter; | ||||
| 	for (iter = 0; applet->groups[iter] != NULL; iter++) | ||||
| 	{ | ||||
| 		const struct if_option_group *group = applet->groups[iter]; | ||||
| 		size_t group_iter; | ||||
| 
 | ||||
| 		for (group_iter = 0; group_iter < group->group_size; group_iter++) | ||||
| 		{ | ||||
| 			const struct if_option *option = &group->group[group_iter]; | ||||
| 
 | ||||
| 			if (option->short_opt == opt) | ||||
| 				return option; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| void | ||||
| process_options(const struct if_applet *applet, int argc, char *argv[]) | ||||
| { | ||||
| 	char short_opts[256] = {}, *p = short_opts; | ||||
| 	struct option long_opts[256] = {}; | ||||
| 
 | ||||
| 	size_t iter, long_opt_iter = 0; | ||||
| 	for (iter = 0; applet->groups[iter] != NULL; iter++) | ||||
| 	{ | ||||
| 		const struct if_option_group *group = applet->groups[iter]; | ||||
| 		size_t group_iter; | ||||
| 
 | ||||
| 		for (group_iter = 0; group_iter < group->group_size; group_iter++) | ||||
| 		{ | ||||
| 			const struct if_option *opt = &group->group[group_iter]; | ||||
| 
 | ||||
| 			if (opt->short_opt) | ||||
| 			{ | ||||
| 				*p++ = opt->short_opt; | ||||
| 				if (opt->require_argument) | ||||
| 					*p++ = ':'; | ||||
| 			} | ||||
| 
 | ||||
| 			if (opt->long_opt) | ||||
| 			{ | ||||
| 				/* XXX: handle long-opts without short-opts */ | ||||
| 				long_opts[long_opt_iter] = (struct option) { | ||||
| 					.name = opt->long_opt, | ||||
| 					.has_arg = opt->require_argument ? required_argument : no_argument, | ||||
| 					.val = opt->short_opt | ||||
| 				}; | ||||
| 
 | ||||
| 				long_opt_iter++; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	for (;;) | ||||
| 	{ | ||||
| 		int c = getopt_long(argc, argv, short_opts, long_opts, NULL); | ||||
| 		if (c == -1) | ||||
| 			break; | ||||
| 
 | ||||
| 		const struct if_option *opt = lookup_option(applet, c); | ||||
| 		if (opt == NULL) | ||||
| 			break; | ||||
| 
 | ||||
| 		opt->handle(c, opt, optarg, applet); | ||||
| 	} | ||||
| } | ||||
|  | @ -13,10 +13,12 @@ | |||
|  * from the use of this software. | ||||
|  */ | ||||
| 
 | ||||
| #define _GNU_SOURCE | ||||
| #include <libgen.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <getopt.h> | ||||
| #include "cmd/multicall.h" | ||||
| 
 | ||||
| char *argv0; | ||||
|  | @ -25,6 +27,7 @@ extern struct if_applet ifquery_applet; | |||
| extern struct if_applet ifup_applet; | ||||
| extern struct if_applet ifdown_applet; | ||||
| struct if_applet ifupdown_applet; | ||||
| const struct if_applet *self_applet = NULL; | ||||
| 
 | ||||
| struct if_applet *applet_table[] = { | ||||
| 	&ifdown_applet, | ||||
|  | @ -33,8 +36,6 @@ struct if_applet *applet_table[] = { | |||
| 	&ifupdown_applet, | ||||
| }; | ||||
| 
 | ||||
| #define ARRAY_SIZE(x)	(sizeof(x) / sizeof(*x)) | ||||
| 
 | ||||
| int | ||||
| applet_cmp(const void *a, const void *b) | ||||
| { | ||||
|  | @ -50,7 +51,7 @@ int | |||
| main(int argc, char *argv[]) | ||||
| { | ||||
| 	argv0 = basename(argv[0]); | ||||
| 	struct if_applet **app; | ||||
| 	const struct if_applet **app; | ||||
| 
 | ||||
| 	app = bsearch(argv0, applet_table, | ||||
| 		      ARRAY_SIZE(applet_table), sizeof(*applet_table), | ||||
|  | @ -62,6 +63,9 @@ main(int argc, char *argv[]) | |||
| 		multicall_usage(EXIT_FAILURE); | ||||
| 	} | ||||
| 
 | ||||
| 	self_applet = *app; | ||||
| 	process_options(*app, argc, argv); | ||||
| 
 | ||||
| 	return (*app)->main(argc, argv); | ||||
| } | ||||
| 
 | ||||
|  | @ -74,6 +78,8 @@ multicall_main(int argc, char *argv[]) | |||
| 	return main(argc - 1, argv + 1); | ||||
| } | ||||
| 
 | ||||
| struct if_applet ifupdown_applet; | ||||
| 
 | ||||
| void | ||||
| multicall_usage(int status) | ||||
| { | ||||
|  | @ -96,5 +102,5 @@ multicall_usage(int status) | |||
| struct if_applet ifupdown_applet = { | ||||
| 	.name = "ifupdown", | ||||
| 	.main = multicall_main, | ||||
| 	.usage = multicall_usage, | ||||
| 	.groups = { &global_option_group, NULL } | ||||
| }; | ||||
|  |  | |||
|  | @ -13,17 +13,63 @@ | |||
|  * from the use of this software. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdbool.h> | ||||
| 
 | ||||
| #ifndef IFUPDOWN_CMD_MULTICALL_H__GUARD | ||||
| #define IFUPDOWN_CMD_MULTICALL_H__GUARD | ||||
| 
 | ||||
| #include "libifupdown/libifupdown.h" | ||||
| 
 | ||||
| #define ARRAY_SIZE(x)   (sizeof(x) / sizeof(*x)) | ||||
| 
 | ||||
| struct if_applet; | ||||
| 
 | ||||
| struct if_option { | ||||
| 	char short_opt; | ||||
| 	const char *long_opt; | ||||
| 	const char *long_opt_desc; | ||||
| 	const char *desc; | ||||
| 	bool require_argument; | ||||
| 	void (*const handle)(int short_opt, const struct if_option *opt, const char *opt_arg, const struct if_applet *applet); | ||||
| }; | ||||
| 
 | ||||
| struct if_option_group { | ||||
| 	const char *desc; | ||||
| 	size_t group_size; | ||||
| 	const struct if_option *group; | ||||
| }; | ||||
| 
 | ||||
| struct if_applet { | ||||
| 	const char *name; | ||||
| 	const char *desc; | ||||
| 	const char *usage; | ||||
| 	int (*const main)(int argc, char *argv[]); | ||||
| 	void (*const usage)(int status); | ||||
| 	const struct if_option_group *groups[16]; | ||||
| }; | ||||
| 
 | ||||
| extern char *argv0; | ||||
| extern const struct if_applet *self_applet; | ||||
| extern struct if_option_group global_option_group; | ||||
| 
 | ||||
| struct match_options { | ||||
| 	bool is_auto; | ||||
| 	const char *exclude_pattern; | ||||
| 	const char *include_pattern; | ||||
| 	bool pretty_print; | ||||
| 	bool dot; | ||||
| 	const char *property; | ||||
| }; | ||||
| 
 | ||||
| extern struct match_options match_opts; | ||||
| 
 | ||||
| extern void process_options(const struct if_applet *applet, int argc, char *argv[]); | ||||
| extern const struct if_option *lookup_option(const struct if_applet *applet, int opt); | ||||
| 
 | ||||
| extern struct if_option_group match_option_group; | ||||
| 
 | ||||
| extern struct lif_execute_opts exec_opts; | ||||
| extern struct if_option_group exec_option_group; | ||||
| 
 | ||||
| void generic_usage(const struct if_applet *applet, int result); | ||||
| 
 | ||||
| #endif | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue