[systemd-commits] 4 commits - TODO man/sysusers.d.xml src/core src/hostname src/locale src/machine src/shared src/sleep src/sysusers src/test units/systemd-machined.service.in

Lennart Poettering lennart at kemper.freedesktop.org
Thu Jul 3 10:55:32 PDT 2014


 TODO                                      |    4 
 man/sysusers.d.xml                        |   29 ++
 src/core/execute.c                        |    2 
 src/hostname/hostnamed.c                  |    2 
 src/locale/localed.c                      |    4 
 src/machine/machine-dbus.c                |   96 +++++++
 src/machine/machine.h                     |    1 
 src/machine/machinectl.c                  |   44 +++
 src/machine/machined-dbus.c               |   22 +
 src/machine/org.freedesktop.machine1.conf |    4 
 src/shared/fileio.c                       |  148 +++++++++---
 src/shared/fileio.h                       |    7 
 src/sleep/sleep.c                         |    2 
 src/sysusers/sysusers.c                   |  368 ++++++++++++++++++++++++------
 src/test/test-fileio.c                    |    8 
 src/test/test-unit-file.c                 |    8 
 units/systemd-machined.service.in         |    2 
 17 files changed, 620 insertions(+), 131 deletions(-)

New commits:
commit 8225488bec7b0e57ea0ca23e2cdc63064bfc48ab
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jul 3 19:55:18 2014 +0200

    update TODO

diff --git a/TODO b/TODO
index 5309543..f09e853 100644
--- a/TODO
+++ b/TODO
@@ -31,8 +31,6 @@ Features:
 
 * order OnCalendar timer units after timer-sync.target if DefaultDependencies=no so that we don't trigger them prematurely
 
-* sysusers: add "m" command for declaring group members
-
 * refuse mounting on symlinks
 
 * logind: allow users to kill or lock their own sessions

commit a12b0cc34d80a2d13c87d1f57059339cfc780912
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jul 3 19:54:46 2014 +0200

    sysusers: add new line type "m" to add users as members to groups

diff --git a/man/sysusers.d.xml b/man/sysusers.d.xml
index af31ec0..549b3f6 100644
--- a/man/sysusers.d.xml
+++ b/man/sysusers.d.xml
@@ -86,7 +86,8 @@
                 <programlisting># Type Name ID GECOS
 u httpd 440 "HTTP User"
 u authd /usr/bin/authd "Authorization user"
-g input - -</programlisting>
+g input - -
+m authd input</programlisting>
 
                 <refsect2>
                         <title>Type</title>
@@ -125,6 +126,15 @@ g input - -</programlisting>
                                         created with no password
                                         set.</para></listitem>
                                 </varlistentry>
+
+                                <varlistentry>
+                                        <term><varname>m</varname></term>
+                                        <listitem><para>Add a user to
+                                        a group. If the user or group
+                                        are not existing yet, they
+                                        will be implicitly
+                                        created.</para></listitem>
+                                </varlistentry>
                         </variablelist>
                 </refsect2>
 
@@ -141,13 +151,18 @@ g input - -</programlisting>
                         scheme to guarantee this is by prefixing all
                         system and group names with the underscore,
                         and avoiding too generic names.</para>
+
+                        <para>For <varname>m</varname> lines this
+                        field should contain the user name to add to a
+                        group.</para>
                 </refsect2>
 
                 <refsect2>
                         <title>ID</title>
 
-                        <para>The numeric 32bit UID or GID of the
-                        user/group. Do not use IDs 65535 or
+                        <para>For <varname>u</varname> and
+                        <varname>g</varname> the numeric 32bit UID or
+                        GID of the user/group. Do not use IDs 65535 or
                         4294967295, as they have special placeholder
                         meanings. Specify "-" for automatic UID/GID
                         allocation for the user or
@@ -157,6 +172,10 @@ g input - -</programlisting>
                         useful to create users whose UID/GID match the
                         owners of pre-existing files (such as SUID or
                         SGID binaries).</para>
+
+                        <para>For <varname>m</varname> lines this
+                        field should contain the group name to add to
+                        a user to.</para>
                 </refsect2>
 
                 <refsect2>
@@ -165,6 +184,10 @@ g input - -</programlisting>
                         <para>A short, descriptive string for users to
                         be created, enclosed in quotation marks. Note
                         that this field may not contain colons.</para>
+
+                        <para>Only applies to lines of type
+                        <varname>u</varname> and should otherwise be
+                        left unset.</para>
                 </refsect2>
 
         </refsect1>
diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c
index c192add..1209a5a 100644
--- a/src/sysusers/sysusers.c
+++ b/src/sysusers/sysusers.c
@@ -38,6 +38,7 @@
 typedef enum ItemType {
         ADD_USER = 'u',
         ADD_GROUP = 'g',
+        ADD_MEMBER = 'm',
 } ItemType;
 typedef struct Item {
         ItemType type;
@@ -69,6 +70,7 @@ static const char conf_file_dirs[] =
 
 static Hashmap *users = NULL, *groups = NULL;
 static Hashmap *todo_uids = NULL, *todo_gids = NULL;
+static Hashmap *members = NULL;
 
 static Hashmap *database_uid = NULL, *database_user = NULL;
 static Hashmap *database_gid = NULL, *database_group = NULL;
@@ -240,11 +242,62 @@ fail:
         return r;
 }
 
+static int putgrent_with_members(const struct group *gr, FILE *group) {
+        char **a;
+
+        assert(gr);
+        assert(group);
+
+        a = hashmap_get(members, gr->gr_name);
+        if (a) {
+                _cleanup_strv_free_ char **l = NULL;
+                bool added = false;
+                char **i;
+
+                l = strv_copy(gr->gr_mem);
+                if (!l)
+                        return -ENOMEM;
+
+                STRV_FOREACH(i, a) {
+                        if (strv_find(l, *i))
+                                continue;
+
+                        if (strv_extend(&l, *i) < 0)
+                                return -ENOMEM;
+
+                        added = true;
+                }
+
+                if (added) {
+                        struct group t;
+
+                        strv_uniq(l);
+                        strv_sort(l);
+
+                        t = *gr;
+                        t.gr_mem = l;
+
+                        errno = 0;
+                        if (putgrent(&t, group) != 0)
+                                return errno ? -errno : -EIO;
+
+                        return 1;
+                }
+        }
+
+        errno = 0;
+        if (putgrent(gr, group) != 0)
+                return errno ? -errno : -EIO;
+
+        return 0;
+}
+
 static int write_files(void) {
 
         _cleanup_fclose_ FILE *passwd = NULL, *group = NULL;
         _cleanup_free_ char *passwd_tmp = NULL, *group_tmp = NULL;
         const char *passwd_path = NULL, *group_path = NULL;
+        bool group_changed = false;
         Iterator iterator;
         Item *i;
         int r;
@@ -252,7 +305,7 @@ static int write_files(void) {
         /* We don't patch /etc/shadow or /etc/gshadow here, since we
          * only create user accounts without passwords anyway. */
 
-        if (hashmap_size(todo_gids) > 0) {
+        if (hashmap_size(todo_gids) > 0 || hashmap_size(members) > 0) {
                 _cleanup_fclose_ FILE *original = NULL;
 
                 group_path = fix_root("/etc/group");
@@ -290,10 +343,12 @@ static int write_files(void) {
                                         goto finish;
                                 }
 
-                                if (putgrent(gr, group) < 0) {
-                                        r = -errno;
+                                r = putgrent_with_members(gr, group);
+                                if (r < 0)
                                         goto finish;
-                                }
+
+                                if (r > 0)
+                                        group_changed = true;
 
                                 errno = 0;
                         }
@@ -314,10 +369,11 @@ static int write_files(void) {
                                 .gr_passwd = (char*) "x",
                         };
 
-                        if (putgrent(&n, group) < 0) {
-                                r = -errno;
+                        r = putgrent_with_members(&n, group);
+                        if (r < 0)
                                 goto finish;
-                        }
+
+                        group_changed = true;
                 }
 
                 r = fflush_and_check(group);
@@ -356,8 +412,9 @@ static int write_files(void) {
                                         goto finish;
                                 }
 
+                                errno = 0;
                                 if (putpwent(pw, passwd) < 0) {
-                                        r = -errno;
+                                        r = errno ? -errno : -EIO;
                                         goto finish;
                                 }
 
@@ -393,8 +450,9 @@ static int write_files(void) {
                                 n.pw_dir = (char*) "/";
                         }
 
-                        if (putpwent(&n, passwd) < 0) {
-                                r = -r;
+                        errno = 0;
+                        if (putpwent(&n, passwd) != 0) {
+                                r = errno ? -errno : -EIO;
                                 goto finish;
                         }
                 }
@@ -405,7 +463,7 @@ static int write_files(void) {
         }
 
         /* Make a backup of the old files */
-        if (group) {
+        if (group && group_changed) {
                 r = make_backup(group_path);
                 if (r < 0)
                         goto finish;
@@ -418,7 +476,7 @@ static int write_files(void) {
         }
 
         /* And make the new files count */
-        if (group) {
+        if (group && group_changed) {
                 if (rename(group_tmp, group_path) < 0) {
                         r = -errno;
                         goto finish;
@@ -438,15 +496,13 @@ static int write_files(void) {
                 passwd_tmp = NULL;
         }
 
-        return 0;
+        r = 0;
 
 finish:
-        if (r < 0) {
-                if (passwd_tmp)
-                        unlink(passwd_tmp);
-                if (group_tmp)
-                        unlink(group_tmp);
-        }
+        if (passwd_tmp)
+                unlink(passwd_tmp);
+        if (group_tmp)
+                unlink(group_tmp);
 
         return r;
 }
@@ -890,9 +946,10 @@ static int process_item(Item *i) {
 
                 return add_group(i);
         }
-        }
 
-        assert_not_reached("Unknown item type");
+        default:
+                assert_not_reached("Unknown item type");
+        }
 }
 
 static void item_free(Item *i) {
@@ -909,6 +966,74 @@ static void item_free(Item *i) {
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(Item*, item_free);
 
+static int add_implicit(void) {
+        char *g, **l;
+        Iterator iterator;
+        int r;
+
+        /* Implicitly create additional users and groups, if they were listed in "m" lines */
+
+        HASHMAP_FOREACH_KEY(l, g, members, iterator) {
+                Item *i;
+                char **m;
+
+                i = hashmap_get(groups, g);
+                if (!i) {
+                        _cleanup_(item_freep) Item *j = NULL;
+
+                        r = hashmap_ensure_allocated(&groups, string_hash_func, string_compare_func);
+                        if (r < 0)
+                                return log_oom();
+
+                        j = new0(Item, 1);
+                        if (!j)
+                                return log_oom();
+
+                        j->type = ADD_GROUP;
+                        j->name = strdup(g);
+                        if (!j->name)
+                                return log_oom();
+
+                        r = hashmap_put(groups, j->name, j);
+                        if (r < 0)
+                                return log_oom();
+
+                        log_debug("Adding implicit group '%s' due to m line", j->name);
+                        j = NULL;
+                }
+
+                STRV_FOREACH(m, l) {
+
+                        i = hashmap_get(users, *m);
+                        if (!i) {
+                                _cleanup_(item_freep) Item *j = NULL;
+
+                                r = hashmap_ensure_allocated(&users, string_hash_func, string_compare_func);
+                                if (r < 0)
+                                        return log_oom();
+
+                                j = new0(Item, 1);
+                                if (!j)
+                                        return log_oom();
+
+                                j->type = ADD_USER;
+                                j->name = strdup(*m);
+                                if (!j->name)
+                                        return log_oom();
+
+                                r = hashmap_put(users, j->name, j);
+                                if (r < 0)
+                                        return log_oom();
+
+                                log_debug("Adding implicit user '%s' due to m line", j->name);
+                                j = NULL;
+                        }
+                }
+        }
+
+        return 0;
+}
+
 static bool item_equal(Item *a, Item *b) {
         assert(a);
         assert(b);
@@ -994,7 +1119,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
                 {}
         };
 
-        _cleanup_free_ char *action = NULL, *name = NULL, *id = NULL;
+        _cleanup_free_ char *action = NULL, *name = NULL, *id = NULL, *resolved_name = NULL;
         _cleanup_(item_freep) Item *i = NULL;
         Item *existing;
         Hashmap *h;
@@ -1020,90 +1145,178 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
                 return -EINVAL;
         }
 
-        if (!IN_SET(action[0], ADD_USER, ADD_GROUP)) {
+        if (!IN_SET(action[0], ADD_USER, ADD_GROUP, ADD_MEMBER)) {
                 log_error("[%s:%u] Unknown command command type '%c'.", fname, line, action[0]);
                 return -EBADMSG;
         }
 
-        i = new0(Item, 1);
-        if (!i)
-                return log_oom();
-
-        i->type = action[0];
-
-        r = specifier_printf(name, specifier_table, NULL, &i->name);
+        r = specifier_printf(name, specifier_table, NULL, &resolved_name);
         if (r < 0) {
                 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
                 return r;
         }
 
-        if (!valid_user_group_name(i->name)) {
-                log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, i->name);
+        if (!valid_user_group_name(resolved_name)) {
+                log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_name);
                 return -EINVAL;
         }
 
         if (n >= 0) {
                 n += strspn(buffer+n, WHITESPACE);
-                if (buffer[n] != 0 && (buffer[n] != '-' || buffer[n+1] != 0)) {
-                        i->description = unquote(buffer+n, "\"");
-                        if (!i->description)
-                                return log_oom();
 
-                        if (!valid_gecos(i->description)) {
-                                log_error("[%s:%u] '%s' is not a valid GECOS field.", fname, line, i->description);
-                                return -EINVAL;
-                        }
-                }
+                if (STR_IN_SET(buffer + n, "", "-"))
+                        n = -1;
         }
 
-        if (id && !streq(id, "-")) {
+        switch (action[0]) {
 
-                if (path_is_absolute(id)) {
-                        char *p;
+        case ADD_MEMBER: {
+                _cleanup_free_ char *resolved_id = NULL;
+                char **l;
 
-                        p = strdup(id);
-                        if (!p)
-                                return log_oom();
+                r = hashmap_ensure_allocated(&members, string_hash_func, string_compare_func);
+                if (r < 0)
+                        return log_oom();
 
-                        path_kill_slashes(p);
+                /* Try to extend an existing member or group item */
 
-                        if (i->type == ADD_USER)
-                                i->uid_path = p;
-                        else
-                                i->gid_path = p;
+                if (!id || streq(id, "-")) {
+                        log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname, line);
+                        return -EINVAL;
+                }
 
-                } else if (i->type == ADD_USER) {
-                        r = parse_uid(id, &i->uid);
-                        if (r < 0) {
-                                log_error("Failed to parse UID: %s", id);
-                                return -EBADMSG;
-                        }
+                r = specifier_printf(id, specifier_table, NULL, &resolved_id);
+                if (r < 0) {
+                        log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
+                        return r;
+                }
 
-                        i->uid_set = true;
+                if (!valid_user_group_name(resolved_id)) {
+                        log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_id);
+                        return -EINVAL;
+                }
 
+                if (n >= 0) {
+                        log_error("[%s:%u] Lines of type 'm' don't take a GECOS field.", fname, line);
+                        return -EINVAL;
+                }
+
+                l = hashmap_get(members, resolved_id);
+                if (l) {
+                        /* A list for this group name already exists, let's append to it */
+                        r = strv_push(&l, resolved_name);
+                        if (r < 0)
+                                return log_oom();
+
+                        resolved_name = NULL;
+
+                        assert_se(hashmap_update(members, resolved_id, l) >= 0);
                 } else {
-                        assert(i->type == ADD_GROUP);
+                        /* No list for this group name exists yet, create one */
+
+                        l = new0(char *, 2);
+                        if (!l)
+                                return -ENOMEM;
+
+                        l[0] = resolved_name;
+                        l[1] = NULL;
 
-                        r = parse_gid(id, &i->gid);
+                        r = hashmap_put(members, resolved_id, l);
                         if (r < 0) {
-                                log_error("Failed to parse GID: %s", id);
-                                return -EBADMSG;
+                                free(l);
+                                return log_oom();
                         }
 
-                        i->gid_set = true;
+                        resolved_id = resolved_name = NULL;
                 }
+
+                return 0;
         }
 
-        if (i->type == ADD_USER) {
+        case ADD_USER:
                 r = hashmap_ensure_allocated(&users, string_hash_func, string_compare_func);
+                if (r < 0)
+                        return log_oom();
+
+                i = new0(Item, 1);
+                if (!i)
+                        return log_oom();
+
+                if (id && !streq(id, "-")) {
+
+                        if (path_is_absolute(id)) {
+                                i->uid_path = strdup(id);
+                                if (!i->uid_path)
+                                        return log_oom();
+
+                                path_kill_slashes(i->uid_path);
+
+                        } else {
+                                r = parse_uid(id, &i->uid);
+                                if (r < 0) {
+                                        log_error("Failed to parse UID: %s", id);
+                                        return -EBADMSG;
+                                }
+
+                                i->uid_set = true;
+                        }
+                }
+
+                if (n >= 0) {
+                        i->description = unquote(buffer+n, "\"");
+                        if (!i->description)
+                                return log_oom();
+
+                        if (!valid_gecos(i->description)) {
+                                log_error("[%s:%u] '%s' is not a valid GECOS field.", fname, line, i->description);
+                                return -EINVAL;
+                        }
+                }
+
                 h = users;
-        } else {
-                assert(i->type == ADD_GROUP);
+                break;
+
+        case ADD_GROUP:
                 r = hashmap_ensure_allocated(&groups, string_hash_func, string_compare_func);
+                if (r < 0)
+                        return log_oom();
+
+                if (n >= 0) {
+                        log_error("[%s:%u] Lines of type 'g' don't take a GECOS field.", fname, line);
+                        return -EINVAL;
+                }
+
+                i = new0(Item, 1);
+                if (!i)
+                        return log_oom();
+
+                if (id && !streq(id, "-")) {
+
+                        if (path_is_absolute(id)) {
+                                i->gid_path = strdup(id);
+                                if (!i->gid_path)
+                                        return log_oom();
+
+                                path_kill_slashes(i->gid_path);
+                        } else {
+                                r = parse_gid(id, &i->gid);
+                                if (r < 0) {
+                                        log_error("Failed to parse GID: %s", id);
+                                        return -EBADMSG;
+                                }
+
+                                i->gid_set = true;
+                        }
+                }
+
+
                 h = groups;
+                break;
         }
-        if (r < 0)
-                return log_oom();
+
+        i->type = action[0];
+        i->name = resolved_name;
+        resolved_name = NULL;
 
         existing = hashmap_get(h, i->name);
         if (existing) {
@@ -1116,10 +1329,8 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
         }
 
         r = hashmap_put(h, i->name, i);
-        if (r < 0) {
-                log_error("Failed to insert item %s: %s", i->name, strerror(-r));
-                return r;
-        }
+        if (r < 0)
+                return log_oom();
 
         i = NULL;
         return 0;
@@ -1292,6 +1503,7 @@ int main(int argc, char *argv[]) {
         Iterator iterator;
         int r, k;
         Item *i;
+        char *n;
 
         r = parse_argv(argc, argv);
         if (r <= 0)
@@ -1330,6 +1542,10 @@ int main(int argc, char *argv[]) {
                 }
         }
 
+        r = add_implicit();
+        if (r < 0)
+                goto finish;
+
         lock = take_lock();
         if (lock < 0) {
                 log_error("Failed to take lock: %s", strerror(-lock));
@@ -1365,8 +1581,14 @@ finish:
         while ((i = hashmap_steal_first(users)))
                 item_free(i);
 
+        while ((n = hashmap_first_key(members))) {
+                strv_free(hashmap_steal_first(members));
+                free(n);
+        }
+
         hashmap_free(groups);
         hashmap_free(users);
+        hashmap_free(members);
         hashmap_free(todo_uids);
         hashmap_free(todo_gids);
 

commit aaf86f95a0d50023fd6d0168714c6e8486d1a201
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jul 3 17:51:36 2014 +0200

    update TODO

diff --git a/TODO b/TODO
index 6bb84cd..5309543 100644
--- a/TODO
+++ b/TODO
@@ -27,8 +27,6 @@ Features:
   been provisioned yet. Similar in style to what the distros have, but
   minimalist, text-only /dev/console stuff.
 
-* machinectl should show /etc/os-release info from the container
-
 * Add a new verb "systemctl top"
 
 * order OnCalendar timer units after timer-sync.target if DefaultDependencies=no so that we don't trigger them prematurely

commit 717603e391b52983ca1fd218e7333a1b9dfc5c05
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jul 3 17:50:55 2014 +0200

    machinectl: show /etc/os-release information of container in status output

diff --git a/src/core/execute.c b/src/core/execute.c
index 1ea6463..88d094e 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 c2b6d3d..14629dd 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 358f6c2..23da149 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 c9c3de0..14dae0a 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 ed1c81c..fa9262d 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 c2bf7e5..022a4eb 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 7c1802c..ffcd7c0 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 ab349a5..3a77c70 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"/>
 
+                <allow send_destination="org.freedesktop.machine1"
+                       send_interface="org.freedesktop.machine1.Machine"
+                       send_member="GetMachineOSRelease"/>
+
                 <allow receive_sender="org.freedesktop.machine1"/>
         </policy>
 
diff --git a/src/shared/fileio.c b/src/shared/fileio.c
index c580624..fb1c1bc 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 06c2887..5122a9a 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 118d10c..5adbea5 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 47a0907..1de59fa 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 4813342..3486572 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 5a890b5..bbb13de 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



More information about the systemd-commits mailing list