mirror of https://gitee.com/Nocallback/glibc.git
Browse Source
This interface allows to obtain the associated process ID from the
process file descriptor. It is done by parsing the procps fdinfo
information. Its prototype is:
pid_t pidfd_getpid (int fd)
It returns the associated pid or -1 in case of an error and sets the
errno accordingly. The possible errno values are those from open, read,
and close (used on procps parsing), along with:
- EBADF if the FD is negative, does not have a PID associated, or if
the fdinfo fields contain a value larger than pid_t.
- EREMOTE if the PID is in a separate namespace.
- ESRCH if the process is already terminated.
Checked on x86_64-linux-gnu on Linux 4.15 (no CLONE_PIDFD or waitid
support), Linux 5.4 (full support), and Linux 6.2.
Reviewed-by: Florian Weimer <fweimer@redhat.com>
zack/remove-libcrypt
committed by
Adhemerval Zanella
44 changed files with 520 additions and 0 deletions
@ -0,0 +1,126 @@ |
|||
/* pidfd_getpid - Get the associated pid from the pid file descriptor.
|
|||
Copyright (C) 2023 Free Software Foundation, Inc. |
|||
This file is part of the GNU C Library. |
|||
|
|||
The GNU C Library is free software; you can redistribute it and/or |
|||
modify it under the terms of the GNU Lesser General Public |
|||
License as published by the Free Software Foundation; either |
|||
version 2.1 of the License, or (at your option) any later version. |
|||
|
|||
The GNU C Library 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 |
|||
Lesser General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU Lesser General Public |
|||
License along with the GNU C Library; if not, see |
|||
<https://www.gnu.org/licenses/>. */
|
|||
|
|||
#include <_itoa.h> |
|||
#include <errno.h> |
|||
#include <intprops.h> |
|||
#include <procutils.h> |
|||
#include <stdlib.h> |
|||
#include <string.h> |
|||
#include <sysdep.h> |
|||
#include <unistd.h> |
|||
|
|||
#define FDINFO_TO_FILENAME_PREFIX "/proc/self/fdinfo/" |
|||
|
|||
#define FDINFO_FILENAME_LEN \ |
|||
(sizeof (FDINFO_TO_FILENAME_PREFIX) + INT_STRLEN_BOUND (int)) |
|||
|
|||
struct parse_fdinfo_t |
|||
{ |
|||
bool found; |
|||
pid_t pid; |
|||
}; |
|||
|
|||
/* Parse the PID field in the fdinfo entry, if existent. Avoid strtol or
|
|||
similar to not be locale dependent. */ |
|||
static int |
|||
parse_fdinfo (const char *l, void *arg) |
|||
{ |
|||
enum { fieldlen = sizeof ("Pid:") - 1 }; |
|||
if (strncmp (l, "Pid:", fieldlen) != 0) |
|||
return 0; |
|||
|
|||
l += fieldlen; |
|||
|
|||
/* Skip leading spaces. */ |
|||
while (*l == ' ' || (unsigned int) (*l) -'\t' < 5) |
|||
l++; |
|||
|
|||
bool neg = false; |
|||
switch (*l) |
|||
{ |
|||
case '-': |
|||
neg = true; |
|||
l++; |
|||
break; |
|||
case '+': |
|||
return -1; |
|||
} |
|||
|
|||
if (*l == '\0') |
|||
return 0; |
|||
|
|||
int n = 0; |
|||
while (*l != '\0') |
|||
{ |
|||
/* Check if '*l' is a digit. */ |
|||
if ('0' > *l || *l > '9') |
|||
return -1; |
|||
|
|||
/* Ignore invalid large values. */ |
|||
if (INT_MULTIPLY_WRAPV (10, n, &n) |
|||
|| INT_ADD_WRAPV (n, *l++ - '0', &n)) |
|||
return -1; |
|||
} |
|||
|
|||
/* -1 indicates that the process is terminated. */ |
|||
if (neg && n != 1) |
|||
return -1; |
|||
|
|||
struct parse_fdinfo_t *fdinfo = arg; |
|||
fdinfo->pid = neg ? -n : n; |
|||
fdinfo->found = true; |
|||
|
|||
return 1; |
|||
} |
|||
|
|||
pid_t |
|||
pidfd_getpid (int fd) |
|||
{ |
|||
if (__glibc_unlikely (fd < 0)) |
|||
{ |
|||
__set_errno (EBADF); |
|||
return -1; |
|||
} |
|||
|
|||
char fdinfoname[FDINFO_FILENAME_LEN]; |
|||
|
|||
char *p = mempcpy (fdinfoname, FDINFO_TO_FILENAME_PREFIX, |
|||
strlen (FDINFO_TO_FILENAME_PREFIX)); |
|||
*_fitoa_word (fd, p, 10, 0) = '\0'; |
|||
|
|||
struct parse_fdinfo_t fdinfo = { .found = false, .pid = -1 }; |
|||
if (!procutils_read_file (fdinfoname, parse_fdinfo, &fdinfo)) |
|||
/* The fdinfo contains an invalid 'Pid:' value. */ |
|||
return INLINE_SYSCALL_ERROR_RETURN_VALUE (EBADF); |
|||
|
|||
/* The FD does not have a 'Pid:' entry associated. */ |
|||
if (!fdinfo.found) |
|||
return INLINE_SYSCALL_ERROR_RETURN_VALUE (EBADF); |
|||
|
|||
/* The pidfd cannot be resolved because it is in a separate pid
|
|||
namespace. */ |
|||
if (fdinfo.pid == 0) |
|||
return INLINE_SYSCALL_ERROR_RETURN_VALUE (EREMOTE); |
|||
|
|||
/* A negative value means the process is terminated. */ |
|||
if (fdinfo.pid < 0) |
|||
return INLINE_SYSCALL_ERROR_RETURN_VALUE (ESRCH); |
|||
|
|||
return fdinfo.pid; |
|||
} |
|||
@ -0,0 +1,97 @@ |
|||
/* Utilities functions to read/parse Linux procfs and sysfs.
|
|||
Copyright (C) 2023 Free Software Foundation, Inc. |
|||
This file is part of the GNU C Library. |
|||
|
|||
The GNU C Library is free software; you can redistribute it and/or |
|||
modify it under the terms of the GNU Lesser General Public |
|||
License as published by the Free Software Foundation; either |
|||
version 2.1 of the License, or (at your option) any later version. |
|||
|
|||
The GNU C Library 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 |
|||
Lesser General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU Lesser General Public |
|||
License along with the GNU C Library; if not, see |
|||
<https://www.gnu.org/licenses/>. */
|
|||
|
|||
#include <assert.h> |
|||
#include <not-cancel.h> |
|||
#include <procutils.h> |
|||
#include <string.h> |
|||
|
|||
static int |
|||
next_line (char **r, int fd, char *const buffer, char **cp, char **re, |
|||
char *const buffer_end) |
|||
{ |
|||
char *res = *cp; |
|||
char *nl = memchr (*cp, '\n', *re - *cp); |
|||
if (nl == NULL) |
|||
{ |
|||
if (*cp != buffer) |
|||
{ |
|||
if (*re == buffer_end) |
|||
{ |
|||
memmove (buffer, *cp, *re - *cp); |
|||
*re = buffer + (*re - *cp); |
|||
*cp = buffer; |
|||
|
|||
ssize_t n = TEMP_FAILURE_RETRY ( |
|||
__read_nocancel (fd, *re, buffer_end - *re)); |
|||
if (n < 0) |
|||
return -1; |
|||
|
|||
*re += n; |
|||
|
|||
nl = memchr (*cp, '\n', *re - *cp); |
|||
if (nl == NULL) |
|||
/* Line too long. */ |
|||
return 0; |
|||
} |
|||
else |
|||
nl = memchr (*cp, '\n', *re - *cp); |
|||
|
|||
res = *cp; |
|||
} |
|||
|
|||
if (nl == NULL) |
|||
nl = *re - 1; |
|||
} |
|||
|
|||
*nl = '\0'; |
|||
*cp = nl + 1; |
|||
assert (*cp <= *re); |
|||
|
|||
if (res == *re) |
|||
return 0; |
|||
|
|||
*r = res; |
|||
return 1; |
|||
} |
|||
|
|||
bool |
|||
procutils_read_file (const char *filename, procutils_closure_t closure, |
|||
void *arg) |
|||
{ |
|||
enum { buffer_size = PROCUTILS_MAX_LINE_LEN }; |
|||
char buffer[buffer_size]; |
|||
char *buffer_end = buffer + buffer_size; |
|||
char *cp = buffer_end; |
|||
char *re = buffer_end; |
|||
|
|||
int fd = TEMP_FAILURE_RETRY ( |
|||
__open64_nocancel (filename, O_RDONLY | O_CLOEXEC)); |
|||
if (fd == -1) |
|||
return false; |
|||
|
|||
char *l; |
|||
int r; |
|||
while ((r = next_line (&l, fd, buffer, &cp, &re, buffer_end)) > 0) |
|||
if (closure (l, arg) != 0) |
|||
break; |
|||
|
|||
__close_nocancel_nostatus (fd); |
|||
|
|||
return r == 1; |
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
/* Utilities functions to read/parse Linux procfs and sysfs.
|
|||
Copyright (C) 2023 Free Software Foundation, Inc. |
|||
This file is part of the GNU C Library. |
|||
|
|||
The GNU C Library is free software; you can redistribute it and/or |
|||
modify it under the terms of the GNU Lesser General Public |
|||
License as published by the Free Software Foundation; either |
|||
version 2.1 of the License, or (at your option) any later version. |
|||
|
|||
The GNU C Library 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 |
|||
Lesser General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU Lesser General Public |
|||
License along with the GNU C Library; if not, see |
|||
<https://www.gnu.org/licenses/>. */
|
|||
|
|||
#ifndef _PROCUTILS_H |
|||
#define _PROCUTILS_H |
|||
|
|||
#include <stdbool.h> |
|||
|
|||
typedef int (*procutils_closure_t) (const char *line, void *arg); |
|||
|
|||
#define PROCUTILS_MAX_LINE_LEN 256 |
|||
|
|||
/* Open and read the path FILENAME, line per line, and call CLOSURE with
|
|||
argument ARG on each line. The read is done with a static buffer, |
|||
with non-cancellable calls, and the line is null terminated. |
|||
|
|||
The CLOSURE should return 0 if the read should continue, otherwise the |
|||
the function should stop and return early. |
|||
|
|||
The '\n' is not included in the CLOSURE input argument and lines longer |
|||
than PROCUTILS_MAX_LINE_LEN characteres are ignored. |
|||
|
|||
It returns true in case the file is fully read or false if CLOSURE |
|||
returns a value diferent than 0. */ |
|||
bool procutils_read_file (const char *filename, procutils_closure_t closure, |
|||
void *arg) attribute_hidden; |
|||
|
|||
#endif |
|||
@ -0,0 +1,123 @@ |
|||
/* Specific tests for Linux pidfd_getpid.
|
|||
Copyright (C) 2023 Free Software Foundation, Inc. |
|||
This file is part of the GNU C Library. |
|||
|
|||
The GNU C Library is free software; you can redistribute it and/or |
|||
modify it under the terms of the GNU Lesser General Public |
|||
License as published by the Free Software Foundation; either |
|||
version 2.1 of the License, or (at your option) any later version. |
|||
|
|||
The GNU C Library 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 |
|||
Lesser General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU Lesser General Public |
|||
License along with the GNU C Library; if not, see |
|||
<https://www.gnu.org/licenses/>. */
|
|||
|
|||
#include <errno.h> |
|||
#include <sched.h> |
|||
#include <stdlib.h> |
|||
#include <support/check.h> |
|||
#include <support/xunistd.h> |
|||
#include <support/test-driver.h> |
|||
#include <sys/pidfd.h> |
|||
#include <sys/wait.h> |
|||
#include <sys/mount.h> |
|||
|
|||
static int |
|||
do_test (void) |
|||
{ |
|||
{ |
|||
/* The pidfd_getfd syscall was the last in the set of pidfd related
|
|||
syscalls added to the kernel. Use pidfd_getfd to decide if this |
|||
kernel has pidfd support that we can test. */ |
|||
int r = pidfd_getfd (0, 0, 1); |
|||
TEST_VERIFY_EXIT (r == -1); |
|||
if (errno == ENOSYS) |
|||
FAIL_UNSUPPORTED ("kernel does not support pidfd_getfd, skipping test"); |
|||
if (errno == EPERM) |
|||
FAIL_UNSUPPORTED ("kernel does not allow pidfd_getfd, skipping test"); |
|||
} |
|||
|
|||
/* Check if pidfd_getpid returns EREMOTE for process not in current
|
|||
namespace. */ |
|||
{ |
|||
pid_t child0 = xfork (); |
|||
TEST_VERIFY_EXIT (child0 >= 0); |
|||
if (child0 == 0) |
|||
{ |
|||
/* Create another unrelated descriptor, so child2 will inherit the
|
|||
file descriptor. */ |
|||
pid_t child1 = xfork (); |
|||
TEST_VERIFY_EXIT (child1 >= 0); |
|||
if (child1 == 0) |
|||
_exit (0); |
|||
int child1_pidfd = pidfd_open (child1, 0); |
|||
TEST_VERIFY_EXIT (child1_pidfd != -1); |
|||
|
|||
if (unshare (CLONE_NEWNS | CLONE_NEWUSER | CLONE_NEWPID) < 0) |
|||
{ |
|||
/* Older kernels may not support all the options, or security
|
|||
policy may block this call. */ |
|||
if (errno == EINVAL || errno == EPERM || errno == ENOSPC) |
|||
exit (EXIT_UNSUPPORTED); |
|||
FAIL_EXIT1 ("unshare user/fs/pid failed: %m"); |
|||
} |
|||
|
|||
if (mount (NULL, "/", NULL, MS_REC | MS_PRIVATE, 0) != 0) |
|||
{ |
|||
/* This happens if we're trying to create a nested container,
|
|||
like if the build is running under podman, and we lack |
|||
priviledges. */ |
|||
if (errno == EPERM) |
|||
_exit (EXIT_UNSUPPORTED); |
|||
else |
|||
_exit (EXIT_FAILURE); |
|||
} |
|||
|
|||
pid_t child2 = xfork (); |
|||
if (child2 > 0) |
|||
{ |
|||
int status; |
|||
xwaitpid (child2, &status, 0); |
|||
TEST_VERIFY (WIFEXITED (status)); |
|||
xwaitpid (child1, &status, 0); |
|||
TEST_VERIFY (WIFEXITED (status)); |
|||
|
|||
_exit (WEXITSTATUS (status)); |
|||
} |
|||
|
|||
/* Now that we're pid 1 (effectively "root") we can mount /proc */ |
|||
if (mount ("proc", "/proc", "proc", 0, NULL) != 0) |
|||
{ |
|||
if (errno == EPERM) |
|||
_exit (EXIT_UNSUPPORTED); |
|||
else |
|||
_exit (EXIT_FAILURE); |
|||
} |
|||
|
|||
TEST_COMPARE (pidfd_getpid (child1_pidfd), -1); |
|||
TEST_COMPARE (errno, EREMOTE); |
|||
|
|||
_exit (EXIT_SUCCESS); |
|||
} |
|||
int child0_pidfd = pidfd_open (child0, 0); |
|||
TEST_VERIFY_EXIT (child0_pidfd != -1); |
|||
|
|||
pid_t child0pid = pidfd_getpid (child0_pidfd); |
|||
|
|||
siginfo_t info; |
|||
TEST_COMPARE (waitid (P_PIDFD, child0_pidfd, &info, WEXITED), 0); |
|||
if (info.si_status == EXIT_UNSUPPORTED) |
|||
FAIL_UNSUPPORTED ("unable to unshare user/fs/pid"); |
|||
TEST_COMPARE (info.si_status, 0); |
|||
TEST_COMPARE (info.si_code, CLD_EXITED); |
|||
TEST_COMPARE (info.si_pid, child0pid); |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
#include <support/test-driver.c> |
|||
Loading…
Reference in new issue