]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lsfd: fill ENDPOINTS column for UNIX one-way sockets
authorMasatake YAMATO <yamato@redhat.com>
Sun, 9 Mar 2025 21:54:04 +0000 (06:54 +0900)
committerMasatake YAMATO <yamato@redhat.com>
Fri, 14 Mar 2025 21:25:27 +0000 (06:25 +0900)
With recvfrom(2) and sendto(2), we can use a UNIX datagram socket at
server side without assigning a peer address with connect(2). Here, I
call such sockets one-way sockets.

    # ss -x -p
    Netid State Recv-Q Send-Q        Local Address:Port  Peer Address:Port Process
    ...
    u_dgr ESTAB      0      0 /run/systemd/notify 13465               * 0  users:(("systemd",pid=1,fd=227))
    u_dgr ESTAB      0      0                   * 35438           * 13465  users:(("systemd-journal",pid=1280,fd=15))
    u_dgr ESTAB      0      0                   * 74792           * 13465  users:(("bluetoothd",pid=13874,fd=4))
    ...

The orignal code could not fill ENDPOINTS for UNIX one-way sockets:

  # ./original-lsfd -Q 'INODE == 13465' -oCOMMAND,PID,TYPE,INODE,NAME,ENDPOINTS
  COMMAND PID TYPE INODE NAME                                                ENDPOINTS
  systemd   1 UNIX 13465 state=connected path=/run/systemd/notify type=dgram

With this change, lsfd can fill the column:

  # ./new-lsfd -Q 'INODE == 13465' -oCOMMAND,PID,TYPE,INODE,NAME,ENDPOINTS
  COMMAND PID TYPE INODE NAME                                                ENDPOINTS
  systemd   1 UNIX 13465 state=connected path=/run/systemd/notify type=dgram 1280,systemd-journal,15rw
                                                                             13874,bluetoothd,4rw

Signed-off-by: Masatake YAMATO <yamato@redhat.com>
lsfd-cmd/lsfd.1.adoc
lsfd-cmd/sock-xinfo.c
tests/expected/lsfd/mkfds-unix-dgram-ENDPOINTS-column
tests/ts/lsfd/mkfds-unix-dgram

index 13311ebd0d9883b3b7e919d5fce167b4b87337d5..4d6fde156a82ac114fd0d52ef0a96d8c80831c89 100644 (file)
@@ -195,6 +195,16 @@ write mode of the endpoint.
 eventfd type:::
 _PID_,_COMMAND_,_ASSOC_
 
+UNIX:::
+Same as _UNIX-STREAM_.
++
+In a container, *lsfd* may not fill _ENDPOINTS_ column.
+*lsfd* uses *sock_diag*(7) to collect information
+about UNIX endpoints. SELinux may limit the use *sock_diag*
+in a container. You can remove the limit by setting
+1 to _virt_sandbox_use_netlink_ bool. See *container_selinux*(8)
+for more details.
+
 UNIX-STREAM:::
 _PID_,_COMMAND_,_ASSOC_[-r?][-w?]
 +
index c5227e3331ce6ff4c5db8658e691fcd76b91bb53..6023e6fc3c10ac353cffe53ecbed400b74910426 100644 (file)
@@ -66,12 +66,17 @@ static void load_xinfo_from_proc_packet(ino_t netns_inode);
 static void load_xinfo_from_diag_unix(int diag, ino_t netns_inode);
 static void load_xinfo_from_diag_vsock(int diag, ino_t netns_inode);
 
+static void fill_peers_of_unix_oneway_ipcs(void);
+
 static int self_netns_fd = -1;
 static struct stat self_netns_sb;
 
 static void *xinfo_tree;       /* for tsearch/tfind */
 static void *netns_tree;
 
+static LIST_HEAD(unix_ipcs);
+static void *unix_oneway_ipc_tree;     /* for tsearch/tfind */
+
 struct iface {
        unsigned int index;
        char name[IF_NAMESIZE];
@@ -290,10 +295,8 @@ void initialize_sock_xinfos(void)
        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;
-       }
+       if (dir == NULL)
+               goto out;
        while ((d = readdir(dir))) {
                struct stat sb;
                int fd;
@@ -310,7 +313,10 @@ void initialize_sock_xinfos(void)
                close(fd);
        }
        closedir(dir);
+ out:
        ul_unref_path(pc);
+
+       fill_peers_of_unix_oneway_ipcs();
 }
 
 static void free_sock_xinfo(void *node)
@@ -321,12 +327,17 @@ static void free_sock_xinfo(void *node)
        free(node);
 }
 
+static void do_nothing(void *node __attribute__((__unused__)))
+{
+}
+
 void finalize_sock_xinfos(void)
 {
        if (self_netns_fd != -1)
                close(self_netns_fd);
        tdestroy(netns_tree, netns_free);
        tdestroy(xinfo_tree, free_sock_xinfo);
+       tdestroy(unix_oneway_ipc_tree, do_nothing);
 }
 
 static int xinfo_compare(const void *a, const void *b)
