};
static struct anon_ops anon_generic_ops;
+static struct anon_ops anon_pidfd_ops;
static bool unkn_fill_column(struct proc *proc __attribute__((__unused__)),
struct file *file,
if (major(file->stat.st_dev) == 0
&& strncmp(file->name, "anon_inode:", 11) == 0) {
- unkn->anon_ops = &anon_generic_ops;
+ const char *rest = file->name + 11;
+
+ if (strncmp(rest, "[pidfd]", 7) == 0)
+ unkn->anon_ops = &anon_pidfd_ops;
+ else
+ unkn->anon_ops = &anon_generic_ops;
+
if (unkn->anon_ops->init)
unkn->anon_ops->init(unkn);
}
return 0; /* Should be handled in parents */
}
+/*
+ * pidfd
+ */
+struct anon_pidfd_data {
+ pid_t pid;
+ char *nspid;
+};
+
+static char *anon_pidfd_get_name(struct unkn *unkn)
+{
+ char *str = NULL;
+ struct anon_pidfd_data *data = (struct anon_pidfd_data *)unkn->anon_data;
+
+ char *comm = NULL;
+ struct proc *proc = get_proc(data->pid);
+ if (proc)
+ comm = proc->command;
+
+ xasprintf(&str, "pidfd: pid=%d comm=%s nspid=%s",
+ data->pid,
+ comm? comm: "",
+ data->nspid? data->nspid: "");
+ return str;
+}
+
+static void anon_pidfd_init(struct unkn *unkn)
+{
+ unkn->anon_data = xcalloc(1, sizeof(struct anon_pidfd_data));
+}
+
+static void anon_pidfd_free(struct unkn *unkn)
+{
+ struct anon_pidfd_data *data = (struct anon_pidfd_data *)unkn->anon_data;
+
+ if (data->nspid)
+ free(data->nspid);
+ free(data);
+}
+
+static int anon_pidfd_handle_fdinfo(struct unkn *unkn, const char *key, const char *value)
+{
+ if (strcmp(key, "Pid") == 0) {
+ uint64_t pid;
+
+ int rc = ul_strtou64(value, &pid, 10);
+ if (rc < 0)
+ return 0; /* ignore -- parse failed */
+ ((struct anon_pidfd_data *)unkn->anon_data)->pid = (pid_t)pid;
+ return 1;
+ }
+ else if (strcmp(key, "NSpid") == 0) {
+ ((struct anon_pidfd_data *)unkn->anon_data)->nspid = xstrdup(value);
+ return 1;
+
+ }
+ return 0;
+}
+
+static struct anon_ops anon_pidfd_ops = {
+ .get_name = anon_pidfd_get_name,
+ .init = anon_pidfd_init,
+ .free = anon_pidfd_free,
+ .handle_fdinfo = anon_pidfd_handle_fdinfo,
+};
+
/*
* generic (fallback implementation)
*/
--- /dev/null
+ 3 UNKN anon_inodefs pidfd: pid=1 comm=systemd nspid=1
+ASSOC,TYPE,SOURCE,NAME: 0
#include <sys/mman.h>
#include <sys/prctl.h>
#include <sys/socket.h>
+#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/un.h>
#include <sys/user.h>
#define _U_ __attribute__((__unused__))
+static int pidfd_open(pid_t pid, unsigned int flags);
+
static void __attribute__((__noreturn__)) usage(FILE *out, int status)
{
fputs(USAGE_HEADER, out);
};
}
+static void make_pidfd(const struct factory *factory, struct fdesc fdescs[], pid_t * child _U_,
+ int argc, char ** argv)
+{
+ struct arg target_pid = decode_arg("target-pid", factory->params, argc, argv);
+ pid_t pid = ARG_INTEGER(target_pid);
+
+ int fd = pidfd_open(pid, 0);
+ if (fd < 0)
+ err(EXIT_FAILURE, "failed in pidfd_open(%d)", (int)pid);
+ free_arg(&target_pid);
+
+ if (fd != fdescs[0].fd) {
+ if (dup2(fd, fdescs[0].fd) < 0) {
+ int e = errno;
+ close(fd);
+ errno = e;
+ err(EXIT_FAILURE, "failed to dup %d -> %d", fd, fdescs[0].fd);
+ }
+ close(fd);
+ }
+
+ fdescs[0] = (struct fdesc){
+ .fd = fdescs[0].fd,
+ .close = close_fdesc,
+ .data = NULL
+ };
+}
+
#define PARAM_END { .name = NULL, }
static const struct factory factories[] = {
{
PARAM_END
},
},
+ {
+
+ .name = "pidfd",
+ .desc = "pidfd returned from pidfd_open(2)",
+ .priv = false,
+ .N = 1,
+ .EX_N = 0,
+ .fork = false,
+ .make = make_pidfd,
+ .params = (struct parameter []) {
+ {
+ .name = "target-pid",
+ .type = PTYPE_INTEGER,
+ .desc = "the pid of the target process",
+ .defv.integer = 1,
+ },
+ PARAM_END
+ },
+ },
};
static int count_parameters(const struct factory *factory)
{
}
+#ifdef __NR_pidfd_open
+
+static int
+pidfd_open(pid_t pid, unsigned int flags)
+{
+ return syscall(__NR_pidfd_open, pid, flags);
+}
+#else
+static int
+pidfd_open(pid_t pid _U_, unsigned int flags _U_)
+{
+ errno = ENOSYS;
+ return -1;
+}
+#endif
+
int main(int argc, char **argv)
{
int c;
--- /dev/null
+#!/bin/bash
+#
+# Copyright (C) 2022 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="pidfd"
+
+. $TS_TOPDIR/functions.sh
+ts_init "$*"
+
+ts_check_test_command "$TS_CMD_LSFD"
+ts_check_test_command "$TS_HELPER_MKFDS"
+ts_check_prog "ps"
+
+ts_cd "$TS_OUTDIR"
+
+[ "$(ps --no-headers -o comm 1)" = 'systemd' ] || ts_skip "pid 1 is not systemd"
+
+PID=
+FD=3
+TARGET=1
+EXPR="(PID != ${TARGET}) and (FD == 3)"
+
+{
+ coproc MKFDS { "$TS_HELPER_MKFDS" pidfd $FD target-pid=${TARGET} ; }
+ if read -u ${MKFDS[0]} PID; then
+ ${TS_CMD_LSFD} -n -o ASSOC,TYPE,SOURCE,NAME -p "${PID}" -p ${TARGET} -Q "${EXPR}"
+ echo 'ASSOC,TYPE,SOURCE,NAME': $?
+
+ kill -CONT ${PID}
+ wait ${MKFDS_PID}
+ fi
+} > $TS_OUTPUT 2>&1
+
+ts_finalize