Merge pull request #4845 from poettering/various-smaller-fixes
Various smaller fixes
This commit is contained in:
+89
-13
@@ -27,6 +27,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/personality.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/types.h>
|
||||
@@ -274,27 +275,100 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rename_process(const char name[8]) {
|
||||
assert(name);
|
||||
int rename_process(const char name[]) {
|
||||
static size_t mm_size = 0;
|
||||
static char *mm = NULL;
|
||||
bool truncated = false;
|
||||
size_t l;
|
||||
|
||||
/* 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 usually one of 10 (i.e. length
|
||||
* of "/sbin/init"), to the third one one of 7 (i.e. length of
|
||||
* "systemd"). If you pass a longer string it will be
|
||||
* truncated */
|
||||
/* 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 */
|
||||
|
||||
l = strlen(name);
|
||||
|
||||
/* First step, change the comm field. */
|
||||
(void) prctl(PR_SET_NAME, name);
|
||||
if (l > 15) /* Linux process names can be 15 chars at max */
|
||||
truncated = true;
|
||||
|
||||
if (program_invocation_name)
|
||||
strncpy(program_invocation_name, name, strlen(program_invocation_name));
|
||||
/* 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.*/
|
||||
if (mm_size < l+1) {
|
||||
size_t nn_size;
|
||||
char *nn;
|
||||
|
||||
/* Let's not bother with this if we don't have euid == 0. Strictly speaking if people do weird stuff
|
||||
* with capabilities this could work even for euid != 0, but our own code generally doesn't do that,
|
||||
* hence let's use this as quick bypass check, to avoid calling mmap() if PR_SET_MM_ARG_START fails
|
||||
* with EPERM later on anyway. After all geteuid() is dead cheap to call, but mmap() is not. */
|
||||
if (geteuid() != 0) {
|
||||
log_debug("Skipping PR_SET_MM_ARG_START, as we don't have privileges.");
|
||||
goto use_saved_argv;
|
||||
}
|
||||
|
||||
nn_size = PAGE_ALIGN(l+1);
|
||||
nn = mmap(NULL, nn_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
|
||||
if (nn == MAP_FAILED) {
|
||||
log_debug_errno(errno, "mmap() failed: %m");
|
||||
goto use_saved_argv;
|
||||
}
|
||||
|
||||
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) {
|
||||
log_debug_errno(errno, "PR_SET_MM_ARG_START failed, proceeding without: %m");
|
||||
(void) munmap(nn, nn_size);
|
||||
goto use_saved_argv;
|
||||
}
|
||||
|
||||
/* 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");
|
||||
|
||||
if (mm)
|
||||
(void) munmap(mm, mm_size);
|
||||
|
||||
mm = nn;
|
||||
mm_size = nn_size;
|
||||
} else
|
||||
strncpy(mm, name, mm_size);
|
||||
|
||||
use_saved_argv:
|
||||
/* 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 */
|
||||
|
||||
if (saved_argc > 0) {
|
||||
int i;
|
||||
|
||||
if (saved_argv[0])
|
||||
strncpy(saved_argv[0], name, strlen(saved_argv[0]));
|
||||
if (saved_argv[0]) {
|
||||
size_t k;
|
||||
|
||||
k = strlen(saved_argv[0]);
|
||||
strncpy(saved_argv[0], name, k);
|
||||
if (l > k)
|
||||
truncated = true;
|
||||
}
|
||||
|
||||
for (i = 1; i < saved_argc; i++) {
|
||||
if (!saved_argv[i])
|
||||
@@ -303,6 +377,8 @@ void rename_process(const char name[8]) {
|
||||
memzero(saved_argv[i], strlen(saved_argv[i]));
|
||||
}
|
||||
}
|
||||
|
||||
return !truncated;
|
||||
}
|
||||
|
||||
int is_kernel_thread(pid_t pid) {
|
||||
|
||||
@@ -64,7 +64,7 @@ void sigkill_waitp(pid_t *pid);
|
||||
|
||||
int kill_and_sigcont(pid_t pid, int sig);
|
||||
|
||||
void rename_process(const char name[8]);
|
||||
int rename_process(const char name[]);
|
||||
int is_kernel_thread(pid_t pid);
|
||||
|
||||
int getenv_for_pid(pid_t pid, const char *field, char **_value);
|
||||
|
||||
+45
-38
@@ -409,8 +409,7 @@ int detect_container(void) {
|
||||
if (cached_found >= 0)
|
||||
return cached_found;
|
||||
|
||||
/* /proc/vz exists in container and outside of the container,
|
||||
* /proc/bc only outside of the container. */
|
||||
/* /proc/vz exists in container and outside of the container, /proc/bc only outside of the container. */
|
||||
if (access("/proc/vz", F_OK) >= 0 &&
|
||||
access("/proc/bc", F_OK) < 0) {
|
||||
r = VIRTUALIZATION_OPENVZ;
|
||||
@@ -418,50 +417,58 @@ int detect_container(void) {
|
||||
}
|
||||
|
||||
if (getpid() == 1) {
|
||||
/* If we are PID 1 we can just check our own
|
||||
* environment variable */
|
||||
/* If we are PID 1 we can just check our own environment variable, and that's authoritative. */
|
||||
|
||||
e = getenv("container");
|
||||
if (isempty(e)) {
|
||||
r = VIRTUALIZATION_NONE;
|
||||
goto finish;
|
||||
}
|
||||
} else {
|
||||
|
||||
/* Otherwise, PID 1 dropped this information into a
|
||||
* file in /run. This is better than accessing
|
||||
* /proc/1/environ, since we don't need CAP_SYS_PTRACE
|
||||
* for that. */
|
||||
|
||||
r = read_one_line_file("/run/systemd/container", &m);
|
||||
if (r == -ENOENT) {
|
||||
|
||||
/* Fallback for cases where PID 1 was not
|
||||
* systemd (for example, cases where
|
||||
* init=/bin/sh is used. */
|
||||
|
||||
r = getenv_for_pid(1, "container", &m);
|
||||
if (r <= 0) {
|
||||
|
||||
/* If that didn't work, give up,
|
||||
* assume no container manager.
|
||||
*
|
||||
* Note: This means we still cannot
|
||||
* detect containers if init=/bin/sh
|
||||
* is passed but privileges dropped,
|
||||
* as /proc/1/environ is only readable
|
||||
* with privileges. */
|
||||
|
||||
r = VIRTUALIZATION_NONE;
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
e = m;
|
||||
goto translate_name;
|
||||
}
|
||||
|
||||
/* Otherwise, PID 1 might have dropped this information into a file in /run. This is better than accessing
|
||||
* /proc/1/environ, since we don't need CAP_SYS_PTRACE for that. */
|
||||
r = read_one_line_file("/run/systemd/container", &m);
|
||||
if (r >= 0) {
|
||||
e = m;
|
||||
goto translate_name;
|
||||
}
|
||||
if (r != -ENOENT)
|
||||
return log_debug_errno(r, "Failed to read /run/systemd/container: %m");
|
||||
|
||||
/* Fallback for cases where PID 1 was not systemd (for example, cases where init=/bin/sh is used. */
|
||||
r = getenv_for_pid(1, "container", &m);
|
||||
if (r > 0) {
|
||||
e = m;
|
||||
goto translate_name;
|
||||
}
|
||||
if (r < 0) /* This only works if we have CAP_SYS_PTRACE, hence let's better ignore failures here */
|
||||
log_debug_errno(r, "Failed to read $container of PID 1, ignoring: %m");
|
||||
|
||||
/* Interestingly /proc/1/sched actually shows the host's PID for what we see as PID 1. Hence, if the PID shown
|
||||
* there is not 1, we know we are in a PID namespace. and hence a container. */
|
||||
r = read_one_line_file("/proc/1/sched", &m);
|
||||
if (r >= 0) {
|
||||
const char *t;
|
||||
|
||||
t = strrchr(m, '(');
|
||||
if (!t)
|
||||
return -EIO;
|
||||
|
||||
if (!startswith(t, "(1,")) {
|
||||
r = VIRTUALIZATION_CONTAINER_OTHER;
|
||||
goto finish;
|
||||
}
|
||||
} else if (r != -ENOENT)
|
||||
return r;
|
||||
|
||||
/* If that didn't work, give up, assume no container manager. */
|
||||
r = VIRTUALIZATION_NONE;
|
||||
goto finish;
|
||||
|
||||
translate_name:
|
||||
for (j = 0; j < ELEMENTSOF(value_table); j++)
|
||||
if (streq(e, value_table[j].value)) {
|
||||
r = value_table[j].id;
|
||||
@@ -471,7 +478,7 @@ int detect_container(void) {
|
||||
r = VIRTUALIZATION_CONTAINER_OTHER;
|
||||
|
||||
finish:
|
||||
log_debug("Found container virtualization %s", virtualization_to_string(r));
|
||||
log_debug("Found container virtualization %s.", virtualization_to_string(r));
|
||||
cached_found = r;
|
||||
return r;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user