]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lsfd: facilitate the way to attach extra info loaded from /proc/net/* to sockets
authorMasatake YAMATO <yamato@redhat.com>
Tue, 20 Sep 2022 20:25:49 +0000 (05:25 +0900)
committerMasatake YAMATO <yamato@redhat.com>
Sat, 24 Sep 2022 12:09:05 +0000 (21:09 +0900)
Files under /proc/net/ like unix, tcp, udp, etc. provides extra
information about sockets. To unitize these information in
lsfd, this change adds stub for loading the information form
/proc/net/* and attaching it to struct file presenting sockets.

Signed-off-by: Masatake YAMATO <yamato@redhat.com>
misc-utils/Makemodule.am
misc-utils/lsfd-sock-xinfo.c [new file with mode: 0644]
misc-utils/lsfd-sock.c
misc-utils/lsfd-sock.h [new file with mode: 0644]
misc-utils/lsfd.c
misc-utils/lsfd.h
misc-utils/meson.build

index dfeff6f4e643942759b8b4f7654923c0618db846..2c9f7ead5c5afad0c2e6e95a92e7fe53c6ab7b27 100644 (file)
@@ -263,6 +263,8 @@ lsfd_SOURCES = \
        misc-utils/lsfd-cdev.c \
        misc-utils/lsfd-bdev.c \
        misc-utils/lsfd-sock.c \
+       misc-utils/lsfd-sock.h \
+       misc-utils/lsfd-sock-xinfo.c \
        misc-utils/lsfd-unkn.c \
        misc-utils/lsfd-fifo.c
 lsfd_LDADD = $(LDADD) libsmartcols.la libcommon.la
diff --git a/misc-utils/lsfd-sock-xinfo.c b/misc-utils/lsfd-sock-xinfo.c
new file mode 100644 (file)
index 0000000..804e394
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ * lsfd-sock-xinfo.c - read various information from files under /proc/net/
+ *
+ * Copyright (C) 2022 Red Hat, Inc. All rights reserved.
+ * Written by Masatake YAMATO <yamato@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <sched.h>             /* for setns(2) */
+#include <search.h>
+
+#include "xalloc.h"
+#include "nls.h"
+#include "libsmartcols.h"
+
+#include "lsfd.h"
+#include "lsfd-sock.h"
+
+static int self_netns_fd = -1;
+struct stat self_netns_sb;
+
+static void *xinfo_tree;       /* for tsearch/tfind */
+static void *netns_tree;
+
+static int netns_compare(const void *a, const void *b)
+{
+       if (*(ino_t *)a < *(ino_t *)b)
+               return -1;
+       else if (*(ino_t *)a > *(ino_t *)b)
+               return 1;
+       else
+               return 0;
+}
+
+static bool is_sock_xinfo_loaded(ino_t netns)
+{
+       return tfind(&netns, &netns_tree, netns_compare)? true: false;
+}
+
+static void mark_sock_xinfo_loaded(ino_t ino)
+{
+       ino_t *netns = xmalloc(sizeof(ino));
+       ino_t **tmp;
+
+       *netns = ino;
+       tmp = tsearch(netns, &netns_tree, netns_compare);
+       if (tmp == NULL)
+               errx(EXIT_FAILURE, _("failed to allocate memory"));
+}
+
+static void load_sock_xinfo_no_nsswitch(ino_t netns __attribute__((__unused__)))
+{
+       /* TODO: load files under /proc/ns */
+}
+
+static void load_sock_xinfo_with_fd(int fd, ino_t netns)
+{
+       if (setns (fd, CLONE_NEWNET) == 0) {
+               load_sock_xinfo_no_nsswitch(netns);
+               setns (self_netns_fd, CLONE_NEWNET);
+       }
+}
+
+void load_sock_xinfo(struct path_cxt *pc, const char *name, ino_t netns)
+{
+       if (self_netns_fd == -1)
+               return;
+
+       if (!is_sock_xinfo_loaded(netns)) {
+               int fd;
+
+               mark_sock_xinfo_loaded(netns);
+               fd = ul_path_open(pc, O_RDONLY, name);
+               if (fd < 0)
+                       return;
+
+               load_sock_xinfo_with_fd(fd, netns);
+               close(fd);
+       }
+}
+
+void initialize_sock_xinfos(void)
+{
+       struct path_cxt *pc;
+       DIR *dir;
+       struct dirent *d;
+
+       self_netns_fd = open("/proc/self/ns/net", O_RDONLY);
+
+       if (self_netns_fd < 0)
+               load_sock_xinfo_no_nsswitch(0);
+       else {
+               if (fstat(self_netns_fd, &self_netns_sb) == 0) {
+                       mark_sock_xinfo_loaded(self_netns_sb.st_ino);
+                       load_sock_xinfo_no_nsswitch(self_netns_sb.st_ino);
+               }
+       }
+
+       /* Load /proc/net/{unix,...} of the network namespace
+        * specified with netns files under /var/run/netns/.
+        *
+        * `ip netns' command pins a network namespace on
+        * /var/run/netns.
+        */
+       pc = ul_new_path("/var/run/netns");
+       if (!pc)
+               err(EXIT_FAILURE, _("failed to alloc path context for /var/run/netns"));
+       dir = ul_path_opendir(pc, NULL);
+       if (dir == NULL) {
+               ul_unref_path(pc);
+               return;
+       }
+       while ((d = readdir(dir))) {
+               struct stat sb;
+               int fd;
+               if (ul_path_stat(pc, &sb, 0, d->d_name) < 0)
+                       continue;
+               if (is_sock_xinfo_loaded(sb.st_ino))
+                       continue;
+               mark_sock_xinfo_loaded(sb.st_ino);
+               fd = ul_path_open(pc, O_RDONLY, d->d_name);
+               if (fd < 0)
+                       continue;
+               load_sock_xinfo_with_fd(fd, sb.st_ino);
+               close(fd);
+       }
+       closedir(dir);
+       ul_unref_path(pc);
+}
+
+static void free_sock_xinfo (void *node)
+{
+       struct sock_xinfo *xinfo = node;
+       if (xinfo->class->free)
+               xinfo->class->free (xinfo);
+       free(node);
+}
+
+void finalize_sock_xinfos(void)
+{
+       if (self_netns_fd != -1)
+               close(self_netns_fd);
+       tdestroy(netns_tree, free);
+       tdestroy(xinfo_tree, free_sock_xinfo);
+}
+
+static int xinfo_compare(const void *a, const void *b)
+{
+       if (((struct sock_xinfo *)a)->inode < ((struct sock_xinfo *)b)->inode)
+               return -1;
+       if (((struct sock_xinfo *)a)->inode > ((struct sock_xinfo *)b)->inode)
+               return 1;
+       return 0;
+}
+
+struct sock_xinfo *get_sock_xinfo(ino_t netns_inode)
+{
+       struct sock_xinfo **xinfo = tfind(&netns_inode, &xinfo_tree, xinfo_compare);
+
+       if (xinfo)
+               return *xinfo;
+       return NULL;
+}
+
+bool is_nsfs_dev(dev_t dev)
+{
+       return (dev == self_netns_sb.st_dev);
+}
index c7adbf35828bde2c0ab0bb1816f0439500e562a0..c768bce5bc47816f95ac39532b5896f758c094a5 100644 (file)
 #include "libsmartcols.h"
 
 #include "lsfd.h"
