]>
Commit | Line | Data |
---|---|---|
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 | 37 | static const char short_options[] = "abd:f:gip:s:uvVx"; |
b484e436 | 38 | static const char dev_short_options[] = "bgp:"; |
c521693b | 39 | |
c521693b | 40 | static int all_good; |
b4a2906b | 41 | static int always_info; |
c521693b GKH |
42 | static int dev_specified; |
43 | static int sys_specified; | |
6ecd4d1e | 44 | static char config_file[MAX_PATH_LEN] = SCSI_ID_CONFIG_FILE; |
c521693b | 45 | static int display_bus_id; |
50be1401 | 46 | static enum page_code default_page_code; |
c521693b GKH |
47 | static int use_stderr; |
48 | static int debug; | |
49 | static int hotplug_mode; | |
01f950e2 | 50 | static int reformat_serial; |
34129109 KS |
51 | static int export; |
52 | static char vendor_str[64]; | |
53 | static char model_str[64]; | |
aaff09a3 KS |
54 | static char revision_str[16]; |
55 | static char type_str[16]; | |
c521693b | 56 | |
1aa1e248 KS |
57 | #ifdef USE_LOG |
58 | void 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 | 82 | static 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 | 114 | static 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 | 152 | static 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 | */ | |
190 | static 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 | ||
219 | static 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 |
237 | static 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 | ||
397 | static 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 | 493 | static 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 | */ | |
586 | static 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 | 613 | static 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 | ||
692 | int 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 | ||
754 | exit: | |
755 | sysfs_cleanup(); | |
756 | logging_close(); | |
757 | return retval; | |
c521693b | 758 | } |