Author: eugen
Date: Thu Jun  4 14:44:44 2020
New Revision: 361790
URL: https://svnweb.freebsd.org/changeset/base/361790

Log:
  ifconfig(8): make it possible to filter output by interface group.
  
  Now options -g/-G allow to select/unselect interfaces by groups
  in the "ifconfig -a" output just like already existing -d/-u.
  
  Examples:
  
  to exclude loopback from the list: ifconfig -a -G lo
  to show vlan interfaces only: ifconfig -a -g vlan
  to show tap interfaces that are up: ifconfig -aug tap
  
  Arguments to -g/-G may be shell patterns and both may be specified.
  Later options -g/-G override previous ones.
  
  MFC after:            2 weeks
  Relnotes:             yes
  Differential Revision:        https://reviews.freebsd.org/D25029

Modified:
  head/sbin/ifconfig/ifconfig.8
  head/sbin/ifconfig/ifconfig.c

Modified: head/sbin/ifconfig/ifconfig.8
==============================================================================
--- head/sbin/ifconfig/ifconfig.8       Thu Jun  4 14:15:39 2020        
(r361789)
+++ head/sbin/ifconfig/ifconfig.8       Thu Jun  4 14:44:44 2020        
(r361790)
@@ -56,6 +56,7 @@
 .Fl a
 .Op Fl L
 .Op Fl d
+.Op Fl [gG] Ar groupname
 .Op Fl m
 .Op Fl u
 .Op Fl v
@@ -2910,9 +2911,26 @@ This flag instructs
 to display information about all interfaces in the system.
 The
 .Fl d
-flag limits this to interfaces that are down, and
+flag limits this to interfaces that are down,
 .Fl u
-limits this to interfaces that are up.
+limits this to interfaces that are up,
+limits this to interfaces that are up,
+.Fl g
+limits this to members of the specified group of interfaces, and
+.Fl G
+excludes members of the specified group from the list.
+Both
+.Fl g
+and
+.Fl G
+flags may be specified to apply both conditions.
+Only one option
+.Fl g
+should be specified as later override previous ones
+(same for
+.Fl G ) .
+.Sy groupname
+may contain shell patterns in which case it should be quoted.
 When no arguments are given,
 .Fl a
 is implied.
@@ -3036,6 +3054,9 @@ Display available wireless networks using
 .Pp
 Display inet and inet6 address subnet masks in CIDR notation
 .Dl # ifconfig -f inet:cidr,inet6:cidr
+.Pp
+Display interfaces that are up with the exception of loopback
+.Dl # ifconfig -a -u -G lo
 .Sh DIAGNOSTICS
 Messages indicating the specified interface does not exist, the
 requested address is unknown, or the user is not privileged and

Modified: head/sbin/ifconfig/ifconfig.c
==============================================================================
--- head/sbin/ifconfig/ifconfig.c       Thu Jun  4 14:15:39 2020        
(r361789)
+++ head/sbin/ifconfig/ifconfig.c       Thu Jun  4 14:44:44 2020        
(r361790)
@@ -63,6 +63,7 @@ static const char rcsid[] =
 #include <arpa/inet.h>
 #include <netdb.h>
 
+#include <fnmatch.h>
 #include <ifaddrs.h>
 #include <ctype.h>
 #include <err.h>
@@ -105,6 +106,8 @@ int exit_code = 0;
 /* Formatter Strings */
 char   *f_inet, *f_inet6, *f_ether, *f_addr;
 
