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>
/* "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",
#include "fileutils.h"
#include "idcache.h"
#include "pathnames.h"
+#include "pidfd-utils.h"
#include "lsfd.h"
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)
{
|| 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.
ASSOC_EXE = 1,
ASSOC_CWD,
ASSOC_ROOT,
+ ASSOC_PIDFS,
ASSOC_NS_CGROUP,
ASSOC_NS_IPC,
ASSOC_NS_MNT,
#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;
--- /dev/null
+INODE: OK
+PID: OK
+COMM: OK
+NSPID: OK
--- /dev/null
+#!/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