]> git.ipfire.org Git - thirdparty/systemd.git/blame - extras/scsi_id/scsi_id.c
libvolume_id: read ufs2 label
[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
380 * alter freeing.
381 */
382 (*newargv)[c] = buffer;
c521693b 383 for (c = 1; c < *argc; c++)
1bed1db4 384 (*newargv)[c] = strsep(&buffer, " ");
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
405 * file) we have to reset this back to 1. [Note glibc handles
406 * setting this to 0, but klibc does not.]
c521693b 407 */
1bed1db4 408 optind = 1;
c521693b 409 while (1) {
853ccc43 410 option = getopt(argc, argv, short_opts);
c521693b
GKH
411 if (option == -1)
412 break;
413
414 if (optarg)
1aa1e248 415 dbg("option '%c' arg '%s'\n", option, optarg);
c521693b 416 else
1aa1e248 417 dbg("option '%c'\n", option);
c521693b
GKH
418
419 switch (option) {
b4a2906b
HR
420 case 'a':
421 always_info = 1;
422 break;
c521693b
GKH
423 case 'b':
424 all_good = 0;
425 break;
426
c521693b
GKH
427 case 'd':
428 dev_specified = 1;
6ecd4d1e
KS
429 strncpy(maj_min_dev, optarg, MAX_PATH_LEN);
430 maj_min_dev[MAX_PATH_LEN-1] = '\0';
c521693b
GKH
431 break;
432
433 case 'e':
434 use_stderr = 1;
435 break;
436
437 case 'f':
6ecd4d1e
KS
438 strncpy(config_file, optarg, MAX_PATH_LEN);
439 config_file[MAX_PATH_LEN-1] = '\0';
c521693b
GKH
440 break;
441
442 case 'g':
443 all_good = 1;
444 break;
445
446 case 'i':
447 display_bus_id = 1;
448 break;
449
450 case 'p':
451 if (strcmp(optarg, "0x80") == 0) {
50be1401 452 default_page_code = PAGE_80;
c521693b 453 } else if (strcmp(optarg, "0x83") == 0) {
50be1401
EG
454 default_page_code = PAGE_83;
455 } else if (strcmp(optarg, "pre-spc3-83") == 0) {
456 default_page_code = PAGE_83_PRE_SPC3;
c521693b 457 } else {
1aa1e248 458 info("Unknown page code '%s'", optarg);
c521693b
GKH
459 return -1;
460 }
461 break;
462
463 case 's':
464 sys_specified = 1;
6ecd4d1e
KS
465 strncpy(target, optarg, MAX_PATH_LEN);
466 target[MAX_PATH_LEN-1] = '\0';
c521693b
GKH
467 break;
468
01f950e2
PM
469 case 'u':
470 reformat_serial = 1;
471 break;
472
34129109
KS
473 case 'x':
474 export = 1;
475 break;
476
c521693b
GKH
477 case 'v':
478 debug++;
479 break;
480
481 case 'V':
1aa1e248 482 info("scsi_id version: %s\n", SCSI_ID_VERSION);
c521693b
GKH
483 exit(0);
484 break;
485
486 default:
1aa1e248 487 info("Unknown or bad option '%c' (0x%x)", option, option);
c521693b
GKH
488 return -1;
489 }
490 }
491 return 0;
492}
493
1aa1e248 494static int per_dev_options(struct sysfs_device *dev_scsi, int *good_bad, int *page_code)
c521693b
GKH
495{
496 int retval;
497 int newargc;
498 char **newargv = NULL;
1aa1e248 499 const char *vendor, *model, *type;
c521693b 500 int option;
c521693b
GKH
501
502 *good_bad = all_good;
503 *page_code = default_page_code;
c521693b 504
1aa1e248 505 vendor = sysfs_attr_get_value(dev_scsi->devpath, "vendor");
4e05e423 506 if (!vendor) {
1aa1e248 507 info("%s: cannot get vendor attribute", dev_scsi->devpath);
c521693b
GKH
508 return -1;
509 }
1aa1e248 510 set_str(vendor_str, vendor, sizeof(vendor_str)-1);
c521693b 511
1aa1e248 512 model = sysfs_attr_get_value(dev_scsi->devpath, "model");
4e05e423 513 if (!model) {
1aa1e248 514 info("%s: cannot get model attribute\n", dev_scsi->devpath);
c521693b
GKH
515 return -1;
516 }
1aa1e248 517 set_str(model_str, model, sizeof(model_str)-1);
c521693b 518
1aa1e248 519 type = sysfs_attr_get_value(dev_scsi->devpath, "type");
aaff09a3 520 if (!type) {
1aa1e248 521 info("%s: cannot get type attribute", dev_scsi->devpath);
aaff09a3
KS
522 return -1;
523 }
1aa1e248 524 set_type(type_str, type, sizeof(type_str));
aaff09a3 525
1aa1e248 526 type = sysfs_attr_get_value(dev_scsi->devpath, "rev");
aaff09a3 527 if (!type) {
1aa1e248 528 info("%s: cannot get type attribute\n", dev_scsi->devpath);
aaff09a3
KS
529 return -1;
530 }
1aa1e248 531 set_str(revision_str, type, sizeof(revision_str)-1);
aaff09a3 532
1aa1e248 533 retval = get_file_options(vendor, model, &newargc, &newargv);
c521693b 534
1bed1db4 535 optind = 1; /* reset this global extern */
c521693b 536 while (retval == 0) {
1bed1db4 537 option = getopt(newargc, newargv, dev_short_options);
c521693b
GKH
538 if (option == -1)
539 break;
540
541 if (optarg)
1aa1e248 542 dbg("option '%c' arg '%s'\n", option, optarg);
c521693b 543 else
1aa1e248 544 dbg("option '%c'\n", option);
c521693b
GKH
545
546 switch (option) {
547 case 'b':
548 *good_bad = 0;
549 break;
550
c521693b
GKH
551 case 'g':
552 *good_bad = 1;
553 break;
554
555 case 'p':
556 if (strcmp(optarg, "0x80") == 0) {
50be1401 557 *page_code = PAGE_80;
c521693b 558 } else if (strcmp(optarg, "0x83") == 0) {
50be1401
EG
559 *page_code = PAGE_83;
560 } else if (strcmp(optarg, "pre-spc3-83") == 0) {
561 *page_code = PAGE_83_PRE_SPC3;
c521693b 562 } else {
1aa1e248 563 info("Unknown page code '%s'", optarg);
c521693b
GKH
564 retval = -1;
565 }
566 break;
567
568 default:
1aa1e248 569 info("Unknown or bad option '%c' (0x%x)", option, option);
c521693b
GKH
570 retval = -1;
571 break;
572 }
573 }
574
1bed1db4
PM
575 if (newargv) {
576 free(newargv[0]);
c521693b 577 free(newargv);
1bed1db4 578 }
c521693b
GKH
579 return retval;
580}
581
01f950e2
PM
582/*
583 * format_serial: replace to whitespaces by underscores for calling
584 * programs that use the serial for device naming (multipath, Suse
585 * naming, etc...)
586 */
587static void format_serial(char *serial)
588{
d313632b 589 char *p = serial, *q;
01f950e2 590
d313632b 591 q = p;
01f950e2 592 while (*p != '\0') {
d313632b
HR
593 if (isspace(*p)) {
594 if (q > serial && q[-1] != '_') {
595 *q = '_';
596 q++;
597 }
598 } else {
599 *q = *p;
600 q++;
601 }
01f950e2
PM
602 p++;
603 }
d313632b 604 *q = '\0';
01f950e2
PM
605}
606
c521693b
GKH
607/*
608 * scsi_id: try to get an id, if one is found, printf it to stdout.
609 * returns a value passed to exit() - 0 if printed an id, else 1. This
610 * could be expanded, for example, if we want to report a failure like no
611 * memory etc. return 2, and return 1 for expected cases (like broken
612 * device found) that do not print an id.
613 */
1aa1e248 614static int scsi_id(const char *devpath, char *maj_min_dev)
c521693b
GKH
615{
616 int retval;
617 int dev_type = 0;
01f950e2 618 char *serial, *unaligned_buf;
1aa1e248
KS
619 struct sysfs_device *dev;
620 struct sysfs_device *dev_scsi;
c521693b
GKH
621 int good_dev;
622 int page_code;
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 653
01f950e2
PM
654#define ALIGN 512
655 unaligned_buf = malloc(MAX_SERIAL_LEN + ALIGN);
b337e607
PM
656 serial = (char*) (((unsigned long) unaligned_buf + (ALIGN - 1))
657 & ~(ALIGN - 1));
1aa1e248 658 dbg("buffer unaligned 0x%p; aligned 0x%p\n", unaligned_buf, serial);
01f950e2
PM
659#undef ALIGN
660
c521693b
GKH
661 if (!good_dev) {
662 retval = 1;
1aa1e248 663 } else if (scsi_get_serial(dev_scsi, maj_min_dev, page_code,
c521693b 664 serial, MAX_SERIAL_LEN)) {
b4a2906b 665 retval = always_info?0:1;
c521693b
GKH
666 } else {
667 retval = 0;
668 }
669 if (!retval) {
34129109
KS
670 if (export) {
671 static char serial_str[64];
672 printf("ID_VENDOR=%s\n", vendor_str);
673 printf("ID_MODEL=%s\n", model_str);
aaff09a3 674 printf("ID_REVISION=%s\n", revision_str);
34129109
KS
675 set_str(serial_str, serial, sizeof(serial_str));
676 printf("ID_SERIAL=%s\n", serial_str);
aaff09a3 677 printf("ID_TYPE=%s\n", type_str);
46f01c6d 678 printf("ID_BUS=scsi\n");
34129109
KS
679 } else {
680 if (reformat_serial)
681 format_serial(serial);
682 if (display_bus_id)
1aa1e248 683 printf("%s: ", dev_scsi->kernel_name);
34129109
KS
684 printf("%s\n", serial);
685 }
1aa1e248 686 dbg("%s\n", serial);
c521693b
GKH
687 retval = 0;
688 }
c521693b
GKH
689
690 if (!dev_specified)
691 unlink(maj_min_dev);
692
693 return retval;
694}
695
696int main(int argc, char **argv)
697{
1aa1e248 698 int retval = 0;
6ecd4d1e
KS
699 char devpath[MAX_PATH_LEN];
700 char maj_min_dev[MAX_PATH_LEN];
c521693b 701 int newargc;
1aa1e248 702 const char *env;
c521693b
GKH
703 char **newargv;
704
1aa1e248
KS
705 logging_init("scsi_id");
706 sysfs_init();
707 dbg("argc is %d\n", argc);
c521693b 708
1aa1e248
KS
709 /* sysfs path can be overridden for testing */
710 env = getenv("SYSFS_PATH");
711 if (env) {
712 strncpy(sysfs_path, env, sizeof(sysfs_path));
713 sysfs_path[sizeof(sysfs_path)-1] = '\0';
714 } else
715 strcpy(sysfs_path, "/sys");
c521693b 716
1aa1e248
KS
717 env = getenv("DEVPATH");
718 if (env) {
07544a93 719 hotplug_mode = 1;
c521693b 720 sys_specified = 1;
6ecd4d1e 721 strncpy(devpath, env, MAX_PATH_LEN);
1aa1e248 722 devpath[sizeof(devpath)-1] = '\0';
c521693b
GKH
723 }
724
725 /*
07544a93 726 * Get config file options.
c521693b
GKH
727 */
728 newargv = NULL;
729 retval = get_file_options(NULL, NULL, &newargc, &newargv);
730 if (retval < 0) {
1aa1e248
KS
731 retval = 1;
732 goto exit;
733 }
734 if (newargv && (retval == 0)) {
735 if (set_options(newargc, newargv, short_options, devpath,
736 maj_min_dev) < 0) {
737 retval = 2;
738 goto exit;
739 }
c521693b
GKH
740 free(newargv);
741 }
1aa1e248 742
07544a93
PM
743 /*
744 * Get command line options (overriding any config file or DEVPATH
745 * settings).
746 */
1aa1e248 747 if (set_options(argc, argv, short_options, devpath, maj_min_dev) < 0)
062db23d 748 exit(1);
c521693b
GKH
749
750 if (!sys_specified) {
1aa1e248
KS
751 info("-s must be specified\n");
752 retval = 1;
753 goto exit;
c521693b
GKH
754 }
755
1aa1e248
KS
756 retval = scsi_id(devpath, maj_min_dev);
757
758exit:
759 sysfs_cleanup();
760 logging_close();
761 return retval;
c521693b 762}