From: Lennart Poettering Date: Thu, 3 Jul 2014 15:50:55 +0000 (+0200) Subject: machinectl: show /etc/os-release information of container in status output X-Git-Tag: v215~7 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=commitdiff_plain;h=717603e391b52983ca1fd218e7333a1b9dfc5c05 machinectl: show /etc/os-release information of container in status output --- diff --git a/src/core/execute.c b/src/core/execute.c index 1ea646334..88d094e8c 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -2023,7 +2023,7 @@ int exec_context_load_environment(const ExecContext *c, char ***l) { return -EINVAL; } for (n = 0; n < count; n++) { - k = load_env_file(pglob.gl_pathv[n], NULL, &p); + k = load_env_file(NULL, pglob.gl_pathv[n], NULL, &p); if (k < 0) { if (ignore) continue; diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c index c2b6d3d6f..14629dd3a 100644 --- a/src/hostname/hostnamed.c +++ b/src/hostname/hostnamed.c @@ -319,7 +319,7 @@ static int context_write_data_machine_info(Context *c) { assert(c); - r = load_env_file("/etc/machine-info", NULL, &l); + r = load_env_file(NULL, "/etc/machine-info", NULL, &l); if (r < 0 && r != -ENOENT) return r; diff --git a/src/locale/localed.c b/src/locale/localed.c index 358f6c200..23da149b0 100644 --- a/src/locale/localed.c +++ b/src/locale/localed.c @@ -288,7 +288,7 @@ static int locale_write_data(Context *c) { int r, p; char **l = NULL; - r = load_env_file("/etc/locale.conf", NULL, &l); + r = load_env_file(NULL, "/etc/locale.conf", NULL, &l); if (r < 0 && r != -ENOENT) return r; @@ -393,7 +393,7 @@ static int vconsole_write_data(Context *c) { int r; _cleanup_strv_free_ char **l = NULL; - r = load_env_file("/etc/vconsole.conf", NULL, &l); + r = load_env_file(NULL, "/etc/vconsole.conf", NULL, &l); if (r < 0 && r != -ENOENT) return r; diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index c9c3de0d0..14dae0a03 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -28,9 +28,11 @@ #include "bus-util.h" #include "bus-label.h" #include "strv.h" -#include "machine.h" #include "rtnl-util.h" #include "bus-errors.h" +#include "copy.h" +#include "fileio.h" +#include "machine.h" static int property_get_id( sd_bus *bus, @@ -333,6 +335,95 @@ int bus_machine_method_get_addresses(sd_bus *bus, sd_bus_message *message, void return sd_bus_send(bus, reply, NULL); } +int bus_machine_method_get_os_release(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + _cleanup_close_ int mntns_fd = -1, root_fd = -1; + _cleanup_close_pair_ int pair[2] = { -1, -1 }; + _cleanup_strv_free_ char **l = NULL; + _cleanup_fclose_ FILE *f = NULL; + Machine *m = userdata; + char **k, **v; + siginfo_t si; + pid_t child; + int r; + + assert(bus); + assert(message); + assert(m); + + r = namespace_open(m->leader, NULL, &mntns_fd, NULL, &root_fd); + if (r < 0) + return sd_bus_error_set_errno(error, r); + + if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0) + return sd_bus_error_set_errno(error, -errno); + + child = fork(); + if (child < 0) + return sd_bus_error_set_errno(error, -errno); + + if (child == 0) { + _cleanup_close_ int fd = -1; + + pair[0] = safe_close(pair[0]); + + r = namespace_enter(-1, mntns_fd, -1, root_fd); + if (r < 0) + _exit(EXIT_FAILURE); + + fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC); + if (fd < 0) { + fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC); + if (fd < 0) + _exit(EXIT_FAILURE); + } + + r = copy_bytes(fd, pair[1], (off_t) -1); + if (r < 0) + _exit(EXIT_FAILURE); + + _exit(EXIT_SUCCESS); + } + + pair[1] = safe_close(pair[1]); + + f = fdopen(pair[0], "re"); + if (!f) + return sd_bus_error_set_errno(error, -errno); + + pair[0] = -1; + + r = load_env_file_pairs(f, "/etc/os-release", NULL, &l); + if (r < 0) + return sd_bus_error_set_errno(error, r); + + r = wait_for_terminate(child, &si); + if (r < 0) + return sd_bus_error_set_errno(error, r); + if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) + return sd_bus_error_set_errno(error, EIO); + + r = sd_bus_message_new_method_return(message, &reply); + if (r < 0) + return sd_bus_error_set_errno(error, r); + + r = sd_bus_message_open_container(reply, 'a', "{ss}"); + if (r < 0) + return sd_bus_error_set_errno(error, r); + + STRV_FOREACH_PAIR(k, v, l) { + r = sd_bus_message_append(reply, "{ss}", *k, *v); + if (r < 0) + return sd_bus_error_set_errno(error, r); + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + return sd_bus_error_set_errno(error, r); + + return sd_bus_send(bus, reply, NULL); +} + const sd_bus_vtable machine_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Machine, name), SD_BUS_VTABLE_PROPERTY_CONST), @@ -348,7 +439,8 @@ const sd_bus_vtable machine_vtable[] = { SD_BUS_METHOD("Terminate", NULL, NULL, bus_machine_method_terminate, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)), SD_BUS_METHOD("Kill", "si", NULL, bus_machine_method_kill, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)), SD_BUS_METHOD("GetAddresses", NULL, "a(yay)", bus_machine_method_get_addresses, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_VTABLE_END + SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_machine_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_VTABLE_END }; int machine_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { diff --git a/src/machine/machine.h b/src/machine/machine.h index ed1c81c4f..fa9262d52 100644 --- a/src/machine/machine.h +++ b/src/machine/machine.h @@ -100,6 +100,7 @@ int machine_node_enumerator(sd_bus *bus, const char *path, void *userdata, char int bus_machine_method_terminate(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_machine_method_kill(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_machine_method_get_addresses(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_machine_method_get_os_release(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error); int machine_send_signal(Machine *m, bool new_machine); int machine_send_create_reply(Machine *m, sd_bus_error *error); diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index c2bf7e519..022a4ebe5 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -224,6 +224,48 @@ static int print_addresses(sd_bus *bus, const char *name, const char *prefix, co return 0; } +static int print_os_release(sd_bus *bus, const char *name, const char *prefix) { + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + const char *k, *v, *pretty = NULL; + int r; + + assert(bus); + assert(name); + assert(prefix); + + r = sd_bus_call_method(bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "GetMachineOSRelease", + NULL, + &reply, + "s", name); + if (r < 0) + return r; + + r = sd_bus_message_enter_container(reply, 'a', "{ss}"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) { + if (streq(k, "PRETTY_NAME")) + pretty = v; + + } + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return bus_log_parse_error(r); + + if (pretty) + printf("%s%s\n", prefix, pretty); + + return 0; +} + typedef struct MachineStatusInfo { char *name; sd_id128_t id; @@ -284,6 +326,8 @@ static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) { "\t Address: ", "\t "); + print_os_release(bus, i->name, "\t OS: "); + if (i->unit) { printf("\t Unit: %s\n", i->unit); show_unit_cgroup(bus, i->unit, i->leader); diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c index 7c1802ce2..ffcd7c026 100644 --- a/src/machine/machined-dbus.c +++ b/src/machine/machined-dbus.c @@ -371,6 +371,27 @@ static int method_get_machine_addresses(sd_bus *bus, sd_bus_message *message, vo return bus_machine_method_get_addresses(bus, message, machine, error); } +static int method_get_machine_os_release(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + Machine *machine; + const char *name; + int r; + + assert(bus); + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return sd_bus_error_set_errno(error, r); + + machine = hashmap_get(m->machines, name); + if (!machine) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); + + return bus_machine_method_get_os_release(bus, message, machine, error); +} + const sd_bus_vtable manager_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_METHOD("GetMachine", "s", "o", method_get_machine, SD_BUS_VTABLE_UNPRIVILEGED), @@ -381,6 +402,7 @@ const sd_bus_vtable manager_vtable[] = { SD_BUS_METHOD("KillMachine", "ssi", NULL, method_kill_machine, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)), SD_BUS_METHOD("TerminateMachine", "s", NULL, method_terminate_machine, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)), SD_BUS_METHOD("GetMachineAddresses", "s", "a(yay)", method_get_machine_addresses, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetMachineOSRelease", "s", "a{ss}", method_get_machine_os_release, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_SIGNAL("MachineNew", "so", 0), SD_BUS_SIGNAL("MachineRemoved", "so", 0), SD_BUS_VTABLE_END diff --git a/src/machine/org.freedesktop.machine1.conf b/src/machine/org.freedesktop.machine1.conf index ab349a536..3a77c70bf 100644 --- a/src/machine/org.freedesktop.machine1.conf +++ b/src/machine/org.freedesktop.machine1.conf @@ -56,6 +56,10 @@ send_interface="org.freedesktop.machine1.Machine" send_member="GetAddresses"/> + + diff --git a/src/shared/fileio.c b/src/shared/fileio.c index c5806249f..fb1c1bcf9 100644 --- a/src/shared/fileio.c +++ b/src/shared/fileio.c @@ -27,8 +27,12 @@ #include "utf8.h" #include "ctype.h" -int write_string_to_file(FILE *f, const char *line) { +int write_string_stream(FILE *f, const char *line) { + assert(f); + assert(line); + errno = 0; + fputs(line, f); if (!endswith(line, "\n")) fputc('\n', f); @@ -51,7 +55,7 @@ int write_string_file(const char *fn, const char *line) { if (!f) return -errno; - return write_string_to_file(f, line); + return write_string_stream(f, line); } int write_string_file_atomic(const char *fn, const char *line) { @@ -189,29 +193,33 @@ ssize_t sendfile_full(int out_fd, const char *fn) { return (ssize_t) l; } -int read_full_file(const char *fn, char **contents, size_t *size) { - _cleanup_fclose_ FILE *f = NULL; +int read_full_stream(FILE *f, char **contents, size_t *size) { size_t n, l; _cleanup_free_ char *buf = NULL; struct stat st; - assert(fn); + assert(f); assert(contents); - f = fopen(fn, "re"); - if (!f) - return -errno; - if (fstat(fileno(f), &st) < 0) return -errno; - /* Safety check */ - if (st.st_size > 4*1024*1024) - return -E2BIG; + n = LINE_MAX; - n = st.st_size > 0 ? st.st_size : LINE_MAX; - l = 0; + if (S_ISREG(st.st_mode)) { + + /* Safety check */ + if (st.st_size > 4*1024*1024) + return -E2BIG; + + /* Start with the right file size, but be prepared for + * files from /proc which generally report a file size + * of 0 */ + if (st.st_size > 0) + n = st.st_size; + } + l = 0; for (;;) { char *t; size_t k; @@ -248,7 +256,21 @@ int read_full_file(const char *fn, char **contents, size_t *size) { return 0; } +int read_full_file(const char *fn, char **contents, size_t *size) { + _cleanup_fclose_ FILE *f = NULL; + + assert(fn); + assert(contents); + + f = fopen(fn, "re"); + if (!f) + return -errno; + + return read_full_stream(f, contents, size); +} + static int parse_env_file_internal( + FILE *f, const char *fname, const char *newline, int (*push) (const char *filename, unsigned line, @@ -275,10 +297,12 @@ static int parse_env_file_internal( COMMENT_ESCAPE } state = PRE_KEY; - assert(fname); assert(newline); - r = read_full_file(fname, &contents, NULL); + if (f) + r = read_full_stream(f, &contents, NULL); + else + r = read_full_file(fname, &contents, NULL); if (r < 0) return r; @@ -532,25 +556,27 @@ fail: return r; } -static int parse_env_file_push(const char *filename, unsigned line, - const char *key, char *value, void *userdata) { +static int parse_env_file_push( + const char *filename, unsigned line, + const char *key, char *value, + void *userdata) { const char *k; va_list aq, *ap = userdata; if (!utf8_is_valid(key)) { - _cleanup_free_ char *p = utf8_escape_invalid(key); + _cleanup_free_ char *p; - log_error("%s:%u: invalid UTF-8 in key '%s', ignoring.", - filename, line, p); + p = utf8_escape_invalid(key); + log_error("%s:%u: invalid UTF-8 in key '%s', ignoring.", strna(filename), line, p); return -EINVAL; } if (value && !utf8_is_valid(value)) { - _cleanup_free_ char *p = utf8_escape_invalid(value); + _cleanup_free_ char *p; - log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", - filename, line, key, p); + p = utf8_escape_invalid(value); + log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, p); return -EINVAL; } @@ -571,6 +597,7 @@ static int parse_env_file_push(const char *filename, unsigned line, va_end(aq); free(value); + return 0; } @@ -585,14 +612,16 @@ int parse_env_file( newline = NEWLINE; va_start(ap, newline); - r = parse_env_file_internal(fname, newline, parse_env_file_push, &ap); + r = parse_env_file_internal(NULL, fname, newline, parse_env_file_push, &ap); va_end(ap); return r; } -static int load_env_file_push(const char *filename, unsigned line, - const char *key, char *value, void *userdata) { +static int load_env_file_push( + const char *filename, unsigned line, + const char *key, char *value, + void *userdata) { char ***m = userdata; char *p; int r; @@ -600,16 +629,14 @@ static int load_env_file_push(const char *filename, unsigned line, if (!utf8_is_valid(key)) { _cleanup_free_ char *t = utf8_escape_invalid(key); - log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", - filename, line, t); + log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t); return -EINVAL; } if (value && !utf8_is_valid(value)) { _cleanup_free_ char *t = utf8_escape_invalid(value); - log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", - filename, line, key, t); + log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t); return -EINVAL; } @@ -625,14 +652,69 @@ static int load_env_file_push(const char *filename, unsigned line, return 0; } -int load_env_file(const char *fname, const char *newline, char ***rl) { +int load_env_file(FILE *f, const char *fname, const char *newline, char ***rl) { + char **m = NULL; + int r; + + if (!newline) + newline = NEWLINE; + + r = parse_env_file_internal(f, fname, newline, load_env_file_push, &m); + if (r < 0) { + strv_free(m); + return r; + } + + *rl = m; + return 0; +} + +static int load_env_file_push_pairs( + const char *filename, unsigned line, + const char *key, char *value, + void *userdata) { + char ***m = userdata; + int r; + + if (!utf8_is_valid(key)) { + _cleanup_free_ char *t = utf8_escape_invalid(key); + + log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t); + return -EINVAL; + } + + if (value && !utf8_is_valid(value)) { + _cleanup_free_ char *t = utf8_escape_invalid(value); + + log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t); + return -EINVAL; + } + + r = strv_extend(m, key); + if (r < 0) + return -ENOMEM; + + if (!value) { + r = strv_extend(m, ""); + if (r < 0) + return -ENOMEM; + } else { + r = strv_push(m, value); + if (r < 0) + return r; + } + + return 0; +} + +int load_env_file_pairs(FILE *f, const char *fname, const char *newline, char ***rl) { char **m = NULL; int r; if (!newline) newline = NEWLINE; - r = parse_env_file_internal(fname, newline, load_env_file_push, &m); + r = parse_env_file_internal(f, fname, newline, load_env_file_push_pairs, &m); if (r < 0) { strv_free(m); return r; diff --git a/src/shared/fileio.h b/src/shared/fileio.h index 06c288715..5122a9a4d 100644 --- a/src/shared/fileio.h +++ b/src/shared/fileio.h @@ -25,16 +25,19 @@ #include "macro.h" -int write_string_to_file(FILE *f, const char *line); +int write_string_stream(FILE *f, const char *line); int write_string_file(const char *fn, const char *line); 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); +int read_full_stream(FILE *f, 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); +int load_env_file(FILE *f, const char *fname, const char *separator, char ***l); +int load_env_file_pairs(FILE *f, const char *fname, const char *separator, char ***l); + int write_env_file(const char *fname, char **l); int executable_is_script(const char *path, char **interpreter); diff --git a/src/sleep/sleep.c b/src/sleep/sleep.c index 118d10c23..5adbea595 100644 --- a/src/sleep/sleep.c +++ b/src/sleep/sleep.c @@ -68,7 +68,7 @@ static int write_state(FILE **f, char **states) { STRV_FOREACH(state, states) { int k; - k = write_string_to_file(*f, *state); + k = write_string_stream(*f, *state); if (k == 0) return 0; log_debug("Failed to write '%s' to /sys/power/state: %s", diff --git a/src/test/test-fileio.c b/src/test/test-fileio.c index 47a0907f9..1de59fa57 100644 --- a/src/test/test-fileio.c +++ b/src/test/test-fileio.c @@ -72,7 +72,7 @@ static void test_parse_env_file(void) { fflush(f); fclose(f); - r = load_env_file(t, NULL, &a); + r = load_env_file(NULL, t, NULL, &a); assert_se(r >= 0); STRV_FOREACH(i, a) @@ -139,7 +139,7 @@ static void test_parse_env_file(void) { r = write_env_file(p, a); assert_se(r >= 0); - r = load_env_file(p, NULL, &b); + r = load_env_file(NULL, p, NULL, &b); assert_se(r >= 0); unlink(t); @@ -179,7 +179,7 @@ static void test_parse_multiline_env_file(void) { fflush(f); fclose(f); - r = load_env_file(t, NULL, &a); + r = load_env_file(NULL, t, NULL, &a); assert_se(r >= 0); STRV_FOREACH(i, a) @@ -193,7 +193,7 @@ static void test_parse_multiline_env_file(void) { r = write_env_file(p, a); assert_se(r >= 0); - r = load_env_file(p, NULL, &b); + r = load_env_file(NULL, p, NULL, &b); assert_se(r >= 0); unlink(t); diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c index 48133428d..34865729f 100644 --- a/src/test/test-unit-file.c +++ b/src/test/test-unit-file.c @@ -234,7 +234,7 @@ static void test_load_env_file_1(void) { assert(fd >= 0); assert_se(write(fd, env_file_1, sizeof(env_file_1)) == sizeof(env_file_1)); - r = load_env_file(name, NULL, &data); + r = load_env_file(NULL, name, NULL, &data); assert(r == 0); assert(streq(data[0], "a=a")); assert(streq(data[1], "b=bc")); @@ -257,7 +257,7 @@ static void test_load_env_file_2(void) { assert(fd >= 0); assert_se(write(fd, env_file_2, sizeof(env_file_2)) == sizeof(env_file_2)); - r = load_env_file(name, NULL, &data); + r = load_env_file(NULL, name, NULL, &data); assert(r == 0); assert(streq(data[0], "a=a")); assert(data[1] == NULL); @@ -275,7 +275,7 @@ static void test_load_env_file_3(void) { assert(fd >= 0); assert_se(write(fd, env_file_3, sizeof(env_file_3)) == sizeof(env_file_3)); - r = load_env_file(name, NULL, &data); + r = load_env_file(NULL, name, NULL, &data); assert(r == 0); assert(data == NULL); unlink(name); @@ -291,7 +291,7 @@ static void test_load_env_file_4(void) { assert(fd >= 0); assert_se(write(fd, env_file_4, sizeof(env_file_4)) == sizeof(env_file_4)); - r = load_env_file(name, NULL, &data); + r = load_env_file(NULL, name, NULL, &data); assert(r == 0); assert(streq(data[0], "HWMON_MODULES=coretemp f71882fg")); assert(streq(data[1], "MODULE_0=coretemp")); diff --git a/units/systemd-machined.service.in b/units/systemd-machined.service.in index 5a890b5b7..bbb13de31 100644 --- a/units/systemd-machined.service.in +++ b/units/systemd-machined.service.in @@ -15,7 +15,7 @@ After=machine.slice [Service] ExecStart=@rootlibexecdir@/systemd-machined BusName=org.freedesktop.machine1 -CapabilityBoundingSet=CAP_KILL CAP_SYS_PTRACE CAP_SYS_ADMIN CAP_SETGID +CapabilityBoundingSet=CAP_KILL CAP_SYS_PTRACE CAP_SYS_ADMIN CAP_SETGID CAP_SYS_CHROOT WatchdogSec=1min PrivateTmp=yes PrivateDevices=yes