mirror of
https://github.com/systemd/systemd.git
synced 2024-12-25 01:34:28 +03:00
systemctl: add "systemctl cat"
This commit is contained in:
parent
f74294c1da
commit
e93c33d4aa
1
TODO
1
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
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 },
|
||||
|
Loading…
Reference in New Issue
Block a user