]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lsfd: add new association "pidfs"
authorMasatake YAMATO <yamato@redhat.com>
Fri, 30 Jan 2026 20:27:02 +0000 (05:27 +0900)
committerMasatake YAMATO <yamato@redhat.com>
Sun, 1 Feb 2026 19:19:43 +0000 (04:19 +0900)
This new association is inspired by the getino command.

Output example:

  $ ./lsfd --pid=1 -Q '(ASSOC == "pidfs")'
  COMMAND PID USER ASSOC  XMODE  TYPE       SOURCE MNTID INODE NAME
  systemd   1 root pidfs ------ pidfd anon_inodefs     5     2 pid=1 comm=systemd nspid=1

Signed-off-by: Masatake YAMATO <yamato@redhat.com>
lsfd-cmd/file.c
lsfd-cmd/lsfd.c
lsfd-cmd/lsfd.h
tests/expected/lsfd/assoc-pidfs [new file with mode: 0644]
tests/ts/lsfd/assoc-pidfs [new file with mode: 0755]

index 41642224b06fa76445949b255de7094f55805c0e..dea556247129b4ba3051096a848846220200dbe9 100644 (file)
@@ -71,6 +71,7 @@ static const char *assocstr[N_ASSOCS] = {
        /* "root" appears as user names, too.
         * So we use "rtd" here instead of "root". */
        [ASSOC_ROOT]      = "rtd",
+       [ASSOC_PIDFS]     = "pidfs",
        [ASSOC_NS_CGROUP] = "cgroup",
        [ASSOC_NS_IPC]    = "ipc",
        [ASSOC_NS_MNT]    = "mnt",
index 1326082d5eadbd7e2b6dc0cd5c29c815905d10e9..39523fa248a0209926a0b62a8ffa86d7eb6d39d7 100644 (file)
@@ -55,6 +55,7 @@
 #include "fileutils.h"
 #include "idcache.h"
 #include "pathnames.h"
+#include "pidfd-utils.h"
 
 #include "lsfd.h"
 
@@ -1105,6 +1106,55 @@ static void collect_execve_file(struct path_cxt *pc, struct proc *proc,
                               sockets_only);
 }
 
