userdbctl: add some basic client-side filtering
This adds some basic client-side user/group filtering to "userdbctl": 1. by uid/gid min/max 2. by user "disposition" (i.e. show only regular users with "userdbctl user -R") 3. by fuzzy name (i.e. search by substring/levenshtein of user name, real name, and other identifiers of the user/group record). In the long run we also want to support this server side, but let's start out with doing this client-side, since many backends won't support server-side filtering anytime soon anyway, so we need it in either case.
This commit is contained in:
@@ -174,6 +174,57 @@
|
|||||||
<xi:include href="version-info.xml" xpointer="v250"/></listitem>
|
<xi:include href="version-info.xml" xpointer="v250"/></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--fuzzy</option></term>
|
||||||
|
<term><option>-z</option></term>
|
||||||
|
|
||||||
|
<listitem><para>When used with the <command>user</command> or <command>group</command> command, do a
|
||||||
|
fuzzy string search. Any specified arguments will be matched against the user name, the real name of
|
||||||
|
the user record, the email address, and other descriptive strings of the user or group
|
||||||
|
record. Moreover, instead of precise matching, a substring match or a match allowing slight
|
||||||
|
deviations in spelling is applied.</para>
|
||||||
|
|
||||||
|
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--disposition=</option></term>
|
||||||
|
|
||||||
|
<listitem><para>When used with the <command>user</command> or <command>group</command> command,
|
||||||
|
filters by disposition of the record. Takes one of <literal>intrinsic</literal>,
|
||||||
|
<literal>system</literal>, <literal>regular</literal>, <literal>dynamic</literal>,
|
||||||
|
<literal>container</literal>. May be used multiple times, in which case only users matching any of
|
||||||
|
the specified dispositions are shown.</para>
|
||||||
|
|
||||||
|
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>-I</option></term>
|
||||||
|
<term><option>-S</option></term>
|
||||||
|
<term><option>-R</option></term>
|
||||||
|
|
||||||
|
<listitem><para>Shortcuts for <option>--disposition=intrinsic</option>,
|
||||||
|
<option>--disposition=system</option>, <option>--disposition=regular</option>,
|
||||||
|
respectively.</para>
|
||||||
|
|
||||||
|
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--uid-min=</option></term>
|
||||||
|
<term><option>--uid-max=</option></term>
|
||||||
|
|
||||||
|
<listitem><para>When used with the <command>user</command> or <command>group</command> command,
|
||||||
|
filters the output by UID/GID ranges. Takes numeric minimum resp. maximum UID/GID values. Shows only
|
||||||
|
records within the specified range. When applied to the <command>user</command> command matches
|
||||||
|
against UIDs, when applied to the <command>group</command> command against GIDs (despite the name of
|
||||||
|
the switch). If unspecified defaults to 0 (for the minimum) and 4294967294 (for the maximum), i.e. by
|
||||||
|
default no filtering is applied as the whole UID/GID range is covered.</para>
|
||||||
|
|
||||||
|
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
<xi:include href="standard-options.xml" xpointer="no-pager" />
|
<xi:include href="standard-options.xml" xpointer="no-pager" />
|
||||||
<xi:include href="standard-options.xml" xpointer="no-legend" />
|
<xi:include href="standard-options.xml" xpointer="no-legend" />
|
||||||
<xi:include href="standard-options.xml" xpointer="help" />
|
<xi:include href="standard-options.xml" xpointer="help" />
|
||||||
|
|||||||
@@ -326,3 +326,28 @@ int group_record_clone(GroupRecord *h, UserRecordLoadFlags flags, GroupRecord **
|
|||||||
*ret = TAKE_PTR(c);
|
*ret = TAKE_PTR(c);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int group_record_match(GroupRecord *h, const UserDBMatch *match) {
|
||||||
|
assert(h);
|
||||||
|
assert(match);
|
||||||
|
|
||||||
|
if (h->gid < match->gid_min || h->gid > match->gid_max)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!FLAGS_SET(match->disposition_mask, UINT64_C(1) << group_record_disposition(h)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!strv_isempty(match->fuzzy_names)) {
|
||||||
|
const char* names[] = {
|
||||||
|
h->group_name,
|
||||||
|
group_record_group_name_and_realm(h),
|
||||||
|
h->description,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!user_name_fuzzy_match(names, ELEMENTSOF(names), match->fuzzy_names))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -43,5 +43,7 @@ int group_record_load(GroupRecord *h, sd_json_variant *v, UserRecordLoadFlags fl
|
|||||||
int group_record_build(GroupRecord **ret, ...);
|
int group_record_build(GroupRecord **ret, ...);
|
||||||
int group_record_clone(GroupRecord *g, UserRecordLoadFlags flags, GroupRecord **ret);
|
int group_record_clone(GroupRecord *g, UserRecordLoadFlags flags, GroupRecord **ret);
|
||||||
|
|
||||||
|
int group_record_match(GroupRecord *h, const UserDBMatch *match);
|
||||||
|
|
||||||
const char* group_record_group_name_and_realm(GroupRecord *h);
|
const char* group_record_group_name_and_realm(GroupRecord *h);
|
||||||
UserDisposition group_record_disposition(GroupRecord *h);
|
UserDisposition group_record_disposition(GroupRecord *h);
|
||||||
|
|||||||
@@ -2401,6 +2401,72 @@ int suitable_blob_filename(const char *name) {
|
|||||||
name[0] != '.';
|
name[0] != '.';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool user_name_fuzzy_match(const char *names[], size_t n_names, char **matches) {
|
||||||
|
assert(names || n_names == 0);
|
||||||
|
|
||||||
|
/* Checks if any of the user record strings in the names[] array matches any of the search strings in
|
||||||
|
* the matches** strv fuzzily. */
|
||||||
|
|
||||||
|
FOREACH_ARRAY(n, names, n_names) {
|
||||||
|
if (!*n)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
_cleanup_free_ char *lcn = strdup(*n);
|
||||||
|
if (!lcn)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ascii_strlower(lcn);
|
||||||
|
|
||||||
|
STRV_FOREACH(i, matches) {
|
||||||
|
_cleanup_free_ char *lc = strdup(*i);
|
||||||
|
if (!lc)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ascii_strlower(lc);
|
||||||
|
|
||||||
|
/* First do substring check */
|
||||||
|
if (strstr(lcn, lc))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
/* Then do some fuzzy string comparison (but only if the needle is non-trivially long) */
|
||||||
|
if (strlen(lc) >= 5 && strlevenshtein(lcn, lc) < 3)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int user_record_match(UserRecord *u, const UserDBMatch *match) {
|
||||||
|
assert(u);
|
||||||
|
assert(match);
|
||||||
|
|
||||||
|
if (u->uid < match->uid_min || u->uid > match->uid_max)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!FLAGS_SET(match->disposition_mask, UINT64_C(1) << user_record_disposition(u)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!strv_isempty(match->fuzzy_names)) {
|
||||||
|
|
||||||
|
/* Note this array of names is sparse, i.e. various entries listed in it will be
|
||||||
|
* NULL. Because of that we are not using a NULL terminated strv here, but a regular
|
||||||
|
* array. */
|
||||||
|
const char* names[] = {
|
||||||
|
u->user_name,
|
||||||
|
user_record_user_name_and_realm(u),
|
||||||
|
u->real_name,
|
||||||
|
u->email_address,
|
||||||
|
u->cifs_user_name,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!user_name_fuzzy_match(names, ELEMENTSOF(names), match->fuzzy_names))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static const char* const user_storage_table[_USER_STORAGE_MAX] = {
|
static const char* const user_storage_table[_USER_STORAGE_MAX] = {
|
||||||
[USER_CLASSIC] = "classic",
|
[USER_CLASSIC] = "classic",
|
||||||
[USER_LUKS] = "luks",
|
[USER_LUKS] = "luks",
|
||||||
|
|||||||
@@ -462,6 +462,24 @@ int user_group_record_mangle(sd_json_variant *v, UserRecordLoadFlags load_flags,
|
|||||||
#define BLOB_DIR_MAX_SIZE (UINT64_C(64) * U64_MB)
|
#define BLOB_DIR_MAX_SIZE (UINT64_C(64) * U64_MB)
|
||||||
int suitable_blob_filename(const char *name);
|
int suitable_blob_filename(const char *name);
|
||||||
|
|
||||||
|
typedef struct UserDBMatch {
|
||||||
|
char **fuzzy_names;
|
||||||
|
uint64_t disposition_mask;
|
||||||
|
union {
|
||||||
|
uid_t uid_min;
|
||||||
|
gid_t gid_min;
|
||||||
|
};
|
||||||
|
union {
|
||||||
|
uid_t uid_max;
|
||||||
|
gid_t gid_max;
|
||||||
|
};
|
||||||
|
} UserDBMatch;
|
||||||
|
|
||||||
|
#define USER_DISPOSITION_MASK_MAX ((UINT64_C(1) << _USER_DISPOSITION_MAX) - UINT64_C(1))
|
||||||
|
|
||||||
|
bool user_name_fuzzy_match(const char *names[], size_t n_names, char **matches);
|
||||||
|
int user_record_match(UserRecord *u, const UserDBMatch *match);
|
||||||
|
|
||||||
const char* user_storage_to_string(UserStorage t) _const_;
|
const char* user_storage_to_string(UserStorage t) _const_;
|
||||||
UserStorage user_storage_from_string(const char *s) _pure_;
|
UserStorage user_storage_from_string(const char *s) _pure_;
|
||||||
|
|
||||||
|
|||||||
+126
-9
@@ -37,6 +37,10 @@ static char** arg_services = NULL;
|
|||||||
static UserDBFlags arg_userdb_flags = 0;
|
static UserDBFlags arg_userdb_flags = 0;
|
||||||
static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF;
|
static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF;
|
||||||
static bool arg_chain = false;
|
static bool arg_chain = false;
|
||||||
|
static uint64_t arg_disposition_mask = UINT64_MAX;
|
||||||
|
static uid_t arg_uid_min = 0;
|
||||||
|
static uid_t arg_uid_max = UID_INVALID-1;
|
||||||
|
static bool arg_fuzzy = false;
|
||||||
|
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_services, strv_freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_services, strv_freep);
|
||||||
|
|
||||||
@@ -176,6 +180,9 @@ static int table_add_uid_boundaries(Table *table, const UIDRange *p) {
|
|||||||
FOREACH_ELEMENT(i, uid_range_table) {
|
FOREACH_ELEMENT(i, uid_range_table) {
|
||||||
_cleanup_free_ char *name = NULL, *comment = NULL;
|
_cleanup_free_ char *name = NULL, *comment = NULL;
|
||||||
|
|
||||||
|
if (!FLAGS_SET(arg_disposition_mask, UINT64_C(1) << i->disposition))
|
||||||
|
continue;
|
||||||
|
|
||||||
if (!uid_range_covers(p, i->first, i->last - i->first + 1))
|
if (!uid_range_covers(p, i->first, i->last - i->first + 1))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -346,7 +353,7 @@ static int display_user(int argc, char *argv[], void *userdata) {
|
|||||||
int ret = 0, r;
|
int ret = 0, r;
|
||||||
|
|
||||||
if (arg_output < 0)
|
if (arg_output < 0)
|
||||||
arg_output = argc > 1 ? OUTPUT_FRIENDLY : OUTPUT_TABLE;
|
arg_output = argc > 1 && !arg_fuzzy ? OUTPUT_FRIENDLY : OUTPUT_TABLE;
|
||||||
|
|
||||||
if (arg_output == OUTPUT_TABLE) {
|
if (arg_output == OUTPUT_TABLE) {
|
||||||
table = table_new(" ", "name", "disposition", "uid", "gid", "realname", "home", "shell", "order");
|
table = table_new(" ", "name", "disposition", "uid", "gid", "realname", "home", "shell", "order");
|
||||||
@@ -360,7 +367,13 @@ static int display_user(int argc, char *argv[], void *userdata) {
|
|||||||
(void) table_set_display(table, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4, (size_t) 5, (size_t) 6, (size_t) 7);
|
(void) table_set_display(table, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4, (size_t) 5, (size_t) 6, (size_t) 7);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argc > 1)
|
UserDBMatch match = {
|
||||||
|
.disposition_mask = arg_disposition_mask,
|
||||||
|
.uid_min = arg_uid_min,
|
||||||
|
.uid_max = arg_uid_max,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (argc > 1 && !arg_fuzzy)
|
||||||
STRV_FOREACH(i, argv + 1) {
|
STRV_FOREACH(i, argv + 1) {
|
||||||
_cleanup_(user_record_unrefp) UserRecord *ur = NULL;
|
_cleanup_(user_record_unrefp) UserRecord *ur = NULL;
|
||||||
uid_t uid;
|
uid_t uid;
|
||||||
@@ -377,8 +390,10 @@ static int display_user(int argc, char *argv[], void *userdata) {
|
|||||||
else
|
else
|
||||||
log_error_errno(r, "Failed to find user %s: %m", *i);
|
log_error_errno(r, "Failed to find user %s: %m", *i);
|
||||||
|
|
||||||
if (ret >= 0)
|
RET_GATHER(ret, r);
|
||||||
ret = r;
|
} else if (!user_record_match(ur, &match)) {
|
||||||
|
log_error("User '%s' does not match filter.", *i);
|
||||||
|
RET_GATHER(ret, -ENOEXEC);
|
||||||
} else {
|
} else {
|
||||||
if (draw_separator && arg_output == OUTPUT_FRIENDLY)
|
if (draw_separator && arg_output == OUTPUT_FRIENDLY)
|
||||||
putchar('\n');
|
putchar('\n');
|
||||||
@@ -392,6 +407,15 @@ static int display_user(int argc, char *argv[], void *userdata) {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
|
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
|
||||||
|
_cleanup_strv_free_ char **names = NULL;
|
||||||
|
|
||||||
|
if (argc > 1) {
|
||||||
|
names = strv_copy(argv + 1);
|
||||||
|
if (!names)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
match.fuzzy_names = names;
|
||||||
|
}
|
||||||
|
|
||||||
r = userdb_all(arg_userdb_flags, &iterator);
|
r = userdb_all(arg_userdb_flags, &iterator);
|
||||||
if (r == -ENOLINK) /* ENOLINK → Didn't find answer without Varlink, and didn't try Varlink because was configured to off. */
|
if (r == -ENOLINK) /* ENOLINK → Didn't find answer without Varlink, and didn't try Varlink because was configured to off. */
|
||||||
@@ -412,6 +436,9 @@ static int display_user(int argc, char *argv[], void *userdata) {
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed acquire next user: %m");
|
return log_error_errno(r, "Failed acquire next user: %m");
|
||||||
|
|
||||||
|
if (!user_record_match(ur, &match))
|
||||||
|
continue;
|
||||||
|
|
||||||
if (draw_separator && arg_output == OUTPUT_FRIENDLY)
|
if (draw_separator && arg_output == OUTPUT_FRIENDLY)
|
||||||
putchar('\n');
|
putchar('\n');
|
||||||
|
|
||||||
@@ -650,7 +677,7 @@ static int display_group(int argc, char *argv[], void *userdata) {
|
|||||||
int ret = 0, r;
|
int ret = 0, r;
|
||||||
|
|
||||||
if (arg_output < 0)
|
if (arg_output < 0)
|
||||||
arg_output = argc > 1 ? OUTPUT_FRIENDLY : OUTPUT_TABLE;
|
arg_output = argc > 1 && !arg_fuzzy ? OUTPUT_FRIENDLY : OUTPUT_TABLE;
|
||||||
|
|
||||||
if (arg_output == OUTPUT_TABLE) {
|
if (arg_output == OUTPUT_TABLE) {
|
||||||
table = table_new(" ", "name", "disposition", "gid", "description", "order");
|
table = table_new(" ", "name", "disposition", "gid", "description", "order");
|
||||||
@@ -663,7 +690,13 @@ static int display_group(int argc, char *argv[], void *userdata) {
|
|||||||
(void) table_set_display(table, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4);
|
(void) table_set_display(table, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argc > 1)
|
UserDBMatch match = {
|
||||||
|
.disposition_mask = arg_disposition_mask,
|
||||||
|
.gid_min = arg_uid_min,
|
||||||
|
.gid_max = arg_uid_max,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (argc > 1 && !arg_fuzzy)
|
||||||
STRV_FOREACH(i, argv + 1) {
|
STRV_FOREACH(i, argv + 1) {
|
||||||
_cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
|
_cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
|
||||||
gid_t gid;
|
gid_t gid;
|
||||||
@@ -680,8 +713,10 @@ static int display_group(int argc, char *argv[], void *userdata) {
|
|||||||
else
|
else
|
||||||
log_error_errno(r, "Failed to find group %s: %m", *i);
|
log_error_errno(r, "Failed to find group %s: %m", *i);
|
||||||
|
|
||||||
if (ret >= 0)
|
RET_GATHER(ret, r);
|
||||||
ret = r;
|
} else if (!group_record_match(gr, &match)) {
|
||||||
|
log_error("Group '%s' does not match filter.", *i);
|
||||||
|
RET_GATHER(ret, -ENOEXEC);
|
||||||
} else {
|
} else {
|
||||||
if (draw_separator && arg_output == OUTPUT_FRIENDLY)
|
if (draw_separator && arg_output == OUTPUT_FRIENDLY)
|
||||||
putchar('\n');
|
putchar('\n');
|
||||||
@@ -695,6 +730,15 @@ static int display_group(int argc, char *argv[], void *userdata) {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
|
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
|
||||||
|
_cleanup_strv_free_ char **names = NULL;
|
||||||
|
|
||||||
|
if (argc > 1) {
|
||||||
|
names = strv_copy(argv + 1);
|
||||||
|
if (!names)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
match.fuzzy_names = names;
|
||||||
|
}
|
||||||
|
|
||||||
r = groupdb_all(arg_userdb_flags, &iterator);
|
r = groupdb_all(arg_userdb_flags, &iterator);
|
||||||
if (r == -ENOLINK)
|
if (r == -ENOLINK)
|
||||||
@@ -715,6 +759,9 @@ static int display_group(int argc, char *argv[], void *userdata) {
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed acquire next group: %m");
|
return log_error_errno(r, "Failed acquire next group: %m");
|
||||||
|
|
||||||
|
if (!group_record_match(gr, &match))
|
||||||
|
continue;
|
||||||
|
|
||||||
if (draw_separator && arg_output == OUTPUT_FRIENDLY)
|
if (draw_separator && arg_output == OUTPUT_FRIENDLY)
|
||||||
putchar('\n');
|
putchar('\n');
|
||||||
|
|
||||||
@@ -1090,6 +1137,13 @@ static int help(int argc, char *argv[], void *userdata) {
|
|||||||
" --multiplexer=BOOL Control whether to use the multiplexer\n"
|
" --multiplexer=BOOL Control whether to use the multiplexer\n"
|
||||||
" --json=pretty|short JSON output mode\n"
|
" --json=pretty|short JSON output mode\n"
|
||||||
" --chain Chain another command\n"
|
" --chain Chain another command\n"
|
||||||
|
" --uid-min=ID Filter by minimum UID/GID (default 0)\n"
|
||||||
|
" --uid-max=ID Filter by maximum UID/GID (default 4294967294)\n"
|
||||||
|
" -z --fuzzy Do a fuzzy name search\n"
|
||||||
|
" --disposition=VALUE Filter by disposition\n"
|
||||||
|
" -I Equivalent to --disposition=intrinsic\n"
|
||||||
|
" -S Equivalent to --disposition=system\n"
|
||||||
|
" -R Equivalent to --disposition=regular\n"
|
||||||
"\nSee the %s for details.\n",
|
"\nSee the %s for details.\n",
|
||||||
program_invocation_short_name,
|
program_invocation_short_name,
|
||||||
ansi_highlight(),
|
ansi_highlight(),
|
||||||
@@ -1113,6 +1167,9 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
ARG_MULTIPLEXER,
|
ARG_MULTIPLEXER,
|
||||||
ARG_JSON,
|
ARG_JSON,
|
||||||
ARG_CHAIN,
|
ARG_CHAIN,
|
||||||
|
ARG_UID_MIN,
|
||||||
|
ARG_UID_MAX,
|
||||||
|
ARG_DISPOSITION,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct option options[] = {
|
static const struct option options[] = {
|
||||||
@@ -1129,6 +1186,10 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
{ "multiplexer", required_argument, NULL, ARG_MULTIPLEXER },
|
{ "multiplexer", required_argument, NULL, ARG_MULTIPLEXER },
|
||||||
{ "json", required_argument, NULL, ARG_JSON },
|
{ "json", required_argument, NULL, ARG_JSON },
|
||||||
{ "chain", no_argument, NULL, ARG_CHAIN },
|
{ "chain", no_argument, NULL, ARG_CHAIN },
|
||||||
|
{ "uid-min", required_argument, NULL, ARG_UID_MIN },
|
||||||
|
{ "uid-max", required_argument, NULL, ARG_UID_MAX },
|
||||||
|
{ "fuzzy", required_argument, NULL, 'z' },
|
||||||
|
{ "disposition", required_argument, NULL, ARG_DISPOSITION },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1159,7 +1220,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
int c;
|
int c;
|
||||||
|
|
||||||
c = getopt_long(argc, argv,
|
c = getopt_long(argc, argv,
|
||||||
arg_chain ? "+hjs:N" : "hjs:N", /* When --chain was used disable parsing of further switches */
|
arg_chain ? "+hjs:NISRz" : "hjs:NISRz", /* When --chain was used disable parsing of further switches */
|
||||||
options, NULL);
|
options, NULL);
|
||||||
if (c < 0)
|
if (c < 0)
|
||||||
break;
|
break;
|
||||||
@@ -1275,6 +1336,55 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
arg_chain = true;
|
arg_chain = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ARG_DISPOSITION: {
|
||||||
|
UserDisposition d = user_disposition_from_string(optarg);
|
||||||
|
if (d < 0)
|
||||||
|
return log_error_errno(d, "Unknown user disposition: %s", optarg);
|
||||||
|
|
||||||
|
if (arg_disposition_mask == UINT64_MAX)
|
||||||
|
arg_disposition_mask = 0;
|
||||||
|
|
||||||
|
arg_disposition_mask |= UINT64_C(1) << d;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'I':
|
||||||
|
if (arg_disposition_mask == UINT64_MAX)
|
||||||
|
arg_disposition_mask = 0;
|
||||||
|
|
||||||
|
arg_disposition_mask |= UINT64_C(1) << USER_INTRINSIC;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'S':
|
||||||
|
if (arg_disposition_mask == UINT64_MAX)
|
||||||
|
arg_disposition_mask = 0;
|
||||||
|
|
||||||
|
arg_disposition_mask |= UINT64_C(1) << USER_SYSTEM;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'R':
|
||||||
|
if (arg_disposition_mask == UINT64_MAX)
|
||||||
|
arg_disposition_mask = 0;
|
||||||
|
|
||||||
|
arg_disposition_mask |= UINT64_C(1) << USER_REGULAR;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ARG_UID_MIN:
|
||||||
|
r = parse_uid(optarg, &arg_uid_min);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to parse --uid-min= value: %s", optarg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ARG_UID_MAX:
|
||||||
|
r = parse_uid(optarg, &arg_uid_max);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to parse --uid-max= value: %s", optarg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'z':
|
||||||
|
arg_fuzzy = true;
|
||||||
|
break;
|
||||||
|
|
||||||
case '?':
|
case '?':
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
@@ -1283,6 +1393,13 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (arg_uid_min > arg_uid_max)
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Minimum UID/GID " UID_FMT " is above maximum UID/GID " UID_FMT ", refusing.", arg_uid_min, arg_uid_max);
|
||||||
|
|
||||||
|
/* If not mask was specified, use the all bits on mask */
|
||||||
|
if (arg_disposition_mask == UINT64_MAX)
|
||||||
|
arg_disposition_mask = USER_DISPOSITION_MASK_MAX;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user