+static bool group_member(const char *ifname, const char *match,
+               const char *nomatch);
 static int ifconfig(int argc, char *const *argv, int iscreate,
                const struct afswtch *afp);
 static void status(const struct afswtch *afp, const struct sockaddr_dl *sdl,
@@ -402,13 +405,14 @@ main(int argc, char *argv[])
        char options[1024], *cp, *envformat, *namecp = NULL;
        struct ifa_queue q = TAILQ_HEAD_INITIALIZER(q);
        struct ifa_order_elt *cur, *tmp;
-       const char *ifname;
+       const char *ifname, *matchgroup, *nogroup;
        struct option *p;
        size_t iflen;
        int flags;
 
        all = downonly = uponly = namesonly = noload = verbose = 0;
        f_inet = f_inet6 = f_ether = f_addr = NULL;
+       matchgroup = nogroup = NULL;
 
        envformat = getenv("IFCONFIG_FORMAT");
        if (envformat != NULL)
@@ -421,7 +425,7 @@ main(int argc, char *argv[])
        atexit(printifnamemaybe);
 
        /* Parse leading line options */
-       strlcpy(options, "f:adklmnuv", sizeof(options));
+       strlcpy(options, "G:adf:klmnuv", sizeof(options));
        for (p = opts; p != NULL; p = p->next)
                strlcat(options, p->opt, sizeof(options));
        while ((c = getopt(argc, argv, options)) != -1) {
@@ -437,6 +441,11 @@ main(int argc, char *argv[])
                                usage();
                        setformat(optarg);
                        break;
+               case 'G':
+                       if (optarg == NULL || all == 0)
+                               usage();
+                       nogroup = optarg;
+                       break;
                case 'k':
                        printkeys++;
                        break;
@@ -455,6 +464,14 @@ main(int argc, char *argv[])
                case 'v':
                        verbose++;
                        break;
+               case 'g':
+                       if (all) {
+                               if (optarg == NULL)
+                                       usage();
+                               matchgroup = optarg;
+                               break;
+                       }
+                       /* FALLTHROUGH */
                default:
                        for (p = opts; p != NULL; p = p->next)
                                if (p->opt[0] == c) {
@@ -626,6 +643,8 @@ main(int argc, char *argv[])
                        continue;
                if (uponly && (ifa->ifa_flags & IFF_UP) == 0)
                        continue;
+               if (!group_member(ifa->ifa_name, matchgroup, nogroup))
+                       continue;
                /*
                 * Are we just listing the interfaces?
                 */
@@ -668,6 +687,73 @@ main(int argc, char *argv[])
 done:
        freeformat();
        exit(exit_code);
+}
+
+/*
+ * Returns true if an interface should be listed because any its groups
+ * matches shell pattern "match" and none of groups matches pattern "nomatch".
+ * If any pattern is NULL, corresponding condition is skipped.
+ */
+static bool
+group_member(const char *ifname, const char *match, const char *nomatch)
+{
+       static int               sock = -1;
+
+       struct ifgroupreq        ifgr;
+       struct ifg_req          *ifg;
+       int                      len;
+       bool                     matched, nomatched;
+
+       /* Sanity checks. */
+       if (match == NULL && nomatch == NULL)
+               return (true);
+       if (ifname == NULL)
+               return (false);
+
+       memset(&ifgr, 0, sizeof(ifgr));
+       strlcpy(ifgr.ifgr_name, ifname, IFNAMSIZ);
+
+       /* The socket is opened once. Let _exit() close it. */
+       if (sock == -1) {
+               sock = socket(AF_LOCAL, SOCK_DGRAM, 0);
+               if (sock == -1)
+                   errx(1, "%s: socket(AF_LOCAL,SOCK_DGRAM)", __func__);
+       }
+
+       /* Determine amount of memory for the list of groups. */
+       if (ioctl(sock, SIOCGIFGROUP, (caddr_t)&ifgr) == -1) {
+               if (errno == EINVAL || errno == ENOTTY)
+                       return (false);
+               else
+                       errx(1, "%s: SIOCGIFGROUP", __func__);
+       }
+
+       /* Obtain the list of groups. */
+       len = ifgr.ifgr_len;
+       ifgr.ifgr_groups =
+           (struct ifg_req *)calloc(len / sizeof(*ifg), sizeof(*ifg));
+
+       if (ifgr.ifgr_groups == NULL)
+               errx(1, "%s: no memory", __func__);
+       if (ioctl(sock, SIOCGIFGROUP, (caddr_t)&ifgr) == -1)
+               errx(1, "%s: SIOCGIFGROUP", __func__);
+
+       /* Perform matching. */
+       matched = false;
+       nomatched = true;
+       for (ifg = ifgr.ifgr_groups; ifg && len >= sizeof(*ifg); ifg++) {
+               len -= sizeof(struct ifg_req);
+               if (match)
+                       matched |= !fnmatch(match, ifg->ifgrq_group, 0);
+               if (nomatch)
+                       nomatched &= fnmatch(nomatch, ifg->ifgrq_group, 0);
+       }
+
+       if (match && !nomatch)
+               return (matched);
+       if (!match && nomatch)
+               return (nomatched);
+       return (matched && nomatched);
 }
 
 static struct afswtch *afs = NULL;
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to