2020-11-09 05:23:58 +01: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>
|
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>
|
2016-12-06 20:29:07 +01:00
|
|
|
|
#include <sys/mman.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 16:48:45 +01:00
|
|
|
|
#include <valgrind/valgrind.h>
|
|
|
|
|
#endif
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
2015-10-27 03:01:06 +01:00
|
|
|
|
#include "alloc-util.h"
|
2016-02-22 15:39:52 +01:00
|
|
|
|
#include "architecture.h"
|
2019-05-11 09:51:33 +02:00
|
|
|
|
#include "env-util.h"
|
2017-08-01 18:38:05 +02: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"
|
2015-10-27 14:24:58 +01:00
|
|
|
|
#include "ioprio.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 03:07:23 +01:00
|
|
|
|
#include "missing_sched.h"
|
|
|
|
|
#include "missing_syscall.h"
|
2019-03-13 11:21:49 +01:00
|
|
|
|
#include "namespace-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 13:37:03 +02: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;
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-17 03:50:35 +02:00
|
|
|
|
int get_process_comm(pid_t pid, char **ret) {
|
|
|
|
|
_cleanup_free_ char *escaped = NULL, *comm = NULL;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
int r;
|
|
|
|
|
|
2018-05-17 03:50:35 +02: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-17 03:50:35 +02: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-17 03:50:35 +02:00
|
|
|
|
*ret = TAKE_PTR(escaped);
|
|
|
|
|
return 0;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-16 12:14:52 +02:00
|
|
|
|
int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **line) {
|
2015-04-10 19:10:00 +02:00
|
|
|
|
_cleanup_fclose_ FILE *f = NULL;
|
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
|
|
|
|
_cleanup_free_ char *t = NULL, *ans = 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
|
|
|
|
int r;
|
|
|
|
|
size_t k;
|
|
|
|
|
|
|
|
|
|
/* This is supposed to be a safety guard against runaway command lines. */
|
|
|
|
|
size_t max_length = sc_arg_max();
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
|
|
|
|
assert(line);
|
|
|
|
|
assert(pid >= 0);
|
|
|
|
|
|
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
|
|
|
|
/* Retrieves a process' command line. Replaces non-utf8 bytes by replacement character (<28>). If
|
|
|
|
|
* max_columns is != -1 will return a string of the specified console width at most, abbreviated with
|
2019-05-16 12:14:52 +02:00
|
|
|
|
* an ellipsis. 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.
|
2016-06-14 23:52:29 +02:00
|
|
|
|
*
|
|
|
|
|
* Returns -ESRCH if the process doesn't exist, and -ENOENT if the process has no command line (and
|
2017-01-15 18:41:34 +01:00
|
|
|
|
* comm_fallback is false). Returns 0 and sets *line otherwise. */
|
2016-06-14 23:52:29 +02:00
|
|
|
|
|
2015-04-10 19:10:00 +02:00
|
|
|
|
p = procfs_file_alloca(pid, "cmdline");
|
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
|
|
|
|
|
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
|
|
|
|
/* We assume that each four-byte character uses one or two columns. If we ever check for combining
|
|
|
|
|
* characters, this assumption will need to be adjusted. */
|
|
|
|
|
if ((size_t) 4 * max_columns + 1 < max_columns)
|
|
|
|
|
max_length = MIN(max_length, (size_t) 4 * max_columns + 1);
|
2016-06-14 23:52:29 +02:00
|
|
|
|
|
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
|
|
|
|
t = new(char, max_length);
|
|
|
|
|
if (!t)
|
|
|
|
|
return -ENOMEM;
|
2016-06-14 23:52:29 +02:00
|
|
|
|
|
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
|
|
|
|
k = fread(t, 1, max_length, f);
|
|
|
|
|
if (k > 0) {
|
|
|
|
|
/* 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] = ' ';
|
2016-06-14 23:52:29 +02:00
|
|
|
|
|
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
|
|
|
|
t[k] = '\0'; /* Normally, t[k] is already NUL, so this is just a guard in case of short read */
|
2015-04-10 19:10:00 +02:00
|
|
|
|
} else {
|
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
|
|
|
|
/* We only treat getting nothing as an error. We *could* also get an error after reading some
|
|
|
|
|
* data, but we ignore that case, as such an error is rather unlikely and we prefer to get
|
|
|
|
|
* some data rather than none. */
|
|
|
|
|
if (ferror(f))
|
|
|
|
|
return -errno;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
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[] */
|
|
|
|
|
_cleanup_free_ char *t2 = NULL;
|
2016-06-14 23:52:29 +02:00
|
|
|
|
|
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
|
|
|
|
r = get_process_comm(pid, &t2);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
2016-06-14 23:52:29 +02:00
|
|
|
|
|
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
|
|
|
|
mfree(t);
|
|
|
|
|
t = strjoin("[", t2, "]");
|
|
|
|
|
if (!t)
|
|
|
|
|
return -ENOMEM;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
delete_trailing_chars(t, WHITESPACE);
|
2019-01-22 14:29:50 +01:00
|
|
|
|
|
2019-05-16 17:44:57 +02:00
|
|
|
|
bool eight_bit = (flags & PROCESS_CMDLINE_USE_LOCALE) && !is_locale_utf8();
|
|
|
|
|
|
|
|
|
|
ans = escape_non_printable_full(t, max_columns, eight_bit);
|
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 (!ans)
|
|
|
|
|
return -ENOMEM;
|
2019-01-22 14:29:50 +01:00
|
|
|
|
|
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
|
|
|
|
(void) str_realloc(&ans);
|
|
|
|
|
*line = TAKE_PTR(ans);
|
2015-04-10 19:10:00 +02:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-22 09:00:49 +02:00
|
|
|
|
static int update_argv(const char name[], size_t l) {
|
basic/process-util: only try PR_SET_MM once
userwork wants to update the title many times, and a strace is full of
attempts that fail the same way:
[pid 21765] prctl(PR_SET_NAME, "systemd-userwor"...) = 0
[pid 21765] geteuid() = 0
[pid 21765] mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fedce329000
[pid 21765] prctl(PR_SET_MM, PR_SET_MM_ARG_START, 0x7fedce329000, 0, 0) = -1 EPERM (Operation not permitted)
[pid 21765] prctl(PR_SET_MM, PR_SET_MM_ARG_END, 0x7fedce32901d, 0, 0) = -1 EPERM (Operation not permitted)
[pid 21765] munmap(0x7fedce329000, 4096) = 0
[pid 21765] accept4(3, NULL, NULL, SOCK_CLOEXEC|SOCK_NONBLOCK) = -1 EAGAIN (Resource temporarily unavailable)
[pid 21765] prctl(PR_SET_NAME, "systemd-userwor"...) = 0
[pid 21765] geteuid() = 0
[pid 21765] mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fedce329000
[pid 21765] prctl(PR_SET_MM, PR_SET_MM_ARG_START, 0x7fedce329000, 0, 0) = -1 EPERM (Operation not permitted)
[pid 21765] prctl(PR_SET_MM, PR_SET_MM_ARG_END, 0x7fedce329020, 0, 0) = -1 EPERM (Operation not permitted)
[pid 21765] munmap(0x7fedce329000, 4096) = 0
[pid 21765] prctl(PR_SET_NAME, "systemd-userwor"...) = 0
[pid 21765] geteuid() = 0
[pid 21765] mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fedce329000
[pid 21765] prctl(PR_SET_MM, PR_SET_MM_ARG_START, 0x7fedce329000, 0, 0) = -1 EPERM (Operation not permitted)
[pid 21765] prctl(PR_SET_MM, PR_SET_MM_ARG_END, 0x7fedce32901d, 0, 0) = -1 EPERM (Operation not permitted)
[pid 21765] munmap(0x7fedce329000, 4096) = 0
[pid 21765] accept4(3, NULL, NULL, SOCK_CLOEXEC|SOCK_NONBLOCK) = -1 EAGAIN (Resource temporarily unavailable)
[pid 21765] prctl(PR_SET_NAME, "systemd-userwor"...) = 0
[pid 21765] geteuid() = 0
[pid 21765] mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fedce329000
[pid 21765] prctl(PR_SET_MM, PR_SET_MM_ARG_START, 0x7fedce329000, 0, 0) = -1 EPERM (Operation not permitted)
[pid 21765] prctl(PR_SET_MM, PR_SET_MM_ARG_END, 0x7fedce329020, 0, 0) = -1 EPERM (Operation not permitted)
[pid 21765] munmap(0x7fedce329000, 4096) = 0
[pid 21765] prctl(PR_SET_NAME, "systemd-userwor"...) = 0
[pid 21765] geteuid() = 0
[pid 21765] mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fedce329000
[pid 21765] prctl(PR_SET_MM, PR_SET_MM_ARG_START, 0x7fedce329000, 0, 0) = -1 EPERM (Operation not permitted)
[pid 21765] prctl(PR_SET_MM, PR_SET_MM_ARG_END, 0x7fedce32901d, 0, 0) = -1 EPERM (Operation not permitted)
[pid 21765] munmap(0x7fedce329000, 4096) = 0
[pid 21765] accept4(3, NULL, NULL, SOCK_CLOEXEC|SOCK_NONBLOCK) = -1 EAGAIN (Resource temporarily unavailable)
[pid 21765] prctl(PR_SET_NAME, "systemd-userwor"...) = 0
[pid 21765] geteuid() = 0
[pid 21765] mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fedce329000
[pid 21765] prctl(PR_SET_MM, PR_SET_MM_ARG_START, 0x7fedce329000, 0, 0) = -1 EPERM (Operation not permitted)
[pid 21765] prctl(PR_SET_MM, PR_SET_MM_ARG_END, 0x7fedce329020, 0, 0) = -1 EPERM (Operation not permitted)
[pid 21765] munmap(0x7fedce329000, 4096) = 0
If we get a permission error, don't try again.
2020-05-22 09:23:31 +02:00
|
|
|
|
static int can_do = -1;
|
|
|
|
|
|
|
|
|
|
if (can_do == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
can_do = false; /* We'll set it to true only if the whole process works */
|
|
|
|
|
|
2017-08-02 17:08:31 +02:00
|
|
|
|
/* Let's not bother with this if we don't have euid == 0. Strictly speaking we should check for the
|
|
|
|
|
* CAP_SYS_RESOURCE capability which is independent of the euid. In our own code the capability generally is
|
|
|
|
|
* present only for euid == 0, hence let's use this as quick bypass check, to avoid calling mmap() if
|
|
|
|
|
* PR_SET_MM_ARG_{START,END} fails with EPERM later on anyway. After all geteuid() is dead cheap to call, but
|
|
|
|
|
* mmap() is not. */
|
|
|
|
|
if (geteuid() != 0)
|
2020-05-22 09:00:49 +02:00
|
|
|
|
return log_debug_errno(SYNTHETIC_ERRNO(EPERM),
|
|
|
|
|
"Skipping PR_SET_MM, as we don't have privileges.");
|
|
|
|
|
|
|
|
|
|
static size_t mm_size = 0;
|
|
|
|
|
static char *mm = NULL;
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
if (mm_size < l+1) {
|
2016-12-06 20:29:07 +01:00
|
|
|
|
size_t nn_size;
|
|
|
|
|
char *nn;
|
|
|
|
|
|
|
|
|
|
nn_size = PAGE_ALIGN(l+1);
|
|
|
|
|
nn = mmap(NULL, nn_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
|
2020-05-22 09:00:49 +02:00
|
|
|
|
if (nn == MAP_FAILED)
|
|
|
|
|
return log_debug_errno(errno, "mmap() failed: %m");
|
2015-10-27 13:56:40 +01:00
|
|
|
|
|
2016-12-06 20:29:07 +01:00
|
|
|
|
strncpy(nn, name, nn_size);
|
|
|
|
|
|
|
|
|
|
/* Now, let's tell the kernel about this new memory */
|
|
|
|
|
if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) {
|
basic/process-util: only try PR_SET_MM once
userwork wants to update the title many times, and a strace is full of
attempts that fail the same way:
[pid 21765] prctl(PR_SET_NAME, "systemd-userwor"...) = 0
[pid 21765] geteuid() = 0
[pid 21765] mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fedce329000
[pid 21765] prctl(PR_SET_MM, PR_SET_MM_ARG_START, 0x7fedce329000, 0, 0) = -1 EPERM (Operation not permitted)
[pid 21765] prctl(PR_SET_MM, PR_SET_MM_ARG_END, 0x7fedce32901d, 0, 0) = -1 EPERM (Operation not permitted)
[pid 21765] munmap(0x7fedce329000, 4096) = 0
[pid 21765] accept4(3, NULL, NULL, SOCK_CLOEXEC|SOCK_NONBLOCK) = -1 EAGAIN (Resource temporarily unavailable)
[pid 21765] prctl(PR_SET_NAME, "systemd-userwor"...) = 0
[pid 21765] geteuid() = 0
[pid 21765] mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fedce329000
[pid 21765] prctl(PR_SET_MM, PR_SET_MM_ARG_START, 0x7fedce329000, 0, 0) = -1 EPERM (Operation not permitted)
[pid 21765] prctl(PR_SET_MM, PR_SET_MM_ARG_END, 0x7fedce329020, 0, 0) = -1 EPERM (Operation not permitted)
[pid 21765] munmap(0x7fedce329000, 4096) = 0
[pid 21765] prctl(PR_SET_NAME, "systemd-userwor"...) = 0
[pid 21765] geteuid() = 0
[pid 21765] mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fedce329000
[pid 21765] prctl(PR_SET_MM, PR_SET_MM_ARG_START, 0x7fedce329000, 0, 0) = -1 EPERM (Operation not permitted)
[pid 21765] prctl(PR_SET_MM, PR_SET_MM_ARG_END, 0x7fedce32901d, 0, 0) = -1 EPERM (Operation not permitted)
[pid 21765] munmap(0x7fedce329000, 4096) = 0
[pid 21765] accept4(3, NULL, NULL, SOCK_CLOEXEC|SOCK_NONBLOCK) = -1 EAGAIN (Resource temporarily unavailable)
[pid 21765] prctl(PR_SET_NAME, "systemd-userwor"...) = 0
[pid 21765] geteuid() = 0
[pid 21765] mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fedce329000
[pid 21765] prctl(PR_SET_MM, PR_SET_MM_ARG_START, 0x7fedce329000, 0, 0) = -1 EPERM (Operation not permitted)
[pid 21765] prctl(PR_SET_MM, PR_SET_MM_ARG_END, 0x7fedce329020, 0, 0) = -1 EPERM (Operation not permitted)
[pid 21765] munmap(0x7fedce329000, 4096) = 0
[pid 21765] prctl(PR_SET_NAME, "systemd-userwor"...) = 0
[pid 21765] geteuid() = 0
[pid 21765] mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fedce329000
[pid 21765] prctl(PR_SET_MM, PR_SET_MM_ARG_START, 0x7fedce329000, 0, 0) = -1 EPERM (Operation not permitted)
[pid 21765] prctl(PR_SET_MM, PR_SET_MM_ARG_END, 0x7fedce32901d, 0, 0) = -1 EPERM (Operation not permitted)
[pid 21765] munmap(0x7fedce329000, 4096) = 0
[pid 21765] accept4(3, NULL, NULL, SOCK_CLOEXEC|SOCK_NONBLOCK) = -1 EAGAIN (Resource temporarily unavailable)
[pid 21765] prctl(PR_SET_NAME, "systemd-userwor"...) = 0
[pid 21765] geteuid() = 0
[pid 21765] mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fedce329000
[pid 21765] prctl(PR_SET_MM, PR_SET_MM_ARG_START, 0x7fedce329000, 0, 0) = -1 EPERM (Operation not permitted)
[pid 21765] prctl(PR_SET_MM, PR_SET_MM_ARG_END, 0x7fedce329020, 0, 0) = -1 EPERM (Operation not permitted)
[pid 21765] munmap(0x7fedce329000, 4096) = 0
If we get a permission error, don't try again.
2020-05-22 09:23:31 +02:00
|
|
|
|
if (ERRNO_IS_PRIVILEGE(errno))
|
|
|
|
|
return log_debug_errno(errno, "PR_SET_MM_ARG_START failed: %m");
|
|
|
|
|
|
2018-10-04 23:06:16 +02:00
|
|
|
|
/* HACK: prctl() API is kind of dumb on this point. The existing end address may already be
|
|
|
|
|
* below the desired start address, in which case the kernel may have kicked this back due
|
|
|
|
|
* to a range-check failure (see linux/kernel/sys.c:validate_prctl_map() to see this in
|
|
|
|
|
* action). The proper solution would be to have a prctl() API that could set both start+end
|
|
|
|
|
* simultaneously, or at least let us query the existing address to anticipate this condition
|
|
|
|
|
* and respond accordingly. For now, we can only guess at the cause of this failure and try
|
|
|
|
|
* a workaround--which will briefly expand the arg space to something potentially huge before
|
|
|
|
|
* resizing it to what we want. */
|
|
|
|
|
log_debug_errno(errno, "PR_SET_MM_ARG_START failed, attempting PR_SET_MM_ARG_END hack: %m");
|
|
|
|
|
|
|
|
|
|
if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0) {
|
2020-05-22 09:00:49 +02:00
|
|
|
|
r = log_debug_errno(errno, "PR_SET_MM_ARG_END hack failed, proceeding without: %m");
|
2018-10-04 23:06:16 +02:00
|
|
|
|
(void) munmap(nn, nn_size);
|
2020-05-22 09:00:49 +02:00
|
|
|
|
return r;
|
2018-10-04 23:06:16 +02:00
|
|
|
|
}
|
2016-12-06 20:29:07 +01:00
|
|
|
|
|
2020-05-22 09:00:49 +02:00
|
|
|
|
if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0)
|
|
|
|
|
return log_debug_errno(errno, "PR_SET_MM_ARG_START still failed, proceeding without: %m");
|
2018-10-04 23:06:16 +02:00
|
|
|
|
} else {
|
|
|
|
|
/* And update the end pointer to the new end, too. If this fails, we don't really know what
|
|
|
|
|
* to do, it's pretty unlikely that we can rollback, hence we'll just accept the failure,
|
|
|
|
|
* and continue. */
|
|
|
|
|
if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0)
|
|
|
|
|
log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m");
|
|
|
|
|
}
|
2016-12-06 20:29:07 +01:00
|
|
|
|
|
|
|
|
|
if (mm)
|
|
|
|
|
(void) munmap(mm, mm_size);
|
|
|
|
|
|
|
|
|
|
mm = nn;
|
|
|
|
|
mm_size = nn_size;
|
2017-08-02 17:08:31 +02:00
|
|
|
|
} else {
|
2016-12-06 20:29:07 +01:00
|
|
|
|
strncpy(mm, name, mm_size);
|
|
|
|
|
|
2017-08-02 17:08:31 +02:00
|
|
|
|
/* Update the end pointer, continuing regardless of any failure. */
|
|
|
|
|
if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) mm + l + 1, 0, 0) < 0)
|
|
|
|
|
log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m");
|
|
|
|
|
}
|
|
|
|
|
|
basic/process-util: only try PR_SET_MM once
userwork wants to update the title many times, and a strace is full of
attempts that fail the same way:
[pid 21765] prctl(PR_SET_NAME, "systemd-userwor"...) = 0
[pid 21765] geteuid() = 0
[pid 21765] mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fedce329000
[pid 21765] prctl(PR_SET_MM, PR_SET_MM_ARG_START, 0x7fedce329000, 0, 0) = -1 EPERM (Operation not permitted)
[pid 21765] prctl(PR_SET_MM, PR_SET_MM_ARG_END, 0x7fedce32901d, 0, 0) = -1 EPERM (Operation not permitted)
[pid 21765] munmap(0x7fedce329000, 4096) = 0
[pid 21765] accept4(3, NULL, NULL, SOCK_CLOEXEC|SOCK_NONBLOCK) = -1 EAGAIN (Resource temporarily unavailable)
[pid 21765] prctl(PR_SET_NAME, "systemd-userwor"...) = 0
[pid 21765] geteuid() = 0
[pid 21765] mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fedce329000
[pid 21765] prctl(PR_SET_MM, PR_SET_MM_ARG_START, 0x7fedce329000, 0, 0) = -1 EPERM (Operation not permitted)
[pid 21765] prctl(PR_SET_MM, PR_SET_MM_ARG_END, 0x7fedce329020, 0, 0) = -1 EPERM (Operation not permitted)
[pid 21765] munmap(0x7fedce329000, 4096) = 0
[pid 21765] prctl(PR_SET_NAME, "systemd-userwor"...) = 0
[pid 21765] geteuid() = 0
[pid 21765] mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fedce329000
[pid 21765] prctl(PR_SET_MM, PR_SET_MM_ARG_START, 0x7fedce329000, 0, 0) = -1 EPERM (Operation not permitted)
[pid 21765] prctl(PR_SET_MM, PR_SET_MM_ARG_END, 0x7fedce32901d, 0, 0) = -1 EPERM (Operation not permitted)
[pid 21765] munmap(0x7fedce329000, 4096) = 0
[pid 21765] accept4(3, NULL, NULL, SOCK_CLOEXEC|SOCK_NONBLOCK) = -1 EAGAIN (Resource temporarily unavailable)
[pid 21765] prctl(PR_SET_NAME, "systemd-userwor"...) = 0
[pid 21765] geteuid() = 0
[pid 21765] mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fedce329000
[pid 21765] prctl(PR_SET_MM, PR_SET_MM_ARG_START, 0x7fedce329000, 0, 0) = -1 EPERM (Operation not permitted)
[pid 21765] prctl(PR_SET_MM, PR_SET_MM_ARG_END, 0x7fedce329020, 0, 0) = -1 EPERM (Operation not permitted)
[pid 21765] munmap(0x7fedce329000, 4096) = 0
[pid 21765] prctl(PR_SET_NAME, "systemd-userwor"...) = 0
[pid 21765] geteuid() = 0
[pid 21765] mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fedce329000
[pid 21765] prctl(PR_SET_MM, PR_SET_MM_ARG_START, 0x7fedce329000, 0, 0) = -1 EPERM (Operation not permitted)
[pid 21765] prctl(PR_SET_MM, PR_SET_MM_ARG_END, 0x7fedce32901d, 0, 0) = -1 EPERM (Operation not permitted)
[pid 21765] munmap(0x7fedce329000, 4096) = 0
[pid 21765] accept4(3, NULL, NULL, SOCK_CLOEXEC|SOCK_NONBLOCK) = -1 EAGAIN (Resource temporarily unavailable)
[pid 21765] prctl(PR_SET_NAME, "systemd-userwor"...) = 0
[pid 21765] geteuid() = 0
[pid 21765] mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fedce329000
[pid 21765] prctl(PR_SET_MM, PR_SET_MM_ARG_START, 0x7fedce329000, 0, 0) = -1 EPERM (Operation not permitted)
[pid 21765] prctl(PR_SET_MM, PR_SET_MM_ARG_END, 0x7fedce329020, 0, 0) = -1 EPERM (Operation not permitted)
[pid 21765] munmap(0x7fedce329000, 4096) = 0
If we get a permission error, don't try again.
2020-05-22 09:23:31 +02:00
|
|
|
|
can_do = true;
|
2020-05-22 09:00:49 +02:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int rename_process(const char name[]) {
|
|
|
|
|
bool truncated = false;
|
|
|
|
|
|
|
|
|
|
/* This is a like a poor man's setproctitle(). It changes the comm field, argv[0], and also the glibc's
|
|
|
|
|
* internally used name of the process. For the first one a limit of 16 chars applies; to the second one in
|
|
|
|
|
* many cases one of 10 (i.e. length of "/sbin/init") — however if we have CAP_SYS_RESOURCES it is unbounded;
|
|
|
|
|
* to the third one 7 (i.e. the length of "systemd". If you pass a longer string it will likely be
|
|
|
|
|
* truncated.
|
|
|
|
|
*
|
|
|
|
|
* Returns 0 if a name was set but truncated, > 0 if it was set but not truncated. */
|
|
|
|
|
|
|
|
|
|
if (isempty(name))
|
|
|
|
|
return -EINVAL; /* let's not confuse users unnecessarily with an empty name */
|
|
|
|
|
|
|
|
|
|
if (!is_main_thread())
|
|
|
|
|
return -EPERM; /* Let's not allow setting the process name from other threads than the main one, as we
|
|
|
|
|
* cache things without locking, and we make assumptions that PR_SET_NAME sets the
|
|
|
|
|
* process name that isn't correct on any other threads */
|
|
|
|
|
|
|
|
|
|
size_t l = strlen(name);
|
|
|
|
|
|
|
|
|
|
/* First step, change the comm field. The main thread's comm is identical to the process comm. This means we
|
|
|
|
|
* can use PR_SET_NAME, which sets the thread name for the calling thread. */
|
|
|
|
|
if (prctl(PR_SET_NAME, name) < 0)
|
|
|
|
|
log_debug_errno(errno, "PR_SET_NAME failed: %m");
|
|
|
|
|
if (l >= TASK_COMM_LEN) /* Linux userspace process names can be 15 chars at max */
|
|
|
|
|
truncated = true;
|
|
|
|
|
|
|
|
|
|
/* Second step, change glibc's ID of the process name. */
|
|
|
|
|
if (program_invocation_name) {
|
|
|
|
|
size_t k;
|
|
|
|
|
|
|
|
|
|
k = strlen(program_invocation_name);
|
|
|
|
|
strncpy(program_invocation_name, name, k);
|
|
|
|
|
if (l > k)
|
|
|
|
|
truncated = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Third step, completely replace the argv[] array the kernel maintains for us. This requires privileges, but
|
|
|
|
|
* has the advantage that the argv[] array is exactly what we want it to be, and not filled up with zeros at
|
|
|
|
|
* the end. This is the best option for changing /proc/self/cmdline. */
|
|
|
|
|
(void) update_argv(name, l);
|
|
|
|
|
|
2016-12-06 20:29:07 +01:00
|
|
|
|
/* Fourth step: in all cases we'll also update the original argv[], so that our own code gets it right too if
|
|
|
|
|
* it still looks here */
|
2015-10-27 13:56:40 +01:00
|
|
|
|
if (saved_argc > 0) {
|
2016-12-06 20:29:07 +01:00
|
|
|
|
if (saved_argv[0]) {
|
|
|
|
|
size_t k;
|
|
|
|
|
|
|
|
|
|
k = strlen(saved_argv[0]);
|
|
|
|
|
strncpy(saved_argv[0], name, k);
|
|
|
|
|
if (l > k)
|
|
|
|
|
truncated = true;
|
|
|
|
|
}
|
2015-10-27 13:56:40 +01:00
|
|
|
|
|
2020-05-22 09:00:49 +02:00
|
|
|
|
for (int i = 1; i < saved_argc; i++) {
|
2015-10-27 13:56:40 +01:00
|
|
|
|
if (!saved_argv[i])
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
memzero(saved_argv[i], strlen(saved_argv[i]));
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-12-06 20:29:07 +01:00
|
|
|
|
|
|
|
|
|
return !truncated;
|
2015-10-27 13:56:40 +01:00
|
|
|
|
}
|
|
|
|
|
|
2015-04-10 19:10:00 +02:00
|
|
|
|
int 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 16:01:32 +02: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
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int get_process_capeff(pid_t pid, char **capeff) {
|
|
|
|
|
const char *p;
|
2015-07-23 23:44:40 +02:00
|
|
|
|
int r;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
|
|
|
|
assert(capeff);
|
|
|
|
|
assert(pid >= 0);
|
|
|
|
|
|
|
|
|
|
p = procfs_file_alloca(pid, "status");
|
|
|
|
|
|
2015-09-30 14:57:55 +02:00
|
|
|
|
r = get_proc_field(p, "CapEff", WHITESPACE, capeff);
|
2015-07-23 23:44:40 +02:00
|
|
|
|
if (r == -ENOENT)
|
|
|
|
|
return -ESRCH;
|
|
|
|
|
|
|
|
|
|
return r;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int get_process_link_contents(const char *proc_file, char **name) {
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
assert(proc_file);
|
|
|
|
|
assert(name);
|
|
|
|
|
|
|
|
|
|
r = readlink_malloc(proc_file, name);
|
2015-07-23 23:44:40 +02:00
|
|
|
|
if (r == -ENOENT)
|
|
|
|
|
return -ESRCH;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
if (r < 0)
|
2015-07-23 23:44:40 +02:00
|
|
|
|
return r;
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int get_process_exe(pid_t pid, char **name) {
|
|
|
|
|
const char *p;
|
|
|
|
|
char *d;
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
assert(pid >= 0);
|
|
|
|
|
|
|
|
|
|
p = procfs_file_alloca(pid, "exe");
|
|
|
|
|
r = get_process_link_contents(p, name);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
d = endswith(*name, " (deleted)");
|
|
|
|
|
if (d)
|
|
|
|
|
*d = '\0';
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int get_process_id(pid_t pid, const char *field, uid_t *uid) {
|
|
|
|
|
_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);
|
|
|
|
|
assert(uid);
|
|
|
|
|
|
2017-10-03 13:05:24 +02: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;
|
|
|
|
|
|
2018-10-18 16:08:51 +02:00
|
|
|
|
r = read_line(f, LONG_LINE_MAX, &line);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
if (r == 0)
|
|
|
|
|
break;
|
|
|
|
|
|
2015-04-10 19:10:00 +02:00
|
|
|
|
l = strstrip(line);
|
|
|
|
|
|
|
|
|
|
if (startswith(l, field)) {
|
|
|
|
|
l += strlen(field);
|
|
|
|
|
l += strspn(l, WHITESPACE);
|
|
|
|
|
|
|
|
|
|
l[strcspn(l, WHITESPACE)] = 0;
|
|
|
|
|
|
|
|
|
|
return parse_uid(l, uid);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return -EIO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int get_process_uid(pid_t pid, uid_t *uid) {
|
2017-07-17 23:35:25 +02:00
|
|
|
|
|
|
|
|
|
if (pid == 0 || pid == getpid_cached()) {
|
|
|
|
|
*uid = getuid();
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-10 19:10:00 +02:00
|
|
|
|
return get_process_id(pid, "Uid:", uid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int get_process_gid(pid_t pid, gid_t *gid) {
|
2017-07-17 23:35:25 +02:00
|
|
|
|
|
|
|
|
|
if (pid == 0 || pid == getpid_cached()) {
|
|
|
|
|
*gid = getgid();
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-10 19:10:00 +02:00
|
|
|
|
assert_cc(sizeof(uid_t) == sizeof(gid_t));
|
|
|
|
|
return get_process_id(pid, "Gid:", gid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int get_process_cwd(pid_t pid, char **cwd) {
|
|
|
|
|
const char *p;
|
|
|
|
|
|
|
|
|
|
assert(pid >= 0);
|
|
|
|
|
|
2019-11-25 14:55:50 +01:00
|
|
|
|
if (pid == 0 || pid == getpid_cached())
|
|
|
|
|
return safe_getcwd(cwd);
|
|
|
|
|
|
2015-04-10 19:10:00 +02:00
|
|
|
|
p = procfs_file_alloca(pid, "cwd");
|
|
|
|
|
|
|
|
|
|
return get_process_link_contents(p, cwd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int get_process_root(pid_t pid, char **root) {
|
|
|
|
|
const char *p;
|
|
|
|
|
|
|
|
|
|
assert(pid >= 0);
|
|
|
|
|
|
|
|
|
|
p = procfs_file_alloca(pid, "root");
|
|
|
|
|
|
|
|
|
|
return get_process_link_contents(p, root);
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-17 11:23:15 +01:00
|
|
|
|
#define ENVIRONMENT_BLOCK_MAX (5U*1024U*1024U)
|
|
|
|
|
|
2015-04-10 19:10:00 +02:00
|
|
|
|
int get_process_environ(pid_t pid, char **env) {
|
|
|
|
|
_cleanup_fclose_ FILE *f = NULL;
|
|
|
|
|
_cleanup_free_ char *outcome = NULL;
|
|
|
|
|
size_t allocated = 0, 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);
|
|
|
|
|
assert(env);
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
2015-04-10 19:10:00 +02:00
|
|
|
|
if (!GREEDY_REALLOC(outcome, allocated, sz + 5))
|
|
|
|
|
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';
|
2018-03-22 16:53:26 +01:00
|
|
|
|
*env = TAKE_PTR(outcome);
|
2015-04-10 19:10:00 +02:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-27 14:01:48 +01:00
|
|
|
|
int get_process_ppid(pid_t pid, pid_t *_ppid) {
|
2015-04-10 19:10:00 +02:00
|
|
|
|
int r;
|
|
|
|
|
_cleanup_free_ char *line = NULL;
|
|
|
|
|
long unsigned ppid;
|
|
|
|
|
const char *p;
|
|
|
|
|
|
|
|
|
|
assert(pid >= 0);
|
|
|
|
|
assert(_ppid);
|
|
|
|
|
|
2017-07-17 23:35:25 +02:00
|
|
|
|
if (pid == 0 || pid == getpid_cached()) {
|
2015-04-10 19:10:00 +02:00
|
|
|
|
*_ppid = getppid();
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
/* 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++;
|
|
|
|
|
|
|
|
|
|
if (sscanf(p, " "
|
|
|
|
|
"%*c " /* state */
|
|
|
|
|
"%lu ", /* ppid */
|
|
|
|
|
&ppid) != 1)
|
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
|
|
if ((long unsigned) (pid_t) ppid != ppid)
|
|
|
|
|
return -ERANGE;
|
|
|
|
|
|
|
|
|
|
*_ppid = (pid_t) ppid;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-03 10:00:25 +02:00
|
|
|
|
int get_process_umask(pid_t pid, mode_t *umask) {
|
|
|
|
|
_cleanup_free_ char *m = NULL;
|
|
|
|
|
const char *p;
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
assert(umask);
|
|
|
|
|
assert(pid >= 0);
|
|
|
|
|
|
|
|
|
|
p = procfs_file_alloca(pid, "status");
|
|
|
|
|
|
|
|
|
|
r = get_proc_field(p, "Umask", WHITESPACE, &m);
|
|
|
|
|
if (r == -ENOENT)
|
|
|
|
|
return -ESRCH;
|
|
|
|
|
|
|
|
|
|
return parse_mode(m, umask);
|
|
|
|
|
}
|
|
|
|
|
|
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) {
|
|
|
|
|
r = get_process_comm(pid, &buffer);
|
|
|
|
|
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 18:49:26 +01:00
|
|
|
|
/*
|
|
|
|
|
* Return values:
|
2018-05-17 04:14:59 +02: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 18:49:26 +01:00
|
|
|
|
* >=0 : The process terminated normally with no failures.
|
|
|
|
|
*
|
2018-05-17 04:14:59 +02: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 18:49:26 +01: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. */
|
|
|
|
|
until = now(CLOCK_MONOTONIC) + timeout;
|
|
|
|
|
for (;;) {
|
|
|
|
|
usec_t n;
|
|
|
|
|
siginfo_t status = {};
|
|
|
|
|
struct timespec ts;
|
|
|
|
|
|
|
|
|
|
n = now(CLOCK_MONOTONIC);
|
|
|
|
|
if (n >= until)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
r = sigtimedwait(&mask, NULL, timespec_store(&ts, until - n)) < 0 ? -errno : 0;
|
|
|
|
|
/* Assuming we woke due to the child exiting. */
|
|
|
|
|
if (waitid(P_PID, pid, &status, WEXITED|WNOHANG) == 0) {
|
|
|
|
|
if (status.si_pid == pid) {
|
|
|
|
|
/* This is the correct child.*/
|
|
|
|
|
if (status.si_code == CLD_EXITED)
|
|
|
|
|
return (status.si_status == 0) ? 0 : -EPROTO;
|
|
|
|
|
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);
|
|
|
|
|
|
2018-11-20 04:35:36 +01:00
|
|
|
|
if (kill(pid, SIGKILL) >= 0)
|
2016-04-29 19:23:23 +02:00
|
|
|
|
(void) wait_for_terminate(pid, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 19:16:33 +01:00
|
|
|
|
void sigterm_wait(pid_t pid) {
|
|
|
|
|
assert(pid > 1);
|
|
|
|
|
|
2018-11-20 04:35:36 +01:00
|
|
|
|
if (kill_and_sigcont(pid, SIGTERM) >= 0)
|
2018-02-03 19:16:33 +01:00
|
|
|
|
(void) wait_for_terminate(pid, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-10 19:10:00 +02:00
|
|
|
|
int kill_and_sigcont(pid_t pid, int sig) {
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
r = kill(pid, sig) < 0 ? -errno : 0;
|
|
|
|
|
|
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 21:39:52 +01: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-18 02:57:22 +01: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;
|
|
|
|
|
|
|
|
|
|
if (pid <= 1)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
r = get_process_ppid(pid, &ppid);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
return ppid == getpid_cached();
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-10 19:10:00 +02:00
|
|
|
|
bool pid_is_unwaited(pid_t pid) {
|
|
|
|
|
/* Checks whether a PID is still valid at all, including a zombie */
|
|
|
|
|
|
2017-10-03 13:05:24 +02:00
|
|
|
|
if (pid < 0)
|
2015-04-10 19:10:00 +02:00
|
|
|
|
return false;
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool pid_is_alive(pid_t pid) {
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
/* Checks whether a PID is still valid and not a zombie */
|
|
|
|
|
|
2017-10-03 13:05:24 +02:00
|
|
|
|
if (pid < 0)
|
2015-04-10 19:10:00 +02:00
|
|
|
|
return false;
|
|
|
|
|
|
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);
|
2017-10-04 16:01:32 +02:00
|
|
|
|
if (IN_SET(r, -ESRCH, 'Z'))
|
2015-04-10 19:10:00 +02:00
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
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 13:05:24 +02: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");
|
|
|
|
|
|
2017-06-17 18:37:16 +02:00
|
|
|
|
return files_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
|
|
|
|
|
2018-03-15 06:23:46 +01:00
|
|
|
|
_noreturn_ void freeze(void) {
|
2015-10-27 14:24:58 +01:00
|
|
|
|
|
2016-05-21 18:40:34 +02:00
|
|
|
|
log_close();
|
|
|
|
|
|
2015-10-27 14:24:58 +01:00
|
|
|
|
/* Make sure nobody waits for us on a socket anymore */
|
2019-03-15 15:35:49 +01:00
|
|
|
|
(void) close_all_fds(NULL, 0);
|
2015-10-27 14:24:58 +01:00
|
|
|
|
|
|
|
|
|
sync();
|
|
|
|
|
|
2018-01-12 13:05:48 +01:00
|
|
|
|
/* Let's not freeze right away, but keep reaping zombies. */
|
|
|
|
|
for (;;) {
|
|
|
|
|
int r;
|
|
|
|
|
siginfo_t si = {};
|
|
|
|
|
|
|
|
|
|
r = waitid(P_ALL, 0, &si, WEXITED);
|
|
|
|
|
if (r < 0 && errno != EINTR)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* waitid() failed with an unexpected error, things are really borked. Freeze now! */
|
2015-10-27 14:24:58 +01:00
|
|
|
|
for (;;)
|
|
|
|
|
pause();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool oom_score_adjust_is_valid(int oa) {
|
|
|
|
|
return oa >= OOM_SCORE_ADJ_MIN && oa <= OOM_SCORE_ADJ_MAX;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned long personality_from_string(const char *p) {
|
2016-02-22 15:39:52 +01:00
|
|
|
|
int 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;
|
2016-02-22 18:29:05 +01:00
|
|
|
|
#ifdef SECONDARY_ARCHITECTURE
|
|
|
|
|
if (architecture == SECONDARY_ARCHITECTURE)
|
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) {
|
2016-02-22 15:39:52 +01:00
|
|
|
|
int 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();
|
|
|
|
|
#ifdef SECONDARY_ARCHITECTURE
|
2016-02-22 15:39:52 +01:00
|
|
|
|
else if (p == PER_LINUX32)
|
2016-02-22 18:29:05 +01:00
|
|
|
|
architecture = SECONDARY_ARCHITECTURE;
|
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 16:48:45 +01: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 16:48:45 +01:00
|
|
|
|
pid_t pid;
|
2016-05-30 02:03:51 +02:00
|
|
|
|
pid = raw_clone(SIGCHLD);
|
2016-01-19 16:48:45 +01:00
|
|
|
|
if (pid < 0)
|
|
|
|
|
log_emergency_errno(errno, "Failed to fork off valgrind helper: %m");
|
|
|
|
|
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 01:39:24 +02: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 01:39:24 +02:00
|
|
|
|
return CMP(*a, *b);
|
2016-04-20 15:28:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
2017-06-26 17:40:08 +02:00
|
|
|
|
int ioprio_parse_priority(const char *s, int *ret) {
|
|
|
|
|
int i, r;
|
|
|
|
|
|
|
|
|
|
assert(s);
|
|
|
|
|
assert(ret);
|
|
|
|
|
|
|
|
|
|
r = safe_atoi(s, &i);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
if (!ioprio_priority_is_valid(i))
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
*ret = i;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* We use glibc __register_atfork() + __dso_handle directly here, as they are not included in the glibc
|
|
|
|
|
* headers. __register_atfork() is mostly equivalent to pthread_atfork(), but doesn't require us to link against
|
|
|
|
|
* libpthread, as it is part of glibc anyway. */
|
2018-07-16 20:06:33 +02:00
|
|
|
|
extern int __register_atfork(void (*prepare) (void), void (*parent) (void), void (*child) (void), void *dso_handle);
|
2018-12-03 13:08:33 +01:00
|
|
|
|
extern void* __dso_handle _weak_;
|
2017-07-20 15:46:05 +02:00
|
|
|
|
|
|
|
|
|
pid_t getpid_cached(void) {
|
2018-02-26 20:50:57 +01:00
|
|
|
|
static bool installed = false;
|
2017-07-20 15:46:05 +02:00
|
|
|
|
pid_t current_value;
|
|
|
|
|
|
|
|
|
|
/* 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
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
current_value = __sync_val_compare_and_swap(&cached_pid, CACHED_PID_UNSET, CACHED_PID_BUSY);
|
|
|
|
|
|
|
|
|
|
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. */
|
|
|
|
|
|
|
|
|
|
if (__register_atfork(NULL, NULL, reset_cached_pid, __dso_handle) != 0) {
|
|
|
|
|
/* 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 17:17:49 +01:00
|
|
|
|
static void restore_sigsetp(sigset_t **ssp) {
|
|
|
|
|
if (*ssp)
|
|
|
|
|
(void) sigprocmask(SIG_SETMASK, *ssp, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-22 13:08:14 +01:00
|
|
|
|
int safe_fork_full(
|
|
|
|
|
const char *name,
|
|
|
|
|
const int except_fds[],
|
|
|
|
|
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;
|
2020-02-11 17:17:49 +01:00
|
|
|
|
_cleanup_(restore_sigsetp) sigset_t *saved_ssp = NULL;
|
2020-02-10 23:17:02 +01:00
|
|
|
|
bool block_signals = false, block_all = false;
|
2017-12-27 21:49:19 +01:00
|
|
|
|
int prio, r;
|
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();
|
|
|
|
|
|
2017-12-29 18:01:37 +01:00
|
|
|
|
if (flags & (FORK_RESET_SIGNALS|FORK_DEATHSIG)) {
|
|
|
|
|
/* 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. */
|
2017-12-22 13:08:14 +01:00
|
|
|
|
|
2018-12-21 09:20:15 +01:00
|
|
|
|
assert_se(sigfillset(&ss) >= 0);
|
2020-02-10 23:17:02 +01: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 17:17:49 +01: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 17:17:49 +01:00
|
|
|
|
saved_ssp = &saved_ss;
|
|
|
|
|
}
|
2017-12-29 18:01:37 +01:00
|
|
|
|
|
2017-12-29 18:52:20 +01:00
|
|
|
|
if (flags & FORK_NEW_MOUNTNS)
|
|
|
|
|
pid = raw_clone(SIGCHLD|CLONE_NEWNS);
|
|
|
|
|
else
|
|
|
|
|
pid = fork();
|
2020-02-11 17:17:49 +01:00
|
|
|
|
if (pid < 0)
|
|
|
|
|
return log_full_errno(prio, errno, "Failed to fork: %m");
|
2017-12-22 13:08:14 +01:00
|
|
|
|
if (pid > 0) {
|
|
|
|
|
/* 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 23:17:02 +01: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 17:17:49 +01: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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
2020-06-09 08:59:33 +02:00
|
|
|
|
if (flags & (FORK_DEATHSIG|FORK_DEATHSIG_SIGINT))
|
|
|
|
|
if (prctl(PR_SET_PDEATHSIG, (flags & FORK_DEATHSIG_SIGINT) ? SIGINT : SIGTERM) < 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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (flags & FORK_DEATHSIG) {
|
2018-01-04 12:37:15 +01: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 12:37:15 +01:00
|
|
|
|
ppid = getppid();
|
|
|
|
|
if (ppid == 0)
|
2020-07-04 10:37:01 +02:00
|
|
|
|
/* Parent is in a different PID namespace. */;
|
2018-01-04 12:37:15 +01:00
|
|
|
|
else if (ppid != original_pid) {
|
2017-12-22 13:08:14 +01:00
|
|
|
|
log_debug("Parent died early, raising SIGTERM.");
|
|
|
|
|
(void) raise(SIGTERM);
|
|
|
|
|
_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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (flags & FORK_NULL_STDIO) {
|
|
|
|
|
r = make_null_stdio();
|
|
|
|
|
if (r < 0) {
|
2017-12-27 21:49:19 +01:00
|
|
|
|
log_full_errno(prio, r, "Failed to connect stdin/stdout to /dev/null: %m");
|
2017-12-22 13:08:14 +01:00
|
|
|
|
_exit(EXIT_FAILURE);
|
|
|
|
|
}
|
2019-05-06 22:38:43 +02:00
|
|
|
|
|
|
|
|
|
} else if (flags & FORK_STDOUT_TO_STDERR) {
|
|
|
|
|
if (dup2(STDERR_FILENO, STDOUT_FILENO) < 0) {
|
2019-12-04 14:51:33 +01:00
|
|
|
|
log_full_errno(prio, errno, "Failed to connect stdout to stderr: %m");
|
2019-05-06 22:38:43 +02:00
|
|
|
|
_exit(EXIT_FAILURE);
|
|
|
|
|
}
|
2017-12-22 13:08:14 +01:00
|
|
|
|
}
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-22 13:08:14 +01:00
|
|
|
|
if (ret_pid)
|
|
|
|
|
*ret_pid = getpid_cached();
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-12 23:37:13 +01:00
|
|
|
|
int namespace_fork(
|
|
|
|
|
const char *outer_name,
|
|
|
|
|
const char *inner_name,
|
|
|
|
|
const int except_fds[],
|
|
|
|
|
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. */
|
|
|
|
|
|
|
|
|
|
r = safe_fork_full(outer_name, except_fds, n_except_fds, (flags|FORK_DEATHSIG) & ~(FORK_REOPEN_LOG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE), ret_pid);
|
|
|
|
|
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 */
|
|
|
|
|
r = safe_fork_full(inner_name, except_fds, n_except_fds, flags & ~(FORK_WAIT|FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_NULL_STDIO), &pid);
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
tree-wide: be more careful with the type of array sizes
Previously we were a bit sloppy with the index and size types of arrays,
we'd regularly use unsigned. While I don't think this ever resulted in
real issues I think we should be more careful there and follow a
stricter regime: unless there's a strong reason not to use size_t for
array sizes and indexes, size_t it should be. Any allocations we do
ultimately will use size_t anyway, and converting forth and back between
unsigned and size_t will always be a source of problems.
Note that on 32bit machines "unsigned" and "size_t" are equivalent, and
on 64bit machines our arrays shouldn't grow that large anyway, and if
they do we have a problem, however that kind of overly large allocation
we have protections for usually, but for overflows we do not have that
so much, hence let's add it.
So yeah, it's a story of the current code being already "good enough",
but I think some extra type hygiene is better.
This patch tries to be comprehensive, but it probably isn't and I missed
a few cases. But I guess we can cover that later as we notice it. Among
smaller fixes, this changes:
1. strv_length()' return type becomes size_t
2. the unit file changes array size becomes size_t
3. DNS answer and query array sizes become size_t
Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=76745
2018-04-27 14:09:31 +02:00
|
|
|
|
int fork_agent(const char *name, const int except[], size_t n_except, pid_t *ret_pid, const char *path, ...) {
|
2017-12-22 15:07:22 +01:00
|
|
|
|
bool stdout_is_tty, stderr_is_tty;
|
tree-wide: be more careful with the type of array sizes
Previously we were a bit sloppy with the index and size types of arrays,
we'd regularly use unsigned. While I don't think this ever resulted in
real issues I think we should be more careful there and follow a
stricter regime: unless there's a strong reason not to use size_t for
array sizes and indexes, size_t it should be. Any allocations we do
ultimately will use size_t anyway, and converting forth and back between
unsigned and size_t will always be a source of problems.
Note that on 32bit machines "unsigned" and "size_t" are equivalent, and
on 64bit machines our arrays shouldn't grow that large anyway, and if
they do we have a problem, however that kind of overly large allocation
we have protections for usually, but for overflows we do not have that
so much, hence let's add it.
So yeah, it's a story of the current code being already "good enough",
but I think some extra type hygiene is better.
This patch tries to be comprehensive, but it probably isn't and I missed
a few cases. But I guess we can cover that later as we notice it. Among
smaller fixes, this changes:
1. strv_length()' return type becomes size_t
2. the unit file changes array size becomes size_t
3. DNS answer and query array sizes become size_t
Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=76745
2018-04-27 14:09:31 +02:00
|
|
|
|
size_t n, i;
|
2017-12-22 15:07:22 +01:00
|
|
|
|
va_list ap;
|
|
|
|
|
char **l;
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
assert(path);
|
|
|
|
|
|
|
|
|
|
/* Spawns a temporary TTY agent, making sure it goes away when we go away */
|
|
|
|
|
|
|
|
|
|
r = safe_fork_full(name, except, n_except, FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS, ret_pid);
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
if (r > 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
/* In the child: */
|
|
|
|
|
|
|
|
|
|
stdout_is_tty = isatty(STDOUT_FILENO);
|
|
|
|
|
stderr_is_tty = isatty(STDERR_FILENO);
|
|
|
|
|
|
|
|
|
|
if (!stdout_is_tty || !stderr_is_tty) {
|
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
|
|
/* Detach from stdout/stderr. and reopen
|
|
|
|
|
* /dev/tty for them. This is important to
|
|
|
|
|
* ensure that when systemctl is started via
|
|
|
|
|
* popen() or a similar call that expects to
|
|
|
|
|
* read EOF we actually do generate EOF and
|
|
|
|
|
* not delay this indefinitely by because we
|
|
|
|
|
* keep an unused copy of stdin around. */
|
|
|
|
|
fd = open("/dev/tty", O_WRONLY);
|
|
|
|
|
if (fd < 0) {
|
|
|
|
|
log_error_errno(errno, "Failed to open /dev/tty: %m");
|
|
|
|
|
_exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!stdout_is_tty && dup2(fd, STDOUT_FILENO) < 0) {
|
|
|
|
|
log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
|
|
|
|
|
_exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!stderr_is_tty && dup2(fd, STDERR_FILENO) < 0) {
|
|
|
|
|
log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
|
|
|
|
|
_exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-26 15:41:38 +01:00
|
|
|
|
safe_close_above_stdio(fd);
|
2017-12-22 15:07:22 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-26 16:06:26 +01:00
|
|
|
|
(void) rlimit_nofile_safe();
|
|
|
|
|
|
2017-12-22 15:07:22 +01:00
|
|
|
|
/* Count arguments */
|
|
|
|
|
va_start(ap, path);
|
|
|
|
|
for (n = 0; va_arg(ap, char*); n++)
|
|
|
|
|
;
|
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
|
|
/* Allocate strv */
|
2018-04-27 14:28:35 +02:00
|
|
|
|
l = newa(char*, n + 1);
|
2017-12-22 15:07:22 +01:00
|
|
|
|
|
|
|
|
|
/* Fill in arguments */
|
|
|
|
|
va_start(ap, path);
|
|
|
|
|
for (i = 0; i <= n; i++)
|
|
|
|
|
l[i] = va_arg(ap, char*);
|
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
|
|
execv(path, l);
|
|
|
|
|
_exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-07 20:44:41 +02:00
|
|
|
|
int set_oom_score_adjust(int value) {
|
|
|
|
|
char t[DECIMAL_STR_MAX(int)];
|
|
|
|
|
|
|
|
|
|
sprintf(t, "%i", value);
|
|
|
|
|
|
|
|
|
|
return write_string_file("/proc/self/oom_score_adj", t,
|
|
|
|
|
WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_DISABLE_BUFFER);
|
|
|
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
if (fd < 0)
|
|
|
|
|
return -EBADF;
|
|
|
|
|
|
|
|
|
|
xsprintf(path, "/proc/self/fdinfo/%i", fd);
|
|
|
|
|
|
|
|
|
|
r = read_full_file(path, &fdinfo, NULL);
|
|
|
|
|
if (r == -ENOENT) /* if fdinfo doesn't exist we assume the process does not exist */
|
|
|
|
|
return -ESRCH;
|
|
|
|
|
if (r < 0)
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
|
|
p = startswith(fdinfo, "Pid:");
|
|
|
|
|
if (!p) {
|
|
|
|
|
p = strstr(fdinfo, "\nPid:");
|
|
|
|
|
if (!p)
|
|
|
|
|
return -ENOTTY; /* not a pidfd? */
|
|
|
|
|
|
|
|
|
|
p += 5;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p += strspn(p, WHITESPACE);
|
|
|
|
|
p[strcspn(p, WHITESPACE)] = 0;
|
|
|
|
|
|
|
|
|
|
return parse_pid(p, ret);
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-01 18:38:05 +02: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;
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-27 14:24:58 +01:00
|
|
|
|
static const char *const ioprio_class_table[] = {
|
|
|
|
|
[IOPRIO_CLASS_NONE] = "none",
|
|
|
|
|
[IOPRIO_CLASS_RT] = "realtime",
|
|
|
|
|
[IOPRIO_CLASS_BE] = "best-effort",
|
2019-05-28 21:28:31 +02:00
|
|
|
|
[IOPRIO_CLASS_IDLE] = "idle",
|
2015-10-27 14:24:58 +01:00
|
|
|
|
};
|
|
|
|
|
|
2018-03-16 11:15:58 +01:00
|
|
|
|
DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ioprio_class, int, IOPRIO_N_CLASSES);
|
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);
|