cgls: beef up control group dumping and introduce cgls tool

This commit is contained in:
Lennart Poettering 2010-07-08 21:01:42 +02:00
parent ab35fb1bc6
commit fa776d8e96
9 changed files with 424 additions and 50 deletions

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
systemd-cgls
systemd.pc
test-cgroup
.libs/

View file

@ -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
View 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>

View file

@ -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;
}

View file

@ -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

View file

@ -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
View 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;
}

View file

@ -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",

View file

@ -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);