chiark / gitweb /
systemctl: add "systemctl cat"
authorShawn Landden <shawn@churchofgit.com>
Sun, 1 Dec 2013 00:09:26 +0000 (16:09 -0800)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Sun, 1 Dec 2013 03:20:20 +0000 (22:20 -0500)
TODO
man/systemctl.xml
src/shared/fileio.c
src/shared/fileio.h
src/shared/util.c
src/systemctl/systemctl.c

diff --git a/TODO b/TODO
index e4acb15..cfabfca 100644 (file)
--- a/TODO
+++ b/TODO
@@ -356,7 +356,6 @@ Features:
   - support "systemctl stop foobar@.service" to stop all units matching a certain template
   - Something is wrong with symlink handling of "autovt@.service" in "systemctl list-unit-files"
   - rework wait filter to not require match callback
-  - "systemctl cat" or "systemctl view" command or or so, that cats the backing unit file of a service, plus its drop-ins and shows them in a pager
   - better error message if you run systemctl without systemd running
   - systemctl status output should should include list of triggering units and their status
   - in systemctl list-timers show time trggering units ran last
index 567f398..5a15e5c 100644 (file)
@@ -733,7 +733,16 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
             human-readable output.</para>
           </listitem>
         </varlistentry>
+        <varlistentry>
+          <term><command>cat <replaceable>NAME</replaceable>...</command></term>
 
+          <listitem>
+            <para>Show backing files of one or more units.
+            Prints the fragment, drop-ins, and source (sysvinit compat)
+            of units. Each file is preceded by a comment which includes the
+            file name.</para>
+          </listitem>
+        </varlistentry>
         <varlistentry>
           <term><command>set-property <replaceable>NAME</replaceable> <replaceable>ASSIGNMENT</replaceable>...</command></term>
 
index 733b320..ac1b409 100644 (file)
@@ -20,6 +20,7 @@
 ***/
 
 #include <unistd.h>
+#include <sys/sendfile.h>
 #include "fileio.h"
 #include "util.h"
 #include "strv.h"
@@ -117,6 +118,77 @@ int read_one_line_file(const char *fn, char **line) {
         return 0;
 }
 
