From 6e262ab3edbfb412ed279dd2fef6b5dad21f124f Mon Sep 17 00:00:00 2001 From: Ariadne Conill Date: Thu, 23 Jul 2020 10:44:45 -0600 Subject: [PATCH] add ifupdown skeleton --- Makefile | 19 +++-- cmd/ifupdown.c | 183 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 197 insertions(+), 5 deletions(-) create mode 100644 cmd/ifupdown.c diff --git a/Makefile b/Makefile index e73d5cb..0a1f262 100644 --- a/Makefile +++ b/Makefile @@ -24,23 +24,32 @@ LIBIFUPDOWN_OBJ = ${LIBIFUPDOWN_SRC:.c=.o} LIBIFUPDOWN_LIB = libifupdown.a -CMDS = \ - ifquery +CMDS = ifquery ifup ifdown LIBS = ${LIBIFUPDOWN_LIB} +all: libifupdown.a ${CMDS} + IFQUERY_SRC = cmd/ifquery.c IFQUERY_OBJ = ${IFQUERY_SRC:.c=.o} ifquery: ${LIBS} ${IFQUERY_OBJ} ${CC} -o $@ ${IFQUERY_OBJ} ${LIBS} +IFUPDOWN_SRC = cmd/ifupdown.c +IFUPDOWN_OBJ = ${IFUPDOWN_SRC:.c=.o} +ifup: ${LIBS} ${IFUPDOWN_OBJ} + ${CC} -o $@ ${IFUPDOWN_OBJ} ${LIBS} + +ifdown: ifup + ln -s ifup $@ + libifupdown.a: ${LIBIFUPDOWN_OBJ} ${AR} -rcs $@ ${LIBIFUPDOWN_OBJ} -all: libifupdown.a ${CMDS} - clean: - rm -f ${LIBIFUPDOWN_OBJ} ${IFQUERY_OBJ} + rm -f ${LIBIFUPDOWN_OBJ} ${IFQUERY_OBJ} ${IFUPDOWN_OBJ} check: libifupdown.a ${CMDS} kyua test + +default: all diff --git a/cmd/ifupdown.c b/cmd/ifupdown.c new file mode 100644 index 0000000..6ebb4d0 --- /dev/null +++ b/cmd/ifupdown.c @@ -0,0 +1,183 @@ +/* + * cmd/ifupdown.c + * Purpose: bring interfaces up or down + * + * Copyright (c) 2020 Ariadne Conill + * + * 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 +#include +#include +#include +#include "libifupdown/libifupdown.h" + +struct match_options { + bool is_auto; + char *exclude_pattern; + char *include_pattern; +}; + +static const char *argv0; +static bool up; +static struct lif_execute_opts exec_opts = {}; + +void +usage() +{ + fprintf(stderr, "usage: %s [options] \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"); + + exit(1); +} + +bool +is_ifdown() +{ + if (strstr(argv0, "ifdown") != NULL) + return true; + + return false; +} + +bool +change_interface(struct lif_interface *iface, struct lif_dict *state, const char *ifname) +{ + if (!lif_lifecycle_run(&exec_opts, iface, state, ifname, up)) + { + fprintf(stderr, "%s: failed to bring interface %s %s\n", + argv0, ifname, up ? "up" : "down"); + return false; + } + + return true; +} + +int +main(int argc, char *argv[]) +{ + argv0 = argv[0]; + up = !is_ifdown(); + + 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'}, + {NULL, 0, 0, 0} + }; + struct match_options match_opts = {}; + char *interfaces_file = INTERFACES_FILE; + char *state_file = STATE_FILE; + + for (;;) + { + int c = getopt_long(argc, argv, "hVi:aI:X:S:nv", long_options, NULL); + if (c == -1) + break; + + switch (c) { + case 'h': + usage(); + break; + case 'V': + lif_common_version(); + break; + case 'i': + 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': + state_file = optarg; + break; + case 'n': + exec_opts.mock = true; + exec_opts.verbose = true; + break; + case 'v': + exec_opts.verbose = true; + break; + } + } + + if (!lif_state_read_path(&state, state_file)) + { + fprintf(stderr, "ifquery: could not parse %s\n", state_file); + return EXIT_FAILURE; + } + + if (!lif_interface_file_parse(&collection, interfaces_file)) + { + fprintf(stderr, "ifquery: could not parse %s\n", interfaces_file); + return EXIT_FAILURE; + } + + if (optind >= argc) + usage(); + + int idx = optind; + for (; idx < argc; idx++) + { + char lifbuf[4096]; + strlcpy(lifbuf, argv[idx], sizeof lifbuf); + + char *ifname = lifbuf; + char *lifname = lifbuf; + char *p; + + if ((p = strchr(lifbuf, '=')) != NULL) + { + *p++ = '\0'; + lifname = p; + } + + struct lif_dict_entry *entry = lif_dict_find(&collection, lifname); + + if (entry == NULL) + { + fprintf(stderr, "%s: unknown interface %s\n", argv0, argv[idx]); + return EXIT_FAILURE; + } + + struct lif_interface *iface = entry->data; + if (!change_interface(iface, &state, ifname)) + return EXIT_FAILURE; + } + + /* XXX: update state file */ + + return EXIT_SUCCESS; +}