2020-11-09 13:23:58 +09:00
|
|
|
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
2015-10-23 18:52:53 +02:00
|
|
|
|
#include <ctype.h>
|
2015-04-10 19:10:00 +02:00
|
|
|
|
#include <errno.h>
|
2015-11-30 21:43:37 +01:00
|
|
|
|
#include <limits.h>
|
|
|
|
|
|
#include <linux/oom.h>
|
2022-02-12 06:05:54 +01:00
|
|
|
|
#include <pthread.h>
|
2023-06-02 16:06:17 +01:00
|
|
|
|
#include <spawn.h>
|
2015-10-23 18:52:53 +02:00
|
|
|
|
#include <stdbool.h>
|
|
|
|
|
|
#include <stdio.h>
|
2015-11-30 21:43:37 +01:00
|
|
|
|
#include <stdlib.h>
|
2018-03-23 20:52:46 +01:00
|
|
|
|
#include <sys/mount.h>
|
2015-10-27 14:24:58 +01:00
|
|
|
|
#include <sys/personality.h>
|
2015-10-27 13:56:40 +01:00
|
|
|
|
#include <sys/prctl.h>
|
2015-10-23 18:52:53 +02:00
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
|
#include <sys/wait.h>
|
2015-11-30 21:43:37 +01:00
|
|
|
|
#include <syslog.h>
|
2015-10-23 18:52:53 +02:00
|
|
|
|
#include <unistd.h>
|
2017-10-03 10:41:51 +02:00
|
|
|
|
#if HAVE_VALGRIND_VALGRIND_H
|
2016-01-19 15:48:45 +00:00
|
|
|
|
#include <valgrind/valgrind.h>
|
|
|
|
|
|
#endif
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
2023-08-30 10:30:42 +01:00
|
|
|
|
#include "sd-messages.h"
|
|
|
|
|
|
|
2015-10-27 03:01:06 +01:00
|
|
|
|
#include "alloc-util.h"
|
2016-02-22 15:39:52 +01:00
|
|
|
|
#include "architecture.h"
|
2022-11-06 16:45:48 +01:00
|
|
|
|
#include "argv-util.h"
|
2023-10-17 13:41:08 +02:00
|
|
|
|
#include "dirent-util.h"
|
2022-11-05 17:40:01 +01:00
|
|
|
|
#include "env-file.h"
|
2019-05-11 09:51:33 +02:00
|
|
|
|
#include "env-util.h"
|
2017-08-01 17:38:05 +01:00
|
|
|
|
#include "errno-util.h"
|
2019-11-25 14:55:50 +01:00
|
|
|
|
#include "escape.h"
|
2015-10-25 13:14:12 +01:00
|
|
|
|
#include "fd-util.h"
|
2015-04-10 19:10:00 +02:00
|
|
|
|
#include "fileio.h"
|
2015-10-26 21:16:26 +01:00
|
|
|
|
#include "fs-util.h"
|
2022-11-05 17:40:01 +01:00
|
|
|
|
#include "hostname-util.h"
|
2019-05-16 17:44:57 +02:00
|
|
|
|
#include "locale-util.h"
|
2015-04-10 19:10:00 +02:00
|
|
|
|
#include "log.h"
|
2015-11-30 21:43:37 +01:00
|
|
|
|
#include "macro.h"
|
2019-03-13 12:02:21 +01:00
|
|
|
|
#include "memory-util.h"
|
2019-10-31 11:07:23 +09:00
|
|
|
|
#include "missing_sched.h"
|
|
|
|
|
|
#include "missing_syscall.h"
|
2023-01-03 17:52:08 +00:00
|
|
|
|
#include "missing_threads.h"
|
2022-12-14 13:40:53 +01:00
|
|
|
|
#include "mountpoint-util.h"
|
2019-03-13 11:21:49 +01:00
|
|
|
|
#include "namespace-util.h"
|
2022-11-11 21:55:00 +01:00
|
|
|
|
#include "nulstr-util.h"
|
2022-11-05 17:40:01 +01:00
|
|
|
|
#include "parse-util.h"
|
2019-11-25 14:55:50 +01:00
|
|
|
|
#include "path-util.h"
|
2015-12-01 23:22:03 +01:00
|
|
|
|
#include "process-util.h"
|
2016-05-30 02:03:51 +02:00
|
|
|
|
#include "raw-clone.h"
|
2018-11-26 15:59:17 +01:00
|
|
|
|
#include "rlimit-util.h"
|
2015-12-01 23:22:03 +01:00
|
|
|
|
#include "signal-util.h"
|
2016-01-24 16:08:36 +01:00
|
|
|
|
#include "stat-util.h"
|
2019-10-30 16:35:48 +01:00
|
|
|
|
#include "stdio-util.h"
|
2015-10-27 14:24:58 +01:00
|
|
|
|
#include "string-table.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
|
#include "string-util.h"
|
2017-12-22 13:08:14 +01:00
|
|
|
|
#include "terminal-util.h"
|
2015-10-25 22:32:30 +01:00
|
|
|
|
#include "user-util.h"
|
Rework cmdline printing to use unicode
The functions to retrieve and print process cmdlines were based on the
assumption that they contain printable ASCII, and everything else
should be filtered out. That assumption doesn't hold in today's world,
where people are free to use unicode everywhere.
This replaces the custom cmdline reading code with a more generic approach
using utf8_escape_non_printable_full().
For kernel threads, truncation is done on the parenthesized name, so we'll
get "[worker]", "[worker…]", …, "[w…]", "[…", "…" as we reduce the number of
available columns.
This implementation is most likely slower for very long cmdlines, but I don't
think this is very important. The common case is to have short commandlines,
and should print those properly. Absurdly long cmdlines are the exception,
which needs to be handled correctly and safely, but speed is not too important.
Fixes #12532.
v2:
- use size_t for the number of columns. This change propagates into various
other functions that call get_process_cmdline(), increasing the size of the
patch, but the changes are rather trivial.
2019-05-15 11:20:26 +02:00
|
|
|
|
#include "utf8.h"
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
2019-05-15 11:55:59 +02:00
|
|
|
|
/* The kernel limits userspace processes to TASK_COMM_LEN (16 bytes), but allows higher values for its own
|
|
|
|
|
|
* workers, e.g. "kworker/u9:3-kcryptd/253:0". Let's pick a fixed smallish limit that will work for the kernel.
|
|
|
|
|
|
*/
|
|
|
|
|
|
#define COMM_MAX_LEN 128
|
|
|
|
|
|
|
2019-05-20 14:37:03 +03:00
|
|
|
|
static int get_process_state(pid_t pid) {
|
2019-11-25 14:59:01 +01:00
|
|
|
|
_cleanup_free_ char *line = NULL;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
const char *p;
|
|
|
|
|
|
char state;
|
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
|
|
assert(pid >= 0);
|
|
|
|
|
|
|
2019-11-25 14:59:01 +01:00
|
|
|
|
/* Shortcut: if we are enquired about our own state, we are obviously running */
|
|
|
|
|
|
if (pid == 0 || pid == getpid_cached())
|
|
|
|
|
|
return (unsigned char) 'R';
|
|
|
|
|
|
|
2015-04-10 19:10:00 +02:00
|
|
|
|
p = procfs_file_alloca(pid, "stat");
|
2015-07-23 23:44:40 +02:00
|
|
|
|
|
2015-04-10 19:10:00 +02:00
|
|
|
|
r = read_one_line_file(p, &line);
|
2015-07-23 23:44:40 +02:00
|
|
|
|
if (r == -ENOENT)
|
|
|
|
|
|
return -ESRCH;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
|
|
p = strrchr(line, ')');
|
|
|
|
|
|
if (!p)
|
|
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
|
|
|
|
p++;
|
|
|
|
|
|
|
|
|
|
|
|
if (sscanf(p, " %c", &state) != 1)
|
|
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
|
|
|
|
return (unsigned char) state;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-10-17 10:16:31 +02:00
|
|
|
|
int pid_get_comm(pid_t pid, char **ret) {
|
2018-05-16 21:50:35 -04:00
|
|
|
|
_cleanup_free_ char *escaped = NULL, *comm = NULL;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
int r;
|
|
|
|
|
|
|
2018-05-16 21:50:35 -04:00
|
|
|
|
assert(ret);
|
2015-04-10 19:10:00 +02:00
|
|
|
|
assert(pid >= 0);
|
|
|
|
|
|
|
2019-11-25 14:58:24 +01:00
|
|
|
|
if (pid == 0 || pid == getpid_cached()) {
|
|
|
|
|
|
comm = new0(char, TASK_COMM_LEN + 1); /* Must fit in 16 byte according to prctl(2) */
|
|
|
|
|
|
if (!comm)
|
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
|
|
if (prctl(PR_GET_NAME, comm) < 0)
|
|
|
|
|
|
return -errno;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
const char *p;
|
|
|
|
|
|
|
|
|
|
|
|
p = procfs_file_alloca(pid, "comm");
|
|
|
|
|
|
|
|
|
|
|
|
/* Note that process names of kernel threads can be much longer than TASK_COMM_LEN */
|
|
|
|
|
|
r = read_one_line_file(p, &comm);
|
|
|
|
|
|
if (r == -ENOENT)
|
|
|
|
|
|
return -ESRCH;
|
|
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-05-15 11:55:59 +02:00
|
|
|
|
escaped = new(char, COMM_MAX_LEN);
|
2018-05-16 21:50:35 -04:00
|
|
|
|
if (!escaped)
|
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
|
|
/* Escape unprintable characters, just in case, but don't grow the string beyond the underlying size */
|
2019-05-15 11:55:59 +02:00
|
|
|
|
cellescape(escaped, COMM_MAX_LEN, comm);
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
2018-05-16 21:50:35 -04:00
|
|
|
|
*ret = TAKE_PTR(escaped);
|
|
|
|
|
|
return 0;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-10-17 10:16:31 +02:00
|
|
|
|
int pidref_get_comm(const PidRef *pid, char **ret) {
|
|
|
|
|
|
_cleanup_free_ char *comm = NULL;
|
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
|
|
if (!pidref_is_set(pid))
|
|
|
|
|
|
return -ESRCH;
|
|
|
|
|
|
|
|
|
|
|
|
r = pid_get_comm(pid->pid, &comm);
|
|
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
|
|
r = pidref_verify(pid);
|
|
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
|
|
*ret = TAKE_PTR(comm);
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-10-16 23:17:29 +02:00
|
|
|
|
static int pid_get_cmdline_nulstr(
|
2021-03-30 19:42:36 +02:00
|
|
|
|
pid_t pid,
|
|
|
|
|
|
size_t max_size,
|
|
|
|
|
|
ProcessCmdlineFlags flags,
|
|
|
|
|
|
char **ret,
|
|
|
|
|
|
size_t *ret_size) {
|
|
|
|
|
|
|
2023-10-16 23:17:29 +02:00
|
|
|
|
_cleanup_free_ char *t = NULL;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
const char *p;
|
Rework cmdline printing to use unicode
The functions to retrieve and print process cmdlines were based on the
assumption that they contain printable ASCII, and everything else
should be filtered out. That assumption doesn't hold in today's world,
where people are free to use unicode everywhere.
This replaces the custom cmdline reading code with a more generic approach
using utf8_escape_non_printable_full().
For kernel threads, truncation is done on the parenthesized name, so we'll
get "[worker]", "[worker…]", …, "[w…]", "[…", "…" as we reduce the number of
available columns.
This implementation is most likely slower for very long cmdlines, but I don't
think this is very important. The common case is to have short commandlines,
and should print those properly. Absurdly long cmdlines are the exception,
which needs to be handled correctly and safely, but speed is not too important.
Fixes #12532.
v2:
- use size_t for the number of columns. This change propagates into various
other functions that call get_process_cmdline(), increasing the size of the
patch, but the changes are rather trivial.
2019-05-15 11:20:26 +02:00
|
|
|
|
size_t k;
|
2021-03-23 00:49:28 -07:00
|
|
|
|
int r;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
2021-03-30 19:42:36 +02:00
|
|
|
|
/* Retrieves a process' command line as a "sized nulstr", i.e. possibly without the last NUL, but
|
|
|
|
|
|
* with a specified size.
|
2016-06-14 23:52:29 +02:00
|
|
|
|
*
|
2021-03-30 19:42:36 +02:00
|
|
|
|
* If PROCESS_CMDLINE_COMM_FALLBACK is specified in flags and the process has no command line set
|
|
|
|
|
|
* (the case for kernel threads), or has a command line that resolves to the empty string, will
|
|
|
|
|
|
* return the "comm" name of the process instead. This will use at most _SC_ARG_MAX bytes of input
|
|
|
|
|
|
* data.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Returns an error, 0 if output was read but is truncated, 1 otherwise.
|
|
|
|
|
|
*/
|
2016-06-14 23:52:29 +02:00
|
|
|
|
|
2015-04-10 19:10:00 +02:00
|
|
|
|
p = procfs_file_alloca(pid, "cmdline");
|
2021-03-30 19:42:36 +02:00
|
|
|
|
r = read_virtual_file(p, max_size, &t, &k); /* Let's assume that each input byte results in >= 1
|
|
|
|
|
|
* columns of output. We ignore zero-width codepoints. */
|
2019-04-04 10:17:16 +02:00
|
|
|
|
if (r == -ENOENT)
|
|
|
|
|
|
return -ESRCH;
|
|
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
2017-12-11 20:01:55 +01:00
|
|
|
|
|
2021-03-30 19:42:36 +02:00
|
|
|
|
if (k == 0) {
|
2019-05-16 12:14:52 +02:00
|
|
|
|
if (!(flags & PROCESS_CMDLINE_COMM_FALLBACK))
|
2015-04-10 19:10:00 +02:00
|
|
|
|
return -ENOENT;
|
|
|
|
|
|
|
Rework cmdline printing to use unicode
The functions to retrieve and print process cmdlines were based on the
assumption that they contain printable ASCII, and everything else
should be filtered out. That assumption doesn't hold in today's world,
where people are free to use unicode everywhere.
This replaces the custom cmdline reading code with a more generic approach
using utf8_escape_non_printable_full().
For kernel threads, truncation is done on the parenthesized name, so we'll
get "[worker]", "[worker…]", …, "[w…]", "[…", "…" as we reduce the number of
available columns.
This implementation is most likely slower for very long cmdlines, but I don't
think this is very important. The common case is to have short commandlines,
and should print those properly. Absurdly long cmdlines are the exception,
which needs to be handled correctly and safely, but speed is not too important.
Fixes #12532.
v2:
- use size_t for the number of columns. This change propagates into various
other functions that call get_process_cmdline(), increasing the size of the
patch, but the changes are rather trivial.
2019-05-15 11:20:26 +02:00
|
|
|
|
/* Kernel threads have no argv[] */
|
2021-03-30 19:42:36 +02:00
|
|
|
|
_cleanup_free_ char *comm = NULL;
|
2016-06-14 23:52:29 +02:00
|
|
|
|
|
2023-10-17 10:16:31 +02:00
|
|
|
|
r = pid_get_comm(pid, &comm);
|
Rework cmdline printing to use unicode
The functions to retrieve and print process cmdlines were based on the
assumption that they contain printable ASCII, and everything else
should be filtered out. That assumption doesn't hold in today's world,
where people are free to use unicode everywhere.
This replaces the custom cmdline reading code with a more generic approach
using utf8_escape_non_printable_full().
For kernel threads, truncation is done on the parenthesized name, so we'll
get "[worker]", "[worker…]", …, "[w…]", "[…", "…" as we reduce the number of
available columns.
This implementation is most likely slower for very long cmdlines, but I don't
think this is very important. The common case is to have short commandlines,
and should print those properly. Absurdly long cmdlines are the exception,
which needs to be handled correctly and safely, but speed is not too important.
Fixes #12532.
v2:
- use size_t for the number of columns. This change propagates into various
other functions that call get_process_cmdline(), increasing the size of the
patch, but the changes are rather trivial.
2019-05-15 11:20:26 +02:00
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
2016-06-14 23:52:29 +02:00
|
|
|
|
|
2023-10-16 23:17:29 +02:00
|
|
|
|
free(t);
|
2021-03-30 19:42:36 +02:00
|
|
|
|
t = strjoin("[", comm, "]");
|
Rework cmdline printing to use unicode
The functions to retrieve and print process cmdlines were based on the
assumption that they contain printable ASCII, and everything else
should be filtered out. That assumption doesn't hold in today's world,
where people are free to use unicode everywhere.
This replaces the custom cmdline reading code with a more generic approach
using utf8_escape_non_printable_full().
For kernel threads, truncation is done on the parenthesized name, so we'll
get "[worker]", "[worker…]", …, "[w…]", "[…", "…" as we reduce the number of
available columns.
This implementation is most likely slower for very long cmdlines, but I don't
think this is very important. The common case is to have short commandlines,
and should print those properly. Absurdly long cmdlines are the exception,
which needs to be handled correctly and safely, but speed is not too important.
Fixes #12532.
v2:
- use size_t for the number of columns. This change propagates into various
other functions that call get_process_cmdline(), increasing the size of the
patch, but the changes are rather trivial.
2019-05-15 11:20:26 +02:00
|
|
|
|
if (!t)
|
|
|
|
|
|
return -ENOMEM;
|
2021-03-30 19:42:36 +02:00
|
|
|
|
|
|
|
|
|
|
k = strlen(t);
|
|
|
|
|
|
r = k <= max_size;
|
|
|
|
|
|
if (r == 0) /* truncation */
|
|
|
|
|
|
t[max_size] = '\0';
|
2015-04-10 19:10:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-10-16 23:17:29 +02:00
|
|
|
|
if (ret)
|
|
|
|
|
|
*ret = TAKE_PTR(t);
|
|
|
|
|
|
if (ret_size)
|
|
|
|
|
|
*ret_size = k;
|
|
|
|
|
|
|
2021-03-30 19:42:36 +02:00
|
|
|
|
return r;
|
|
|
|
|
|
}
|
2019-01-22 14:29:50 +01:00
|
|
|
|
|
2023-10-16 23:17:29 +02:00
|
|
|
|
int pid_get_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **ret) {
|
2021-03-30 19:42:36 +02:00
|
|
|
|
_cleanup_free_ char *t = NULL;
|
|
|
|
|
|
size_t k;
|
|
|
|
|
|
char *ans;
|
2019-05-16 17:44:57 +02:00
|
|
|
|
|
2021-03-30 19:42:36 +02:00
|
|
|
|
assert(pid >= 0);
|
2021-11-01 08:50:08 +09:00
|
|
|
|
assert(ret);
|
2021-03-30 19:42:36 +02:00
|
|
|
|
|
2023-09-19 17:45:44 +02:00
|
|
|
|
/* Retrieve and format a command line. See above for discussion of retrieval options.
|
2021-03-30 19:42:36 +02:00
|
|
|
|
*
|
|
|
|
|
|
* There are two main formatting modes:
|
|
|
|
|
|
*
|
|
|
|
|
|
* - when PROCESS_CMDLINE_QUOTE is specified, output is quoted in C/Python style. If no shell special
|
|
|
|
|
|
* characters are present, this output can be copy-pasted into the terminal to execute. UTF-8
|
|
|
|
|
|
* output is assumed.
|
|
|
|
|
|
*
|
|
|
|
|
|
* - otherwise, a compact non-roundtrippable form is returned. Non-UTF8 bytes are replaced by <EFBFBD>. The
|
|
|
|
|
|
* returned string is of the specified console width at most, abbreviated with an ellipsis.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Returns -ESRCH if the process doesn't exist, and -ENOENT if the process has no command line (and
|
|
|
|
|
|
* PROCESS_CMDLINE_COMM_FALLBACK is not specified). Returns 0 and sets *line otherwise. */
|
|
|
|
|
|
|
2023-10-16 23:17:29 +02:00
|
|
|
|
int full = pid_get_cmdline_nulstr(pid, max_columns, flags, &t, &k);
|
2021-03-30 19:42:36 +02:00
|
|
|
|
if (full < 0)
|
|
|
|
|
|
return full;
|
|
|
|
|
|
|
2021-03-11 00:10:02 +01:00
|
|
|
|
if (flags & (PROCESS_CMDLINE_QUOTE | PROCESS_CMDLINE_QUOTE_POSIX)) {
|
|
|
|
|
|
ShellEscapeFlags shflags = SHELL_ESCAPE_EMPTY |
|
|
|
|
|
|
FLAGS_SET(flags, PROCESS_CMDLINE_QUOTE_POSIX) * SHELL_ESCAPE_POSIX;
|
|
|
|
|
|
|
2021-03-30 19:42:36 +02:00
|
|
|
|
assert(!(flags & PROCESS_CMDLINE_USE_LOCALE));
|
|
|
|
|
|
|
|
|
|
|
|
_cleanup_strv_free_ char **args = NULL;
|
|
|
|
|
|
|
2023-03-23 11:59:44 +09:00
|
|
|
|
/* Drop trailing NULs, otherwise strv_parse_nulstr() adds additional empty strings at the end.
|
|
|
|
|
|
* See also issue #21186. */
|
|
|
|
|
|
args = strv_parse_nulstr_full(t, k, /* drop_trailing_nuls = */ true);
|
2021-03-30 19:42:36 +02:00
|
|
|
|
if (!args)
|
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
2021-11-11 09:33:10 +01:00
|
|
|
|
ans = quote_command_line(args, shflags);
|
2021-03-30 19:42:36 +02:00
|
|
|
|
if (!ans)
|
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
/* Arguments are separated by NULs. Let's replace those with spaces. */
|
|
|
|
|
|
for (size_t i = 0; i < k - 1; i++)
|
|
|
|
|
|
if (t[i] == '\0')
|
|
|
|
|
|
t[i] = ' ';
|
|
|
|
|
|
|
|
|
|
|
|
delete_trailing_chars(t, WHITESPACE);
|
|
|
|
|
|
|
|
|
|
|
|
bool eight_bit = (flags & PROCESS_CMDLINE_USE_LOCALE) && !is_locale_utf8();
|
|
|
|
|
|
|
|
|
|
|
|
ans = escape_non_printable_full(t, max_columns,
|
|
|
|
|
|
eight_bit * XESCAPE_8_BIT | !full * XESCAPE_FORCE_ELLIPSIS);
|
|
|
|
|
|
if (!ans)
|
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
|
|
ans = str_realloc(ans);
|
|
|
|
|
|
}
|
2019-01-22 14:29:50 +01:00
|
|
|
|
|
2021-11-01 08:50:08 +09:00
|
|
|
|
*ret = ans;
|
2022-11-05 17:40:01 +01:00
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-10-16 23:17:29 +02:00
|
|
|
|
int pidref_get_cmdline(const PidRef *pid, size_t max_columns, ProcessCmdlineFlags flags, char **ret) {
|
|
|
|
|
|
_cleanup_free_ char *s = NULL;
|
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
|
|
if (!pidref_is_set(pid))
|
|
|
|
|
|
return -ESRCH;
|
|
|
|
|
|
|
|
|
|
|
|
r = pid_get_cmdline(pid->pid, max_columns, flags, &s);
|
|
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
|
|
r = pidref_verify(pid);
|
|
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
|
|
*ret = TAKE_PTR(s);
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int pid_get_cmdline_strv(pid_t pid, ProcessCmdlineFlags flags, char ***ret) {
|
2023-03-23 01:05:38 +09:00
|
|
|
|
_cleanup_free_ char *t = NULL;
|
|
|
|
|
|
char **args;
|
|
|
|
|
|
size_t k;
|
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
|
|
assert(pid >= 0);
|
|
|
|
|
|
assert((flags & ~PROCESS_CMDLINE_COMM_FALLBACK) == 0);
|
|
|
|
|
|
assert(ret);
|
|
|
|
|
|
|
2023-10-16 23:17:29 +02:00
|
|
|
|
r = pid_get_cmdline_nulstr(pid, SIZE_MAX, flags, &t, &k);
|
2023-03-23 01:05:38 +09:00
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
|
|
args = strv_parse_nulstr_full(t, k, /* drop_trailing_nuls = */ true);
|
|
|
|
|
|
if (!args)
|
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
|
|
*ret = args;
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-10-16 23:17:29 +02:00
|
|
|
|
int pidref_get_cmdline_strv(const PidRef *pid, ProcessCmdlineFlags flags, char ***ret) {
|
|
|
|
|
|
_cleanup_strv_free_ char **args = NULL;
|
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
|
|
if (!pidref_is_set(pid))
|
|
|
|
|
|
return -ESRCH;
|
|
|
|
|
|
|
|
|
|
|
|
r = pid_get_cmdline_strv(pid->pid, flags, &args);
|
|
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
|
|
r = pidref_verify(pid);
|
|
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
|
|
*ret = TAKE_PTR(args);
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-11-05 17:40:01 +01:00
|
|
|
|
int container_get_leader(const char *machine, pid_t *pid) {
|
|
|
|
|
|
_cleanup_free_ char *s = NULL, *class = NULL;
|
|
|
|
|
|
const char *p;
|
|
|
|
|
|
pid_t leader;
|
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
|
|
assert(machine);
|
|
|
|
|
|
assert(pid);
|
|
|
|
|
|
|
|
|
|
|
|
if (streq(machine, ".host")) {
|
|
|
|
|
|
*pid = 1;
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!hostname_is_valid(machine, 0))
|
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
|
|
p = strjoina("/run/systemd/machines/", machine);
|
|
|
|
|
|
r = parse_env_file(NULL, p,
|
|
|
|
|
|
"LEADER", &s,
|
|
|
|
|
|
"CLASS", &class);
|
|
|
|
|
|
if (r == -ENOENT)
|
|
|
|
|
|
return -EHOSTDOWN;
|
|
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
|
|
|
|
|
if (!s)
|
|
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
|
|
|
|
if (!streq_ptr(class, "container"))
|
|
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
|
|
|
|
r = parse_pid(s, &leader);
|
|
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
|
|
|
|
|
if (leader <= 1)
|
|
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
|
|
|
|
*pid = leader;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-09-08 17:03:56 -04:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-10-16 23:18:05 +02:00
|
|
|
|
int pid_is_kernel_thread(pid_t pid) {
|
2018-02-06 15:59:55 +01:00
|
|
|
|
_cleanup_free_ char *line = NULL;
|
|
|
|
|
|
unsigned long long flags;
|
|
|
|
|
|
size_t l, i;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
const char *p;
|
2018-02-06 15:59:55 +01:00
|
|
|
|
char *q;
|
|
|
|
|
|
int r;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
2017-10-04 23:01:32 +09:00
|
|
|
|
if (IN_SET(pid, 0, 1) || pid == getpid_cached()) /* pid 1, and we ourselves certainly aren't a kernel thread */
|
2015-04-10 19:10:00 +02:00
|
|
|
|
return 0;
|
2018-02-06 15:59:55 +01:00
|
|
|
|
if (!pid_is_valid(pid))
|
|
|
|
|
|
return -EINVAL;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
2018-02-06 15:59:55 +01:00
|
|
|
|
p = procfs_file_alloca(pid, "stat");
|
|
|
|
|
|
r = read_one_line_file(p, &line);
|
|
|
|
|
|
if (r == -ENOENT)
|
|
|
|
|
|
return -ESRCH;
|
|
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
2018-02-06 15:59:55 +01:00
|
|
|
|
/* Skip past the comm field */
|
|
|
|
|
|
q = strrchr(line, ')');
|
|
|
|
|
|
if (!q)
|
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
q++;
|
|
|
|
|
|
|
|
|
|
|
|
/* Skip 6 fields to reach the flags field */
|
|
|
|
|
|
for (i = 0; i < 6; i++) {
|
|
|
|
|
|
l = strspn(q, WHITESPACE);
|
|
|
|
|
|
if (l < 1)
|
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
q += l;
|
|
|
|
|
|
|
|
|
|
|
|
l = strcspn(q, WHITESPACE);
|
|
|
|
|
|
if (l < 1)
|
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
q += l;
|
2015-07-23 23:44:40 +02:00
|
|
|
|
}
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
2018-09-30 21:20:08 +02:00
|
|
|
|
/* Skip preceding whitespace */
|
2018-02-06 15:59:55 +01:00
|
|
|
|
l = strspn(q, WHITESPACE);
|
|
|
|
|
|
if (l < 1)
|
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
q += l;
|
2017-12-11 20:01:55 +01:00
|
|
|
|
|
2018-02-06 15:59:55 +01:00
|
|
|
|
/* Truncate the rest */
|
|
|
|
|
|
l = strcspn(q, WHITESPACE);
|
|
|
|
|
|
if (l < 1)
|
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
q[l] = 0;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
2018-02-06 15:59:55 +01:00
|
|
|
|
r = safe_atollu(q, &flags);
|
|
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
2018-02-06 15:59:55 +01:00
|
|
|
|
return !!(flags & PF_KTHREAD);
|
2015-04-10 19:10:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-10-16 23:18:05 +02:00
|
|
|
|
int pidref_is_kernel_thread(const PidRef *pid) {
|
|
|
|
|
|
int result, r;
|
|
|
|
|
|
|
|
|
|
|
|
if (!pidref_is_set(pid))
|
|
|
|
|
|
return -ESRCH;
|
|
|
|
|
|
|
|
|
|
|
|
result = pid_is_kernel_thread(pid->pid);
|
|
|
|
|
|
if (result < 0)
|
|
|
|
|
|
return result;
|
|
|
|
|
|
|
|
|
|
|
|
r = pidref_verify(pid); /* Verify that the PID wasn't reused since */
|
|
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-01 08:50:08 +09:00
|
|
|
|
int get_process_capeff(pid_t pid, char **ret) {
|
2015-04-10 19:10:00 +02:00
|
|
|
|
const char *p;
|
2015-07-23 23:44:40 +02:00
|
|
|
|
int r;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
|
|
|
|
|
assert(pid >= 0);
|
2021-11-01 08:50:08 +09:00
|
|
|
|
assert(ret);
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
|
|
|
|
|
p = procfs_file_alloca(pid, "status");
|
|
|
|
|
|
|
2021-11-01 08:50:08 +09:00
|
|
|
|
r = get_proc_field(p, "CapEff", WHITESPACE, ret);
|
2015-07-23 23:44:40 +02:00
|
|
|
|
if (r == -ENOENT)
|
|
|
|
|
|
return -ESRCH;
|
|
|
|
|
|
|
|
|
|
|
|
return r;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-04-01 10:43:49 +02:00
|
|
|
|
static int get_process_link_contents(pid_t pid, const char *proc_file, char **ret) {
|
|
|
|
|
|
const char *p;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
|
|
assert(proc_file);
|
|
|
|
|
|
|
2022-04-01 10:43:49 +02:00
|
|
|
|
p = procfs_file_alloca(pid, proc_file);
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
2022-04-01 10:43:49 +02:00
|
|
|
|
r = readlink_malloc(p, ret);
|
|
|
|
|
|
return r == -ENOENT ? -ESRCH : r;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-01 08:50:08 +09:00
|
|
|
|
int get_process_exe(pid_t pid, char **ret) {
|
2015-04-10 19:10:00 +02:00
|
|
|
|
char *d;
|
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
|
|
assert(pid >= 0);
|
|
|
|
|
|
|
2022-04-01 10:43:49 +02:00
|
|
|
|
r = get_process_link_contents(pid, "exe", ret);
|
2015-04-10 19:10:00 +02:00
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
|
|
|
|
|
|
2022-04-01 10:43:49 +02:00
|
|
|
|
if (ret) {
|
|
|
|
|
|
d = endswith(*ret, " (deleted)");
|
|
|
|
|
|
if (d)
|
|
|
|
|
|
*d = '\0';
|
|
|
|
|
|
}
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-01 08:50:08 +09:00
|
|
|
|
static int get_process_id(pid_t pid, const char *field, uid_t *ret) {
|
2015-04-10 19:10:00 +02:00
|
|
|
|
_cleanup_fclose_ FILE *f = NULL;
|
|
|
|
|
|
const char *p;
|
2018-10-18 16:08:51 +02:00
|
|
|
|
int r;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
|
|
|
|
|
assert(field);
|
2021-11-01 08:50:08 +09:00
|
|
|
|
assert(ret);
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
2017-10-03 12:05:24 +01:00
|
|
|
|
if (pid < 0)
|
2017-07-17 23:35:25 +02:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
2015-04-10 19:10:00 +02:00
|
|
|
|
p = procfs_file_alloca(pid, "status");
|
2019-04-04 10:17:16 +02:00
|
|
|
|
r = fopen_unlocked(p, "re", &f);
|
|
|
|
|
|
if (r == -ENOENT)
|
|
|
|
|
|
return -ESRCH;
|
|
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
2017-12-11 20:01:55 +01:00
|
|
|
|
|
2018-10-18 16:08:51 +02:00
|
|
|
|
for (;;) {
|
|
|
|
|
|
_cleanup_free_ char *line = NULL;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
char *l;
|
|
|
|
|
|
|
2023-10-16 18:01:00 +02:00
|
|
|
|
r = read_stripped_line(f, LONG_LINE_MAX, &line);
|
2018-10-18 16:08:51 +02:00
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
|
|
|
|
|
if (r == 0)
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
2023-10-16 18:01:00 +02:00
|
|
|
|
l = startswith(line, field);
|
|
|
|
|
|
if (l) {
|
2015-04-10 19:10:00 +02:00
|
|
|
|
l += strspn(l, WHITESPACE);
|
|
|
|
|
|
|
|
|
|
|
|
l[strcspn(l, WHITESPACE)] = 0;
|
|
|
|
|
|
|
2021-11-01 08:50:08 +09:00
|
|
|
|
return parse_uid(l, ret);
|
2015-04-10 19:10:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return -EIO;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-10-17 11:27:06 +02:00
|
|
|
|
int pid_get_uid(pid_t pid, uid_t *ret) {
|
|
|
|
|
|
assert(ret);
|
2017-07-17 23:35:25 +02:00
|
|
|
|
|
|
|
|
|
|
if (pid == 0 || pid == getpid_cached()) {
|
2021-11-01 08:50:08 +09:00
|
|
|
|
*ret = getuid();
|
2017-07-17 23:35:25 +02:00
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-01 08:50:08 +09:00
|
|
|
|
return get_process_id(pid, "Uid:", ret);
|
2015-04-10 19:10:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-10-17 11:27:06 +02:00
|
|
|
|
int pidref_get_uid(const PidRef *pid, uid_t *ret) {
|
|
|
|
|
|
uid_t uid;
|
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
|
|
if (!pidref_is_set(pid))
|
|
|
|
|
|
return -ESRCH;
|
|
|
|
|
|
|
|
|
|
|
|
r = pid_get_uid(pid->pid, &uid);
|
|
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
|
|
r = pidref_verify(pid);
|
|
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
|
|
*ret = uid;
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-01 08:50:08 +09:00
|
|
|
|
int get_process_gid(pid_t pid, gid_t *ret) {
|
2017-07-17 23:35:25 +02:00
|
|
|
|
|
|
|
|
|
|
if (pid == 0 || pid == getpid_cached()) {
|
2021-11-01 08:50:08 +09:00
|
|
|
|
*ret = getgid();
|
2017-07-17 23:35:25 +02:00
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-04-10 19:10:00 +02:00
|
|
|
|
assert_cc(sizeof(uid_t) == sizeof(gid_t));
|
2021-11-01 08:50:08 +09:00
|
|
|
|
return get_process_id(pid, "Gid:", ret);
|
2015-04-10 19:10:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-01 08:50:08 +09:00
|
|
|
|
int get_process_cwd(pid_t pid, char **ret) {
|
2015-04-10 19:10:00 +02:00
|
|
|
|
assert(pid >= 0);
|
|
|
|
|
|
|
2019-11-25 14:55:50 +01:00
|
|
|
|
if (pid == 0 || pid == getpid_cached())
|
2021-11-01 08:50:08 +09:00
|
|
|
|
return safe_getcwd(ret);
|
2019-11-25 14:55:50 +01:00
|
|
|
|
|
2022-04-01 10:43:49 +02:00
|
|
|
|
return get_process_link_contents(pid, "cwd", ret);
|
2015-04-10 19:10:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-01 08:50:08 +09:00
|
|
|
|
int get_process_root(pid_t pid, char **ret) {
|
2015-04-10 19:10:00 +02:00
|
|
|
|
assert(pid >= 0);
|
2022-04-01 10:43:49 +02:00
|
|
|
|
return get_process_link_contents(pid, "root", ret);
|
2015-04-10 19:10:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-12-17 11:23:15 +01:00
|
|
|
|
#define ENVIRONMENT_BLOCK_MAX (5U*1024U*1024U)
|
|
|
|
|
|
|
2021-11-01 08:50:08 +09:00
|
|
|
|
int get_process_environ(pid_t pid, char **ret) {
|
2015-04-10 19:10:00 +02:00
|
|
|
|
_cleanup_fclose_ FILE *f = NULL;
|
|
|
|
|
|
_cleanup_free_ char *outcome = NULL;
|
2021-05-18 23:01:32 +02:00
|
|
|
|
size_t sz = 0;
|
2018-12-17 11:23:15 +01:00
|
|
|
|
const char *p;
|
|
|
|
|
|
int r;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
|
|
|
|
|
assert(pid >= 0);
|
2021-11-01 08:50:08 +09:00
|
|
|
|
assert(ret);
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
|
|
|
|
|
p = procfs_file_alloca(pid, "environ");
|
|
|
|
|
|
|
2019-04-04 10:17:16 +02:00
|
|
|
|
r = fopen_unlocked(p, "re", &f);
|
|
|
|
|
|
if (r == -ENOENT)
|
|
|
|
|
|
return -ESRCH;
|
|
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
2017-12-11 20:01:55 +01:00
|
|
|
|
|
2018-12-17 11:23:15 +01:00
|
|
|
|
for (;;) {
|
|
|
|
|
|
char c;
|
|
|
|
|
|
|
|
|
|
|
|
if (sz >= ENVIRONMENT_BLOCK_MAX)
|
|
|
|
|
|
return -ENOBUFS;
|
|
|
|
|
|
|
2021-05-18 23:01:32 +02:00
|
|
|
|
if (!GREEDY_REALLOC(outcome, sz + 5))
|
2015-04-10 19:10:00 +02:00
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
2018-12-17 11:23:15 +01:00
|
|
|
|
r = safe_fgetc(f, &c);
|
|
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
|
|
|
|
|
if (r == 0)
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
2015-04-10 19:10:00 +02:00
|
|
|
|
if (c == '\0')
|
|
|
|
|
|
outcome[sz++] = '\n';
|
|
|
|
|
|
else
|
|
|
|
|
|
sz += cescape_char(c, outcome + sz);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-12-17 11:23:15 +01:00
|
|
|
|
outcome[sz] = '\0';
|
2021-11-01 08:50:08 +09:00
|
|
|
|
*ret = TAKE_PTR(outcome);
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-07-07 15:57:51 +02:00
|
|
|
|
int get_process_ppid(pid_t pid, pid_t *ret) {
|
2015-04-10 19:10:00 +02:00
|
|
|
|
_cleanup_free_ char *line = NULL;
|
2022-02-10 17:19:27 +01:00
|
|
|
|
unsigned long ppid;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
const char *p;
|
2021-07-07 15:57:51 +02:00
|
|
|
|
int r;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
|
|
|
|
|
assert(pid >= 0);
|
|
|
|
|
|
|
2017-07-17 23:35:25 +02:00
|
|
|
|
if (pid == 0 || pid == getpid_cached()) {
|
2021-07-07 15:57:51 +02:00
|
|
|
|
if (ret)
|
|
|
|
|
|
*ret = getppid();
|
2015-04-10 19:10:00 +02:00
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-07-07 15:57:51 +02:00
|
|
|
|
if (pid == 1) /* PID 1 has no parent, shortcut this case */
|
|
|
|
|
|
return -EADDRNOTAVAIL;
|
|
|
|
|
|
|
2015-04-10 19:10:00 +02:00
|
|
|
|
p = procfs_file_alloca(pid, "stat");
|
|
|
|
|
|
r = read_one_line_file(p, &line);
|
2015-07-23 23:44:40 +02:00
|
|
|
|
if (r == -ENOENT)
|
|
|
|
|
|
return -ESRCH;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
|
|
|
|
|
|
2021-07-07 15:57:51 +02:00
|
|
|
|
/* Let's skip the pid and comm fields. The latter is enclosed in () but does not escape any () in its
|
|
|
|
|
|
* value, so let's skip over it manually */
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
|
|
|
|
|
p = strrchr(line, ')');
|
|
|
|
|
|
if (!p)
|
|
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
|
|
|
|
p++;
|
|
|
|
|
|
|
|
|
|
|
|
if (sscanf(p, " "
|
|
|
|
|
|
"%*c " /* state */
|
|
|
|
|
|
"%lu ", /* ppid */
|
|
|
|
|
|
&ppid) != 1)
|
|
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
2021-07-07 15:57:51 +02:00
|
|
|
|
/* If ppid is zero the process has no parent. Which might be the case for PID 1 but also for
|
|
|
|
|
|
* processes originating in other namespaces that are inserted into a pidns. Return a recognizable
|
|
|
|
|
|
* error in this case. */
|
|
|
|
|
|
if (ppid == 0)
|
|
|
|
|
|
return -EADDRNOTAVAIL;
|
|
|
|
|
|
|
2022-02-10 17:19:27 +01:00
|
|
|
|
if ((pid_t) ppid < 0 || (unsigned long) (pid_t) ppid != ppid)
|
2015-04-10 19:10:00 +02:00
|
|
|
|
return -ERANGE;
|
|
|
|
|
|
|
2021-07-07 15:57:51 +02:00
|
|
|
|
if (ret)
|
|
|
|
|
|
*ret = (pid_t) ppid;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-23 18:09:30 +01:00
|
|
|
|
int pid_get_start_time(pid_t pid, uint64_t *ret) {
|
|
|
|
|
|
_cleanup_free_ char *line = NULL;
|
|
|
|
|
|
const char *p;
|
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
|
|
assert(pid >= 0);
|
|
|
|
|
|
|
|
|
|
|
|
p = procfs_file_alloca(pid, "stat");
|
|
|
|
|
|
r = read_one_line_file(p, &line);
|
|
|
|
|
|
if (r == -ENOENT)
|
|
|
|
|
|
return -ESRCH;
|
|
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
|
|
/* Let's skip the pid and comm fields. The latter is enclosed in () but does not escape any () in its
|
|
|
|
|
|
* value, so let's skip over it manually */
|
|
|
|
|
|
|
|
|
|
|
|
p = strrchr(line, ')');
|
|
|
|
|
|
if (!p)
|
|
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
|
|
|
|
p++;
|
|
|
|
|
|
|
|
|
|
|
|
unsigned long llu;
|
|
|
|
|
|
|
|
|
|
|
|
if (sscanf(p, " "
|
|
|
|
|
|
"%*c " /* state */
|
|
|
|
|
|
"%*u " /* ppid */
|
|
|
|
|
|
"%*u " /* pgrp */
|
|
|
|
|
|
"%*u " /* session */
|
|
|
|
|
|
"%*u " /* tty_nr */
|
|
|
|
|
|
"%*u " /* tpgid */
|
|
|
|
|
|
"%*u " /* flags */
|
|
|
|
|
|
"%*u " /* minflt */
|
|
|
|
|
|
"%*u " /* cminflt */
|
|
|
|
|
|
"%*u " /* majflt */
|
|
|
|
|
|
"%*u " /* cmajflt */
|
|
|
|
|
|
"%*u " /* utime */
|
|
|
|
|
|
"%*u " /* stime */
|
|
|
|
|
|
"%*u " /* cutime */
|
|
|
|
|
|
"%*u " /* cstime */
|
|
|
|
|
|
"%*i " /* priority */
|
|
|
|
|
|
"%*i " /* nice */
|
|
|
|
|
|
"%*u " /* num_threads */
|
|
|
|
|
|
"%*u " /* itrealvalue */
|
|
|
|
|
|
"%lu ", /* starttime */
|
|
|
|
|
|
&llu) != 1)
|
|
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
|
|
*ret = llu;
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int pidref_get_start_time(const PidRef *pid, uint64_t *ret) {
|
|
|
|
|
|
uint64_t t;
|
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
|
|
if (!pidref_is_set(pid))
|
|
|
|
|
|
return -ESRCH;
|
|
|
|
|
|
|
|
|
|
|
|
r = pid_get_start_time(pid->pid, ret ? &t : NULL);
|
|
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
|
|
r = pidref_verify(pid);
|
|
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
|
|
*ret = t;
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-01 08:50:08 +09:00
|
|
|
|
int get_process_umask(pid_t pid, mode_t *ret) {
|
2020-04-03 10:00:25 +02:00
|
|
|
|
_cleanup_free_ char *m = NULL;
|
|
|
|
|
|
const char *p;
|
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
|
|
assert(pid >= 0);
|
2021-11-01 08:50:08 +09:00
|
|
|
|
assert(ret);
|
2020-04-03 10:00:25 +02:00
|
|
|
|
|
|
|
|
|
|
p = procfs_file_alloca(pid, "status");
|
|
|
|
|
|
|
|
|
|
|
|
r = get_proc_field(p, "Umask", WHITESPACE, &m);
|
|
|
|
|
|
if (r == -ENOENT)
|
|
|
|
|
|
return -ESRCH;
|
2023-02-08 18:02:27 +01:00
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
2020-04-03 10:00:25 +02:00
|
|
|
|
|
2021-11-01 08:50:08 +09:00
|
|
|
|
return parse_mode(m, ret);
|
2020-04-03 10:00:25 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2015-04-10 19:10:00 +02:00
|
|
|
|
int wait_for_terminate(pid_t pid, siginfo_t *status) {
|
|
|
|
|
|
siginfo_t dummy;
|
|
|
|
|
|
|
|
|
|
|
|
assert(pid >= 1);
|
|
|
|
|
|
|
|
|
|
|
|
if (!status)
|
|
|
|
|
|
status = &dummy;
|
|
|
|
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
|
|
zero(*status);
|
|
|
|
|
|
|
|
|
|
|
|
if (waitid(P_PID, pid, status, WEXITED) < 0) {
|
|
|
|
|
|
|
|
|
|
|
|
if (errno == EINTR)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
2016-06-21 13:20:02 +02:00
|
|
|
|
return negative_errno();
|
2015-04-10 19:10:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* Return values:
|
|
|
|
|
|
* < 0 : wait_for_terminate() failed to get the state of the
|
|
|
|
|
|
* process, the process was terminated by a signal, or
|
|
|
|
|
|
* failed for an unknown reason.
|
|
|
|
|
|
* >=0 : The process terminated normally, and its exit code is
|
|
|
|
|
|
* returned.
|
|
|
|
|
|
*
|
|
|
|
|
|
* That is, success is indicated by a return value of zero, and an
|
|
|
|
|
|
* error is indicated by a non-zero value.
|
|
|
|
|
|
*
|
|
|
|
|
|
* A warning is emitted if the process terminates abnormally,
|
|
|
|
|
|
* and also if it returns non-zero unless check_exit_code is true.
|
|
|
|
|
|
*/
|
2017-12-28 00:51:19 +01:00
|
|
|
|
int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags) {
|
|
|
|
|
|
_cleanup_free_ char *buffer = NULL;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
siginfo_t status;
|
2017-12-28 00:51:19 +01:00
|
|
|
|
int r, prio;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
|
|
|
|
|
assert(pid > 1);
|
|
|
|
|
|
|
2017-12-28 00:51:19 +01:00
|
|
|
|
if (!name) {
|
2023-10-17 10:16:31 +02:00
|
|
|
|
r = pid_get_comm(pid, &buffer);
|
2017-12-28 00:51:19 +01:00
|
|
|
|
if (r < 0)
|
|
|
|
|
|
log_debug_errno(r, "Failed to acquire process name of " PID_FMT ", ignoring: %m", pid);
|
|
|
|
|
|
else
|
|
|
|
|
|
name = buffer;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
prio = flags & WAIT_LOG_ABNORMAL ? LOG_ERR : LOG_DEBUG;
|
|
|
|
|
|
|
2015-04-10 19:10:00 +02:00
|
|
|
|
r = wait_for_terminate(pid, &status);
|
|
|
|
|
|
if (r < 0)
|
2017-12-28 00:51:19 +01:00
|
|
|
|
return log_full_errno(prio, r, "Failed to wait for %s: %m", strna(name));
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
|
|
|
|
|
if (status.si_code == CLD_EXITED) {
|
2017-12-28 00:51:19 +01:00
|
|
|
|
if (status.si_status != EXIT_SUCCESS)
|
|
|
|
|
|
log_full(flags & WAIT_LOG_NON_ZERO_EXIT_STATUS ? LOG_ERR : LOG_DEBUG,
|
|
|
|
|
|
"%s failed with exit status %i.", strna(name), status.si_status);
|
2015-04-10 19:10:00 +02:00
|
|
|
|
else
|
|
|
|
|
|
log_debug("%s succeeded.", name);
|
|
|
|
|
|
|
|
|
|
|
|
return status.si_status;
|
2017-12-28 00:51:19 +01:00
|
|
|
|
|
2017-09-29 00:37:23 +02:00
|
|
|
|
} else if (IN_SET(status.si_code, CLD_KILLED, CLD_DUMPED)) {
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
2017-12-28 00:51:19 +01:00
|
|
|
|
log_full(prio, "%s terminated by signal %s.", strna(name), signal_to_string(status.si_status));
|
2015-04-10 19:10:00 +02:00
|
|
|
|
return -EPROTO;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-28 00:51:19 +01:00
|
|
|
|
log_full(prio, "%s failed due to unknown reason.", strna(name));
|
2015-04-10 19:10:00 +02:00
|
|
|
|
return -EPROTO;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-13 12:49:26 -05:00
|
|
|
|
/*
|
|
|
|
|
|
* Return values:
|
2018-05-16 22:14:59 -04:00
|
|
|
|
*
|
|
|
|
|
|
* < 0 : wait_for_terminate_with_timeout() failed to get the state of the process, the process timed out, the process
|
|
|
|
|
|
* was terminated by a signal, or failed for an unknown reason.
|
|
|
|
|
|
*
|
2017-12-13 12:49:26 -05:00
|
|
|
|
* >=0 : The process terminated normally with no failures.
|
|
|
|
|
|
*
|
2018-05-16 22:14:59 -04:00
|
|
|
|
* Success is indicated by a return value of zero, a timeout is indicated by ETIMEDOUT, and all other child failure
|
|
|
|
|
|
* states are indicated by error is indicated by a non-zero value.
|
|
|
|
|
|
*
|
|
|
|
|
|
* This call assumes SIGCHLD has been blocked already, in particular before the child to wait for has been forked off
|
|
|
|
|
|
* to remain entirely race-free.
|
2017-12-13 12:49:26 -05:00
|
|
|
|
*/
|
|
|
|
|
|
int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout) {
|
|
|
|
|
|
sigset_t mask;
|
|
|
|
|
|
int r;
|
|
|
|
|
|
usec_t until;
|
|
|
|
|
|
|
|
|
|
|
|
assert_se(sigemptyset(&mask) == 0);
|
|
|
|
|
|
assert_se(sigaddset(&mask, SIGCHLD) == 0);
|
|
|
|
|
|
|
|
|
|
|
|
/* Drop into a sigtimewait-based timeout. Waiting for the
|
|
|
|
|
|
* pid to exit. */
|
2021-03-03 12:56:52 +09:00
|
|
|
|
until = usec_add(now(CLOCK_MONOTONIC), timeout);
|
2017-12-13 12:49:26 -05:00
|
|
|
|
for (;;) {
|
|
|
|
|
|
usec_t n;
|
|
|
|
|
|
siginfo_t status = {};
|
|
|
|
|
|
|
|
|
|
|
|
n = now(CLOCK_MONOTONIC);
|
|
|
|
|
|
if (n >= until)
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
2022-03-18 14:19:20 +01:00
|
|
|
|
r = RET_NERRNO(sigtimedwait(&mask, NULL, TIMESPEC_STORE(until - n)));
|
2017-12-13 12:49:26 -05:00
|
|
|
|
/* Assuming we woke due to the child exiting. */
|
|
|
|
|
|
if (waitid(P_PID, pid, &status, WEXITED|WNOHANG) == 0) {
|
|
|
|
|
|
if (status.si_pid == pid) {
|
2021-06-15 14:09:29 +09:00
|
|
|
|
/* This is the correct child. */
|
2017-12-13 12:49:26 -05:00
|
|
|
|
if (status.si_code == CLD_EXITED)
|
2022-10-10 14:50:35 +02:00
|
|
|
|
return status.si_status == 0 ? 0 : -EPROTO;
|
2017-12-13 12:49:26 -05:00
|
|
|
|
else
|
|
|
|
|
|
return -EPROTO;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Not the child, check for errors and proceed appropriately */
|
|
|
|
|
|
if (r < 0) {
|
|
|
|
|
|
switch (r) {
|
|
|
|
|
|
case -EAGAIN:
|
|
|
|
|
|
/* Timed out, child is likely hung. */
|
|
|
|
|
|
return -ETIMEDOUT;
|
|
|
|
|
|
case -EINTR:
|
|
|
|
|
|
/* Received a different signal and should retry */
|
|
|
|
|
|
continue;
|
|
|
|
|
|
default:
|
|
|
|
|
|
/* Return any unexpected errors */
|
|
|
|
|
|
return r;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return -EPROTO;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-04-29 19:23:23 +02:00
|
|
|
|
void sigkill_wait(pid_t pid) {
|
|
|
|
|
|
assert(pid > 1);
|
|
|
|
|
|
|
2021-11-03 15:54:28 +01:00
|
|
|
|
(void) kill(pid, SIGKILL);
|
|
|
|
|
|
(void) wait_for_terminate(pid, NULL);
|
2016-04-29 19:23:23 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void sigkill_waitp(pid_t *pid) {
|
2018-01-10 17:21:15 +01:00
|
|
|
|
PROTECT_ERRNO;
|
|
|
|
|
|
|
2015-10-26 01:13:11 +01:00
|
|
|
|
if (!pid)
|
|
|
|
|
|
return;
|
|
|
|
|
|
if (*pid <= 1)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
2016-04-29 19:23:23 +02:00
|
|
|
|
sigkill_wait(*pid);
|
2015-10-26 01:13:11 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-02-03 10:16:33 -08:00
|
|
|
|
void sigterm_wait(pid_t pid) {
|
|
|
|
|
|
assert(pid > 1);
|
|
|
|
|
|
|
2021-11-03 15:54:28 +01:00
|
|
|
|
(void) kill_and_sigcont(pid, SIGTERM);
|
|
|
|
|
|
(void) wait_for_terminate(pid, NULL);
|
2018-02-03 10:16:33 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-09 14:07:07 +01:00
|
|
|
|
void sigkill_nowait(pid_t pid) {
|
|
|
|
|
|
assert(pid > 1);
|
|
|
|
|
|
|
|
|
|
|
|
(void) kill(pid, SIGKILL);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void sigkill_nowaitp(pid_t *pid) {
|
|
|
|
|
|
PROTECT_ERRNO;
|
|
|
|
|
|
|
|
|
|
|
|
if (!pid)
|
|
|
|
|
|
return;
|
|
|
|
|
|
if (*pid <= 1)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
sigkill_nowait(*pid);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-04-10 19:10:00 +02:00
|
|
|
|
int kill_and_sigcont(pid_t pid, int sig) {
|
|
|
|
|
|
int r;
|
|
|
|
|
|
|
2021-11-14 22:40:49 +01:00
|
|
|
|
r = RET_NERRNO(kill(pid, sig));
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
2016-07-20 11:14:48 +02:00
|
|
|
|
/* If this worked, also send SIGCONT, unless we already just sent a SIGCONT, or SIGKILL was sent which isn't
|
|
|
|
|
|
* affected by a process being suspended anyway. */
|
2017-02-21 20:39:52 +00:00
|
|
|
|
if (r >= 0 && !IN_SET(sig, SIGCONT, SIGKILL))
|
2016-07-20 11:14:48 +02:00
|
|
|
|
(void) kill(pid, SIGCONT);
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
|
|
|
|
|
return r;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-02-14 14:27:31 +01:00
|
|
|
|
int getenv_for_pid(pid_t pid, const char *field, char **ret) {
|
2015-04-10 19:10:00 +02:00
|
|
|
|
_cleanup_fclose_ FILE *f = NULL;
|
|
|
|
|
|
char *value = NULL;
|
|
|
|
|
|
const char *path;
|
2018-12-17 12:17:36 +01:00
|
|
|
|
size_t l, sum = 0;
|
|
|
|
|
|
int r;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
|
|
|
|
|
assert(pid >= 0);
|
|
|
|
|
|
assert(field);
|
2018-02-14 14:27:31 +01:00
|
|
|
|
assert(ret);
|
|
|
|
|
|
|
|
|
|
|
|
if (pid == 0 || pid == getpid_cached()) {
|
|
|
|
|
|
const char *e;
|
|
|
|
|
|
|
|
|
|
|
|
e = getenv(field);
|
|
|
|
|
|
if (!e) {
|
|
|
|
|
|
*ret = NULL;
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
value = strdup(e);
|
|
|
|
|
|
if (!value)
|
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
|
|
*ret = value;
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
}
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
2018-12-17 12:17:36 +01:00
|
|
|
|
if (!pid_is_valid(pid))
|
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
2015-04-10 19:10:00 +02:00
|
|
|
|
path = procfs_file_alloca(pid, "environ");
|
|
|
|
|
|
|
2019-04-04 10:17:16 +02:00
|
|
|
|
r = fopen_unlocked(path, "re", &f);
|
|
|
|
|
|
if (r == -ENOENT)
|
|
|
|
|
|
return -ESRCH;
|
|
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
2017-12-11 20:01:55 +01:00
|
|
|
|
|
2015-04-10 19:10:00 +02:00
|
|
|
|
l = strlen(field);
|
2018-12-17 12:17:36 +01:00
|
|
|
|
for (;;) {
|
|
|
|
|
|
_cleanup_free_ char *line = NULL;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
2018-12-17 12:17:36 +01:00
|
|
|
|
if (sum > ENVIRONMENT_BLOCK_MAX) /* Give up searching eventually */
|
|
|
|
|
|
return -ENOBUFS;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
2018-12-17 12:17:36 +01:00
|
|
|
|
r = read_nul_string(f, LONG_LINE_MAX, &line);
|
|
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
|
|
|
|
|
if (r == 0) /* EOF */
|
|
|
|
|
|
break;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
2018-12-17 12:17:36 +01:00
|
|
|
|
sum += r;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
2016-11-17 20:57:22 -05:00
|
|
|
|
if (strneq(line, field, l) && line[l] == '=') {
|
2015-04-10 19:10:00 +02:00
|
|
|
|
value = strdup(line + l + 1);
|
|
|
|
|
|
if (!value)
|
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
2018-02-14 14:27:31 +01:00
|
|
|
|
*ret = value;
|
|
|
|
|
|
return 1;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
}
|
2018-12-17 12:17:36 +01:00
|
|
|
|
}
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
2018-02-14 14:27:31 +01:00
|
|
|
|
*ret = NULL;
|
|
|
|
|
|
return 0;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-03-18 11:48:34 +01:00
|
|
|
|
int pid_is_my_child(pid_t pid) {
|
|
|
|
|
|
pid_t ppid;
|
|
|
|
|
|
int r;
|
|
|
|
|
|
|
2023-10-17 12:20:16 +02:00
|
|
|
|
if (pid < 0)
|
|
|
|
|
|
return -ESRCH;
|
|
|
|
|
|
|
2019-03-18 11:48:34 +01:00
|
|
|
|
if (pid <= 1)
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
r = get_process_ppid(pid, &ppid);
|
|
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
|
|
return ppid == getpid_cached();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-10-17 12:20:16 +02:00
|
|
|
|
int pidref_is_my_child(const PidRef *pid) {
|
|
|
|
|
|
int r, result;
|
|
|
|
|
|
|
|
|
|
|
|
if (!pidref_is_set(pid))
|
|
|
|
|
|
return -ESRCH;
|
|
|
|
|
|
|
|
|
|
|
|
result = pid_is_my_child(pid->pid);
|
|
|
|
|
|
if (result < 0)
|
|
|
|
|
|
return result;
|
|
|
|
|
|
|
|
|
|
|
|
r = pidref_verify(pid);
|
|
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-10-17 12:32:00 +02:00
|
|
|
|
int pid_is_unwaited(pid_t pid) {
|
2015-04-10 19:10:00 +02:00
|
|
|
|
/* Checks whether a PID is still valid at all, including a zombie */
|
|
|
|
|
|
|
2017-10-03 12:05:24 +01:00
|
|
|
|
if (pid < 0)
|
2023-10-17 12:32:00 +02:00
|
|
|
|
return -ESRCH;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
2015-10-27 14:02:45 +01:00
|
|
|
|
if (pid <= 1) /* If we or PID 1 would be dead and have been waited for, this code would not be running */
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
2017-07-17 23:35:25 +02:00
|
|
|
|
if (pid == getpid_cached())
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
2015-04-10 19:10:00 +02:00
|
|
|
|
if (kill(pid, 0) >= 0)
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
|
|
return errno != ESRCH;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-10-17 12:32:00 +02:00
|
|
|
|
int pidref_is_unwaited(const PidRef *pid) {
|
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
|
|
if (!pidref_is_set(pid))
|
|
|
|
|
|
return -ESRCH;
|
|
|
|
|
|
|
|
|
|
|
|
if (pid->pid == 1 || pidref_is_self(pid))
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
|
|
r = pidref_kill(pid, 0);
|
|
|
|
|
|
if (r == -ESRCH)
|
|
|
|
|
|
return false;
|
|
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-10-17 12:12:05 +02:00
|
|
|
|
int pid_is_alive(pid_t pid) {
|
2015-04-10 19:10:00 +02:00
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
|
|
/* Checks whether a PID is still valid and not a zombie */
|
|
|
|
|
|
|
2017-10-03 12:05:24 +01:00
|
|
|
|
if (pid < 0)
|
2023-10-17 12:12:05 +02:00
|
|
|
|
return -ESRCH;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
2015-10-27 14:02:45 +01:00
|
|
|
|
if (pid <= 1) /* If we or PID 1 would be a zombie, this code would not be running */
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
2017-07-17 23:35:25 +02:00
|
|
|
|
if (pid == getpid_cached())
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
2015-04-10 19:10:00 +02:00
|
|
|
|
r = get_process_state(pid);
|
2023-10-17 12:12:05 +02:00
|
|
|
|
if (r == -ESRCH)
|
2015-04-10 19:10:00 +02:00
|
|
|
|
return false;
|
2023-10-17 12:12:05 +02:00
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
|
|
return r != 'Z';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int pidref_is_alive(const PidRef *pidref) {
|
|
|
|
|
|
int r, result;
|
|
|
|
|
|
|
|
|
|
|
|
if (!pidref_is_set(pidref))
|
|
|
|
|
|
return -ESRCH;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
2023-10-17 12:12:05 +02:00
|
|
|
|
result = pid_is_alive(pidref->pid);
|
|
|
|
|
|
if (result < 0)
|
|
|
|
|
|
return result;
|
|
|
|
|
|
|
|
|
|
|
|
r = pidref_verify(pidref);
|
|
|
|
|
|
if (r == -ESRCH)
|
|
|
|
|
|
return false;
|
|
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
|
|
return result;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
}
|
2015-10-27 00:02:45 +01:00
|
|
|
|
|
2016-01-24 16:08:36 +01:00
|
|
|
|
int pid_from_same_root_fs(pid_t pid) {
|
|
|
|
|
|
const char *root;
|
|
|
|
|
|
|
2017-10-03 12:05:24 +01:00
|
|
|
|
if (pid < 0)
|
2017-07-17 23:35:25 +02:00
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
if (pid == 0 || pid == getpid_cached())
|
|
|
|
|
|
return true;
|
2016-01-24 16:08:36 +01:00
|
|
|
|
|
|
|
|
|
|
root = procfs_file_alloca(pid, "root");
|
|
|
|
|
|
|
2023-05-19 14:47:37 +02:00
|
|
|
|
return inode_same(root, "/proc/1/root", 0);
|
2016-01-24 16:08:36 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2015-10-27 00:02:45 +01:00
|
|
|
|
bool is_main_thread(void) {
|
|
|
|
|
|
static thread_local int cached = 0;
|
|
|
|
|
|
|
|
|
|
|
|
if (_unlikely_(cached == 0))
|
2017-07-20 16:19:18 +02:00
|
|
|
|
cached = getpid_cached() == gettid() ? 1 : -1;
|
2015-10-27 00:02:45 +01:00
|
|
|
|
|
|
|
|
|
|
return cached > 0;
|
|
|
|
|
|
}
|
2015-10-27 14:24:58 +01:00
|
|
|
|
|
2021-07-28 18:22:00 +02:00
|
|
|
|
bool oom_score_adjust_is_valid(int oa) {
|
|
|
|
|
|
return oa >= OOM_SCORE_ADJ_MIN && oa <= OOM_SCORE_ADJ_MAX;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-10-27 14:24:58 +01:00
|
|
|
|
unsigned long personality_from_string(const char *p) {
|
2022-04-04 12:46:03 +02:00
|
|
|
|
Architecture architecture;
|
2015-10-27 14:24:58 +01:00
|
|
|
|
|
2016-02-22 18:29:05 +01:00
|
|
|
|
if (!p)
|
|
|
|
|
|
return PERSONALITY_INVALID;
|
|
|
|
|
|
|
2016-02-22 15:39:52 +01:00
|
|
|
|
/* Parse a personality specifier. We use our own identifiers that indicate specific ABIs, rather than just
|
|
|
|
|
|
* hints regarding the register size, since we want to keep things open for multiple locally supported ABIs for
|
|
|
|
|
|
* the same register size. */
|
|
|
|
|
|
|
|
|
|
|
|
architecture = architecture_from_string(p);
|
|
|
|
|
|
if (architecture < 0)
|
|
|
|
|
|
return PERSONALITY_INVALID;
|
2015-10-27 14:24:58 +01:00
|
|
|
|
|
2016-02-22 18:29:05 +01:00
|
|
|
|
if (architecture == native_architecture())
|
2015-10-27 14:24:58 +01:00
|
|
|
|
return PER_LINUX;
|
2021-11-23 17:49:30 +01:00
|
|
|
|
#ifdef ARCHITECTURE_SECONDARY
|
|
|
|
|
|
if (architecture == ARCHITECTURE_SECONDARY)
|
2016-02-22 15:50:35 +01:00
|
|
|
|
return PER_LINUX32;
|
2015-10-27 14:24:58 +01:00
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
return PERSONALITY_INVALID;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const char* personality_to_string(unsigned long p) {
|
2022-04-04 12:46:03 +02:00
|
|
|
|
Architecture architecture = _ARCHITECTURE_INVALID;
|
2015-10-27 14:24:58 +01:00
|
|
|
|
|
|
|
|
|
|
if (p == PER_LINUX)
|
2016-02-22 18:29:05 +01:00
|
|
|
|
architecture = native_architecture();
|
2021-11-23 17:49:30 +01:00
|
|
|
|
#ifdef ARCHITECTURE_SECONDARY
|
2016-02-22 15:39:52 +01:00
|
|
|
|
else if (p == PER_LINUX32)
|
2021-11-23 17:49:30 +01:00
|
|
|
|
architecture = ARCHITECTURE_SECONDARY;
|
2015-10-27 14:24:58 +01:00
|
|
|
|
#endif
|
|
|
|
|
|
|
2016-02-22 15:39:52 +01:00
|
|
|
|
if (architecture < 0)
|
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
|
|
return architecture_to_string(architecture);
|
2015-10-27 14:24:58 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-09-08 16:16:29 +02:00
|
|
|
|
int safe_personality(unsigned long p) {
|
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
|
|
/* So here's the deal, personality() is weirdly defined by glibc. In some cases it returns a failure via errno,
|
|
|
|
|
|
* and in others as negative return value containing an errno-like value. Let's work around this: this is a
|
|
|
|
|
|
* wrapper that uses errno if it is set, and uses the return value otherwise. And then it sets both errno and
|
|
|
|
|
|
* the return value indicating the same issue, so that we are definitely on the safe side.
|
|
|
|
|
|
*
|
|
|
|
|
|
* See https://github.com/systemd/systemd/issues/6737 */
|
|
|
|
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
|
|
ret = personality(p);
|
|
|
|
|
|
if (ret < 0) {
|
|
|
|
|
|
if (errno != 0)
|
|
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
|
|
|
|
errno = -ret;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-08-09 20:40:26 +02:00
|
|
|
|
int opinionated_personality(unsigned long *ret) {
|
|
|
|
|
|
int current;
|
|
|
|
|
|
|
|
|
|
|
|
/* Returns the current personality, or PERSONALITY_INVALID if we can't determine it. This function is a bit
|
|
|
|
|
|
* opinionated though, and ignores all the finer-grained bits and exotic personalities, only distinguishing the
|
|
|
|
|
|
* two most relevant personalities: PER_LINUX and PER_LINUX32. */
|
|
|
|
|
|
|
2017-09-08 16:16:29 +02:00
|
|
|
|
current = safe_personality(PERSONALITY_INVALID);
|
2017-08-09 20:40:26 +02:00
|
|
|
|
if (current < 0)
|
2017-09-08 16:16:29 +02:00
|
|
|
|
return current;
|
2017-08-09 20:40:26 +02:00
|
|
|
|
|
|
|
|
|
|
if (((unsigned long) current & 0xffff) == PER_LINUX32)
|
|
|
|
|
|
*ret = PER_LINUX32;
|
|
|
|
|
|
else
|
|
|
|
|
|
*ret = PER_LINUX;
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-01-19 15:48:45 +00:00
|
|
|
|
void valgrind_summary_hack(void) {
|
2017-10-03 10:41:51 +02:00
|
|
|
|
#if HAVE_VALGRIND_VALGRIND_H
|
2017-07-20 16:19:18 +02:00
|
|
|
|
if (getpid_cached() == 1 && RUNNING_ON_VALGRIND) {
|
2016-01-19 15:48:45 +00:00
|
|
|
|
pid_t pid;
|
2016-05-30 02:03:51 +02:00
|
|
|
|
pid = raw_clone(SIGCHLD);
|
2016-01-19 15:48:45 +00:00
|
|
|
|
if (pid < 0)
|
2023-08-30 10:30:42 +01:00
|
|
|
|
log_struct_errno(
|
|
|
|
|
|
LOG_EMERG, errno,
|
|
|
|
|
|
"MESSAGE_ID=" SD_MESSAGE_VALGRIND_HELPER_FORK_STR,
|
|
|
|
|
|
LOG_MESSAGE( "Failed to fork off valgrind helper: %m"));
|
2016-01-19 15:48:45 +00:00
|
|
|
|
else if (pid == 0)
|
|
|
|
|
|
exit(EXIT_SUCCESS);
|
|
|
|
|
|
else {
|
|
|
|
|
|
log_info("Spawned valgrind helper as PID "PID_FMT".", pid);
|
|
|
|
|
|
(void) wait_for_terminate(pid, NULL);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-09-18 08:39:24 +09:00
|
|
|
|
int pid_compare_func(const pid_t *a, const pid_t *b) {
|
2016-04-20 15:28:28 +02:00
|
|
|
|
/* Suitable for usage in qsort() */
|
2018-09-18 08:39:24 +09:00
|
|
|
|
return CMP(*a, *b);
|
2016-04-20 15:28:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-07-20 15:46:05 +02:00
|
|
|
|
/* The cached PID, possible values:
|
|
|
|
|
|
*
|
|
|
|
|
|
* == UNSET [0] → cache not initialized yet
|
|
|
|
|
|
* == BUSY [-1] → some thread is initializing it at the moment
|
|
|
|
|
|
* any other → the cached PID
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#define CACHED_PID_UNSET ((pid_t) 0)
|
|
|
|
|
|
#define CACHED_PID_BUSY ((pid_t) -1)
|
|
|
|
|
|
|
|
|
|
|
|
static pid_t cached_pid = CACHED_PID_UNSET;
|
|
|
|
|
|
|
2017-12-29 16:45:04 +01:00
|
|
|
|
void reset_cached_pid(void) {
|
2017-07-20 15:46:05 +02:00
|
|
|
|
/* Invoked in the child after a fork(), i.e. at the first moment the PID changed */
|
|
|
|
|
|
cached_pid = CACHED_PID_UNSET;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pid_t getpid_cached(void) {
|
2018-02-26 20:50:57 +01:00
|
|
|
|
static bool installed = false;
|
2022-07-09 23:39:51 -04:00
|
|
|
|
pid_t current_value = CACHED_PID_UNSET;
|
2017-07-20 15:46:05 +02:00
|
|
|
|
|
|
|
|
|
|
/* getpid_cached() is much like getpid(), but caches the value in local memory, to avoid having to invoke a
|
|
|
|
|
|
* system call each time. This restores glibc behaviour from before 2.24, when getpid() was unconditionally
|
|
|
|
|
|
* cached. Starting with 2.24 getpid() started to become prohibitively expensive when used for detecting when
|
|
|
|
|
|
* objects were used across fork()s. With this caching the old behaviour is somewhat restored.
|
|
|
|
|
|
*
|
|
|
|
|
|
* https://bugzilla.redhat.com/show_bug.cgi?id=1443976
|
2017-09-21 20:54:16 +02:00
|
|
|
|
* https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=c579f48edba88380635ab98cb612030e3ed8691e
|
2017-07-20 15:46:05 +02:00
|
|
|
|
*/
|
|
|
|
|
|
|
2023-07-02 11:39:00 +02:00
|
|
|
|
(void) __atomic_compare_exchange_n(
|
2022-07-09 23:39:51 -04:00
|
|
|
|
&cached_pid,
|
|
|
|
|
|
¤t_value,
|
|
|
|
|
|
CACHED_PID_BUSY,
|
|
|
|
|
|
false,
|
|
|
|
|
|
__ATOMIC_SEQ_CST,
|
|
|
|
|
|
__ATOMIC_SEQ_CST);
|
2017-07-20 15:46:05 +02:00
|
|
|
|
|
|
|
|
|
|
switch (current_value) {
|
|
|
|
|
|
|
|
|
|
|
|
case CACHED_PID_UNSET: { /* Not initialized yet, then do so now */
|
|
|
|
|
|
pid_t new_pid;
|
|
|
|
|
|
|
2018-02-07 03:10:09 +01:00
|
|
|
|
new_pid = raw_getpid();
|
2017-07-20 15:46:05 +02:00
|
|
|
|
|
2018-02-26 20:50:57 +01:00
|
|
|
|
if (!installed) {
|
|
|
|
|
|
/* __register_atfork() either returns 0 or -ENOMEM, in its glibc implementation. Since it's
|
|
|
|
|
|
* only half-documented (glibc doesn't document it but LSB does — though only superficially)
|
|
|
|
|
|
* we'll check for errors only in the most generic fashion possible. */
|
|
|
|
|
|
|
2022-02-12 06:05:54 +01:00
|
|
|
|
if (pthread_atfork(NULL, NULL, reset_cached_pid) != 0) {
|
2018-02-26 20:50:57 +01:00
|
|
|
|
/* OOM? Let's try again later */
|
|
|
|
|
|
cached_pid = CACHED_PID_UNSET;
|
|
|
|
|
|
return new_pid;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
installed = true;
|
2017-07-20 15:46:05 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
cached_pid = new_pid;
|
|
|
|
|
|
return new_pid;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
case CACHED_PID_BUSY: /* Somebody else is currently initializing */
|
2018-02-07 03:10:09 +01:00
|
|
|
|
return raw_getpid();
|
2017-07-20 15:46:05 +02:00
|
|
|
|
|
|
|
|
|
|
default: /* Properly initialized */
|
|
|
|
|
|
return current_value;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-11 23:00:57 +01:00
|
|
|
|
int must_be_root(void) {
|
|
|
|
|
|
|
|
|
|
|
|
if (geteuid() == 0)
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
2018-11-20 23:40:44 +01:00
|
|
|
|
return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Need to be root.");
|
2017-12-11 23:00:57 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-02-11 11:17:49 -05:00
|
|
|
|
static void restore_sigsetp(sigset_t **ssp) {
|
|
|
|
|
|
if (*ssp)
|
|
|
|
|
|
(void) sigprocmask(SIG_SETMASK, *ssp, NULL);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-06-22 10:27:17 +02:00
|
|
|
|
pid_t clone_with_nested_stack(int (*fn)(void *), int flags, void *userdata) {
|
|
|
|
|
|
size_t ps;
|
|
|
|
|
|
pid_t pid;
|
|
|
|
|
|
void *mystack;
|
|
|
|
|
|
|
|
|
|
|
|
/* A wrapper around glibc's clone() call that automatically sets up a "nested" stack. Only supports
|
|
|
|
|
|
* invocations without CLONE_VM, so that we can continue to use the parent's stack mapping.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Note: glibc's clone() wrapper does not synchronize malloc() locks. This means that if the parent
|
|
|
|
|
|
* is threaded these locks will be in an undefined state in the child, and hence memory allocations
|
|
|
|
|
|
* are likely going to run into deadlocks. Hence: if you use this function make sure your parent is
|
|
|
|
|
|
* strictly single-threaded or your child never calls malloc(). */
|
|
|
|
|
|
|
|
|
|
|
|
assert((flags & (CLONE_VM|CLONE_PARENT_SETTID|CLONE_CHILD_SETTID|
|
|
|
|
|
|
CLONE_CHILD_CLEARTID|CLONE_SETTLS)) == 0);
|
|
|
|
|
|
|
|
|
|
|
|
/* We allocate some space on the stack to use as the stack for the child (hence "nested"). Note that
|
|
|
|
|
|
* the net effect is that the child will have the start of its stack inside the stack of the parent,
|
|
|
|
|
|
* but since they are a CoW copy of each other that's fine. We allocate one page-aligned page. But
|
|
|
|
|
|
* since we don't want to deal with differences between systems where the stack grows backwards or
|
|
|
|
|
|
* forwards we'll allocate one more and place the stack address in the middle. Except that we also
|
|
|
|
|
|
* want it page aligned, hence we'll allocate one page more. Makes 3. */
|
|
|
|
|
|
|
|
|
|
|
|
ps = page_size();
|
|
|
|
|
|
mystack = alloca(ps*3);
|
|
|
|
|
|
mystack = (uint8_t*) mystack + ps; /* move pointer one page ahead since stacks usually grow backwards */
|
|
|
|
|
|
mystack = (void*) ALIGN_TO((uintptr_t) mystack, ps); /* align to page size (moving things further ahead) */
|
|
|
|
|
|
|
2023-07-08 16:43:28 +01:00
|
|
|
|
#if HAVE_CLONE
|
2023-06-22 10:27:17 +02:00
|
|
|
|
pid = clone(fn, mystack, flags, userdata);
|
2023-07-08 16:43:28 +01:00
|
|
|
|
#else
|
|
|
|
|
|
pid = __clone2(fn, mystack, ps, flags, userdata);
|
|
|
|
|
|
#endif
|
2023-06-22 10:27:17 +02:00
|
|
|
|
if (pid < 0)
|
|
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
|
|
|
|
return pid;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-02 11:04:36 +01:00
|
|
|
|
static int fork_flags_to_signal(ForkFlags flags) {
|
|
|
|
|
|
return (flags & FORK_DEATHSIG_SIGTERM) ? SIGTERM :
|
|
|
|
|
|
(flags & FORK_DEATHSIG_SIGINT) ? SIGINT :
|
|
|
|
|
|
SIGKILL;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-22 13:08:14 +01:00
|
|
|
|
int safe_fork_full(
|
|
|
|
|
|
const char *name,
|
2023-02-07 18:19:55 +09:00
|
|
|
|
const int stdio_fds[3],
|
2021-07-29 16:36:15 +02:00
|
|
|
|
const int except_fds[],
|
2017-12-22 13:08:14 +01:00
|
|
|
|
size_t n_except_fds,
|
|
|
|
|
|
ForkFlags flags,
|
|
|
|
|
|
pid_t *ret_pid) {
|
|
|
|
|
|
|
|
|
|
|
|
pid_t original_pid, pid;
|
2017-12-29 18:01:37 +01:00
|
|
|
|
sigset_t saved_ss, ss;
|
2021-09-15 10:56:21 +02:00
|
|
|
|
_unused_ _cleanup_(restore_sigsetp) sigset_t *saved_ssp = NULL;
|
2023-06-22 11:51:25 +02:00
|
|
|
|
bool block_signals = false, block_all = false, intermediary = false;
|
2017-12-27 21:49:19 +01:00
|
|
|
|
int prio, r;
|
2017-12-22 13:08:14 +01:00
|
|
|
|
|
2023-06-22 11:51:25 +02:00
|
|
|
|
assert(!FLAGS_SET(flags, FORK_DETACH) || !ret_pid);
|
|
|
|
|
|
assert(!FLAGS_SET(flags, FORK_DETACH|FORK_WAIT));
|
|
|
|
|
|
|
2017-12-22 13:08:14 +01:00
|
|
|
|
/* A wrapper around fork(), that does a couple of important initializations in addition to mere forking. Always
|
|
|
|
|
|
* returns the child's PID in *ret_pid. Returns == 0 in the child, and > 0 in the parent. */
|
|
|
|
|
|
|
2017-12-27 21:49:19 +01:00
|
|
|
|
prio = flags & FORK_LOG ? LOG_ERR : LOG_DEBUG;
|
|
|
|
|
|
|
2017-12-22 13:08:14 +01:00
|
|
|
|
original_pid = getpid_cached();
|
|
|
|
|
|
|
2021-03-10 21:54:59 +01:00
|
|
|
|
if (flags & FORK_FLUSH_STDIO) {
|
|
|
|
|
|
fflush(stdout);
|
|
|
|
|
|
fflush(stderr); /* This one shouldn't be necessary, stderr should be unbuffered anyway, but let's better be safe than sorry */
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-02 11:04:36 +01:00
|
|
|
|
if (flags & (FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGINT)) {
|
|
|
|
|
|
/* We temporarily block all signals, so that the new child has them blocked initially. This
|
|
|
|
|
|
* way, we can be sure that SIGTERMs are not lost we might send to the child. (Note that for
|
|
|
|
|
|
* FORK_DEATHSIG_SIGKILL we don't bother, since it cannot be blocked anyway.) */
|
2017-12-22 13:08:14 +01:00
|
|
|
|
|
2018-12-21 09:20:15 +01:00
|
|
|
|
assert_se(sigfillset(&ss) >= 0);
|
2020-02-10 17:17:02 -05:00
|
|
|
|
block_signals = block_all = true;
|
2017-12-29 18:01:37 +01:00
|
|
|
|
|
|
|
|
|
|
} else if (flags & FORK_WAIT) {
|
|
|
|
|
|
/* Let's block SIGCHLD at least, so that we can safely watch for the child process */
|
|
|
|
|
|
|
2018-12-21 09:20:15 +01:00
|
|
|
|
assert_se(sigemptyset(&ss) >= 0);
|
|
|
|
|
|
assert_se(sigaddset(&ss, SIGCHLD) >= 0);
|
2017-12-29 18:01:37 +01:00
|
|
|
|
block_signals = true;
|
2017-12-22 13:08:14 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-02-11 11:17:49 -05:00
|
|
|
|
if (block_signals) {
|
2017-12-29 18:01:37 +01:00
|
|
|
|
if (sigprocmask(SIG_SETMASK, &ss, &saved_ss) < 0)
|
|
|
|
|
|
return log_full_errno(prio, errno, "Failed to set signal mask: %m");
|
2020-02-11 11:17:49 -05:00
|
|
|
|
saved_ssp = &saved_ss;
|
|
|
|
|
|
}
|
2017-12-29 18:01:37 +01:00
|
|
|
|
|
2023-06-22 11:51:25 +02:00
|
|
|
|
if (FLAGS_SET(flags, FORK_DETACH)) {
|
|
|
|
|
|
assert(!FLAGS_SET(flags, FORK_WAIT));
|
|
|
|
|
|
assert(!ret_pid);
|
|
|
|
|
|
|
|
|
|
|
|
/* Fork off intermediary child if needed */
|
|
|
|
|
|
|
|
|
|
|
|
r = is_reaper_process();
|
|
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return log_full_errno(prio, r, "Failed to determine if we are a reaper process: %m");
|
|
|
|
|
|
|
|
|
|
|
|
if (!r) {
|
|
|
|
|
|
/* Not a reaper process, hence do a double fork() so we are reparented to one */
|
|
|
|
|
|
|
|
|
|
|
|
pid = fork();
|
|
|
|
|
|
if (pid < 0)
|
|
|
|
|
|
return log_full_errno(prio, errno, "Failed to fork off '%s': %m", strna(name));
|
|
|
|
|
|
if (pid > 0) {
|
|
|
|
|
|
log_debug("Successfully forked off intermediary '%s' as PID " PID_FMT ".", strna(name), pid);
|
|
|
|
|
|
return 1; /* return in the parent */
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
intermediary = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-04-27 17:00:07 +02:00
|
|
|
|
if ((flags & (FORK_NEW_MOUNTNS|FORK_NEW_USERNS)) != 0)
|
|
|
|
|
|
pid = raw_clone(SIGCHLD|
|
|
|
|
|
|
(FLAGS_SET(flags, FORK_NEW_MOUNTNS) ? CLONE_NEWNS : 0) |
|
|
|
|
|
|
(FLAGS_SET(flags, FORK_NEW_USERNS) ? CLONE_NEWUSER : 0));
|
2017-12-29 18:52:20 +01:00
|
|
|
|
else
|
|
|
|
|
|
pid = fork();
|
2020-02-11 11:17:49 -05:00
|
|
|
|
if (pid < 0)
|
2023-02-19 01:26:39 +09:00
|
|
|
|
return log_full_errno(prio, errno, "Failed to fork off '%s': %m", strna(name));
|
2017-12-22 13:08:14 +01:00
|
|
|
|
if (pid > 0) {
|
|
|
|
|
|
|
2023-06-22 11:51:25 +02:00
|
|
|
|
/* If we are in the intermediary process, exit now */
|
|
|
|
|
|
if (intermediary)
|
|
|
|
|
|
_exit(EXIT_SUCCESS);
|
|
|
|
|
|
|
|
|
|
|
|
/* We are in the parent process */
|
2017-12-29 18:01:37 +01:00
|
|
|
|
log_debug("Successfully forked off '%s' as PID " PID_FMT ".", strna(name), pid);
|
|
|
|
|
|
|
|
|
|
|
|
if (flags & FORK_WAIT) {
|
2020-02-10 17:17:02 -05:00
|
|
|
|
if (block_all) {
|
|
|
|
|
|
/* undo everything except SIGCHLD */
|
|
|
|
|
|
ss = saved_ss;
|
|
|
|
|
|
assert_se(sigaddset(&ss, SIGCHLD) >= 0);
|
|
|
|
|
|
(void) sigprocmask(SIG_SETMASK, &ss, NULL);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-29 18:01:37 +01:00
|
|
|
|
r = wait_for_terminate_and_check(name, pid, (flags & FORK_LOG ? WAIT_LOG : 0));
|
|
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
|
|
|
|
|
if (r != EXIT_SUCCESS) /* exit status > 0 should be treated as failure, too */
|
|
|
|
|
|
return -EPROTO;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-22 13:08:14 +01:00
|
|
|
|
if (ret_pid)
|
|
|
|
|
|
*ret_pid = pid;
|
|
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* We are in the child process */
|
|
|
|
|
|
|
2020-02-11 11:17:49 -05:00
|
|
|
|
/* Restore signal mask manually */
|
|
|
|
|
|
saved_ssp = NULL;
|
|
|
|
|
|
|
2017-12-22 13:08:14 +01:00
|
|
|
|
if (flags & FORK_REOPEN_LOG) {
|
|
|
|
|
|
/* Close the logs if requested, before we log anything. And make sure we reopen it if needed. */
|
|
|
|
|
|
log_close();
|
|
|
|
|
|
log_set_open_when_needed(true);
|
2023-03-20 13:26:57 +01:00
|
|
|
|
log_settle_target();
|
2017-12-22 13:08:14 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (name) {
|
|
|
|
|
|
r = rename_process(name);
|
|
|
|
|
|
if (r < 0)
|
2017-12-27 21:49:19 +01:00
|
|
|
|
log_full_errno(flags & FORK_LOG ? LOG_WARNING : LOG_DEBUG,
|
|
|
|
|
|
r, "Failed to rename process, ignoring: %m");
|
2017-12-22 13:08:14 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-02 11:04:36 +01:00
|
|
|
|
if (flags & (FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGINT|FORK_DEATHSIG_SIGKILL))
|
|
|
|
|
|
if (prctl(PR_SET_PDEATHSIG, fork_flags_to_signal(flags)) < 0) {
|
2017-12-27 21:49:19 +01:00
|
|
|
|
log_full_errno(prio, errno, "Failed to set death signal: %m");
|
2017-12-22 13:08:14 +01:00
|
|
|
|
_exit(EXIT_FAILURE);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (flags & FORK_RESET_SIGNALS) {
|
|
|
|
|
|
r = reset_all_signal_handlers();
|
|
|
|
|
|
if (r < 0) {
|
2017-12-27 21:49:19 +01:00
|
|
|
|
log_full_errno(prio, r, "Failed to reset signal handlers: %m");
|
2017-12-22 13:08:14 +01:00
|
|
|
|
_exit(EXIT_FAILURE);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* This implicitly undoes the signal mask stuff we did before the fork()ing above */
|
|
|
|
|
|
r = reset_signal_mask();
|
|
|
|
|
|
if (r < 0) {
|
2017-12-27 21:49:19 +01:00
|
|
|
|
log_full_errno(prio, r, "Failed to reset signal mask: %m");
|
2017-12-22 13:08:14 +01:00
|
|
|
|
_exit(EXIT_FAILURE);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if (block_signals) { /* undo what we did above */
|
|
|
|
|
|
if (sigprocmask(SIG_SETMASK, &saved_ss, NULL) < 0) {
|
2017-12-27 21:49:19 +01:00
|
|
|
|
log_full_errno(prio, errno, "Failed to restore signal mask: %m");
|
2017-12-22 13:08:14 +01:00
|
|
|
|
_exit(EXIT_FAILURE);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-02 11:04:36 +01:00
|
|
|
|
if (flags & (FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGKILL|FORK_DEATHSIG_SIGINT)) {
|
2018-01-04 03:37:15 -08:00
|
|
|
|
pid_t ppid;
|
2017-12-22 13:08:14 +01:00
|
|
|
|
/* Let's see if the parent PID is still the one we started from? If not, then the parent
|
|
|
|
|
|
* already died by the time we set PR_SET_PDEATHSIG, hence let's emulate the effect */
|
|
|
|
|
|
|
2018-01-04 03:37:15 -08:00
|
|
|
|
ppid = getppid();
|
|
|
|
|
|
if (ppid == 0)
|
2020-07-04 11:37:01 +03:00
|
|
|
|
/* Parent is in a different PID namespace. */;
|
2018-01-04 03:37:15 -08:00
|
|
|
|
else if (ppid != original_pid) {
|
2023-11-02 11:04:36 +01:00
|
|
|
|
int sig = fork_flags_to_signal(flags);
|
|
|
|
|
|
log_debug("Parent died early, raising %s.", signal_to_string(sig));
|
|
|
|
|
|
(void) raise(sig);
|
2017-12-22 13:08:14 +01:00
|
|
|
|
_exit(EXIT_FAILURE);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-04-20 15:36:20 +02:00
|
|
|
|
if (FLAGS_SET(flags, FORK_NEW_MOUNTNS | FORK_MOUNTNS_SLAVE)) {
|
2018-03-23 20:52:46 +01:00
|
|
|
|
/* Optionally, make sure we never propagate mounts to the host. */
|
|
|
|
|
|
if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) {
|
|
|
|
|
|
log_full_errno(prio, errno, "Failed to remount root directory as MS_SLAVE: %m");
|
|
|
|
|
|
_exit(EXIT_FAILURE);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-12-14 13:40:53 +01:00
|
|
|
|
if (FLAGS_SET(flags, FORK_PRIVATE_TMP)) {
|
|
|
|
|
|
assert(FLAGS_SET(flags, FORK_NEW_MOUNTNS));
|
|
|
|
|
|
|
|
|
|
|
|
/* Optionally, overmount new tmpfs instance on /tmp/. */
|
|
|
|
|
|
r = mount_nofollow("tmpfs", "/tmp", "tmpfs",
|
|
|
|
|
|
MS_NOSUID|MS_NODEV,
|
|
|
|
|
|
"mode=01777" TMPFS_LIMITS_RUN);
|
|
|
|
|
|
if (r < 0) {
|
|
|
|
|
|
log_full_errno(prio, r, "Failed to overmount /tmp/: %m");
|
|
|
|
|
|
_exit(EXIT_FAILURE);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-02-07 18:19:55 +09:00
|
|
|
|
if (flags & FORK_REARRANGE_STDIO) {
|
|
|
|
|
|
if (stdio_fds) {
|
|
|
|
|
|
r = rearrange_stdio(stdio_fds[0], stdio_fds[1], stdio_fds[2]);
|
|
|
|
|
|
if (r < 0) {
|
|
|
|
|
|
log_full_errno(prio, r, "Failed to rearrange stdio fds: %m");
|
|
|
|
|
|
_exit(EXIT_FAILURE);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
r = make_null_stdio();
|
|
|
|
|
|
if (r < 0) {
|
|
|
|
|
|
log_full_errno(prio, r, "Failed to connect stdin/stdout to /dev/null: %m");
|
|
|
|
|
|
_exit(EXIT_FAILURE);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if (flags & FORK_STDOUT_TO_STDERR) {
|
|
|
|
|
|
if (dup2(STDERR_FILENO, STDOUT_FILENO) < 0) {
|
|
|
|
|
|
log_full_errno(prio, errno, "Failed to connect stdout to stderr: %m");
|
|
|
|
|
|
_exit(EXIT_FAILURE);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-22 13:08:14 +01:00
|
|
|
|
if (flags & FORK_CLOSE_ALL_FDS) {
|
|
|
|
|
|
/* Close the logs here in case it got reopened above, as close_all_fds() would close them for us */
|
|
|
|
|
|
log_close();
|
|
|
|
|
|
|
|
|
|
|
|
r = close_all_fds(except_fds, n_except_fds);
|
|
|
|
|
|
if (r < 0) {
|
2017-12-27 21:49:19 +01:00
|
|
|
|
log_full_errno(prio, r, "Failed to close all file descriptors: %m");
|
2017-12-22 13:08:14 +01:00
|
|
|
|
_exit(EXIT_FAILURE);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-11-04 18:20:47 +01:00
|
|
|
|
if (flags & FORK_CLOEXEC_OFF) {
|
|
|
|
|
|
r = fd_cloexec_many(except_fds, n_except_fds, false);
|
|
|
|
|
|
if (r < 0) {
|
|
|
|
|
|
log_full_errno(prio, r, "Failed to turn off O_CLOEXEC on file descriptors: %m");
|
|
|
|
|
|
_exit(EXIT_FAILURE);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-22 13:08:14 +01:00
|
|
|
|
/* When we were asked to reopen the logs, do so again now */
|
|
|
|
|
|
if (flags & FORK_REOPEN_LOG) {
|
|
|
|
|
|
log_open();
|
|
|
|
|
|
log_set_open_when_needed(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-11-26 15:59:17 +01:00
|
|
|
|
if (flags & FORK_RLIMIT_NOFILE_SAFE) {
|
|
|
|
|
|
r = rlimit_nofile_safe();
|
|
|
|
|
|
if (r < 0) {
|
|
|
|
|
|
log_full_errno(prio, r, "Failed to lower RLIMIT_NOFILE's soft limit to 1K: %m");
|
|
|
|
|
|
_exit(EXIT_FAILURE);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-04-17 02:09:38 +09:00
|
|
|
|
if (!FLAGS_SET(flags, FORK_KEEP_NOTIFY_SOCKET)) {
|
|
|
|
|
|
r = RET_NERRNO(unsetenv("NOTIFY_SOCKET"));
|
|
|
|
|
|
if (r < 0) {
|
|
|
|
|
|
log_full_errno(prio, r, "Failed to unset $NOTIFY_SOCKET: %m");
|
|
|
|
|
|
_exit(EXIT_FAILURE);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-22 13:08:14 +01:00
|
|
|
|
if (ret_pid)
|
|
|
|
|
|
*ret_pid = getpid_cached();
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-23 18:07:44 +01:00
|
|
|
|
int pidref_safe_fork_full(
|
|
|
|
|
|
const char *name,
|
|
|
|
|
|
const int stdio_fds[3],
|
|
|
|
|
|
const int except_fds[],
|
|
|
|
|
|
size_t n_except_fds,
|
|
|
|
|
|
ForkFlags flags,
|
|
|
|
|
|
PidRef *ret_pid) {
|
|
|
|
|
|
|
|
|
|
|
|
pid_t pid;
|
|
|
|
|
|
int r, q;
|
|
|
|
|
|
|
|
|
|
|
|
assert(!FLAGS_SET(flags, FORK_WAIT));
|
|
|
|
|
|
|
|
|
|
|
|
r = safe_fork_full(name, stdio_fds, except_fds, n_except_fds, flags, &pid);
|
|
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
|
|
q = pidref_set_pid(ret_pid, pid);
|
|
|
|
|
|
if (q < 0) /* Let's not fail for this, no matter what, the process exists after all, and that's key */
|
|
|
|
|
|
*ret_pid = PIDREF_MAKE_FROM_PID(pid);
|
|
|
|
|
|
|
|
|
|
|
|
return r;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-11-12 23:37:13 +01:00
|
|
|
|
int namespace_fork(
|
|
|
|
|
|
const char *outer_name,
|
|
|
|
|
|
const char *inner_name,
|
2021-07-29 16:36:15 +02:00
|
|
|
|
const int except_fds[],
|
2018-11-12 23:37:13 +01:00
|
|
|
|
size_t n_except_fds,
|
|
|
|
|
|
ForkFlags flags,
|
|
|
|
|
|
int pidns_fd,
|
|
|
|
|
|
int mntns_fd,
|
|
|
|
|
|
int netns_fd,
|
|
|
|
|
|
int userns_fd,
|
|
|
|
|
|
int root_fd,
|
|
|
|
|
|
pid_t *ret_pid) {
|
|
|
|
|
|
|
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
|
|
/* This is much like safe_fork(), but forks twice, and joins the specified namespaces in the middle
|
|
|
|
|
|
* process. This ensures that we are fully a member of the destination namespace, with pidns an all, so that
|
|
|
|
|
|
* /proc/self/fd works correctly. */
|
|
|
|
|
|
|
2023-02-07 18:19:55 +09:00
|
|
|
|
r = safe_fork_full(outer_name,
|
|
|
|
|
|
NULL,
|
|
|
|
|
|
except_fds, n_except_fds,
|
2023-11-02 11:04:36 +01:00
|
|
|
|
(flags|FORK_DEATHSIG_SIGINT|FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGKILL) & ~(FORK_REOPEN_LOG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE), ret_pid);
|
2018-11-12 23:37:13 +01:00
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
|
|
|
|
|
if (r == 0) {
|
|
|
|
|
|
pid_t pid;
|
|
|
|
|
|
|
|
|
|
|
|
/* Child */
|
|
|
|
|
|
|
|
|
|
|
|
r = namespace_enter(pidns_fd, mntns_fd, netns_fd, userns_fd, root_fd);
|
|
|
|
|
|
if (r < 0) {
|
|
|
|
|
|
log_full_errno(FLAGS_SET(flags, FORK_LOG) ? LOG_ERR : LOG_DEBUG, r, "Failed to join namespace: %m");
|
|
|
|
|
|
_exit(EXIT_FAILURE);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* We mask a few flags here that either make no sense for the grandchild, or that we don't have to do again */
|
2023-02-07 18:19:55 +09:00
|
|
|
|
r = safe_fork_full(inner_name,
|
|
|
|
|
|
NULL,
|
|
|
|
|
|
except_fds, n_except_fds,
|
|
|
|
|
|
flags & ~(FORK_WAIT|FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_REARRANGE_STDIO), &pid);
|
2018-11-12 23:37:13 +01:00
|
|
|
|
if (r < 0)
|
|
|
|
|
|
_exit(EXIT_FAILURE);
|
|
|
|
|
|
if (r == 0) {
|
|
|
|
|
|
/* Child */
|
|
|
|
|
|
if (ret_pid)
|
|
|
|
|
|
*ret_pid = pid;
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
r = wait_for_terminate_and_check(inner_name, pid, FLAGS_SET(flags, FORK_LOG) ? WAIT_LOG : 0);
|
|
|
|
|
|
if (r < 0)
|
|
|
|
|
|
_exit(EXIT_FAILURE);
|
|
|
|
|
|
|
|
|
|
|
|
_exit(r);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-05-07 20:44:41 +02:00
|
|
|
|
int set_oom_score_adjust(int value) {
|
|
|
|
|
|
char t[DECIMAL_STR_MAX(int)];
|
|
|
|
|
|
|
2021-07-23 11:06:26 +02:00
|
|
|
|
xsprintf(t, "%i", value);
|
2018-05-07 20:44:41 +02:00
|
|
|
|
|
|
|
|
|
|
return write_string_file("/proc/self/oom_score_adj", t,
|
|
|
|
|
|
WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_DISABLE_BUFFER);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-09-30 11:19:11 +02:00
|
|
|
|
int get_oom_score_adjust(int *ret) {
|
2021-11-23 16:54:19 +01:00
|
|
|
|
_cleanup_free_ char *t = NULL;
|
2021-09-30 11:19:11 +02:00
|
|
|
|
int r, a;
|
|
|
|
|
|
|
|
|
|
|
|
r = read_virtual_file("/proc/self/oom_score_adj", SIZE_MAX, &t, NULL);
|
|
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
|
|
delete_trailing_chars(t, WHITESPACE);
|
|
|
|
|
|
|
|
|
|
|
|
assert_se(safe_atoi(t, &a) >= 0);
|
|
|
|
|
|
assert_se(oom_score_adjust_is_valid(a));
|
|
|
|
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
|
|
*ret = a;
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-10-30 16:35:48 +01:00
|
|
|
|
int pidfd_get_pid(int fd, pid_t *ret) {
|
|
|
|
|
|
char path[STRLEN("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
|
|
|
|
|
|
_cleanup_free_ char *fdinfo = NULL;
|
|
|
|
|
|
char *p;
|
|
|
|
|
|
int r;
|
|
|
|
|
|
|
2023-04-14 17:47:43 +02:00
|
|
|
|
/* 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
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2019-10-30 16:35:48 +01:00
|
|
|
|
if (fd < 0)
|
|
|
|
|
|
return -EBADF;
|
|
|
|
|
|
|
|
|
|
|
|
xsprintf(path, "/proc/self/fdinfo/%i", fd);
|
|
|
|
|
|
|
2021-03-17 18:43:42 +01:00
|
|
|
|
r = read_full_virtual_file(path, &fdinfo, NULL);
|
2019-10-30 16:35:48 +01:00
|
|
|
|
if (r == -ENOENT) /* if fdinfo doesn't exist we assume the process does not exist */
|
2023-04-14 17:47:43 +02:00
|
|
|
|
return proc_mounted() > 0 ? -EBADF : -ENOSYS;
|
2019-10-30 16:35:48 +01:00
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
|
|
|
|
|
|
2023-03-24 18:25:13 +01:00
|
|
|
|
p = find_line_startswith(fdinfo, "Pid:");
|
|
|
|
|
|
if (!p)
|
|
|
|
|
|
return -ENOTTY; /* not a pidfd? */
|
2019-10-30 16:35:48 +01:00
|
|
|
|
|
|
|
|
|
|
p += strspn(p, WHITESPACE);
|
|
|
|
|
|
p[strcspn(p, WHITESPACE)] = 0;
|
|
|
|
|
|
|
2023-04-14 17:47:43 +02:00
|
|
|
|
if (streq(p, "0"))
|
|
|
|
|
|
return -EREMOTE; /* PID is in foreign PID namespace? */
|
|
|
|
|
|
if (streq(p, "-1"))
|
|
|
|
|
|
return -ESRCH; /* refers to reaped process? */
|
|
|
|
|
|
|
2019-10-30 16:35:48 +01:00
|
|
|
|
return parse_pid(p, ret);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-20 12:13:22 +00:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-08-01 17:38:05 +01:00
|
|
|
|
static int rlimit_to_nice(rlim_t limit) {
|
|
|
|
|
|
if (limit <= 1)
|
|
|
|
|
|
return PRIO_MAX-1; /* i.e. 19 */
|
|
|
|
|
|
|
|
|
|
|
|
if (limit >= -PRIO_MIN + PRIO_MAX)
|
|
|
|
|
|
return PRIO_MIN; /* i.e. -20 */
|
|
|
|
|
|
|
|
|
|
|
|
return PRIO_MAX - (int) limit;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int setpriority_closest(int priority) {
|
|
|
|
|
|
int current, limit, saved_errno;
|
|
|
|
|
|
struct rlimit highest;
|
|
|
|
|
|
|
|
|
|
|
|
/* Try to set requested nice level */
|
|
|
|
|
|
if (setpriority(PRIO_PROCESS, 0, priority) >= 0)
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
|
|
/* Permission failed */
|
|
|
|
|
|
saved_errno = -errno;
|
|
|
|
|
|
if (!ERRNO_IS_PRIVILEGE(saved_errno))
|
|
|
|
|
|
return saved_errno;
|
|
|
|
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
|
|
current = getpriority(PRIO_PROCESS, 0);
|
|
|
|
|
|
if (errno != 0)
|
|
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
|
|
|
|
if (priority == current)
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
|
|
/* Hmm, we'd expect that raising the nice level from our status quo would always work. If it doesn't,
|
|
|
|
|
|
* then the whole setpriority() system call is blocked to us, hence let's propagate the error
|
|
|
|
|
|
* right-away */
|
|
|
|
|
|
if (priority > current)
|
|
|
|
|
|
return saved_errno;
|
|
|
|
|
|
|
|
|
|
|
|
if (getrlimit(RLIMIT_NICE, &highest) < 0)
|
|
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
|
|
|
|
limit = rlimit_to_nice(highest.rlim_cur);
|
|
|
|
|
|
|
|
|
|
|
|
/* We are already less nice than limit allows us */
|
|
|
|
|
|
if (current < limit) {
|
|
|
|
|
|
log_debug("Cannot raise nice level, permissions and the resource limit do not allow it.");
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Push to the allowed limit */
|
|
|
|
|
|
if (setpriority(PRIO_PROCESS, 0, limit) < 0)
|
|
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
|
|
|
|
log_debug("Cannot set requested nice level (%i), used next best (%i).", priority, limit);
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-10-20 13:14:11 +02:00
|
|
|
|
_noreturn_ void freeze(void) {
|
|
|
|
|
|
log_close();
|
|
|
|
|
|
|
2021-07-29 16:50:44 +02:00
|
|
|
|
/* Make sure nobody waits for us (i.e. on one of our sockets) anymore. Note that we use
|
|
|
|
|
|
* close_all_fds_without_malloc() instead of plain close_all_fds() here, since we want this function
|
|
|
|
|
|
* to be compatible with being called from signal handlers. */
|
|
|
|
|
|
(void) close_all_fds_without_malloc(NULL, 0);
|
2021-10-20 13:14:11 +02:00
|
|
|
|
|
|
|
|
|
|
/* Let's not freeze right away, but keep reaping zombies. */
|
|
|
|
|
|
for (;;) {
|
|
|
|
|
|
siginfo_t si = {};
|
|
|
|
|
|
|
|
|
|
|
|
if (waitid(P_ALL, 0, &si, WEXITED) < 0 && errno != EINTR)
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* waitid() failed with an unexpected error, things are really borked. Freeze now! */
|
|
|
|
|
|
for (;;)
|
|
|
|
|
|
pause();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-02-08 18:01:26 +01:00
|
|
|
|
int get_process_threads(pid_t pid) {
|
|
|
|
|
|
_cleanup_free_ char *t = NULL;
|
|
|
|
|
|
const char *p;
|
|
|
|
|
|
int n, r;
|
|
|
|
|
|
|
|
|
|
|
|
if (pid < 0)
|
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
|
|
p = procfs_file_alloca(pid, "status");
|
|
|
|
|
|
|
|
|
|
|
|
r = get_proc_field(p, "Threads", WHITESPACE, &t);
|
|
|
|
|
|
if (r == -ENOENT)
|
|
|
|
|
|
return proc_mounted() == 0 ? -ENOSYS : -ESRCH;
|
|
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
|
|
r = safe_atoi(t, &n);
|
|
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
|
|
|
|
|
if (n < 0)
|
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
|
|
return n;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-06-22 11:52:06 +02:00
|
|
|
|
int is_reaper_process(void) {
|
|
|
|
|
|
int b = 0;
|
|
|
|
|
|
|
|
|
|
|
|
/* Checks if we are running in a reaper process, i.e. if we are expected to deal with processes
|
|
|
|
|
|
* reparented to us. This simply checks if we are PID 1 or if PR_SET_CHILD_SUBREAPER was called. */
|
|
|
|
|
|
|
|
|
|
|
|
if (getpid_cached() == 1)
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
|
|
if (prctl(PR_GET_CHILD_SUBREAPER, (unsigned long) &b, 0UL, 0UL, 0UL) < 0)
|
|
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
|
|
|
|
return b != 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-06-22 22:24:04 +02:00
|
|
|
|
int make_reaper_process(bool b) {
|
|
|
|
|
|
|
|
|
|
|
|
if (getpid_cached() == 1) {
|
|
|
|
|
|
|
|
|
|
|
|
if (!b)
|
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Some prctl()s insist that all 5 arguments are specified, others do not. Let's always specify all,
|
|
|
|
|
|
* to avoid any ambiguities */
|
|
|
|
|
|
if (prctl(PR_SET_CHILD_SUBREAPER, (unsigned long) b, 0UL, 0UL, 0UL) < 0)
|
|
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-06-02 16:06:17 +01:00
|
|
|
|
int posix_spawn_wrapper(const char *path, char *const *argv, char *const *envp, pid_t *ret_pid) {
|
|
|
|
|
|
posix_spawnattr_t attr;
|
|
|
|
|
|
sigset_t mask;
|
|
|
|
|
|
pid_t pid;
|
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
|
|
/* Forks and invokes 'path' with 'argv' and 'envp' using CLONE_VM and CLONE_VFORK, which means the
|
|
|
|
|
|
* caller will be blocked until the child either exits or exec's. The memory of the child will be
|
|
|
|
|
|
* fully shared with the memory of the parent, so that there are no copy-on-write or memory.max
|
|
|
|
|
|
* issues. */
|
|
|
|
|
|
|
|
|
|
|
|
assert(path);
|
|
|
|
|
|
assert(argv);
|
|
|
|
|
|
assert(ret_pid);
|
|
|
|
|
|
|
|
|
|
|
|
assert_se(sigfillset(&mask) >= 0);
|
|
|
|
|
|
|
|
|
|
|
|
r = posix_spawnattr_init(&attr);
|
|
|
|
|
|
if (r != 0)
|
|
|
|
|
|
return -r; /* These functions return a positive errno on failure */
|
|
|
|
|
|
r = posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETSIGMASK);
|
|
|
|
|
|
if (r != 0)
|
|
|
|
|
|
goto fail;
|
|
|
|
|
|
r = posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETSIGDEF); /* Set all signals to SIG_DFL */
|
|
|
|
|
|
if (r != 0)
|
|
|
|
|
|
goto fail;
|
|
|
|
|
|
r = posix_spawnattr_setsigmask(&attr, &mask);
|
|
|
|
|
|
if (r != 0)
|
|
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
|
|
|
|
r = posix_spawn(&pid, path, NULL, &attr, argv, envp);
|
|
|
|
|
|
if (r != 0)
|
|
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
|
|
|
|
*ret_pid = pid;
|
|
|
|
|
|
|
|
|
|
|
|
posix_spawnattr_destroy(&attr);
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
|
|
fail:
|
|
|
|
|
|
assert(r > 0);
|
|
|
|
|
|
posix_spawnattr_destroy(&attr);
|
|
|
|
|
|
return -r;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-10-17 13:41:08 +02:00
|
|
|
|
int proc_dir_open(DIR **ret) {
|
|
|
|
|
|
DIR *d;
|
|
|
|
|
|
|
|
|
|
|
|
assert(ret);
|
|
|
|
|
|
|
|
|
|
|
|
d = opendir("/proc");
|
|
|
|
|
|
if (!d)
|
|
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
|
|
|
|
*ret = d;
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int proc_dir_read(DIR *d, pid_t *ret) {
|
|
|
|
|
|
assert(d);
|
|
|
|
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
|
|
struct dirent *de;
|
|
|
|
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
|
|
de = readdir_no_dot(d);
|
|
|
|
|
|
if (!de) {
|
|
|
|
|
|
if (errno != 0)
|
|
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!IN_SET(de->d_type, DT_DIR, DT_UNKNOWN))
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
if (parse_pid(de->d_name, ret) >= 0)
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
|
|
*ret = 0;
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int proc_dir_read_pidref(DIR *d, PidRef *ret) {
|
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
|
|
assert(d);
|
|
|
|
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
|
|
pid_t pid;
|
|
|
|
|
|
|
|
|
|
|
|
r = proc_dir_read(d, &pid);
|
|
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
|
|
|
|
|
if (r == 0)
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
r = pidref_set_pid(ret, pid);
|
|
|
|
|
|
if (r == -ESRCH) /* gone by now? skip it */
|
|
|
|
|
|
continue;
|
|
|
|
|
|
if (r < 0)
|
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
|
|
*ret = PIDREF_NULL;
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-10-27 14:24:58 +01:00
|
|
|
|
static const char *const sigchld_code_table[] = {
|
|
|
|
|
|
[CLD_EXITED] = "exited",
|
|
|
|
|
|
[CLD_KILLED] = "killed",
|
|
|
|
|
|
[CLD_DUMPED] = "dumped",
|
|
|
|
|
|
[CLD_TRAPPED] = "trapped",
|
|
|
|
|
|
[CLD_STOPPED] = "stopped",
|
|
|
|
|
|
[CLD_CONTINUED] = "continued",
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
DEFINE_STRING_TABLE_LOOKUP(sigchld_code, int);
|
|
|
|
|
|
|
|
|
|
|
|
static const char* const sched_policy_table[] = {
|
|
|
|
|
|
[SCHED_OTHER] = "other",
|
|
|
|
|
|
[SCHED_BATCH] = "batch",
|
|
|
|
|
|
[SCHED_IDLE] = "idle",
|
|
|
|
|
|
[SCHED_FIFO] = "fifo",
|
2019-05-28 21:28:31 +02:00
|
|
|
|
[SCHED_RR] = "rr",
|
2015-10-27 14:24:58 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(sched_policy, int, INT_MAX);
|