* Adhemerval Zanella via Libc-alpha:
> +If the operation fails, @code{pidfd_getpid} return @code{-1} and the following
> +@code{errno} error conditionas are defined:
> +
> +@table @code
> +@item EBADF
> +The input file descriptor is invalid, does not have a pidfd associated, or an
> +error has occurred parsing the kernel data.
> +@item EREMOTE
> +There is no process ID to denote the process in the current namespace.
> +@item ESRCH
> +The process for which the file descriptor refers to is terminated.
> +@end table
Maybe document ENOENT (/proc not mounted), ENFILE, EMFILE, ENOMEM as
well?
There are missing spaces in a few places:
+ while (*l == ' ' || (unsigned int)(*l) -'\t' < 5)
+ if ((unsigned int)(*l) - '0' >= 10)
+typedef int (*procutils_closure_t)(const char *line, void *arg);
+ char buf[CMSG_SPACE(sizeof(int))];
> diff --git a/sysdeps/unix/sysv/linux/pidfd_getpid.c b/sysdeps/unix/sysv/linux/pidfd_getpid.c
> new file mode 100644
> index 0000000000..46848a5983
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/pidfd_getpid.c
> + bool neg = false;
> + switch (*l)
> + {
> + case '-': neg = true;
> + case '+': l++;
'+' should probably return -1?
> + }
> +
> + if (*l == '\0')
> + return 0;
> +
> + int n = 0;
> + while (*l != '\0')
> + {
> + /* Check if '*l' is a digit. */
> + if ((unsigned int)(*l) - '0' >= 10)
It's a strange way to write this condition. '0' <= *l && l <= '9' should
work equally well. I know is supposed to optimize this into one
condition, but it's not immediately obvious why this works with an early
cast of unsigned int instead of unsigned char.
> + /* Ignore invalid large values. */
> + if (INT_MULTIPLY_WRAPV (10, n, &n)
> + || INT_ADD_WRAPV (n, *l++ - '0', &n))
> + return 0;
Shouldn't this return -1?
I think these error returns should set errno (EINVAL perhaps, since
that's unlikely to come from read or open), so that we have a chance to
identify parser problems.
> diff --git a/sysdeps/unix/sysv/linux/procutils.c b/sysdeps/unix/sysv/linux/procutils.c
> new file mode 100644
> index 0000000000..83b327cb9a
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/procutils.c
> @@ -0,0 +1,104 @@
> +/* 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 = __read_nocancel (fd, *re, buffer_end - *re);
Missing TEMP_FAILURE_RETRY, I would (also below, and further below for
__open64_nocancel).
> + if (n < 0)
> + return -1;
> +
> + *re += n;
> +
> + nl = memchr (*cp, '\n', *re - *cp);
> + while (nl == NULL && *re == buffer_end)
> + {
> + /* Truncate too long lines. */
> + *re = buffer + 3 * (buffer_end - buffer) / 4;
> + n = __read_nocancel (fd, *re, buffer_end - *re);
> + if (n < 0)
> + return -1;
> +
> + nl = memchr (*re, '\n', n);
> + **re = '\0';
> + *re += n;
> + }
Should we just skip long lines? The 3/4 business is a bit strange.
This results in an endless loop if the file does not end with '\n', I
think.
> diff --git a/sysdeps/unix/sysv/linux/procutils.h b/sysdeps/unix/sysv/linux/procutils.h
> new file mode 100644
> index 0000000000..64e1080920
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/procutils.h
> @@ -0,0 +1,35 @@
> +typedef int (*procutils_closure_t)(const char *line, void *arg);
> +
> +/* 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 true if the read should continue, or false
> + if the function should stop.
> +
> + It returns 0 in case of success, or -1 otherwise. */
> +int procutils_read_file (const char *filename, procutils_closure_t closure,
> + void *arg) attribute_hidden;
> +
A comment should say whether '\n' is included in line argument, and what
happens to overlong lines.
The return value for the closure should be an actual bool, or otherwise
the int return value should be passed through to the caller of
procutils_read_file.
> diff --git a/sysdeps/unix/sysv/linux/tst-pidfd.c b/sysdeps/unix/sysv/linux/tst-pidfd.c
> index 64d8a2ef40..53d223f702 100644
> --- a/sysdeps/unix/sysv/linux/tst-pidfd.c
> +++ b/sysdeps/unix/sysv/linux/tst-pidfd.c
> @@ -18,6 +18,7 @@
> + /* Check if pidfd_getpid returns ESRCH for exited subprocess. */
> + {
> + int pidfd;
> + pid_t pidfork = pidfd_fork (&pidfd, -1, 0);
> + if (pidfork == 0)
> + _exit (EXIT_SUCCESS);
> +
> + /* The process might be still running or already in zombie state, in any
> + case the PID is still allocated to the process. */
> + pid_t pid = pidfd_getpid (pidfd);
> + if (pid > 0)
> + support_process_state_wait (pid, support_process_state_zombie);
The condition does not match the comment. I don't know which one is
correct. Please verify that pid > 0 (if the PID remains available), or
change the comment to “in [either] case”.
> diff --git a/sysdeps/unix/sysv/linux/tst-pidfd_getpid.c b/sysdeps/unix/sysv/linux/tst-pidfd_getpid.c
> new file mode 100644
> index 0000000000..41d03a04ad
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/tst-pidfd_getpid.c
> + TEST_VERIFY_EXIT (socketpair (AF_UNIX, SOCK_STREAM, 0, sockfd) == 0);
> +
> + /* Check if pidfd_getpid returns EREMOTE for process not in current
> + namespace. */
> + {
> + int pidfd;
> + pid_t pid = pidfd_fork (&pidfd, -1, 0);
I think you can avoid the file descriptor passing if you call pidfd_fork
to create an unrelated descriptor, and then do the namespace thing below
after another fork. This way, the descriptor will just be inherited via
fork.
> + send_fd (sockfd[1], pidfd);
> +
> + siginfo_t info;
> + TEST_COMPARE (waitid (P_PIDFD, 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);
I think this could have a few tests, like the pidfd_getpid value
matching what comes back subsequently in si_pid.
> diff --git a/sysdeps/unix/sysv/linux/sys/pidfd.h b/sysdeps/unix/sysv/linux/sys/pidfd.h
> index 87095212a7..8cf4df6b81 100644
> --- a/sysdeps/unix/sysv/linux/sys/pidfd.h
> +++ b/sysdeps/unix/sysv/linux/sys/pidfd.h
> @@ -67,4 +67,8 @@ extern int pidfd_send_signal (int __pidfd, int __sig, siginfo_t *__info,
> extern pid_t pidfd_fork (int *__pidfd, int __cgroup, unsigned int __flags)
> __THROW;
>
> +/* Query the process ID (PID) from process descriptor __FD. Return the PID
> + or -1 in case of an error. */
> +extern pid_t pidfd_getpid (int __fd) __THROW;
> +
__FD should be FD.
Thanks,
Florian
On 11/08/23 11:36, Florian Weimer wrote:
> * Adhemerval Zanella via Libc-alpha:
>
>> +If the operation fails, @code{pidfd_getpid} return @code{-1} and the following
>> +@code{errno} error conditionas are defined:
>> +
>> +@table @code
>> +@item EBADF
>> +The input file descriptor is invalid, does not have a pidfd associated, or an
>> +error has occurred parsing the kernel data.
>> +@item EREMOTE
>> +There is no process ID to denote the process in the current namespace.
>> +@item ESRCH
>> +The process for which the file descriptor refers to is terminated.
>> +@end table
>
> Maybe document ENOENT (/proc not mounted), ENFILE, EMFILE, ENOMEM as
> well?
Ack.
>
> There are missing spaces in a few places:
>
> + while (*l == ' ' || (unsigned int)(*l) -'\t' < 5)
> + if ((unsigned int)(*l) - '0' >= 10)
> +typedef int (*procutils_closure_t)(const char *line, void *arg);
> + char buf[CMSG_SPACE(sizeof(int))];
Ack.
>
>> diff --git a/sysdeps/unix/sysv/linux/pidfd_getpid.c b/sysdeps/unix/sysv/linux/pidfd_getpid.c
>> new file mode 100644
>> index 0000000000..46848a5983
>> --- /dev/null
>> +++ b/sysdeps/unix/sysv/linux/pidfd_getpid.c
>
>> + bool neg = false;
>> + switch (*l)
>> + {
>> + case '-': neg = true;
>> + case '+': l++;
>
> '+' should probably return -1?
I think it makes sense here indeed.
>
>> + }
>> +
>> + if (*l == '\0')
>> + return 0;
>> +
>> + int n = 0;
>> + while (*l != '\0')
>> + {
>> + /* Check if '*l' is a digit. */
>> + if ((unsigned int)(*l) - '0' >= 10)
>
> It's a strange way to write this condition. '0' <= *l && l <= '9' should
> work equally well. I know is supposed to optimize this into one
> condition, but it's not immediately obvious why this works with an early
> cast of unsigned int instead of unsigned char.
'0' > *l || *l > '9' works for me as well.
>
>> + /* Ignore invalid large values. */
>> + if (INT_MULTIPLY_WRAPV (10, n, &n)
>> + || INT_ADD_WRAPV (n, *l++ - '0', &n))
>> + return 0;
>
> Shouldn't this return -1?
I think there is no point in returning 0 for any parsing failure when a
'Pid:' line is found (since multiple 'Pid:' entries also do not make
sense).
>
> I think these error returns should set errno (EINVAL perhaps, since
> that's unlikely to come from read or open), so that we have a chance to
> identify parser problems.
The procutils_read_file already returns -1 for the case the closure
returns before reading whole file, so pidfd_getpid can return EBADF
in the case of an invalid 'Pid:'. And it also return the same error
for the case 'Pid:' is not existent (procutils_read_file return 0,
but parse_fdinfo_t is { false, ... }).
We can change the later to EINVAL, but I am not sure if really fits
here. An inexistent 'Pid: ' associated with the input file descriptor
argument means that its not a pidfd one; which imho does make to
EBADF.
>
>
>> diff --git a/sysdeps/unix/sysv/linux/procutils.c b/sysdeps/unix/sysv/linux/procutils.c
>> new file mode 100644
>> index 0000000000..83b327cb9a
>> --- /dev/null
>> +++ b/sysdeps/unix/sysv/linux/procutils.c
>> @@ -0,0 +1,104 @@
>> +/* 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 = __read_nocancel (fd, *re, buffer_end - *re);
>
> Missing TEMP_FAILURE_RETRY, I would (also below, and further below for
> __open64_nocancel).
Ack.
>
>> + if (n < 0)
>> + return -1;
>> +
>> + *re += n;
>> +
>> + nl = memchr (*cp, '\n', *re - *cp);
>> + while (nl == NULL && *re == buffer_end)
>> + {
>> + /* Truncate too long lines. */
>> + *re = buffer + 3 * (buffer_end - buffer) / 4;
>> + n = __read_nocancel (fd, *re, buffer_end - *re);
>> + if (n < 0)
>> + return -1;
>> +
>> + nl = memchr (*re, '\n', n);
>> + **re = '\0';
>> + *re += n;
>> + }
>
> Should we just skip long lines? The 3/4 business is a bit strange.
So this is essentially the same code from sysdeps/unix/sysv/linux/getsysstats.c
(which I plan to consolidate later), and I agree with you that 3/4 is not really
clear.
>
> This results in an endless loop if the file does not end with '\n', I
> think.
I think it is meant to be a generic interface to read procfs/sysfs, I
would be better to bail out for long lines. I will change it.
>
>> diff --git a/sysdeps/unix/sysv/linux/procutils.h b/sysdeps/unix/sysv/linux/procutils.h
>> new file mode 100644
>> index 0000000000..64e1080920
>> --- /dev/null
>> +++ b/sysdeps/unix/sysv/linux/procutils.h
>> @@ -0,0 +1,35 @@
>
>> +typedef int (*procutils_closure_t)(const char *line, void *arg);
>> +
>> +/* 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 true if the read should continue, or false
>> + if the function should stop.
>> +
>> + It returns 0 in case of success, or -1 otherwise. */
>> +int procutils_read_file (const char *filename, procutils_closure_t closure,
>> + void *arg) attribute_hidden;
>> +
>
> A comment should say whether '\n' is included in line argument, and what
> happens to overlong lines.
>
> The return value for the closure should be an actual bool, or otherwise
> the int return value should be passed through to the caller of
> procutils_read_file.
Ack, I changed to bool.
>
>> diff --git a/sysdeps/unix/sysv/linux/tst-pidfd.c b/sysdeps/unix/sysv/linux/tst-pidfd.c
>> index 64d8a2ef40..53d223f702 100644
>> --- a/sysdeps/unix/sysv/linux/tst-pidfd.c
>> +++ b/sysdeps/unix/sysv/linux/tst-pidfd.c
>> @@ -18,6 +18,7 @@
>
>> + /* Check if pidfd_getpid returns ESRCH for exited subprocess. */
>> + {
>> + int pidfd;
>> + pid_t pidfork = pidfd_fork (&pidfd, -1, 0);
>> + if (pidfork == 0)
>> + _exit (EXIT_SUCCESS);
>> +
>> + /* The process might be still running or already in zombie state, in any
>> + case the PID is still allocated to the process. */
>> + pid_t pid = pidfd_getpid (pidfd);
>> + if (pid > 0)
>> + support_process_state_wait (pid, support_process_state_zombie);
>
> The condition does not match the comment. I don't know which one is
> correct. Please verify that pid > 0 (if the PID remains available), or
> change the comment to “in [either] case”.
I changed the comment, what this snippet is trying to test if the process
it still executing pid will be positive one and thus we need to wait it
become a zombie. Otherwise we can issue the pidfd_getpid directly.
>
>> diff --git a/sysdeps/unix/sysv/linux/tst-pidfd_getpid.c b/sysdeps/unix/sysv/linux/tst-pidfd_getpid.c
>> new file mode 100644
>> index 0000000000..41d03a04ad
>> --- /dev/null
>> +++ b/sysdeps/unix/sysv/linux/tst-pidfd_getpid.c
>
>> + TEST_VERIFY_EXIT (socketpair (AF_UNIX, SOCK_STREAM, 0, sockfd) == 0);
>> +
>> + /* Check if pidfd_getpid returns EREMOTE for process not in current
>> + namespace. */
>> + {
>> + int pidfd;
>> + pid_t pid = pidfd_fork (&pidfd, -1, 0);
>
> I think you can avoid the file descriptor passing if you call pidfd_fork
> to create an unrelated descriptor, and then do the namespace thing below
> after another fork. This way, the descriptor will just be inherited via
> fork.
Ok, I will check if this simplifies things.
>
>> + send_fd (sockfd[1], pidfd);
>> +
>> + siginfo_t info;
>> + TEST_COMPARE (waitid (P_PIDFD, 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);
>
> I think this could have a few tests, like the pidfd_getpid value
> matching what comes back subsequently in si_pid.
Ack.
>
>> diff --git a/sysdeps/unix/sysv/linux/sys/pidfd.h b/sysdeps/unix/sysv/linux/sys/pidfd.h
>> index 87095212a7..8cf4df6b81 100644
>> --- a/sysdeps/unix/sysv/linux/sys/pidfd.h
>> +++ b/sysdeps/unix/sysv/linux/sys/pidfd.h
>> @@ -67,4 +67,8 @@ extern int pidfd_send_signal (int __pidfd, int __sig, siginfo_t *__info,
>> extern pid_t pidfd_fork (int *__pidfd, int __cgroup, unsigned int __flags)
>> __THROW;
>>
>> +/* Query the process ID (PID) from process descriptor __FD. Return the PID
>> + or -1 in case of an error. */
>> +extern pid_t pidfd_getpid (int __fd) __THROW;
>> +
>
> __FD should be FD.
Ack.
@@ -27,6 +27,10 @@ Major new features:
of return a process ID, it returns a file descriptor that can be used
along other pidfd functions.
+* On Linux, the pidfd_getpid function has been added. It allows to retrieve
+ the process ID associated with process file descriptor created with
+ pid_spawn, pidfd_fork, or pidfd_open.
+
Deprecated and removed features, and other changes affecting compatibility:
[Add deprecations, removals and changes affecting compatibility here]
@@ -33,6 +33,7 @@ primitive functions to do each step individually instead.
* Process Creation Concepts:: An overview of the hard way to do it.
* Process Identification:: How to get the process ID of a process.
* Creating a Process:: How to fork a child process.
+* Querying a Process:: How to query a child process.
* Executing a File:: How to make a process execute another program.
* Process Completion:: How to tell when a child process has completed.
* Process Completion Status:: How to interpret the status value
@@ -401,6 +402,36 @@ Do not send a @code{SIGCHLD} termination signal when child terminates.
This function is specific to Linux.
+@node Querying a Process
+@section Querying a Process
+
+The file descriptor returned by the @code{pidfd_fork} function can be used to
+query process extra information.
+
+@deftypefun pid_t pidfd_getpid (int @var{fd})
+@standards{GNU, sys/pidfd.h}
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
+
+The @code{pidfd_getpid} function retrieves the process ID associated with process
+file descriptor created with @code{pid_spawn}, @code{pidfd_fork}, or
+@code{pidfd_open}.
+
+If the operation fails, @code{pidfd_getpid} return @code{-1} and the following
+@code{errno} error conditionas are defined:
+
+@table @code
+@item EBADF
+The input file descriptor is invalid, does not have a pidfd associated, or an
+error has occurred parsing the kernel data.
+@item EREMOTE
+There is no process ID to denote the process in the current namespace.
+@item ESRCH
+The process for which the file descriptor refers to is terminated.
+@end table
+
+This function is specific to Linux.
+@end deftypefun
+
@node Executing a File
@section Executing a File
@cindex executing a file
@@ -213,6 +213,7 @@ tests += \
tst-ofdlocks \
tst-personality \
tst-pidfd \
+ tst-pidfd_getpid \
tst-pkey \
tst-ppoll \
tst-prctl \
@@ -494,8 +495,10 @@ sysdep_routines += \
getcpu \
oldglob \
pidfd_fork \
+ pidfd_getpid \
pidfd_spawn \
pidfd_spawnp \
+ procutils \
sched_getcpu \
spawnattr_getcgroup_np \
spawnattr_setcgroup_np \
@@ -325,6 +325,7 @@ libc {
posix_spawnattr_getcgroup_np;
posix_spawnattr_setcgroup_np;
pidfd_fork;
+ pidfd_getpid;
pidfd_spawn;
pidfd_spawnp;
}
@@ -2674,6 +2674,7 @@ GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2783,6 +2783,7 @@ GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2435,6 +2435,7 @@ GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -555,6 +555,7 @@ GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -552,6 +552,7 @@ GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2711,6 +2711,7 @@ GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2660,6 +2660,7 @@ GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2844,6 +2844,7 @@ GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2609,6 +2609,7 @@ GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2195,6 +2195,7 @@ GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -556,6 +556,7 @@ GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2787,6 +2787,7 @@ GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2760,6 +2760,7 @@ GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2757,6 +2757,7 @@ GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2752,6 +2752,7 @@ GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2750,6 +2750,7 @@ GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2758,6 +2758,7 @@ GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2660,6 +2660,7 @@ GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2799,6 +2799,7 @@ GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2181,6 +2181,7 @@ GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
new file mode 100644
@@ -0,0 +1,122 @@
+/* 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;
+ case '+': l++;
+ }
+
+ if (*l == '\0')
+ return 0;
+
+ int n = 0;
+ while (*l != '\0')
+ {
+ /* Check if '*l' is a digit. */
+ if ((unsigned int)(*l) - '0' >= 10)
+ return 0;
+
+ /* Ignore invalid large values. */
+ if (INT_MULTIPLY_WRAPV (10, n, &n)
+ || INT_ADD_WRAPV (n, *l++ - '0', &n))
+ return 0;
+ }
+
+ /* -1 indicates that the process is terminated. */
+ if (neg && n != 1)
+ return 0;
+
+ 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) == -1)
+ /* 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;
+}
@@ -2826,6 +2826,7 @@ GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2859,6 +2859,7 @@ GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2580,6 +2580,7 @@ GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2894,6 +2894,7 @@ GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
new file mode 100644
@@ -0,0 +1,104 @@
+/* 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 = __read_nocancel (fd, *re, buffer_end - *re);
+ if (n < 0)
+ return -1;
+
+ *re += n;
+
+ nl = memchr (*cp, '\n', *re - *cp);
+ while (nl == NULL && *re == buffer_end)
+ {
+ /* Truncate too long lines. */
+ *re = buffer + 3 * (buffer_end - buffer) / 4;
+ n = __read_nocancel (fd, *re, buffer_end - *re);
+ if (n < 0)
+ return -1;
+
+ nl = memchr (*re, '\n', n);
+ **re = '\0';
+ *re += n;
+ }
+ }
+ 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;
+}
+
+int
+procutils_read_file (const char *filename, procutils_closure_t closure,
+ void *arg)
+{
+ enum { buffer_size = 1024 };
+ char buffer[buffer_size];
+ char *buffer_end = buffer + buffer_size;
+ char *cp = buffer_end;
+ char *re = buffer_end;
+
+ int fd = __open64_nocancel (filename, O_RDONLY | O_CLOEXEC);
+ if (fd == -1)
+ return -1;
+
+ 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;
+}
new file mode 100644
@@ -0,0 +1,35 @@
+/* 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
+
+typedef int (*procutils_closure_t)(const char *line, void *arg);
+
+/* 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 true if the read should continue, or false
+ if the function should stop.
+
+ It returns 0 in case of success, or -1 otherwise. */
+int procutils_read_file (const char *filename, procutils_closure_t closure,
+ void *arg) attribute_hidden;
+
+#endif
@@ -2437,6 +2437,7 @@ GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2637,6 +2637,7 @@ GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2824,6 +2824,7 @@ GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2617,6 +2617,7 @@ GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2667,6 +2667,7 @@ GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2664,6 +2664,7 @@ GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2819,6 +2819,7 @@ GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2632,6 +2632,7 @@ GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -67,4 +67,8 @@ extern int pidfd_send_signal (int __pidfd, int __sig, siginfo_t *__info,
extern pid_t pidfd_fork (int *__pidfd, int __cgroup, unsigned int __flags)
__THROW;
+/* Query the process ID (PID) from process descriptor __FD. Return the PID
+ or -1 in case of an error. */
+extern pid_t pidfd_getpid (int __fd) __THROW;
+
#endif /* _PIDFD_H */
@@ -18,6 +18,7 @@
#include <errno.h>
#include <fcntl.h>
+#include <limits.h>
#include <support/capture_subprocess.h>
#include <support/check.h>
#include <support/process_state.h>
@@ -27,6 +28,9 @@
#include <support/xsocket.h>
#include <sys/pidfd.h>
#include <sys/wait.h>
+#include <stdlib.h>
+
+#include <string.h>
#define REMOTE_PATH "/dev/null"
@@ -102,6 +106,43 @@ do_test (void)
ppid = getpid ();
puid = getuid ();
+ /* Sanity check for invalid inputs. */
+ TEST_COMPARE (pidfd_getpid (-1), -1);
+ TEST_COMPARE (errno, EBADF);
+
+ {
+ pid_t pid = pidfd_getpid (STDOUT_FILENO);
+ TEST_COMPARE (pid, -1);
+ TEST_COMPARE (errno, EBADF);
+ }
+
+ /* Check if pidfd_getpid returns ESRCH for exited subprocess. */
+ {
+ int pidfd;
+ pid_t pidfork = pidfd_fork (&pidfd, -1, 0);
+ if (pidfork == 0)
+ _exit (EXIT_SUCCESS);
+
+ /* The process might be still running or already in zombie state, in any
+ case the PID is still allocated to the process. */
+ pid_t pid = pidfd_getpid (pidfd);
+ if (pid > 0)
+ support_process_state_wait (pid, support_process_state_zombie);
+
+ siginfo_t info;
+ TEST_COMPARE (waitid (P_PIDFD, pidfd, &info, WEXITED), 0);
+ TEST_COMPARE (info.si_pid, pidfork);
+ TEST_COMPARE (info.si_status, 0);
+ TEST_COMPARE (info.si_code, CLD_EXITED);
+
+ /* Once the process is reaped the associated PID is not available. */
+ pid = pidfd_getpid (pidfd);
+ TEST_COMPARE (pid, -1);
+ TEST_COMPARE (errno, ESRCH);
+
+ xclose (pidfd);
+ }
+
TEST_COMPARE (socketpair (AF_UNIX, SOCK_STREAM, 0, sockets), 0);
pid_t pid = xfork ();
@@ -118,6 +159,12 @@ do_test (void)
int pidfd = pidfd_open (pid, 0);
TEST_VERIFY (pidfd != -1);
+ TEST_COMPARE (pidfd_getpid (INT_MAX), -1);
+ {
+ pid_t querypid = pidfd_getpid (pidfd);
+ TEST_COMPARE (querypid, pid);
+ }
+
/* Wait for first sigtimedwait. */
support_process_state_wait (pid, support_process_state_sleeping);
TEST_COMPARE (pidfd_send_signal (pidfd, SIGUSR1, NULL, 0), 0);
new file mode 100644
@@ -0,0 +1,187 @@
+/* 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/xsocket.h>
+#include <support/xunistd.h>
+#include <support/test-driver.h>
+#include <sys/pidfd.h>
+#include <sys/wait.h>
+#include <sys/mount.h>
+#include <string.h>
+
+#include <stdio.h>
+
+static int sockfd[2];
+
+static void
+send_fd (const int sock, const int fd)
+{
+ union
+ {
+ struct cmsghdr hdr;
+ char buf[CMSG_SPACE (sizeof (int))];
+ } cmsgbuf = {0};
+ struct cmsghdr *cmsg;
+ char ch = 'A';
+ struct iovec vec =
+ {
+ .iov_base = &ch,
+ .iov_len = sizeof ch
+ };
+ struct msghdr msg =
+ {
+ .msg_control = &cmsgbuf.buf,
+ .msg_controllen = sizeof (cmsgbuf.buf),
+ .msg_iov = &vec,
+ .msg_iovlen = 1,
+ };
+
+ cmsg = CMSG_FIRSTHDR (&msg);
+ cmsg->cmsg_len = CMSG_LEN (sizeof (int));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ memcpy (CMSG_DATA (cmsg), &fd, sizeof (fd));
+
+ ssize_t n;
+ while ((n = sendmsg (sock, &msg, 0)) == -1 && errno == EINTR);
+
+ TEST_VERIFY_EXIT (n == 1);
+}
+
+static int
+recv_fd (const int sock)
+{
+ union
+ {
+ struct cmsghdr hdr;
+ char buf[CMSG_SPACE(sizeof(int))];
+ } cmsgbuf = {0};
+ struct cmsghdr *cmsg;
+ char ch = '\0';
+ struct iovec vec =
+ {
+ .iov_base = &ch,
+ .iov_len = sizeof ch
+ };
+ struct msghdr msg =
+ {
+ .msg_control = &cmsgbuf.buf,
+ .msg_controllen = sizeof (cmsgbuf.buf),
+ .msg_iov = &vec,
+ .msg_iovlen = 1,
+ };
+
+ ssize_t n;
+ while ((n = recvmsg (sock, &msg, 0)) == -1 && errno == EINTR);
+ if (n != 1 || ch != 'A')
+ return -1;
+
+ cmsg = CMSG_FIRSTHDR (&msg);
+ if (cmsg == NULL)
+ return -1;
+ if (cmsg->cmsg_type != SCM_RIGHTS)
+ return -1;
+
+ int fd = -1;
+ memcpy (&fd, CMSG_DATA (cmsg), sizeof (fd));
+ if (fd < 0)
+ return -1;
+ return fd;
+}
+
+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");
+ }
+
+ TEST_VERIFY_EXIT (socketpair (AF_UNIX, SOCK_STREAM, 0, sockfd) == 0);
+
+ /* Check if pidfd_getpid returns EREMOTE for process not in current
+ namespace. */
+ {
+ int pidfd;
+ pid_t pid = pidfd_fork (&pidfd, -1, 0);
+ TEST_VERIFY_EXIT (pid >= 0);
+ if (pid == 0)
+ {
+ 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");
+ }
+
+ TEST_VERIFY_EXIT (mount (NULL, "/", NULL, MS_REC | MS_PRIVATE, 0)
+ == 0);
+
+ pid_t child = xfork ();
+ if (child > 0)
+ {
+ int status;
+ xwaitpid (child, &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)
+ /* 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);
+ }
+
+ int ppidfd = recv_fd (sockfd[0]);
+ TEST_COMPARE (pidfd_getpid (ppidfd), -1);
+ TEST_COMPARE (errno, EREMOTE);
+
+ _exit (EXIT_SUCCESS);
+ }
+
+ send_fd (sockfd[1], pidfd);
+
+ siginfo_t info;
+ TEST_COMPARE (waitid (P_PIDFD, 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);
+ }
+
+ return 0;
+}
+
+#include <support/test-driver.c>
@@ -2583,6 +2583,7 @@ GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2689,6 +2689,7 @@ GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F