@@ -461,6 +472,7 @@ struct unix_ipc {
        struct ipc ipc;
        ino_t inode;
        ino_t ipeer;
+       struct list_head unix_ipcs;
 };
 
 struct unix_xinfo {
@@ -665,18 +677,25 @@ static bool unix_fill_column(struct proc *proc __attribute__((__unused__)),
                }
                break;
        case COL_ENDPOINTS:
+               if (ux->unix_ipc == NULL)
+                       break;
+
                peer_ipc = unix_get_peer_ipc(ux, sock);
-               if (!peer_ipc)
+               if (!peer_ipc && ux->unix_ipc->ipeer != 0)
                        break;
 
-               unix_fill_column_append_endpoints(peer_ipc, str);
+               if (peer_ipc)
+                       unix_fill_column_append_endpoints(peer_ipc, str);
 
-                       if (*str)
-                               xstrputc(str, '\n');
-                       estr = unix_xstrendpoint(peer_sock);
-                       xstrappend(str, estr);
-                       free(estr);
+               if (ux->unix_ipc->ipeer == 0) {
+                       struct list_head *e;
+                       list_for_each(e, &ux->unix_ipc->unix_ipcs) {
+                               struct unix_ipc *peer_unix_ipc = list_entry(e, struct unix_ipc, unix_ipcs);
+                               peer_ipc = &peer_unix_ipc->ipc;
+                               unix_fill_column_append_endpoints(peer_ipc, str);
+                       }
                }
+
                if (*str)
                        return true;
                break;
@@ -785,6 +804,33 @@ static void unix_refill_name(struct sock_xinfo *xinfo, const char *name, size_t
        ux->path[min_len] = '\0';
 }
 
+static int unix_oneway_ipc_compare(const void *a, const void *b)
+{
+       return ((struct unix_ipc *)a)->inode - ((struct unix_ipc *)b)->inode;
+}
+
+static void add_unix_oneway_ipc(struct unix_ipc *unix_oneway_ipc)
+{
+       struct unix_ipc **tmp = tsearch(unix_oneway_ipc,
+                                       &unix_oneway_ipc_tree,
+                                       unix_oneway_ipc_compare);
+
+       if (tmp == NULL)
+               errx(EXIT_FAILURE, _("failed to allocate memory"));
+}
+
+static struct unix_ipc *get_unix_oneway_ipc(ino_t inode)
+{
+       struct unix_ipc key = { .inode = inode };
+       struct unix_ipc  **unix_oneway_ipc = tfind(&key,
+                                                  &unix_oneway_ipc_tree,
+                                                  unix_oneway_ipc_compare);
+
+       if (unix_oneway_ipc)
+               return *unix_oneway_ipc;
+       return NULL;
+}
+
 static bool handle_diag_unix(ino_t netns __attribute__((__unused__)),
                             size_t nlmsg_len, void *nlmsg_data)
 {
@@ -852,6 +898,10 @@ static bool handle_diag_unix(ino_t netns __attribute__((__unused__)),
                        unix_xinfo->unix_ipc = (struct unix_ipc *)new_ipc(&unix_ipc_class);
                        unix_xinfo->unix_ipc->inode = inode;
                        unix_xinfo->unix_ipc->ipeer = (ino_t)(*(uint32_t *)RTA_DATA(attr));
+
+                       INIT_LIST_HEAD(&unix_xinfo->unix_ipc->unix_ipcs);
+                       list_add(&unix_xinfo->unix_ipc->unix_ipcs, &unix_ipcs);
+
                        add_ipc(&unix_xinfo->unix_ipc->ipc, inode % UINT_MAX);
                        peer_added = true;
                        break;
@@ -863,6 +913,10 @@ static bool handle_diag_unix(ino_t netns __attribute__((__unused__)),
                unix_xinfo->unix_ipc = (struct unix_ipc *)new_ipc(&unix_ipc_class);
                unix_xinfo->unix_ipc->inode = inode;
                unix_xinfo->unix_ipc->ipeer = 0;
+
+               INIT_LIST_HEAD(&unix_xinfo->unix_ipc->unix_ipcs);
+               add_unix_oneway_ipc(unix_xinfo->unix_ipc);
+
                add_ipc(&unix_xinfo->unix_ipc->ipc, inode % UINT_MAX);
        };
        return true;
@@ -879,6 +933,25 @@ static void load_xinfo_from_diag_unix(int diagsd, ino_t netns)
        send_diag_request(diagsd, &udr, sizeof(udr), handle_diag_unix, netns);
 }
 
+static void fill_peers_of_unix_oneway_ipcs(void)
+{
+       struct list_head *e, *enext;
+
+       list_for_each_safe(e, enext, &unix_ipcs) {
+               struct unix_ipc *unix_ipc = list_entry(e, struct unix_ipc, unix_ipcs);
+               struct unix_ipc *unix_oneway_ipc;
+
+               list_del_init(e);
+               if (unix_ipc->ipeer == 0)
+                       continue;
+
+               unix_oneway_ipc = get_unix_oneway_ipc(unix_ipc->ipeer);
+               if (unix_oneway_ipc == NULL)
+                       continue;
+               list_add(e, &unix_oneway_ipc->unix_ipcs);
+       };
+}
+
 /*
  * AF_INET
  */
index 71ba0525f08277ab0fb2b0d7289776fabe023611..2515a4e0df3866b28b47264f30106261ffa16d43 100644 (file)
@@ -1,2 +1,4 @@
 endpoint_c: 0
 endpoint_c == PID,test_mkfs,3rw: 0
+endpoint_s: 0
+endpoint_s == PID,test_mkfs,4rw: 0
index c19e8ca29c7c0cab83ee3410831f8d7384d0943d..1de791061a1c38f68a94f89f93a26f4d30824e47 100755 (executable)
@@ -89,6 +89,12 @@ else
            test "$endpoint_c" = "${PID},test_mkfds,${FDS}rw"
            echo "endpoint_c == PID,test_mkfs,${FDS}rw:" $?
 
+           endpoint_s=$(${TS_CMD_LSFD} -n -r -Q "PID == ${PID} && FD == $FDS" -o ENDPOINTS)
+           echo endpoint_s: $?
+
+           test "$endpoint_s" = "${PID},test_mkfds,${FDC}rw"
+           echo "endpoint_s == PID,test_mkfs,${FDC}rw:" $?
+
            echo DONE >&"${MKFDS[1]}"
        fi
        wait "${MKFDS_PID}"