brctl: make "show" command retrieve data from /sys
authorDenys Vlasenko <vda.linux@googlemail.com>
Fri, 12 Apr 2019 16:52:31 +0000 (18:52 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Fri, 12 Apr 2019 16:52:31 +0000 (18:52 +0200)
ioctl interface is obsolete and has no 32/64 compat shim,
making "brctl show" fail for 32-bit userspace and 64-bit kernel.

function                                             old     new   delta
show_bridge                                            -     310    +310
read_file                                              -      64     +64
if_indextoname                                       117       -    -117
brctl_main                                          1183     885    -298
------------------------------------------------------------------------------
(add/remove: 2/1 grow/shrink: 0/1 up/down: 374/-415)          Total: -41 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
networking/brctl.c

index ba4a714f850842e80d1abff4c475f00584472b07..706ecfc07273ebd49044f6767dc78eeda8ca85b3 100644 (file)
@@ -67,6 +67,7 @@
 //usage:       )
 
 #include "libbb.h"
+#include "common_bufsiz.h"
 #include <linux/sockios.h>
 #include <net/if.h>
 
@@ -198,6 +199,69 @@ static void arm_ioctl(unsigned long *args,
 }
 #endif
 
+#define filedata bb_common_bufsiz1
+static int read_file(const char *name)
+{
+       int n = open_read_close(name, filedata, COMMON_BUFSIZE - 1);
+       if (n < 0) {
+               filedata[0] = '\0';
+       } else {
+               filedata[n] = '\0';
+               if (n != 0 && filedata[n - 1] == '\n')
+                       filedata[--n] = '\0';
+       }
+       return n;
+}
+
+/* NB: we are in /sys/class/net
+ */
+static int show_bridge(const char *name, int need_hdr)
+{
+// Output:
+//bridge name  bridge id               STP enabled     interfaces
+//br0          8000.000000000000       no              eth0
+       char pathbuf[IFNAMSIZ + sizeof("/bridge/bridge_id") + 32];
+       int tabs;
+       DIR *ifaces;
+       struct dirent *ent;
+       char *sfx;
+
+       sfx = pathbuf + sprintf(pathbuf, "%s/bridge/", name);
+       strcpy(sfx, "bridge_id");
+       if (read_file(pathbuf) < 0)
+               return -1; /* this iface is not a bridge */
+
+       if (need_hdr)
+               puts("bridge name\tbridge id\t\tSTP enabled\tinterfaces");
+       printf("%s\t\t", name);
+       printf("%s\t", filedata);
+
+       strcpy(sfx, "stp_state");
+       read_file(pathbuf);
+       if (LONE_CHAR(filedata, '0'))
+               strcpy(filedata, "no");
+       else
+       if (LONE_CHAR(filedata, '1'))
+               strcpy(filedata, "yes");
+       printf(filedata);
+
+       strcpy(sfx, "brif");
+       tabs = 0;
+       ifaces = opendir(pathbuf);
+       if (ifaces) {
+               while ((ent = readdir(ifaces)) != NULL) {
+                       if (tabs)
+                               printf("\t\t\t\t\t");
+                       else
+                               tabs = 1;
+                       printf("\t\t%s\n", ent->d_name);
+               }
+               closedir(ifaces);
+       }
+       if (!tabs)  /* bridge has no interfaces */
+               bb_putchar('\n');
+       return 0;
+}
 
 int brctl_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int brctl_main(int argc UNUSED_PARAM, char **argv)
@@ -226,6 +290,13 @@ int brctl_main(int argc UNUSED_PARAM, char **argv)
        char *br, *brif;
 
        argv++;
+       if (!*argv) {
+               /* bare "brctl" shows --help */
+               bb_show_usage();
+       }
+
+       xchdir("/sys/class/net");
+
        while (*argv) {
 #if ENABLE_FEATURE_BRCTL_FANCY
                int ifidx[MAX_PORTS];
@@ -237,68 +308,50 @@ int brctl_main(int argc UNUSED_PARAM, char **argv)
                if (key == -1) /* no match found in keywords array, bail out. */
                        bb_error_msg_and_die(bb_msg_invalid_arg_to, *argv, applet_name);
                argv++;
-               fd = xsocket(AF_INET, SOCK_STREAM, 0);
 
 #if ENABLE_FEATURE_BRCTL_SHOW
                if (key == ARG_show) { /* show */
-                       char buf[IFNAMSIZ];
-                       int bridx[MAX_PORTS];
-                       int i, num;
-                       arm_ioctl(args, BRCTL_GET_BRIDGES,
-                                               (unsigned long) bridx, MAX_PORTS);
-                       num = xioctl(fd, SIOCGIFBR, args);
-                       puts("bridge name\tbridge id\t\tSTP enabled\tinterfaces");
-                       for (i = 0; i < num; i++) {
-                               int j, tabs;
-                               struct __bridge_info bi;
-                               unsigned char *x;
-
-                               if (!if_indextoname(bridx[i], buf))
-                                       bb_perror_msg_and_die("can't get bridge name for index %d", i);
-                               strncpy_IFNAMSIZ(ifr.ifr_name, buf);
-
-                               arm_ioctl(args, BRCTL_GET_BRIDGE_INFO,
-                                                       (unsigned long) &bi, 0);
-                               xioctl(fd, SIOCDEVPRIVATE, &ifr);
-                               printf("%s\t\t", buf);
-
-                               /* print bridge id */
-                               x = (unsigned char *) &bi.bridge_id;
-                               for (j = 0; j < 8; j++) {
-                                       printf("%02x", x[j]);
-                                       if (j == 1)
-                                               bb_putchar('.');
-                               }
-                               printf(bi.stp_enabled ? "\tyes" : "\tno");
+                       DIR *net;
+                       struct dirent *ent;
+                       int need_hdr = 1;
+                       int exitcode = EXIT_SUCCESS;
+
+                       if (*argv) {
+                               /* "brctl show BR1 BR2 BR3" */
+                               do {
+                                       if (show_bridge(*argv, need_hdr) >= 0) {
+                                               need_hdr = 0;
+                                       } else {
+                                               bb_error_msg("bridge %s does not exist", *argv);
+//TODO: if device exists, but is not a BR, brctl from bridge-utils 1.6 says this instead:
+// "device eth0 is not a bridge"
+                                               exitcode = EXIT_FAILURE;
+                                       }
+                               } while (*++argv != NULL);
+                               return exitcode;
+                       }
 
-                               /* print interface list */
-                               arm_ioctl(args, BRCTL_GET_PORT_LIST,
-                                                       (unsigned long) ifidx, MAX_PORTS);
-                               xioctl(fd, SIOCDEVPRIVATE, &ifr);
-                               tabs = 0;
-                               for (j = 0; j < MAX_PORTS; j++) {
-                                       if (!ifidx[j])
-                                               continue;
-                                       if (!if_indextoname(ifidx[j], buf))
-                                               bb_perror_msg_and_die("can't get interface name for index %d", j);
-                                       if (tabs)
-                                               printf("\t\t\t\t\t");
-                                       else
-                                               tabs = 1;
-                                       printf("\t\t%s\n", buf);
-                               }
-                               if (!tabs)  /* bridge has no interfaces */
-                                       bb_putchar('\n');
+                       /* "brctl show" (if no ifaces, shows nothing, not even header) */
+                       net = xopendir(".");
+                       while ((ent = readdir(net)) != NULL) {
+                               if (DOT_OR_DOTDOT(ent->d_name))
+                                       continue; /* . or .. */
+                               if (show_bridge(ent->d_name, need_hdr) >= 0)
+                                       need_hdr = 0;
                        }
-                       goto done;
+                       closedir(net);
+                       return exitcode;
                }
 #endif
 
                if (!*argv) /* all but 'show' need at least one argument */
                        bb_show_usage();
 
+               fd = xsocket(AF_INET, SOCK_STREAM, 0);
                br = *argv++;
 
+//brctl from bridge-utils 1.6 also still uses ioctl
+//for SIOCBRADDBR / SIOCBRDELBR, not /sys accesses
                if (key == ARG_addbr || key == ARG_delbr) { /* addbr or delbr */
                        ioctl_or_perror_and_die(fd,
                                        key == ARG_addbr ? SIOCBRADDBR : SIOCBRDELBR,
@@ -329,6 +382,8 @@ int brctl_main(int argc UNUSED_PARAM, char **argv)
                        int onoff = index_in_strings(no_yes, *argv);
                        if (onoff < 0)
                                bb_error_msg_and_die(bb_msg_invalid_arg_to, *argv, applet_name);
+//TODO: replace with:
+//write "0\n" or "1\n" to /sys/class/net/BR/bridge/stp_state
                        onoff = (unsigned)onoff / 4;
                        arm_ioctl(args, BRCTL_SET_BRIDGE_STP_STATE, onoff, 0);
                        goto fire;
@@ -340,6 +395,11 @@ int brctl_main(int argc UNUSED_PARAM, char **argv)
                                BRCTL_SET_BRIDGE_HELLO_TIME,    /* ARG_sethello  */
                                BRCTL_SET_BRIDGE_MAX_AGE        /* ARG_setmaxage */
                        };
+//TODO: replace with:
+//setageing BR N: write "N*100\n" to /sys/class/net/BR/bridge/ageing_time
+//setfd BR N: write "N*100\n" to /sys/class/net/BR/bridge/forward_delay
+//sethello BR N: write "N*100\n" to /sys/class/net/BR/bridge/hello_time
+//setmaxage BR N: write "N*100\n" to /sys/class/net/BR/bridge/max_age
                        arm_ioctl(args, ops[key - ARG_setageing], str_to_jiffies(*argv), 0);
                        goto fire;
                }
@@ -355,6 +415,11 @@ int brctl_main(int argc UNUSED_PARAM, char **argv)
                        int port = -1;
                        unsigned arg1, arg2;
 
+//TODO: replace with:
+//setbridgeprio BR N: write "N\n" to /sys/class/net/BR/bridge/priority
+//setpathcost BR PORT N: ??
+//setportprio BR PORT N: ??
+
                        if (key != ARG_setbridgeprio) {
                                /* get portnum */
                                unsigned i;