X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=src%2Fsysusers%2Fsysusers.c;h=1fc1b0ae96162b3af5c1181cf9cdb02cab18a738;hb=d0696f3b7501744d91bcbef83478b9cd4a8160f4;hp=91722408da382c49f5de540a6882a0c8707d6191;hpb=37ec0fdd3443a77a5120cf55002fedcb5b1dd069;p=thirdparty%2Fsystemd.git diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c index 91722408da3..1fc1b0ae961 100644 --- a/src/sysusers/sysusers.c +++ b/src/sysusers/sysusers.c @@ -8,18 +8,21 @@ #include "copy.h" #include "def.h" #include "fd-util.h" -#include "fileio-label.h" +#include "fileio.h" #include "format-util.h" #include "fs-util.h" #include "hashmap.h" +#include "main-func.h" #include "pager.h" #include "path-util.h" +#include "pretty-print.h" +#include "set.h" #include "selinux-util.h" #include "smack-util.h" #include "specifier.h" #include "string-util.h" #include "strv.h" -#include "terminal-util.h" +#include "tmpfile-util-label.h" #include "uid-range.h" #include "user-util.h" #include "utf8.h" @@ -62,19 +65,34 @@ static char *arg_root = NULL; static bool arg_cat_config = false; static const char *arg_replace = NULL; static bool arg_inline = false; -static bool arg_no_pager = false; +static PagerFlags arg_pager_flags = 0; static OrderedHashmap *users = NULL, *groups = NULL; static OrderedHashmap *todo_uids = NULL, *todo_gids = NULL; static OrderedHashmap *members = NULL; -static Hashmap *database_uid = NULL, *database_user = NULL; -static Hashmap *database_gid = NULL, *database_group = NULL; +static Hashmap *database_by_uid = NULL, *database_by_username = NULL; +static Hashmap *database_by_gid = NULL, *database_by_groupname = NULL; +static Set *database_users = NULL, *database_groups = NULL; static uid_t search_uid = UID_INVALID; static UidRange *uid_range = NULL; static unsigned n_uid_range = 0; +STATIC_DESTRUCTOR_REGISTER(groups, ordered_hashmap_freep); +STATIC_DESTRUCTOR_REGISTER(users, ordered_hashmap_freep); +STATIC_DESTRUCTOR_REGISTER(members, ordered_hashmap_freep); +STATIC_DESTRUCTOR_REGISTER(todo_uids, ordered_hashmap_freep); +STATIC_DESTRUCTOR_REGISTER(todo_gids, ordered_hashmap_freep); +STATIC_DESTRUCTOR_REGISTER(database_by_uid, hashmap_freep); +STATIC_DESTRUCTOR_REGISTER(database_by_username, hashmap_freep); +STATIC_DESTRUCTOR_REGISTER(database_users, set_free_freep); +STATIC_DESTRUCTOR_REGISTER(database_by_gid, hashmap_freep); +STATIC_DESTRUCTOR_REGISTER(database_by_groupname, hashmap_freep); +STATIC_DESTRUCTOR_REGISTER(database_groups, set_free_freep); +STATIC_DESTRUCTOR_REGISTER(uid_range, freep); +STATIC_DESTRUCTOR_REGISTER(arg_root, freep); + static int load_user_database(void) { _cleanup_fclose_ FILE *f = NULL; const char *passwd_path; @@ -86,11 +104,15 @@ static int load_user_database(void) { if (!f) return errno == ENOENT ? 0 : -errno; - r = hashmap_ensure_allocated(&database_user, &string_hash_ops); + r = hashmap_ensure_allocated(&database_by_username, &string_hash_ops); + if (r < 0) + return r; + + r = hashmap_ensure_allocated(&database_by_uid, NULL); if (r < 0) return r; - r = hashmap_ensure_allocated(&database_uid, NULL); + r = set_ensure_allocated(&database_users, NULL); if (r < 0) return r; @@ -102,21 +124,19 @@ static int load_user_database(void) { if (!n) return -ENOMEM; - k = hashmap_put(database_user, n, UID_TO_PTR(pw->pw_uid)); - if (k < 0 && k != -EEXIST) { + k = set_put(database_users, n); + if (k < 0) { free(n); return k; } - q = hashmap_put(database_uid, UID_TO_PTR(pw->pw_uid), n); - if (q < 0 && q != -EEXIST) { - if (k <= 0) - free(n); - return q; - } + k = hashmap_put(database_by_username, n, UID_TO_PTR(pw->pw_uid)); + if (k < 0 && k != -EEXIST) + return k; - if (k <= 0 && q <= 0) - free(n); + q = hashmap_put(database_by_uid, UID_TO_PTR(pw->pw_uid), n); + if (q < 0 && q != -EEXIST) + return q; } return r; } @@ -132,16 +152,19 @@ static int load_group_database(void) { if (!f) return errno == ENOENT ? 0 : -errno; - r = hashmap_ensure_allocated(&database_group, &string_hash_ops); + r = hashmap_ensure_allocated(&database_by_groupname, &string_hash_ops); + if (r < 0) + return r; + + r = hashmap_ensure_allocated(&database_by_gid, NULL); if (r < 0) return r; - r = hashmap_ensure_allocated(&database_gid, NULL); + r = set_ensure_allocated(&database_groups, NULL); if (r < 0) return r; - errno = 0; - while ((gr = fgetgrent(f))) { + while ((r = fgetgrent_sane(f, &gr)) > 0) { char *n; int k, q; @@ -149,28 +172,21 @@ static int load_group_database(void) { if (!n) return -ENOMEM; - k = hashmap_put(database_group, n, GID_TO_PTR(gr->gr_gid)); - if (k < 0 && k != -EEXIST) { + k = set_put(database_groups, n); + if (k < 0) { free(n); return k; } - q = hashmap_put(database_gid, GID_TO_PTR(gr->gr_gid), n); - if (q < 0 && q != -EEXIST) { - if (k <= 0) - free(n); - return q; - } - - if (k <= 0 && q <= 0) - free(n); + k = hashmap_put(database_by_groupname, n, GID_TO_PTR(gr->gr_gid)); + if (k < 0 && k != -EEXIST) + return k; - errno = 0; + q = hashmap_put(database_by_gid, GID_TO_PTR(gr->gr_gid), n); + if (q < 0 && q != -EEXIST) + return q; } - if (!IN_SET(errno, 0, ENOENT)) - return -errno; - - return 0; + return r; } static int make_backup(const char *target, const char *x) { @@ -228,7 +244,7 @@ static int make_backup(const char *target, const char *x) { return 0; fail: - unlink(temp); + (void) unlink(temp); return r; } @@ -345,7 +361,7 @@ static int rename_and_apply_smack(const char *temp_path, const char *dest_path) } static const char* default_shell(uid_t uid) { - return uid == 0 ? "/bin/sh" : "/sbin/nologin"; + return uid == 0 ? "/bin/sh" : NOLOGIN; } static int write_temporary_passwd(const char *passwd_path, FILE **tmpfile, char **tmpfile_path) { @@ -373,15 +389,15 @@ static int write_temporary_passwd(const char *passwd_path, FILE **tmpfile, char while ((r = fgetpwent_sane(original, &pw)) > 0) { i = ordered_hashmap_get(users, pw->pw_name); - if (i && i->todo_user) { - log_error("%s: User \"%s\" already exists.", passwd_path, pw->pw_name); - return -EEXIST; - } + if (i && i->todo_user) + return log_error_errno(SYNTHETIC_ERRNO(EEXIST), + "%s: User \"%s\" already exists.", + passwd_path, pw->pw_name); - if (ordered_hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid))) { - log_error("%s: Detected collision for UID " UID_FMT ".", passwd_path, pw->pw_uid); - return -EEXIST; - } + if (ordered_hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid))) + return log_error_errno(SYNTHETIC_ERRNO(EEXIST), + "%s: Detected collision for UID " UID_FMT ".", + passwd_path, pw->pw_uid); /* Make sure we keep the NIS entries (if any) at the end. */ if (IN_SET(pw->pw_name[0], '+', '-')) @@ -412,7 +428,7 @@ static int write_temporary_passwd(const char *passwd_path, FILE **tmpfile, char .pw_passwd = (char*) "x", /* We default to the root directory as home */ - .pw_dir = i->home ? i->home : (char*) "/", + .pw_dir = i->home ?: (char*) "/", /* Initialize the shell to nologin, with one exception: * for root we patch in something special */ @@ -506,13 +522,13 @@ static int write_temporary_shadow(const char *shadow_path, FILE **tmpfile, char ORDERED_HASHMAP_FOREACH(i, todo_uids, iterator) { struct spwd n = { .sp_namp = i->name, - .sp_pwdp = (char*) "!!", + .sp_pwdp = (char*) "!!", /* lock this password, and make it invalid */ .sp_lstchg = lstchg, .sp_min = -1, .sp_max = -1, .sp_warn = -1, .sp_inact = -1, - .sp_expire = -1, + .sp_expire = i->uid == 0 ? -1 : 1, /* lock account as a whole, unless this is root */ .sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */ }; @@ -576,15 +592,15 @@ static int write_temporary_group(const char *group_path, FILE **tmpfile, char ** * step that we don't generate duplicate entries. */ i = ordered_hashmap_get(groups, gr->gr_name); - if (i && i->todo_group) { - log_error("%s: Group \"%s\" already exists.", group_path, gr->gr_name); - return -EEXIST; - } + if (i && i->todo_group) + return log_error_errno(SYNTHETIC_ERRNO(EEXIST), + "%s: Group \"%s\" already exists.", + group_path, gr->gr_name); - if (ordered_hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid))) { - log_error("%s: Detected collision for GID " GID_FMT ".", group_path, gr->gr_gid); - return -EEXIST; - } + if (ordered_hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid))) + return log_error_errno(SYNTHETIC_ERRNO(EEXIST), + "%s: Detected collision for GID " GID_FMT ".", + group_path, gr->gr_gid); /* Make sure we keep the NIS entries (if any) at the end. */ if (IN_SET(gr->gr_name[0], '+', '-')) @@ -671,10 +687,10 @@ static int write_temporary_gshadow(const char * gshadow_path, FILE **tmpfile, ch while ((r = fgetsgent_sane(original, &sg)) > 0) { i = ordered_hashmap_get(groups, sg->sg_namp); - if (i && i->todo_group) { - log_error("%s: Group \"%s\" already exists.", gshadow_path, sg->sg_namp); - return -EEXIST; - } + if (i && i->todo_group) + return log_error_errno(SYNTHETIC_ERRNO(EEXIST), + "%s: Group \"%s\" already exists.", + gshadow_path, sg->sg_namp); r = putsgent_with_members(sg, gshadow); if (r < 0) @@ -822,11 +838,11 @@ static int uid_is_ok(uid_t uid, const char *name, bool check_with_gid) { } /* Let's check the files directly */ - if (hashmap_contains(database_uid, UID_TO_PTR(uid))) + if (hashmap_contains(database_by_uid, UID_TO_PTR(uid))) return 0; if (check_with_gid) { - n = hashmap_get(database_gid, GID_TO_PTR(uid)); + n = hashmap_get(database_by_gid, GID_TO_PTR(uid)); if (n && !streq(n, name)) return 0; } @@ -929,7 +945,7 @@ static int add_user(Item *i) { assert(i); /* Check the database directly */ - z = hashmap_get(database_user, i->name); + z = hashmap_get(database_by_username, i->name); if (z) { log_debug("User %s already exists.", i->name); i->uid = PTR_TO_UID(z); @@ -1005,10 +1021,8 @@ static int add_user(Item *i) { if (!i->uid_set) { for (;;) { r = uid_range_next_lower(uid_range, n_uid_range, &search_uid); - if (r < 0) { - log_error("No free user ID available for %s.", i->name); - return r; - } + if (r < 0) + return log_error_errno(r, "No free user ID available for %s.", i->name); r = uid_is_ok(search_uid, i->name, true); if (r < 0) @@ -1046,10 +1060,10 @@ static int gid_is_ok(gid_t gid) { if (ordered_hashmap_get(todo_uids, UID_TO_PTR(gid))) return 0; - if (hashmap_contains(database_gid, GID_TO_PTR(gid))) + if (hashmap_contains(database_by_gid, GID_TO_PTR(gid))) return 0; - if (hashmap_contains(database_uid, UID_TO_PTR(gid))) + if (hashmap_contains(database_by_uid, UID_TO_PTR(gid))) return 0; if (!arg_root) { @@ -1078,7 +1092,7 @@ static int add_group(Item *i) { assert(i); /* Check the database directly */ - z = hashmap_get(database_group, i->name); + z = hashmap_get(database_by_groupname, i->name); if (z) { log_debug("Group %s already exists.", i->name); i->gid = PTR_TO_GID(z); @@ -1112,10 +1126,10 @@ static int add_group(Item *i) { * r > 0: means the gid does not exist -> fail * r == 0: means the gid exists -> nothing more to do. */ - if (r > 0) { - log_error("Failed to create %s: please create GID %d", i->name, i->gid); - return -EINVAL; - } + if (r > 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Failed to create %s: please create GID %d", + i->name, i->gid); if (r == 0) return 0; } @@ -1162,10 +1176,8 @@ static int add_group(Item *i) { for (;;) { /* We look for new GIDs in the UID pool! */ r = uid_range_next_lower(uid_range, n_uid_range, &search_uid); - if (r < 0) { - log_error("No free group ID available for %s.", i->name); - return r; - } + if (r < 0) + return log_error_errno(r, "No free group ID available for %s.", i->name); r = gid_is_ok(search_uid); if (r < 0) @@ -1227,10 +1239,9 @@ static int process_item(Item *i) { } } -static void item_free(Item *i) { - +static Item* item_free(Item *i) { if (!i) - return; + return NULL; free(i->name); free(i->uid_path); @@ -1238,10 +1249,11 @@ static void item_free(Item *i) { free(i->description); free(i->home); free(i->shell); - free(i); + return mfree(i); } DEFINE_TRIVIAL_CLEANUP_FUNC(Item*, item_free); +DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(item_hash_ops, char, string_hash_func, string_compare_func, Item, item_free); static int add_implicit(void) { char *g, **l; @@ -1256,7 +1268,7 @@ static int add_implicit(void) { if (!ordered_hashmap_get(users, *m)) { _cleanup_(item_freep) Item *j = NULL; - r = ordered_hashmap_ensure_allocated(&users, &string_hash_ops); + r = ordered_hashmap_ensure_allocated(&users, &item_hash_ops); if (r < 0) return log_oom(); @@ -1281,7 +1293,7 @@ static int add_implicit(void) { ordered_hashmap_get(groups, g))) { _cleanup_(item_freep) Item *j = NULL; - r = ordered_hashmap_ensure_allocated(&groups, &string_hash_ops); + r = ordered_hashmap_ensure_allocated(&groups, &item_hash_ops); if (r < 0) return log_oom(); @@ -1363,7 +1375,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { *id = NULL, *resolved_id = NULL, *description = NULL, *resolved_description = NULL, *home = NULL, *resolved_home = NULL, - *shell, *resolved_shell = NULL; + *shell = NULL, *resolved_shell = NULL; _cleanup_(item_freep) Item *i = NULL; Item *existing; OrderedHashmap *h; @@ -1376,209 +1388,163 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { /* Parse columns */ p = buffer; - r = extract_many_words(&p, NULL, EXTRACT_QUOTES, + r = extract_many_words(&p, NULL, EXTRACT_UNQUOTE, &action, &name, &id, &description, &home, &shell, NULL); - if (r < 0) { - log_error("[%s:%u] Syntax error.", fname, line); - return r; - } - if (r < 2) { - log_error("[%s:%u] Missing action and name columns.", fname, line); - return -EINVAL; - } - if (!isempty(p)) { - log_error("[%s:%u] Trailing garbage.", fname, line); - return -EINVAL; - } + if (r < 0) + return log_error_errno(r, "[%s:%u] Syntax error.", fname, line); + if (r < 2) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] Missing action and name columns.", fname, line); + if (!isempty(p)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] Trailing garbage.", fname, line); /* Verify action */ - if (strlen(action) != 1) { - log_error("[%s:%u] Unknown modifier '%s'", fname, line, action); - return -EINVAL; - } + if (strlen(action) != 1) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] Unknown modifier '%s'", fname, line, action); - if (!IN_SET(action[0], ADD_USER, ADD_GROUP, ADD_MEMBER, ADD_RANGE)) { - log_error("[%s:%u] Unknown command type '%c'.", fname, line, action[0]); - return -EBADMSG; - } + if (!IN_SET(action[0], ADD_USER, ADD_GROUP, ADD_MEMBER, ADD_RANGE)) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), + "[%s:%u] Unknown command type '%c'.", fname, line, action[0]); /* Verify name */ - if (isempty(name) || streq(name, "-")) + if (empty_or_dash(name)) name = mfree(name); if (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 (r < 0) + log_error_errno(r, "[%s:%u] Failed to replace specifiers: %s", fname, line, 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 (!valid_user_group_name(resolved_name)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] '%s' is not a valid user or group name.", + fname, line, resolved_name); } /* Verify id */ - if (isempty(id) || streq(id, "-")) + if (empty_or_dash(id)) id = mfree(id); if (id) { 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; - } + if (r < 0) + return log_error_errno(r, "[%s:%u] Failed to replace specifiers: %s", + fname, line, name); } /* Verify description */ - if (isempty(description) || streq(description, "-")) + if (empty_or_dash(description)) description = mfree(description); if (description) { r = specifier_printf(description, specifier_table, NULL, &resolved_description); - if (r < 0) { - log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, description); - return r; - } + if (r < 0) + return log_error_errno(r, "[%s:%u] Failed to replace specifiers: %s", + fname, line, description); - if (!valid_gecos(resolved_description)) { - log_error("[%s:%u] '%s' is not a valid GECOS field.", fname, line, resolved_description); - return -EINVAL; - } + if (!valid_gecos(resolved_description)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] '%s' is not a valid GECOS field.", + fname, line, resolved_description); } /* Verify home */ - if (isempty(home) || streq(home, "-")) + if (empty_or_dash(home)) home = mfree(home); if (home) { r = specifier_printf(home, specifier_table, NULL, &resolved_home); - if (r < 0) { - log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, home); - return r; - } + if (r < 0) + return log_error_errno(r, "[%s:%u] Failed to replace specifiers: %s", + fname, line, home); - if (!valid_home(resolved_home)) { - log_error("[%s:%u] '%s' is not a valid home directory field.", fname, line, resolved_home); - return -EINVAL; - } + if (!valid_home(resolved_home)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] '%s' is not a valid home directory field.", + fname, line, resolved_home); } /* Verify shell */ - if (isempty(shell) || streq(shell, "-")) + if (empty_or_dash(shell)) shell = mfree(shell); if (shell) { r = specifier_printf(shell, specifier_table, NULL, &resolved_shell); - if (r < 0) { - log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, shell); - return r; - } + if (r < 0) + return log_error_errno(r, "[%s:%u] Failed to replace specifiers: %s", + fname, line, shell); - if (!valid_shell(resolved_shell)) { - log_error("[%s:%u] '%s' is not a valid login shell field.", fname, line, resolved_shell); - return -EINVAL; - } + if (!valid_shell(resolved_shell)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] '%s' is not a valid login shell field.", + fname, line, resolved_shell); } switch (action[0]) { case ADD_RANGE: - if (resolved_name) { - log_error("[%s:%u] Lines of type 'r' don't take a name field.", fname, line); - return -EINVAL; - } - - if (!resolved_id) { - log_error("[%s:%u] Lines of type 'r' require a ID range in the third field.", fname, line); - return -EINVAL; - } - - if (description || home || shell) { - log_error("[%s:%u] Lines of type '%c' don't take a %s field.", - fname, line, action[0], - description ? "GECOS" : home ? "home directory" : "login shell"); - return -EINVAL; - } + if (resolved_name) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] Lines of type 'r' don't take a name field.", + fname, line); + + if (!resolved_id) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] Lines of type 'r' require a ID range in the third field.", + fname, line); + + if (description || home || shell) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] Lines of type '%c' don't take a %s field.", + fname, line, action[0], + description ? "GECOS" : home ? "home directory" : "login shell"); r = uid_range_add_str(&uid_range, &n_uid_range, resolved_id); - if (r < 0) { - log_error("[%s:%u] Invalid UID range %s.", fname, line, resolved_id); - return -EINVAL; - } + if (r < 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] Invalid UID range %s.", fname, line, resolved_id); return 0; case ADD_MEMBER: { - char **l; - /* Try to extend an existing member or group item */ - if (!name) { - log_error("[%s:%u] Lines of type 'm' require a user name in the second field.", fname, line); - return -EINVAL; - } - - if (!resolved_id) { - log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname, line); - return -EINVAL; - } - - 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 (description || home || shell) { - log_error("[%s:%u] Lines of type '%c' don't take a %s field.", - fname, line, action[0], - description ? "GECOS" : home ? "home directory" : "login shell"); - return -EINVAL; - } - - r = ordered_hashmap_ensure_allocated(&members, &string_hash_ops); + if (!name) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] Lines of type 'm' require a user name in the second field.", + fname, line); + + if (!resolved_id) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] Lines of type 'm' require a group name in the third field.", + fname, line); + + if (!valid_user_group_name(resolved_id)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] '%s' is not a valid user or group name.", + fname, line, resolved_id); + + if (description || home || shell) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] Lines of type '%c' don't take a %s field.", + fname, line, action[0], + description ? "GECOS" : home ? "home directory" : "login shell"); + + r = string_strv_ordered_hashmap_put(&members, resolved_id, resolved_name); if (r < 0) - return log_oom(); - - l = ordered_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(ordered_hashmap_update(members, resolved_id, l) >= 0); - } else { - /* 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 = ordered_hashmap_put(members, resolved_id, l); - if (r < 0) { - free(l); - return log_oom(); - } - - resolved_id = resolved_name = NULL; - } + return log_error_errno(r, "Failed to store mapping for %s: %m", resolved_id); return 0; } case ADD_USER: - if (!name) { - log_error("[%s:%u] Lines of type 'u' require a user name in the second field.", fname, line); - return -EINVAL; - } + if (!name) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] Lines of type 'u' require a user name in the second field.", + fname, line); - r = ordered_hashmap_ensure_allocated(&users, &string_hash_ops); + r = ordered_hashmap_ensure_allocated(&users, &item_hash_ops); if (r < 0) return log_oom(); @@ -1617,19 +1583,18 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { break; case ADD_GROUP: - if (!name) { - log_error("[%s:%u] Lines of type 'g' require a user name in the second field.", fname, line); - return -EINVAL; - } + if (!name) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] Lines of type 'g' require a user name in the second field.", + fname, line); - if (description || home || shell) { - log_error("[%s:%u] Lines of type '%c' don't take a %s field.", - fname, line, action[0], - description ? "GECOS" : home ? "home directory" : "login shell"); - return -EINVAL; - } + if (description || home || shell) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] Lines of type '%c' don't take a %s field.", + fname, line, action[0], + description ? "GECOS" : home ? "home directory" : "login shell"); - r = ordered_hashmap_ensure_allocated(&groups, &string_hash_ops); + r = ordered_hashmap_ensure_allocated(&groups, &item_hash_ops); if (r < 0) return log_oom(); @@ -1662,7 +1627,6 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { existing = ordered_hashmap_get(h, i->name); if (existing) { - /* Two identical items are fine */ if (!item_equal(existing, i)) log_warning("Two or more conflicting lines for %s configured, ignoring.", i->name); @@ -1681,7 +1645,6 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { static int read_config_file(const char *fn, bool ignore_enoent) { _cleanup_fclose_ FILE *rf = NULL; FILE *f = NULL; - char line[LINE_MAX]; unsigned v = 0; int r = 0; @@ -1701,10 +1664,17 @@ static int read_config_file(const char *fn, bool ignore_enoent) { f = rf; } - FOREACH_LINE(line, f, break) { + for (;;) { + _cleanup_free_ char *line = NULL; char *l; int k; + k = read_line(f, LONG_LINE_MAX, &line); + if (k < 0) + return log_error_errno(k, "Failed to read '%s': %m", fn); + if (k == 0) + break; + v++; l = strstrip(line); @@ -1725,27 +1695,6 @@ static int read_config_file(const char *fn, bool ignore_enoent) { return r; } -static void free_database(Hashmap *by_name, Hashmap *by_id) { - char *name; - - for (;;) { - name = hashmap_first(by_id); - if (!name) - break; - - hashmap_remove(by_name, name); - - hashmap_steal_first_key(by_id); - free(name); - } - - while ((name = hashmap_steal_first_key(by_name))) - free(name); - - hashmap_free(by_name); - hashmap_free(by_id); -} - static int cat_config(void) { _cleanup_strv_free_ char **files = NULL; int r; @@ -1754,7 +1703,7 @@ static int cat_config(void) { if (r < 0) return r; - (void) pager_open(arg_no_pager, false); + (void) pager_open(arg_pager_flags); return cat_files(NULL, files, 0); } @@ -1833,10 +1782,9 @@ static int parse_argv(int argc, char *argv[]) { case ARG_REPLACE: if (!path_is_absolute(optarg) || - !endswith(optarg, ".conf")) { - log_error("The argument to --replace= must an absolute path to a config file"); - return -EINVAL; - } + !endswith(optarg, ".conf")) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "The argument to --replace= must an absolute path to a config file"); arg_replace = optarg; break; @@ -1846,7 +1794,7 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_NO_PAGER: - arg_no_pager = true; + arg_pager_flags |= PAGER_DISABLE; break; case '?': @@ -1856,15 +1804,13 @@ static int parse_argv(int argc, char *argv[]) { assert_not_reached("Unhandled option"); } - if (arg_replace && arg_cat_config) { - log_error("Option --replace= is not supported with --cat-config"); - return -EINVAL; - } + if (arg_replace && arg_cat_config) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Option --replace= is not supported with --cat-config"); - if (arg_replace && optind >= argc) { - log_error("When --replace= is given, some configuration items must be specified"); - return -EINVAL; - } + if (arg_replace && optind >= argc) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "When --replace= is given, some configuration items must be specified"); return 1; } @@ -1916,33 +1862,26 @@ static int read_config_files(char **args) { return 0; } -int main(int argc, char *argv[]) { +static int run(int argc, char *argv[]) { _cleanup_close_ int lock = -1; Iterator iterator; - int r; Item *i; - char *n; + int r; r = parse_argv(argc, argv); if (r <= 0) - goto finish; + return r; - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); + log_setup_service(); - if (arg_cat_config) { - r = cat_config(); - goto finish; - } + if (arg_cat_config) + return cat_config(); umask(0022); r = mac_selinux_init(); - if (r < 0) { - log_error_errno(r, "SELinux setup failed: %m"); - goto finish; - } + if (r < 0) + return log_error_errno(r, "SELinux setup failed: %m"); /* If command line arguments are specified along with --replace, read all * configuration files and insert the positional arguments at the specified @@ -1955,48 +1894,38 @@ int main(int argc, char *argv[]) { else r = parse_arguments(argv + optind); if (r < 0) - goto finish; + return r; /* Let's tell nss-systemd not to synthesize the "root" and "nobody" entries for it, so that our detection * whether the names or UID/GID area already used otherwise doesn't get confused. After all, even though * nss-systemd synthesizes these users/groups, they should still appear in /etc/passwd and /etc/group, as the * synthesizing logic is merely supposed to be fallback for cases where we run with a completely unpopulated * /etc. */ - if (setenv("SYSTEMD_NSS_BYPASS_SYNTHETIC", "1", 1) < 0) { - r = log_error_errno(errno, "Failed to set SYSTEMD_NSS_BYPASS_SYNTHETIC environment variable: %m"); - goto finish; - } + if (setenv("SYSTEMD_NSS_BYPASS_SYNTHETIC", "1", 1) < 0) + return log_error_errno(errno, "Failed to set SYSTEMD_NSS_BYPASS_SYNTHETIC environment variable: %m"); if (!uid_range) { /* Default to default range of 1..SYSTEM_UID_MAX */ r = uid_range_add(&uid_range, &n_uid_range, 1, SYSTEM_UID_MAX); - if (r < 0) { - log_oom(); - goto finish; - } + if (r < 0) + return log_oom(); } r = add_implicit(); if (r < 0) - goto finish; + return r; lock = take_etc_passwd_lock(arg_root); - if (lock < 0) { - log_error_errno(lock, "Failed to take /etc/passwd lock: %m"); - goto finish; - } + if (lock < 0) + return log_error_errno(lock, "Failed to take /etc/passwd lock: %m"); r = load_user_database(); - if (r < 0) { - log_error_errno(r, "Failed to load user database: %m"); - goto finish; - } + if (r < 0) + return log_error_errno(r, "Failed to load user database: %m"); r = load_group_database(); - if (r < 0) { - log_error_errno(r, "Failed to read group database: %m"); - goto finish; - } + if (r < 0) + return log_error_errno(r, "Failed to read group database: %m"); ORDERED_HASHMAP_FOREACH(i, groups, iterator) (void) process_item(i); @@ -2006,29 +1935,9 @@ int main(int argc, char *argv[]) { r = write_files(); if (r < 0) - log_error_errno(r, "Failed to write files: %m"); + return log_error_errno(r, "Failed to write files: %m"); -finish: - pager_close(); - - ordered_hashmap_free_with_destructor(groups, item_free); - ordered_hashmap_free_with_destructor(users, item_free); - - while ((n = ordered_hashmap_first_key(members))) { - strv_free(ordered_hashmap_steal_first(members)); - free(n); - } - ordered_hashmap_free(members); - - ordered_hashmap_free(todo_uids); - ordered_hashmap_free(todo_gids); - - free_database(database_user, database_uid); - free_database(database_group, database_gid); - - free(uid_range); - - free(arg_root); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; + return 0; } + +DEFINE_MAIN_FUNCTION(run);