diff options
-rw-r--r-- | TODO | 1 | ||||
-rw-r--r-- | man/systemctl.xml | 9 | ||||
-rw-r--r-- | src/shared/fileio.c | 73 | ||||
-rw-r--r-- | src/shared/fileio.h | 1 | ||||
-rw-r--r-- | src/shared/util.c | 2 | ||||
-rw-r--r-- | src/systemctl/systemctl.c | 103 |
6 files changed, 187 insertions, 2 deletions
@@ -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 diff --git a/man/systemctl.xml b/man/systemctl.xml index 567f39839c..5a15e5ca05 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -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> diff --git a/src/shared/fileio.c b/src/shared/fileio.c index 733b320388..ac1b409a1c 100644 --- a/src/shared/fileio.c +++ b/src/shared/fileio.c @@ -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; diff --git a/src/shared/fileio.h b/src/shared/fileio.h index 59e41502b1..06c2887157 100644 --- a/src/shared/fileio.h +++ b/src/shared/fileio.h @@ -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); diff --git a/src/shared/util.c b/src/shared/util.c index 206fc803d0..305a6c2fb6 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -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; diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index e10721362d..f7b2fb4019 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -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 }, |