diff --git a/.gitignore b/.gitignore
index 3e65a61ea3..b3aba0921b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,11 +1,6 @@
-*.a
*.cache
-*.gch
-*.log
-*.o
*.plist
*.py[co]
-*.stamp
*.swp
*.trs
*~
diff --git a/man/systemd-sysusers.xml b/man/systemd-sysusers.xml
index cee8e6ef54..3ad8135dcd 100644
--- a/man/systemd-sysusers.xml
+++ b/man/systemd-sysusers.xml
@@ -115,6 +115,12 @@
+
+
+ Process the configuration and figure out what entries would be created, but don't
+ actually write anything.
+
+
Treat each positional argument as a separate configuration
diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c
index 0011ff4770..c3a9e54f3f 100644
--- a/src/sysusers/sysusers.c
+++ b/src/sysusers/sysusers.c
@@ -38,12 +38,27 @@
#include "util.h"
typedef enum ItemType {
- ADD_USER = 'u',
- ADD_GROUP = 'g',
+ ADD_USER = 'u',
+ ADD_GROUP = 'g',
ADD_MEMBER = 'm',
- ADD_RANGE = 'r',
+ ADD_RANGE = 'r',
} ItemType;
+static inline const char* item_type_to_string(ItemType t) {
+ switch (t) {
+ case ADD_USER:
+ return "user";
+ case ADD_GROUP:
+ return "group";
+ case ADD_MEMBER:
+ return "member";
+ case ADD_RANGE:
+ return "range";
+ default:
+ assert_not_reached();
+ }
+}
+
typedef struct Item {
ItemType type;
@@ -60,8 +75,8 @@ typedef struct Item {
bool gid_set:1;
- /* When set the group with the specified gid must exist
- * and the check if a uid clashes with the gid is skipped.
+ /* When set the group with the specified GID must exist
+ * and the check if a UID clashes with the GID is skipped.
*/
bool id_set_strict:1;
@@ -75,6 +90,7 @@ static char *arg_root = NULL;
static char *arg_image = NULL;
static bool arg_cat_config = false;
static const char *arg_replace = NULL;
+static bool arg_dry_run = false;
static bool arg_inline = false;
static PagerFlags arg_pager_flags = 0;
@@ -387,9 +403,14 @@ static int write_temporary_passwd(const char *passwd_path, FILE **tmpfile, char
Item *i;
int r;
- if (ordered_hashmap_size(todo_uids) == 0)
+ if (ordered_hashmap_isempty(todo_uids))
return 0;
+ if (arg_dry_run) {
+ log_info("Would write /etc/passwd…");
+ return 0;
+ }
+
r = fopen_temporary_label("/etc/passwd", passwd_path, &passwd, &passwd_tmp);
if (r < 0)
return log_debug_errno(r, "Failed to open temporary copy of %s: %m", passwd_path);
@@ -506,9 +527,14 @@ static int write_temporary_shadow(const char *shadow_path, FILE **tmpfile, char
Item *i;
int r;
- if (ordered_hashmap_size(todo_uids) == 0)
+ if (ordered_hashmap_isempty(todo_uids))
return 0;
+ if (arg_dry_run) {
+ log_info("Would write /etc/shadow…");
+ return 0;
+ }
+
r = fopen_temporary_label("/etc/shadow", shadow_path, &shadow, &shadow_tmp);
if (r < 0)
return log_debug_errno(r, "Failed to open temporary copy of %s: %m", shadow_path);
@@ -639,9 +665,14 @@ static int write_temporary_group(const char *group_path, FILE **tmpfile, char **
Item *i;
int r;
- if (ordered_hashmap_size(todo_gids) == 0 && ordered_hashmap_size(members) == 0)
+ if (ordered_hashmap_isempty(todo_gids) && ordered_hashmap_isempty(members))
return 0;
+ if (arg_dry_run) {
+ log_info("Would write /etc/group…");
+ return 0;
+ }
+
r = fopen_temporary_label("/etc/group", group_path, &group, &group_tmp);
if (r < 0)
return log_debug_errno(r, "Failed to open temporary copy of %s: %m", group_path);
@@ -740,9 +771,14 @@ static int write_temporary_gshadow(const char * gshadow_path, FILE **tmpfile, ch
Item *i;
int r;
- if (ordered_hashmap_size(todo_gids) == 0 && ordered_hashmap_size(members) == 0)
+ if (ordered_hashmap_isempty(todo_gids) && ordered_hashmap_isempty(members))
return 0;
+ if (arg_dry_run) {
+ log_info("Would write /etc/gshadow…");
+ return 0;
+ }
+
r = fopen_temporary_label("/etc/gshadow", gshadow_path, &gshadow, &gshadow_tmp);
if (r < 0)
return log_debug_errno(r, "Failed to open temporary copy of %s: %m", gshadow_path);
@@ -969,13 +1005,13 @@ static int read_id_from_file(Item *i, uid_t *_uid, gid_t *_gid) {
assert(i);
- /* First, try to get the gid directly */
+ /* First, try to get the GID directly */
if (_gid && i->gid_path && root_stat(i->gid_path, &st) >= 0) {
gid = st.st_gid;
found_gid = true;
}
- /* Then, try to get the uid directly */
+ /* Then, try to get the UID directly */
if ((_uid || (_gid && !found_gid))
&& i->uid_path
&& root_stat(i->uid_path, &st) >= 0) {
@@ -983,14 +1019,14 @@ static int read_id_from_file(Item *i, uid_t *_uid, gid_t *_gid) {
uid = st.st_uid;
found_uid = true;
- /* If we need the gid, but had no success yet, also derive it from the uid path */
+ /* If we need the gid, but had no success yet, also derive it from the UID path */
if (_gid && !found_gid) {
gid = st.st_gid;
found_gid = true;
}
}
- /* If that didn't work yet, then let's reuse the gid as uid */
+ /* If that didn't work yet, then let's reuse the GID as UID */
if (_uid && !found_uid && i->gid_path) {
if (found_gid) {
@@ -1055,13 +1091,13 @@ static int add_user(Item *i) {
return log_error_errno(errno, "Failed to check if user %s already exists: %m", i->name);
}
- /* Try to use the suggested numeric uid */
+ /* Try to use the suggested numeric UID */
if (i->uid_set) {
r = uid_is_ok(i->uid, i->name, !i->id_set_strict);
if (r < 0)
- return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid);
+ return log_error_errno(r, "Failed to verify UID " UID_FMT ": %m", i->uid);
if (r == 0) {
- log_debug("Suggested user ID " UID_FMT " for %s already used.", i->uid, i->name);
+ log_info("Suggested user ID " UID_FMT " for %s already used.", i->uid, i->name);
i->uid_set = false;
}
}
@@ -1077,7 +1113,7 @@ static int add_user(Item *i) {
else {
r = uid_is_ok(c, i->name, true);
if (r < 0)
- return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid);
+ return log_error_errno(r, "Failed to verify UID " UID_FMT ": %m", i->uid);
else if (r > 0) {
i->uid = c;
i->uid_set = true;
@@ -1091,7 +1127,7 @@ static int add_user(Item *i) {
if (!i->uid_set && i->gid_set) {
r = uid_is_ok((uid_t) i->gid, i->name, true);
if (r < 0)
- return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid);
+ return log_error_errno(r, "Failed to verify UID " UID_FMT ": %m", i->uid);
if (r > 0) {
i->uid = (uid_t) i->gid;
i->uid_set = true;
@@ -1109,7 +1145,7 @@ static int add_user(Item *i) {
r = uid_is_ok(search_uid, i->name, true);
if (r < 0)
- return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid);
+ return log_error_errno(r, "Failed to verify UID " UID_FMT ": %m", i->uid);
else if (r > 0)
break;
}
@@ -1120,16 +1156,16 @@ static int add_user(Item *i) {
r = ordered_hashmap_ensure_put(&todo_uids, NULL, UID_TO_PTR(i->uid), i);
if (r == -EEXIST)
- return log_error_errno(r, "Requested user %s with uid " UID_FMT " and gid" GID_FMT " to be created is duplicated "
+ return log_error_errno(r, "Requested user %s with UID " UID_FMT " and gid" GID_FMT " to be created is duplicated "
"or conflicts with another user.", i->name, i->uid, i->gid);
if (r == -ENOMEM)
return log_oom();
if (r < 0)
- return log_error_errno(r, "Failed to store user %s with uid " UID_FMT " and gid " GID_FMT " to be created: %m",
+ return log_error_errno(r, "Failed to store user %s with UID " UID_FMT " and GID " GID_FMT " to be created: %m",
i->name, i->uid, i->gid);
i->todo_user = true;
- log_info("Creating user %s (%s) with uid " UID_FMT " and gid " GID_FMT ".",
+ log_info("Creating user '%s' (%s) with UID " UID_FMT " and GID " GID_FMT ".",
i->name, strna(i->description), i->uid, i->gid);
return 0;
@@ -1214,15 +1250,15 @@ static int add_group(Item *i) {
return 0;
}
- /* Try to use the suggested numeric gid */
+ /* Try to use the suggested numeric GID */
if (i->gid_set) {
r = gid_is_ok(i->gid);
if (r < 0)
- return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid);
+ return log_error_errno(r, "Failed to verify GID " GID_FMT ": %m", i->gid);
if (i->id_set_strict) {
- /* If we require the gid to already exist we can return here:
- * r > 0: means the gid does not exist -> fail
- * r == 0: means the gid exists -> nothing more to do.
+ /* If we require the GID to already exist we can return here:
+ * r > 0: means the GID does not exist -> fail
+ * r == 0: means the GID exists -> nothing more to do.
*/
if (r > 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
@@ -1232,7 +1268,7 @@ static int add_group(Item *i) {
return 0;
}
if (r == 0) {
- log_debug("Suggested group ID " GID_FMT " for %s already used.", i->gid, i->name);
+ log_info("Suggested group ID " GID_FMT " for %s already used.", i->gid, i->name);
i->gid_set = false;
}
}
@@ -1241,7 +1277,7 @@ static int add_group(Item *i) {
if (!i->gid_set && i->uid_set) {
r = gid_is_ok((gid_t) i->uid);
if (r < 0)
- return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid);
+ return log_error_errno(r, "Failed to verify GID " GID_FMT ": %m", i->gid);
if (r > 0) {
i->gid = (gid_t) i->uid;
i->gid_set = true;
@@ -1259,7 +1295,7 @@ static int add_group(Item *i) {
else {
r = gid_is_ok(c);
if (r < 0)
- return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid);
+ return log_error_errno(r, "Failed to verify GID " GID_FMT ": %m", i->gid);
else if (r > 0) {
i->gid = c;
i->gid_set = true;
@@ -1281,7 +1317,7 @@ static int add_group(Item *i) {
r = gid_is_ok(search_uid);
if (r < 0)
- return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid);
+ return log_error_errno(r, "Failed to verify GID " GID_FMT ": %m", i->gid);
else if (r > 0)
break;
}
@@ -1292,14 +1328,14 @@ static int add_group(Item *i) {
r = ordered_hashmap_ensure_put(&todo_gids, NULL, GID_TO_PTR(i->gid), i);
if (r == -EEXIST)
- return log_error_errno(r, "Requested group %s with gid "GID_FMT " to be created is duplicated or conflicts with another user.", i->name, i->gid);
+ return log_error_errno(r, "Requested group %s with GID "GID_FMT " to be created is duplicated or conflicts with another user.", i->name, i->gid);
if (r == -ENOMEM)
return log_oom();
if (r < 0)
- return log_error_errno(r, "Failed to store group %s with gid " GID_FMT " to be created: %m", i->name, i->gid);
+ return log_error_errno(r, "Failed to store group %s with GID " GID_FMT " to be created: %m", i->name, i->gid);
i->todo_group = true;
- log_info("Creating group %s with gid " GID_FMT ".", i->name, i->gid);
+ log_info("Creating group '%s' with GID " GID_FMT ".", i->name, i->gid);
return 0;
}
@@ -1463,7 +1499,6 @@ static bool item_equal(Item *a, Item *b) {
}
static int parse_line(const char *fname, unsigned line, const char *buffer) {
-
_cleanup_free_ char *action = NULL,
*name = NULL, *resolved_name = NULL,
*id = NULL, *resolved_id = NULL,
@@ -1728,7 +1763,9 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
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);
+ log_warning("%s:%u: conflict with earlier configuration for %s '%s', ignoring line.",
+ fname, line,
+ item_type_to_string(i->type), i->name);
return 0;
}
@@ -1825,6 +1862,7 @@ static int help(void) {
" --root=PATH Operate on an alternate filesystem root\n"
" --image=PATH Operate on disk image as filesystem root\n"
" --replace=PATH Treat arguments as replacement for PATH\n"
+ " --dry-run Just print what would be done\n"
" --inline Treat arguments as configuration lines\n"
" --no-pager Do not pipe output into a pager\n"
"\nSee the %s for details.\n",
@@ -1842,6 +1880,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_ROOT,
ARG_IMAGE,
ARG_REPLACE,
+ ARG_DRY_RUN,
ARG_INLINE,
ARG_NO_PAGER,
};
@@ -1851,8 +1890,9 @@ static int parse_argv(int argc, char *argv[]) {
{ "version", no_argument, NULL, ARG_VERSION },
{ "cat-config", no_argument, NULL, ARG_CAT_CONFIG },
{ "root", required_argument, NULL, ARG_ROOT },
- { "image", required_argument, NULL, ARG_IMAGE },
+ { "image", required_argument, NULL, ARG_IMAGE },
{ "replace", required_argument, NULL, ARG_REPLACE },
+ { "dry-run", no_argument, NULL, ARG_DRY_RUN },
{ "inline", no_argument, NULL, ARG_INLINE },
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
{}
@@ -1903,6 +1943,10 @@ static int parse_argv(int argc, char *argv[]) {
arg_replace = optarg;
break;
+ case ARG_DRY_RUN:
+ arg_dry_run = true;
+ break;
+
case ARG_INLINE:
arg_inline = true;
break;
@@ -2061,7 +2105,7 @@ static int run(int argc, char *argv[]) {
login_defs_need_warning = true;
/* We pick a range that very conservative: we look at compiled-in maximum and the value in
- * /etc/login.defs. That way the uids/gids which we allocate will be interpreted correctly,
+ * /etc/login.defs. That way the UIDs/GIDs which we allocate will be interpreted correctly,
* even if /etc/login.defs is removed later. (The bottom bound doesn't matter much, since
* it's only used during allocation, so we use the configured value directly). */
uid_t begin = login_defs.system_alloc_uid_min,
@@ -2077,9 +2121,11 @@ static int run(int argc, char *argv[]) {
if (r < 0)
return r;
- lock = take_etc_passwd_lock(arg_root);
- if (lock < 0)
- return log_error_errno(lock, "Failed to take /etc/passwd lock: %m");
+ if (!arg_dry_run) {
+ lock = take_etc_passwd_lock(arg_root);
+ if (lock < 0)
+ return log_error_errno(lock, "Failed to take /etc/passwd lock: %m");
+ }
r = load_user_database();
if (r < 0)