+ssize_t sendfile_full(int out_fd, const char *fn) {
+        _cleanup_fclose_ FILE *f;
+        struct stat st;
+        int r;
+        ssize_t s;
+
+        size_t n, l;
+        _cleanup_free_ char *buf = NULL;
+
+        assert(out_fd > 0);
+        assert(fn);
+
+        f = fopen(fn, "r");
+        if (!f)
+                return -errno;
+
+        r = fstat(fileno(f), &st);
+        if (r < 0)
+                return -errno;
+
+        s = sendfile(out_fd, fileno(f), NULL, st.st_size);
+        if (s < 0)
+                if (errno == EINVAL || errno == ENOSYS) {
+                        /* continue below */
+                } else
+                        return -errno;
+        else
+                return s;
+
+        /* sendfile() failed, fall back to read/write */
+
+        /* Safety check */
+        if (st.st_size > 4*1024*1024)
+                return -E2BIG;
+
+        n = st.st_size > 0 ? st.st_size : LINE_MAX;
+        l = 0;
+
+        while (true) {
+                char *t;
+                size_t k;
+
+                t = realloc(buf, n);
+                if (!t)
+                        return -ENOMEM;
+
+                buf = t;
+                k = fread(buf + l, 1, n - l, f);
+
+                if (k <= 0) {
+                        if (ferror(f))
+                                return -errno;
+
+                        break;
+                }
+
+                l += k;
+                n *= 2;
+
+                /* Safety check */
+                if (n > 4*1024*1024)
+                        return -E2BIG;
+        }
+
+        r = write(out_fd, buf, l);
+        if (r < 0)
+                return -errno;
+
+        return (ssize_t) l;
+}
+
 int read_full_file(const char *fn, char **contents, size_t *size) {
         _cleanup_fclose_ FILE *f = NULL;
         size_t n, l;
@@ -168,7 +240,6 @@ int read_full_file(const char *fn, char **contents, size_t *size) {
 
         buf[l] = 0;
         *contents = buf;
-        buf = NULL;
 
         if (size)
                 *size = l;
index 59e4150..06c2887 100644 (file)
@@ -31,6 +31,7 @@ int write_string_file_atomic(const char *fn, const char *line);
 
 int read_one_line_file(const char *fn, char **line);
 int read_full_file(const char *fn, char **contents, size_t *size);
+ssize_t sendfile_full(int out_fd, const char *fn);
 
 int parse_env_file(const char *fname, const char *separator, ...) _sentinel_;
 int load_env_file(const char *fname, const char *separator, char ***l);
index 206fc80..305a6c2 100644 (file)
@@ -5791,6 +5791,8 @@ void* greedy_realloc(void **p, size_t *allocated, size_t need) {
         size_t a;
         void *q;
 
+        assert(allocated);
+
         if (*allocated >= need)
                 return *p;
 
index e107213..f7b2fb4 100644 (file)
@@ -3675,6 +3675,107 @@ static int show_all(
         return 0;
 }
 
+static int cat(sd_bus *bus, char **args) {
+        int r = 0;
+        char **name;
+
+        _cleanup_free_ char *unit = NULL, *n = NULL;
+
+        assert(bus);
+        assert(args);
+
+        pager_open_if_enabled();
+
+        STRV_FOREACH(name, args+1) {
+                _cleanup_free_ char *fragment_path = NULL;
+                _cleanup_strv_free_ char **dropin_paths = NULL;
+                sd_bus_error error;
+                FILE *stdout;
+                char **path;
+
+                n = unit_name_mangle(*name);
+                if (!n)
+                        return log_oom();
+
+                unit = unit_dbus_path_from_name(n);
+                if (!unit)
+                        return log_oom();
+
+                if (need_daemon_reload(bus, n) > 0)
+                        log_warning("Unit file of %s changed on disk. Run 'systemctl%s daemon-reload'.",
+                                    n, arg_scope == UNIT_FILE_SYSTEM ? "" : " --user");
+
+                r = sd_bus_get_property_string(
+                                bus,
+                                "org.freedesktop.systemd1",
+                                unit,
+                                "org.freedesktop.systemd1.Unit",
+                                "FragmentPath",
+                                &error,
+                                &fragment_path);
+                if (r < 0) {
+                        log_warning("Failed to get FragmentPath: %s", bus_error_message(&error, r));
+                        continue;
+                }
+
+                if (isempty(fragment_path)) {
+                        free(fragment_path);
+                        fragment_path = NULL;
+
+                        if (sd_bus_get_property_string(
+                                        bus,
+                                        "org.freedesktop.systemd1",
+                                        unit,
+                                        "org.freedesktop.systemd1.Unit",
+                                        "SourcePath",
+                                        &error,
+                                        &fragment_path) < 0) {
+                                log_warning("Failed to get SourcePath: %s", bus_error_message(&error, r));
+                                continue;
+                        }
+                }
+
+                r = sd_bus_get_property_strv(
+                                bus,
+                                "org.freedesktop.systemd1",
+                                unit,
+                                "org.freedesktop.systemd1.Unit",
+                                "DropInPaths",
+                                &error,
+                                &dropin_paths);
+                if (r < 0) {
+                        log_warning("Failed to get DropInPaths: %s", bus_error_message(&error, r));
+                        continue;
+                }
+
+                stdout = fdopen(STDOUT_FILENO, "a");
+
+                if (!isempty(fragment_path)) {
+                        fprintf(stdout, "# %s\n", fragment_path);
+                        fflush(stdout);
+                        r = sendfile_full(STDOUT_FILENO, fragment_path);
+                        if (r < 0) {
+                                log_warning("Failed to cat %s: %s", fragment_path, strerror(-r));
+                                continue;
+                        }
+                }
+
+                STRV_FOREACH(path, dropin_paths) {
+                        fprintf(stdout,   "%s# %s\n",
+                                isempty(fragment_path) && path == dropin_paths ? "" : "\n",
+                                *path);
+                        fflush(stdout);
+                        r = sendfile_full(STDOUT_FILENO, *path);
+                        if (r < 0) {
+                                log_warning("Failed to cat %s: %s", *path, strerror(-r));
+                                continue;
+                        }
+                }
+        }
+
+        return r;
+}
+
 static int show(sd_bus *bus, char **args) {
         int r, ret = 0;
         bool show_properties, show_status, new_line = false;
@@ -4707,6 +4808,7 @@ static int systemctl_help(void) {
                "  status [NAME...|PID...]         Show runtime status of one or more units\n"
                "  show [NAME...|JOB...]           Show properties of one or more\n"
                "                                  units/jobs or the manager\n"
+               "  cat [NAME...]                   Show files and drop-ins of one or more units\n"
                "  set-property [NAME] [ASSIGNMENT...]\n"
                "                                  Sets one or more properties of a unit\n"
                "  help [NAME...|PID...]           Show manual for one or more units\n"
@@ -5690,6 +5792,7 @@ static int systemctl_main(sd_bus *bus, int argc, char *argv[], int bus_error) {
                 { "check",                 MORE,  2, check_unit_active },
                 { "is-failed",             MORE,  2, check_unit_failed },
                 { "show",                  MORE,  1, show              },
+                { "cat",                   MORE,  2, cat               },
                 { "status",                MORE,  1, show              },
                 { "help",                  MORE,  2, show              },
                 { "snapshot",              LESS,  2, snapshot          },