+static void collect_pidfs_file(struct proc *proc, bool sockets_only)
+{
+       struct file *f = NULL;
+       int pidfd;
+
+       if (sockets_only)
+               return;
+
+       pidfd = pidfd_open(proc->pid, 0);
+       if (pidfd < 0)
+               return;
+
+       {
+               struct stat sb;
+               const int assoc = ASSOC_PIDFS * -1;
+
+               if (fstat(pidfd, &sb) < 0)
+                       goto out;
+
+               if ((sb.st_mode & S_IFMT) == S_IFREG) {
+                       char *name = NULL;
+                       xasprintf(&name, "pidfd:[%llu]", (unsigned long long)sb.st_ino);
+                       f = new_file(proc, &pidfs_file_class, &sb, name, assoc);
+                       free(name);
+               }
+               else
+                       f = new_file(proc, &unkn_class, &sb, "anon_inode:[pidfd]", assoc);
+       }
+
+       file_init_content(f);
+
+       {
+               char *fdinfo = NULL;
+               FILE *fdinfo_fp;
+               const pid_t lsfd_pid = getpid();
+
+               xasprintf(&fdinfo, "%s/%d/fdinfo/%d", _PATH_PROC, lsfd_pid, pidfd);
+               fdinfo_fp = fopen(fdinfo, "r");
+               if (fdinfo_fp) {
+                       read_fdinfo(f, fdinfo_fp);
+                       fclose(fdinfo_fp);
+               }
+               free(fdinfo);
+       }
+
+ out:
+       close(pidfd);
+}
+
 static void collect_fs_files(struct path_cxt *pc, struct proc *proc,
                             bool sockets_only)
 {
@@ -2038,6 +2088,8 @@ static void read_process(struct lsfd_control *ctl, struct path_cxt *pc,
            || kcmp(proc->leader->pid, proc->pid, KCMP_FS, 0, 0) != 0)
                collect_fs_files(pc, proc, ctl->sockets_only);
 
+       collect_pidfs_file(proc, ctl->sockets_only);
+
        /* Reading /proc/$pid/mountinfo is expensive.
         * mnt_namespaces is a table for avoiding reading mountinfo files
         * for an identical mnt namespace.
index a98bcbe3ebe006cb5bb781c59b6f9359ac57e54b..469e58bce115e4b3ea7ecadf6231956891fab538 100644 (file)
@@ -163,6 +163,7 @@ enum association {
        ASSOC_EXE = 1,
        ASSOC_CWD,
        ASSOC_ROOT,
+       ASSOC_PIDFS,
        ASSOC_NS_CGROUP,
        ASSOC_NS_IPC,
        ASSOC_NS_MNT,
@@ -226,7 +227,8 @@ struct file {
 #define is_mapped_file(_f) (is_association((_f), SHM) || is_association((_f), MEM))
 #define is_association(_f, a)  ((_f)->association < 0 && (_f)->association == -ASSOC_ ## a)
 #define has_mnt_id(_f) (is_opened_file(_f) || is_mapped_file(_f) \
-                       || is_association(_f, EXE) || is_association(_f, CWD) || is_association(_f, ROOT))
+                       || is_association(_f, EXE) || is_association(_f, CWD) || is_association(_f, ROOT) \
+                       || is_association(_f, PIDFS))
 
 struct file_class {
        const struct file_class *super;
diff --git a/tests/expected/lsfd/assoc-pidfs b/tests/expected/lsfd/assoc-pidfs
new file mode 100644 (file)
index 0000000..6395445
--- /dev/null
@@ -0,0 +1,4 @@
+INODE: OK
+PID: OK
+COMM: OK
+NSPID: OK
diff --git a/tests/ts/lsfd/assoc-pidfs b/tests/ts/lsfd/assoc-pidfs
new file mode 100755 (executable)
index 0000000..238d6ca
--- /dev/null
@@ -0,0 +1,75 @@
+#!/bin/bash
+#
+# Copyright (C) 2026 Masatake YAMATO <yamato@redhat.com>
+#
+# This file is part of util-linux.
+#
+# This file 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 file is distributed in the hope that it will 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.
+#
+TS_TOPDIR="${0%/*}/../.."
+TS_DESC="pidfs association"
+
+. "$TS_TOPDIR"/functions.sh
+ts_init "$*"
+
+ts_check_test_command "$TS_CMD_LSFD"
+ts_check_test_command "$TS_CMD_GETINO"
+ts_check_test_command "$TS_HELPER_MKFDS"
+
+ts_cd "$TS_OUTDIR"
+
+PID=
+INODE=
+{
+    coproc MKFDS { "$TS_HELPER_MKFDS" nop; }
+    if read -u ${MKFDS[0]} PID; then
+       INODE=$("$TS_CMD_GETINO" "$PID")
+       inode=$(${TS_CMD_LSFD} --pid="$PID" -n -o INODE -Q '(ASSOC == "pidfs")')
+       if [[ -z "$inode" ]]; then
+           echo INODE: "FAILED (empty)"
+       elif [[ "$inode" == "$INODE" ]]; then
+           echo INODE: OK
+       else
+           echo INODE: "FAILED (getino: $INODE, lsfd: $inode)"
+       fi
+
+       pid=$(${TS_CMD_LSFD} --pid="$PID" -n -o PIDFD.PID -Q '(ASSOC == "pidfs")')
+       if [[ -z "$pid" ]]; then
+           echo PID: "FAILED (empty)"
+       elif [[ "$pid" == "$PID" ]]; then
+           echo PID: OK
+       else
+           echo PID: "FAILED (PID: $PID, lsfd: $pid)"
+       fi
+
+       comm=$(${TS_CMD_LSFD} --pid="$PID" -n -o PIDFD.COMM -Q '(ASSOC == "pidfs")')
+       if [[ "$comm" =~ .*test_mkfds ]]; then
+           echo COMM: OK
+       else
+           echo COMM: "FAILED (lsfd: $comm)"
+       fi
+
+       nspid=$(${TS_CMD_LSFD} --pid="$PID" -n -o PIDFD.NSPID -Q '(ASSOC == "pidfs")')
+       if [[ -z "$nspid" ]]; then
+           echo NSPID: "FAILED (empty)"
+       elif [[ "$nspid" == "$PID" ]]; then
+           echo NSPID: OK
+       else
+           echo NSPID: "FAILED (PID: $PID, lsfd: $nspid)"
+       fi
+
+       echo DONE >&"${MKFDS[1]}"
+
+    fi
+    wait ${MKFDS_PID}
+} > $TS_OUTPUT 2>&1
+
+ts_finalize