From 05e0ac139be5e1c15875b5dfaa3caf60424082e5 Mon Sep 17 00:00:00 2001 From: "A. Wilcox" Date: Wed, 12 Aug 2020 17:38:15 -0500 Subject: [PATCH 1/5] ifctrstat: Initial structure for new applet --- .gitignore | 1 + Makefile | 7 +++ cmd/ifctrstat.c | 111 ++++++++++++++++++++++++++++++++++++++++++ cmd/ifctrstat_linux.c | 37 ++++++++++++++ cmd/multicall.c | 7 +++ 5 files changed, 163 insertions(+) create mode 100644 cmd/ifctrstat.c create mode 100644 cmd/ifctrstat_linux.c diff --git a/.gitignore b/.gitignore index 03cfe15..54feeec 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,5 @@ ifupdown ifquery ifup ifdown +ifctrstat *.lock diff --git a/Makefile b/Makefile index 91aee84..a284e5a 100644 --- a/Makefile +++ b/Makefile @@ -62,6 +62,13 @@ MULTICALL_${CONFIG_IFQUERY}_OBJ += ${IFQUERY_SRC:.c=.o} CMDS_${CONFIG_IFQUERY} += ifquery CPPFLAGS_${CONFIG_IFQUERY} += -DCONFIG_IFQUERY +# enable ifctrstat applet (+9 KB) +CONFIG_IFCTRSTAT ?= Y +IFCTRSTAT_SRC = cmd/ifctrstat.c cmd/ifctrstat_linux.c +MULTICALL_${CONFIG_IFCTRSTAT}_OBJ += ${IFCTRSTAT_SRC:.c=.o} +CMDS_${CONFIG_IFCTRSTAT} += ifctrstat +CPPFLAGS_${CONFIG_IFCTRSTAT} += -DCONFIG_IFCTRSTAT + MULTICALL_OBJ += ${MULTICALL_Y_OBJ} CMDS += ${CMDS_Y} CPPFLAGS += ${CPPFLAGS_Y} diff --git a/cmd/ifctrstat.c b/cmd/ifctrstat.c new file mode 100644 index 0000000..aa4460b --- /dev/null +++ b/cmd/ifctrstat.c @@ -0,0 +1,111 @@ +/* + * cmd/ifctrstat.c + * Purpose: Display statistics about interfaces on the system + * + * Copyright (c) 2020 Adélie Software in the Public Benefit, Inc. + * + * 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. + */ + +#include +#include +#include +#include +#include "libifupdown/libifupdown.h" +#include "cmd/multicall.h" + +extern const char *avail_counters[]; +extern int avail_counters_count; + +extern const char *read_counter(const char *interface, const char *counter); + +static bool +counter_is_valid(const char *candidate) +{ + for (int i = 0; i < avail_counters_count; i++) + { + if (strcasecmp(avail_counters[i], candidate) == 0) + return true; + } + + return false; +} + +void +ifstats_list_counters(int short_opt, const struct if_option *opt, + const char *opt_arg, const struct if_applet *applet) +{ + (void) short_opt; + (void) opt; + (void) opt_arg; + (void) applet; + + for (int i = 0; i < avail_counters_count; i++) { + fprintf(stdout, "%s\n", avail_counters[i]); + } + + exit(EXIT_SUCCESS); +} + +int +ifstats_main(int argc, char *argv[]) +{ + if (optind >= argc) + generic_usage(self_applet, EXIT_FAILURE); + + int idx = optind; + if (argc - idx < 2) + { + fprintf(stderr, "%s: interface and counter(s) required\n", + argv0); + return EXIT_FAILURE; + } + + const char *iface = argv[idx++]; + + for (; idx < argc; idx++) + { + if (!counter_is_valid(argv[idx])) + { + fprintf(stderr, "%s: counter %s is not valid or not " + "available\n", argv0, argv[idx]); + return EXIT_FAILURE; + } + + errno = 0; + const char *res = read_counter(iface, argv[idx]); + if (!res) + { + fprintf(stderr, "%s: could not determine value of " + "%s for interface %s: %s\n", argv0, + argv[idx], iface, strerror(errno)); + return EXIT_FAILURE; + } + } + + return EXIT_SUCCESS; +} + +static struct if_option local_options[] = { + {'L', "list", NULL, "List available counters", false, ifstats_list_counters} +}; + +static struct if_option_group local_option_group = { + .desc = "Program-specific options", + .group_size = ARRAY_SIZE(local_options), + .group = local_options +}; + +struct if_applet ifctrstat_applet = { + .name = "ifctrstat", + .desc = "Display statistics about an interface", + .main = ifstats_main, + .usage = "ifctrstat [options] \n ifctrstat [options] --list\n", + .groups = { &global_option_group, &local_option_group, NULL } +}; diff --git a/cmd/ifctrstat_linux.c b/cmd/ifctrstat_linux.c new file mode 100644 index 0000000..0d572da --- /dev/null +++ b/cmd/ifctrstat_linux.c @@ -0,0 +1,37 @@ +/* + * cmd/ifctrstat_linux.c + * Purpose: Implement ifstats system-specific routines for Linux + * + * Copyright (c) 2020 Adélie Software in the Public Benefit, Inc. + * + * 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. + */ + +#include +#include + +const char *avail_counters[] = { + "rx.octets", + "rx.packets", + "rx.discard", + "rx.errors", + "tx.octets", + "tx.packets", + "tx.discard", + "tx.errors" +}; + +size_t avail_counters_count = sizeof(avail_counters) / sizeof(*avail_counters); + +const char * +read_counter(const char *interface, const char *counter) +{ + errno = ENOSYS; + return NULL; +} diff --git a/cmd/multicall.c b/cmd/multicall.c index 8f18f81..0c041ad 100644 --- a/cmd/multicall.c +++ b/cmd/multicall.c @@ -32,10 +32,17 @@ extern struct if_applet ifup_applet; extern struct if_applet ifdown_applet; #endif +#ifdef CONFIG_IFCTRSTAT +extern struct if_applet ifctrstat_applet; +#endif + struct if_applet ifupdown_applet; const struct if_applet *self_applet = NULL; struct if_applet *applet_table[] = { +#ifdef CONFIG_IFCTRSTAT + &ifctrstat_applet, +#endif #ifdef CONFIG_IFUPDOWN &ifdown_applet, #endif From 95d943ee44e3f0f4ca2fcb48bf0f2096fd21f785 Mon Sep 17 00:00:00 2001 From: "A. Wilcox" Date: Fri, 14 Aug 2020 10:17:01 -0500 Subject: [PATCH 2/5] ifctrstat: Misc style fixes --- Makefile | 2 +- cmd/{ifctrstat_linux.c => ifctrstat-linux.c} | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) rename cmd/{ifctrstat_linux.c => ifctrstat-linux.c} (81%) diff --git a/Makefile b/Makefile index a284e5a..4df2adf 100644 --- a/Makefile +++ b/Makefile @@ -64,7 +64,7 @@ CPPFLAGS_${CONFIG_IFQUERY} += -DCONFIG_IFQUERY # enable ifctrstat applet (+9 KB) CONFIG_IFCTRSTAT ?= Y -IFCTRSTAT_SRC = cmd/ifctrstat.c cmd/ifctrstat_linux.c +IFCTRSTAT_SRC = cmd/ifctrstat.c cmd/ifctrstat-${LAYOUT}.c MULTICALL_${CONFIG_IFCTRSTAT}_OBJ += ${IFCTRSTAT_SRC:.c=.o} CMDS_${CONFIG_IFCTRSTAT} += ifctrstat CPPFLAGS_${CONFIG_IFCTRSTAT} += -DCONFIG_IFCTRSTAT diff --git a/cmd/ifctrstat_linux.c b/cmd/ifctrstat-linux.c similarity index 81% rename from cmd/ifctrstat_linux.c rename to cmd/ifctrstat-linux.c index 0d572da..664e37e 100644 --- a/cmd/ifctrstat_linux.c +++ b/cmd/ifctrstat-linux.c @@ -1,6 +1,6 @@ /* - * cmd/ifctrstat_linux.c - * Purpose: Implement ifstats system-specific routines for Linux + * cmd/ifctrstat-linux.c + * Purpose: Implement ifctrstat system-specific routines for Linux * * Copyright (c) 2020 Adélie Software in the Public Benefit, Inc. * @@ -15,6 +15,7 @@ #include #include +#include "multicall.h" const char *avail_counters[] = { "rx.octets", @@ -27,7 +28,7 @@ const char *avail_counters[] = { "tx.errors" }; -size_t avail_counters_count = sizeof(avail_counters) / sizeof(*avail_counters); +size_t avail_counters_count = ARRAY_SIZE(avail_counters); const char * read_counter(const char *interface, const char *counter) From 6106164d7c2fe45bd6c6875af7ff41acdab6e9d0 Mon Sep 17 00:00:00 2001 From: "A. Wilcox" Date: Fri, 14 Aug 2020 10:49:45 -0500 Subject: [PATCH 3/5] ifctrstat: Add ability to output counter values --- Makefile | 2 +- cmd/ifctrstat-linux.c | 68 +++++++++++++++++++++++++++++++++++++++++-- cmd/ifctrstat.c | 58 +++++++++++++++++++++++++++--------- 3 files changed, 111 insertions(+), 17 deletions(-) diff --git a/Makefile b/Makefile index 4df2adf..d050032 100644 --- a/Makefile +++ b/Makefile @@ -62,7 +62,7 @@ MULTICALL_${CONFIG_IFQUERY}_OBJ += ${IFQUERY_SRC:.c=.o} CMDS_${CONFIG_IFQUERY} += ifquery CPPFLAGS_${CONFIG_IFQUERY} += -DCONFIG_IFQUERY -# enable ifctrstat applet (+9 KB) +# enable ifctrstat applet (+16 KB) CONFIG_IFCTRSTAT ?= Y IFCTRSTAT_SRC = cmd/ifctrstat.c cmd/ifctrstat-${LAYOUT}.c MULTICALL_${CONFIG_IFCTRSTAT}_OBJ += ${IFCTRSTAT_SRC:.c=.o} diff --git a/cmd/ifctrstat-linux.c b/cmd/ifctrstat-linux.c index 664e37e..52d48b8 100644 --- a/cmd/ifctrstat-linux.c +++ b/cmd/ifctrstat-linux.c @@ -14,7 +14,9 @@ */ #include +#include #include +#include #include "multicall.h" const char *avail_counters[] = { @@ -30,9 +32,69 @@ const char *avail_counters[] = { size_t avail_counters_count = ARRAY_SIZE(avail_counters); -const char * +char * read_counter(const char *interface, const char *counter) { - errno = ENOSYS; - return NULL; + FILE *fp; + const char *path; + char full_path[PATH_MAX]; + char buffer[1024]; + size_t in_count; + + errno = 0; + + if (strcasecmp(counter, "rx.octets") == 0) + { + path = "rx_bytes"; + } else if (strcasecmp(counter, "rx.packets") == 0) { + path = "rx_packets"; + } else if (strcasecmp(counter, "rx.discard") == 0) { + path = "rx_dropped"; + } else if (strcasecmp(counter, "rx.errors") == 0) { + path = "rx_errors"; + } else if (strcasecmp(counter, "tx.octets") == 0) { + path = "tx_bytes"; + } else if (strcasecmp(counter, "tx.packets") == 0) { + path = "tx_packets"; + } else if (strcasecmp(counter, "tx.discard") == 0) { + path = "tx_dropped"; + } else if (strcasecmp(counter, "tx.errors") == 0) { + path = "tx_errors"; + } else { + errno = ENOSYS; + return NULL; + } + + if (snprintf(full_path, PATH_MAX, "/sys/class/net/%s/statistics/%s", interface, path) > PATH_MAX) + { + errno = ENOMEM; + return NULL; + } + + fp = fopen(full_path, "r"); + if (!fp) + { + return NULL; + } + + in_count = fread(buffer, 1, sizeof(buffer), fp); + + if (in_count == sizeof(buffer)) + { + errno = ENOMEM; + fclose(fp); + return NULL; + } + + if (ferror(fp)) + { + return NULL; + } + + fclose(fp); + + /* take away the \n, we add our own */ + buffer[in_count - 1] = '\0'; + + return strdup(buffer); } diff --git a/cmd/ifctrstat.c b/cmd/ifctrstat.c index aa4460b..1d376cd 100644 --- a/cmd/ifctrstat.c +++ b/cmd/ifctrstat.c @@ -37,16 +37,44 @@ counter_is_valid(const char *candidate) return false; } +static void +print_counter(const char *iface, const char *name, const char *value) +{ + fprintf(stdout, "%s: %s\n", name, value); +} + +static int +print_all_counters(const char *iface) +{ + int code = EXIT_SUCCESS; + const char *res; + + for (int i = 0; i < avail_counters_count; i++) { + const char *ctr = avail_counters[i]; + + res = read_counter(iface, ctr); + if (!res) + { + fprintf(stderr, "%s: could not determine value of %s for interface %s: %s\n", argv0, ctr, iface, strerror(errno)); + code = EXIT_FAILURE; + } else { + print_counter(iface, ctr, res); + } + } + + return code; +} + void -ifstats_list_counters(int short_opt, const struct if_option *opt, - const char *opt_arg, const struct if_applet *applet) +ifctrstat_list_counters(int short_opt, const struct if_option *opt, const char *opt_arg, const struct if_applet *applet) { (void) short_opt; (void) opt; (void) opt_arg; (void) applet; - for (int i = 0; i < avail_counters_count; i++) { + for (int i = 0; i < avail_counters_count; i++) + { fprintf(stdout, "%s\n", avail_counters[i]); } @@ -54,27 +82,31 @@ ifstats_list_counters(int short_opt, const struct if_option *opt, } int -ifstats_main(int argc, char *argv[]) +ifctrstat_main(int argc, char *argv[]) { if (optind >= argc) generic_usage(self_applet, EXIT_FAILURE); int idx = optind; - if (argc - idx < 2) + if (argc - idx == 0) { - fprintf(stderr, "%s: interface and counter(s) required\n", + fprintf(stderr, "%s: interface required\n", argv0); return EXIT_FAILURE; } const char *iface = argv[idx++]; + if (argc - idx == 0) + { + return print_all_counters(iface); + } + for (; idx < argc; idx++) { if (!counter_is_valid(argv[idx])) { - fprintf(stderr, "%s: counter %s is not valid or not " - "available\n", argv0, argv[idx]); + fprintf(stderr, "%s: counter %s is not valid or not available\n", argv0, argv[idx]); return EXIT_FAILURE; } @@ -82,18 +114,18 @@ ifstats_main(int argc, char *argv[]) const char *res = read_counter(iface, argv[idx]); if (!res) { - fprintf(stderr, "%s: could not determine value of " - "%s for interface %s: %s\n", argv0, - argv[idx], iface, strerror(errno)); + fprintf(stderr, "%s: could not determine value of %s for interface %s: %s\n", argv0, argv[idx], iface, strerror(errno)); return EXIT_FAILURE; } + + print_counter(iface, argv[idx], res); } return EXIT_SUCCESS; } static struct if_option local_options[] = { - {'L', "list", NULL, "List available counters", false, ifstats_list_counters} + {'L', "list", NULL, "List available counters", false, ifctrstat_list_counters} }; static struct if_option_group local_option_group = { @@ -105,7 +137,7 @@ static struct if_option_group local_option_group = { struct if_applet ifctrstat_applet = { .name = "ifctrstat", .desc = "Display statistics about an interface", - .main = ifstats_main, + .main = ifctrstat_main, .usage = "ifctrstat [options] \n ifctrstat [options] --list\n", .groups = { &global_option_group, &local_option_group, NULL } }; From 7b45d3939d6e6ad5cdedd5943f020de6e28f3246 Mon Sep 17 00:00:00 2001 From: "A. Wilcox" Date: Fri, 14 Aug 2020 14:27:23 -0500 Subject: [PATCH 4/5] ifctrstat: Add -n option to omit label printing --- cmd/ifctrstat.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/cmd/ifctrstat.c b/cmd/ifctrstat.c index 1d376cd..0409438 100644 --- a/cmd/ifctrstat.c +++ b/cmd/ifctrstat.c @@ -14,9 +14,10 @@ */ #include +#include +#include #include #include -#include #include "libifupdown/libifupdown.h" #include "cmd/multicall.h" @@ -25,6 +26,8 @@ extern int avail_counters_count; extern const char *read_counter(const char *interface, const char *counter); +static bool show_label = true; + static bool counter_is_valid(const char *candidate) { @@ -40,7 +43,10 @@ counter_is_valid(const char *candidate) static void print_counter(const char *iface, const char *name, const char *value) { - fprintf(stdout, "%s: %s\n", name, value); + if (show_label) + fprintf(stdout, "%s: %s\n", name, value); + else + fprintf(stdout, "%s\n", value); } static int @@ -81,6 +87,17 @@ ifctrstat_list_counters(int short_opt, const struct if_option *opt, const char * exit(EXIT_SUCCESS); } +void +ifctrstat_set_nolabel(int short_opt, const struct if_option *opt, const char *opt_arg, const struct if_applet *applet) +{ + (void) short_opt; + (void) opt; + (void) opt_arg; + (void) applet; + + show_label = false; +} + int ifctrstat_main(int argc, char *argv[]) { @@ -125,7 +142,8 @@ ifctrstat_main(int argc, char *argv[]) } static struct if_option local_options[] = { - {'L', "list", NULL, "List available counters", false, ifctrstat_list_counters} + {'L', "list", NULL, "List available counters", false, ifctrstat_list_counters}, + {'n', "no-label", NULL, "Print value without counter label", false, ifctrstat_set_nolabel} }; static struct if_option_group local_option_group = { From 1a11bb768f450a512304e5f1235e3ce395ce59c1 Mon Sep 17 00:00:00 2001 From: "A. Wilcox" Date: Mon, 17 Aug 2020 14:13:16 -0500 Subject: [PATCH 5/5] ifctrstat: Use new structure and recalculate binary size --- Makefile | 2 +- cmd/ifctrstat-linux.c | 48 ++++++++++++++++++++----------------------- cmd/ifctrstat.c | 8 ++++---- 3 files changed, 27 insertions(+), 31 deletions(-) diff --git a/Makefile b/Makefile index d050032..8ec124a 100644 --- a/Makefile +++ b/Makefile @@ -62,7 +62,7 @@ MULTICALL_${CONFIG_IFQUERY}_OBJ += ${IFQUERY_SRC:.c=.o} CMDS_${CONFIG_IFQUERY} += ifquery CPPFLAGS_${CONFIG_IFQUERY} += -DCONFIG_IFQUERY -# enable ifctrstat applet (+16 KB) +# enable ifctrstat applet (+1 KB) CONFIG_IFCTRSTAT ?= Y IFCTRSTAT_SRC = cmd/ifctrstat.c cmd/ifctrstat-${LAYOUT}.c MULTICALL_${CONFIG_IFCTRSTAT}_OBJ += ${IFCTRSTAT_SRC:.c=.o} diff --git a/cmd/ifctrstat-linux.c b/cmd/ifctrstat-linux.c index 52d48b8..e7dcfa3 100644 --- a/cmd/ifctrstat-linux.c +++ b/cmd/ifctrstat-linux.c @@ -19,19 +19,28 @@ #include #include "multicall.h" -const char *avail_counters[] = { - "rx.octets", - "rx.packets", - "rx.discard", - "rx.errors", - "tx.octets", - "tx.packets", - "tx.discard", - "tx.errors" +struct counter_desc { + const char *name; + const void *data; +} avail_counters[] = { + {"rx.discard", "rx_dropped"}, + {"rx.errors", "rx_errors"}, + {"rx.octets", "rx_bytes"}, + {"rx.packets", "rx_packets"}, + {"tx.discard", "tx_dropped"}, + {"tx.errors", "tx_errors"}, + {"tx.octets", "tx_bytes"}, + {"tx.packets", "tx_packets"} }; size_t avail_counters_count = ARRAY_SIZE(avail_counters); +static int +counter_compare(const void *key, const void *candidate) +{ + return strcasecmp((const char *)key, ((struct counter_desc *)candidate)->name); +} + char * read_counter(const char *interface, const char *counter) { @@ -40,26 +49,13 @@ read_counter(const char *interface, const char *counter) char full_path[PATH_MAX]; char buffer[1024]; size_t in_count; + struct counter_desc *ctrdata; errno = 0; - if (strcasecmp(counter, "rx.octets") == 0) - { - path = "rx_bytes"; - } else if (strcasecmp(counter, "rx.packets") == 0) { - path = "rx_packets"; - } else if (strcasecmp(counter, "rx.discard") == 0) { - path = "rx_dropped"; - } else if (strcasecmp(counter, "rx.errors") == 0) { - path = "rx_errors"; - } else if (strcasecmp(counter, "tx.octets") == 0) { - path = "tx_bytes"; - } else if (strcasecmp(counter, "tx.packets") == 0) { - path = "tx_packets"; - } else if (strcasecmp(counter, "tx.discard") == 0) { - path = "tx_dropped"; - } else if (strcasecmp(counter, "tx.errors") == 0) { - path = "tx_errors"; + ctrdata = bsearch(counter, avail_counters, avail_counters_count, sizeof(struct counter_desc), counter_compare); + if (ctrdata) { + path = (const char *)ctrdata->data; } else { errno = ENOSYS; return NULL; diff --git a/cmd/ifctrstat.c b/cmd/ifctrstat.c index 0409438..9c12231 100644 --- a/cmd/ifctrstat.c +++ b/cmd/ifctrstat.c @@ -21,7 +21,7 @@ #include "libifupdown/libifupdown.h" #include "cmd/multicall.h" -extern const char *avail_counters[]; +extern struct counter_desc { const char *name; const void *data; } avail_counters[]; extern int avail_counters_count; extern const char *read_counter(const char *interface, const char *counter); @@ -33,7 +33,7 @@ counter_is_valid(const char *candidate) { for (int i = 0; i < avail_counters_count; i++) { - if (strcasecmp(avail_counters[i], candidate) == 0) + if (strcasecmp(avail_counters[i].name, candidate) == 0) return true; } @@ -56,7 +56,7 @@ print_all_counters(const char *iface) const char *res; for (int i = 0; i < avail_counters_count; i++) { - const char *ctr = avail_counters[i]; + const char *ctr = avail_counters[i].name; res = read_counter(iface, ctr); if (!res) @@ -81,7 +81,7 @@ ifctrstat_list_counters(int short_opt, const struct if_option *opt, const char * for (int i = 0; i < avail_counters_count; i++) { - fprintf(stdout, "%s\n", avail_counters[i]); + fprintf(stdout, "%s\n", avail_counters[i].name); } exit(EXIT_SUCCESS);