basic: port various pidfd/pidref helpers to PIDFD_GET_INFO and PIDFD_GET_*_NAMESPACE (#35242)
Supersedes #35308 (cherry-picked one commit and replaced the rest) (I left a few comments that's folded by GitHub. Please make sure to check them too.)
This commit is contained in:
@@ -68,6 +68,27 @@ int cg_cgroupid_open(int cgroupfs_fd, uint64_t id) {
|
||||
return RET_NERRNO(open_by_handle_at(cgroupfs_fd, &fh.file_handle, O_DIRECTORY|O_CLOEXEC));
|
||||
}
|
||||
|
||||
int cg_path_from_cgroupid(int cgroupfs_fd, uint64_t id, char **ret) {
|
||||
_cleanup_close_ int cgfd = -EBADF;
|
||||
int r;
|
||||
|
||||
cgfd = cg_cgroupid_open(cgroupfs_fd, id);
|
||||
if (cgfd < 0)
|
||||
return cgfd;
|
||||
|
||||
_cleanup_free_ char *path = NULL;
|
||||
r = fd_get_path(cgfd, &path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!path_startswith(path, "/sys/fs/cgroup/"))
|
||||
return -EXDEV; /* recognizable error */
|
||||
|
||||
if (ret)
|
||||
*ret = TAKE_PTR(path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cg_get_cgroupid_at(int dfd, const char *path, uint64_t *ret) {
|
||||
cg_file_handle fh = CG_FILE_HANDLE_INIT;
|
||||
int mnt_id;
|
||||
@@ -842,6 +863,10 @@ int cg_pidref_get_path(const char *controller, const PidRef *pidref, char **ret_
|
||||
if (pidref_is_remote(pidref))
|
||||
return -EREMOTE;
|
||||
|
||||
// XXX: Ideally we'd use pidfd_get_cgroupid() + cg_path_from_cgroupid() here, to extract this
|
||||
// bit of information from pidfd directly. However, the latter requires privilege and it's
|
||||
// not entirely clear how to handle cgroups from outer namespace.
|
||||
|
||||
r = cg_pid_get_path(controller, pidref->pid, &path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -183,6 +183,7 @@ typedef enum CGroupUnified {
|
||||
int cg_path_open(const char *controller, const char *path);
|
||||
int cg_cgroupid_open(int fsfd, uint64_t id);
|
||||
|
||||
int cg_path_from_cgroupid(int cgroupfs_fd, uint64_t id, char **ret);
|
||||
int cg_get_cgroupid_at(int dfd, const char *path, uint64_t *ret);
|
||||
static inline int cg_path_get_cgroupid(const char *path, uint64_t *ret) {
|
||||
return cg_get_cgroupid_at(AT_FDCWD, path, ret);
|
||||
|
||||
@@ -103,7 +103,7 @@ int chattr_full(
|
||||
continue;
|
||||
|
||||
if (ioctl(fd, FS_IOC_SETFLAGS, &new_one) < 0) {
|
||||
if (errno != EINVAL && !ERRNO_IS_NOT_SUPPORTED(errno))
|
||||
if (!ERRNO_IS_IOCTL_NOT_SUPPORTED(errno))
|
||||
return -errno;
|
||||
|
||||
log_full_errno(FLAGS_SET(flags, CHATTR_WARN_UNSUPPORTED_FLAGS) ? LOG_WARNING : LOG_DEBUG,
|
||||
|
||||
@@ -158,7 +158,7 @@ static inline bool ERRNO_IS_NEG_RESOURCE(intmax_t r) {
|
||||
}
|
||||
_DEFINE_ABS_WRAPPER(RESOURCE);
|
||||
|
||||
/* Seven different errors for "operation/system call/ioctl/socket feature not supported" */
|
||||
/* Seven different errors for "operation/system call/socket feature not supported" */
|
||||
static inline bool ERRNO_IS_NEG_NOT_SUPPORTED(intmax_t r) {
|
||||
return IN_SET(r,
|
||||
-EOPNOTSUPP,
|
||||
@@ -172,6 +172,12 @@ static inline bool ERRNO_IS_NEG_NOT_SUPPORTED(intmax_t r) {
|
||||
}
|
||||
_DEFINE_ABS_WRAPPER(NOT_SUPPORTED);
|
||||
|
||||
/* ioctl() with unsupported command/arg might additionally return EINVAL */
|
||||
static inline bool ERRNO_IS_NEG_IOCTL_NOT_SUPPORTED(intmax_t r) {
|
||||
return ERRNO_IS_NEG_NOT_SUPPORTED(r) || r == -EINVAL;
|
||||
}
|
||||
_DEFINE_ABS_WRAPPER(IOCTL_NOT_SUPPORTED);
|
||||
|
||||
/* Two different errors for access problems */
|
||||
static inline bool ERRNO_IS_NEG_PRIVILEGE(intmax_t r) {
|
||||
return IN_SET(r,
|
||||
|
||||
@@ -72,6 +72,7 @@ basic_sources = files(
|
||||
'parse-util.c',
|
||||
'path-util.c',
|
||||
'percent-util.c',
|
||||
'pidfd-util.c',
|
||||
'pidref.c',
|
||||
'prioq.c',
|
||||
'proc-cmdline.c',
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <linux/types.h>
|
||||
#if HAVE_PIDFD_OPEN
|
||||
#include <sys/pidfd.h>
|
||||
#endif
|
||||
|
||||
#ifndef PIDFS_IOCTL_MAGIC
|
||||
# define PIDFS_IOCTL_MAGIC 0xFF
|
||||
#endif
|
||||
|
||||
#ifndef PIDFD_GET_CGROUP_NAMESPACE
|
||||
# define PIDFD_GET_CGROUP_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 1)
|
||||
# define PIDFD_GET_IPC_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 2)
|
||||
# define PIDFD_GET_MNT_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 3)
|
||||
# define PIDFD_GET_NET_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 4)
|
||||
# define PIDFD_GET_PID_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 5)
|
||||
# define PIDFD_GET_PID_FOR_CHILDREN_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 6)
|
||||
# define PIDFD_GET_TIME_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 7)
|
||||
# define PIDFD_GET_TIME_FOR_CHILDREN_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 8)
|
||||
# define PIDFD_GET_USER_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 9)
|
||||
# define PIDFD_GET_UTS_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 10)
|
||||
#endif
|
||||
|
||||
#ifndef PIDFD_GET_INFO
|
||||
struct pidfd_info {
|
||||
__u64 mask;
|
||||
__u64 cgroupid;
|
||||
__u32 pid;
|
||||
__u32 tgid;
|
||||
__u32 ppid;
|
||||
__u32 ruid;
|
||||
__u32 rgid;
|
||||
__u32 euid;
|
||||
__u32 egid;
|
||||
__u32 suid;
|
||||
__u32 sgid;
|
||||
__u32 fsuid;
|
||||
__u32 fsgid;
|
||||
__u32 spare0[1];
|
||||
};
|
||||
|
||||
#define PIDFD_GET_INFO _IOWR(PIDFS_IOCTL_MAGIC, 11, struct pidfd_info)
|
||||
#define PIDFD_INFO_PID (1UL << 0)
|
||||
#define PIDFD_INFO_CREDS (1UL << 1)
|
||||
#define PIDFD_INFO_CGROUPID (1UL << 2)
|
||||
#endif
|
||||
+203
-163
@@ -18,29 +18,30 @@
|
||||
#include "mountpoint-util.h"
|
||||
#include "namespace-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "pidfd-util.h"
|
||||
#include "process-util.h"
|
||||
#include "stat-util.h"
|
||||
#include "stdio-util.h"
|
||||
#include "user-util.h"
|
||||
|
||||
const struct namespace_info namespace_info[_NAMESPACE_TYPE_MAX + 1] = {
|
||||
[NAMESPACE_CGROUP] = { "cgroup", "ns/cgroup", CLONE_NEWCGROUP, PROC_CGROUP_INIT_INO },
|
||||
[NAMESPACE_IPC] = { "ipc", "ns/ipc", CLONE_NEWIPC, PROC_IPC_INIT_INO },
|
||||
[NAMESPACE_NET] = { "net", "ns/net", CLONE_NEWNET, 0 },
|
||||
[NAMESPACE_CGROUP] = { "cgroup", "ns/cgroup", CLONE_NEWCGROUP, PIDFD_GET_CGROUP_NAMESPACE, PROC_CGROUP_INIT_INO },
|
||||
[NAMESPACE_IPC] = { "ipc", "ns/ipc", CLONE_NEWIPC, PIDFD_GET_IPC_NAMESPACE, PROC_IPC_INIT_INO },
|
||||
[NAMESPACE_NET] = { "net", "ns/net", CLONE_NEWNET, PIDFD_GET_NET_NAMESPACE, 0 },
|
||||
/* So, the mount namespace flag is called CLONE_NEWNS for historical
|
||||
* reasons. Let's expose it here under a more explanatory name: "mnt".
|
||||
* This is in-line with how the kernel exposes namespaces in /proc/$PID/ns. */
|
||||
[NAMESPACE_MOUNT] = { "mnt", "ns/mnt", CLONE_NEWNS, 0 },
|
||||
[NAMESPACE_PID] = { "pid", "ns/pid", CLONE_NEWPID, PROC_PID_INIT_INO },
|
||||
[NAMESPACE_USER] = { "user", "ns/user", CLONE_NEWUSER, PROC_USER_INIT_INO },
|
||||
[NAMESPACE_UTS] = { "uts", "ns/uts", CLONE_NEWUTS, PROC_UTS_INIT_INO },
|
||||
[NAMESPACE_TIME] = { "time", "ns/time", CLONE_NEWTIME, PROC_TIME_INIT_INO },
|
||||
{ /* Allow callers to iterate over the array without using _NAMESPACE_TYPE_MAX. */ },
|
||||
[NAMESPACE_MOUNT] = { "mnt", "ns/mnt", CLONE_NEWNS, PIDFD_GET_MNT_NAMESPACE, 0 },
|
||||
[NAMESPACE_PID] = { "pid", "ns/pid", CLONE_NEWPID, PIDFD_GET_PID_NAMESPACE, PROC_PID_INIT_INO },
|
||||
[NAMESPACE_USER] = { "user", "ns/user", CLONE_NEWUSER, PIDFD_GET_USER_NAMESPACE, PROC_USER_INIT_INO },
|
||||
[NAMESPACE_UTS] = { "uts", "ns/uts", CLONE_NEWUTS, PIDFD_GET_UTS_NAMESPACE, PROC_UTS_INIT_INO },
|
||||
[NAMESPACE_TIME] = { "time", "ns/time", CLONE_NEWTIME, PIDFD_GET_TIME_NAMESPACE, PROC_TIME_INIT_INO },
|
||||
{}, /* Allow callers to iterate over the array without using _NAMESPACE_TYPE_MAX. */
|
||||
};
|
||||
|
||||
#define pid_namespace_path(pid, type) procfs_file_alloca(pid, namespace_info[type].proc_path)
|
||||
|
||||
static NamespaceType clone_flag_to_namespace_type(unsigned long clone_flag) {
|
||||
NamespaceType clone_flag_to_namespace_type(unsigned long clone_flag) {
|
||||
for (NamespaceType t = 0; t < _NAMESPACE_TYPE_MAX; t++)
|
||||
if (((namespace_info[t].clone_flag ^ clone_flag) & (CLONE_NEWCGROUP|CLONE_NEWIPC|CLONE_NEWNET|CLONE_NEWNS|CLONE_NEWPID|CLONE_NEWUSER|CLONE_NEWUTS|CLONE_NEWTIME)) == 0)
|
||||
return t;
|
||||
@@ -48,6 +49,62 @@ static NamespaceType clone_flag_to_namespace_type(unsigned long clone_flag) {
|
||||
return _NAMESPACE_TYPE_INVALID;
|
||||
}
|
||||
|
||||
static int pidref_namespace_open_by_type_internal(const PidRef *pidref, NamespaceType type, bool *need_verify) {
|
||||
int r;
|
||||
|
||||
assert(pidref_is_set(pidref));
|
||||
assert(type >= 0 && type < _NAMESPACE_TYPE_MAX);
|
||||
|
||||
if (pidref_is_remote(pidref))
|
||||
return -EREMOTE;
|
||||
|
||||
if (pidref->fd >= 0) {
|
||||
r = pidfd_get_namespace(pidref->fd, namespace_info[type].pidfd_get_ns_ioctl_cmd);
|
||||
if (!ERRNO_IS_NEG_NOT_SUPPORTED(r))
|
||||
return r;
|
||||
}
|
||||
|
||||
if (need_verify) /* The caller shall call pidref_verify() later */
|
||||
*need_verify = true;
|
||||
|
||||
_cleanup_close_ int nsfd = -EBADF;
|
||||
const char *p;
|
||||
|
||||
p = pid_namespace_path(pidref->pid, type);
|
||||
nsfd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
|
||||
if (nsfd < 0) {
|
||||
if (errno == ENOENT && proc_mounted() == 0)
|
||||
return -ENOSYS;
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (!need_verify) { /* Otherwise we verify on our own */
|
||||
r = pidref_verify(pidref);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return TAKE_FD(nsfd);
|
||||
}
|
||||
|
||||
int pidref_namespace_open_by_type(const PidRef *pidref, NamespaceType type) {
|
||||
return pidref_namespace_open_by_type_internal(pidref, type, NULL);
|
||||
}
|
||||
|
||||
int namespace_open_by_type(NamespaceType type) {
|
||||
_cleanup_(pidref_done) PidRef self = PIDREF_NULL;
|
||||
int r;
|
||||
|
||||
assert(type >= 0 && type < _NAMESPACE_TYPE_MAX);
|
||||
|
||||
r = pidref_set_self(&self);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return pidref_namespace_open_by_type(&self, type);
|
||||
}
|
||||
|
||||
int pidref_namespace_open(
|
||||
const PidRef *pidref,
|
||||
int *ret_pidns_fd,
|
||||
@@ -58,58 +115,56 @@ int pidref_namespace_open(
|
||||
|
||||
_cleanup_close_ int pidns_fd = -EBADF, mntns_fd = -EBADF, netns_fd = -EBADF,
|
||||
userns_fd = -EBADF, root_fd = -EBADF;
|
||||
bool need_verify = false;
|
||||
int r;
|
||||
|
||||
assert(pidref_is_set(pidref));
|
||||
|
||||
if (ret_pidns_fd) {
|
||||
const char *pidns;
|
||||
if (pidref_is_remote(pidref))
|
||||
return -EREMOTE;
|
||||
|
||||
pidns = pid_namespace_path(pidref->pid, NAMESPACE_PID);
|
||||
pidns_fd = open(pidns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
|
||||
if (ret_pidns_fd) {
|
||||
pidns_fd = pidref_namespace_open_by_type_internal(pidref, NAMESPACE_PID, &need_verify);
|
||||
if (pidns_fd < 0)
|
||||
return -errno;
|
||||
return pidns_fd;
|
||||
}
|
||||
|
||||
if (ret_mntns_fd) {
|
||||
const char *mntns;
|
||||
|
||||
mntns = pid_namespace_path(pidref->pid, NAMESPACE_MOUNT);
|
||||
mntns_fd = open(mntns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
|
||||
mntns_fd = pidref_namespace_open_by_type_internal(pidref, NAMESPACE_MOUNT, &need_verify);
|
||||
if (mntns_fd < 0)
|
||||
return -errno;
|
||||
return mntns_fd;
|
||||
}
|
||||
|
||||
if (ret_netns_fd) {
|
||||
const char *netns;
|
||||
|
||||
netns = pid_namespace_path(pidref->pid, NAMESPACE_NET);
|
||||
netns_fd = open(netns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
|
||||
netns_fd = pidref_namespace_open_by_type_internal(pidref, NAMESPACE_NET, &need_verify);
|
||||
if (netns_fd < 0)
|
||||
return -errno;
|
||||
return netns_fd;
|
||||
}
|
||||
|
||||
if (ret_userns_fd) {
|
||||
const char *userns;
|
||||
|
||||
userns = pid_namespace_path(pidref->pid, NAMESPACE_USER);
|
||||
userns_fd = open(userns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
|
||||
if (userns_fd < 0 && errno != ENOENT)
|
||||
return -errno;
|
||||
userns_fd = pidref_namespace_open_by_type_internal(pidref, NAMESPACE_USER, &need_verify);
|
||||
if (userns_fd < 0 && !IN_SET(userns_fd, -ENOENT, -ENOPKG))
|
||||
return userns_fd;
|
||||
}
|
||||
|
||||
if (ret_root_fd) {
|
||||
const char *root;
|
||||
|
||||
root = procfs_file_alloca(pidref->pid, "root");
|
||||
root_fd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
|
||||
root_fd = RET_NERRNO(open(root, O_CLOEXEC|O_DIRECTORY));
|
||||
if (root_fd == -ENOENT && proc_mounted() == 0)
|
||||
return -ENOSYS;
|
||||
if (root_fd < 0)
|
||||
return -errno;
|
||||
return root_fd;
|
||||
|
||||
need_verify = true;
|
||||
}
|
||||
|
||||
r = pidref_verify(pidref);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (need_verify) {
|
||||
r = pidref_verify(pidref);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (ret_pidns_fd)
|
||||
*ret_pidns_fd = TAKE_FD(pidns_fd);
|
||||
@@ -154,10 +209,10 @@ int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int
|
||||
/* Can't setns to your own userns, since then you could escalate from non-root to root in
|
||||
* your own namespace, so check if namespaces are equal before attempting to enter. */
|
||||
|
||||
r = inode_same_at(userns_fd, "", AT_FDCWD, "/proc/self/ns/user", AT_EMPTY_PATH);
|
||||
r = is_our_namespace(userns_fd, NAMESPACE_USER);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r)
|
||||
if (r > 0)
|
||||
userns_fd = -EBADF;
|
||||
}
|
||||
|
||||
@@ -188,50 +243,128 @@ int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int
|
||||
return reset_uid_gid();
|
||||
}
|
||||
|
||||
int fd_is_ns(int fd, unsigned long nsflag) {
|
||||
struct statfs s;
|
||||
int fd_is_namespace(int fd, NamespaceType type) {
|
||||
int r;
|
||||
|
||||
/* Checks whether the specified file descriptor refers to a namespace created by specifying nsflag in clone().
|
||||
* On old kernels there's no nice way to detect that, hence on those we'll return a recognizable error (EUCLEAN),
|
||||
* so that callers can handle this somewhat nicely.
|
||||
*
|
||||
* This function returns > 0 if the fd definitely refers to a network namespace, 0 if it definitely does not
|
||||
* refer to a network namespace, -EUCLEAN if we can't determine, and other negative error codes on error. */
|
||||
/* Checks whether the specified file descriptor refers to a namespace (of type if type != _NAMESPACE_INVALID). */
|
||||
|
||||
if (fstatfs(fd, &s) < 0)
|
||||
assert(fd >= 0);
|
||||
assert(type < _NAMESPACE_TYPE_MAX);
|
||||
|
||||
r = fd_is_fs_type(fd, NSFS_MAGIC);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
if (type < 0)
|
||||
return true;
|
||||
|
||||
int clone_flag = ioctl(fd, NS_GET_NSTYPE);
|
||||
if (clone_flag < 0)
|
||||
return -errno;
|
||||
|
||||
if (!is_fs_type(&s, NSFS_MAGIC)) {
|
||||
/* On really old kernels, there was no "nsfs", and network namespace sockets belonged to procfs
|
||||
* instead. Handle that in a somewhat smart way. */
|
||||
NamespaceType found_type = clone_flag_to_namespace_type(clone_flag);
|
||||
if (found_type < 0)
|
||||
return -EBADF; /* Uh? Unknown namespace type? */
|
||||
|
||||
if (is_fs_type(&s, PROC_SUPER_MAGIC)) {
|
||||
struct statfs t;
|
||||
return found_type == type;
|
||||
}
|
||||
|
||||
/* OK, so it is procfs. Let's see if our own network namespace is procfs, too. If so, then the
|
||||
* passed fd might refer to a network namespace, but we can't know for sure. In that case,
|
||||
* return a recognizable error. */
|
||||
int is_our_namespace(int fd, NamespaceType type) {
|
||||
int r;
|
||||
|
||||
if (statfs("/proc/self/ns/net", &t) < 0)
|
||||
return -errno;
|
||||
assert(fd >= 0);
|
||||
assert(type < _NAMESPACE_TYPE_MAX);
|
||||
|
||||
if (s.f_type == t.f_type)
|
||||
return -EUCLEAN; /* It's possible, we simply don't know */
|
||||
r = fd_is_namespace(fd, type);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) /* Not a namespace or not of the right type? */
|
||||
return -EUCLEAN;
|
||||
|
||||
_cleanup_close_ int our_ns = namespace_open_by_type(type);
|
||||
if (our_ns < 0)
|
||||
return our_ns;
|
||||
|
||||
return fd_inode_same(fd, our_ns);
|
||||
}
|
||||
|
||||
int namespace_is_init(NamespaceType type) {
|
||||
int r;
|
||||
|
||||
assert(type >= 0);
|
||||
assert(type < _NAMESPACE_TYPE_MAX);
|
||||
|
||||
if (namespace_info[type].root_inode == 0)
|
||||
return -EBADR; /* Cannot answer this question */
|
||||
|
||||
const char *p = pid_namespace_path(0, type);
|
||||
|
||||
struct stat st;
|
||||
r = RET_NERRNO(stat(p, &st));
|
||||
if (r == -ENOENT)
|
||||
/* If the /proc/ns/<type> API is not around in /proc/ then ns is off in the kernel and we are in the init ns */
|
||||
return proc_mounted() == 0 ? -ENOSYS : true;
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return st.st_ino == namespace_info[type].root_inode;
|
||||
}
|
||||
|
||||
int pidref_in_same_namespace(PidRef *pid1, PidRef *pid2, NamespaceType type) {
|
||||
_cleanup_close_ int ns1 = -EBADF, ns2 = -EBADF;
|
||||
|
||||
/* Accepts NULL to indicate our own process */
|
||||
|
||||
assert(!pid1 || pidref_is_set(pid1));
|
||||
assert(!pid2 || pidref_is_set(pid2));
|
||||
assert(type >= 0 && type < _NAMESPACE_TYPE_MAX);
|
||||
|
||||
if (pidref_equal(pid1, pid2))
|
||||
return true;
|
||||
|
||||
if (!pid1)
|
||||
ns1 = namespace_open_by_type(type);
|
||||
else
|
||||
ns1 = pidref_namespace_open_by_type(pid1, type);
|
||||
if (ns1 < 0)
|
||||
return ns1;
|
||||
|
||||
if (!pid2)
|
||||
ns2 = namespace_open_by_type(type);
|
||||
else
|
||||
ns2 = pidref_namespace_open_by_type(pid2, type);
|
||||
if (ns2 < 0)
|
||||
return ns2;
|
||||
|
||||
return fd_inode_same(ns1, ns2);
|
||||
}
|
||||
|
||||
int namespace_get_leader(pid_t pid, NamespaceType type, pid_t *ret) {
|
||||
int r;
|
||||
|
||||
assert(pid >= 0);
|
||||
assert(type >= 0 && type < _NAMESPACE_TYPE_MAX);
|
||||
assert(ret);
|
||||
|
||||
for (;;) {
|
||||
pid_t ppid;
|
||||
|
||||
r = pid_get_ppid(pid, &ppid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = in_same_namespace(pid, ppid, type);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) {
|
||||
/* If the parent and the child are not in the same namespace, then the child is
|
||||
* the leader we are looking for. */
|
||||
*ret = pid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0; /* No! */
|
||||
pid = ppid;
|
||||
}
|
||||
|
||||
r = ioctl(fd, NS_GET_NSTYPE);
|
||||
if (r < 0) {
|
||||
if (errno == ENOTTY) /* Old kernels didn't know this ioctl, let's also return a recognizable error in that case */
|
||||
return -EUCLEAN;
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return (unsigned long) r == nsflag;
|
||||
}
|
||||
|
||||
int detach_mount_namespace(void) {
|
||||
@@ -402,30 +535,6 @@ int netns_acquire(void) {
|
||||
return TAKE_FD(netns_fd);
|
||||
}
|
||||
|
||||
int in_same_namespace(pid_t pid1, pid_t pid2, NamespaceType type) {
|
||||
const char *ns_path;
|
||||
struct stat ns_st1, ns_st2;
|
||||
|
||||
if (pid1 == 0)
|
||||
pid1 = getpid_cached();
|
||||
|
||||
if (pid2 == 0)
|
||||
pid2 = getpid_cached();
|
||||
|
||||
if (pid1 == pid2)
|
||||
return 1;
|
||||
|
||||
ns_path = pid_namespace_path(pid1, type);
|
||||
if (stat(ns_path, &ns_st1) < 0)
|
||||
return -errno;
|
||||
|
||||
ns_path = pid_namespace_path(pid2, type);
|
||||
if (stat(ns_path, &ns_st2) < 0)
|
||||
return -errno;
|
||||
|
||||
return stat_inode_same(&ns_st1, &ns_st2);
|
||||
}
|
||||
|
||||
int parse_userns_uid_range(const char *s, uid_t *ret_uid_shift, uid_t *ret_uid_range) {
|
||||
_cleanup_free_ char *buffer = NULL;
|
||||
const char *range, *shift;
|
||||
@@ -464,75 +573,6 @@ int parse_userns_uid_range(const char *s, uid_t *ret_uid_shift, uid_t *ret_uid_r
|
||||
return 0;
|
||||
}
|
||||
|
||||
int namespace_open_by_type(NamespaceType type) {
|
||||
const char *p;
|
||||
int fd;
|
||||
|
||||
assert(type >= 0);
|
||||
assert(type < _NAMESPACE_TYPE_MAX);
|
||||
|
||||
p = pid_namespace_path(0, type);
|
||||
|
||||
fd = RET_NERRNO(open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC));
|
||||
if (fd == -ENOENT && proc_mounted() == 0)
|
||||
return -ENOSYS;
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int namespace_is_init(NamespaceType type) {
|
||||
int r;
|
||||
|
||||
assert(type >= 0);
|
||||
assert(type <= _NAMESPACE_TYPE_MAX);
|
||||
|
||||
if (namespace_info[type].root_inode == 0)
|
||||
return -EBADR; /* Cannot answer this question */
|
||||
|
||||
const char *p = pid_namespace_path(0, type);
|
||||
|
||||
struct stat st;
|
||||
r = RET_NERRNO(stat(p, &st));
|
||||
if (r == -ENOENT)
|
||||
/* If the /proc/ns/<type> API is not around in /proc/ then ns is off in the kernel and we are in the init ns */
|
||||
return proc_mounted() == 0 ? -ENOSYS : true;
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return st.st_ino == namespace_info[type].root_inode;
|
||||
}
|
||||
|
||||
int is_our_namespace(int fd, NamespaceType request_type) {
|
||||
int clone_flag;
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
clone_flag = ioctl(fd, NS_GET_NSTYPE);
|
||||
if (clone_flag < 0)
|
||||
return -errno;
|
||||
|
||||
NamespaceType found_type = clone_flag_to_namespace_type(clone_flag);
|
||||
if (found_type < 0)
|
||||
return -EBADF; /* Uh? Unknown namespace type? */
|
||||
|
||||
if (request_type >= 0 && request_type != found_type) /* It's a namespace, but not of the right type? */
|
||||
return -EUCLEAN;
|
||||
|
||||
struct stat st_fd, st_ours;
|
||||
if (fstat(fd, &st_fd) < 0)
|
||||
return -errno;
|
||||
|
||||
const char *p = pid_namespace_path(0, found_type);
|
||||
if (stat(p, &st_ours) < 0) {
|
||||
if (errno == ENOENT)
|
||||
return proc_mounted() == 0 ? -ENOSYS : -ENOENT;
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return stat_inode_same(&st_ours, &st_fd);
|
||||
}
|
||||
|
||||
int is_idmapping_supported(const char *path) {
|
||||
_cleanup_close_ int mount_fd = -EBADF, userns_fd = -EBADF, dir_fd = -EBADF;
|
||||
_cleanup_free_ char *uid_map = NULL, *gid_map = NULL;
|
||||
|
||||
+25
-14
@@ -3,11 +3,9 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
typedef enum NamespaceType NamespaceType;
|
||||
|
||||
#include "pidref.h"
|
||||
|
||||
enum NamespaceType {
|
||||
typedef enum NamespaceType {
|
||||
NAMESPACE_CGROUP,
|
||||
NAMESPACE_IPC,
|
||||
NAMESPACE_NET,
|
||||
@@ -18,15 +16,21 @@ enum NamespaceType {
|
||||
NAMESPACE_TIME,
|
||||
_NAMESPACE_TYPE_MAX,
|
||||
_NAMESPACE_TYPE_INVALID = -EINVAL,
|
||||
};
|
||||
} NamespaceType;
|
||||
|
||||
extern const struct namespace_info {
|
||||
const char *proc_name;
|
||||
const char *proc_path;
|
||||
unsigned int clone_flag;
|
||||
unsigned long clone_flag;
|
||||
unsigned long pidfd_get_ns_ioctl_cmd;
|
||||
ino_t root_inode;
|
||||
} namespace_info[_NAMESPACE_TYPE_MAX + 1];
|
||||
|
||||
NamespaceType clone_flag_to_namespace_type(unsigned long clone_flag);
|
||||
|
||||
int pidref_namespace_open_by_type(const PidRef *pidref, NamespaceType type);
|
||||
int namespace_open_by_type(NamespaceType type);
|
||||
|
||||
int pidref_namespace_open(
|
||||
const PidRef *pidref,
|
||||
int *ret_pidns_fd,
|
||||
@@ -41,9 +45,24 @@ int namespace_open(
|
||||
int *ret_netns_fd,
|
||||
int *ret_userns_fd,
|
||||
int *ret_root_fd);
|
||||
|
||||
int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd);
|
||||
|
||||
int fd_is_ns(int fd, unsigned long nsflag);
|
||||
int fd_is_namespace(int fd, NamespaceType type);
|
||||
int is_our_namespace(int fd, NamespaceType type);
|
||||
|
||||
int namespace_is_init(NamespaceType type);
|
||||
|
||||
int pidref_in_same_namespace(PidRef *pid1, PidRef *pid2, NamespaceType type);
|
||||
static inline int in_same_namespace(pid_t pid1, pid_t pid2, NamespaceType type) {
|
||||
assert(pid1 >= 0);
|
||||
assert(pid2 >= 0);
|
||||
return pidref_in_same_namespace(pid1 == 0 ? NULL : &PIDREF_MAKE_FROM_PID(pid1),
|
||||
pid2 == 0 ? NULL : &PIDREF_MAKE_FROM_PID(pid2),
|
||||
type);
|
||||
}
|
||||
|
||||
int namespace_get_leader(pid_t pid, NamespaceType type, pid_t *ret);
|
||||
|
||||
int detach_mount_namespace(void);
|
||||
int detach_mount_namespace_harder(uid_t target_uid, gid_t target_gid);
|
||||
@@ -69,14 +88,6 @@ int userns_acquire(const char *uid_map, const char *gid_map);
|
||||
|
||||
int netns_acquire(void);
|
||||
|
||||
int in_same_namespace(pid_t pid1, pid_t pid2, NamespaceType type);
|
||||
|
||||
int parse_userns_uid_range(const char *s, uid_t *ret_uid_shift, uid_t *ret_uid_range);
|
||||
|
||||
int namespace_open_by_type(NamespaceType type);
|
||||
|
||||
int namespace_is_init(NamespaceType type);
|
||||
|
||||
int is_our_namespace(int fd, NamespaceType type);
|
||||
|
||||
int is_idmapping_supported(const char *path);
|
||||
|
||||
@@ -0,0 +1,251 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "macro.h"
|
||||
#include "memory-util.h"
|
||||
#include "missing_magic.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "pidfd-util.h"
|
||||
#include "process-util.h"
|
||||
#include "stat-util.h"
|
||||
#include "string-util.h"
|
||||
|
||||
static int have_pidfs = -1;
|
||||
|
||||
static int pidfd_check_pidfs(void) {
|
||||
|
||||
if (have_pidfs >= 0)
|
||||
return have_pidfs;
|
||||
|
||||
_cleanup_close_ int fd = pidfd_open(getpid_cached(), 0);
|
||||
if (fd < 0) {
|
||||
if (ERRNO_IS_NOT_SUPPORTED(errno))
|
||||
return (have_pidfs = false);
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return (have_pidfs = fd_is_fs_type(fd, PID_FS_MAGIC));
|
||||
}
|
||||
|
||||
int pidfd_get_namespace(int fd, unsigned long ns_type_cmd) {
|
||||
static bool cached_supported = true;
|
||||
|
||||
/* Obtain the namespace fd from pidfd directly through ioctl(PIDFD_GET_*_NAMESPACE).
|
||||
*
|
||||
* Returns -EOPNOTSUPP if ioctl on pidfds are not supported, -ENOPKG if the requested namespace
|
||||
* is disabled in kernel. (The errno used are different from what kernel returns via ioctl(),
|
||||
* see below) */
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
/* If we know ahead of time that pidfs is unavailable, shortcut things. But otherwise we don't
|
||||
* call pidfd_check_pidfs() here, which is kinda extraneous and our own cache is required
|
||||
* anyways (pidfs is introduced in kernel 6.9 while ioctl support there is added in 6.11). */
|
||||
if (have_pidfs == 0 || !cached_supported)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
int nsfd = ioctl(fd, ns_type_cmd);
|
||||
if (nsfd < 0) {
|
||||
/* Kernel returns EOPNOTSUPP if the ns type in question is disabled. Hence we need to look
|
||||
* at precise errno instead of generic ERRNO_IS_(IOCTL_)NOT_SUPPORTED. */
|
||||
if (IN_SET(errno, ENOTTY, EINVAL)) {
|
||||
cached_supported = false;
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
if (errno == EOPNOTSUPP) /* Translate to something more recognizable */
|
||||
return -ENOPKG;
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return nsfd;
|
||||
}
|
||||
|
||||
static int pidfd_get_info(int fd, struct pidfd_info *info) {
|
||||
static bool cached_supported = true;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(info);
|
||||
|
||||
if (have_pidfs == 0 || !cached_supported)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (ioctl(fd, PIDFD_GET_INFO, info) < 0) {
|
||||
if (ERRNO_IS_IOCTL_NOT_SUPPORTED(errno)) {
|
||||
cached_supported = false;
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pidfd_get_pid_fdinfo(int fd, pid_t *ret) {
|
||||
char path[STRLEN("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
|
||||
_cleanup_free_ char *fdinfo = NULL;
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
xsprintf(path, "/proc/self/fdinfo/%i", fd);
|
||||
|
||||
r = read_full_virtual_file(path, &fdinfo, NULL);
|
||||
if (r == -ENOENT)
|
||||
return proc_fd_enoent_errno();
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
char *p = find_line_startswith(fdinfo, "Pid:");
|
||||
if (!p)
|
||||
return -ENOTTY; /* not a pidfd? */
|
||||
|
||||
p = skip_leading_chars(p, /* bad = */ NULL);
|
||||
p[strcspn(p, WHITESPACE)] = 0;
|
||||
|
||||
if (streq(p, "0"))
|
||||
return -EREMOTE; /* PID is in foreign PID namespace? */
|
||||
if (streq(p, "-1"))
|
||||
return -ESRCH; /* refers to reaped process? */
|
||||
|
||||
return parse_pid(p, ret);
|
||||
}
|
||||
|
||||
static int pidfd_get_pid_ioctl(int fd, pid_t *ret) {
|
||||
struct pidfd_info info = { .mask = PIDFD_INFO_PID };
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
r = pidfd_get_info(fd, &info);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
assert(FLAGS_SET(info.mask, PIDFD_INFO_PID));
|
||||
|
||||
if (ret)
|
||||
*ret = info.pid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pidfd_get_pid(int fd, pid_t *ret) {
|
||||
int r;
|
||||
|
||||
/* Converts a pidfd into a pid. We try ioctl(PIDFD_GET_INFO) (kernel 6.13+) first,
|
||||
* /proc/self/fdinfo/ as fallback. Well known errors:
|
||||
*
|
||||
* -EBADF → fd invalid
|
||||
* -ESRCH → fd valid, but process is already reaped
|
||||
*
|
||||
* pidfd_get_pid_fdinfo() might additionally fail for other reasons:
|
||||
*
|
||||
* -ENOSYS → /proc/ not mounted
|
||||
* -ENOTTY → fd valid, but not a pidfd
|
||||
* -EREMOTE → fd valid, but pid is in another namespace we cannot translate to the local one
|
||||
* (when using PIDFD_GET_INFO this is indistinguishable from -ESRCH)
|
||||
*/
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
r = pidfd_get_pid_ioctl(fd, ret);
|
||||
if (r != -EOPNOTSUPP)
|
||||
return r;
|
||||
|
||||
return pidfd_get_pid_fdinfo(fd, ret);
|
||||
}
|
||||
|
||||
int pidfd_verify_pid(int pidfd, pid_t pid) {
|
||||
pid_t current_pid;
|
||||
int r;
|
||||
|
||||
assert(pidfd >= 0);
|
||||
assert(pid > 0);
|
||||
|
||||
r = pidfd_get_pid(pidfd, ¤t_pid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return current_pid != pid ? -ESRCH : 0;
|
||||
}
|
||||
|
||||
int pidfd_get_ppid(int fd, pid_t *ret) {
|
||||
struct pidfd_info info = { .mask = PIDFD_INFO_PID };
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
r = pidfd_get_info(fd, &info);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
assert(FLAGS_SET(info.mask, PIDFD_INFO_PID));
|
||||
|
||||
if (info.ppid == 0) /* See comments in pid_get_ppid() */
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
if (ret)
|
||||
*ret = info.ppid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pidfd_get_uid(int fd, uid_t *ret) {
|
||||
struct pidfd_info info = { .mask = PIDFD_INFO_CREDS };
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
r = pidfd_get_info(fd, &info);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
assert(FLAGS_SET(info.mask, PIDFD_INFO_CREDS));
|
||||
|
||||
if (ret)
|
||||
*ret = info.ruid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pidfd_get_cgroupid(int fd, uint64_t *ret) {
|
||||
struct pidfd_info info = { .mask = PIDFD_INFO_CGROUPID };
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
r = pidfd_get_info(fd, &info);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
assert(FLAGS_SET(info.mask, PIDFD_INFO_CGROUPID));
|
||||
|
||||
if (ret)
|
||||
*ret = info.cgroupid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pidfd_get_inode_id(int fd, uint64_t *ret) {
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
r = pidfd_check_pidfs();
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) < 0)
|
||||
return -errno;
|
||||
|
||||
if (ret)
|
||||
*ret = (uint64_t) st.st_ino;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "missing_pidfd.h"
|
||||
#include "missing_syscall.h"
|
||||
|
||||
int pidfd_get_namespace(int fd, unsigned long ns_type_cmd);
|
||||
|
||||
int pidfd_get_pid(int fd, pid_t *ret);
|
||||
int pidfd_verify_pid(int pidfd, pid_t pid);
|
||||
|
||||
int pidfd_get_ppid(int fd, pid_t *ret);
|
||||
int pidfd_get_uid(int fd, uid_t *ret);
|
||||
int pidfd_get_cgroupid(int fd, uint64_t *ret);
|
||||
|
||||
int pidfd_get_inode_id(int fd, uint64_t *ret);
|
||||
+7
-34
@@ -1,36 +1,14 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#if HAVE_PIDFD_OPEN
|
||||
#include <sys/pidfd.h>
|
||||
#endif
|
||||
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "missing_magic.h"
|
||||
#include "missing_syscall.h"
|
||||
#include "missing_wait.h"
|
||||
#include "parse-util.h"
|
||||
#include "pidfd-util.h"
|
||||
#include "pidref.h"
|
||||
#include "process-util.h"
|
||||
#include "signal-util.h"
|
||||
#include "stat-util.h"
|
||||
|
||||
static int pidfd_inode_ids_supported(void) {
|
||||
static int cached = -1;
|
||||
|
||||
if (cached >= 0)
|
||||
return cached;
|
||||
|
||||
_cleanup_close_ int fd = pidfd_open(getpid_cached(), 0);
|
||||
if (fd < 0) {
|
||||
if (ERRNO_IS_NOT_SUPPORTED(errno))
|
||||
return (cached = false);
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return (cached = fd_is_fs_type(fd, PID_FS_MAGIC));
|
||||
}
|
||||
|
||||
int pidref_acquire_pidfd_id(PidRef *pidref) {
|
||||
int r;
|
||||
@@ -49,19 +27,14 @@ int pidref_acquire_pidfd_id(PidRef *pidref) {
|
||||
if (pidref->fd_id > 0)
|
||||
return 0;
|
||||
|
||||
r = pidfd_inode_ids_supported();
|
||||
if (r < 0)
|
||||
r = pidfd_get_inode_id(pidref->fd, &pidref->fd_id);
|
||||
if (r < 0) {
|
||||
if (!ERRNO_IS_NEG_NOT_SUPPORTED(r))
|
||||
log_debug_errno(r, "Failed to get inode number of pidfd for pid " PID_FMT ": %m",
|
||||
pidref->pid);
|
||||
return r;
|
||||
if (r == 0)
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
|
||||
if (fstat(pidref->fd, &st) < 0)
|
||||
return log_debug_errno(errno, "Failed to get inode number of pidfd for pid " PID_FMT ": %m",
|
||||
pidref->pid);
|
||||
|
||||
pidref->fd_id = (uint64_t) st.st_ino;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+58
-119
@@ -48,6 +48,7 @@
|
||||
#include "nulstr-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "pidfd-util.h"
|
||||
#include "process-util.h"
|
||||
#include "raw-clone.h"
|
||||
#include "rlimit-util.h"
|
||||
@@ -395,33 +396,6 @@ int container_get_leader(const char *machine, pid_t *pid) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int namespace_get_leader(pid_t pid, NamespaceType type, pid_t *ret) {
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
for (;;) {
|
||||
pid_t ppid;
|
||||
|
||||
r = get_process_ppid(pid, &ppid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = in_same_namespace(pid, ppid, type);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) {
|
||||
/* If the parent and the child are not in the same
|
||||
* namespace, then the child is the leader we are
|
||||
* looking for. */
|
||||
*ret = pid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
pid = ppid;
|
||||
}
|
||||
}
|
||||
|
||||
int pid_is_kernel_thread(pid_t pid) {
|
||||
_cleanup_free_ char *line = NULL;
|
||||
unsigned long long flags;
|
||||
@@ -584,7 +558,6 @@ int pid_get_uid(pid_t pid, uid_t *ret) {
|
||||
}
|
||||
|
||||
int pidref_get_uid(const PidRef *pid, uid_t *ret) {
|
||||
uid_t uid;
|
||||
int r;
|
||||
|
||||
if (!pidref_is_set(pid))
|
||||
@@ -593,6 +566,13 @@ int pidref_get_uid(const PidRef *pid, uid_t *ret) {
|
||||
if (pidref_is_remote(pid))
|
||||
return -EREMOTE;
|
||||
|
||||
if (pid->fd >= 0) {
|
||||
r = pidfd_get_uid(pid->fd, ret);
|
||||
if (!ERRNO_IS_NEG_NOT_SUPPORTED(r))
|
||||
return r;
|
||||
}
|
||||
|
||||
uid_t uid;
|
||||
r = pid_get_uid(pid->pid, &uid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@@ -678,7 +658,7 @@ int get_process_environ(pid_t pid, char **ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_process_ppid(pid_t pid, pid_t *ret) {
|
||||
int pid_get_ppid(pid_t pid, pid_t *ret) {
|
||||
_cleanup_free_ char *line = NULL;
|
||||
unsigned long ppid;
|
||||
const char *p;
|
||||
@@ -733,6 +713,35 @@ int get_process_ppid(pid_t pid, pid_t *ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pidref_get_ppid(const PidRef *pidref, pid_t *ret) {
|
||||
int r;
|
||||
|
||||
if (!pidref_is_set(pidref))
|
||||
return -ESRCH;
|
||||
|
||||
if (pidref_is_remote(pidref))
|
||||
return -EREMOTE;
|
||||
|
||||
if (pidref->fd >= 0) {
|
||||
r = pidfd_get_ppid(pidref->fd, ret);
|
||||
if (!ERRNO_IS_NEG_NOT_SUPPORTED(r))
|
||||
return r;
|
||||
}
|
||||
|
||||
pid_t ppid;
|
||||
r = pid_get_ppid(pidref->pid, ret ? &ppid : NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = pidref_verify(pidref);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ret)
|
||||
*ret = ppid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pid_get_start_time(pid_t pid, usec_t *ret) {
|
||||
_cleanup_free_ char *line = NULL;
|
||||
const char *p;
|
||||
@@ -1073,25 +1082,8 @@ int getenv_for_pid(pid_t pid, const char *field, char **ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pid_is_my_child(pid_t pid) {
|
||||
pid_t ppid;
|
||||
int r;
|
||||
|
||||
if (pid < 0)
|
||||
return -ESRCH;
|
||||
|
||||
if (pid <= 1)
|
||||
return false;
|
||||
|
||||
r = get_process_ppid(pid, &ppid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return ppid == getpid_cached();
|
||||
}
|
||||
|
||||
int pidref_is_my_child(const PidRef *pid) {
|
||||
int r, result;
|
||||
int r;
|
||||
|
||||
if (!pidref_is_set(pid))
|
||||
return -ESRCH;
|
||||
@@ -1099,38 +1091,30 @@ int pidref_is_my_child(const PidRef *pid) {
|
||||
if (pidref_is_remote(pid))
|
||||
return -EREMOTE;
|
||||
|
||||
result = pid_is_my_child(pid->pid);
|
||||
if (result < 0)
|
||||
return result;
|
||||
if (pid->pid == 1 || pidref_is_self(pid))
|
||||
return false;
|
||||
|
||||
r = pidref_verify(pid);
|
||||
pid_t ppid;
|
||||
r = pidref_get_ppid(pid, &ppid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return result;
|
||||
return ppid == getpid_cached();
|
||||
}
|
||||
|
||||
int pid_is_unwaited(pid_t pid) {
|
||||
/* Checks whether a PID is still valid at all, including a zombie */
|
||||
int pid_is_my_child(pid_t pid) {
|
||||
|
||||
if (pid < 0)
|
||||
return -ESRCH;
|
||||
if (pid == 0)
|
||||
return false;
|
||||
|
||||
if (pid <= 1) /* If we or PID 1 would be dead and have been waited for, this code would not be running */
|
||||
return true;
|
||||
|
||||
if (pid == getpid_cached())
|
||||
return true;
|
||||
|
||||
if (kill(pid, 0) >= 0)
|
||||
return true;
|
||||
|
||||
return errno != ESRCH;
|
||||
return pidref_is_my_child(&PIDREF_MAKE_FROM_PID(pid));
|
||||
}
|
||||
|
||||
int pidref_is_unwaited(const PidRef *pid) {
|
||||
int r;
|
||||
|
||||
/* Checks whether a PID is still valid at all, including a zombie */
|
||||
|
||||
if (!pidref_is_set(pid))
|
||||
return -ESRCH;
|
||||
|
||||
@@ -1149,6 +1133,14 @@ int pidref_is_unwaited(const PidRef *pid) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int pid_is_unwaited(pid_t pid) {
|
||||
|
||||
if (pid == 0)
|
||||
return true;
|
||||
|
||||
return pidref_is_unwaited(&PIDREF_MAKE_FROM_PID(pid));
|
||||
}
|
||||
|
||||
int pid_is_alive(pid_t pid) {
|
||||
int r;
|
||||
|
||||
@@ -1859,59 +1851,6 @@ int get_oom_score_adjust(int *ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pidfd_get_pid(int fd, pid_t *ret) {
|
||||
char path[STRLEN("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
|
||||
_cleanup_free_ char *fdinfo = NULL;
|
||||
int r;
|
||||
|
||||
/* Converts a pidfd into a pid. Well known errors:
|
||||
*
|
||||
* -EBADF → fd invalid
|
||||
* -ENOSYS → /proc/ not mounted
|
||||
* -ENOTTY → fd valid, but not a pidfd
|
||||
* -EREMOTE → fd valid, but pid is in another namespace we cannot translate to the local one
|
||||
* -ESRCH → fd valid, but process is already reaped
|
||||
*/
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
xsprintf(path, "/proc/self/fdinfo/%i", fd);
|
||||
|
||||
r = read_full_virtual_file(path, &fdinfo, NULL);
|
||||
if (r == -ENOENT)
|
||||
return proc_fd_enoent_errno();
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
char *p = find_line_startswith(fdinfo, "Pid:");
|
||||
if (!p)
|
||||
return -ENOTTY; /* not a pidfd? */
|
||||
|
||||
p = skip_leading_chars(p, /* bad = */ NULL);
|
||||
p[strcspn(p, WHITESPACE)] = 0;
|
||||
|
||||
if (streq(p, "0"))
|
||||
return -EREMOTE; /* PID is in foreign PID namespace? */
|
||||
if (streq(p, "-1"))
|
||||
return -ESRCH; /* refers to reaped process? */
|
||||
|
||||
return parse_pid(p, ret);
|
||||
}
|
||||
|
||||
int pidfd_verify_pid(int pidfd, pid_t pid) {
|
||||
pid_t current_pid;
|
||||
int r;
|
||||
|
||||
assert(pidfd >= 0);
|
||||
assert(pid > 0);
|
||||
|
||||
r = pidfd_get_pid(pidfd, ¤t_pid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return current_pid != pid ? -ESRCH : 0;
|
||||
}
|
||||
|
||||
static int rlimit_to_nice(rlim_t limit) {
|
||||
if (limit <= 1)
|
||||
return PRIO_MAX-1; /* i.e. 19 */
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include "alloc-util.h"
|
||||
#include "format-util.h"
|
||||
#include "macro.h"
|
||||
#include "namespace-util.h"
|
||||
#include "pidref.h"
|
||||
#include "time-util.h"
|
||||
|
||||
@@ -53,15 +52,14 @@ int get_process_gid(pid_t pid, gid_t *ret);
|
||||
int get_process_cwd(pid_t pid, char **ret);
|
||||
int get_process_root(pid_t pid, char **ret);
|
||||
int get_process_environ(pid_t pid, char **ret);
|
||||
int get_process_ppid(pid_t pid, pid_t *ret);
|
||||
int pid_get_ppid(pid_t pid, pid_t *ret);
|
||||
int pidref_get_ppid(const PidRef *pidref, pid_t *ret);
|
||||
int pid_get_start_time(pid_t pid, usec_t *ret);
|
||||
int pidref_get_start_time(const PidRef* pid, usec_t *ret);
|
||||
int pidref_get_start_time(const PidRef *pid, usec_t *ret);
|
||||
int get_process_umask(pid_t pid, mode_t *ret);
|
||||
|
||||
int container_get_leader(const char *machine, pid_t *pid);
|
||||
|
||||
int namespace_get_leader(pid_t pid, NamespaceType type, pid_t *ret);
|
||||
|
||||
int wait_for_terminate(pid_t pid, siginfo_t *status);
|
||||
|
||||
typedef enum WaitFlags {
|
||||
@@ -250,9 +248,6 @@ assert_cc(TASKS_MAX <= (unsigned long) PID_T_MAX);
|
||||
/* Like TAKE_PTR() but for pid_t, resetting them to 0 */
|
||||
#define TAKE_PID(pid) TAKE_GENERIC(pid, pid_t, 0)
|
||||
|
||||
int pidfd_get_pid(int fd, pid_t *ret);
|
||||
int pidfd_verify_pid(int pidfd, pid_t pid);
|
||||
|
||||
int setpriority_closest(int priority);
|
||||
|
||||
_noreturn_ void freeze(void);
|
||||
|
||||
@@ -2334,7 +2334,7 @@ int pty_open_peer_racefree(int fd, int mode) {
|
||||
if (peer_fd >= 0)
|
||||
return peer_fd;
|
||||
|
||||
if (ERRNO_IS_NOT_SUPPORTED(errno) || errno == EINVAL) /* new ioctl() is not supported, return a clear error */
|
||||
if (ERRNO_IS_IOCTL_NOT_SUPPORTED(errno)) /* new ioctl() is not supported, return a clear error */
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (errno != EIO)
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "fileio.h"
|
||||
#include "macro.h"
|
||||
#include "missing_threads.h"
|
||||
#include "namespace-util.h"
|
||||
#include "process-util.h"
|
||||
#include "stat-util.h"
|
||||
#include "string-table.h"
|
||||
|
||||
Reference in New Issue
Block a user