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 } };