#include "sd-varlink.h"
+#include "bitfield.h"
#include "conf-files.h"
#include "dirent-util.h"
#include "dlfcn-util.h"
LookupWhat what;
UserDBFlags flags;
Set *links;
+
+ const char *method; /* Note, this is a const static string! */
+ sd_json_variant *query;
+
+ bool more:1;
bool nss_covered:1;
bool nss_iterating:1;
bool dropin_covered:1;
bool synthesize_root:1;
bool synthesize_nobody:1;
bool nss_systemd_blocked:1;
+
char **dropins;
size_t current_dropin;
int error;
unsigned n_found;
+
sd_event *event;
UserRecord *found_user; /* when .what == LOOKUP_USER */
GroupRecord *found_group; /* when .what == LOOKUP_GROUP */
char *filter_user_name, *filter_group_name;
};
+static int userdb_connect(UserDBIterator *iterator, const char *path, const char *method, bool more, sd_json_variant *query);
+
UserDBIterator* userdb_iterator_free(UserDBIterator *iterator) {
if (!iterator)
return NULL;
+ sd_json_variant_unref(iterator->query);
+
set_free(iterator->links);
strv_free(iterator->dropins);
free(d->group_name);
}
+static int userdb_maybe_restart_query(
+ UserDBIterator *iterator,
+ sd_varlink *link,
+ sd_json_variant *parameters,
+ const char *error_id) {
+
+ int r;
+
+ assert(iterator);
+ assert(link);
+ assert(error_id);
+
+ /* These fields were added in v258 and didn't exist in previous implementations. Hence, we consider
+ * their support optional: if any service refuses any of these fields, we'll restart the query
+ * without them, and apply the filtering they are supposed to do client side. */
+ static const char *const fields[] = {
+ "fuzzyNames",
+ "dispositionMask",
+ "uidMin",
+ "uidMax",
+ "gidMin",
+ "gidMax",
+ NULL
+ };
+
+ /* Figure out if the reported error indicates any of the suppressable fields are at fault, and that
+ * our query actually included them */
+ bool restart = false;
+ STRV_FOREACH(f, fields) {
+ if (!sd_varlink_error_is_invalid_parameter(error_id, parameters, *f))
+ continue;
+
+ if (!sd_json_variant_by_key(iterator->query, *f))
+ continue;
+
+ restart = true;
+ break;
+ }
+
+ if (!restart)
+ return 0;
+
+ /* Now patch the fields out */
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *patched_query =
+ sd_json_variant_ref(iterator->query);
+
+ r = sd_json_variant_filter(&patched_query, (char**const) fields);
+ if (r < 0)
+ return r;
+
+ /* NB: we stored the socket path in the varlink connection description when we set things up here! */
+ r = userdb_connect(
+ iterator,
+ ASSERT_PTR(sd_varlink_get_description(link)),
+ iterator->method,
+ iterator->more,
+ patched_query);
+ if (r < 0)
+ return r;
+
+ log_debug("Restarted query to service '%s' due to missing features.", sd_varlink_get_description(link));
+ return 1;
+}
+
static int userdb_on_query_reply(
sd_varlink *link,
sd_json_variant *parameters,
if (error_id) {
log_debug("Got lookup error: %s", error_id);
+ r = userdb_maybe_restart_query(iterator, link, parameters, error_id);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ r = 0;
+ goto finish;
+ }
+
/* Convert various forms of record not found into -ESRCH, since NSS typically doesn't care,
* about the details. Note that if a userName specification is refused as invalid parameter,
* we also turn this into -ESRCH following the logic that there cannot be a user record for a
sd_varlink_error_is_invalid_parameter(error_id, parameters, "userName") ||
sd_varlink_error_is_invalid_parameter(error_id, parameters, "groupName"))
r = -ESRCH;
+ else if (streq(error_id, "io.systemd.UserDatabase.NonMatchingRecordFound"))
+ r = -ENOEXEC;
else if (streq(error_id, "io.systemd.UserDatabase.ServiceNotAvailable"))
r = -EHOSTDOWN;
else if (streq(error_id, "io.systemd.UserDatabase.EnumerationNotSupported"))
}
finish:
- /* If we got one ESRCH, let that win. This way when we do a wild dump we won't be tripped up by bad
- * errors if at least one connection ended cleanly */
- if (r == -ESRCH || iterator->error == 0)
+ /* If we got one ESRCH or ENOEXEC, let that win. This way when we do a wild dump we won't be tripped
+ * up by bad errors – as long as at least one connection ended somewhat cleanly */
+ if (IN_SET(r, -ESRCH, -ENOEXEC) || iterator->error == 0)
iterator->error = -r;
assert_se(set_remove(iterator->links, link) == link);
if (r < 0)
return log_debug_errno(r, "Failed to attach varlink connection to event loop: %m");
- (void) sd_varlink_set_description(vl, path);
+ /* Note, this is load bearing: we store the socket path as description for the varlink
+ * connection. That's not just good for debugging, but we reuse this information in case we need to
+ * reissue the query with a reduced set of parameters. */
+ r = sd_varlink_set_description(vl, path);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to set varlink connection description: %m");
r = sd_varlink_bind_reply(vl, userdb_on_query_reply);
if (r < 0)
static int userdb_start_query(
UserDBIterator *iterator,
- const char *method,
+ const char *method, /* must be a static string, we are not going to copy this here! */
bool more,
sd_json_variant *query,
UserDBFlags flags) {
if (FLAGS_SET(flags, USERDB_EXCLUDE_VARLINK))
return -ENOLINK;
+ assert(!iterator->query);
+ iterator->method = method; /* note: we don't make a copy here! */
+ iterator->query = sd_json_variant_ref(query);
+ iterator->more = more;
+
e = getenv("SYSTEMD_BYPASS_USERDB");
if (e) {
r = parse_boolean(e);
return 0;
}
-int userdb_by_name(const char *name, UserDBFlags flags, UserRecord **ret) {
- _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
- _cleanup_(sd_json_variant_unrefp) sd_json_variant *query = NULL;
+static int query_append_disposition_mask(sd_json_variant **query, uint64_t mask) {
int r;
- if (FLAGS_SET(flags, USERDB_PARSE_NUMERIC)) {
- uid_t uid;
+ assert(query);
- if (parse_uid(name, &uid) >= 0)
- return userdb_by_uid(uid, flags, ret);
+ if (FLAGS_SET(mask, USER_DISPOSITION_MASK_ALL))
+ return 0;
+
+ _cleanup_strv_free_ char **dispositions = NULL;
+ for (UserDisposition d = 0; d < _USER_DISPOSITION_MAX; d++) {
+ if (!BITS_SET(mask, d))
+ continue;
+
+ r = strv_extend(&dispositions, user_disposition_to_string(d));
+ if (r < 0)
+ return r;
}
- if (!valid_user_group_name(name, VALID_USER_RELAX))
- return -EINVAL;
+ return sd_json_variant_merge_objectbo(
+ query,
+ SD_JSON_BUILD_PAIR_STRV("dispositionMask", dispositions));
+}
- r = sd_json_buildo(&query, SD_JSON_BUILD_PAIR("userName", SD_JSON_BUILD_STRING(name)));
+static int query_append_uid_match(sd_json_variant **query, const UserDBMatch *match) {
+ int r;
+
+ assert(query);
+
+ if (!userdb_match_is_set(match))
+ return 0;
+
+ r = sd_json_variant_merge_objectbo(
+ query,
+ SD_JSON_BUILD_PAIR_CONDITION(!strv_isempty(match->fuzzy_names), "fuzzyNames", SD_JSON_BUILD_STRV(match->fuzzy_names)),
+ SD_JSON_BUILD_PAIR_CONDITION(match->uid_min > 0, "uidMin", SD_JSON_BUILD_UNSIGNED(match->uid_min)),
+ SD_JSON_BUILD_PAIR_CONDITION(match->uid_max < UID_INVALID-1, "uidMax", SD_JSON_BUILD_UNSIGNED(match->uid_max)));
if (r < 0)
return r;
- iterator = userdb_iterator_new(LOOKUP_USER, flags);
- if (!iterator)
- return -ENOMEM;
+ return query_append_disposition_mask(query, match->disposition_mask);
+}
- r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetUserRecord", false, query, flags);
- if (r >= 0) {
- r = userdb_process(iterator, ret, NULL, NULL, NULL);
- if (r >= 0)
- return r;
- }
+static int userdb_by_name_fallbacks(
+ const char *name,
+ UserDBIterator *iterator,
+ UserDBFlags flags,
+ UserRecord **ret) {
+ int r;
+
+ assert(name);
+ assert(iterator);
+ assert(ret);
if (!FLAGS_SET(flags, USERDB_EXCLUDE_DROPIN) && !iterator->dropin_covered) {
- r = dropin_user_record_by_name(name, NULL, flags, ret);
+ r = dropin_user_record_by_name(name, /* path= */ NULL, flags, ret);
if (r >= 0)
return r;
}
return r;
if (r > 0)
return synthetic_foreign_user_build(foreign_uid, ret);
- r = -ESRCH;
}
- return r;
+ return -ESRCH;
}
-int userdb_by_uid(uid_t uid, UserDBFlags flags, UserRecord **ret) {
+int userdb_by_name(const char *name, const UserDBMatch *match, UserDBFlags flags, UserRecord **ret) {
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
_cleanup_(sd_json_variant_unrefp) sd_json_variant *query = NULL;
int r;
- if (!uid_is_valid(uid))
+ /* Well known errors this returns:
+ * -EINVAL → user name is not valid
+ * -ESRCH → no such user
+ * -ENOEXEC → found a user by request UID or name, but it does not match filter
+ * -EHOSTDOWN → service failed for some reason
+ * -ETIMEDOUT → service timed out
+ */
+
+ assert(name);
+
+ if (FLAGS_SET(flags, USERDB_PARSE_NUMERIC)) {
+ uid_t uid;
+
+ if (parse_uid(name, &uid) >= 0)
+ return userdb_by_uid(uid, match, flags, ret);
+ }
+
+ if (!valid_user_group_name(name, VALID_USER_RELAX))
return -EINVAL;
- r = sd_json_buildo(&query, SD_JSON_BUILD_PAIR("uid", SD_JSON_BUILD_UNSIGNED(uid)));
+ r = sd_json_buildo(&query, SD_JSON_BUILD_PAIR("userName", SD_JSON_BUILD_STRING(name)));
+ if (r < 0)
+ return r;
+
+ r = query_append_uid_match(&query, match);
if (r < 0)
return r;
if (!iterator)
return -ENOMEM;
- r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetUserRecord", false, query, flags);
+ _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
+ r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetUserRecord", /* more= */ false, query, flags);
if (r >= 0) {
- r = userdb_process(iterator, ret, NULL, NULL, NULL);
- if (r >= 0)
+ r = userdb_process(iterator, &ur, /* ret_group_record= */ NULL, /* ret_user_name= */ NULL, /* ret_group_name= */ NULL);
+ if (r == -ENOEXEC) /* found a user matching UID or name, but not filter. In this case the
+ * fallback paths below are pointless */
return r;
}
+ if (r < 0) { /* If the above fails for any other reason, try fallback paths */
+ r = userdb_by_name_fallbacks(name, iterator, flags, &ur);
+ if (r < 0)
+ return r;
+ }
+
+ /* NB: we always apply our own filtering here, explicitly, regardless if the server supported it or
+ * not. It's more robust this way, we never know how carefully the server is written, and whether it
+ * properly implements all details of the filtering logic. */
+ r = user_record_match(ur, match);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENOEXEC;
+
+ if (ret)
+ *ret = TAKE_PTR(ur);
+
+ return 0;
+}
+
+static int userdb_by_uid_fallbacks(
+ uid_t uid,
+ UserDBIterator *iterator,
+ UserDBFlags flags,
+ UserRecord **ret) {
+ int r;
+
+ assert(uid_is_valid(uid));
+ assert(iterator);
+ assert(ret);
if (!FLAGS_SET(flags, USERDB_EXCLUDE_DROPIN) && !iterator->dropin_covered) {
r = dropin_user_record_by_uid(uid, NULL, flags, ret);
if (!FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE_FOREIGN) && uid_is_foreign(uid))
return synthetic_foreign_user_build(uid - FOREIGN_UID_BASE, ret);
- return r;
+ return -ESRCH;
}
-int userdb_all(UserDBFlags flags, UserDBIterator **ret) {
+int userdb_by_uid(uid_t uid, const UserDBMatch *match, UserDBFlags flags, UserRecord **ret) {
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *query = NULL;
+ int r;
+
+ if (!uid_is_valid(uid))
+ return -EINVAL;
+
+ r = sd_json_buildo(&query, SD_JSON_BUILD_PAIR("uid", SD_JSON_BUILD_UNSIGNED(uid)));
+ if (r < 0)
+ return r;
+
+ r = query_append_uid_match(&query, match);
+ if (r < 0)
+ return r;
+
+ iterator = userdb_iterator_new(LOOKUP_USER, flags);
+ if (!iterator)
+ return -ENOMEM;
+
+ _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
+ r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetUserRecord", /* more= */ false, query, flags);
+ if (r >= 0) {
+ r = userdb_process(iterator, &ur, /* ret_group_record= */ NULL, /* ret_user_name= */ NULL, /* ret_group_name= */ NULL);
+ if (r == -ENOEXEC)
+ return r;
+ }
+ if (r < 0) {
+ r = userdb_by_uid_fallbacks(uid, iterator, flags, &ur);
+ if (r < 0)
+ return r;
+ }
+
+ r = user_record_match(ur, match);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENOEXEC;
+
+ if (ret)
+ *ret = TAKE_PTR(ur);
+
+ return 0;
+}
+
+int userdb_all(const UserDBMatch *match, UserDBFlags flags, UserDBIterator **ret) {
+ _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *query = NULL;
int r, qr;
assert(ret);
+ r = query_append_uid_match(&query, match);
+ if (r < 0)
+ return r;
+
iterator = userdb_iterator_new(LOOKUP_USER, flags);
if (!iterator)
return -ENOMEM;
- qr = userdb_start_query(iterator, "io.systemd.UserDatabase.GetUserRecord", true, NULL, flags);
+ qr = userdb_start_query(iterator, "io.systemd.UserDatabase.GetUserRecord", /* more= */ true, query, flags);
if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && (qr < 0 || !iterator->nss_covered)) {
r = userdb_iterator_block_nss_systemd(iterator);
return 0;
}
-int userdb_iterator_get(UserDBIterator *iterator, UserRecord **ret) {
+static int userdb_iterator_get_one(UserDBIterator *iterator, UserRecord **ret) {
int r;
assert(iterator);
}
/* Then, let's return the users provided by varlink IPC */
- r = userdb_process(iterator, ret, NULL, NULL, NULL);
+ r = userdb_process(iterator, ret, /* ret_group_record= */ NULL, /* ret_user_name= */ NULL, /* ret_group_name= */ NULL);
if (r < 0) {
/* Finally, synthesize root + nobody if not done yet */
return r;
}
+int userdb_iterator_get(UserDBIterator *iterator, const UserDBMatch *match, UserRecord **ret) {
+ int r;
+
+ assert(iterator);
+ assert(iterator->what == LOOKUP_USER);
+
+ for (;;) {
+ _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
+
+ r = userdb_iterator_get_one(iterator, userdb_match_is_set(match) || ret ? &ur : NULL);
+ if (r < 0)
+ return r;
+
+ if (ur && !user_record_match(ur, match))
+ continue;
+
+ if (ret)
+ *ret = TAKE_PTR(ur);
+
+ return r;
+ }
+}
+
static int synthetic_root_group_build(GroupRecord **ret) {
return group_record_build(
ret,
SD_JSON_BUILD_PAIR("disposition", JSON_BUILD_CONST_STRING("foreign"))));
}
-int groupdb_by_name(const char *name, UserDBFlags flags, GroupRecord **ret) {
- _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
- _cleanup_(sd_json_variant_unrefp) sd_json_variant *query = NULL;
+static int query_append_gid_match(sd_json_variant **query, const UserDBMatch *match) {
int r;
- if (FLAGS_SET(flags, USERDB_PARSE_NUMERIC)) {
- gid_t gid;
-
- if (parse_gid(name, &gid) >= 0)
- return groupdb_by_gid(gid, flags, ret);
- }
+ assert(query);
- if (!valid_user_group_name(name, VALID_USER_RELAX))
- return -EINVAL;
+ if (!userdb_match_is_set(match))
+ return 0;
- r = sd_json_buildo(&query, SD_JSON_BUILD_PAIR("groupName", SD_JSON_BUILD_STRING(name)));
+ r = sd_json_variant_merge_objectbo(
+ query,
+ SD_JSON_BUILD_PAIR_CONDITION(!strv_isempty(match->fuzzy_names), "fuzzyNames", SD_JSON_BUILD_STRV(match->fuzzy_names)),
+ SD_JSON_BUILD_PAIR_CONDITION(match->gid_min > 0, "gidMin", SD_JSON_BUILD_UNSIGNED(match->gid_min)),
+ SD_JSON_BUILD_PAIR_CONDITION(match->gid_max < GID_INVALID-1, "gidMax", SD_JSON_BUILD_UNSIGNED(match->gid_max)));
if (r < 0)
return r;
- iterator = userdb_iterator_new(LOOKUP_GROUP, flags);
- if (!iterator)
- return -ENOMEM;
+ return query_append_disposition_mask(query, match->disposition_mask);
+}
- r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetGroupRecord", false, query, flags);
- if (r >= 0) {
- r = userdb_process(iterator, NULL, ret, NULL, NULL);
- if (r >= 0)
- return r;
- }
+static int groupdb_by_name_fallbacks(
+ const char *name,
+ UserDBIterator *iterator,
+ UserDBFlags flags,
+ GroupRecord **ret) {
- if (!FLAGS_SET(flags, USERDB_EXCLUDE_DROPIN) && !(iterator && iterator->dropin_covered)) {
+ int r;
+
+ assert(name);
+ assert(iterator);
+ assert(ret);
+
+ if (!FLAGS_SET(flags, USERDB_EXCLUDE_DROPIN) && !iterator->dropin_covered) {
r = dropin_group_record_by_name(name, NULL, flags, ret);
if (r >= 0)
return r;
}
- if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && !(iterator && iterator->nss_covered)) {
+ if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && !iterator->nss_covered) {
r = userdb_iterator_block_nss_systemd(iterator);
if (r >= 0) {
r = nss_group_record_by_name(name, !FLAGS_SET(flags, USERDB_SUPPRESS_SHADOW), ret);
return r;
if (r > 0)
return synthetic_foreign_group_build(foreign_gid, ret);
- r = -ESRCH;
}
- return r;
+ return -ESRCH;
}
-int groupdb_by_gid(gid_t gid, UserDBFlags flags, GroupRecord **ret) {
+int groupdb_by_name(const char *name, const UserDBMatch *match, UserDBFlags flags, GroupRecord **ret) {
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
_cleanup_(sd_json_variant_unrefp) sd_json_variant *query = NULL;
int r;
- if (!gid_is_valid(gid))
+ assert(name);
+
+ if (FLAGS_SET(flags, USERDB_PARSE_NUMERIC)) {
+ gid_t gid;
+
+ if (parse_gid(name, &gid) >= 0)
+ return groupdb_by_gid(gid, match, flags, ret);
+ }
+
+ if (!valid_user_group_name(name, VALID_USER_RELAX))
return -EINVAL;
- r = sd_json_buildo(&query, SD_JSON_BUILD_PAIR("gid", SD_JSON_BUILD_UNSIGNED(gid)));
+ r = sd_json_buildo(&query, SD_JSON_BUILD_PAIR("groupName", SD_JSON_BUILD_STRING(name)));
+ if (r < 0)
+ return r;
+
+ r = query_append_gid_match(&query, match);
if (r < 0)
return r;
if (!iterator)
return -ENOMEM;
- r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetGroupRecord", false, query, flags);
+ _cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
+ r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetGroupRecord", /* more= */ false, query, flags);
if (r >= 0) {
- r = userdb_process(iterator, NULL, ret, NULL, NULL);
- if (r >= 0)
+ r = userdb_process(iterator, /* ret_user_record= */ NULL, &gr, /* ret_user_name= */ NULL, /* ret_group_name= */ NULL);
+ if (r == -ENOEXEC)
+ return r;
+ }
+ if (r < 0) {
+ r = groupdb_by_name_fallbacks(name, iterator, flags, &gr);
+ if (r < 0)
return r;
}
+ /* As above, we apply our own client-side filtering even if server-side filtering worked, for robustness and simplicity reasons. */
+ r = group_record_match(gr, match);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENOEXEC;
+
+ if (ret)
+ *ret = TAKE_PTR(gr);
+
+ return r;
+}
+
+static int groupdb_by_gid_fallbacks(
+ gid_t gid,
+ UserDBIterator *iterator,
+ UserDBFlags flags,
+ GroupRecord **ret) {
+ int r;
+
+ assert(gid_is_valid(gid));
+ assert(iterator);
+ assert(ret);
+
if (!FLAGS_SET(flags, USERDB_EXCLUDE_DROPIN) && !(iterator && iterator->dropin_covered)) {
r = dropin_group_record_by_gid(gid, NULL, flags, ret);
if (r >= 0)
if (!FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE_FOREIGN) && gid_is_foreign(gid))
return synthetic_foreign_group_build(gid - FOREIGN_UID_BASE, ret);
- return r;
+ return -ESRCH;
}
-int groupdb_all(UserDBFlags flags, UserDBIterator **ret) {
+int groupdb_by_gid(gid_t gid, const UserDBMatch *match, UserDBFlags flags, GroupRecord **ret) {
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *query = NULL;
+ int r;
+
+ if (!gid_is_valid(gid))
+ return -EINVAL;
+
+ r = sd_json_buildo(&query, SD_JSON_BUILD_PAIR("gid", SD_JSON_BUILD_UNSIGNED(gid)));
+ if (r < 0)
+ return r;
+
+ r = query_append_gid_match(&query, match);
+ if (r < 0)
+ return r;
+
+ iterator = userdb_iterator_new(LOOKUP_GROUP, flags);
+ if (!iterator)
+ return -ENOMEM;
+
+ _cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
+ r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetGroupRecord", /* more= */ false, query, flags);
+ if (r >= 0) {
+ r = userdb_process(iterator, /* ret_user_record= */ NULL, &gr, /* ret_user_name= */ NULL, /* ret_group_name= */ NULL);
+ if (r == -ENOEXEC)
+ return r;
+ }
+ if (r < 0) {
+ r = groupdb_by_gid_fallbacks(gid, iterator, flags, &gr);
+ if (r < 0)
+ return r;
+ }
+
+ r = group_record_match(gr, match);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENOEXEC;
+
+ if (ret)
+ *ret = TAKE_PTR(gr);
+
+ return 0;
+}
+
+int groupdb_all(const UserDBMatch *match, UserDBFlags flags, UserDBIterator **ret) {
+ _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *query = NULL;
int r, qr;
assert(ret);
+ r = query_append_gid_match(&query, match);
+ if (r < 0)
+ return r;
+
iterator = userdb_iterator_new(LOOKUP_GROUP, flags);
if (!iterator)
return -ENOMEM;
- qr = userdb_start_query(iterator, "io.systemd.UserDatabase.GetGroupRecord", true, NULL, flags);
+ qr = userdb_start_query(iterator, "io.systemd.UserDatabase.GetGroupRecord", /* more= */ true, query, flags);
if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && (qr < 0 || !iterator->nss_covered)) {
r = userdb_iterator_block_nss_systemd(iterator);
return 0;
}
-int groupdb_iterator_get(UserDBIterator *iterator, GroupRecord **ret) {
+static int groupdb_iterator_get_one(UserDBIterator *iterator, GroupRecord **ret) {
int r;
assert(iterator);
return r;
}
+int groupdb_iterator_get(UserDBIterator *iterator, const UserDBMatch *match, GroupRecord **ret) {
+ int r;
+
+ assert(iterator);
+ assert(iterator->what == LOOKUP_GROUP);
+
+ for (;;) {
+ _cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
+
+ r = groupdb_iterator_get_one(iterator, userdb_match_is_set(match) || ret ? &gr : NULL);
+ if (r < 0)
+ return r;
+
+ if (gr && !group_record_match(gr, match))
+ continue;
+
+ if (ret)
+ *ret = TAKE_PTR(gr);
+
+ return r;
+ }
+}
+
static void discover_membership_dropins(UserDBIterator *i, UserDBFlags flags) {
int r;
(void) table_hide_column_from_display(table, (size_t) 0);
}
- UserDBMatch match = {
+ _cleanup_(userdb_match_done) UserDBMatch match = {
.disposition_mask = arg_disposition_mask,
.uid_min = arg_uid_min,
.uid_max = arg_uid_max,
STRV_FOREACH(i, argv + 1) {
_cleanup_(user_record_unrefp) UserRecord *ur = NULL;
- r = userdb_by_name(*i, arg_userdb_flags|USERDB_PARSE_NUMERIC, &ur);
+ r = userdb_by_name(*i, &match, arg_userdb_flags|USERDB_PARSE_NUMERIC, &ur);
if (r < 0) {
if (r == -ESRCH)
log_error_errno(r, "User %s does not exist.", *i);
else if (r == -EHOSTDOWN)
log_error_errno(r, "Selected user database service is not available for this request.");
+ else if (r == -ENOEXEC)
+ log_error_errno(r, "User '%s' exists but does not match specified filter.", *i);
else
log_error_errno(r, "Failed to find user %s: %m", *i);
RET_GATHER(ret, r);
- } else if (!user_record_match(ur, &match)) {
- log_error("User '%s' does not match filter.", *i);
- RET_GATHER(ret, -ENOEXEC);
} else {
if (draw_separator && arg_output == OUTPUT_FRIENDLY)
putchar('\n');
}
}
else {
- _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
- _cleanup_strv_free_ char **names = NULL;
-
if (argc > 1) {
- names = strv_copy(argv + 1);
- if (!names)
+ /* If there are further arguments, they are the fuzzy match strings. */
+ match.fuzzy_names = strv_copy(strv_skip(argv, 1));
+ if (!match.fuzzy_names)
return log_oom();
-
- match.fuzzy_names = names;
}
- r = userdb_all(arg_userdb_flags, &iterator);
+ _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+ r = userdb_all(&match, arg_userdb_flags, &iterator);
if (r == -ENOLINK) /* ENOLINK → Didn't find answer without Varlink, and didn't try Varlink because was configured to off. */
log_debug_errno(r, "No entries found. (Didn't check via Varlink.)");
else if (r == -ESRCH) /* ESRCH → Couldn't find any suitable entry, but we checked all sources */
for (;;) {
_cleanup_(user_record_unrefp) UserRecord *ur = NULL;
- r = userdb_iterator_get(iterator, &ur);
+ r = userdb_iterator_get(iterator, &match, &ur);
if (r == -ESRCH)
break;
if (r == -EHOSTDOWN)
if (r < 0)
return log_error_errno(r, "Failed acquire next user: %m");
- if (!user_record_match(ur, &match))
- continue;
-
if (draw_separator && arg_output == OUTPUT_FRIENDLY)
putchar('\n');
(void) table_hide_column_from_display(table, (size_t) 0);
}
- UserDBMatch match = {
+ _cleanup_(userdb_match_done) UserDBMatch match = {
.disposition_mask = arg_disposition_mask,
.gid_min = arg_uid_min,
.gid_max = arg_uid_max,
STRV_FOREACH(i, argv + 1) {
_cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
- r = groupdb_by_name(*i, arg_userdb_flags|USERDB_PARSE_NUMERIC, &gr);
+ r = groupdb_by_name(*i, &match, arg_userdb_flags|USERDB_PARSE_NUMERIC, &gr);
if (r < 0) {
if (r == -ESRCH)
log_error_errno(r, "Group %s does not exist.", *i);
else if (r == -EHOSTDOWN)
log_error_errno(r, "Selected group database service is not available for this request.");
+ else if (r == -ENOEXEC)
+ log_error_errno(r, "Group '%s' exists but does not match specified filter.", *i);
else
log_error_errno(r, "Failed to find group %s: %m", *i);
RET_GATHER(ret, r);
- } else if (!group_record_match(gr, &match)) {
- log_error("Group '%s' does not match filter.", *i);
- RET_GATHER(ret, -ENOEXEC);
} else {
if (draw_separator && arg_output == OUTPUT_FRIENDLY)
putchar('\n');
}
}
else {
- _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
- _cleanup_strv_free_ char **names = NULL;
-
if (argc > 1) {
- names = strv_copy(argv + 1);
- if (!names)
+ match.fuzzy_names = strv_copy(strv_skip(argv, 1));
+ if (!match.fuzzy_names)
return log_oom();
-
- match.fuzzy_names = names;
}
- r = groupdb_all(arg_userdb_flags, &iterator);
+ _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+ r = groupdb_all(&match, arg_userdb_flags, &iterator);
if (r == -ENOLINK)
log_debug_errno(r, "No entries found. (Didn't check via Varlink.)");
else if (r == -ESRCH)
for (;;) {
_cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
- r = groupdb_iterator_get(iterator, &gr);
+ r = groupdb_iterator_get(iterator, &match, &gr);
if (r == -ESRCH)
break;
if (r == -EHOSTDOWN)
if (r < 0)
return log_error_errno(r, "Failed acquire next group: %m");
- if (!group_record_match(gr, &match))
- continue;
-
if (draw_separator && arg_output == OUTPUT_FRIENDLY)
putchar('\n');
chain_invocation = NULL;
}
- r = userdb_by_name(argv[1], arg_userdb_flags, &ur);
+ r = userdb_by_name(argv[1], /* match= */ NULL, arg_userdb_flags, &ur);
if (r == -ESRCH)
log_error_errno(r, "User %s does not exist.", argv[1]);
else if (r == -EHOSTDOWN)