diff --git a/.gitignore b/.gitignore index f8743a5..655afe5 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /vde_vmnet +*.o diff --git a/Makefile b/Makefile index 5c3eae8..d90c5cd 100644 --- a/Makefile +++ b/Makefile @@ -2,12 +2,20 @@ PREFIX ?= /usr/local CFLAGS ?= -O3 -LDFLAGS ?= -lvdeplug -framework vmnet +VERSION ?= $(shell git describe --match 'v[0-9]*' --dirty='.m' --always --tags) +CFLAGS += -DVERSION=\"$(VERSION)\" + +LDFLAGS += -lvdeplug -framework vmnet all: vde_vmnet -vde_vmnet: *.c - $(CC) $(CFLAGS) -o $@ $(LDFLAGS) $< +OBJS = $(patsubst %.c, %.o, $(wildcard *.c)) + +%.o: %.c *.h + $(CC) $(CFLAGS) -c $< -o $@ + +vde_vmnet: $(OBJS) + $(CC) $(CFLAGS) -o $@ $(LDFLAGS) $(OBJS) install.bin: vde_vmnet install vde_vmnet "$(DESTDIR)/$(PREFIX)/bin/vde_vmnet" @@ -39,4 +47,4 @@ uninstall: uninstall.launchd.plist uninstall.bin .PHONY: clean clean: - rm -f vde_vmnet + rm -f vde_vmnet *.o diff --git a/cli.c b/cli.c new file mode 100644 index 0000000..30781c3 --- /dev/null +++ b/cli.c @@ -0,0 +1,86 @@ +#include +#include +#include + +#include + +#include "cli.h" + +#ifndef VERSION +#define VERSION "UNKNOWN" +#endif + +static void print_usage(const char *argv0) { + printf("Usage: %s [OPTION]... VDESWITCH\n", argv0); + printf("vmnet.framework support for rootless QEMU.\n"); + printf("vde_vmnet does not require QEMU to run as the root user, but " + "vde_vmnet itself has to run as the root, in most cases.\n"); + printf("\n"); + printf("--vde-group=GROUP VDE group name (default: \"staff\")\n"); + printf("-h, --help display this help and exit\n"); + printf("-v, --version display version information and exit\n"); + printf("\n"); + printf("version: " VERSION "\n"); +} + +static void print_version() { puts(VERSION); } + +#define CLI_OPTIONS_ID_VDE_GROUP -42 +struct cli_options *cli_options_parse(int argc, char *argv[]) { + struct cli_options *res = malloc(sizeof(*res)); + if (res == NULL) { + goto error; + } + memset(res, 0, sizeof(*res)); + res->vde_group = strdup("staff"); /* use strdup to make it freeable */ + + const struct option longopts[] = { + {"vde-group", required_argument, NULL, CLI_OPTIONS_ID_VDE_GROUP}, + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'v'}, + {0, 0, 0, 0}, + }; + int opt = 0; + while ((opt = getopt_long(argc, argv, "hv", longopts, NULL)) != -1) { + switch (opt) { + case CLI_OPTIONS_ID_VDE_GROUP: + res->vde_group = strdup(optarg); + break; + case 'h': + print_usage(argv[0]); + cli_options_destroy(res); + exit(EXIT_SUCCESS); + return NULL; + break; + case 'v': + print_version(); + cli_options_destroy(res); + exit(EXIT_SUCCESS); + return NULL; + break; + default: + goto error; + break; + } + } + if (argc - optind != 1) { + goto error; + } + res->vde_switch = strdup(argv[optind]); + return res; +error: + print_usage(argv[0]); + cli_options_destroy(res); + exit(EXIT_FAILURE); + return NULL; +} + +void cli_options_destroy(struct cli_options *x) { + if (x == NULL) + return; + if (x->vde_group != NULL) + free(x->vde_group); + if (x->vde_switch != NULL) + free(x->vde_switch); + free(x); +} diff --git a/cli.h b/cli.h new file mode 100644 index 0000000..858f2b3 --- /dev/null +++ b/cli.h @@ -0,0 +1,12 @@ +#ifndef VDE_VMNET_CLI_H +#define VDE_VMNET_CLI_H + +struct cli_options { + char *vde_group; // --vde-group + char *vde_switch; // arg +}; + +struct cli_options *cli_options_parse(int argc, char *argv[]); +void cli_options_destroy(struct cli_options *); + +#endif /* VDE_VMNET_CLI_H */ diff --git a/main.c b/main.c index dbffefa..af301fb 100644 --- a/main.c +++ b/main.c @@ -1,9 +1,14 @@ -#include +#include #include #include #include + #include +#include + +#include "cli.h" + static bool debug = false; #define DEBUGF(fmt, ...) \ @@ -218,26 +223,22 @@ int main(int argc, char *argv[]) { debug = getenv("DEBUG") != NULL; int rc = 1; VDECONN *vdeconn = NULL; - char *vdeswitch = NULL; // don't free __block interface_ref iface = NULL; void *buf = NULL; - if (argc != 2) { - fprintf(stderr, "Usage: %s VDESWITCH\n", argv[0]); - return 1; - } + struct cli_options *cliopt = cli_options_parse(argc, argv); + assert(cliopt != NULL); if (geteuid() != 0) { fprintf(stderr, "WARNING: Running without root. This is very unlikely to " "work. See README.md .\n"); } - vdeswitch = argv[1]; + DEBUGF("Opening VDE \"%s\" (for UNIX group \"%s\")", cliopt->vde_switch, + cliopt->vde_group); struct vde_open_args vdeargs = { - .port = 0, // VDE switch port number, not TCP port number - .group = "staff", // TODO: this shouldn't be hard-coded ? + .port = 0, // VDE switch port number, not TCP port number + .group = cliopt->vde_group, .mode = 0770, }; - DEBUGF("Opening VDE \"%s\" (for UNIX group \"%s\")", vdeswitch, - vdeargs.group); - vdeconn = vde_open(vdeswitch, "vde_vmnet", &vdeargs); + vdeconn = vde_open(cliopt->vde_switch, "vde_vmnet", &vdeargs); if (vdeconn == NULL) { perror("vde_open"); goto done; @@ -296,5 +297,6 @@ int main(int argc, char *argv[]) { if (buf != NULL) { free(buf); } + cli_options_destroy(cliopt); return rc; }