Convert udev helper programs to option macros (#41820)
This commit is contained in:
+57
-32
@@ -311,6 +311,59 @@ size_t option_parser_get_n_args(const OptionParser *state) {
|
||||
return state->argc - state->positional_offset;
|
||||
}
|
||||
|
||||
char* option_get_synopsis(const char *prefix, const Option *opt, const char *joiner, bool show_metavar) {
|
||||
assert(opt);
|
||||
assert(!FLAGS_SET(opt->flags, OPTION_GROUP_MARKER)); /* A group marker should not be displayed */
|
||||
|
||||
if (!prefix)
|
||||
prefix = "";
|
||||
|
||||
if (opt->flags & (OPTION_HELP_ENTRY_VERBATIM | OPTION_POSITIONAL_ENTRY))
|
||||
return strjoin(prefix, ASSERT_PTR(opt->long_code));
|
||||
|
||||
/* The option formatted appropriately for --help strings, error messages, and similar:
|
||||
* <prefix>-<short><joiner>--<long>=[<metavar>]
|
||||
* "=" is shown only when a long form is defined: -l --long=ARG, --long=ARG, -s ARG.
|
||||
* The joiner arg is used between the short and long forms.
|
||||
* As a special case, if the option has no long form and show_metavar is true,
|
||||
* a space is used ('-a ARG' or '-a [ARG]').
|
||||
*/
|
||||
assert(opt->short_code != 0 || opt->long_code);
|
||||
|
||||
char sc[3] = "";
|
||||
if (opt->short_code != 0)
|
||||
xsprintf(sc, "-%c", opt->short_code);
|
||||
|
||||
if (show_metavar && opt->metavar && !opt->long_code)
|
||||
joiner = " "; /* Return '-x ARG', no matter what joiner was specified. */
|
||||
else if (opt->short_code == 0 || !opt->long_code)
|
||||
joiner = "";
|
||||
else if (!joiner)
|
||||
joiner = " ";
|
||||
|
||||
bool need_eq = option_takes_arg(opt) && opt->long_code;
|
||||
if (!show_metavar)
|
||||
return strjoin(prefix,
|
||||
sc,
|
||||
joiner,
|
||||
opt->long_code ? "--" : "",
|
||||
strempty(opt->long_code),
|
||||
need_eq ? "=" : "");
|
||||
|
||||
bool need_quote = opt->metavar && strchr(opt->metavar, ' ');
|
||||
return strjoin(prefix,
|
||||
sc,
|
||||
joiner,
|
||||
opt->long_code ? "--" : "",
|
||||
strempty(opt->long_code),
|
||||
option_arg_optional(opt) ? "[" : "",
|
||||
need_eq ? "=" : "",
|
||||
need_quote ? "'" : "",
|
||||
strempty(opt->metavar),
|
||||
need_quote ? "'" : "",
|
||||
option_arg_optional(opt) ? "]" : "");
|
||||
}
|
||||
|
||||
int _option_parser_get_help_table(
|
||||
const Option options[],
|
||||
const Option options_end[],
|
||||
@@ -341,38 +394,10 @@ int _option_parser_get_help_table(
|
||||
/* No help string — we do not show the option */
|
||||
continue;
|
||||
|
||||
_cleanup_free_ char *s = NULL;
|
||||
|
||||
if (FLAGS_SET(opt->flags, OPTION_HELP_ENTRY_VERBATIM)) {
|
||||
assert(opt->long_code);
|
||||
|
||||
s = strjoin(" ",
|
||||
opt->long_code);
|
||||
} else {
|
||||
char sc[3] = " ";
|
||||
if (opt->short_code != 0)
|
||||
xsprintf(sc, "-%c", opt->short_code);
|
||||
|
||||
/* We indent the option string by two spaces. We could set the minimum cell width and
|
||||
* right-align for a similar result, but that'd be more work. This is only used for
|
||||
* display.
|
||||
*
|
||||
* "=" is shown only when a long option is defined: -l --long=ARG, --long=ARG, -s ARG.
|
||||
*/
|
||||
bool need_eq = option_takes_arg(opt) && opt->long_code;
|
||||
bool need_quote = opt->metavar && strchr(opt->metavar, ' ');
|
||||
s = strjoin(" ",
|
||||
sc,
|
||||
" ",
|
||||
opt->long_code ? "--" : "",
|
||||
strempty(opt->long_code),
|
||||
option_arg_optional(opt) ? "[" : "",
|
||||
need_eq ? "=" : "",
|
||||
need_quote ? "'" : "",
|
||||
strempty(opt->metavar),
|
||||
need_quote ? "'" : "",
|
||||
option_arg_optional(opt) ? "]" : "");
|
||||
}
|
||||
/* We indent the option string by two spaces. We could set the minimum cell width and
|
||||
* right-align for a similar result, but that'd be more work. This is only used for
|
||||
* display. */
|
||||
_cleanup_free_ char *s = option_get_synopsis(" ", opt, " ", /* show_metavar= */ true);
|
||||
if (!s)
|
||||
return log_oom();
|
||||
|
||||
|
||||
@@ -109,6 +109,12 @@ typedef struct Option {
|
||||
"Allows the certificate to be loaded from an OpenSSL provider " \
|
||||
"(file, provider:PROVIDER)")
|
||||
|
||||
/* A form used in udev code for compatibility. -V is accepted but not documented. */
|
||||
#define OPTION_COMMON_VERSION_WITH_HIDDEN_V \
|
||||
OPTION_COMMON_VERSION: {} \
|
||||
OPTION_SHORT('V', NULL, /* help= */ NULL)
|
||||
|
||||
|
||||
/* This is magically mapped to the beginning and end of the section */
|
||||
extern const Option __start_SYSTEMD_OPTIONS[];
|
||||
extern const Option __stop_SYSTEMD_OPTIONS[];
|
||||
@@ -166,6 +172,7 @@ char* option_parser_consume_next_arg(OptionParser *state);
|
||||
|
||||
char** option_parser_get_args(const OptionParser *state);
|
||||
size_t option_parser_get_n_args(const OptionParser *state);
|
||||
char* option_get_synopsis(const char *prefix, const Option *opt, const char *joiner, bool show_metavar);
|
||||
|
||||
int _option_parser_get_help_table(
|
||||
const Option options[],
|
||||
|
||||
@@ -1302,4 +1302,53 @@ TEST(option_optional_arg_consume) {
|
||||
}
|
||||
}
|
||||
|
||||
static void test_option_get_synopsis_one(
|
||||
const Option *opt,
|
||||
const char *joiner,
|
||||
bool show_metavar,
|
||||
const char *expected) {
|
||||
log_debug("%s", expected);
|
||||
_cleanup_free_ char *s = option_get_synopsis(". ", opt, joiner, show_metavar);
|
||||
ASSERT_STREQ(s, expected);
|
||||
}
|
||||
|
||||
TEST(option_get_synopsis) {
|
||||
test_option_get_synopsis_one(&(const Option) { 0, 0, 'x', "xxx", "X" }, "/", true, ". -x/--xxx=X");
|
||||
test_option_get_synopsis_one(&(const Option) { 0, 0, 'x', "xxx", "X" }, NULL, true, ". -x --xxx=X");
|
||||
test_option_get_synopsis_one(&(const Option) { 0, 0, 'x', "xxx", "X" }, "/", false, ". -x/--xxx=" );
|
||||
test_option_get_synopsis_one(&(const Option) { 0, 0, 'x', "xxx", "X" }, " ", true, ". -x --xxx=X");
|
||||
test_option_get_synopsis_one(&(const Option) { 0, 0, 'x', "xxx", "X" }, " ", false, ". -x --xxx=" );
|
||||
test_option_get_synopsis_one(&(const Option) { 0, 0, 0, "xxx", "X" }, "+", true, ". --xxx=X" );
|
||||
test_option_get_synopsis_one(&(const Option) { 0, 0, 0, "xxx", "X" }, "+", false, ". --xxx=" );
|
||||
test_option_get_synopsis_one(&(const Option) { 0, 0, 'x', NULL, "X" }, " ", true, ". -x X" );
|
||||
test_option_get_synopsis_one(&(const Option) { 0, 0, 'x', NULL, "X" }, "/", false, ". -x" );
|
||||
|
||||
test_option_get_synopsis_one(&(const Option) { 0, 0, 'x', "xxx", "A B" }, "/", true, ". -x/--xxx='A B'");
|
||||
test_option_get_synopsis_one(&(const Option) { 0, 0, 'x', "xxx", "A B" }, " ", true, ". -x --xxx='A B'");
|
||||
test_option_get_synopsis_one(&(const Option) { 0, 0, 0, "xxx", "A B" }, "+", true, ". --xxx='A B'" );
|
||||
test_option_get_synopsis_one(&(const Option) { 0, 0, 'x', NULL, "A B" }, " ", true, ". -x 'A B'" );
|
||||
|
||||
test_option_get_synopsis_one(&(const Option) { 0, OPTION_OPTIONAL_ARG, 'x', "xxx", "X" }, "/", true, ". -x/--xxx[=X]");
|
||||
test_option_get_synopsis_one(&(const Option) { 0, OPTION_OPTIONAL_ARG, 'x', "xxx", "X" }, NULL, true, ". -x --xxx[=X]");
|
||||
/* Note: --xxx[=] would be silly, so we show --xxx=. It's a corner case. Maybe this should change. */
|
||||
test_option_get_synopsis_one(&(const Option) { 0, OPTION_OPTIONAL_ARG, 'x', "xxx", "X" }, "/", false, ". -x/--xxx=" );
|
||||
test_option_get_synopsis_one(&(const Option) { 0, OPTION_OPTIONAL_ARG, 'x', "xxx", "X" }, " ", true, ". -x --xxx[=X]");
|
||||
test_option_get_synopsis_one(&(const Option) { 0, OPTION_OPTIONAL_ARG, 'x', "xxx", "X" }, " ", false, ". -x --xxx=" );
|
||||
test_option_get_synopsis_one(&(const Option) { 0, OPTION_OPTIONAL_ARG, 0, "xxx", "X" }, "+", true, ". --xxx[=X]" );
|
||||
test_option_get_synopsis_one(&(const Option) { 0, OPTION_OPTIONAL_ARG, 0, "xxx", "X" }, "+", false, ". --xxx=" );
|
||||
test_option_get_synopsis_one(&(const Option) { 0, OPTION_OPTIONAL_ARG, 'x', NULL, "X" }, " ", true, ". -x [X]" );
|
||||
test_option_get_synopsis_one(&(const Option) { 0, OPTION_OPTIONAL_ARG, 'x', NULL, "X" }, "/", false, ". -x" );
|
||||
|
||||
test_option_get_synopsis_one(&(const Option) { 0, OPTION_OPTIONAL_ARG, 'x', "xxx", "A B" }, "/", true, ". -x/--xxx[='A B']");
|
||||
test_option_get_synopsis_one(&(const Option) { 0, OPTION_OPTIONAL_ARG, 'x', "xxx", "A B" }, " ", true, ". -x --xxx[='A B']");
|
||||
test_option_get_synopsis_one(&(const Option) { 0, OPTION_OPTIONAL_ARG, 0, "xxx", "A B" }, "+", true, ". --xxx[='A B']" );
|
||||
test_option_get_synopsis_one(&(const Option) { 0, OPTION_OPTIONAL_ARG, 'x', NULL, "A B" }, " ", true, ". -x ['A B']" );
|
||||
|
||||
test_option_get_synopsis_one(&(const Option) { 0, OPTION_OPTIONAL_ARG | OPTION_HELP_ENTRY | OPTION_STOPS_PARSING,
|
||||
'x', "xxx", "A B" }, "/", true, ". -x/--xxx[='A B']");
|
||||
|
||||
test_option_get_synopsis_one(&(const Option) { 0, OPTION_HELP_ENTRY_VERBATIM, 'u', "special special", "unused" }, "/", true, ". special special");
|
||||
test_option_get_synopsis_one(&(const Option) { 0, OPTION_POSITIONAL_ENTRY, 'u', "(fixed)", "unused" }, "/", true, ". (fixed)");
|
||||
}
|
||||
|
||||
DEFINE_TEST_MAIN(LOG_DEBUG);
|
||||
|
||||
+38
-28
@@ -6,7 +6,6 @@
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <linux/bsg.h>
|
||||
#include <linux/hdreg.h>
|
||||
#include <scsi/sg.h>
|
||||
@@ -18,8 +17,12 @@
|
||||
#include "device-nodes.h"
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "format-table.h"
|
||||
#include "help-util.h"
|
||||
#include "log.h"
|
||||
#include "main-func.h"
|
||||
#include "options.h"
|
||||
#include "strv.h"
|
||||
#include "udev-util.h"
|
||||
#include "unaligned.h"
|
||||
|
||||
@@ -356,40 +359,47 @@ static int disk_identify(int fd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
static const struct option options[] = {
|
||||
{ "export", no_argument, NULL, 'x' },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, 'v' },
|
||||
{}
|
||||
};
|
||||
int c;
|
||||
static int help(void) {
|
||||
_cleanup_(table_unrefp) Table *options = NULL;
|
||||
int r;
|
||||
|
||||
while ((c = getopt_long(argc, argv, "xh", options, NULL)) >= 0)
|
||||
r = option_parser_get_help_table(&options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
help_cmdline("[OPTIONS...] DEVICE");
|
||||
help_section("Options:");
|
||||
|
||||
return table_print_or_warn(options);
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
|
||||
OptionParser state = { argc, argv };
|
||||
const char *arg;
|
||||
|
||||
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
|
||||
switch (c) {
|
||||
case 'x':
|
||||
|
||||
OPTION_COMMON_HELP:
|
||||
return help();
|
||||
|
||||
OPTION_COMMON_VERSION:
|
||||
return version();
|
||||
|
||||
OPTION('x', "export", NULL,
|
||||
"Print values as environment keys"):
|
||||
arg_export = true;
|
||||
break;
|
||||
case 'h':
|
||||
printf("%s [OPTIONS...] DEVICE\n\n"
|
||||
" -x --export Print values as environment keys\n"
|
||||
" -h --help Show this help text\n"
|
||||
" --version Show package version\n",
|
||||
program_invocation_short_name);
|
||||
return 0;
|
||||
case 'v':
|
||||
return version();
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
if (!argv[optind])
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"DEVICE argument missing.");
|
||||
char **args = option_parser_get_args(&state);
|
||||
if (strv_length(args) != 1)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Need exactly one DEVICE argument.");
|
||||
|
||||
arg_device = argv[optind];
|
||||
arg_device = args[0];
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <linux/cdrom.h>
|
||||
#include <scsi/sg.h>
|
||||
#include <sys/ioctl.h>
|
||||
@@ -13,11 +12,15 @@
|
||||
#include "build.h"
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "format-table.h"
|
||||
#include "help-util.h"
|
||||
#include "main-func.h"
|
||||
#include "options.h"
|
||||
#include "random-util.h"
|
||||
#include "sort-util.h"
|
||||
#include "string-table.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "time-util.h"
|
||||
#include "udev-util.h"
|
||||
#include "unaligned.h"
|
||||
@@ -898,60 +901,63 @@ static void print_properties(const Context *c) {
|
||||
}
|
||||
|
||||
static int help(void) {
|
||||
printf("%s [OPTIONS...] DEVICE\n\n"
|
||||
" -l --lock-media Lock the media (to enable eject request events)\n"
|
||||
" -u --unlock-media Unlock the media\n"
|
||||
" -e --eject-media Eject the media\n"
|
||||
" -d --debug Print debug messages to stderr\n"
|
||||
" -h --help Show this help text\n"
|
||||
" --version Show package version\n",
|
||||
program_invocation_short_name);
|
||||
_cleanup_(table_unrefp) Table *options = NULL;
|
||||
int r;
|
||||
|
||||
return 0;
|
||||
r = option_parser_get_help_table(&options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
help_cmdline("[OPTIONS...] DEVICE");
|
||||
help_section("Options:");
|
||||
|
||||
return table_print_or_warn(options);
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
static const struct option options[] = {
|
||||
{ "lock-media", no_argument, NULL, 'l' },
|
||||
{ "unlock-media", no_argument, NULL, 'u' },
|
||||
{ "eject-media", no_argument, NULL, 'e' },
|
||||
{ "debug", no_argument, NULL, 'd' },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, 'v' },
|
||||
{}
|
||||
};
|
||||
int c;
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
|
||||
while ((c = getopt_long(argc, argv, "deluh", options, NULL)) >= 0)
|
||||
OptionParser state = { argc, argv };
|
||||
const char *arg;
|
||||
|
||||
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
|
||||
switch (c) {
|
||||
case 'l':
|
||||
|
||||
OPTION_COMMON_HELP:
|
||||
return help();
|
||||
|
||||
OPTION_COMMON_VERSION:
|
||||
return version();
|
||||
|
||||
OPTION('l', "lock-media", NULL,
|
||||
"Lock the media (to enable eject request events)"):
|
||||
arg_lock = true;
|
||||
break;
|
||||
case 'u':
|
||||
|
||||
OPTION('u', "unlock-media", NULL,
|
||||
"Unlock the media"):
|
||||
arg_unlock = true;
|
||||
break;
|
||||
case 'e':
|
||||
|
||||
OPTION('e', "eject-media", NULL,
|
||||
"Eject the media"):
|
||||
arg_eject = true;
|
||||
break;
|
||||
case 'd':
|
||||
|
||||
OPTION('d', "debug", NULL,
|
||||
"Print debug messages to stderr"):
|
||||
log_set_target(LOG_TARGET_CONSOLE);
|
||||
log_set_max_level(LOG_DEBUG);
|
||||
log_open();
|
||||
break;
|
||||
case 'h':
|
||||
return help();
|
||||
case 'v':
|
||||
return version();
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
arg_node = argv[optind];
|
||||
if (!arg_node)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No device specified.");
|
||||
char **args = option_parser_get_args(&state);
|
||||
if (strv_length(args) != 1)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Need exactly one DEVICE argument.");
|
||||
|
||||
arg_node = args[0];
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,13 +42,15 @@
|
||||
* https://www.dmtf.org/sites/default/files/DSP0270_1.0.1.pdf
|
||||
*/
|
||||
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "build.h"
|
||||
#include "fileio.h"
|
||||
#include "format-table.h"
|
||||
#include "help-util.h"
|
||||
#include "main-func.h"
|
||||
#include "options.h"
|
||||
#include "string-util.h"
|
||||
#include "udev-util.h"
|
||||
#include "unaligned.h"
|
||||
@@ -643,45 +645,49 @@ static int legacy_decode(const uint8_t *buf, const char *devmem, bool no_file_of
|
||||
}
|
||||
|
||||
static int help(void) {
|
||||
printf("%s [OPTIONS...]\n\n"
|
||||
" -F --from-dump FILE Read DMI information from a binary file\n"
|
||||
" -h --help Show this help text\n"
|
||||
" --version Show package version\n",
|
||||
program_invocation_short_name);
|
||||
return 0;
|
||||
_cleanup_(table_unrefp) Table *options = NULL;
|
||||
int r;
|
||||
|
||||
r = option_parser_get_help_table(&options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
help_cmdline("[OPTIONS...]");
|
||||
help_section("Options:");
|
||||
|
||||
return table_print_or_warn(options);
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char * const *argv) {
|
||||
static const struct option options[] = {
|
||||
{ "from-dump", required_argument, NULL, 'F' },
|
||||
{ "version", no_argument, NULL, 'V' },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, 'v' },
|
||||
{}
|
||||
};
|
||||
int c;
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
|
||||
while ((c = getopt_long(argc, argv, "F:hV", options, NULL)) >= 0)
|
||||
OptionParser state = { argc, argv };
|
||||
const char *arg;
|
||||
|
||||
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
|
||||
switch (c) {
|
||||
case 'F':
|
||||
arg_source_file = optarg;
|
||||
break;
|
||||
case 'V':
|
||||
return version();
|
||||
case 'h':
|
||||
|
||||
OPTION_COMMON_HELP:
|
||||
return help();
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
case 'v':
|
||||
|
||||
OPTION_COMMON_VERSION_WITH_HIDDEN_V:
|
||||
return version();
|
||||
default:
|
||||
assert_not_reached();
|
||||
|
||||
OPTION('F', "from-dump", "FILE",
|
||||
"Read DMI information from a binary file"):
|
||||
arg_source_file = arg;
|
||||
break;
|
||||
}
|
||||
|
||||
if (option_parser_get_n_args(&state) > 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"This program takes no arguments.");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int run(int argc, char* const* argv) {
|
||||
static int run(int argc, char *argv[]) {
|
||||
_cleanup_free_ uint8_t *buf = NULL;
|
||||
bool no_file_offset = false;
|
||||
size_t size;
|
||||
|
||||
+34
-22
@@ -7,7 +7,6 @@
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <linux/hid.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
@@ -18,41 +17,54 @@
|
||||
#include "device-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fido_id_desc.h"
|
||||
#include "format-table.h"
|
||||
#include "help-util.h"
|
||||
#include "log.h"
|
||||
#include "main-func.h"
|
||||
#include "options.h"
|
||||
#include "path-util.h"
|
||||
#include "strv.h"
|
||||
#include "udev-util.h"
|
||||
|
||||
static const char *arg_device = NULL;
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
static const struct option options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, 'v' },
|
||||
{}
|
||||
};
|
||||
int c;
|
||||
static int help(void) {
|
||||
_cleanup_(table_unrefp) Table *options = NULL;
|
||||
int r;
|
||||
|
||||
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
|
||||
r = option_parser_get_help_table(&options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
help_cmdline("[OPTIONS...] SYSFS_PATH");
|
||||
help_abstract("Identify FIDO security tokens.");
|
||||
help_section("Options:");
|
||||
|
||||
return table_print_or_warn(options);
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
|
||||
OptionParser state = { argc, argv };
|
||||
const char *arg;
|
||||
|
||||
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
|
||||
switch (c) {
|
||||
case 'h':
|
||||
printf("%s [OPTIONS...] SYSFS_PATH\n\n"
|
||||
" -h --help Show this help text\n"
|
||||
" --version Show package version\n",
|
||||
program_invocation_short_name);
|
||||
return 0;
|
||||
case 'v':
|
||||
|
||||
OPTION_COMMON_HELP:
|
||||
return help();
|
||||
|
||||
OPTION_COMMON_VERSION:
|
||||
return version();
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
if (argc > 2)
|
||||
char **args = option_parser_get_args(&state);
|
||||
if (strv_length(args) > 1)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Error: unexpected argument.");
|
||||
|
||||
arg_device = argv[optind];
|
||||
arg_device = args[0];
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
+43
-47
@@ -1,6 +1,5 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "sd-device.h"
|
||||
@@ -11,7 +10,10 @@
|
||||
#include "conf-parser.h"
|
||||
#include "device-util.h"
|
||||
#include "devnum-util.h"
|
||||
#include "format-table.h"
|
||||
#include "help-util.h"
|
||||
#include "main-func.h"
|
||||
#include "options.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "udev-util.h"
|
||||
@@ -50,53 +52,52 @@ static int parse_config(void) {
|
||||
}
|
||||
|
||||
static int help(void) {
|
||||
printf("%s [OPTIONS...]\n\n"
|
||||
"Set up iocost model and qos solutions for block devices\n"
|
||||
"\nCommands:\n"
|
||||
" apply <path> [SOLUTION] Apply solution for the device if\n"
|
||||
" found, do nothing otherwise\n"
|
||||
" query <path> Query the known solution for\n"
|
||||
" the device\n"
|
||||
"\nOptions:\n"
|
||||
" -h --help Show this help\n"
|
||||
" --version Show package version\n",
|
||||
program_invocation_short_name);
|
||||
_cleanup_(table_unrefp) Table *options = NULL, *verbs = NULL;
|
||||
int r;
|
||||
|
||||
return 0;
|
||||
r = option_parser_get_help_table(&options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = verbs_get_help_table(&verbs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void) table_sync_column_widths(0, options, verbs);
|
||||
|
||||
help_cmdline("[OPTIONS...] COMMAND");
|
||||
help_abstract("Set up iocost model and qos solutions for block devices.");
|
||||
|
||||
help_section("Commands:");
|
||||
r = table_print_or_warn(verbs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
help_section("Options:");
|
||||
return table_print_or_warn(options);
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
enum {
|
||||
ARG_VERSION = 0x100,
|
||||
};
|
||||
VERB_COMMON_HELP_HIDDEN(help);
|
||||
|
||||
static const struct option options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, ARG_VERSION },
|
||||
{}
|
||||
};
|
||||
|
||||
int c;
|
||||
|
||||
assert(argc >= 1);
|
||||
static int parse_argv(int argc, char *argv[], char ***remaining_args) {
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
assert(remaining_args);
|
||||
|
||||
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
|
||||
OptionParser state = { argc, argv };
|
||||
const char *arg;
|
||||
|
||||
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
|
||||
switch (c) {
|
||||
|
||||
case 'h':
|
||||
OPTION_COMMON_HELP:
|
||||
return help();
|
||||
|
||||
case ARG_VERSION:
|
||||
OPTION_COMMON_VERSION:
|
||||
return version();
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
*remaining_args = option_parser_get_args(&state);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -280,32 +281,27 @@ static int query_solutions_for_path(const char *path) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
VERB(verb_query, "query", "PATH", 2, 2, 0,
|
||||
"Query the known solution for the device");
|
||||
static int verb_query(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
return query_solutions_for_path(ASSERT_PTR(argv[1]));
|
||||
}
|
||||
|
||||
VERB(verb_apply, "apply", "PATH [SOLUTION]", 2, 3, 0,
|
||||
"Apply solution for the device if found, do nothing otherwise");
|
||||
static int verb_apply(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
return apply_solution_for_path(
|
||||
ASSERT_PTR(argv[1]),
|
||||
argc > 2 ? ASSERT_PTR(argv[2]) : NULL);
|
||||
}
|
||||
|
||||
static int iocost_main(int argc, char *argv[]) {
|
||||
static const Verb verbs[] = {
|
||||
{ "query", 2, 2, 0, verb_query },
|
||||
{ "apply", 2, 3, 0, verb_apply },
|
||||
{},
|
||||
};
|
||||
|
||||
return dispatch_verb(argc, argv, verbs, NULL);
|
||||
}
|
||||
|
||||
static int run(int argc, char *argv[]) {
|
||||
char **args = NULL;
|
||||
int r;
|
||||
|
||||
log_setup();
|
||||
|
||||
r = parse_argv(argc, argv);
|
||||
r = parse_argv(argc, argv, &args);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
@@ -313,7 +309,7 @@ static int run(int argc, char *argv[]) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return iocost_main(argc, argv);
|
||||
return dispatch_verb_with_args(args, NULL);
|
||||
}
|
||||
|
||||
DEFINE_MAIN_FUNCTION(run);
|
||||
|
||||
@@ -19,48 +19,59 @@
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <mtd/mtd-user.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "build.h"
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "format-table.h"
|
||||
#include "help-util.h"
|
||||
#include "log.h"
|
||||
#include "main-func.h"
|
||||
#include "mtd_probe.h"
|
||||
#include "options.h"
|
||||
#include "strv.h"
|
||||
|
||||
static const char *arg_device = NULL;
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
static const struct option options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, 'v' },
|
||||
{}
|
||||
};
|
||||
int c;
|
||||
static int help(void) {
|
||||
_cleanup_(table_unrefp) Table *options = NULL;
|
||||
int r;
|
||||
|
||||
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
|
||||
r = option_parser_get_help_table(&options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
help_cmdline("[OPTIONS...] /dev/mtd[n]");
|
||||
help_abstract("Probe MTD devices.");
|
||||
help_section("Options:");
|
||||
|
||||
return table_print_or_warn(options);
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
|
||||
OptionParser state = { argc, argv };
|
||||
const char *arg;
|
||||
|
||||
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
|
||||
switch (c) {
|
||||
case 'h':
|
||||
printf("%s /dev/mtd[n]\n\n"
|
||||
" -h --help Show this help text\n"
|
||||
" --version Show package version\n",
|
||||
program_invocation_short_name);
|
||||
return 0;
|
||||
case 'v':
|
||||
|
||||
OPTION_COMMON_HELP:
|
||||
return help();
|
||||
|
||||
OPTION_COMMON_VERSION:
|
||||
return version();
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
if (argc > 2)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Error: unexpected argument.");
|
||||
char **args = option_parser_get_args(&state);
|
||||
if (strv_length(args) != 1)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Need exactly one DEVICE argument.");
|
||||
|
||||
arg_device = argv[optind];
|
||||
arg_device = args[0];
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -73,12 +84,12 @@ static int run(int argc, char** argv) {
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
mtd_fd = open(argv[1], O_RDONLY|O_CLOEXEC|O_NOCTTY);
|
||||
mtd_fd = open(arg_device, O_RDONLY|O_CLOEXEC|O_NOCTTY);
|
||||
if (mtd_fd < 0) {
|
||||
bool ignore = ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(errno);
|
||||
log_full_errno(ignore ? LOG_DEBUG : LOG_WARNING, errno,
|
||||
"Failed to open device node '%s'%s: %m",
|
||||
argv[1], ignore ? ", ignoring" : "");
|
||||
arg_device, ignore ? ", ignoring" : "");
|
||||
return ignore ? 0 : -errno;
|
||||
}
|
||||
|
||||
|
||||
+168
-213
@@ -5,7 +5,6 @@
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
@@ -15,6 +14,10 @@
|
||||
#include "extract-word.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "format-table.h"
|
||||
#include "help-util.h"
|
||||
#include "main-func.h"
|
||||
#include "options.h"
|
||||
#include "parse-util.h"
|
||||
#include "scsi_id.h"
|
||||
#include "string-util.h"
|
||||
@@ -23,23 +26,6 @@
|
||||
#include "udev-util.h"
|
||||
#include "utf8.h"
|
||||
|
||||
static const struct option options[] = {
|
||||
{ "device", required_argument, NULL, 'd' },
|
||||
{ "config", required_argument, NULL, 'f' },
|
||||
{ "page", required_argument, NULL, 'p' },
|
||||
{ "denylisted", no_argument, NULL, 'b' },
|
||||
{ "allowlisted", no_argument, NULL, 'g' },
|
||||
{ "blacklisted", no_argument, NULL, 'b' }, /* backward compat */
|
||||
{ "whitelisted", no_argument, NULL, 'g' }, /* backward compat */
|
||||
{ "replace-whitespace", no_argument, NULL, 'u' },
|
||||
{ "sg-version", required_argument, NULL, 's' },
|
||||
{ "verbose", no_argument, NULL, 'v' },
|
||||
{ "version", no_argument, NULL, 'V' }, /* don't advertise -V */
|
||||
{ "export", no_argument, NULL, 'x' },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{}
|
||||
};
|
||||
|
||||
static bool all_good = false;
|
||||
static bool dev_specified = false;
|
||||
static char config_file[MAX_PATH_LEN] = "/etc/scsi_id.config";
|
||||
@@ -101,31 +87,22 @@ static void set_type(unsigned type_num, char *to, size_t len) {
|
||||
*
|
||||
* vendor and model can end in '\n'.
|
||||
*/
|
||||
static int get_file_options(const char *vendor, const char *model,
|
||||
int *argc, char ***newargv) {
|
||||
static int get_file_options(const char *vendor, const char *model, char ***ret_argv) {
|
||||
_cleanup_free_ char *vendor_in = NULL, *model_in = NULL, *options_in = NULL; /* read in from file */
|
||||
_cleanup_strv_free_ char **options_argv = NULL;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
int lineno, r;
|
||||
int r;
|
||||
|
||||
f = fopen(config_file, "re");
|
||||
_cleanup_fclose_ FILE *f = fopen(config_file, "re");
|
||||
if (!f) {
|
||||
if (errno == ENOENT)
|
||||
return 1;
|
||||
else {
|
||||
log_error_errno(errno, "can't open %s: %m", config_file);
|
||||
return -1;
|
||||
}
|
||||
goto finish;
|
||||
return log_error_errno(errno, "Cannot open %s: %m", config_file);
|
||||
}
|
||||
|
||||
*newargv = NULL;
|
||||
lineno = 0;
|
||||
for (;;) {
|
||||
for (int lineno = 0;;) {
|
||||
_cleanup_free_ char *buffer = NULL, *key = NULL, *value = NULL;
|
||||
const char *buf;
|
||||
|
||||
vendor_in = model_in = options_in = NULL;
|
||||
|
||||
r = read_line(f, MAX_BUFFER_LEN, &buffer);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "read_line() on line %d of %s failed: %m", lineno, config_file);
|
||||
@@ -197,189 +174,179 @@ static int get_file_options(const char *vendor, const char *model,
|
||||
|
||||
}
|
||||
|
||||
if (vendor_in == NULL && model_in == NULL && options_in == NULL)
|
||||
return 1; /* No matches */
|
||||
if (!vendor_in && !model_in && !options_in)
|
||||
goto finish; /* No matches */
|
||||
|
||||
/*
|
||||
* Something matched. Allocate newargv, and store
|
||||
* values found in options_in.
|
||||
*/
|
||||
/* Something matched. Allocate newargv, and store values found in options_in. */
|
||||
options_argv = strv_split(options_in, " \t");
|
||||
if (!options_argv)
|
||||
return log_oom();
|
||||
r = strv_prepend(&options_argv, ""); /* getopt skips over argv[0] */
|
||||
r = strv_prepend(&options_argv, ""); /* argv[0] is skipped */
|
||||
if (r < 0)
|
||||
return r;
|
||||
*newargv = TAKE_PTR(options_argv);
|
||||
*argc = strv_length(*newargv);
|
||||
|
||||
finish:
|
||||
*ret_argv = TAKE_PTR(options_argv);
|
||||
return !!*ret_argv; /* true if something matched, false otherwise */
|
||||
}
|
||||
|
||||
static int parse_page_code(const char *value, enum page_code *ret) {
|
||||
assert(value);
|
||||
assert(ret);
|
||||
|
||||
if (streq(value, "0x80"))
|
||||
*ret = PAGE_80;
|
||||
else if (streq(value, "0x83"))
|
||||
*ret = PAGE_83;
|
||||
else if (streq(value, "pre-spc3-83"))
|
||||
*ret = PAGE_83_PRE_SPC3;
|
||||
else
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Unknown page code '%s'", value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void help(void) {
|
||||
printf("Usage: %s [OPTION...] DEVICE\n\n"
|
||||
"SCSI device identification.\n\n"
|
||||
" -h --help Print this message\n"
|
||||
" --version Print version of the program\n\n"
|
||||
" -d --device= Device node for SG_IO commands\n"
|
||||
" -f --config= Location of config file\n"
|
||||
" -p --page=0x80|0x83|pre-spc3-83 SCSI page (0x80, 0x83, pre-spc3-83)\n"
|
||||
" -s --sg-version=3|4 Use SGv3 or SGv4\n"
|
||||
" -b --denylisted Treat device as denylisted\n"
|
||||
" -g --allowlisted Treat device as allowlisted\n"
|
||||
" -u --replace-whitespace Replace all whitespace by underscores\n"
|
||||
" -v --verbose Verbose logging\n"
|
||||
" -x --export Print values as environment keys\n",
|
||||
program_invocation_short_name);
|
||||
static int help(void) {
|
||||
_cleanup_(table_unrefp) Table *options = NULL;
|
||||
int r;
|
||||
|
||||
r = option_parser_get_help_table(&options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
help_cmdline("[OPTION...] DEVICE");
|
||||
help_abstract("SCSI device identification.");
|
||||
help_section("Options:");
|
||||
|
||||
return table_print_or_warn(options);
|
||||
}
|
||||
|
||||
static int set_options(int argc, char **argv,
|
||||
char *maj_min_dev) {
|
||||
int option, r;
|
||||
static int set_options(int argc, char **argv, char *maj_min_dev) {
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
|
||||
/*
|
||||
* optind is a global extern used by getopt. Since we can call
|
||||
* set_options twice (once for command line, and once for config
|
||||
* file) we have to reset this back to 1.
|
||||
*/
|
||||
optind = 1;
|
||||
while ((option = getopt_long(argc, argv, "d:f:gp:uvVxhbs:", options, NULL)) >= 0)
|
||||
switch (option) {
|
||||
case 'b':
|
||||
OptionParser state = { argc, argv };
|
||||
const char *arg;
|
||||
int r;
|
||||
|
||||
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
|
||||
switch (c) {
|
||||
|
||||
OPTION_COMMON_HELP:
|
||||
return help();
|
||||
|
||||
OPTION_COMMON_VERSION_WITH_HIDDEN_V:
|
||||
return version();
|
||||
|
||||
OPTION('d', "device", "PATH", "Device node for SG_IO commands"):
|
||||
dev_specified = true;
|
||||
strscpy(maj_min_dev, MAX_PATH_LEN, arg);
|
||||
break;
|
||||
|
||||
OPTION('f', "config", "PATH", "Location of config file"):
|
||||
strscpy(config_file, MAX_PATH_LEN, arg);
|
||||
break;
|
||||
|
||||
OPTION('p', "page", "0x80|0x83|pre-spc3-83", "SCSI page"):
|
||||
r = parse_page_code(arg, &default_page_code);
|
||||
if (r < 0)
|
||||
return r;
|
||||
break;
|
||||
|
||||
OPTION('s', "sg-version", "3|4", "Use SGv3 or SGv4"):
|
||||
r = safe_atoi(arg, &sg_version);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Invalid SG version '%s'", arg);
|
||||
if (!IN_SET(sg_version, 3, 4))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ERANGE),
|
||||
"Unknown SG version '%s'", arg);
|
||||
break;
|
||||
|
||||
OPTION('b', "denylisted", NULL, "Treat device as denylisted"): {}
|
||||
OPTION('b', "blacklisted", NULL, /* help= */ NULL): /* backward compat */
|
||||
all_good = false;
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
dev_specified = true;
|
||||
strscpy(maj_min_dev, MAX_PATH_LEN, optarg);
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
strscpy(config_file, MAX_PATH_LEN, optarg);
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
OPTION('g', "allowlisted", NULL, "Treat device as allowlisted"): {}
|
||||
OPTION('g', "whitelisted", NULL, /* help= */ NULL): /* backward compat */
|
||||
all_good = true;
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
help();
|
||||
exit(EXIT_SUCCESS);
|
||||
|
||||
case 'p':
|
||||
if (streq(optarg, "0x80"))
|
||||
default_page_code = PAGE_80;
|
||||
else if (streq(optarg, "0x83"))
|
||||
default_page_code = PAGE_83;
|
||||
else if (streq(optarg, "pre-spc3-83"))
|
||||
default_page_code = PAGE_83_PRE_SPC3;
|
||||
else
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Unknown page code '%s'",
|
||||
optarg);
|
||||
break;
|
||||
|
||||
case 's':
|
||||
r = safe_atoi(optarg, &sg_version);
|
||||
if (r < 0)
|
||||
return log_error_errno(r,
|
||||
"Invalid SG version '%s'",
|
||||
optarg);
|
||||
if (sg_version < 3 || sg_version > 4)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Unknown SG version '%s'",
|
||||
optarg);
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
OPTION('u', "replace-whitespace", NULL, "Replace all whitespace by underscores"):
|
||||
reformat_serial = true;
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
OPTION('v', "verbose", NULL, "Verbose logging"):
|
||||
log_set_target(LOG_TARGET_CONSOLE);
|
||||
log_set_max_level(LOG_DEBUG);
|
||||
log_open();
|
||||
break;
|
||||
|
||||
case 'V':
|
||||
version();
|
||||
exit(EXIT_SUCCESS);
|
||||
|
||||
case 'x':
|
||||
OPTION('x', "export", NULL, "Print values as environment keys"):
|
||||
export = true;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -1;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
if (optind < argc && !dev_specified) {
|
||||
char **args = option_parser_get_args(&state);
|
||||
if (!strv_isempty(args) && !dev_specified) {
|
||||
dev_specified = true;
|
||||
strscpy(maj_min_dev, MAX_PATH_LEN, argv[optind]);
|
||||
strscpy(maj_min_dev, MAX_PATH_LEN, args[0]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int per_dev_options(struct scsi_id_device *dev_scsi, int *good_bad, int *page_code) {
|
||||
_cleanup_strv_free_ char **newargv = NULL;
|
||||
int retval;
|
||||
int newargc;
|
||||
int option;
|
||||
static int per_dev_options(struct scsi_id_device *dev_scsi, int *good_bad, enum page_code *page_code) {
|
||||
int r;
|
||||
|
||||
assert(dev_scsi);
|
||||
assert(good_bad);
|
||||
assert(page_code);
|
||||
|
||||
*good_bad = all_good;
|
||||
*page_code = default_page_code;
|
||||
|
||||
retval = get_file_options(vendor_str, model_str, &newargc, &newargv);
|
||||
_cleanup_strv_free_ char **newargv = NULL;
|
||||
r = get_file_options(vendor_str, model_str, &newargv);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
optind = 1; /* reset this global extern */
|
||||
while (retval == 0) {
|
||||
option = getopt_long(newargc, newargv, "bgp:", options, NULL);
|
||||
if (option == -1)
|
||||
break;
|
||||
size_t newargc = strv_length(newargv);
|
||||
if (newargc > INT_MAX)
|
||||
return log_oom(); /* Close enough :) */
|
||||
|
||||
switch (option) {
|
||||
case 'b':
|
||||
OptionParser state = { newargc, newargv };
|
||||
const Option *opt;
|
||||
const char *arg;
|
||||
|
||||
/* We reuse the option parser, but only a subset of the options is supported here.
|
||||
* If any others are encountered, return an error. */
|
||||
|
||||
FOREACH_OPTION_FULL(&state, c, &opt, &arg, /* on_error= */ return c)
|
||||
if (opt->short_code == 'b')
|
||||
*good_bad = 0;
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
else if (opt->short_code == 'g')
|
||||
*good_bad = 1;
|
||||
break;
|
||||
else if (opt->short_code == 'p') {
|
||||
r = parse_page_code(arg, page_code);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Option %s not supported in the config file.",
|
||||
strnull(option_get_synopsis("", opt, "/", /* show_metavar=*/ false)));
|
||||
|
||||
case 'p':
|
||||
if (streq(optarg, "0x80")) {
|
||||
*page_code = PAGE_80;
|
||||
} else if (streq(optarg, "0x83")) {
|
||||
*page_code = PAGE_83;
|
||||
} else if (streq(optarg, "pre-spc3-83")) {
|
||||
*page_code = PAGE_83_PRE_SPC3;
|
||||
} else {
|
||||
log_error("Unknown page code '%s'", optarg);
|
||||
retval = -1;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
log_error("Unknown or bad option '%c' (0x%x)", option, (unsigned) option);
|
||||
retval = -1;
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_inq_values(struct scsi_id_device *dev_scsi, const char *path) {
|
||||
int retval;
|
||||
int r;
|
||||
|
||||
dev_scsi->use_sg = sg_version;
|
||||
|
||||
retval = scsi_std_inquiry(dev_scsi, path);
|
||||
if (retval)
|
||||
return retval;
|
||||
r = scsi_std_inquiry(dev_scsi, path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
encode_devnode_name(dev_scsi->vendor, vendor_enc_str, sizeof(vendor_enc_str));
|
||||
encode_devnode_name(dev_scsi->model, model_enc_str, sizeof(model_enc_str));
|
||||
@@ -400,28 +367,25 @@ static bool scsi_string_is_valid(const char *s) {
|
||||
|
||||
/*
|
||||
* scsi_id: try to get an id, if one is found, printf it to stdout.
|
||||
* returns a value passed to exit() - 0 if printed an id, else 1.
|
||||
*/
|
||||
static int scsi_id(char *maj_min_dev) {
|
||||
struct scsi_id_device dev_scsi = {};
|
||||
int good_dev;
|
||||
int page_code;
|
||||
int retval = 0;
|
||||
enum page_code page_code;
|
||||
int good_dev, r;
|
||||
|
||||
if (set_inq_values(&dev_scsi, maj_min_dev) < 0) {
|
||||
retval = 1;
|
||||
goto out;
|
||||
}
|
||||
r = set_inq_values(&dev_scsi, maj_min_dev);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* get per device (vendor + model) options from the config file */
|
||||
per_dev_options(&dev_scsi, &good_dev, &page_code);
|
||||
if (!good_dev) {
|
||||
retval = 1;
|
||||
goto out;
|
||||
}
|
||||
r = per_dev_options(&dev_scsi, &good_dev, &page_code);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (!good_dev)
|
||||
return -EIO;
|
||||
|
||||
/* read serial number from mode pages (no values for optical drives) */
|
||||
scsi_get_serial(&dev_scsi, maj_min_dev, page_code, MAX_SERIAL_LEN);
|
||||
(void) scsi_get_serial(&dev_scsi, maj_min_dev, page_code, MAX_SERIAL_LEN);
|
||||
|
||||
if (export) {
|
||||
char serial_str[MAX_SERIAL_LEN];
|
||||
@@ -453,13 +417,11 @@ static int scsi_id(char *maj_min_dev) {
|
||||
printf("ID_TARGET_PORT=%s\n", dev_scsi.tgpt_group);
|
||||
if (scsi_string_is_valid(dev_scsi.unit_serial_number))
|
||||
printf("ID_SCSI_SERIAL=%s\n", dev_scsi.unit_serial_number);
|
||||
goto out;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (dev_scsi.serial[0] == '\0') {
|
||||
retval = 1;
|
||||
goto out;
|
||||
}
|
||||
if (dev_scsi.serial[0] == '\0')
|
||||
return -ENODATA;
|
||||
|
||||
if (reformat_serial) {
|
||||
char serial_str[MAX_SERIAL_LEN];
|
||||
@@ -467,19 +429,17 @@ static int scsi_id(char *maj_min_dev) {
|
||||
udev_replace_whitespace(dev_scsi.serial, serial_str, sizeof(serial_str)-1);
|
||||
udev_replace_chars(serial_str, NULL);
|
||||
printf("%s\n", serial_str);
|
||||
goto out;
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("%s\n", dev_scsi.serial);
|
||||
out:
|
||||
return retval;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
static int run(int argc, char **argv) {
|
||||
_cleanup_strv_free_ char **newargv = NULL;
|
||||
int retval = 0;
|
||||
char maj_min_dev[MAX_PATH_LEN];
|
||||
int newargc;
|
||||
int r;
|
||||
|
||||
(void) udev_parse_config();
|
||||
log_setup();
|
||||
@@ -487,35 +447,30 @@ int main(int argc, char **argv) {
|
||||
/*
|
||||
* Get config file options.
|
||||
*/
|
||||
retval = get_file_options(NULL, NULL, &newargc, &newargv);
|
||||
if (retval < 0) {
|
||||
retval = 1;
|
||||
goto exit;
|
||||
}
|
||||
if (retval == 0) {
|
||||
assert(newargv);
|
||||
r = get_file_options(NULL, NULL, &newargv);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 1) {
|
||||
size_t newargc = strv_length(newargv);
|
||||
if (newargc > INT_MAX)
|
||||
return log_oom(); /* Close enough :) */
|
||||
|
||||
if (set_options(newargc, newargv, maj_min_dev) < 0) {
|
||||
retval = 2;
|
||||
goto exit;
|
||||
}
|
||||
r = set_options(newargc, newargv, maj_min_dev);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get command line options (overriding any config file settings).
|
||||
*/
|
||||
if (set_options(argc, argv, maj_min_dev) < 0)
|
||||
exit(EXIT_FAILURE);
|
||||
r = set_options(argc, argv, maj_min_dev);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
if (!dev_specified) {
|
||||
log_error("No device specified.");
|
||||
retval = 1;
|
||||
goto exit;
|
||||
}
|
||||
if (!dev_specified)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No device specified.");
|
||||
|
||||
retval = scsi_id(maj_min_dev);
|
||||
|
||||
exit:
|
||||
log_close();
|
||||
return retval;
|
||||
return scsi_id(maj_min_dev);
|
||||
}
|
||||
|
||||
DEFINE_MAIN_FUNCTION(run);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include "devnum-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "log.h"
|
||||
#include "random-util.h"
|
||||
@@ -762,30 +763,23 @@ static int do_scsi_page80_inquiry(struct scsi_id_device *dev_scsi, int fd,
|
||||
}
|
||||
|
||||
int scsi_std_inquiry(struct scsi_id_device *dev_scsi, const char *devname) {
|
||||
int fd;
|
||||
unsigned char buf[SCSI_INQ_BUFF_LEN];
|
||||
unsigned char buf[SCSI_INQ_BUFF_LEN] = {};
|
||||
struct stat statbuf;
|
||||
int err = 0;
|
||||
int r;
|
||||
|
||||
fd = open(devname, O_RDONLY | O_NONBLOCK | O_CLOEXEC | O_NOCTTY);
|
||||
if (fd < 0) {
|
||||
log_debug_errno(errno, "scsi_id: cannot open %s: %m", devname);
|
||||
return 1;
|
||||
}
|
||||
_cleanup_close_ int fd = open(devname, O_RDONLY | O_NONBLOCK | O_CLOEXEC | O_NOCTTY);
|
||||
if (fd < 0)
|
||||
return log_debug_errno(errno, "scsi_id: cannot open %s: %m", devname);
|
||||
|
||||
if (fstat(fd, &statbuf) < 0)
|
||||
return log_debug_errno(errno, "scsi_id: cannot stat %s: %m", devname);
|
||||
|
||||
if (fstat(fd, &statbuf) < 0) {
|
||||
log_debug_errno(errno, "scsi_id: cannot stat %s: %m", devname);
|
||||
err = 2;
|
||||
goto out;
|
||||
}
|
||||
format_devnum(statbuf.st_rdev, dev_scsi->kernel);
|
||||
|
||||
memzero(buf, SCSI_INQ_BUFF_LEN);
|
||||
err = scsi_inquiry(dev_scsi, fd, 0, 0, buf, SCSI_INQ_BUFF_LEN);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
r = scsi_inquiry(dev_scsi, fd, 0, 0, buf, SCSI_INQ_BUFF_LEN);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
err = 0;
|
||||
memcpy(dev_scsi->vendor, buf + 8, 8);
|
||||
dev_scsi->vendor[8] = '\0';
|
||||
memcpy(dev_scsi->model, buf + 16, 16);
|
||||
@@ -793,10 +787,7 @@ int scsi_std_inquiry(struct scsi_id_device *dev_scsi, const char *devname) {
|
||||
memcpy(dev_scsi->revision, buf + 32, 4);
|
||||
dev_scsi->revision[4] = '\0';
|
||||
dev_scsi->type = buf[0] & 0x1f;
|
||||
|
||||
out:
|
||||
close(fd);
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int scsi_get_serial(struct scsi_id_device *dev_scsi, const char *devname,
|
||||
|
||||
+35
-24
@@ -4,7 +4,6 @@
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <linux/videodev2.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/ioctl.h>
|
||||
@@ -12,43 +11,55 @@
|
||||
#include "build.h"
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "format-table.h"
|
||||
#include "help-util.h"
|
||||
#include "log.h"
|
||||
#include "main-func.h"
|
||||
#include "options.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "utf8.h"
|
||||
|
||||
static const char *arg_device = NULL;
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
static const struct option options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, 'v' },
|
||||
{}
|
||||
};
|
||||
int c;
|
||||
static int help(void) {
|
||||
_cleanup_(table_unrefp) Table *options = NULL;
|
||||
int r;
|
||||
|
||||
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
|
||||
r = option_parser_get_help_table(&options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
help_cmdline("[OPTIONS...] DEVICE");
|
||||
help_abstract("Video4Linux device identification.");
|
||||
help_section("Options:");
|
||||
|
||||
return table_print_or_warn(options);
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
|
||||
OptionParser state = { argc, argv };
|
||||
const char *arg;
|
||||
|
||||
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
|
||||
switch (c) {
|
||||
case 'h':
|
||||
printf("%s [OPTIONS...] DEVICE\n\n"
|
||||
"Video4Linux device identification.\n\n"
|
||||
" -h --help Show this help text\n"
|
||||
" --version Show package version\n",
|
||||
program_invocation_short_name);
|
||||
return 0;
|
||||
case 'v':
|
||||
|
||||
OPTION_COMMON_HELP:
|
||||
return help();
|
||||
|
||||
OPTION_COMMON_VERSION:
|
||||
return version();
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
if (!argv[optind])
|
||||
char **args = option_parser_get_args(&state);
|
||||
if (strv_length(args) != 1)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"DEVICE argument missing.");
|
||||
"Need exactly one DEVICE argument.");
|
||||
|
||||
arg_device = argv[optind];
|
||||
arg_device = args[0];
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user