proc-utils: Make sure '/proc/<pid>/*' files exist before opening them

This avoids warnings if the specific file does not exist, especially when the
process has being removed quickly.

Fixes #816
This commit is contained in:
Julian Bouzas 2025-06-23 08:45:54 -04:00 committed by George Kiagiadakis
parent 5c6a72e3cf
commit f8be5a76e6

View file

@ -6,7 +6,9 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#include <fcntl.h>
#include <stdio.h> #include <stdio.h>
#include <spa/utils/cleanup.h>
#include "log.h" #include "log.h"
#include "proc-utils.h" #include "proc-utils.h"
@ -145,6 +147,21 @@ wp_proc_info_get_cgroup (WpProcInfo * self)
return self->cgroup; return self->cgroup;
} }
static FILE *
fdopenat (int dirfd, const char *path, int flags, const char *mode, mode_t perm)
{
int fd = openat (dirfd, path, flags, perm);
if (fd >= 0) {
FILE *f = fdopen (fd, mode);
if (f)
return f;
close (fd);
}
return NULL;
}
/*! /*!
* \brief Gets the process information of a given PID * \brief Gets the process information of a given PID
* \ingroup wpprocutils * \ingroup wpprocutils
@ -155,51 +172,46 @@ WpProcInfo *
wp_proc_utils_get_proc_info (pid_t pid) wp_proc_utils_get_proc_info (pid_t pid)
{ {
WpProcInfo *ret = wp_proc_info_new (pid); WpProcInfo *ret = wp_proc_info_new (pid);
g_autofree gchar *status = NULL; char path [64];
g_autoptr (GError) error = NULL; spa_autoclose int base_fd = -1;
gsize length = 0; FILE *file;
g_autofree gchar *line = NULL;
size_t size = 0;
snprintf (path, sizeof(path), "/proc/%d", pid);
base_fd = open (path,
O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY, 0);
if (base_fd < 0) {
wp_info ("Could not open process info directory %s, skipping", path);
return ret;
}
/* Get parent PID */ /* Get parent PID */
{ file = fdopenat (base_fd, "status",
g_autofree gchar *path = g_strdup_printf ("/proc/%d/status", pid); O_RDONLY | O_NONBLOCK | O_CLOEXEC | O_NOCTTY, "r", 0);
if (g_file_get_contents (path, &status, &length, &error)) { if (file) {
const gchar *loc = strstr (status, "\nPPid:"); while (getline (&line, &size, file) > 1)
if (loc) { if (sscanf (line, "PPid:%d\n", &ret->parent) == 1)
const gint res = sscanf (loc, "\nPPid:%d\n", &ret->parent); break;
if (!res || res == EOF) fclose (file);
wp_warning ("failed to parse status PPID for PID %d", pid);
} else {
wp_warning ("failed to find status parent PID for PID %d", pid);
}
} else {
wp_warning ("failed to get status for PID %d: %s", pid, error->message);
}
} }
/* Get cgroup */ /* Get cgroup */
{ file = fdopenat (base_fd, "cgroup",
g_autofree gchar *path = g_strdup_printf ("/proc/%d/cgroup", pid); O_RDONLY | O_NONBLOCK | O_CLOEXEC | O_NOCTTY, "r", 0);
if (g_file_get_contents (path, &ret->cgroup, &length, &error)) { if (file) {
if (length > 0) if (getline (&line, &size, file) > 1)
ret->cgroup [length - 1] = '\0'; /* Remove EOF character */ ret->cgroup = g_strstrip (g_strdup (line));
} else { fclose (file);
wp_warning ("failed to get cgroup for PID %d: %s", pid, error->message);
}
} }
/* Get args */ /* Get args */
{ file = fdopenat (base_fd, "cmdline",
g_autofree gchar *path = g_strdup_printf ("/proc/%d/cmdline", pid); O_RDONLY | O_NONBLOCK | O_CLOEXEC | O_NOCTTY, "r", 0);
FILE *file = fopen (path, "rb");
if (file) { if (file) {
g_autofree gchar *lineptr = NULL; while (getdelim (&line, &size, 0, file) > 1 && ret->n_args < MAX_ARGS)
size_t size = 0; ret->args[ret->n_args++] = g_strdup (line);
while (getdelim (&lineptr, &size, 0, file) > 1 && ret->n_args < MAX_ARGS)
ret->args[ret->n_args++] = g_strdup (lineptr);
fclose (file); fclose (file);
} else {
wp_warning ("failed to get cmdline for PID %d: %m", pid);
}
} }
return ret; return ret;