[systemd-devel] [PATCH 2/2] systemctl: add "systemctl cat"
Shawn Landden
shawn at churchofgit.com
Fri Nov 22 13:37:43 PST 2013
---
TODO | 2 --
src/shared/fileio.c | 73 ++++++++++++++++++++++++++++++++++++-
src/shared/fileio.h | 1 +
src/systemctl/systemctl.c | 91 +++++++++++++++++++++++++++++++++++++++--------
4 files changed, 149 insertions(+), 18 deletions(-)
diff --git a/TODO b/TODO
index 6ba4b31..7c6003b 100644
--- a/TODO
+++ b/TODO
@@ -125,8 +125,6 @@ Features:
* optimize the cgroup propagation bits, especially unit_get_members_mask(), cgroup_context_get_mask()
-* "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
-
* rfkill,backlight: we probably should run the load tools inside of the udev rules so that the state is properly initialized by the time other software sees it
* tmpfiles: when applying ownership to /run/log/journal, also do this for the journal fails contained in it
diff --git a/src/shared/fileio.c b/src/shared/fileio.c
index 733b320..ac1b409 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 59e4150..06c2887 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/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 576396f..18d5e45 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -3665,16 +3665,25 @@ static int show_all(
return 0;
}
-static int get_unit_from_arg(sd_bus *bus, char *arg, char **unitp, bool interpret_as_job_id) {
- uint32_t id;
+static int cat(sd_bus *bus, char **args) {
int r = 0;
+ char **name;
- if (safe_atou32(arg, &id) < 0) {
- _cleanup_free_ char *n = NULL;
- char *unit;
- /* Interpret as unit name */
+ _cleanup_free_ char *unit = NULL, *n = NULL;
+
+ assert(bus);
+ assert(args);
- n = unit_name_mangle(arg);
+ 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();
@@ -3682,14 +3691,64 @@ static int get_unit_from_arg(sd_bus *bus, char *arg, char **unitp, bool interpre
if (!unit)
return log_oom();
- *unitp = unit;
- } else if (interpret_as_job_id) {
- /* Interpret as job id */
- if (asprintf(unitp, "/org/freedesktop/systemd1/job/%u", id) < 0)
- return log_oom();
- } else {
- /* Interpret as PID */
- r = get_unit_by_pid(bus, id, unitp);
+ if (need_daemon_reload(bus, n)) {
+ log_error("Unit file of %s changed on disk. Run 'systemctl%s daemon-reload'.",
+ n, arg_scope == UNIT_FILE_SYSTEM ? "" : " --user");
+ r = -ENODATA;
+ continue;
+ }
+
+ 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))
+ 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;
+ }
+
+ r = sendfile_full(STDOUT_FILENO, fragment_path);
+ if (r < 0) {
+ log_warning("Failed to cat %s: %s\n", fragment_path, strerror(-r));
+ continue;
+ }
+
+ STRV_FOREACH(path, dropin_paths) {
+ if (r < 0) {
+ log_warning("Failed to cat %s: %s\n", *path, strerror(-r));
+ continue;
+ }
+ }
}
return r;
@@ -4731,6 +4790,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"
@@ -5708,6 +5768,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 },
--
1.8.4.3
More information about the systemd-devel
mailing list