#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"
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;
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;
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;
}
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;
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) {
return 0;
fail:
- unlink(temp);
+ (void) unlink(temp);
return r;
}
}
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) {
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], '+', '-'))
.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 */
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 ... */
};
* 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], '+', '-'))
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)
}
/* 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;
}
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);
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)
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) {
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);
* 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;
}
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)
}
}
-static void item_free(Item *i) {
-
+static Item* item_free(Item *i) {
if (!i)
- return;
+ return NULL;
free(i->name);
free(i->uid_path);
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;
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();
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();
*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;
/* 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();
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();
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);
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;
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);
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;
if (r < 0)
return r;
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
return cat_files(NULL, files, 0);
}
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;
break;
case ARG_NO_PAGER:
- arg_no_pager = true;
+ arg_pager_flags |= PAGER_DISABLE;
break;
case '?':
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;
}
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
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);
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);