| File: | build-scan/../src/udev/scsi_id/scsi_id.c |
| Warning: | line 290, column 34 Potential leak of memory pointed to by 'buffer' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* SPDX-License-Identifier: GPL-2.0+ */ | ||||
| 2 | /* | ||||
| 3 | * Copyright © IBM Corp. 2003 | ||||
| 4 | * Copyright © SUSE Linux Products GmbH, 2006 | ||||
| 5 | * | ||||
| 6 | */ | ||||
| 7 | |||||
| 8 | #include <ctype.h> | ||||
| 9 | #include <errno(*__errno_location ()).h> | ||||
| 10 | #include <fcntl.h> | ||||
| 11 | #include <getopt.h> | ||||
| 12 | #include <signal.h> | ||||
| 13 | #include <stdarg.h> | ||||
| 14 | #include <stdbool.h> | ||||
| 15 | #include <stdio.h> | ||||
| 16 | #include <stdlib.h> | ||||
| 17 | #include <string.h> | ||||
| 18 | #include <sys/stat.h> | ||||
| 19 | #include <unistd.h> | ||||
| 20 | |||||
| 21 | #include "libudev.h" | ||||
| 22 | |||||
| 23 | #include "fd-util.h" | ||||
| 24 | #include "libudev-private.h" | ||||
| 25 | #include "scsi_id.h" | ||||
| 26 | #include "string-util.h" | ||||
| 27 | #include "udev-util.h" | ||||
| 28 | |||||
| 29 | static const struct option options[] = { | ||||
| 30 | { "device", required_argument1, NULL((void*)0), 'd' }, | ||||
| 31 | { "config", required_argument1, NULL((void*)0), 'f' }, | ||||
| 32 | { "page", required_argument1, NULL((void*)0), 'p' }, | ||||
| 33 | { "blacklisted", no_argument0, NULL((void*)0), 'b' }, | ||||
| 34 | { "whitelisted", no_argument0, NULL((void*)0), 'g' }, | ||||
| 35 | { "replace-whitespace", no_argument0, NULL((void*)0), 'u' }, | ||||
| 36 | { "sg-version", required_argument1, NULL((void*)0), 's' }, | ||||
| 37 | { "verbose", no_argument0, NULL((void*)0), 'v' }, | ||||
| 38 | { "version", no_argument0, NULL((void*)0), 'V' }, /* don't advertise -V */ | ||||
| 39 | { "export", no_argument0, NULL((void*)0), 'x' }, | ||||
| 40 | { "help", no_argument0, NULL((void*)0), 'h' }, | ||||
| 41 | {} | ||||
| 42 | }; | ||||
| 43 | |||||
| 44 | static bool_Bool all_good = false0; | ||||
| 45 | static bool_Bool dev_specified = false0; | ||||
| 46 | static char config_file[MAX_PATH_LEN512] = "/etc/scsi_id.config"; | ||||
| 47 | static enum page_code default_page_code = PAGE_UNSPECIFIED; | ||||
| 48 | static int sg_version = 4; | ||||
| 49 | static bool_Bool reformat_serial = false0; | ||||
| 50 | static bool_Bool export = false0; | ||||
| 51 | static char vendor_str[64]; | ||||
| 52 | static char model_str[64]; | ||||
| 53 | static char vendor_enc_str[256]; | ||||
| 54 | static char model_enc_str[256]; | ||||
| 55 | static char revision_str[16]; | ||||
| 56 | static char type_str[16]; | ||||
| 57 | |||||
| 58 | static void set_type(const char *from, char *to, size_t len) | ||||
| 59 | { | ||||
| 60 | int type_num; | ||||
| 61 | char *eptr; | ||||
| 62 | const char *type = "generic"; | ||||
| 63 | |||||
| 64 | type_num = strtoul(from, &eptr, 0); | ||||
| 65 | if (eptr != from) { | ||||
| 66 | switch (type_num) { | ||||
| 67 | case 0: | ||||
| 68 | type = "disk"; | ||||
| 69 | break; | ||||
| 70 | case 1: | ||||
| 71 | type = "tape"; | ||||
| 72 | break; | ||||
| 73 | case 4: | ||||
| 74 | type = "optical"; | ||||
| 75 | break; | ||||
| 76 | case 5: | ||||
| 77 | type = "cd"; | ||||
| 78 | break; | ||||
| 79 | case 7: | ||||
| 80 | type = "optical"; | ||||
| 81 | break; | ||||
| 82 | case 0xe: | ||||
| 83 | type = "disk"; | ||||
| 84 | break; | ||||
| 85 | case 0xf: | ||||
| 86 | type = "optical"; | ||||
| 87 | break; | ||||
| 88 | default: | ||||
| 89 | break; | ||||
| 90 | } | ||||
| 91 | } | ||||
| 92 | strscpy(to, len, type); | ||||
| 93 | } | ||||
| 94 | |||||
| 95 | /* | ||||
| 96 | * get_value: | ||||
| 97 | * | ||||
| 98 | * buf points to an '=' followed by a quoted string ("foo") or a string ending | ||||
| 99 | * with a space or ','. | ||||
| 100 | * | ||||
| 101 | * Return a pointer to the NUL terminated string, returns NULL if no | ||||
| 102 | * matches. | ||||
| 103 | */ | ||||
| 104 | static char *get_value(char **buffer) | ||||
| 105 | { | ||||
| 106 | static const char *quote_string = "\"\n"; | ||||
| 107 | static const char *comma_string = ",\n"; | ||||
| 108 | char *val; | ||||
| 109 | const char *end; | ||||
| 110 | |||||
| 111 | if (**buffer == '"') { | ||||
| 112 | /* | ||||
| 113 | * skip leading quote, terminate when quote seen | ||||
| 114 | */ | ||||
| 115 | (*buffer)++; | ||||
| 116 | end = quote_string; | ||||
| 117 | } else { | ||||
| 118 | end = comma_string; | ||||
| 119 | } | ||||
| 120 | val = strsep(buffer, end); | ||||
| 121 | if (val && end == quote_string) | ||||
| 122 | /* | ||||
| 123 | * skip trailing quote | ||||
| 124 | */ | ||||
| 125 | (*buffer)++; | ||||
| 126 | |||||
| 127 | while (isspace(**buffer)((*__ctype_b_loc ())[(int) ((**buffer))] & (unsigned short int) _ISspace)) | ||||
| 128 | (*buffer)++; | ||||
| 129 | |||||
| 130 | return val; | ||||
| 131 | } | ||||
| 132 | |||||
| 133 | static int argc_count(char *opts) | ||||
| 134 | { | ||||
| 135 | int i = 0; | ||||
| 136 | while (*opts != '\0') | ||||
| 137 | if (*opts++ == ' ') | ||||
| 138 | i++; | ||||
| 139 | return i; | ||||
| 140 | } | ||||
| 141 | |||||
| 142 | /* | ||||
| 143 | * get_file_options: | ||||
| 144 | * | ||||
| 145 | * If vendor == NULL, find a line in the config file with only "OPTIONS="; | ||||
| 146 | * if vendor and model are set find the first OPTIONS line in the config | ||||
| 147 | * file that matches. Set argc and argv to match the OPTIONS string. | ||||
| 148 | * | ||||
| 149 | * vendor and model can end in '\n'. | ||||
| 150 | */ | ||||
| 151 | static int get_file_options(struct udev *udev, | ||||
| 152 | const char *vendor, const char *model, | ||||
| 153 | int *argc, char ***newargv) | ||||
| 154 | { | ||||
| 155 | _cleanup_free___attribute__((cleanup(freep))) char *buffer = NULL((void*)0); | ||||
| 156 | _cleanup_fclose___attribute__((cleanup(fclosep))) FILE *f; | ||||
| 157 | char *buf; | ||||
| 158 | char *str1; | ||||
| 159 | char *vendor_in, *model_in, *options_in; /* read in from file */ | ||||
| 160 | int lineno; | ||||
| 161 | int c; | ||||
| 162 | int retval = 0; | ||||
| 163 | |||||
| 164 | f = fopen(config_file, "re"); | ||||
| 165 | if (f == NULL((void*)0)) { | ||||
| 166 | if (errno(*__errno_location ()) == ENOENT2) | ||||
| 167 | return 1; | ||||
| 168 | else { | ||||
| 169 | log_error_errno(errno, "can't open %s: %m", config_file)({ int _level = ((3)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_UDEV); (log_get_max_level_realm(_realm) >= ( (_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/udev/scsi_id/scsi_id.c", 169, __func__ , "can't open %s: %m", config_file) : -abs(_e); }); | ||||
| 170 | return -1; | ||||
| 171 | } | ||||
| 172 | } | ||||
| 173 | |||||
| 174 | /* | ||||
| 175 | * Allocate a buffer rather than put it on the stack so we can | ||||
| 176 | * keep it around to parse any options (any allocated newargv | ||||
| 177 | * points into this buffer for its strings). | ||||
| 178 | */ | ||||
| 179 | buffer = malloc(MAX_BUFFER_LEN256); | ||||
| 180 | if (!buffer) | ||||
| 181 | return log_oom()log_oom_internal(LOG_REALM_UDEV, "../src/udev/scsi_id/scsi_id.c" , 181, __func__); | ||||
| 182 | |||||
| 183 | *newargv = NULL((void*)0); | ||||
| 184 | lineno = 0; | ||||
| 185 | for (;;) { | ||||
| 186 | vendor_in = model_in = options_in = NULL((void*)0); | ||||
| 187 | |||||
| 188 | buf = fgets(buffer, MAX_BUFFER_LEN256, f); | ||||
| 189 | if (buf == NULL((void*)0)) | ||||
| 190 | break; | ||||
| 191 | lineno++; | ||||
| 192 | if (buf[strlen(buffer) - 1] != '\n') { | ||||
| 193 | log_error("Config file line %d too long", lineno)({ int _level = (((3))), _e = ((0)), _realm = (LOG_REALM_UDEV ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/udev/scsi_id/scsi_id.c", 193, __func__, "Config file line %d too long" , lineno) : -abs(_e); }); | ||||
| 194 | break; | ||||
| 195 | } | ||||
| 196 | |||||
| 197 | while (isspace(*buf)((*__ctype_b_loc ())[(int) ((*buf))] & (unsigned short int ) _ISspace)) | ||||
| 198 | buf++; | ||||
| 199 | |||||
| 200 | /* blank or all whitespace line */ | ||||
| 201 | if (*buf == '\0') | ||||
| 202 | continue; | ||||
| 203 | |||||
| 204 | /* comment line */ | ||||
| 205 | if (*buf == '#') | ||||
| 206 | continue; | ||||
| 207 | |||||
| 208 | str1 = strsep(&buf, "="); | ||||
| 209 | if (str1 && strcaseeq(str1, "VENDOR")(strcasecmp((str1),("VENDOR")) == 0)) { | ||||
| 210 | str1 = get_value(&buf); | ||||
| 211 | if (!str1) { | ||||
| 212 | retval = log_oom()log_oom_internal(LOG_REALM_UDEV, "../src/udev/scsi_id/scsi_id.c" , 212, __func__); | ||||
| 213 | break; | ||||
| 214 | } | ||||
| 215 | vendor_in = str1; | ||||
| 216 | |||||
| 217 | str1 = strsep(&buf, "="); | ||||
| 218 | if (str1 && strcaseeq(str1, "MODEL")(strcasecmp((str1),("MODEL")) == 0)) { | ||||
| 219 | str1 = get_value(&buf); | ||||
| 220 | if (!str1) { | ||||
| 221 | retval = log_oom()log_oom_internal(LOG_REALM_UDEV, "../src/udev/scsi_id/scsi_id.c" , 221, __func__); | ||||
| 222 | break; | ||||
| 223 | } | ||||
| 224 | model_in = str1; | ||||
| 225 | str1 = strsep(&buf, "="); | ||||
| 226 | } | ||||
| 227 | } | ||||
| 228 | |||||
| 229 | if (str1 && strcaseeq(str1, "OPTIONS")(strcasecmp((str1),("OPTIONS")) == 0)) { | ||||
| 230 | str1 = get_value(&buf); | ||||
| 231 | if (!str1) { | ||||
| 232 | retval = log_oom()log_oom_internal(LOG_REALM_UDEV, "../src/udev/scsi_id/scsi_id.c" , 232, __func__); | ||||
| 233 | break; | ||||
| 234 | } | ||||
| 235 | options_in = str1; | ||||
| 236 | } | ||||
| 237 | |||||
| 238 | /* | ||||
| 239 | * Only allow: [vendor=foo[,model=bar]]options=stuff | ||||
| 240 | */ | ||||
| 241 | if (!options_in || (!vendor_in && model_in)) { | ||||
| 242 | log_error("Error parsing config file line %d '%s'", lineno, buffer)({ int _level = (((3))), _e = ((0)), _realm = (LOG_REALM_UDEV ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/udev/scsi_id/scsi_id.c", 242, __func__, "Error parsing config file line %d '%s'" , lineno, buffer) : -abs(_e); }); | ||||
| 243 | retval = -1; | ||||
| 244 | break; | ||||
| 245 | } | ||||
| 246 | if (vendor == NULL((void*)0)) { | ||||
| 247 | if (vendor_in == NULL((void*)0)) | ||||
| 248 | break; | ||||
| 249 | } else if (vendor_in && | ||||
| 250 | startswith(vendor, vendor_in) && | ||||
| 251 | (!model_in || startswith(model, model_in))) { | ||||
| 252 | /* | ||||
| 253 | * Matched vendor and optionally model. | ||||
| 254 | * | ||||
| 255 | * Note: a short vendor_in or model_in can | ||||
| 256 | * give a partial match (that is FOO | ||||
| 257 | * matches FOOBAR). | ||||
| 258 | */ | ||||
| 259 | break; | ||||
| 260 | } | ||||
| 261 | } | ||||
| 262 | |||||
| 263 | if (retval
| ||||
| 264 | if (vendor_in
| ||||
| 265 | options_in
| ||||
| 266 | /* | ||||
| 267 | * Something matched. Allocate newargv, and store | ||||
| 268 | * values found in options_in. | ||||
| 269 | */ | ||||
| 270 | strcpy(buffer, options_in); | ||||
| 271 | c = argc_count(buffer) + 2; | ||||
| 272 | *newargv = calloc(c, sizeof(**newargv)); | ||||
| 273 | if (!*newargv) | ||||
| 274 | retval = log_oom()log_oom_internal(LOG_REALM_UDEV, "../src/udev/scsi_id/scsi_id.c" , 274, __func__); | ||||
| 275 | else { | ||||
| 276 | *argc = c; | ||||
| 277 | c = 0; | ||||
| 278 | /* | ||||
| 279 | * argv[0] at 0 is skipped by getopt, but | ||||
| 280 | * store the buffer address there for | ||||
| 281 | * later freeing | ||||
| 282 | */ | ||||
| 283 | (*newargv)[c] = buffer; | ||||
| 284 | for (c = 1; c < *argc; c++) | ||||
| 285 | (*newargv)[c] = strsep(&buffer, " \t"); | ||||
| 286 | buffer = NULL((void*)0); | ||||
| 287 | } | ||||
| 288 | } else { | ||||
| 289 | /* No matches */ | ||||
| 290 | retval = 1; | ||||
| |||||
| 291 | } | ||||
| 292 | } | ||||
| 293 | return retval; | ||||
| 294 | } | ||||
| 295 | |||||
| 296 | static void help(void) { | ||||
| 297 | printf("Usage: %s [OPTION...] DEVICE\n\n" | ||||
| 298 | "SCSI device identification.\n\n" | ||||
| 299 | " -h --help Print this message\n" | ||||
| 300 | " --version Print version of the program\n\n" | ||||
| 301 | " -d --device= Device node for SG_IO commands\n" | ||||
| 302 | " -f --config= Location of config file\n" | ||||
| 303 | " -p --page=0x80|0x83|pre-spc3-83 SCSI page (0x80, 0x83, pre-spc3-83)\n" | ||||
| 304 | " -s --sg-version=3|4 Use SGv3 or SGv4\n" | ||||
| 305 | " -b --blacklisted Treat device as blacklisted\n" | ||||
| 306 | " -g --whitelisted Treat device as whitelisted\n" | ||||
| 307 | " -u --replace-whitespace Replace all whitespace by underscores\n" | ||||
| 308 | " -v --verbose Verbose logging\n" | ||||
| 309 | " -x --export Print values as environment keys\n" | ||||
| 310 | , program_invocation_short_name); | ||||
| 311 | |||||
| 312 | } | ||||
| 313 | |||||
| 314 | static int set_options(struct udev *udev, | ||||
| 315 | int argc, char **argv, | ||||
| 316 | char *maj_min_dev) | ||||
| 317 | { | ||||
| 318 | int option; | ||||
| 319 | |||||
| 320 | /* | ||||
| 321 | * optind is a global extern used by getopt. Since we can call | ||||
| 322 | * set_options twice (once for command line, and once for config | ||||
| 323 | * file) we have to reset this back to 1. | ||||
| 324 | */ | ||||
| 325 | optind = 1; | ||||
| 326 | while ((option = getopt_long(argc, argv, "d:f:gp:uvVxhbs:", options, NULL((void*)0))) >= 0) | ||||
| 327 | switch (option) { | ||||
| 328 | case 'b': | ||||
| 329 | all_good = false0; | ||||
| 330 | break; | ||||
| 331 | |||||
| 332 | case 'd': | ||||
| 333 | dev_specified = true1; | ||||
| 334 | strscpy(maj_min_dev, MAX_PATH_LEN512, optarg); | ||||
| 335 | break; | ||||
| 336 | |||||
| 337 | case 'f': | ||||
| 338 | strscpy(config_file, MAX_PATH_LEN512, optarg); | ||||
| 339 | break; | ||||
| 340 | |||||
| 341 | case 'g': | ||||
| 342 | all_good = true1; | ||||
| 343 | break; | ||||
| 344 | |||||
| 345 | case 'h': | ||||
| 346 | help(); | ||||
| 347 | exit(EXIT_SUCCESS0); | ||||
| 348 | |||||
| 349 | case 'p': | ||||
| 350 | if (streq(optarg, "0x80")(strcmp((optarg),("0x80")) == 0)) | ||||
| 351 | default_page_code = PAGE_80; | ||||
| 352 | else if (streq(optarg, "0x83")(strcmp((optarg),("0x83")) == 0)) | ||||
| 353 | default_page_code = PAGE_83; | ||||
| 354 | else if (streq(optarg, "pre-spc3-83")(strcmp((optarg),("pre-spc3-83")) == 0)) | ||||
| 355 | default_page_code = PAGE_83_PRE_SPC3; | ||||
| 356 | else { | ||||
| 357 | log_error("Unknown page code '%s'", optarg)({ int _level = (((3))), _e = ((0)), _realm = (LOG_REALM_UDEV ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/udev/scsi_id/scsi_id.c", 357, __func__, "Unknown page code '%s'" , optarg) : -abs(_e); }); | ||||
| 358 | return -1; | ||||
| 359 | } | ||||
| 360 | break; | ||||
| 361 | |||||
| 362 | case 's': | ||||
| 363 | sg_version = atoi(optarg); | ||||
| 364 | if (sg_version < 3 || sg_version > 4) { | ||||
| 365 | log_error("Unknown SG version '%s'", optarg)({ int _level = (((3))), _e = ((0)), _realm = (LOG_REALM_UDEV ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/udev/scsi_id/scsi_id.c", 365, __func__, "Unknown SG version '%s'" , optarg) : -abs(_e); }); | ||||
| 366 | return -1; | ||||
| 367 | } | ||||
| 368 | break; | ||||
| 369 | |||||
| 370 | case 'u': | ||||
| 371 | reformat_serial = true1; | ||||
| 372 | break; | ||||
| 373 | |||||
| 374 | case 'v': | ||||
| 375 | log_set_target(LOG_TARGET_CONSOLE); | ||||
| 376 | log_set_max_level(LOG_DEBUG)log_set_max_level_realm(LOG_REALM_UDEV, (7)); | ||||
| 377 | log_open(); | ||||
| 378 | break; | ||||
| 379 | |||||
| 380 | case 'V': | ||||
| 381 | printf("%s\n", PACKAGE_VERSION"239"); | ||||
| 382 | exit(EXIT_SUCCESS0); | ||||
| 383 | |||||
| 384 | case 'x': | ||||
| 385 | export = true1; | ||||
| 386 | break; | ||||
| 387 | |||||
| 388 | case '?': | ||||
| 389 | return -1; | ||||
| 390 | |||||
| 391 | default: | ||||
| 392 | assert_not_reached("Unknown option")do { log_assert_failed_unreachable_realm(LOG_REALM_UDEV, ("Unknown option" ), "../src/udev/scsi_id/scsi_id.c", 392, __PRETTY_FUNCTION__) ; } while (0); | ||||
| 393 | } | ||||
| 394 | |||||
| 395 | if (optind < argc && !dev_specified) { | ||||
| 396 | dev_specified = true1; | ||||
| 397 | strscpy(maj_min_dev, MAX_PATH_LEN512, argv[optind]); | ||||
| 398 | } | ||||
| 399 | |||||
| 400 | return 0; | ||||
| 401 | } | ||||
| 402 | |||||
| 403 | static int per_dev_options(struct udev *udev, | ||||
| 404 | struct scsi_id_device *dev_scsi, int *good_bad, int *page_code) | ||||
| 405 | { | ||||
| 406 | int retval; | ||||
| 407 | int newargc; | ||||
| 408 | char **newargv = NULL((void*)0); | ||||
| 409 | int option; | ||||
| 410 | |||||
| 411 | *good_bad = all_good; | ||||
| 412 | *page_code = default_page_code; | ||||
| 413 | |||||
| 414 | retval = get_file_options(udev, vendor_str, model_str, &newargc, &newargv); | ||||
| 415 | |||||
| 416 | optind = 1; /* reset this global extern */ | ||||
| 417 | while (retval == 0) { | ||||
| 418 | option = getopt_long(newargc, newargv, "bgp:", options, NULL((void*)0)); | ||||
| 419 | if (option == -1) | ||||
| 420 | break; | ||||
| 421 | |||||
| 422 | switch (option) { | ||||
| 423 | case 'b': | ||||
| 424 | *good_bad = 0; | ||||
| 425 | break; | ||||
| 426 | |||||
| 427 | case 'g': | ||||
| 428 | *good_bad = 1; | ||||
| 429 | break; | ||||
| 430 | |||||
| 431 | case 'p': | ||||
| 432 | if (streq(optarg, "0x80")(strcmp((optarg),("0x80")) == 0)) { | ||||
| 433 | *page_code = PAGE_80; | ||||
| 434 | } else if (streq(optarg, "0x83")(strcmp((optarg),("0x83")) == 0)) { | ||||
| 435 | *page_code = PAGE_83; | ||||
| 436 | } else if (streq(optarg, "pre-spc3-83")(strcmp((optarg),("pre-spc3-83")) == 0)) { | ||||
| 437 | *page_code = PAGE_83_PRE_SPC3; | ||||
| 438 | } else { | ||||
| 439 | log_error("Unknown page code '%s'", optarg)({ int _level = (((3))), _e = ((0)), _realm = (LOG_REALM_UDEV ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/udev/scsi_id/scsi_id.c", 439, __func__, "Unknown page code '%s'" , optarg) : -abs(_e); }); | ||||
| 440 | retval = -1; | ||||
| 441 | } | ||||
| 442 | break; | ||||
| 443 | |||||
| 444 | default: | ||||
| 445 | log_error("Unknown or bad option '%c' (0x%x)", option, option)({ int _level = (((3))), _e = ((0)), _realm = (LOG_REALM_UDEV ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/udev/scsi_id/scsi_id.c", 445, __func__, "Unknown or bad option '%c' (0x%x)" , option, option) : -abs(_e); }); | ||||
| 446 | retval = -1; | ||||
| 447 | break; | ||||
| 448 | } | ||||
| 449 | } | ||||
| 450 | |||||
| 451 | if (newargv) { | ||||
| 452 | free(newargv[0]); | ||||
| 453 | free(newargv); | ||||
| 454 | } | ||||
| 455 | return retval; | ||||
| 456 | } | ||||
| 457 | |||||
| 458 | static int set_inq_values(struct udev *udev, struct scsi_id_device *dev_scsi, const char *path) | ||||
| 459 | { | ||||
| 460 | int retval; | ||||
| 461 | |||||
| 462 | dev_scsi->use_sg = sg_version; | ||||
| 463 | |||||
| 464 | retval = scsi_std_inquiry(udev, dev_scsi, path); | ||||
| 465 | if (retval) | ||||
| 466 | return retval; | ||||
| 467 | |||||
| 468 | udev_util_encode_string(dev_scsi->vendor, vendor_enc_str, sizeof(vendor_enc_str)); | ||||
| 469 | udev_util_encode_string(dev_scsi->model, model_enc_str, sizeof(model_enc_str)); | ||||
| 470 | |||||
| 471 | util_replace_whitespace(dev_scsi->vendor, vendor_str, sizeof(vendor_str)); | ||||
| 472 | util_replace_chars(vendor_str, NULL((void*)0)); | ||||
| 473 | util_replace_whitespace(dev_scsi->model, model_str, sizeof(model_str)); | ||||
| 474 | util_replace_chars(model_str, NULL((void*)0)); | ||||
| 475 | set_type(dev_scsi->type, type_str, sizeof(type_str)); | ||||
| 476 | util_replace_whitespace(dev_scsi->revision, revision_str, sizeof(revision_str)); | ||||
| 477 | util_replace_chars(revision_str, NULL((void*)0)); | ||||
| 478 | return 0; | ||||
| 479 | } | ||||
| 480 | |||||
| 481 | /* | ||||
| 482 | * scsi_id: try to get an id, if one is found, printf it to stdout. | ||||
| 483 | * returns a value passed to exit() - 0 if printed an id, else 1. | ||||
| 484 | */ | ||||
| 485 | static int scsi_id(struct udev *udev, char *maj_min_dev) | ||||
| 486 | { | ||||
| 487 | struct scsi_id_device dev_scsi = {}; | ||||
| 488 | int good_dev; | ||||
| 489 | int page_code; | ||||
| 490 | int retval = 0; | ||||
| 491 | |||||
| 492 | if (set_inq_values(udev, &dev_scsi, maj_min_dev) < 0) { | ||||
| 493 | retval = 1; | ||||
| 494 | goto out; | ||||
| 495 | } | ||||
| 496 | |||||
| 497 | /* get per device (vendor + model) options from the config file */ | ||||
| 498 | per_dev_options(udev, &dev_scsi, &good_dev, &page_code); | ||||
| 499 | if (!good_dev) { | ||||
| 500 | retval = 1; | ||||
| 501 | goto out; | ||||
| 502 | } | ||||
| 503 | |||||
| 504 | /* read serial number from mode pages (no values for optical drives) */ | ||||
| 505 | scsi_get_serial(udev, &dev_scsi, maj_min_dev, page_code, MAX_SERIAL_LEN256); | ||||
| 506 | |||||
| 507 | if (export) { | ||||
| 508 | char serial_str[MAX_SERIAL_LEN256]; | ||||
| 509 | |||||
| 510 | printf("ID_SCSI=1\n"); | ||||
| 511 | printf("ID_VENDOR=%s\n", vendor_str); | ||||
| 512 | printf("ID_VENDOR_ENC=%s\n", vendor_enc_str); | ||||
| 513 | printf("ID_MODEL=%s\n", model_str); | ||||
| 514 | printf("ID_MODEL_ENC=%s\n", model_enc_str); | ||||
| 515 | printf("ID_REVISION=%s\n", revision_str); | ||||
| 516 | printf("ID_TYPE=%s\n", type_str); | ||||
| 517 | if (dev_scsi.serial[0] != '\0') { | ||||
| 518 | util_replace_whitespace(dev_scsi.serial, serial_str, sizeof(serial_str)); | ||||
| 519 | util_replace_chars(serial_str, NULL((void*)0)); | ||||
| 520 | printf("ID_SERIAL=%s\n", serial_str); | ||||
| 521 | util_replace_whitespace(dev_scsi.serial_short, serial_str, sizeof(serial_str)); | ||||
| 522 | util_replace_chars(serial_str, NULL((void*)0)); | ||||
| 523 | printf("ID_SERIAL_SHORT=%s\n", serial_str); | ||||
| 524 | } | ||||
| 525 | if (dev_scsi.wwn[0] != '\0') { | ||||
| 526 | printf("ID_WWN=0x%s\n", dev_scsi.wwn); | ||||
| 527 | if (dev_scsi.wwn_vendor_extension[0] != '\0') { | ||||
| 528 | printf("ID_WWN_VENDOR_EXTENSION=0x%s\n", dev_scsi.wwn_vendor_extension); | ||||
| 529 | printf("ID_WWN_WITH_EXTENSION=0x%s%s\n", dev_scsi.wwn, dev_scsi.wwn_vendor_extension); | ||||
| 530 | } else | ||||
| 531 | printf("ID_WWN_WITH_EXTENSION=0x%s\n", dev_scsi.wwn); | ||||
| 532 | } | ||||
| 533 | if (dev_scsi.tgpt_group[0] != '\0') | ||||
| 534 | printf("ID_TARGET_PORT=%s\n", dev_scsi.tgpt_group); | ||||
| 535 | if (dev_scsi.unit_serial_number[0] != '\0') | ||||
| 536 | printf("ID_SCSI_SERIAL=%s\n", dev_scsi.unit_serial_number); | ||||
| 537 | goto out; | ||||
| 538 | } | ||||
| 539 | |||||
| 540 | if (dev_scsi.serial[0] == '\0') { | ||||
| 541 | retval = 1; | ||||
| 542 | goto out; | ||||
| 543 | } | ||||
| 544 | |||||
| 545 | if (reformat_serial) { | ||||
| 546 | char serial_str[MAX_SERIAL_LEN256]; | ||||
| 547 | |||||
| 548 | util_replace_whitespace(dev_scsi.serial, serial_str, sizeof(serial_str)); | ||||
| 549 | util_replace_chars(serial_str, NULL((void*)0)); | ||||
| 550 | printf("%s\n", serial_str); | ||||
| 551 | goto out; | ||||
| 552 | } | ||||
| 553 | |||||
| 554 | printf("%s\n", dev_scsi.serial); | ||||
| 555 | out: | ||||
| 556 | return retval; | ||||
| 557 | } | ||||
| 558 | |||||
| 559 | int main(int argc, char **argv) { | ||||
| 560 | _cleanup_(udev_unrefp)__attribute__((cleanup(udev_unrefp))) struct udev *udev; | ||||
| 561 | int retval = 0; | ||||
| 562 | char maj_min_dev[MAX_PATH_LEN512]; | ||||
| 563 | int newargc; | ||||
| 564 | char **newargv = NULL((void*)0); | ||||
| 565 | |||||
| 566 | log_set_target(LOG_TARGET_AUTO); | ||||
| 567 | udev_parse_config(); | ||||
| 568 | log_parse_environment()log_parse_environment_realm(LOG_REALM_UDEV); | ||||
| 569 | log_open(); | ||||
| 570 | |||||
| 571 | udev = udev_new(); | ||||
| 572 | if (udev == NULL((void*)0)) | ||||
| |||||
| 573 | goto exit; | ||||
| 574 | |||||
| 575 | /* | ||||
| 576 | * Get config file options. | ||||
| 577 | */ | ||||
| 578 | retval = get_file_options(udev, NULL((void*)0), NULL((void*)0), &newargc, &newargv); | ||||
| 579 | if (retval < 0) { | ||||
| 580 | retval = 1; | ||||
| 581 | goto exit; | ||||
| 582 | } | ||||
| 583 | if (retval == 0) { | ||||
| 584 | assert(newargv)do { if ((__builtin_expect(!!(!(newargv)),0))) log_assert_failed_realm (LOG_REALM_UDEV, ("newargv"), "../src/udev/scsi_id/scsi_id.c" , 584, __PRETTY_FUNCTION__); } while (0); | ||||
| 585 | |||||
| 586 | if (set_options(udev, newargc, newargv, maj_min_dev) < 0) { | ||||
| 587 | retval = 2; | ||||
| 588 | goto exit; | ||||
| 589 | } | ||||
| 590 | } | ||||
| 591 | |||||
| 592 | /* | ||||
| 593 | * Get command line options (overriding any config file settings). | ||||
| 594 | */ | ||||
| 595 | if (set_options(udev, argc, argv, maj_min_dev) < 0) | ||||
| 596 | exit(EXIT_FAILURE1); | ||||
| 597 | |||||
| 598 | if (!dev_specified) { | ||||
| 599 | log_error("No device specified.")({ int _level = (((3))), _e = ((0)), _realm = (LOG_REALM_UDEV ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/udev/scsi_id/scsi_id.c", 599, __func__, "No device specified." ) : -abs(_e); }); | ||||
| 600 | retval = 1; | ||||
| 601 | goto exit; | ||||
| 602 | } | ||||
| 603 | |||||
| 604 | retval = scsi_id(udev, maj_min_dev); | ||||
| 605 | |||||
| 606 | exit: | ||||
| 607 | if (newargv) { | ||||
| 608 | free(newargv[0]); | ||||
| 609 | free(newargv); | ||||
| 610 | } | ||||
| 611 | log_close(); | ||||
| 612 | return retval; | ||||
| 613 | } |