2020-11-09 05:25:50 +01:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
2010-06-21 16:55:16 +02:00
|
|
|
/*
|
2018-06-12 17:15:23 +02:00
|
|
|
* Copyright © IBM Corp. 2003
|
|
|
|
* Copyright © SUSE Linux Products GmbH, 2006
|
2003-11-13 15:34:36 +01:00
|
|
|
*/
|
|
|
|
|
2015-10-24 22:58:24 +02:00
|
|
|
#include <ctype.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <getopt.h>
|
|
|
|
#include <signal.h>
|
2013-12-15 23:15:54 +01:00
|
|
|
#include <stdarg.h>
|
|
|
|
#include <stdbool.h>
|
2015-10-24 22:58:24 +02:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2003-11-13 15:34:36 +01:00
|
|
|
#include <sys/stat.h>
|
2015-10-24 22:58:24 +02:00
|
|
|
#include <unistd.h>
|
2003-11-13 15:34:36 +01:00
|
|
|
|
2018-08-22 09:25:17 +02:00
|
|
|
#include "alloc-util.h"
|
2018-12-20 20:35:25 +01:00
|
|
|
#include "build.h"
|
2020-12-14 08:59:37 +01:00
|
|
|
#include "device-nodes.h"
|
2015-10-25 13:14:12 +01:00
|
|
|
#include "fd-util.h"
|
2006-01-09 21:18:00 +01:00
|
|
|
#include "scsi_id.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "string-util.h"
|
2018-11-16 03:44:17 +01:00
|
|
|
#include "strxcpyx.h"
|
2013-12-15 23:15:54 +01:00
|
|
|
#include "udev-util.h"
|
2003-11-13 15:34:36 +01:00
|
|
|
|
2007-05-25 17:37:47 +02:00
|
|
|
static const struct option options[] = {
|
2013-12-15 23:15:54 +01:00
|
|
|
{ "device", required_argument, NULL, 'd' },
|
|
|
|
{ "config", required_argument, NULL, 'f' },
|
|
|
|
{ "page", required_argument, NULL, 'p' },
|
|
|
|
{ "blacklisted", no_argument, NULL, 'b' },
|
|
|
|
{ "whitelisted", no_argument, NULL, 'g' },
|
|
|
|
{ "replace-whitespace", no_argument, NULL, 'u' },
|
|
|
|
{ "sg-version", required_argument, NULL, 's' },
|
|
|
|
{ "verbose", no_argument, NULL, 'v' },
|
2013-12-18 03:48:14 +01:00
|
|
|
{ "version", no_argument, NULL, 'V' }, /* don't advertise -V */
|
2013-12-15 23:15:54 +01:00
|
|
|
{ "export", no_argument, NULL, 'x' },
|
|
|
|
{ "help", no_argument, NULL, 'h' },
|
2012-01-10 01:34:15 +01:00
|
|
|
{}
|
2007-05-25 17:37:47 +02:00
|
|
|
};
|
|
|
|
|
2013-12-15 23:15:54 +01:00
|
|
|
static bool all_good = false;
|
|
|
|
static bool dev_specified = false;
|
2012-10-28 04:04:22 +01:00
|
|
|
static char config_file[MAX_PATH_LEN] = "/etc/scsi_id.config";
|
2013-12-15 23:15:54 +01:00
|
|
|
static enum page_code default_page_code = PAGE_UNSPECIFIED;
|
2008-05-14 15:02:17 +02:00
|
|
|
static int sg_version = 4;
|
2013-12-15 23:15:54 +01:00
|
|
|
static bool reformat_serial = false;
|
|
|
|
static bool export = false;
|
2005-06-27 02:51:49 +02:00
|
|
|
static char vendor_str[64];
|
|
|
|
static char model_str[64];
|
2009-02-17 20:15:17 +01:00
|
|
|
static char vendor_enc_str[256];
|
|
|
|
static char model_enc_str[256];
|
2005-06-27 17:04:56 +02:00
|
|
|
static char revision_str[16];
|
|
|
|
static char type_str[16];
|
2003-11-13 15:34:36 +01:00
|
|
|
|
2018-08-24 05:30:38 +02:00
|
|
|
static void set_type(const char *from, char *to, size_t len) {
|
2012-01-10 01:34:15 +01:00
|
|
|
int type_num;
|
|
|
|
char *eptr;
|
2012-04-04 05:31:21 +02:00
|
|
|
const char *type = "generic";
|
2012-01-10 01:34:15 +01:00
|
|
|
|
|
|
|
type_num = strtoul(from, &eptr, 0);
|
|
|
|
if (eptr != from) {
|
|
|
|
switch (type_num) {
|
|
|
|
case 0:
|
|
|
|
type = "disk";
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
type = "tape";
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
type = "optical";
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
type = "cd";
|
|
|
|
break;
|
|
|
|
case 7:
|
|
|
|
type = "optical";
|
|
|
|
break;
|
|
|
|
case 0xe:
|
|
|
|
type = "disk";
|
|
|
|
break;
|
|
|
|
case 0xf:
|
|
|
|
type = "optical";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2013-01-09 19:06:46 +01:00
|
|
|
strscpy(to, len, type);
|
2005-06-27 02:51:49 +02:00
|
|
|
}
|
|
|
|
|
2003-11-13 15:34:36 +01:00
|
|
|
/*
|
|
|
|
* get_value:
|
|
|
|
*
|
|
|
|
* buf points to an '=' followed by a quoted string ("foo") or a string ending
|
|
|
|
* with a space or ','.
|
|
|
|
*
|
|
|
|
* Return a pointer to the NUL terminated string, returns NULL if no
|
|
|
|
* matches.
|
|
|
|
*/
|
2018-08-24 05:30:38 +02:00
|
|
|
static char *get_value(char **buffer) {
|
2012-04-04 05:31:21 +02:00
|
|
|
static const char *quote_string = "\"\n";
|
|
|
|
static const char *comma_string = ",\n";
|
2012-01-10 01:34:15 +01:00
|
|
|
char *val;
|
2012-04-04 05:31:21 +02:00
|
|
|
const char *end;
|
2012-01-10 01:34:15 +01:00
|
|
|
|
|
|
|
if (**buffer == '"') {
|
|
|
|
/*
|
|
|
|
* skip leading quote, terminate when quote seen
|
|
|
|
*/
|
|
|
|
(*buffer)++;
|
|
|
|
end = quote_string;
|
2020-04-12 18:26:05 +02:00
|
|
|
} else
|
2012-01-10 01:34:15 +01:00
|
|
|
end = comma_string;
|
|
|
|
val = strsep(buffer, end);
|
|
|
|
if (val && end == quote_string)
|
|
|
|
/*
|
|
|
|
* skip trailing quote
|
|
|
|
*/
|
|
|
|
(*buffer)++;
|
|
|
|
|
|
|
|
while (isspace(**buffer))
|
|
|
|
(*buffer)++;
|
|
|
|
|
|
|
|
return val;
|
2003-11-13 15:34:36 +01:00
|
|
|
}
|
|
|
|
|
2018-08-24 05:30:38 +02:00
|
|
|
static int argc_count(char *opts) {
|
2012-01-10 01:34:15 +01:00
|
|
|
int i = 0;
|
|
|
|
while (*opts != '\0')
|
|
|
|
if (*opts++ == ' ')
|
|
|
|
i++;
|
|
|
|
return i;
|
2003-11-13 15:34:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* get_file_options:
|
|
|
|
*
|
|
|
|
* If vendor == NULL, find a line in the config file with only "OPTIONS=";
|
|
|
|
* if vendor and model are set find the first OPTIONS line in the config
|
|
|
|
* file that matches. Set argc and argv to match the OPTIONS string.
|
|
|
|
*
|
|
|
|
* vendor and model can end in '\n'.
|
|
|
|
*/
|
2018-08-24 05:30:12 +02:00
|
|
|
static int get_file_options(const char *vendor, const char *model,
|
|
|
|
int *argc, char ***newargv) {
|
2018-06-07 23:32:07 +02:00
|
|
|
_cleanup_free_ char *buffer = NULL;
|
2013-12-15 23:15:54 +01:00
|
|
|
_cleanup_fclose_ FILE *f;
|
2012-01-10 01:34:15 +01:00
|
|
|
char *buf;
|
|
|
|
char *str1;
|
|
|
|
char *vendor_in, *model_in, *options_in; /* read in from file */
|
|
|
|
int lineno;
|
|
|
|
int c;
|
|
|
|
int retval = 0;
|
|
|
|
|
2013-12-15 23:15:54 +01:00
|
|
|
f = fopen(config_file, "re");
|
2019-04-28 14:28:49 +02:00
|
|
|
if (!f) {
|
2013-12-15 23:15:54 +01:00
|
|
|
if (errno == ENOENT)
|
2012-01-10 01:34:15 +01:00
|
|
|
return 1;
|
2013-12-15 23:15:54 +01:00
|
|
|
else {
|
2014-11-28 19:29:59 +01:00
|
|
|
log_error_errno(errno, "can't open %s: %m", config_file);
|
2012-01-10 01:34:15 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Allocate a buffer rather than put it on the stack so we can
|
|
|
|
* keep it around to parse any options (any allocated newargv
|
|
|
|
* points into this buffer for its strings).
|
|
|
|
*/
|
|
|
|
buffer = malloc(MAX_BUFFER_LEN);
|
2013-12-15 23:15:54 +01:00
|
|
|
if (!buffer)
|
2012-07-25 23:55:59 +02:00
|
|
|
return log_oom();
|
2012-01-10 01:34:15 +01:00
|
|
|
|
|
|
|
*newargv = NULL;
|
|
|
|
lineno = 0;
|
2015-09-09 14:33:32 +02:00
|
|
|
for (;;) {
|
2012-01-10 01:34:15 +01:00
|
|
|
vendor_in = model_in = options_in = NULL;
|
|
|
|
|
2013-12-15 23:15:54 +01:00
|
|
|
buf = fgets(buffer, MAX_BUFFER_LEN, f);
|
2019-04-28 14:28:49 +02:00
|
|
|
if (!buf)
|
2012-01-10 01:34:15 +01:00
|
|
|
break;
|
|
|
|
lineno++;
|
|
|
|
if (buf[strlen(buffer) - 1] != '\n') {
|
2013-12-24 16:39:37 +01:00
|
|
|
log_error("Config file line %d too long", lineno);
|
2012-01-10 01:34:15 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (isspace(*buf))
|
|
|
|
buf++;
|
|
|
|
|
|
|
|
/* blank or all whitespace line */
|
|
|
|
if (*buf == '\0')
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* comment line */
|
|
|
|
if (*buf == '#')
|
|
|
|
continue;
|
|
|
|
|
|
|
|
str1 = strsep(&buf, "=");
|
2013-02-12 21:47:37 +01:00
|
|
|
if (str1 && strcaseeq(str1, "VENDOR")) {
|
2012-01-10 01:34:15 +01:00
|
|
|
str1 = get_value(&buf);
|
|
|
|
if (!str1) {
|
2012-07-25 23:55:59 +02:00
|
|
|
retval = log_oom();
|
2012-01-10 01:34:15 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
vendor_in = str1;
|
|
|
|
|
|
|
|
str1 = strsep(&buf, "=");
|
2013-02-12 21:47:37 +01:00
|
|
|
if (str1 && strcaseeq(str1, "MODEL")) {
|
2012-01-10 01:34:15 +01:00
|
|
|
str1 = get_value(&buf);
|
|
|
|
if (!str1) {
|
2012-07-25 23:55:59 +02:00
|
|
|
retval = log_oom();
|
2012-01-10 01:34:15 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
model_in = str1;
|
|
|
|
str1 = strsep(&buf, "=");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-12 21:47:37 +01:00
|
|
|
if (str1 && strcaseeq(str1, "OPTIONS")) {
|
2012-01-10 01:34:15 +01:00
|
|
|
str1 = get_value(&buf);
|
|
|
|
if (!str1) {
|
2012-07-25 23:55:59 +02:00
|
|
|
retval = log_oom();
|
2012-01-10 01:34:15 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
options_in = str1;
|
|
|
|
}
|
2012-04-08 16:06:20 +02:00
|
|
|
|
2012-01-10 01:34:15 +01:00
|
|
|
/*
|
|
|
|
* Only allow: [vendor=foo[,model=bar]]options=stuff
|
|
|
|
*/
|
|
|
|
if (!options_in || (!vendor_in && model_in)) {
|
2013-12-24 16:39:37 +01:00
|
|
|
log_error("Error parsing config file line %d '%s'", lineno, buffer);
|
2012-01-10 01:34:15 +01:00
|
|
|
retval = -1;
|
|
|
|
break;
|
|
|
|
}
|
2020-10-04 11:29:23 +02:00
|
|
|
if (!vendor) {
|
2019-04-28 14:28:49 +02:00
|
|
|
if (!vendor_in)
|
2012-01-10 01:34:15 +01:00
|
|
|
break;
|
2013-12-15 23:15:54 +01:00
|
|
|
} else if (vendor_in &&
|
2018-03-16 10:29:57 +01:00
|
|
|
startswith(vendor, vendor_in) &&
|
|
|
|
(!model_in || startswith(model, model_in))) {
|
2012-01-10 01:34:15 +01:00
|
|
|
/*
|
|
|
|
* Matched vendor and optionally model.
|
|
|
|
*
|
|
|
|
* Note: a short vendor_in or model_in can
|
|
|
|
* give a partial match (that is FOO
|
|
|
|
* matches FOOBAR).
|
|
|
|
*/
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (retval == 0) {
|
|
|
|
if (vendor_in != NULL || model_in != NULL ||
|
|
|
|
options_in != NULL) {
|
|
|
|
/*
|
|
|
|
* Something matched. Allocate newargv, and store
|
|
|
|
* values found in options_in.
|
|
|
|
*/
|
|
|
|
strcpy(buffer, options_in);
|
|
|
|
c = argc_count(buffer) + 2;
|
|
|
|
*newargv = calloc(c, sizeof(**newargv));
|
2015-09-09 14:23:02 +02:00
|
|
|
if (!*newargv)
|
2012-07-25 23:55:59 +02:00
|
|
|
retval = log_oom();
|
2015-09-09 14:23:02 +02:00
|
|
|
else {
|
2012-01-10 01:34:15 +01:00
|
|
|
*argc = c;
|
|
|
|
c = 0;
|
|
|
|
/*
|
|
|
|
* argv[0] at 0 is skipped by getopt, but
|
|
|
|
* store the buffer address there for
|
|
|
|
* later freeing
|
|
|
|
*/
|
|
|
|
(*newargv)[c] = buffer;
|
|
|
|
for (c = 1; c < *argc; c++)
|
|
|
|
(*newargv)[c] = strsep(&buffer, " \t");
|
2018-06-07 23:32:07 +02:00
|
|
|
buffer = NULL;
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* No matches */
|
|
|
|
retval = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return retval;
|
2003-11-13 15:34:36 +01:00
|
|
|
}
|
|
|
|
|
2013-12-18 03:48:14 +01:00
|
|
|
static void help(void) {
|
2015-01-05 13:19:55 +01:00
|
|
|
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 --blacklisted Treat device as blacklisted\n"
|
|
|
|
" -g --whitelisted Treat device as whitelisted\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);
|
2013-12-18 03:48:14 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-08-24 05:30:12 +02:00
|
|
|
static int set_options(int argc, char **argv,
|
|
|
|
char *maj_min_dev) {
|
2012-01-10 01:34:15 +01:00
|
|
|
int option;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
2017-08-02 10:12:33 +02:00
|
|
|
while ((option = getopt_long(argc, argv, "d:f:gp:uvVxhbs:", options, NULL)) >= 0)
|
2012-01-10 01:34:15 +01:00
|
|
|
switch (option) {
|
|
|
|
case 'b':
|
2013-12-15 23:15:54 +01:00
|
|
|
all_good = false;
|
2012-01-10 01:34:15 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'd':
|
2013-12-15 23:15:54 +01:00
|
|
|
dev_specified = true;
|
2013-01-09 19:06:46 +01:00
|
|
|
strscpy(maj_min_dev, MAX_PATH_LEN, optarg);
|
2012-01-10 01:34:15 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'f':
|
2013-01-09 19:06:46 +01:00
|
|
|
strscpy(config_file, MAX_PATH_LEN, optarg);
|
2012-01-10 01:34:15 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'g':
|
2013-12-15 23:15:54 +01:00
|
|
|
all_good = true;
|
2012-01-10 01:34:15 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'h':
|
2013-12-18 03:48:14 +01:00
|
|
|
help();
|
2017-12-22 13:24:40 +01:00
|
|
|
exit(EXIT_SUCCESS);
|
2012-01-10 01:34:15 +01:00
|
|
|
|
|
|
|
case 'p':
|
2013-12-15 23:15:54 +01:00
|
|
|
if (streq(optarg, "0x80"))
|
2012-01-10 01:34:15 +01:00
|
|
|
default_page_code = PAGE_80;
|
2013-12-15 23:15:54 +01:00
|
|
|
else if (streq(optarg, "0x83"))
|
2012-01-10 01:34:15 +01:00
|
|
|
default_page_code = PAGE_83;
|
2013-12-15 23:15:54 +01:00
|
|
|
else if (streq(optarg, "pre-spc3-83"))
|
2012-01-10 01:34:15 +01:00
|
|
|
default_page_code = PAGE_83_PRE_SPC3;
|
2019-04-30 18:45:29 +02:00
|
|
|
else
|
2019-04-29 18:22:22 +02:00
|
|
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
|
|
|
"Unknown page code '%s'",
|
|
|
|
optarg);
|
2012-01-10 01:34:15 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 's':
|
|
|
|
sg_version = atoi(optarg);
|
2019-04-30 18:45:29 +02:00
|
|
|
if (sg_version < 3 || sg_version > 4)
|
2019-04-29 18:22:22 +02:00
|
|
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
|
|
|
"Unknown SG version '%s'",
|
|
|
|
optarg);
|
2012-01-10 01:34:15 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'u':
|
2013-12-15 23:15:54 +01:00
|
|
|
reformat_serial = true;
|
2012-01-10 01:34:15 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'v':
|
2014-08-11 20:13:38 +02:00
|
|
|
log_set_target(LOG_TARGET_CONSOLE);
|
|
|
|
log_set_max_level(LOG_DEBUG);
|
|
|
|
log_open();
|
2012-01-10 01:34:15 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'V':
|
2018-12-20 20:35:25 +01:00
|
|
|
printf("%s\n", GIT_VERSION);
|
2017-12-22 13:24:40 +01:00
|
|
|
exit(EXIT_SUCCESS);
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2013-12-15 23:15:54 +01:00
|
|
|
case 'x':
|
|
|
|
export = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '?':
|
|
|
|
return -1;
|
|
|
|
|
2012-01-10 01:34:15 +01:00
|
|
|
default:
|
2013-12-15 23:15:54 +01:00
|
|
|
assert_not_reached("Unknown option");
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
2013-12-15 23:15:54 +01:00
|
|
|
|
2012-01-10 01:34:15 +01:00
|
|
|
if (optind < argc && !dev_specified) {
|
2013-12-15 23:15:54 +01:00
|
|
|
dev_specified = true;
|
2013-01-09 19:06:46 +01:00
|
|
|
strscpy(maj_min_dev, MAX_PATH_LEN, argv[optind]);
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
2013-12-15 23:15:54 +01:00
|
|
|
|
2012-01-10 01:34:15 +01:00
|
|
|
return 0;
|
2003-11-13 15:34:36 +01:00
|
|
|
}
|
|
|
|
|
2018-08-24 05:30:12 +02:00
|
|
|
static int per_dev_options(struct scsi_id_device *dev_scsi, int *good_bad, int *page_code) {
|
2012-01-10 01:34:15 +01:00
|
|
|
int retval;
|
|
|
|
int newargc;
|
|
|
|
char **newargv = NULL;
|
|
|
|
int option;
|
|
|
|
|
|
|
|
*good_bad = all_good;
|
|
|
|
*page_code = default_page_code;
|
|
|
|
|
2018-08-24 05:30:12 +02:00
|
|
|
retval = get_file_options(vendor_str, model_str, &newargc, &newargv);
|
2012-01-10 01:34:15 +01:00
|
|
|
|
|
|
|
optind = 1; /* reset this global extern */
|
|
|
|
while (retval == 0) {
|
2013-12-15 23:15:54 +01:00
|
|
|
option = getopt_long(newargc, newargv, "bgp:", options, NULL);
|
2012-01-10 01:34:15 +01:00
|
|
|
if (option == -1)
|
|
|
|
break;
|
|
|
|
|
|
|
|
switch (option) {
|
|
|
|
case 'b':
|
|
|
|
*good_bad = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'g':
|
|
|
|
*good_bad = 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'p':
|
2013-02-13 18:13:22 +01:00
|
|
|
if (streq(optarg, "0x80")) {
|
2012-01-10 01:34:15 +01:00
|
|
|
*page_code = PAGE_80;
|
2013-02-13 18:13:22 +01:00
|
|
|
} else if (streq(optarg, "0x83")) {
|
2012-01-10 01:34:15 +01:00
|
|
|
*page_code = PAGE_83;
|
2013-02-13 18:13:22 +01:00
|
|
|
} else if (streq(optarg, "pre-spc3-83")) {
|
2012-01-10 01:34:15 +01:00
|
|
|
*page_code = PAGE_83_PRE_SPC3;
|
|
|
|
} else {
|
2013-12-24 16:39:37 +01:00
|
|
|
log_error("Unknown page code '%s'", optarg);
|
2012-01-10 01:34:15 +01:00
|
|
|
retval = -1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2013-12-24 16:39:37 +01:00
|
|
|
log_error("Unknown or bad option '%c' (0x%x)", option, option);
|
2012-01-10 01:34:15 +01:00
|
|
|
retval = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (newargv) {
|
|
|
|
free(newargv[0]);
|
|
|
|
free(newargv);
|
|
|
|
}
|
|
|
|
return retval;
|
2003-11-13 15:34:36 +01:00
|
|
|
}
|
|
|
|
|
2018-08-24 05:30:12 +02:00
|
|
|
static int set_inq_values(struct scsi_id_device *dev_scsi, const char *path) {
|
2012-01-10 01:34:15 +01:00
|
|
|
int retval;
|
2007-05-25 14:48:08 +02:00
|
|
|
|
2012-01-10 01:34:15 +01:00
|
|
|
dev_scsi->use_sg = sg_version;
|
2008-05-14 13:55:49 +02:00
|
|
|
|
2018-08-24 05:30:12 +02:00
|
|
|
retval = scsi_std_inquiry(dev_scsi, path);
|
2012-01-10 01:34:15 +01:00
|
|
|
if (retval)
|
|
|
|
return retval;
|
2007-05-25 14:48:08 +02:00
|
|
|
|
2020-12-14 08:59:37 +01:00
|
|
|
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));
|
2009-02-17 20:15:17 +01:00
|
|
|
|
2020-12-14 08:11:51 +01:00
|
|
|
udev_replace_whitespace(dev_scsi->vendor, vendor_str, sizeof(vendor_str)-1);
|
2020-12-14 08:26:13 +01:00
|
|
|
udev_replace_chars(vendor_str, NULL);
|
2020-12-14 08:11:51 +01:00
|
|
|
udev_replace_whitespace(dev_scsi->model, model_str, sizeof(model_str)-1);
|
2020-12-14 08:26:13 +01:00
|
|
|
udev_replace_chars(model_str, NULL);
|
2012-01-10 01:34:15 +01:00
|
|
|
set_type(dev_scsi->type, type_str, sizeof(type_str));
|
2020-12-14 08:11:51 +01:00
|
|
|
udev_replace_whitespace(dev_scsi->revision, revision_str, sizeof(revision_str)-1);
|
2020-12-14 08:26:13 +01:00
|
|
|
udev_replace_chars(revision_str, NULL);
|
2012-01-10 01:34:15 +01:00
|
|
|
return 0;
|
2007-05-25 14:48:08 +02:00
|
|
|
}
|
|
|
|
|
2003-11-13 15:34:36 +01:00
|
|
|
/*
|
|
|
|
* scsi_id: try to get an id, if one is found, printf it to stdout.
|
2008-12-02 19:23:38 +01:00
|
|
|
* returns a value passed to exit() - 0 if printed an id, else 1.
|
2003-11-13 15:34:36 +01:00
|
|
|
*/
|
2018-08-24 05:30:12 +02:00
|
|
|
static int scsi_id(char *maj_min_dev) {
|
2013-12-15 23:15:54 +01:00
|
|
|
struct scsi_id_device dev_scsi = {};
|
2012-01-10 01:34:15 +01:00
|
|
|
int good_dev;
|
|
|
|
int page_code;
|
|
|
|
int retval = 0;
|
|
|
|
|
2018-08-24 05:30:12 +02:00
|
|
|
if (set_inq_values(&dev_scsi, maj_min_dev) < 0) {
|
2012-01-10 01:34:15 +01:00
|
|
|
retval = 1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* get per device (vendor + model) options from the config file */
|
2018-08-24 05:30:12 +02:00
|
|
|
per_dev_options(&dev_scsi, &good_dev, &page_code);
|
2012-01-10 01:34:15 +01:00
|
|
|
if (!good_dev) {
|
|
|
|
retval = 1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* read serial number from mode pages (no values for optical drives) */
|
2018-08-24 05:30:12 +02:00
|
|
|
scsi_get_serial(&dev_scsi, maj_min_dev, page_code, MAX_SERIAL_LEN);
|
2012-01-10 01:34:15 +01:00
|
|
|
|
|
|
|
if (export) {
|
|
|
|
char serial_str[MAX_SERIAL_LEN];
|
|
|
|
|
|
|
|
printf("ID_SCSI=1\n");
|
|
|
|
printf("ID_VENDOR=%s\n", vendor_str);
|
|
|
|
printf("ID_VENDOR_ENC=%s\n", vendor_enc_str);
|
|
|
|
printf("ID_MODEL=%s\n", model_str);
|
|
|
|
printf("ID_MODEL_ENC=%s\n", model_enc_str);
|
|
|
|
printf("ID_REVISION=%s\n", revision_str);
|
|
|
|
printf("ID_TYPE=%s\n", type_str);
|
|
|
|
if (dev_scsi.serial[0] != '\0') {
|
2020-12-14 08:11:51 +01:00
|
|
|
udev_replace_whitespace(dev_scsi.serial, serial_str, sizeof(serial_str)-1);
|
2020-12-14 08:26:13 +01:00
|
|
|
udev_replace_chars(serial_str, NULL);
|
2012-01-10 01:34:15 +01:00
|
|
|
printf("ID_SERIAL=%s\n", serial_str);
|
2020-12-14 08:11:51 +01:00
|
|
|
udev_replace_whitespace(dev_scsi.serial_short, serial_str, sizeof(serial_str)-1);
|
2020-12-14 08:26:13 +01:00
|
|
|
udev_replace_chars(serial_str, NULL);
|
2012-01-10 01:34:15 +01:00
|
|
|
printf("ID_SERIAL_SHORT=%s\n", serial_str);
|
|
|
|
}
|
|
|
|
if (dev_scsi.wwn[0] != '\0') {
|
|
|
|
printf("ID_WWN=0x%s\n", dev_scsi.wwn);
|
|
|
|
if (dev_scsi.wwn_vendor_extension[0] != '\0') {
|
|
|
|
printf("ID_WWN_VENDOR_EXTENSION=0x%s\n", dev_scsi.wwn_vendor_extension);
|
|
|
|
printf("ID_WWN_WITH_EXTENSION=0x%s%s\n", dev_scsi.wwn, dev_scsi.wwn_vendor_extension);
|
2015-09-09 14:23:02 +02:00
|
|
|
} else
|
2012-01-10 01:34:15 +01:00
|
|
|
printf("ID_WWN_WITH_EXTENSION=0x%s\n", dev_scsi.wwn);
|
|
|
|
}
|
2015-09-09 14:23:02 +02:00
|
|
|
if (dev_scsi.tgpt_group[0] != '\0')
|
2012-01-10 01:34:15 +01:00
|
|
|
printf("ID_TARGET_PORT=%s\n", dev_scsi.tgpt_group);
|
2015-09-09 14:23:02 +02:00
|
|
|
if (dev_scsi.unit_serial_number[0] != '\0')
|
2012-01-10 01:34:15 +01:00
|
|
|
printf("ID_SCSI_SERIAL=%s\n", dev_scsi.unit_serial_number);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dev_scsi.serial[0] == '\0') {
|
|
|
|
retval = 1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (reformat_serial) {
|
|
|
|
char serial_str[MAX_SERIAL_LEN];
|
|
|
|
|
2020-12-14 08:11:51 +01:00
|
|
|
udev_replace_whitespace(dev_scsi.serial, serial_str, sizeof(serial_str)-1);
|
2020-12-14 08:26:13 +01:00
|
|
|
udev_replace_chars(serial_str, NULL);
|
2012-01-10 01:34:15 +01:00
|
|
|
printf("%s\n", serial_str);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("%s\n", dev_scsi.serial);
|
2008-12-02 19:23:38 +01:00
|
|
|
out:
|
2012-01-10 01:34:15 +01:00
|
|
|
return retval;
|
2003-11-13 15:34:36 +01:00
|
|
|
}
|
|
|
|
|
tree-wide: drop redundant _cleanup_ macros (#8810)
This drops a good number of type-specific _cleanup_ macros, and patches
all users to just use the generic ones.
In most recent code we abstained from defining type-specific macros, and
this basically removes all those added already, with the exception of
the really low-level ones.
Having explicit macros for this is not too useful, as the expression
without the extra macro is generally just 2ch wider. We should generally
emphesize generic code, unless there are really good reasons for
specific code, hence let's follow this in this case too.
Note that _cleanup_free_ and similar really low-level, libc'ish, Linux
API'ish macros continue to be defined, only the really high-level OO
ones are dropped. From now on this should really be the rule: for really
low-level stuff, such as memory allocation, fd handling and so one, go
ahead and define explicit per-type macros, but for high-level, specific
program code, just use the generic _cleanup_() macro directly, in order
to keep things simple and as readable as possible for the uninitiated.
Note that before this patch some of the APIs (notable libudev ones) were
already used with the high-level macros at some places and with the
generic _cleanup_ macro at others. With this patch we hence unify on the
latter.
2018-04-25 12:31:45 +02:00
|
|
|
int main(int argc, char **argv) {
|
2012-01-10 01:34:15 +01:00
|
|
|
int retval = 0;
|
|
|
|
char maj_min_dev[MAX_PATH_LEN];
|
|
|
|
int newargc;
|
2013-12-15 23:15:54 +01:00
|
|
|
char **newargv = NULL;
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2017-02-23 09:16:44 +01:00
|
|
|
log_set_target(LOG_TARGET_AUTO);
|
|
|
|
udev_parse_config();
|
2014-08-11 20:13:38 +02:00
|
|
|
log_parse_environment();
|
|
|
|
log_open();
|
|
|
|
|
2012-01-10 01:34:15 +01:00
|
|
|
/*
|
|
|
|
* Get config file options.
|
|
|
|
*/
|
2018-08-24 05:30:12 +02:00
|
|
|
retval = get_file_options(NULL, NULL, &newargc, &newargv);
|
2012-01-10 01:34:15 +01:00
|
|
|
if (retval < 0) {
|
|
|
|
retval = 1;
|
|
|
|
goto exit;
|
|
|
|
}
|
2013-12-15 23:15:54 +01:00
|
|
|
if (retval == 0) {
|
|
|
|
assert(newargv);
|
|
|
|
|
2018-08-24 05:30:12 +02:00
|
|
|
if (set_options(newargc, newargv, maj_min_dev) < 0) {
|
2012-01-10 01:34:15 +01:00
|
|
|
retval = 2;
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get command line options (overriding any config file settings).
|
|
|
|
*/
|
2018-08-24 05:30:12 +02:00
|
|
|
if (set_options(argc, argv, maj_min_dev) < 0)
|
2017-12-22 13:24:40 +01:00
|
|
|
exit(EXIT_FAILURE);
|
2012-01-10 01:34:15 +01:00
|
|
|
|
|
|
|
if (!dev_specified) {
|
2015-01-05 13:19:55 +01:00
|
|
|
log_error("No device specified.");
|
2012-01-10 01:34:15 +01:00
|
|
|
retval = 1;
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
2018-08-24 05:30:12 +02:00
|
|
|
retval = scsi_id(maj_min_dev);
|
2006-01-09 21:18:00 +01:00
|
|
|
|
|
|
|
exit:
|
2013-12-15 23:15:54 +01:00
|
|
|
if (newargv) {
|
|
|
|
free(newargv[0]);
|
|
|
|
free(newargv);
|
|
|
|
}
|
2012-04-08 16:06:20 +02:00
|
|
|
log_close();
|
2012-01-10 01:34:15 +01:00
|
|
|
return retval;
|
2003-11-13 15:34:36 +01:00
|
|
|
}
|