mirror of
https://github.com/systemd/systemd.git
synced 2024-11-30 13:53:39 +08:00
cgls: beef up control group dumping and introduce cgls tool
This commit is contained in:
parent
ab35fb1bc6
commit
fa776d8e96
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
systemd-cgls
|
||||
systemd.pc
|
||||
test-cgroup
|
||||
.libs/
|
||||
|
14
Makefile.am
14
Makefile.am
@ -58,7 +58,8 @@ rootbin_PROGRAMS = \
|
||||
systemd-notify
|
||||
|
||||
bin_PROGRAMS = \
|
||||
systemd-install
|
||||
systemd-install \
|
||||
systemd-cgls
|
||||
|
||||
if HAVE_GTK
|
||||
bin_PROGRAMS += \
|
||||
@ -320,6 +321,7 @@ MANPAGES = \
|
||||
man/systemctl.1 \
|
||||
man/systemadm.1 \
|
||||
man/systemd-install.1 \
|
||||
man/systemd-cgls.1 \
|
||||
man/systemd-notify.1 \
|
||||
man/sd_notify.3 \
|
||||
man/sd_booted.3 \
|
||||
@ -511,6 +513,16 @@ systemd_install_CFLAGS = \
|
||||
$(AM_CFLAGS) \
|
||||
$(DBUS_CFLAGS)
|
||||
|
||||
systemd_cgls_SOURCES = \
|
||||
src/systemd-cgls.c \
|
||||
src/cgroup-show.c
|
||||
|
||||
systemd_cgls_LDADD = \
|
||||
libsystemd-basic.la
|
||||
|
||||
systemd_cgls_CFLAGS = \
|
||||
$(AM_CFLAGS)
|
||||
|
||||
systemadm_SOURCES = \
|
||||
src/systemadm.vala \
|
||||
src/systemd-interfaces.vala
|
||||
|
106
man/systemd-cgls.xml
Normal file
106
man/systemd-cgls.xml
Normal file
@ -0,0 +1,106 @@
|
||||
<?xml version='1.0'?> <!--*-nxml-*-->
|
||||
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
||||
|
||||
<!--
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2010 Lennart Poettering
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<refentry id="systemd-cgls">
|
||||
|
||||
<refentryinfo>
|
||||
<title>systemd-cgls</title>
|
||||
<productname>systemd</productname>
|
||||
|
||||
<authorgroup>
|
||||
<author>
|
||||
<contrib>Developer</contrib>
|
||||
<firstname>Lennart</firstname>
|
||||
<surname>Poettering</surname>
|
||||
<email>lennart@poettering.net</email>
|
||||
</author>
|
||||
</authorgroup>
|
||||
</refentryinfo>
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>systemd-cgls</refentrytitle>
|
||||
<manvolnum>1</manvolnum>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>systemd-cgls</refname>
|
||||
<refpurpose>Recursively show control group contents</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<cmdsynopsis>
|
||||
<command>systemd-cgls <arg choice="opt" rep="repeat">OPTIONS</arg> <arg choice="opt" rep="repeat">CGROUP</arg></command>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para><command>systemd-cgls</command> recursively
|
||||
shows the contents of the selected Linux control group
|
||||
hierarchy in a tree. If arguments are specified shows
|
||||
all member processes of the specified control groups
|
||||
plus all their subgroups and their members. The
|
||||
control groups may either be specified by their full
|
||||
file paths or are assumed in the systemd control group
|
||||
hierarchy. If no argument is specified and the current
|
||||
working directory is beneath the control group mount
|
||||
point <filename>/cgroup</filename> shows the contents
|
||||
of the control group the working directory refers
|
||||
to. Otherwise the full systemd control group hierarchy
|
||||
is shown.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Options</title>
|
||||
|
||||
<para>The following options are understood:</para>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><option>--help</option></term>
|
||||
|
||||
<listitem><para>Prints a short help
|
||||
text and exits.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Exit status</title>
|
||||
|
||||
<para>On success 0 is returned, a non-zero failure
|
||||
code otherwise.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>See Also</title>
|
||||
<para>
|
||||
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
</refentry>
|
@ -21,6 +21,8 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "macro.h"
|
||||
@ -36,25 +38,57 @@ static int compare(const void *a, const void *b) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void show_cgroup(const char *name, const char *prefix, unsigned columns) {
|
||||
static char *get_cgroup_path(const char *name) {
|
||||
|
||||
if (startswith(name, "name=systemd:"))
|
||||
name += 13;
|
||||
|
||||
if (path_startswith(name, "/cgroup"))
|
||||
return strdup(name);
|
||||
|
||||
return strappend("/cgroup/systemd/", name);
|
||||
}
|
||||
|
||||
static unsigned ilog10(unsigned long ul) {
|
||||
int n = 0;
|
||||
|
||||
while (ul > 0) {
|
||||
n++;
|
||||
ul /= 10;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static int show_cgroup_full(const char *name, const char *prefix, unsigned n_columns, bool more) {
|
||||
char *fn;
|
||||
FILE *f;
|
||||
size_t n = 0, n_allocated = 0;
|
||||
pid_t *pids = NULL;
|
||||
char *p;
|
||||
int r;
|
||||
unsigned long biggest = 0;
|
||||
|
||||
if (!startswith(name, "name=systemd:"))
|
||||
return;
|
||||
if (n_columns <= 0)
|
||||
n_columns = columns();
|
||||
|
||||
if (asprintf(&fn, "/cgroup/systemd/%s/cgroup.procs", name + 13) < 0) {
|
||||
log_error("Out of memory");
|
||||
return;
|
||||
}
|
||||
if (!prefix)
|
||||
prefix = "";
|
||||
|
||||
f = fopen(fn, "r");
|
||||
if (!(p = get_cgroup_path(name)))
|
||||
return -ENOMEM;
|
||||
|
||||
r = asprintf(&fn, "%s/cgroup.procs", p);
|
||||
free(p);
|
||||
|
||||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
f = fopen(fn, "re");
|
||||
free(fn);
|
||||
|
||||
if (!f)
|
||||
return;
|
||||
return -errno;
|
||||
|
||||
while (!feof(f)) {
|
||||
unsigned long ul;
|
||||
@ -71,7 +105,7 @@ void show_cgroup(const char *name, const char *prefix, unsigned columns) {
|
||||
n_allocated = MAX(16U, n*2U);
|
||||
|
||||
if (!(npids = realloc(pids, sizeof(pid_t) * n_allocated))) {
|
||||
log_error("Out of memory");
|
||||
r = -ENOMEM;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
@ -80,6 +114,9 @@ void show_cgroup(const char *name, const char *prefix, unsigned columns) {
|
||||
|
||||
assert(n < n_allocated);
|
||||
pids[n++] = (pid_t) ul;
|
||||
|
||||
if (ul > biggest)
|
||||
biggest = ul;
|
||||
}
|
||||
|
||||
if (n > 0) {
|
||||
@ -102,33 +139,124 @@ void show_cgroup(const char *name, const char *prefix, unsigned columns) {
|
||||
/* And sort */
|
||||
qsort(pids, n, sizeof(pid_t), compare);
|
||||
|
||||
if (!prefix)
|
||||
prefix = "";
|
||||
|
||||
if (columns > 8)
|
||||
columns -= 8;
|
||||
if (n_columns > 8)
|
||||
n_columns -= 8;
|
||||
else
|
||||
columns = 20;
|
||||
|
||||
printf("%s\342\224\202\n", prefix);
|
||||
n_columns = 20;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
char *t = NULL;
|
||||
|
||||
get_process_cmdline(pids[i], columns, &t);
|
||||
get_process_cmdline(pids[i], n_columns, &t);
|
||||
|
||||
printf("%s%s %5lu %s\n",
|
||||
printf("%s%s %*lu %s\n",
|
||||
prefix,
|
||||
i < n-1 ? "\342\224\234" : "\342\224\224",
|
||||
(unsigned long) pids[i], strna(t));
|
||||
(more || i < n-1) ? "\342\224\234" : "\342\224\224",
|
||||
ilog10(biggest),
|
||||
(unsigned long) pids[i],
|
||||
strna(t));
|
||||
|
||||
free(t);
|
||||
}
|
||||
}
|
||||
|
||||
r = 0;
|
||||
|
||||
finish:
|
||||
free(pids);
|
||||
|
||||
if (f)
|
||||
fclose(f);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int show_cgroup(const char *name, const char *prefix, unsigned n_columns) {
|
||||
return show_cgroup_full(name, prefix, n_columns, false);
|
||||
}
|
||||
|
||||
int show_cgroup_recursive(const char *name, const char *prefix, unsigned n_columns) {
|
||||
DIR *d;
|
||||
char *last = NULL;
|
||||
char *p1 = NULL, *p2 = NULL, *fn = NULL;
|
||||
struct dirent *de;
|
||||
bool shown_pids = false;
|
||||
int r;
|
||||
|
||||
assert(name);
|
||||
|
||||
if (n_columns <= 0)
|
||||
n_columns = columns();
|
||||
|
||||
if (!prefix)
|
||||
prefix = "";
|
||||
|
||||
if (!(fn = get_cgroup_path(name)))
|
||||
return -ENOMEM;
|
||||
|
||||
if (!(d = opendir(fn))) {
|
||||
free(fn);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
while ((de = readdir(d))) {
|
||||
|
||||
if (de->d_type != DT_DIR)
|
||||
continue;
|
||||
|
||||
if (ignore_file(de->d_name))
|
||||
continue;
|
||||
|
||||
if (!shown_pids) {
|
||||
show_cgroup_full(name, prefix, n_columns, true);
|
||||
shown_pids = true;
|
||||
}
|
||||
|
||||
if (last) {
|
||||
printf("%s\342\224\234 %s\n", prefix, file_name_from_path(last));
|
||||
|
||||
if (!p1)
|
||||
if (!(p1 = strappend(prefix, "\342\224\202 "))) {
|
||||
r = -ENOMEM;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
show_cgroup_recursive(last, p1, n_columns-2);
|
||||
|
||||
free(last);
|
||||
last = NULL;
|
||||
}
|
||||
|
||||
if (asprintf(&last, "%s/%s", name, de->d_name) < 0) {
|
||||
log_error("Out of memory");
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
|
||||
if (!shown_pids)
|
||||
show_cgroup_full(name, prefix, n_columns, !!last);
|
||||
|
||||
if (last) {
|
||||
printf("%s\342\224\224 %s\n", prefix, file_name_from_path(last));
|
||||
|
||||
if (!p2)
|
||||
if (!(p2 = strappend(prefix, " "))) {
|
||||
r = -ENOMEM;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
show_cgroup_recursive(last, p2, n_columns-2);
|
||||
}
|
||||
|
||||
r = 0;
|
||||
|
||||
finish:
|
||||
free(p1);
|
||||
free(p2);
|
||||
free(last);
|
||||
free(fn);
|
||||
|
||||
closedir(d);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
void show_cgroup(const char *name, const char *prefix, unsigned columns);
|
||||
int show_cgroup(const char *name, const char *prefix, unsigned columns);
|
||||
int show_cgroup_recursive(const char *name, const char *prefix, unsigned columns);
|
||||
|
||||
#endif
|
||||
|
@ -110,30 +110,6 @@ static int bus_iter_get_basic_and_next(DBusMessageIter *iter, int type, void *da
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int columns(void) {
|
||||
static int parsed_columns = 0;
|
||||
const char *e;
|
||||
|
||||
if (parsed_columns > 0)
|
||||
return parsed_columns;
|
||||
|
||||
if ((e = getenv("COLUMNS")))
|
||||
parsed_columns = atoi(e);
|
||||
|
||||
if (parsed_columns <= 0) {
|
||||
struct winsize ws;
|
||||
zero(ws);
|
||||
|
||||
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) >= 0)
|
||||
parsed_columns = ws.ws_col;
|
||||
}
|
||||
|
||||
if (parsed_columns <= 0)
|
||||
parsed_columns = 80;
|
||||
|
||||
return parsed_columns;
|
||||
}
|
||||
|
||||
static void warn_wall(enum action action) {
|
||||
static const char *table[_ACTION_MAX] = {
|
||||
[ACTION_HALT] = "The system is going down for system halt NOW!",
|
||||
@ -1076,7 +1052,7 @@ static void print_status_info(UnitStatusInfo *i) {
|
||||
else
|
||||
c = 0;
|
||||
|
||||
show_cgroup(i->default_control_group, "\t\t ", c);
|
||||
show_cgroup_recursive(i->default_control_group, "\t\t ", c);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3068,7 +3044,7 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[]) {
|
||||
{ "reboot", EQUAL, 1, start_special },
|
||||
{ "default", EQUAL, 1, start_special },
|
||||
{ "rescue", EQUAL, 1, start_special },
|
||||
{ "emergency", EQUAL, 1, start_special },
|
||||
{ "emergency", EQUAL, 1, start_special }
|
||||
};
|
||||
|
||||
int left;
|
||||
|
121
src/systemd-cgls.c
Normal file
121
src/systemd-cgls.c
Normal file
@ -0,0 +1,121 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2010 Lennart Poettering
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "cgroup-show.h"
|
||||
#include "log.h"
|
||||
#include "util.h"
|
||||
|
||||
static void help(void) {
|
||||
|
||||
printf("%s [OPTIONS...] [CGROUP...]\n\n"
|
||||
"Recursively show control group contents.\n\n"
|
||||
" -h --help Show this help\n",
|
||||
program_invocation_short_name);
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
|
||||
static const struct option options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
int c;
|
||||
|
||||
assert(argc >= 1);
|
||||
assert(argv);
|
||||
|
||||
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
|
||||
|
||||
switch (c) {
|
||||
|
||||
case 'h':
|
||||
help();
|
||||
return 0;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
default:
|
||||
log_error("Unknown option code %c", c);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int r = 0, retval = 1;
|
||||
|
||||
log_parse_environment();
|
||||
|
||||
if ((r = parse_argv(argc, argv)) < 0)
|
||||
goto finish;
|
||||
else if (r == 0) {
|
||||
retval = 0;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (optind < argc) {
|
||||
unsigned i;
|
||||
|
||||
for (i = (unsigned) optind; i < (unsigned) argc; i++) {
|
||||
int q;
|
||||
printf("%s:\n", argv[i]);
|
||||
|
||||
if ((q = show_cgroup_recursive(argv[i], NULL, 0)) < 0)
|
||||
r = q;
|
||||
}
|
||||
|
||||
} else {
|
||||
char *p;
|
||||
|
||||
if (!(p = get_current_dir_name())) {
|
||||
log_error("Cannot determine current working directory: %m");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (path_startswith(p, "/cgroup")) {
|
||||
printf("Working Directory %s:\n", p);
|
||||
r = show_cgroup_recursive(p, NULL, 0);
|
||||
} else
|
||||
r = show_cgroup_recursive("", NULL, 0);
|
||||
|
||||
free(p);
|
||||
}
|
||||
|
||||
if (r < 0)
|
||||
log_error("Failed to list cgroup tree: %s", strerror(-r));
|
||||
|
||||
retval = 0;
|
||||
|
||||
finish:
|
||||
|
||||
return retval;
|
||||
}
|
27
src/util.c
27
src/util.c
@ -609,6 +609,9 @@ int get_process_cmdline(pid_t pid, size_t max_length, char **line) {
|
||||
|
||||
fclose(f);
|
||||
|
||||
if (r[0] == 0)
|
||||
return get_process_name(pid, line);
|
||||
|
||||
*line = r;
|
||||
return 0;
|
||||
}
|
||||
@ -2798,6 +2801,30 @@ char **replace_env_argv(char **argv, char **env) {
|
||||
return r;
|
||||
}
|
||||
|
||||
int columns(void) {
|
||||
static __thread int parsed_columns = 0;
|
||||
const char *e;
|
||||
|
||||
if (parsed_columns > 0)
|
||||
return parsed_columns;
|
||||
|
||||
if ((e = getenv("COLUMNS")))
|
||||
parsed_columns = atoi(e);
|
||||
|
||||
if (parsed_columns <= 0) {
|
||||
struct winsize ws;
|
||||
zero(ws);
|
||||
|
||||
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) >= 0)
|
||||
parsed_columns = ws.ws_col;
|
||||
}
|
||||
|
||||
if (parsed_columns <= 0)
|
||||
parsed_columns = 80;
|
||||
|
||||
return parsed_columns;
|
||||
}
|
||||
|
||||
static const char *const ioprio_class_table[] = {
|
||||
[IOPRIO_CLASS_NONE] = "none",
|
||||
[IOPRIO_CLASS_RT] = "realtime",
|
||||
|
@ -328,6 +328,8 @@ void status_vprintf(const char *format, va_list ap);
|
||||
void status_printf(const char *format, ...);
|
||||
void status_welcome(void);
|
||||
|
||||
int columns(void);
|
||||
|
||||
const char *ioprio_class_to_string(int i);
|
||||
int ioprio_class_from_string(const char *s);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user