+#include "lsfd-sock.h"
 
 struct sock {
        struct file file;
        char *protoname;
+       struct sock_xinfo *xinfo;
 };
 
+static void attach_sock_xinfo(struct file *file)
+{
+       struct sock *sock = (struct sock *)file;
+       sock->xinfo = get_sock_xinfo(file->stat.st_ino);
+}
+
 static bool sock_fill_column(struct proc *proc __attribute__((__unused__)),
                             struct file *file,
                             struct libscols_line *ln,
@@ -110,10 +118,23 @@ static void free_sock_content(struct file *file)
        }
 }
 
+static void initialize_sock_class(void)
+{
+       initialize_sock_xinfos();
+}
+
+static void finalize_sock_class(void)
+{
+       finalize_sock_xinfos();
+}
+
 const struct file_class sock_class = {
        .super = &file_class,
        .size = sizeof(struct sock),
        .fill_column = sock_fill_column,
+       .attach_xinfo = attach_sock_xinfo,
        .initialize_content = init_sock_content,
        .free_content = free_sock_content,
+       .initialize_class = initialize_sock_class,
+       .finalize_class = finalize_sock_class,
 };
diff --git a/misc-utils/lsfd-sock.h b/misc-utils/lsfd-sock.h
new file mode 100644 (file)
index 0000000..0a22fc1
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * lsfd(1) - list file descriptors
+ *
+ * Copyright (C) 2022 Red Hat, Inc. All rights reserved.
+ * Written by Masatake YAMATO <yamato@redhat.com>
+ *
+ * Very generally based on lsof(8) by Victor A. Abell <abe@purdue.edu>
+ * It supports multiple OSes. lsfd specializes to Linux.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef UTIL_LINUX_LSFD_SOCK_H
+#define UTIL_LINUX_LSFD_SOCK_H
+
+#include <sys/stat.h>
+
+/*
+ * xinfo: eXtra inforation about sockets
+ */
+struct sock_xinfo {
+       ino_t inode;            /* inode in sockfs */
+       ino_t netns_inode;      /* inode of netns where
+                                  the socket belongs to */
+       const struct sock_xinfo_class *class;
+};
+
+struct sock_xinfo_class {
+       const char *class;
+       void (*free)(struct sock_xinfo *);
+};
+
+void initialize_sock_xinfos(void);
+void finalize_sock_xinfos(void);
+
+struct sock_xinfo *get_sock_xinfo(ino_t netns_inode);
+
+#endif /* UTIL_LINUX_LSFD_SOCK_H */
index 0d7e7e58a1344591cb07e1452a2f708e3a8306ab..740223e43807403eeb854cafabe178acf89d5517 100644 (file)
@@ -605,6 +605,8 @@ static struct file *collect_file_symlink(struct path_cxt *pc,
 
        if (is_association(f, NS_MNT))
                proc->ns_mnt = f->stat.st_ino;
+       else if (is_association(f, NS_NET))
+               load_sock_xinfo(pc, name, f->stat.st_ino);
 
        else if (assoc >= 0) {
                /* file-descriptor based association */
@@ -613,6 +615,9 @@ static struct file *collect_file_symlink(struct path_cxt *pc,
                if (ul_path_stat(pc, &sb, AT_SYMLINK_NOFOLLOW, name) == 0)
                        f->mode = sb.st_mode;
 
+               if (is_nsfs_dev(f->stat.st_dev))
+                       load_sock_xinfo(pc, name, f->stat.st_ino);
+
                fdinfo = ul_path_fopenf(pc, "r", "fdinfo/%d", assoc);
                if (fdinfo) {
                        read_fdinfo(f, fdinfo);
@@ -1620,6 +1625,22 @@ static void emit_summary(struct lsfd_control *ctl, struct lsfd_counter **counter
        scols_unref_table(tb);
 }
 
+static void attach_xinfos(struct list_head *procs)
+{
+       struct list_head *p;
+
+       list_for_each (p, procs) {
+               struct proc *proc = list_entry(p, struct proc, procs);
+               struct list_head *f;
+
+               list_for_each (f, &proc->files) {
+                       struct file *file = list_entry(f, struct file, files);
+                       if (file->class->attach_xinfo)
+                               file->class->attach_xinfo(file);
+               }
+       }
+}
+
 int main(int argc, char *argv[])
 {
        int c;
@@ -1806,6 +1827,8 @@ int main(int argc, char *argv[])
        collect_processes(&ctl, pids, n_pids);
        free(pids);
 
+       attach_xinfos(&ctl.procs);
+
        convert(&ctl.procs, &ctl);
 
        /* print */
index 41f574ff4ad1ff1742529589436e26153823fa95..1633976dd669b2cee306effa4f8eff684e724f60 100644 (file)
@@ -30,6 +30,7 @@
 #include <inttypes.h>
 
 #include "list.h"
+#include "path.h"
 #include "strutils.h"
 
 /*
@@ -145,6 +146,7 @@ struct file_class {
                            int column_id,
                            size_t column_index);
        int  (*handle_fdinfo)(struct file *file, const char *key, const char* value);
+       void (*attach_xinfo)(struct file *file);
        void (*initialize_content)(struct file *file);
        void (*free_content)(struct file *file);
        struct ipc_class *(*get_ipc_class)(struct file *file);
@@ -203,4 +205,10 @@ static inline void xstrputc(char **a, char c)
        xstrappend(a, b);
 }
 
+/*
+ * Net namespace
+ */
+void load_sock_xinfo(struct path_cxt *pc, const char *name, ino_t netns);
+bool is_nsfs_dev(dev_t dev);
+
 #endif /* UTIL_LINUX_LSFD_H */
index 818232a580df055a5a25112412b5a7885a3ffa16..b0dcb801d56395860e463c9dbcd40b14e446759f 100644 (file)
@@ -51,6 +51,8 @@ lsfd_sources = files (
   'lsfd-cdev.c',
   'lsfd-bdev.c',
   'lsfd-sock.c',
+  'lsfd-sock.h',
+  'lsfd-sock-xinfo.c',
   'lsfd-unkn.c',
   'lsfd-fifo.c',
 )