summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Makefile.am14
-rw-r--r--man/systemd-cgls.xml106
-rw-r--r--src/cgroup-show.c172
-rw-r--r--src/cgroup-show.h3
-rw-r--r--src/systemctl.c28
-rw-r--r--src/systemd-cgls.c121
-rw-r--r--src/util.c27
-rw-r--r--src/util.h2
9 files changed, 424 insertions, 50 deletions
diff --git a/.gitignore b/.gitignore
index 1632e3f196..98b13dd7e3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+systemd-cgls
systemd.pc
test-cgroup
.libs/
diff --git a/Makefile.am b/Makefile.am
index eb911fc896..debe475967 100644
--- a/Makefile.am
+++ b/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
diff --git a/man/systemd-cgls.xml b/man/systemd-cgls.xml
new file mode 100644
index 0000000000..da3e0cac73
--- /dev/null
+++ b/man/systemd-cgls.xml
@@ -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>
diff --git a/src/cgroup-show.c b/src/cgroup-show.c
index b637c1c163..b44180e1fb 100644
--- a/src/cgroup-show.c
+++ b/src/cgroup-show.c
@@ -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;
}
diff --git a/src/cgroup-show.h b/src/cgroup-show.h
index 3def2f3095..1f0c319054 100644
--- a/src/cgroup-show.h
+++ b/src/cgroup-show.h
@@ -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
diff --git a/src/systemctl.c b/src/systemctl.c
index bb831adb5c..a9a975ee81 100644
--- a/src/systemctl.c
+++ b/src/systemctl.c
@@ -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;
diff --git a/src/systemd-cgls.c b/src/systemd-cgls.c
new file mode 100644
index 0000000000..6614f0fd25
--- /dev/null
+++ b/src/systemd-cgls.c
@@ -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;
+}
diff --git a/src/util.c b/src/util.c
index 6cbc7ce477..2e5827f32d 100644
--- a/src/util.c
+++ b/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",
diff --git a/src/util.h b/src/util.h
index b29f890b5f..5d51c07e2f 100644
--- a/src/util.h
+++ b/src/util.h
@@ -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);