]> git.ipfire.org Git - thirdparty/systemd.git/blame - extras/scsi_id/scsi_id.c
add install test to 'make buildtest'
[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
7 *
8 * This library is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License as
10 * published by the Free Software Foundation; either version 2.1 of the
11 * License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 * USA
22 */
23
24#include <stdio.h>
25#include <stdlib.h>
c521693b
GKH
26#include <unistd.h>
27#include <signal.h>
28#include <fcntl.h>
29#include <errno.h>
30#include <string.h>
31#include <syslog.h>
32#include <stdarg.h>
33#include <ctype.h>
34#include <sys/stat.h>
1bed1db4 35#include <sysfs/libsysfs.h>
e996d978 36#include "scsi_id_version.h"
c521693b
GKH
37#include "scsi_id.h"
38
1bed1db4 39#ifndef SCSI_ID_VERSION
c521693b 40#warning No version
1bed1db4 41#define SCSI_ID_VERSION "unknown"
c521693b
GKH
42#endif
43
44/*
45 * temporary names for mknod.
46 */
07544a93
PM
47#define TMP_DIR "/tmp"
48#define TMP_PREFIX "scsi"
c521693b 49
b484e436
PM
50/*
51 * XXX Note the 'e' (send output to stderr in all cases), and 'c' (callout)
52 * options are not supported, but other code is still left in place for
53 * now.
54 */
b4a2906b 55static const char short_options[] = "abd:f:gip:s:uvVx";
c521693b
GKH
56/*
57 * Just duplicate per dev options.
58 */
b484e436 59static const char dev_short_options[] = "bgp:";
c521693b
GKH
60
61char sysfs_mnt_path[SYSFS_PATH_MAX];
62
63static int all_good;
b4a2906b 64static int always_info;
c521693b
GKH
65static char *default_callout;
66static int dev_specified;
67static int sys_specified;
e996d978 68static char config_file[MAX_NAME_LEN] = SCSI_ID_CONFIG_FILE;
c521693b
GKH
69static int display_bus_id;
70static int default_page_code;
71static int use_stderr;
72static int debug;
73static int hotplug_mode;
01f950e2 74static int reformat_serial;
34129109
KS
75static int export;
76static char vendor_str[64];
77static char model_str[64];
aaff09a3
KS
78static char revision_str[16];
79static char type_str[16];
c521693b
GKH
80
81void log_message (int level, const char *format, ...)
82{
83 va_list args;
84
85 if (!debug && level == LOG_DEBUG)
86 return;
87
88 va_start (args, format);
89 if (!hotplug_mode || use_stderr) {
90 vfprintf(stderr, format, args);
91 } else {
92 static int logging_init = 0;
93 if (!logging_init) {
34129109 94 openlog ("scsi_id", LOG_PID, LOG_DAEMON);
c521693b
GKH
95 logging_init = 1;
96 }
97
98 vsyslog(level, format, args);
99 }
100 va_end (args);
101 return;
102}
103
70721db6 104static void set_str(char *to, const char *from, size_t count)
34129109 105{
70721db6 106 size_t i, j, len;
34129109 107
aaff09a3 108 /* strip trailing whitespace */
34129109 109 len = strnlen(from, count);
c907c823 110 while (len && isspace(from[len-1]))
34129109
KS
111 len--;
112
aaff09a3 113 /* strip leading whitespace */
34129109
KS
114 i = 0;
115 while (isspace(from[i]) && (i < len))
116 i++;
117
118 j = 0;
119 while (i < len) {
aaff09a3
KS
120 /* substitute multiple whitespace */
121 if (isspace(from[i])) {
122 while (isspace(from[i]))
123 i++;
34129109 124 to[j++] = '_';
aaff09a3
KS
125 }
126 /* skip chars */
127 if (from[i] == '/') {
128 i++;
129 continue;
130 }
131 to[j++] = from[i++];
132 }
133 to[j] = '\0';
134}
135
853ccc43 136static void set_type(char *to, const char *from, size_t len)
aaff09a3
KS
137{
138 int type_num;
139 char *eptr;
853ccc43 140 char *type = "generic";
aaff09a3
KS
141
142 type_num = strtoul(from, &eptr, 0);
143 if (eptr != from) {
144 switch (type_num) {
145 case 0:
853ccc43 146 type = "disk";
aaff09a3
KS
147 break;
148 case 1:
853ccc43 149 type = "tape";
aaff09a3
KS
150 break;
151 case 4:
853ccc43 152 type = "optical";
aaff09a3
KS
153 break;
154 case 5:
853ccc43 155 type = "cd";
aaff09a3
KS
156 break;
157 case 7:
853ccc43 158 type = "optical";
aaff09a3
KS
159 break;
160 case 0xe:
853ccc43 161 type = "disk";
aaff09a3
KS
162 break;
163 case 0xf:
853ccc43 164 type = "optical";
34129109
KS
165 break;
166 default:
aaff09a3 167 break;
34129109 168 }
34129109 169 }
853ccc43
KS
170 strncpy(to, type, len);
171 to[len-1] = '\0';
34129109
KS
172}
173
4e05e423
PM
174static int get_major_minor(struct sysfs_class_device *class_dev, int *maj,
175 int *min)
e996d978 176{
4e05e423 177 struct sysfs_attribute *dev_attr;
e996d978 178
4e05e423
PM
179 dev_attr = sysfs_get_classdev_attr(class_dev, "dev");
180 if (!dev_attr) {
c521693b
GKH
181 /*
182 * XXX This happens a lot, since sg has no dev attr.
e996d978
PM
183 * And now sysfsutils does not set a meaningful errno
184 * value. Someday change this back to a LOG_WARNING.
185 * And if sysfsutils changes, check for ENOENT and handle
186 * it separately.
c521693b 187 */
b484e436 188 log_message(LOG_DEBUG, "%s: could not get dev attribute: %s\n",
4e05e423 189 class_dev->name, strerror(errno));
c521693b
GKH
190 return -1;
191 }
c521693b 192
4e05e423
PM
193 dprintf("dev value %s", dev_attr->value); /* value has a trailing \n */
194 if (sscanf(dev_attr->value, "%u:%u", maj, min) != 2) {
c521693b 195 log_message(LOG_WARNING, "%s: invalid dev major/minor\n",
4e05e423 196 class_dev->name);
c521693b
GKH
197 return -1;
198 }
199
200 return 0;
201}
202
4e05e423
PM
203static int create_tmp_dev(struct sysfs_class_device *class_dev, char *tmpdev,
204 int dev_type)
c521693b 205{
062db23d 206 int maj, min;
c521693b 207
4e05e423 208 dprintf("(%s)\n", class_dev->name);
c521693b 209
4e05e423 210 if (get_major_minor(class_dev, &maj, &min))
c521693b
GKH
211 return -1;
212 snprintf(tmpdev, MAX_NAME_LEN, "%s/%s-maj%d-min%d-%u",
062db23d 213 TMP_DIR, TMP_PREFIX, maj, min, getpid());
c521693b
GKH
214
215 dprintf("tmpdev '%s'\n", tmpdev);
216
062db23d 217 if (mknod(tmpdev, 0600 | dev_type, makedev(maj, min))) {
c521693b
GKH
218 log_message(LOG_WARNING, "mknod failed: %s\n", strerror(errno));
219 return -1;
220 }
221 return 0;
222}
223
224static int has_sysfs_prefix(const char *path, const char *prefix)
225{
226 char match[MAX_NAME_LEN];
227
228 strncpy(match, sysfs_mnt_path, MAX_NAME_LEN);
229 strncat(match, prefix, MAX_NAME_LEN);
230 if (strncmp(path, match, strlen(match)) == 0)
231 return 1;
232 else
233 return 0;
234}
235
236/*
237 * get_value:
238 *
239 * buf points to an '=' followed by a quoted string ("foo") or a string ending
240 * with a space or ','.
241 *
242 * Return a pointer to the NUL terminated string, returns NULL if no
243 * matches.
244 */
245static char *get_value(char **buffer)
246{
247 static char *quote_string = "\"\n";
248 static char *comma_string = ",\n";
249 char *val;
250 char *end;
251
252 if (**buffer == '"') {
253 /*
254 * skip leading quote, terminate when quote seen
255 */
256 (*buffer)++;
257 end = quote_string;
258 } else {
259 end = comma_string;
260 }
261 val = strsep(buffer, end);
262 if (val && end == quote_string)
263 /*
264 * skip trailing quote
265 */
266 (*buffer)++;
267
268 while (isspace(**buffer))
269 (*buffer)++;
270
271 return val;
272}
273
274static int argc_count(char *opts)
275{
276 int i = 0;
277 while (*opts != '\0')
278 if (*opts++ == ' ')
279 i++;
280 return i;
281}
282
283/*
284 * get_file_options:
285 *
286 * If vendor == NULL, find a line in the config file with only "OPTIONS=";
287 * if vendor and model are set find the first OPTIONS line in the config
288 * file that matches. Set argc and argv to match the OPTIONS string.
289 *
290 * vendor and model can end in '\n'.
291 */
292static int get_file_options(char *vendor, char *model, int *argc,
293 char ***newargv)
294{
1bed1db4 295 char *buffer;
c521693b
GKH
296 FILE *fd;
297 char *buf;
298 char *str1;
299 char *vendor_in, *model_in, *options_in; /* read in from file */
300 int lineno;
301 int c;
302 int retval = 0;
c521693b
GKH
303
304 dprintf("vendor='%s'; model='%s'\n", vendor, model);
305 fd = fopen(config_file, "r");
306 if (fd == NULL) {
307 dprintf("can't open %s\n", config_file);
308 if (errno == ENOENT) {
309 return 1;
310 } else {
311 log_message(LOG_WARNING, "can't open %s: %s\n",
312 config_file, strerror(errno));
313 return -1;
314 }
315 }
316
1bed1db4
PM
317 /*
318 * Allocate a buffer rather than put it on the stack so we can
319 * keep it around to parse any options (any allocated newargv
320 * points into this buffer for its strings).
321 */
322 buffer = malloc(MAX_BUFFER_LEN);
323 if (!buffer) {
324 log_message(LOG_WARNING, "Can't allocate memory.\n");
325 return -1;
326 }
327
c521693b
GKH
328 *newargv = NULL;
329 lineno = 0;
c521693b
GKH
330 while (1) {
331 vendor_in = model_in = options_in = NULL;
332
1bed1db4 333 buf = fgets(buffer, MAX_BUFFER_LEN, fd);
c521693b
GKH
334 if (buf == NULL)
335 break;
336 lineno++;
1bed1db4
PM
337 if (buf[strlen(buffer) - 1] != '\n') {
338 log_message(LOG_WARNING,
339 "Config file line %d too long.\n", lineno);
340 break;
341 }
c521693b
GKH
342
343 while (isspace(*buf))
344 buf++;
345
346 if (*buf == '\0')
347 /*
348 * blank or all whitespace line
349 */
350 continue;
351
352 if (*buf == '#')
353 /*
354 * comment line
355 */
356 continue;
357
358#ifdef LOTS
359 dprintf("lineno %d: '%s'\n", lineno, buf);
360#endif
361 str1 = strsep(&buf, "=");
362 if (str1 && strcasecmp(str1, "VENDOR") == 0) {
363 str1 = get_value(&buf);
364 if (!str1) {
365 retval = -1;
366 break;
367 }
368 vendor_in = str1;
369
370 str1 = strsep(&buf, "=");
371 if (str1 && strcasecmp(str1, "MODEL") == 0) {
372 str1 = get_value(&buf);
373 if (!str1) {
374 retval = -1;
375 break;
376 }
377 model_in = str1;
378 str1 = strsep(&buf, "=");
379 }
380 }
381
382 if (str1 && strcasecmp(str1, "OPTIONS") == 0) {
383 str1 = get_value(&buf);
384 if (!str1) {
385 retval = -1;
386 break;
387 }
388 options_in = str1;
389 }
390 dprintf("config file line %d:"
391 " vendor '%s'; model '%s'; options '%s'\n",
392 lineno, vendor_in, model_in, options_in);
393 /*
394 * Only allow: [vendor=foo[,model=bar]]options=stuff
395 */
396 if (!options_in || (!vendor_in && model_in)) {
397 log_message(LOG_WARNING,
398 "Error parsing config file line %d '%s'\n",
399 lineno, buffer);
400 retval = -1;
401 break;
402 }
403 if (vendor == NULL) {
404 if (vendor_in == NULL) {
405 dprintf("matched global option\n");
406 break;
407 }
408 } else if ((vendor_in && strncmp(vendor, vendor_in,
409 strlen(vendor_in)) == 0) &&
410 (!model_in || (strncmp(model, model_in,
411 strlen(model_in)) == 0))) {
412 /*
413 * Matched vendor and optionally model.
414 *
415 * Note: a short vendor_in or model_in can
416 * give a partial match (that is FOO
417 * matches FOOBAR).
418 */
419 dprintf("matched vendor/model\n");
420 break;
421 } else {
422 dprintf("no match\n");
423 }
424 }
425
426 if (retval == 0) {
427 if (vendor_in != NULL || model_in != NULL ||
428 options_in != NULL) {
429 /*
430 * Something matched. Allocate newargv, and store
431 * values found in options_in.
432 */
1bed1db4
PM
433 strcpy(buffer, options_in);
434 c = argc_count(buffer) + 2;
c521693b
GKH
435 *newargv = calloc(c, sizeof(**newargv));
436 if (!*newargv) {
437 log_message(LOG_WARNING,
1bed1db4 438 "Can't allocate memory.\n");
c521693b
GKH
439 retval = -1;
440 } else {
441 *argc = c;
442 c = 0;
1bed1db4
PM
443 /*
444 * argv[0] at 0 is skipped by getopt, but
445 * store the buffer address there for
446 * alter freeing.
447 */
448 (*newargv)[c] = buffer;
c521693b 449 for (c = 1; c < *argc; c++)
1bed1db4 450 (*newargv)[c] = strsep(&buffer, " ");
c521693b
GKH
451 }
452 } else {
453 /*
454 * No matches.
455 */
456 retval = 1;
457 }
458 }
1bed1db4
PM
459 if (retval != 0)
460 free(buffer);
c521693b
GKH
461 fclose(fd);
462 return retval;
463}
464
465static int set_options(int argc, char **argv, const char *short_opts,
1bed1db4 466 char *target, char *maj_min_dev)
c521693b
GKH
467{
468 int option;
c521693b
GKH
469
470 /*
1bed1db4
PM
471 * optind is a global extern used by getopt. Since we can call
472 * set_options twice (once for command line, and once for config
473 * file) we have to reset this back to 1. [Note glibc handles
474 * setting this to 0, but klibc does not.]
c521693b 475 */
1bed1db4 476 optind = 1;
c521693b 477 while (1) {
853ccc43 478 option = getopt(argc, argv, short_opts);
c521693b
GKH
479 if (option == -1)
480 break;
481
482 if (optarg)
483 dprintf("option '%c' arg '%s'\n", option, optarg);
484 else
485 dprintf("option '%c'\n", option);
486
487 switch (option) {
b4a2906b
HR
488 case 'a':
489 always_info = 1;
490 break;
c521693b
GKH
491 case 'b':
492 all_good = 0;
493 break;
494
495 case 'c':
496 default_callout = optarg;
497 break;
498
499 case 'd':
500 dev_specified = 1;
501 strncpy(maj_min_dev, optarg, MAX_NAME_LEN);
502 break;
503
504 case 'e':
505 use_stderr = 1;
506 break;
507
508 case 'f':
509 strncpy(config_file, optarg, MAX_NAME_LEN);
510 break;
511
512 case 'g':
513 all_good = 1;
514 break;
515
516 case 'i':
517 display_bus_id = 1;
518 break;
519
520 case 'p':
521 if (strcmp(optarg, "0x80") == 0) {
522 default_page_code = 0x80;
523 } else if (strcmp(optarg, "0x83") == 0) {
524 default_page_code = 0x83;
525 } else {
526 log_message(LOG_WARNING,
527 "Unknown page code '%s'\n", optarg);
528 return -1;
529 }
530 break;
531
532 case 's':
533 sys_specified = 1;
534 strncpy(target, sysfs_mnt_path, MAX_NAME_LEN);
535 strncat(target, optarg, MAX_NAME_LEN);
536 break;
537
01f950e2
PM
538 case 'u':
539 reformat_serial = 1;
540 break;
541
34129109
KS
542 case 'x':
543 export = 1;
544 break;
545
c521693b
GKH
546 case 'v':
547 debug++;
548 break;
549
550 case 'V':
551 log_message(LOG_WARNING, "scsi_id version: %s\n",
1bed1db4 552 SCSI_ID_VERSION);
c521693b
GKH
553 exit(0);
554 break;
555
556 default:
557 log_message(LOG_WARNING,
558 "Unknown or bad option '%c' (0x%x)\n",
559 option, option);
560 return -1;
561 }
562 }
563 return 0;
564}
565
b484e436 566static int per_dev_options(struct sysfs_device *scsi_dev, int *good_bad,
c521693b
GKH
567 int *page_code, char *callout)
568{
569 int retval;
570 int newargc;
571 char **newargv = NULL;
aaff09a3 572 struct sysfs_attribute *vendor, *model, *type;
c521693b 573 int option;
c521693b
GKH
574
575 *good_bad = all_good;
576 *page_code = default_page_code;
577 if (default_callout && (callout != default_callout))
578 strncpy(callout, default_callout, MAX_NAME_LEN);
579 else
580 callout[0] = '\0';
581
4e05e423
PM
582 vendor = sysfs_get_device_attr(scsi_dev, "vendor");
583 if (!vendor) {
e996d978 584 log_message(LOG_WARNING, "%s: cannot get vendor attribute\n",
c521693b
GKH
585 scsi_dev->name);
586 return -1;
587 }
34129109 588 set_str(vendor_str, vendor->value, sizeof(vendor_str)-1);
c521693b 589
4e05e423
PM
590 model = sysfs_get_device_attr(scsi_dev, "model");
591 if (!model) {
e996d978 592 log_message(LOG_WARNING, "%s: cannot get model attribute\n",
c521693b
GKH
593 scsi_dev->name);
594 return -1;
595 }
34129109 596 set_str(model_str, model->value, sizeof(model_str)-1);
c521693b 597
aaff09a3
KS
598 type = sysfs_get_device_attr(scsi_dev, "type");
599 if (!type) {
600 log_message(LOG_WARNING, "%s: cannot get type attribute\n",
601 scsi_dev->name);
602 return -1;
603 }
853ccc43 604 set_type(type_str, type->value, sizeof(type_str));
aaff09a3
KS
605
606 type = sysfs_get_device_attr(scsi_dev, "rev");
607 if (!type) {
608 log_message(LOG_WARNING, "%s: cannot get type attribute\n",
609 scsi_dev->name);
610 return -1;
611 }
612 set_str(revision_str, type->value, sizeof(revision_str)-1);
613
4e05e423
PM
614 retval = get_file_options(vendor->value, model->value, &newargc,
615 &newargv);
c521693b 616
1bed1db4 617 optind = 1; /* reset this global extern */
c521693b 618 while (retval == 0) {
1bed1db4 619 option = getopt(newargc, newargv, dev_short_options);
c521693b
GKH
620 if (option == -1)
621 break;
622
623 if (optarg)
624 dprintf("option '%c' arg '%s'\n", option, optarg);
625 else
626 dprintf("option '%c'\n", option);
627
628 switch (option) {
629 case 'b':
630 *good_bad = 0;
631 break;
632
633 case 'c':
634 strncpy(callout, default_callout, MAX_NAME_LEN);
635 break;
636
637 case 'g':
638 *good_bad = 1;
639 break;
640
641 case 'p':
642 if (strcmp(optarg, "0x80") == 0) {
643 *page_code = 0x80;
644 } else if (strcmp(optarg, "0x83") == 0) {
645 *page_code = 0x83;
646 } else {
647 log_message(LOG_WARNING,
648 "Unknown page code '%s'\n", optarg);
649 retval = -1;
650 }
651 break;
652
653 default:
654 log_message(LOG_WARNING,
655 "Unknown or bad option '%c' (0x%x)\n",
656 option, option);
657 retval = -1;
658 break;
659 }
660 }
661
1bed1db4
PM
662 if (newargv) {
663 free(newargv[0]);
c521693b 664 free(newargv);
1bed1db4 665 }
c521693b
GKH
666 return retval;
667}
668
01f950e2
PM
669/*
670 * format_serial: replace to whitespaces by underscores for calling
671 * programs that use the serial for device naming (multipath, Suse
672 * naming, etc...)
673 */
674static void format_serial(char *serial)
675{
676 char *p = serial;
677
678 while (*p != '\0') {
679 if (isspace(*p))
680 *p = '_';
681 p++;
682 }
01f950e2
PM
683}
684
c521693b
GKH
685/*
686 * scsi_id: try to get an id, if one is found, printf it to stdout.
687 * returns a value passed to exit() - 0 if printed an id, else 1. This
688 * could be expanded, for example, if we want to report a failure like no
689 * memory etc. return 2, and return 1 for expected cases (like broken
690 * device found) that do not print an id.
691 */
692static int scsi_id(const char *target_path, char *maj_min_dev)
693{
694 int retval;
695 int dev_type = 0;
01f950e2 696 char *serial, *unaligned_buf;
b484e436
PM
697 struct sysfs_class_device *class_dev; /* of target_path */
698 struct sysfs_class_device *class_dev_parent; /* for partitions */
699 struct sysfs_device *scsi_dev; /* the scsi_device */
c521693b
GKH
700 int good_dev;
701 int page_code;
702 char callout[MAX_NAME_LEN];
703
704 dprintf("target_path %s\n", target_path);
705
706 /*
707 * Ugly: depend on the sysfs path to tell us whether this is a
708 * block or char device. This should probably be encoded in the
709 * "dev" along with the major/minor.
710 */
711 if (has_sysfs_prefix(target_path, "/block")) {
712 dev_type = S_IFBLK;
713 } else if (has_sysfs_prefix(target_path, "/class")) {
714 dev_type = S_IFCHR;
715 } else {
716 if (!hotplug_mode) {
717 log_message(LOG_WARNING,
718 "Non block or class device '%s'\n",
719 target_path);
720 return 1;
721 } else {
722 /*
723 * Expected in some cases.
724 */
725 dprintf("Non block or class device\n");
726 return 0;
727 }
728 }
729
b484e436
PM
730 class_dev = sysfs_open_class_device_path(target_path);
731 if (!class_dev) {
732 log_message(LOG_WARNING, "open class %s failed: %s\n",
733 target_path, strerror(errno));
734 return 1;
735 }
736 class_dev_parent = sysfs_get_classdev_parent(class_dev);
737 dprintf("class_dev 0x%p; class_dev_parent 0x%p\n", class_dev,
738 class_dev_parent);
739 if (class_dev_parent) {
740 scsi_dev = sysfs_get_classdev_device(class_dev_parent);
741 } else {
742 scsi_dev = sysfs_get_classdev_device(class_dev);
743 }
744
745 /*
746 * The close of scsi_dev will close class_dev or class_dev_parent.
747 */
748
749 /*
750 * We assume we are called after the device is completely ready,
751 * so we don't have to loop here like udev. (And we are usually
752 * called via udev.)
753 */
754 if (!scsi_dev) {
755 /*
756 * errno is not set if we can't find the device link, so
757 * don't print it out here.
758 */
759 log_message(LOG_WARNING, "Cannot find sysfs device associated with %s\n",
760 target_path);
c521693b 761 return 1;
b484e436 762 }
c521693b 763
c521693b
GKH
764
765 /*
b484e436 766 * Allow only scsi devices.
c521693b
GKH
767 *
768 * Other block devices can support SG IO, but only ide-cd does, so
769 * for now, don't bother with anything else.
770 */
b484e436 771 if (strcmp(scsi_dev->bus, "scsi") != 0) {
c521693b
GKH
772 if (hotplug_mode)
773 /*
774 * Expected in some cases.
775 */
776 dprintf("%s is not a scsi device\n", target_path);
777 else
778 log_message(LOG_WARNING, "%s is not a scsi device\n",
779 target_path);
780 return 1;
c521693b
GKH
781 }
782
783 /*
784 * mknod a temp dev to communicate with the device.
785 */
4e05e423 786 if (!dev_specified && create_tmp_dev(class_dev, maj_min_dev,
c521693b
GKH
787 dev_type)) {
788 dprintf("create_tmp_dev failed\n");
789 return 1;
790 }
791
c521693b
GKH
792 /*
793 * Get any per device (vendor + model) options from the config
794 * file.
795 */
796 retval = per_dev_options(scsi_dev, &good_dev, &page_code, callout);
797 dprintf("per dev options: good %d; page code 0x%x; callout '%s'\n",
798 good_dev, page_code, callout);
799
01f950e2
PM
800#define ALIGN 512
801 unaligned_buf = malloc(MAX_SERIAL_LEN + ALIGN);
b337e607
PM
802 serial = (char*) (((unsigned long) unaligned_buf + (ALIGN - 1))
803 & ~(ALIGN - 1));
01f950e2
PM
804 dprintf("buffer unaligned 0x%p; aligned 0x%p\n", unaligned_buf, serial);
805#undef ALIGN
806
c521693b
GKH
807 if (!good_dev) {
808 retval = 1;
809 } else if (callout[0] != '\0') {
810 /*
b484e436 811 * XXX Disabled for now ('c' is not in any options[]).
c521693b
GKH
812 */
813 retval = 1;
814 } else if (scsi_get_serial(scsi_dev, maj_min_dev, page_code,
815 serial, MAX_SERIAL_LEN)) {
b4a2906b 816 retval = always_info?0:1;
c521693b
GKH
817 } else {
818 retval = 0;
819 }
820 if (!retval) {
34129109
KS
821 if (export) {
822 static char serial_str[64];
823 printf("ID_VENDOR=%s\n", vendor_str);
824 printf("ID_MODEL=%s\n", model_str);
aaff09a3 825 printf("ID_REVISION=%s\n", revision_str);
34129109
KS
826 set_str(serial_str, serial, sizeof(serial_str));
827 printf("ID_SERIAL=%s\n", serial_str);
aaff09a3 828 printf("ID_TYPE=%s\n", type_str);
46f01c6d 829 printf("ID_BUS=scsi\n");
34129109
KS
830 } else {
831 if (reformat_serial)
832 format_serial(serial);
833 if (display_bus_id)
834 printf("%s: ", scsi_dev->name);
835 printf("%s\n", serial);
836 }
c521693b
GKH
837 dprintf("%s\n", serial);
838 retval = 0;
839 }
b484e436 840 sysfs_close_device(scsi_dev);
c521693b
GKH
841
842 if (!dev_specified)
843 unlink(maj_min_dev);
844
845 return retval;
846}
847
848int main(int argc, char **argv)
849{
850 int retval;
851 char *devpath;
852 char target_path[MAX_NAME_LEN];
853 char maj_min_dev[MAX_NAME_LEN];
854 int newargc;
855 char **newargv;
856
857 if (getenv("DEBUG"))
858 debug++;
859
c521693b
GKH
860 dprintf("argc is %d\n", argc);
861 if (sysfs_get_mnt_path(sysfs_mnt_path, MAX_NAME_LEN)) {
862 log_message(LOG_WARNING, "sysfs_get_mnt_path failed: %s\n",
863 strerror(errno));
864 exit(1);
865 }
866
07544a93
PM
867 devpath = getenv("DEVPATH");
868 if (devpath) {
c521693b 869 /*
07544a93 870 * This implies that we were invoked via udev or hotplug.
c521693b 871 */
07544a93 872 hotplug_mode = 1;
c521693b 873 sys_specified = 1;
c521693b
GKH
874 strncpy(target_path, sysfs_mnt_path, MAX_NAME_LEN);
875 strncat(target_path, devpath, MAX_NAME_LEN);
c521693b
GKH
876 }
877
878 /*
07544a93 879 * Get config file options.
c521693b
GKH
880 */
881 newargv = NULL;
882 retval = get_file_options(NULL, NULL, &newargc, &newargv);
883 if (retval < 0) {
884 exit(1);
885 } else if (newargv && (retval == 0)) {
1bed1db4
PM
886 if (set_options(newargc, newargv, short_options, target_path,
887 maj_min_dev) < 0)
c521693b
GKH
888 exit(1);
889 free(newargv);
890 }
07544a93
PM
891 /*
892 * Get command line options (overriding any config file or DEVPATH
893 * settings).
894 */
895 if (set_options(argc, argv, short_options, target_path,
896 maj_min_dev) < 0)
062db23d 897 exit(1);
c521693b
GKH
898
899 if (!sys_specified) {
900 log_message(LOG_WARNING, "-s must be specified\n");
901 exit(1);
902 }
903
904 retval = scsi_id(target_path, maj_min_dev);
905 exit(retval);
906}