diff --git a/common/common-pidfds.c b/common/common-pidfds.c new file mode 100644 index 0000000..a01cab7 --- /dev/null +++ b/common/common-pidfds.c @@ -0,0 +1,207 @@ +/* + +Copyright (c) 2017-2019, Feral Interactive +Copyright (c) 2019, Red Hat +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Feral Interactive nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ +#define _GNU_SOURCE +#include + +#include "common-helpers.h" +#include "common-pidfds.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if !HAVE_FN_PIDFD_OPEN +#include + +#ifndef __NR_pidfd_open +#define __NR_pidfd_open 434 +#endif + +static int pidfd_open(pid_t pid, unsigned int flags) +{ + return (int)syscall(__NR_pidfd_open, pid, flags); +} +#endif + +/* pidfd functions */ +int open_pidfds(pid_t *pids, int *fds, int count) +{ + int i = 0; + + for (i = 0; i < count; i++) { + int pid = pids[i]; + int fd = pidfd_open(pid, 0); + + if (fd < 0) + break; + + fds[i] = fd; + } + + return i; +} + +static int parse_pid(const char *str, pid_t *pid) +{ + unsigned long long int v; + char *end; + pid_t p; + + errno = 0; + v = strtoull(str, &end, 0); + if (end == str) + return -ENOENT; + else if (errno != 0) + return -errno; + + p = (pid_t)v; + + if (p < 1 || (unsigned long long int)p != v) + return -ERANGE; + + if (pid) + *pid = p; + + return 0; +} + +static int parse_status_field_pid(const char *val, pid_t *pid) +{ + const char *t; + + t = strrchr(val, '\t'); + if (t == NULL) + return -ENOENT; + + return parse_pid(t, pid); +} + +static int pidfd_to_pid(int fdinfo, int pidfd, pid_t *pid) +{ + autofree char *key = NULL; + autofree char *val = NULL; + char name[256] = { + 0, + }; + bool found = false; + FILE *f = NULL; + size_t keylen = 0; + size_t vallen = 0; + ssize_t n; + int fd; + int r = 0; + + *pid = 0; + + buffered_snprintf(name, "%d", pidfd); + + fd = openat(fdinfo, name, O_RDONLY | O_CLOEXEC | O_NOCTTY); + + if (fd != -1) + f = fdopen(fd, "r"); + + if (f == NULL) + return -errno; + + do { + n = getdelim(&key, &keylen, ':', f); + if (n == -1) { + r = errno; + break; + } + + n = getdelim(&val, &vallen, '\n', f); + if (n == -1) { + r = errno; + break; + } + + // TODO: strstrip (key); + + if (!strncmp(key, "Pid", 3)) { + r = parse_status_field_pid(val, pid); + found = r > -1; + } + + } while (r == 0 && !found); + + fclose(f); + + if (r < 0) + return r; + else if (!found) + return -ENOENT; + + return 0; +} + +int pidfds_to_pids(int *fds, pid_t *pids, int count) +{ + int fdinfo = -1; + int r = 0; + int i; + + fdinfo = open_fdinfo_dir(); + if (fdinfo == -1) + return -1; + + for (i = 0; i < count && r == 0; i++) + r = pidfd_to_pid(fdinfo, fds[i], &pids[i]); + + (void)close(fdinfo); + + if (r != 0) + errno = -r; + + return i; +} + +/* misc directory helpers */ +int open_fdinfo_dir(void) +{ + int fd; + + fd = open("/proc/self/fdinfo", O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY); + + if (fd == -1) + return errno; + + return fd; +} diff --git a/common/common-pidfds.h b/common/common-pidfds.h new file mode 100644 index 0000000..b158517 --- /dev/null +++ b/common/common-pidfds.h @@ -0,0 +1,53 @@ +/* + +Copyright (c) 2017-2019, Feral Interactive +Copyright (c) 2019, Red Hat +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Feral Interactive nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include + +/* Open pidfds for up to count process ids specified in pids. The + * pointer fds needs to point to an array with at least count + * entries. Will stop when it encounters an error (and sets errno). + * Returns the number of successfully opened pidfds (or -1 in case + * of other errors. */ +int open_pidfds(pid_t *pids, int *fds, int count); + +/* Translate up to count process ids to the corresponding process ids. + * The pointer pids needs to point to an array with at least count + * entries. Will stop when it encounters an error (and sets errno). + * Returns the number of successfully translated pidfds (or -1 in + * case of other errors. */ +int pidfds_to_pids(int *fds, pid_t *pids, int count); + +/* Helper to open the fdinfo directory for the current process, i.e. + * does open("/proc/self/fdinfo", ...). Returns the file descriptor + * for the directory, ownership is transferred and caller needs to + * call close on it. */ +int open_fdinfo_dir(void); diff --git a/common/meson.build b/common/meson.build index 686dd81..93c9fde 100644 --- a/common/meson.build +++ b/common/meson.build @@ -5,12 +5,14 @@ common_sources = [ 'common-external.c', 'common-helpers.c', 'common-gpu.c', + 'common-pidfds.c', ] daemon_common = static_library( 'daemon-common', sources: common_sources, install: false, + include_directories: [config_h_dir] ) link_daemon_common = declare_dependency( diff --git a/meson.build b/meson.build index 97da208..881d28e 100644 --- a/meson.build +++ b/meson.build @@ -128,9 +128,13 @@ with_examples = get_option('with-examples') with_util = get_option('with-util') # Provide a config.h +pidfd_open = cc.has_function('pidfd_open', args: '-D_GNU_SOURCE') + cdata = configuration_data() cdata.set_quoted('LIBEXECDIR', path_libexecdir) cdata.set_quoted('GAMEMODE_VERSION', meson.project_version()) +cdata.set10('HAVE_FN_PIDFD_OPEN', pidfd_open) + config_h = configure_file( configuration: cdata, output: 'build-config.h',