mirror of
https://github.com/systemd/systemd.git
synced 2024-11-01 17:51:22 +03:00
util-lib: rework get_process_cmdline() (#3529)
This reworks get_process_cmdline() quite substantially, fixing the following:
- Fixes:
a4e3bf4d7a (r66837630)
- The passed max_length is also applied to the "comm" name, if comm_fallback is
set.
- The right thing happens if max_length == 1 is specified
- when the cmdline "foobar" is abbreviated to 6 characters the result is not
"foobar" instead of "foo...".
- trailing whitespace are removed before the ... suffix is appended. The 7
character abbreviation of "foo barz" is hence "foo..." instead of "foo ...".
- leading whitespace are suppressed from the cmdline
- a comprehensive test case is added
This commit is contained in:
parent
8dd57192bc
commit
69281c49eb
@ -110,6 +110,15 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
|
||||
assert(line);
|
||||
assert(pid >= 0);
|
||||
|
||||
/* Retrieves a process' command line. Replaces unprintable characters while doing so by whitespace (coalescing
|
||||
* multiple sequential ones into one). If max_length is != 0 will return a string of the specified size at most
|
||||
* (the trailing NUL byte does count towards the length here!), abbreviated with a "..." ellipsis. If
|
||||
* comm_fallback is true 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.
|
||||
*
|
||||
* Returns -ESRCH if the process doesn't exist, and -ENOENT if the process has no command line (and
|
||||
* comm_fallback is false). */
|
||||
|
||||
p = procfs_file_alloca(pid, "cmdline");
|
||||
|
||||
f = fopen(p, "re");
|
||||
@ -119,12 +128,22 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (max_length == 0) {
|
||||
if (max_length == 1) {
|
||||
|
||||
/* If there's only room for one byte, return the empty string */
|
||||
r = new0(char, 1);
|
||||
if (!r)
|
||||
return -ENOMEM;
|
||||
|
||||
*line = r;
|
||||
return 0;
|
||||
|
||||
} else if (max_length == 0) {
|
||||
size_t len = 0, allocated = 0;
|
||||
|
||||
while ((c = getc(f)) != EOF) {
|
||||
|
||||
if (!GREEDY_REALLOC(r, allocated, len+2)) {
|
||||
if (!GREEDY_REALLOC(r, allocated, len+3)) {
|
||||
free(r);
|
||||
return -ENOMEM;
|
||||
}
|
||||
@ -136,7 +155,7 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
|
||||
}
|
||||
|
||||
r[len++] = c;
|
||||
} else
|
||||
} else if (len > 0)
|
||||
space = true;
|
||||
}
|
||||
|
||||
@ -144,6 +163,7 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
|
||||
r[len] = 0;
|
||||
|
||||
} else {
|
||||
bool dotdotdot = false;
|
||||
size_t left;
|
||||
|
||||
r = new(char, max_length);
|
||||
@ -155,28 +175,46 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
|
||||
while ((c = getc(f)) != EOF) {
|
||||
|
||||
if (isprint(c)) {
|
||||
|
||||
if (space) {
|
||||
if (left <= 4)
|
||||
if (left <= 2) {
|
||||
dotdotdot = true;
|
||||
break;
|
||||
}
|
||||
|
||||
*(k++) = ' ';
|
||||
left--;
|
||||
space = false;
|
||||
}
|
||||
|
||||
if (left <= 4)
|
||||
if (left <= 1) {
|
||||
dotdotdot = true;
|
||||
break;
|
||||
}
|
||||
|
||||
*(k++) = (char) c;
|
||||
left--;
|
||||
} else
|
||||
} else if (k > r)
|
||||
space = true;
|
||||
}
|
||||
|
||||
if (left <= 4) {
|
||||
size_t n = MIN(left-1, 3U);
|
||||
memcpy(k, "...", n);
|
||||
k[n] = 0;
|
||||
if (dotdotdot) {
|
||||
if (max_length <= 4) {
|
||||
k = r;
|
||||
left = max_length;
|
||||
} else {
|
||||
k = r + max_length - 4;
|
||||
left = 4;
|
||||
|
||||
/* Eat up final spaces */
|
||||
while (k > r && isspace(k[-1])) {
|
||||
k--;
|
||||
left++;
|
||||
}
|
||||
}
|
||||
|
||||
strncpy(k, "...", left-1);
|
||||
k[left] = 0;
|
||||
} else
|
||||
*k = 0;
|
||||
}
|
||||
@ -195,7 +233,37 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
|
||||
if (h < 0)
|
||||
return h;
|
||||
|
||||
r = strjoin("[", t, "]", NULL);
|
||||
if (max_length == 0)
|
||||
r = strjoin("[", t, "]", NULL);
|
||||
else {
|
||||
size_t l;
|
||||
|
||||
l = strlen(t);
|
||||
|
||||
if (l + 3 <= max_length)
|
||||
r = strjoin("[", t, "]", NULL);
|
||||
else if (max_length <= 6) {
|
||||
|
||||
r = new(char, max_length);
|
||||
if (!r)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(r, "[...]", max_length-1);
|
||||
r[max_length-1] = 0;
|
||||
} else {
|
||||
char *e;
|
||||
|
||||
t[max_length - 6] = 0;
|
||||
|
||||
/* Chop off final spaces */
|
||||
e = strchr(t, 0);
|
||||
while (e > t && isspace(e[-1]))
|
||||
e--;
|
||||
*e = 0;
|
||||
|
||||
r = strjoin("[", t, "...]", NULL);
|
||||
}
|
||||
}
|
||||
if (!r)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
@ -18,7 +18,10 @@
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <sched.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/personality.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
@ -26,6 +29,7 @@
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "architecture.h"
|
||||
#include "fd-util.h"
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
#include "parse-util.h"
|
||||
@ -59,7 +63,11 @@ static void test_get_process_comm(pid_t pid) {
|
||||
log_info("PID"PID_FMT" cmdline: '%s'", pid, c);
|
||||
|
||||
assert_se(get_process_cmdline(pid, 8, false, &d) >= 0);
|
||||
log_info("PID"PID_FMT" cmdline truncated: '%s'", pid, d);
|
||||
log_info("PID"PID_FMT" cmdline truncated to 8: '%s'", pid, d);
|
||||
|
||||
free(d);
|
||||
assert_se(get_process_cmdline(pid, 1, false, &d) >= 0);
|
||||
log_info("PID"PID_FMT" cmdline truncated to 1: '%s'", pid, d);
|
||||
|
||||
assert_se(get_process_ppid(pid, &e) >= 0);
|
||||
log_info("PID"PID_FMT" PPID: "PID_FMT, pid, e);
|
||||
@ -147,6 +155,187 @@ static void test_personality(void) {
|
||||
#endif
|
||||
}
|
||||
|
||||
static void test_get_process_cmdline_harder(void) {
|
||||
char path[] = "/tmp/test-cmdlineXXXXXX";
|
||||
_cleanup_close_ int fd = -1;
|
||||
_cleanup_free_ char *line = NULL;
|
||||
pid_t pid;
|
||||
|
||||
if (geteuid() != 0)
|
||||
return;
|
||||
|
||||
pid = fork();
|
||||
if (pid > 0) {
|
||||
siginfo_t si;
|
||||
|
||||
(void) wait_for_terminate(pid, &si);
|
||||
|
||||
assert_se(si.si_code == CLD_EXITED);
|
||||
assert_se(si.si_status == 0);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
assert_se(pid == 0);
|
||||
assert_se(unshare(CLONE_NEWNS) >= 0);
|
||||
|
||||
fd = mkostemp(path, O_CLOEXEC);
|
||||
assert_se(fd >= 0);
|
||||
assert_se(mount(path, "/proc/self/cmdline", "bind", MS_BIND, NULL) >= 0);
|
||||
assert_se(unlink(path) >= 0);
|
||||
|
||||
assert_se(prctl(PR_SET_NAME, "testa") >= 0);
|
||||
|
||||
assert_se(get_process_cmdline(getpid(), 0, false, &line) == -ENOENT);
|
||||
|
||||
assert_se(get_process_cmdline(getpid(), 0, true, &line) >= 0);
|
||||
assert_se(streq(line, "[testa]"));
|
||||
line = mfree(line);
|
||||
|
||||
assert_se(get_process_cmdline(getpid(), 1, true, &line) >= 0);
|
||||
assert_se(streq(line, ""));
|
||||
line = mfree(line);
|
||||
|
||||
assert_se(get_process_cmdline(getpid(), 2, true, &line) >= 0);
|
||||
assert_se(streq(line, "["));
|
||||
line = mfree(line);
|
||||
|
||||
assert_se(get_process_cmdline(getpid(), 3, true, &line) >= 0);
|
||||
assert_se(streq(line, "[."));
|
||||
line = mfree(line);
|
||||
|
||||
assert_se(get_process_cmdline(getpid(), 4, true, &line) >= 0);
|
||||
assert_se(streq(line, "[.."));
|
||||
line = mfree(line);
|
||||
|
||||
assert_se(get_process_cmdline(getpid(), 5, true, &line) >= 0);
|
||||
assert_se(streq(line, "[..."));
|
||||
line = mfree(line);
|
||||
|
||||
assert_se(get_process_cmdline(getpid(), 6, true, &line) >= 0);
|
||||
assert_se(streq(line, "[...]"));
|
||||
line = mfree(line);
|
||||
|
||||
assert_se(get_process_cmdline(getpid(), 7, true, &line) >= 0);
|
||||
assert_se(streq(line, "[t...]"));
|
||||
line = mfree(line);
|
||||
|
||||
assert_se(get_process_cmdline(getpid(), 8, true, &line) >= 0);
|
||||
assert_se(streq(line, "[testa]"));
|
||||
line = mfree(line);
|
||||
|
||||
assert_se(write(fd, "\0\0\0\0\0\0\0\0\0", 10) == 10);
|
||||
|
||||
assert_se(get_process_cmdline(getpid(), 0, false, &line) == -ENOENT);
|
||||
|
||||
assert_se(get_process_cmdline(getpid(), 0, true, &line) >= 0);
|
||||
assert_se(streq(line, "[testa]"));
|
||||
line = mfree(line);
|
||||
|
||||
assert_se(write(fd, "foo\0bar\0\0\0\0\0", 10) == 10);
|
||||
|
||||
assert_se(get_process_cmdline(getpid(), 0, false, &line) >= 0);
|
||||
assert_se(streq(line, "foo bar"));
|
||||
line = mfree(line);
|
||||
|
||||
assert_se(get_process_cmdline(getpid(), 0, true, &line) >= 0);
|
||||
assert_se(streq(line, "foo bar"));
|
||||
line = mfree(line);
|
||||
|
||||
assert_se(write(fd, "quux", 4) == 4);
|
||||
assert_se(get_process_cmdline(getpid(), 0, false, &line) >= 0);
|
||||
assert_se(streq(line, "foo bar quux"));
|
||||
line = mfree(line);
|
||||
|
||||
assert_se(get_process_cmdline(getpid(), 0, true, &line) >= 0);
|
||||
assert_se(streq(line, "foo bar quux"));
|
||||
line = mfree(line);
|
||||
|
||||
assert_se(get_process_cmdline(getpid(), 1, true, &line) >= 0);
|
||||
assert_se(streq(line, ""));
|
||||
line = mfree(line);
|
||||
|
||||
assert_se(get_process_cmdline(getpid(), 2, true, &line) >= 0);
|
||||
assert_se(streq(line, "."));
|
||||
line = mfree(line);
|
||||
|
||||
assert_se(get_process_cmdline(getpid(), 3, true, &line) >= 0);
|
||||
assert_se(streq(line, ".."));
|
||||
line = mfree(line);
|
||||
|
||||
assert_se(get_process_cmdline(getpid(), 4, true, &line) >= 0);
|
||||
assert_se(streq(line, "..."));
|
||||
line = mfree(line);
|
||||
|
||||
assert_se(get_process_cmdline(getpid(), 5, true, &line) >= 0);
|
||||
assert_se(streq(line, "f..."));
|
||||
line = mfree(line);
|
||||
|
||||
assert_se(get_process_cmdline(getpid(), 6, true, &line) >= 0);
|
||||
assert_se(streq(line, "fo..."));
|
||||
line = mfree(line);
|
||||
|
||||
assert_se(get_process_cmdline(getpid(), 7, true, &line) >= 0);
|
||||
assert_se(streq(line, "foo..."));
|
||||
line = mfree(line);
|
||||
|
||||
assert_se(get_process_cmdline(getpid(), 8, true, &line) >= 0);
|
||||
assert_se(streq(line, "foo..."));
|
||||
line = mfree(line);
|
||||
|
||||
assert_se(get_process_cmdline(getpid(), 9, true, &line) >= 0);
|
||||
assert_se(streq(line, "foo b..."));
|
||||
line = mfree(line);
|
||||
|
||||
assert_se(get_process_cmdline(getpid(), 10, true, &line) >= 0);
|
||||
assert_se(streq(line, "foo ba..."));
|
||||
line = mfree(line);
|
||||
|
||||
assert_se(get_process_cmdline(getpid(), 11, true, &line) >= 0);
|
||||
assert_se(streq(line, "foo bar..."));
|
||||
line = mfree(line);
|
||||
|
||||
assert_se(get_process_cmdline(getpid(), 12, true, &line) >= 0);
|
||||
assert_se(streq(line, "foo bar..."));
|
||||
line = mfree(line);
|
||||
|
||||
assert_se(get_process_cmdline(getpid(), 13, true, &line) >= 0);
|
||||
assert_se(streq(line, "foo bar quux"));
|
||||
line = mfree(line);
|
||||
|
||||
assert_se(get_process_cmdline(getpid(), 14, true, &line) >= 0);
|
||||
assert_se(streq(line, "foo bar quux"));
|
||||
line = mfree(line);
|
||||
|
||||
assert_se(get_process_cmdline(getpid(), 1000, true, &line) >= 0);
|
||||
assert_se(streq(line, "foo bar quux"));
|
||||
line = mfree(line);
|
||||
|
||||
assert_se(ftruncate(fd, 0) >= 0);
|
||||
assert_se(prctl(PR_SET_NAME, "aaaa bbbb cccc") >= 0);
|
||||
|
||||
assert_se(get_process_cmdline(getpid(), 0, false, &line) == -ENOENT);
|
||||
|
||||
assert_se(get_process_cmdline(getpid(), 0, true, &line) >= 0);
|
||||
assert_se(streq(line, "[aaaa bbbb cccc]"));
|
||||
line = mfree(line);
|
||||
|
||||
assert_se(get_process_cmdline(getpid(), 10, true, &line) >= 0);
|
||||
assert_se(streq(line, "[aaaa...]"));
|
||||
line = mfree(line);
|
||||
|
||||
assert_se(get_process_cmdline(getpid(), 11, true, &line) >= 0);
|
||||
assert_se(streq(line, "[aaaa...]"));
|
||||
line = mfree(line);
|
||||
|
||||
assert_se(get_process_cmdline(getpid(), 12, true, &line) >= 0);
|
||||
assert_se(streq(line, "[aaaa b...]"));
|
||||
line = mfree(line);
|
||||
|
||||
safe_close(fd);
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
log_parse_environment();
|
||||
log_open();
|
||||
@ -164,6 +353,7 @@ int main(int argc, char *argv[]) {
|
||||
test_pid_is_unwaited();
|
||||
test_pid_is_alive();
|
||||
test_personality();
|
||||
test_get_process_cmdline_harder();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user