]> git.ipfire.org Git - thirdparty/systemd.git/blame - extras/scsi_id/scsi_id.c
volume_id: support for long-filename based labels
[thirdparty/systemd.git] / extras / scsi_id / scsi_id.c
CommitLineData
c521693b
GKH
1/*
2 * scsi_id.c
3 *
4 * Main section of the scsi_id program
5 *
6 * Copyright (C) IBM Corp. 2003
3d94fb87 7 * Copyright (C) SUSE Linux Products GmbH, 2006
c521693b 8 *
3d94fb87
KS
9 * Author:
10 * Patrick Mansfield<patmans@us.ibm.com>
c521693b 11 *
3d94fb87
KS
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation version 2 of the License.
c521693b
GKH
15 */
16
17#include <stdio.h>
18#include <stdlib.h>
c521693b
GKH
19#include <unistd.h>
20#include <signal.h>
21#include <fcntl.h>
22#include <errno.h>
23#include <string.h>
24#include <syslog.h>
25#include <stdarg.h>
26#include <ctype.h>
27#include <sys/stat.h>
c521693b 28
1aa1e248
KS
29#include "../../udev.h"
30#include "scsi_id.h"
31#include "scsi_id_version.h"
c521693b 32
1aa1e248
KS
33/* temporary names for mknod */
34#define TMP_DIR "/dev"
35#define TMP_PREFIX "tmp-scsi"
c521693b 36
b4a2906b 37static const char short_options[] = "abd:f:gip:s:uvVx";
b484e436 38static const char dev_short_options[] = "bgp:";
c521693b 39
c521693b 40static int all_good;
b4a2906b 41static int always_info;
c521693b
GKH
42static int dev_specified;
43static int sys_specified;
6ecd4d1e 44static char config_file[MAX_PATH_LEN] = SCSI_ID_CONFIG_FILE;
c521693b 45static int display_bus_id;
50be1401 46static enum page_code default_page_code;
c521693b
GKH
47static int use_stderr;
48static int debug;
49static int hotplug_mode;
01f950e2 50static int reformat_serial;
34129109
KS
51static int export;
52static char vendor_str[64];
53static char model_str[64];
aaff09a3
KS
54static char revision_str[16];
55static char type_str[16];
c521693b 56
1aa1e248
KS
57#ifdef USE_LOG
58void log_message(int priority, const char *format, ...)
c521693b 59{
1aa1e248
KS
60 va_list args;
61 static int udev_log = -1;
c521693b 62
1aa1e248
KS
63 if (udev_log == -1) {
64 const char *value;
c521693b 65
1aa1e248
KS
66 value = getenv("UDEV_LOG");
67 if (value)
68 udev_log = log_priority(value);
69 else
70 udev_log = LOG_ERR;
c521693b 71 }
1aa1e248
KS
72
73 if (priority > udev_log)
74 return;
75
76 va_start(args, format);
77 vsyslog(priority, format, args);
78 va_end(args);
c521693b 79}
1aa1e248 80#endif
c521693b 81
70721db6 82static void set_str(char *to, const char *from, size_t count)
34129109 83{
70721db6 84 size_t i, j, len;
34129109 85
aaff09a3 86 /* strip trailing whitespace */
34129109 87 len = strnlen(from, count);
c907c823 88 while (len && isspace(from[len-1]))
34129109
KS
89 len--;
90
aaff09a3 91 /* strip leading whitespace */
34129109
KS
92 i = 0;
93 while (isspace(from[i]) && (i < len))
94 i++;
95
96 j = 0;
97 while (i < len) {
aaff09a3
KS
98 /* substitute multiple whitespace */
99 if (isspace(from[i])) {
100 while (isspace(from[i]))
101 i++;
34129109 102 to[j++] = '_';
aaff09a3
KS
103 }
104 /* skip chars */
105 if (from[i] == '/') {
106 i++;
107 continue;
108 }
109 to[j++] = from[i++];
110 }
111 to[j] = '\0';
112}
113
853ccc43 114static void set_type(char *to, const char *from, size_t len)
aaff09a3
KS
115{
116 int type_num;
117 char *eptr;
853ccc43 118 char *type = "generic";
aaff09a3
KS
119
120 type_num = strtoul(from, &eptr, 0);
121 if (eptr != from) {
122 switch (type_num) {
123 case 0:
853ccc43 124 type = "disk";
aaff09a3
KS
125 break;
126 case 1:
853ccc43 127 type = "tape";
aaff09a3
KS
128 break;
129 case 4:
853ccc43 130 type = "optical";
aaff09a3
KS
131 break;
132 case 5:
853ccc43 133 type = "cd";
aaff09a3
KS
134 break;
135 case 7:
853ccc43 136 type = "optical";
aaff09a3
KS
137 break;
138 case 0xe:
853ccc43 139 type = "disk";
aaff09a3
KS
140 break;
141 case 0xf:
853ccc43 142 type = "optical";
34129109
KS
143 break;
144 default:
aaff09a3 145 break;
34129109 146 }
34129109 147 }
853ccc43
KS
148 strncpy(to, type, len);
149 to[len-1] = '\0';
34129109
KS
150}
151
1aa1e248 152static int create_tmp_dev(const char *devpath, char *tmpdev, int dev_type)
e996d978 153{
1aa1e248
KS
154 unsigned int maj, min;
155 const char *attr;
e996d978 156
1aa1e248
KS
157 dbg("%s", devpath);
158 attr = sysfs_attr_get_value(devpath, "dev");
159 if (attr == NULL) {
160 dbg("%s: could not get dev attribute: %s", devpath, strerror(errno));
c521693b
GKH
161 return -1;
162 }
c521693b 163
1aa1e248
KS
164 dbg("dev value %s", attr);
165 if (sscanf(attr, "%u:%u", &maj, &min) != 2) {
166 err("%s: invalid dev major/minor", devpath);
c521693b
GKH
167 return -1;
168 }
169
6ecd4d1e 170 snprintf(tmpdev, MAX_PATH_LEN, "%s/%s-maj%d-min%d-%u",
062db23d 171 TMP_DIR, TMP_PREFIX, maj, min, getpid());
c521693b 172
1aa1e248 173 dbg("tmpdev '%s'", tmpdev);
062db23d 174 if (mknod(tmpdev, 0600 | dev_type, makedev(maj, min))) {
1aa1e248 175 err("mknod failed: %s", strerror(errno));
c521693b
GKH
176 return -1;
177 }
178 return 0;
179}
180
c521693b
GKH
181/*
182 * get_value:
183 *
184 * buf points to an '=' followed by a quoted string ("foo") or a string ending
185 * with a space or ','.
186 *
187 * Return a pointer to the NUL terminated string, returns NULL if no
188 * matches.
189 */
190static char *get_value(char **buffer)
191{
192 static char *quote_string = "\"\n";
193 static char *comma_string = ",\n";
194 char *val;
195 char *end;
196
197 if (**buffer == '"') {
198 /*
199 * skip leading quote, terminate when quote seen
200 */
201 (*buffer)++;
202 end = quote_string;
203 } else {
204 end = comma_string;
205 }
206 val = strsep(buffer, end);
207 if (val && end == quote_string)
208 /*
209 * skip trailing quote
210 */
211 (*buffer)++;
212
213 while (isspace(**buffer))
214 (*buffer)++;
215
216 return val;
217}
218
219static int argc_count(char *opts)
220{
221 int i = 0;
222 while (*opts != '\0')
223 if (*opts++ == ' ')
224 i++;
225 return i;
226}
227
228/*
229 * get_file_options:
230 *
231 * If vendor == NULL, find a line in the config file with only "OPTIONS=";
232 * if vendor and model are set find the first OPTIONS line in the config
233 * file that matches. Set argc and argv to match the OPTIONS string.
234 *
235 * vendor and model can end in '\n'.
236 */
1aa1e248
KS
237static int get_file_options(const char *vendor, const char *model,
238 int *argc, char ***newargv)
c521693b 239{
1bed1db4 240 char *buffer;
c521693b
GKH
241 FILE *fd;
242 char *buf;
243 char *str1;
244 char *vendor_in, *model_in, *options_in; /* read in from file */
245 int lineno;
246 int c;
247 int retval = 0;
c521693b 248
1aa1e248 249 dbg("vendor='%s'; model='%s'\n", vendor, model);
c521693b
GKH
250 fd = fopen(config_file, "r");
251 if (fd == NULL) {
1aa1e248 252 dbg("can't open %s\n", config_file);
c521693b
GKH
253 if (errno == ENOENT) {
254 return 1;
255 } else {
1aa1e248 256 err("can't open %s: %s", config_file, strerror(errno));
c521693b
GKH
257 return -1;
258 }
259 }
260
1bed1db4
PM
261 /*
262 * Allocate a buffer rather than put it on the stack so we can
263 * keep it around to parse any options (any allocated newargv
264 * points into this buffer for its strings).
265 */
266 buffer = malloc(MAX_BUFFER_LEN);
267 if (!buffer) {
1aa1e248 268 err("Can't allocate memory.");
1bed1db4
PM
269 return -1;
270 }
271
c521693b
GKH
272 *newargv = NULL;
273 lineno = 0;
c521693b
GKH
274 while (1) {
275 vendor_in = model_in = options_in = NULL;
276
1bed1db4 277 buf = fgets(buffer, MAX_BUFFER_LEN, fd);
c521693b
GKH
278 if (buf == NULL)
279 break;
280 lineno++;
1bed1db4 281 if (buf[strlen(buffer) - 1] != '\n') {
1aa1e248 282 info("Config file line %d too long.\n", lineno);
1bed1db4
PM
283 break;
284 }
c521693b
GKH
285
286 while (isspace(*buf))
287 buf++;
288
1aa1e248 289 /* blank or all whitespace line */
c521693b 290 if (*buf == '\0')
c521693b
GKH
291 continue;
292
1aa1e248 293 /* comment line */
c521693b 294 if (*buf == '#')
c521693b
GKH
295 continue;
296
1aa1e248 297 dbg("lineno %d: '%s'\n", lineno, buf);
c521693b
GKH
298 str1 = strsep(&buf, "=");
299 if (str1 && strcasecmp(str1, "VENDOR") == 0) {
300 str1 = get_value(&buf);
301 if (!str1) {
302 retval = -1;
303 break;
304 }
305 vendor_in = str1;
306
307 str1 = strsep(&buf, "=");
308 if (str1 && strcasecmp(str1, "MODEL") == 0) {
309 str1 = get_value(&buf);
310 if (!str1) {
311 retval = -1;
312 break;
313 }
314 model_in = str1;
315 str1 = strsep(&buf, "=");
316 }
317 }
318
319 if (str1 && strcasecmp(str1, "OPTIONS") == 0) {
320 str1 = get_value(&buf);
321 if (!str1) {
322 retval = -1;
323 break;
324 }
325 options_in = str1;
326 }
1aa1e248 327 dbg("config file line %d:"
c521693b
GKH
328 " vendor '%s'; model '%s'; options '%s'\n",
329 lineno, vendor_in, model_in, options_in);
330 /*
331 * Only allow: [vendor=foo[,model=bar]]options=stuff
332 */
333 if (!options_in || (!vendor_in && model_in)) {
1aa1e248 334 info("Error parsing config file line %d '%s'", lineno, buffer);
c521693b
GKH
335 retval = -1;
336 break;
337 }
338 if (vendor == NULL) {
339 if (vendor_in == NULL) {
1aa1e248 340 dbg("matched global option\n");
c521693b
GKH
341 break;
342 }
343 } else if ((vendor_in && strncmp(vendor, vendor_in,
344 strlen(vendor_in)) == 0) &&
345 (!model_in || (strncmp(model, model_in,
346 strlen(model_in)) == 0))) {
347 /*
348 * Matched vendor and optionally model.
349 *
350 * Note: a short vendor_in or model_in can
351 * give a partial match (that is FOO
352 * matches FOOBAR).
353 */
1aa1e248 354 dbg("matched vendor/model\n");
c521693b
GKH
355 break;
356 } else {
1aa1e248 357 dbg("no match\n");
c521693b
GKH
358 }
359 }
360
361 if (retval == 0) {
362 if (vendor_in != NULL || model_in != NULL ||
363 options_in != NULL) {
364 /*
365 * Something matched. Allocate newargv, and store
366 * values found in options_in.
367 */
1bed1db4
PM
368 strcpy(buffer, options_in);
369 c = argc_count(buffer) + 2;
c521693b
GKH
370 *newargv = calloc(c, sizeof(**newargv));
371 if (!*newargv) {
1aa1e248 372 err("Can't allocate memory.");
c521693b
GKH
373 retval = -1;
374 } else {
375 *argc = c;
376 c = 0;
1bed1db4
PM
377 /*
378 * argv[0] at 0 is skipped by getopt, but
379 * store the buffer address there for
58310f66 380 * later freeing
1bed1db4
PM
381 */
382 (*newargv)[c] = buffer;
c521693b 383 for (c = 1; c < *argc; c++)
58310f66 384 (*newargv)[c] = strsep(&buffer, " \t");
c521693b
GKH
385 }
386 } else {
1aa1e248 387 /* No matches */
c521693b
GKH
388 retval = 1;
389 }
390 }
1bed1db4
PM
391 if (retval != 0)
392 free(buffer);
c521693b
GKH
393 fclose(fd);
394 return retval;
395}
396
397static int set_options(int argc, char **argv, const char *short_opts,
1bed1db4 398 char *target, char *maj_min_dev)
c521693b
GKH
399{
400 int option;
c521693b
GKH
401
402 /*
1bed1db4
PM
403 * optind is a global extern used by getopt. Since we can call
404 * set_options twice (once for command line, and once for config
05ec6e75 405 * file) we have to reset this back to 1.
c521693b 406 */
1bed1db4 407 optind = 1;
c521693b 408 while (1) {
853ccc43 409 option = getopt(argc, argv, short_opts);
c521693b
GKH
410 if (option == -1)
411 break;
412
413 if (optarg)
1aa1e248 414 dbg("option '%c' arg '%s'\n", option, optarg);
c521693b 415 else
1aa1e248 416 dbg("option '%c'\n", option);
c521693b
GKH
417
418 switch (option) {
b4a2906b
HR
419 case 'a':
420 always_info = 1;
421 break;
c521693b
GKH
422 case 'b':
423 all_good = 0;
424 break;
425
c521693b
GKH
426 case 'd':
427 dev_specified = 1;
6ecd4d1e
KS
428 strncpy(maj_min_dev, optarg, MAX_PATH_LEN);
429 maj_min_dev[MAX_PATH_LEN-1] = '\0';
c521693b
GKH
430 break;
431
432 case 'e':
433 use_stderr = 1;
434 break;
435
436 case 'f':
6ecd4d1e
KS
437 strncpy(config_file, optarg, MAX_PATH_LEN);
438 config_file[MAX_PATH_LEN-1] = '\0';
c521693b
GKH
439 break;
440
441 case 'g':
442 all_good = 1;
443 break;
444
445 case 'i':
446 display_bus_id = 1;
447 break;
448
449 case 'p':
450 if (strcmp(optarg, "0x80") == 0) {
50be1401 451 default_page_code = PAGE_80;
c521693b 452 } else if (strcmp(optarg, "0x83") == 0) {
50be1401
EG
453 default_page_code = PAGE_83;
454 } else if (strcmp(optarg, "pre-spc3-83") == 0) {
455 default_page_code = PAGE_83_PRE_SPC3;
c521693b 456 } else {
1aa1e248 457 info("Unknown page code '%s'", optarg);
c521693b
GKH
458 return -1;
459 }
460 break;
461
462 case 's':
463 sys_specified = 1;
6ecd4d1e
KS
464 strncpy(target, optarg, MAX_PATH_LEN);
465 target[MAX_PATH_LEN-1] = '\0';
c521693b
GKH
466 break;
467
01f950e2
PM
468 case 'u':
469 reformat_serial = 1;
470 break;
471
34129109
KS
472 case 'x':
473 export = 1;
474 break;
475
c521693b
GKH
476 case 'v':
477 debug++;
478 break;
479
480 case 'V':
1aa1e248 481 info("scsi_id version: %s\n", SCSI_ID_VERSION);
c521693b
GKH
482 exit(0);
483 break;
484
485 default:
1aa1e248 486 info("Unknown or bad option '%c' (0x%x)", option, option);
c521693b
GKH
487 return -1;
488 }
489 }
490 return 0;
491}
492
1aa1e248 493static int per_dev_options(struct sysfs_device *dev_scsi, int *good_bad, int *page_code)
c521693b
GKH
494{
495 int retval;
496 int newargc;
497 char **newargv = NULL;
1aa1e248 498 const char *vendor, *model, *type;
c521693b 499 int option;
c521693b
GKH
500
501 *good_bad = all_good;
502 *page_code = default_page_code;
c521693b 503
1aa1e248 504 vendor = sysfs_attr_get_value(dev_scsi->devpath, "vendor");
4e05e423 505 if (!vendor) {
1aa1e248 506 info("%s: cannot get vendor attribute", dev_scsi->devpath);
c521693b
GKH
507 return -1;
508 }
1aa1e248 509 set_str(vendor_str, vendor, sizeof(vendor_str)-1);
c521693b 510
1aa1e248 511 model = sysfs_attr_get_value(dev_scsi->devpath, "model");
4e05e423 512 if (!model) {
1aa1e248 513 info("%s: cannot get model attribute\n", dev_scsi->devpath);
c521693b
GKH
514 return -1;
515 }
1aa1e248 516 set_str(model_str, model, sizeof(model_str)-1);
c521693b 517
1aa1e248 518 type = sysfs_attr_get_value(dev_scsi->devpath, "type");
aaff09a3 519 if (!type) {
1aa1e248 520 info("%s: cannot get type attribute", dev_scsi->devpath);
aaff09a3
KS
521 return -1;
522 }
1aa1e248 523 set_type(type_str, type, sizeof(type_str));
aaff09a3 524
1aa1e248 525 type = sysfs_attr_get_value(dev_scsi->devpath, "rev");
aaff09a3 526 if (!type) {
1aa1e248 527 info("%s: cannot get type attribute\n", dev_scsi->devpath);
aaff09a3
KS
528 return -1;
529 }
1aa1e248 530 set_str(revision_str, type, sizeof(revision_str)-1);
aaff09a3 531
1aa1e248 532 retval = get_file_options(vendor, model, &newargc, &newargv);
c521693b 533
1bed1db4 534 optind = 1; /* reset this global extern */
c521693b 535 while (retval == 0) {
1bed1db4 536 option = getopt(newargc, newargv, dev_short_options);
c521693b
GKH
537 if (option == -1)
538 break;
539
540 if (optarg)
1aa1e248 541 dbg("option '%c' arg '%s'\n", option, optarg);
c521693b 542 else
1aa1e248 543 dbg("option '%c'\n", option);
c521693b
GKH
544
545 switch (option) {
546 case 'b':
547 *good_bad = 0;
548 break;
549
c521693b
GKH
550 case 'g':
551 *good_bad = 1;
552 break;
553
554 case 'p':
555 if (strcmp(optarg, "0x80") == 0) {
50be1401 556 *page_code = PAGE_80;
c521693b 557 } else if (strcmp(optarg, "0x83") == 0) {
50be1401
EG
558 *page_code = PAGE_83;
559 } else if (strcmp(optarg, "pre-spc3-83") == 0) {
560 *page_code = PAGE_83_PRE_SPC3;
c521693b 561 } else {
1aa1e248 562 info("Unknown page code '%s'", optarg);
c521693b
GKH
563 retval = -1;
564 }
565 break;
566
567 default:
1aa1e248 568 info("Unknown or bad option '%c' (0x%x)", option, option);
c521693b
GKH
569 retval = -1;
570 break;
571 }
572 }
573
1bed1db4
PM
574 if (newargv) {
575 free(newargv[0]);
c521693b 576 free(newargv);
1bed1db4 577 }
c521693b
GKH
578 return retval;
579}
580
01f950e2
PM
581/*
582 * format_serial: replace to whitespaces by underscores for calling
583 * programs that use the serial for device naming (multipath, Suse
584 * naming, etc...)
585 */
586static void format_serial(char *serial)
587{
d313632b 588 char *p = serial, *q;
01f950e2 589
d313632b 590 q = p;
01f950e2 591 while (*p != '\0') {
d313632b
HR
592 if (isspace(*p)) {
593 if (q > serial && q[-1] != '_') {
594 *q = '_';
595 q++;
596 }
597 } else {
598 *q = *p;
599 q++;
600 }
01f950e2
PM
601 p++;
602 }
d313632b 603 *q = '\0';
01f950e2
PM
604}
605
c521693b
GKH
606/*
607 * scsi_id: try to get an id, if one is found, printf it to stdout.
608 * returns a value passed to exit() - 0 if printed an id, else 1. This
609 * could be expanded, for example, if we want to report a failure like no
610 * memory etc. return 2, and return 1 for expected cases (like broken
611 * device found) that do not print an id.
612 */
1aa1e248 613static int scsi_id(const char *devpath, char *maj_min_dev)
c521693b
GKH
614{
615 int retval;
616 int dev_type = 0;
1aa1e248
KS
617 struct sysfs_device *dev;
618 struct sysfs_device *dev_scsi;
c521693b
GKH
619 int good_dev;
620 int page_code;
11678eff
KS
621 char serial[MAX_SERIAL_LEN];
622 char serial_short[MAX_SERIAL_LEN];
c521693b 623
1aa1e248 624 dbg("devpath %s\n", devpath);
c521693b 625
1aa1e248
KS
626 dev = sysfs_device_get(devpath);
627 if (dev == NULL) {
628 err("unable to access '%s'", devpath);
b484e436
PM
629 return 1;
630 }
b484e436 631
1aa1e248
KS
632 if (strcmp(dev->subsystem, "block") == 0)
633 dev_type = S_IFBLK;
634 else
635 dev_type = S_IFCHR;
b484e436 636
1aa1e248 637 /* get scsi parent device */
6ecd4d1e 638 dev_scsi = sysfs_device_get_parent_with_subsystem(dev, "scsi");
1aa1e248
KS
639 if (dev_scsi == NULL) {
640 err("unable to access parent device of '%s'", devpath);
c521693b 641 return 1;
b484e436 642 }
c521693b 643
1aa1e248
KS
644 /* mknod a temp dev to communicate with the device */
645 if (!dev_specified && create_tmp_dev(dev->devpath, maj_min_dev, dev_type)) {
646 dbg("create_tmp_dev failed\n");
c521693b
GKH
647 return 1;
648 }
649
1aa1e248
KS
650 /* get per device (vendor + model) options from the config file */
651 retval = per_dev_options(dev_scsi, &good_dev, &page_code);
652 dbg("per dev options: good %d; page code 0x%x", good_dev, page_code);
c521693b
GKH
653
654 if (!good_dev) {
655 retval = 1;
1aa1e248 656 } else if (scsi_get_serial(dev_scsi, maj_min_dev, page_code,
11678eff 657 serial, serial_short, MAX_SERIAL_LEN)) {
b4a2906b 658 retval = always_info?0:1;
c521693b
GKH
659 } else {
660 retval = 0;
661 }
662 if (!retval) {
34129109 663 if (export) {
11678eff
KS
664 char serial_str[MAX_SERIAL_LEN];
665
34129109
KS
666 printf("ID_VENDOR=%s\n", vendor_str);
667 printf("ID_MODEL=%s\n", model_str);
aaff09a3 668 printf("ID_REVISION=%s\n", revision_str);
34129109
KS
669 set_str(serial_str, serial, sizeof(serial_str));
670 printf("ID_SERIAL=%s\n", serial_str);
11678eff
KS
671 set_str(serial_str, serial_short, sizeof(serial_str));
672 printf("ID_SERIAL_SHORT=%s\n", serial_str);
aaff09a3 673 printf("ID_TYPE=%s\n", type_str);
46f01c6d 674 printf("ID_BUS=scsi\n");
34129109
KS
675 } else {
676 if (reformat_serial)
677 format_serial(serial);
678 if (display_bus_id)
95776dc6 679 printf("%s: ", dev_scsi->kernel);
34129109
KS
680 printf("%s\n", serial);
681 }
1aa1e248 682 dbg("%s\n", serial);
c521693b
GKH
683 retval = 0;
684 }
c521693b
GKH
685
686 if (!dev_specified)
687 unlink(maj_min_dev);
688
689 return retval;
690}
691
692int main(int argc, char **argv)
693{
1aa1e248 694 int retval = 0;
6ecd4d1e
KS
695 char devpath[MAX_PATH_LEN];
696 char maj_min_dev[MAX_PATH_LEN];
c521693b 697 int newargc;
1aa1e248 698 const char *env;
c521693b
GKH
699 char **newargv;
700
1aa1e248
KS
701 logging_init("scsi_id");
702 sysfs_init();
703 dbg("argc is %d\n", argc);
c521693b 704
1aa1e248
KS
705 /* sysfs path can be overridden for testing */
706 env = getenv("SYSFS_PATH");
707 if (env) {
708 strncpy(sysfs_path, env, sizeof(sysfs_path));
709 sysfs_path[sizeof(sysfs_path)-1] = '\0';
710 } else
711 strcpy(sysfs_path, "/sys");
c521693b 712
1aa1e248
KS
713 env = getenv("DEVPATH");
714 if (env) {
07544a93 715 hotplug_mode = 1;
c521693b 716 sys_specified = 1;
6ecd4d1e 717 strncpy(devpath, env, MAX_PATH_LEN);
1aa1e248 718 devpath[sizeof(devpath)-1] = '\0';
c521693b
GKH
719 }
720
721 /*
07544a93 722 * Get config file options.
c521693b
GKH
723 */
724 newargv = NULL;
725 retval = get_file_options(NULL, NULL, &newargc, &newargv);
726 if (retval < 0) {
1aa1e248
KS
727 retval = 1;
728 goto exit;
729 }
730 if (newargv && (retval == 0)) {
731 if (set_options(newargc, newargv, short_options, devpath,
732 maj_min_dev) < 0) {
733 retval = 2;
734 goto exit;
735 }
c521693b
GKH
736 free(newargv);
737 }
1aa1e248 738
07544a93
PM
739 /*
740 * Get command line options (overriding any config file or DEVPATH
741 * settings).
742 */
1aa1e248 743 if (set_options(argc, argv, short_options, devpath, maj_min_dev) < 0)
062db23d 744 exit(1);
c521693b
GKH
745
746 if (!sys_specified) {
1aa1e248
KS
747 info("-s must be specified\n");
748 retval = 1;
749 goto exit;
c521693b
GKH
750 }
751
1aa1e248
KS
752 retval = scsi_id(devpath, maj_min_dev);
753
754exit:
755 sysfs_cleanup();
756 logging_close();
757 return retval;
c521693b 758}