Commit fa776d8e authored by Lennart Poettering's avatar Lennart Poettering
Browse files

cgls: beef up control group dumping and introduce cgls tool

parent ab35fb1b
systemd-cgls
systemd.pc
test-cgroup
.libs/
......
......@@ -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
......
<?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 = "";
if (!(p = get_cgroup_path(name)))
return -ENOMEM;
f = fopen(fn, "r");
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;
......
/*-*- 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;
}
......@@ -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);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment