chiark / gitweb /
[PATCH] udev use new libsysfs header file location
[elogind.git] / extras / scsi_id / scsi_serial.c
1 /*
2  * scsi_serial.c
3  *
4  * Code related to requesting and getting an id from a scsi device
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 <sys/types.h>
25 #include <sys/ioctl.h>
26 #include <stdio.h>
27 #include <errno.h>
28 #include <string.h>
29 #include <fcntl.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <syslog.h>
33 #include <scsi/sg.h>
34 #include <sysfs/libsysfs.h>
35
36 #include "scsi_id.h"
37 #include "scsi.h"
38
39 /*
40  * A priority based list of id, naa, and binary/ascii for the identifier
41  * descriptor in VPD page 0x83.
42  *
43  * Brute force search for a match starting with the first value in the
44  * following id_search_list. This is not a performance issue, since there
45  * is normally one or some small number of descriptors.
46  */
47 static const struct scsi_id_search_values id_search_list[] = {
48         { SCSI_ID_NAA,  SCSI_ID_NAA_IEEE_REG_EXTENDED,  SCSI_ID_BINARY },
49         { SCSI_ID_NAA,  SCSI_ID_NAA_IEEE_REG_EXTENDED,  SCSI_ID_ASCII },
50         { SCSI_ID_NAA,  SCSI_ID_NAA_IEEE_REG,   SCSI_ID_BINARY },
51         { SCSI_ID_NAA,  SCSI_ID_NAA_IEEE_REG,   SCSI_ID_ASCII },
52         /*
53          * Devices already exist using NAA values that are now marked
54          * reserved. These should not conflict with other values, or it is
55          * a bug in the device. As long as we find the IEEE extended one
56          * first, we really don't care what other ones are used. Using
57          * don't care here means that a device that returns multiple
58          * non-IEEE descriptors in a random order will get different
59          * names.
60          */
61         { SCSI_ID_NAA,  SCSI_ID_NAA_DONT_CARE,  SCSI_ID_BINARY },
62         { SCSI_ID_NAA,  SCSI_ID_NAA_DONT_CARE,  SCSI_ID_ASCII },
63         { SCSI_ID_EUI_64,       SCSI_ID_NAA_DONT_CARE,  SCSI_ID_BINARY },
64         { SCSI_ID_EUI_64,       SCSI_ID_NAA_DONT_CARE,  SCSI_ID_ASCII },
65         { SCSI_ID_T10_VENDOR,   SCSI_ID_NAA_DONT_CARE,  SCSI_ID_BINARY },
66         { SCSI_ID_T10_VENDOR,   SCSI_ID_NAA_DONT_CARE,  SCSI_ID_ASCII },
67         { SCSI_ID_VENDOR_SPECIFIC,      SCSI_ID_NAA_DONT_CARE,  SCSI_ID_BINARY },
68         { SCSI_ID_VENDOR_SPECIFIC,      SCSI_ID_NAA_DONT_CARE,  SCSI_ID_ASCII },
69 };
70
71 static const char hex_str[]="0123456789abcdef";
72
73 /*
74  * XXX maybe move all these to an sg_io.c file.
75  *
76  * From here ...
77  */
78
79 /*
80  * Values returned in the result/status, only the ones used by the code
81  * are used here.
82  */
83
84 #define DID_NO_CONNECT 0x01     /* Unable to connect before timeout */
85
86 #define DID_BUS_BUSY 0x02       /* Bus remain busy until timeout */
87 #define DID_TIME_OUT 0x03       /* Timed out for some other reason */
88
89 #define DRIVER_TIMEOUT 0x06
90 #define DRIVER_SENSE 0x08       /* Sense_buffer has been set */
91
92 /* The following "category" function returns one of the following */
93 #define SG_ERR_CAT_CLEAN        0      /* No errors or other information */
94 #define SG_ERR_CAT_MEDIA_CHANGED        1 /* interpreted from sense buffer */
95 #define SG_ERR_CAT_RESET        2      /* interpreted from sense buffer */
96 #define SG_ERR_CAT_TIMEOUT      3
97 #define SG_ERR_CAT_RECOVERED    4  /* Successful command after recovered err */
98 #define SG_ERR_CAT_SENSE        98     /* Something else in the sense buffer */
99 #define SG_ERR_CAT_OTHER        99     /* Some other error/warning */
100
101 static int sg_err_category_new(int scsi_status, int msg_status, int
102                                host_status, int driver_status, const
103                                unsigned char *sense_buffer, int sb_len)
104 {
105         scsi_status &= 0x7e;
106
107         /*
108          * XXX change to return only two values - failed or OK.
109          */
110
111         /*
112          * checks msg_status
113          */
114         if (!scsi_status && !msg_status && !host_status && !driver_status)
115                 return SG_ERR_CAT_CLEAN;
116
117         if ((scsi_status == SCSI_CHECK_CONDITION) ||
118             (scsi_status == SCSI_COMMAND_TERMINATED) ||
119             ((driver_status & 0xf) == DRIVER_SENSE)) {
120                 if (sense_buffer && (sb_len > 2)) {
121                         int sense_key;
122                         unsigned char asc;
123
124                         if (sense_buffer[0] & 0x2) {
125                                 sense_key = sense_buffer[1] & 0xf;
126                                 asc = sense_buffer[2];
127                         } else {
128                                 sense_key = sense_buffer[2] & 0xf;
129                                 asc = (sb_len > 12) ? sense_buffer[12] : 0;
130                         }
131
132                         if (sense_key == RECOVERED_ERROR)
133                                 return SG_ERR_CAT_RECOVERED;
134                         else if (sense_key == UNIT_ATTENTION) {
135                                 if (0x28 == asc)
136                                         return SG_ERR_CAT_MEDIA_CHANGED;
137                                 if (0x29 == asc)
138                                         return SG_ERR_CAT_RESET;
139                         }
140                 }
141                 return SG_ERR_CAT_SENSE;
142         }
143         if (!host_status) {
144                 if ((host_status == DID_NO_CONNECT) ||
145                     (host_status == DID_BUS_BUSY) ||
146                     (host_status == DID_TIME_OUT))
147                         return SG_ERR_CAT_TIMEOUT;
148         }
149         if (!driver_status) {
150                 if (driver_status == DRIVER_TIMEOUT)
151                         return SG_ERR_CAT_TIMEOUT;
152         }
153         return SG_ERR_CAT_OTHER;
154 }
155
156 static int sg_err_category3(struct sg_io_hdr *hp)
157 {
158         return sg_err_category_new(hp->status, hp->msg_status,
159                                    hp->host_status, hp->driver_status,
160                                    hp->sbp, hp->sb_len_wr);
161 }
162
163 static int scsi_dump_sense(struct sysfs_class_device *scsi_dev,
164                            struct sg_io_hdr *io)
165 {
166         unsigned char *sense_buffer;
167         int s;
168         int sb_len;
169         int code;
170         int sense_class;
171         int sense_key;
172         int descriptor_format;
173         int asc, ascq;
174 #ifdef DUMP_SENSE
175         char out_buffer[256];
176         int i, j;
177 #endif
178
179         /*
180          * Figure out and print the sense key, asc and ascq.
181          *
182          * If you want to suppress these for a particular drive model, add
183          * a black list entry in the scsi_id config file.
184          *
185          * XXX We probably need to: lookup the sense/asc/ascq in a retry
186          * table, and if found return 1 (after dumping the sense, asc, and
187          * ascq). So, if/when we get something like a power on/reset,
188          * we'll retry the command.
189          */
190
191         dprintf("got check condition\n");
192
193         sb_len = io->sb_len_wr;
194         if (sb_len < 1) {
195                 log_message(LOG_WARNING, "%s: sense buffer empty\n",
196                             scsi_dev->name);
197                 return -1;
198         }
199
200         sense_buffer = io->sbp;
201         sense_class = (sense_buffer[0] >> 4) & 0x07;
202         code = sense_buffer[0] & 0xf;
203
204         if (sense_class == 7) {
205                 /*
206                  * extended sense data.
207                  */
208                 s = sense_buffer[7] + 8;
209                 if (sb_len < s) {
210                         log_message(LOG_WARNING,
211                                     "%s: sense buffer too small %d bytes,"
212                                     " %d bytes too short\n", scsi_dev->name,
213                                     sb_len, s - sb_len);
214                         return -1;
215                 }
216                 if ((code == 0x0) || (code == 0x1)) {
217                         descriptor_format = 0;
218                         sense_key = sense_buffer[2] & 0xf;
219                         if (s < 14) {
220                                 /*
221                                  * Possible?
222                                  */
223                                 log_message(LOG_WARNING, "%s: sense result too"
224                                             " small %d bytes\n",
225                                             scsi_dev->name, s);
226                                 return -1;
227                         }
228                         asc = sense_buffer[12];
229                         ascq = sense_buffer[13];
230                 } else if ((code == 0x2) || (code == 0x3)) {
231                         descriptor_format = 1;
232                         sense_key = sense_buffer[1] & 0xf;
233                         asc = sense_buffer[2];
234                         ascq = sense_buffer[3];
235                 } else {
236                         log_message(LOG_WARNING,
237                                     "%s: invalid sense code 0x%x\n",
238                                     scsi_dev->name, code);
239                         return -1;
240                 }
241                 log_message(LOG_WARNING,
242                             "%s: sense key 0x%x ASC 0x%x ASCQ 0x%x\n",
243                             scsi_dev->name, sense_key, asc, ascq);
244         } else {
245                 if (sb_len < 4) {
246                         log_message(LOG_WARNING,
247                                     "%s: sense buffer too small %d bytes, %d bytes too short\n",
248                                     scsi_dev->name, sb_len, 4 - sb_len);
249                         return -1;
250                 }
251
252                 if (sense_buffer[0] < 15)
253                         log_message(LOG_WARNING, "%s: old sense key: 0x%x\n",
254                                     scsi_dev->name, sense_buffer[0] & 0x0f);
255                 else
256                         log_message(LOG_WARNING, "%s: sense = %2x %2x\n",
257                                     scsi_dev->name,  sense_buffer[0],
258                                     sense_buffer[2]);
259                 log_message(LOG_WARNING,
260                             "%s: non-extended sense class %d code 0x%0x ",
261                             scsi_dev->name, sense_class, code);
262
263         }
264
265 #ifdef DUMP_SENSE
266         for (i = 0, j = 0; (i < s) && (j < 254); i++) {
267                 dprintf("i %d, j %d\n", i, j);
268                 out_buffer[j++] = hex_str[(sense_buffer[i] & 0xf0) >> 4];
269                 out_buffer[j++] = hex_str[sense_buffer[i] & 0x0f];
270                 out_buffer[j++] = ' ';
271         }
272         out_buffer[j] = '\0';
273         log_message(LOG_WARNING, "%s: sense dump:\n", scsi_dev->name);
274         log_message(LOG_WARNING, "%s: %s\n", scsi_dev->name, out_buffer);
275
276 #endif
277         return -1;
278 }
279
280 static int scsi_dump(struct sysfs_class_device *scsi_dev, struct sg_io_hdr *io)
281 {
282         if (!io->status && !io->host_status && !io->msg_status &&
283             !io->driver_status) {
284                 /*
285                  * Impossible, should not be called.
286                  */
287                 log_message(LOG_WARNING, "%s: called with no error\n",
288                             __FUNCTION__);
289                 return -1;
290         }
291
292         log_message(LOG_WARNING, "%s: sg_io failed status 0x%x 0x%x 0x%x 0x%x\n",
293                     scsi_dev->name, io->driver_status, io->host_status,
294                     io->msg_status, io->status);
295         if (io->status == SCSI_CHECK_CONDITION)
296                 return scsi_dump_sense(scsi_dev, io);
297         else
298                 return -1;
299 }
300
301 static int scsi_inquiry(struct sysfs_class_device *scsi_dev, int fd,
302                         unsigned char evpd, unsigned char page, unsigned
303                         char *buf, unsigned int buflen)
304 {
305         unsigned char inq_cmd[INQUIRY_CMDLEN] =
306                 { INQUIRY_CMD, evpd, page, 0, buflen, 0 };
307         unsigned char sense[SENSE_BUFF_LEN];
308         struct sg_io_hdr io_hdr;
309         int retval;
310         unsigned char *inq;
311         unsigned char *buffer;
312         int retry = 3; /* rather random */
313
314         if (buflen > 255) {
315                 log_message(LOG_WARNING, "buflen %d too long\n", buflen);
316                 return -1;
317         }
318         inq = malloc(OFFSET + sizeof (inq_cmd) + 512);
319         memset(inq, 0, OFFSET + sizeof (inq_cmd) + 512);
320         buffer = inq + OFFSET;
321
322 resend:
323         memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
324         io_hdr.interface_id = 'S';
325         io_hdr.cmd_len = sizeof(inq_cmd);
326         io_hdr.mx_sb_len = sizeof(sense);
327         io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
328         io_hdr.dxfer_len = buflen;
329         io_hdr.dxferp = buffer;
330         io_hdr.cmdp = inq_cmd;
331         io_hdr.sbp = sense;
332         io_hdr.timeout = DEF_TIMEOUT;
333
334         if (ioctl(fd, SG_IO, &io_hdr) < 0) {
335                 log_message(LOG_WARNING, "%s ioctl failed: %s\n",
336                             scsi_dev->name, strerror(errno));
337                 return -1;
338         }
339
340         retval = sg_err_category3(&io_hdr);
341
342         switch (retval) {
343                 case SG_ERR_CAT_CLEAN:
344                 case SG_ERR_CAT_RECOVERED:
345                         retval = 0;
346                         break;
347
348                 default:
349                         retval = scsi_dump(scsi_dev, &io_hdr);
350         }
351
352         /*
353          * XXX where is the length checked? That is, was our request
354          * buffer long enough?
355          */
356
357         if (!retval) {
358                 retval = buflen;
359                 memcpy(buf, buffer, retval);
360         } else if (retval > 0) {
361                 if (--retry > 0) {
362                         dprintf("%s: Retrying ...\n", scsi_dev->name);
363                         goto resend;
364                 }
365                 retval = -1;
366         }
367
368         free(inq);
369         return retval;
370 }
371
372 /*
373  * XXX maybe move all these to an sg_io.c file.
374  *
375  * Ending here.
376  */
377
378 static int do_scsi_page0_inquiry(struct sysfs_class_device *scsi_dev, int fd,
379                                  char *buffer, int len)
380 {
381         int retval;
382         char vendor[MAX_ATTR_LEN];
383
384         memset(buffer, 0, len);
385         retval = scsi_inquiry(scsi_dev, fd, 1, 0x0, buffer, len);
386         if (retval < 0)
387                 return 1;
388
389         if (buffer[1] != 0) {
390                 log_message(LOG_WARNING, "%s: page 0 not available.\n",
391                             scsi_dev->name);
392                 return 1;
393         }
394         if (buffer[3] > len) {
395                 log_message(LOG_WARNING, "%s: page 0 buffer too long %d",
396                            scsi_dev->name,  buffer[3]);
397                 return 1;
398         }
399
400         /*
401          * Following check is based on code once included in the 2.5.x
402          * kernel.
403          *
404          * Some ill behaved devices return the standard inquiry here
405          * rather than the evpd data, snoop the data to verify.
406          */
407         if (buffer[3] > MODEL_LENGTH) {
408                 /*
409                  * If the vendor id appears in the page assume the page is
410                  * invalid.
411                  */
412                 if (sysfs_get_attr(scsi_dev->path, "vendor", vendor,
413                                    MAX_ATTR_LEN)) {
414                         log_message(LOG_WARNING,
415                                     "%s: cannot get model attribute\n",
416                                     scsi_dev->name);
417                         return 1;
418                 }
419                 if (!strncmp(&buffer[VENDOR_LENGTH], vendor, VENDOR_LENGTH)) {
420                         log_message(LOG_WARNING, "%s invalid page0 data\n",
421                                     scsi_dev->name);
422                         return 1;
423                 }
424         }
425         return 0;
426 }
427
428 /*
429  * The caller checks that serial is long enough to include the vendor +
430  * model.
431  */
432 static int prepend_vendor_model(struct sysfs_class_device *scsi_dev,
433                                 char *serial)
434 {
435         char attr[MAX_ATTR_LEN];
436         int ind;
437
438         if (sysfs_get_attr(scsi_dev->path, "vendor", attr, MAX_ATTR_LEN)) {
439                 log_message(LOG_WARNING, "%s: cannot get vendor attribute\n",
440                             scsi_dev->name);
441                 return 1;
442         }
443         strncpy(serial, attr, VENDOR_LENGTH);
444         ind = strlen(serial) - 1;
445         /*
446          * Remove sysfs added newlines.
447          */
448         if (serial[ind] == '\n')
449                 serial[ind] = '\0';
450
451         if (sysfs_get_attr(scsi_dev->path, "model", attr, MAX_ATTR_LEN)) {
452                 log_message(LOG_WARNING, "%s: cannot get model attribute\n",
453                             scsi_dev->name);
454                 return 1;
455         }
456         strncat(serial, attr, MODEL_LENGTH);
457         ind = strlen(serial) - 1;
458         if (serial[ind] == '\n')
459                 serial[ind] = '\0';
460         else
461                 ind++;
462
463         /*
464          * This is not a complete check, since we are using strncat/cpy
465          * above, ind will never be too large.
466          */
467         if (ind != (VENDOR_LENGTH + MODEL_LENGTH)) {
468                 log_message(LOG_WARNING, "%s: expected length %d, got length %d\n",
469                             scsi_dev->name, (VENDOR_LENGTH + MODEL_LENGTH),
470                             ind);
471                 return 1;
472         }
473         return ind;
474 }
475
476 /**
477  * check_fill_0x83_id - check the page 0x83 id, if OK allocate and fill
478  * serial number.
479  **/
480 static int check_fill_0x83_id(struct sysfs_class_device *scsi_dev,
481                               char *page_83,
482                               const struct scsi_id_search_values *id_search,
483                               char *serial, int max_len)
484 {
485         int i, j, len;
486
487         /*
488          * ASSOCIATION must be with the device (value 0)
489          */
490         if ((page_83[1] & 0x30) != 0)
491                 return 1;
492
493         if ((page_83[1] & 0x0f) != id_search->id_type)
494                 return 1;
495
496         /*
497          * Possibly check NAA sub-type.
498          */
499         if ((id_search->naa_type != SCSI_ID_NAA_DONT_CARE) &&
500             (id_search->naa_type != (page_83[4] & 0xf0) >> 4))
501                 return 1;
502
503         /*
504          * Check for matching code set - ASCII or BINARY.
505          */
506         if ((page_83[0] & 0x0f) != id_search->code_set)
507                 return 1;
508
509         /*
510          * page_83[3]: identifier length
511          */
512         len = page_83[3];
513         if ((page_83[0] & 0x0f) != SCSI_ID_ASCII)
514                 /*
515                  * If not ASCII, use two bytes for each binary value.
516                  */
517                 len *= 2;
518
519         /*
520          * Add one byte for the NUL termination, and one for the id_type.
521          */
522         len += 2;
523         if (id_search->id_type == SCSI_ID_VENDOR_SPECIFIC)
524                 len += VENDOR_LENGTH + MODEL_LENGTH;
525
526         if (max_len < len) {
527                 log_message(LOG_WARNING, "%s: length %d too short - need %d\n",
528                             scsi_dev->name, max_len, len);
529                 return 1;
530         }
531
532         serial[0] = hex_str[id_search->id_type];
533
534         /*
535          * For SCSI_ID_VENDOR_SPECIFIC prepend the vendor and model before
536          * the id since it is not unique across all vendors and models,
537          * this differs from SCSI_ID_T10_VENDOR, where the vendor is
538          * included in the identifier.
539          */
540         if (id_search->id_type == SCSI_ID_VENDOR_SPECIFIC)
541                 if (prepend_vendor_model(scsi_dev, &serial[1]) < 0) {
542                         dprintf("prepend failed\n");
543                         return 1;
544                 }
545
546         i = 4; /* offset to the start of the identifier */
547         j = strlen(serial);
548         if ((page_83[0] & 0x0f) == SCSI_ID_ASCII) {
549                 /*
550                  * ASCII descriptor.
551                  */
552                 while (i < (4 + page_83[3]))
553                         serial[j++] = page_83[i++];
554         } else {
555                 /*
556                  * Binary descriptor, convert to ASCII, using two bytes of
557                  * ASCII for each byte in the page_83.
558                  */
559                 while (i < (4 + page_83[3])) {
560                         serial[j++] = hex_str[(page_83[i] & 0xf0) >> 4];
561                         serial[j++] = hex_str[page_83[i] & 0x0f];
562                         i++;
563                 }
564         }
565         return 0;
566 }
567
568 static int do_scsi_page83_inquiry(struct sysfs_class_device *scsi_dev, int fd,
569                                   char *serial, int len)
570 {
571         int retval;
572         int id_ind, j;
573         unsigned char page_83[256];
574
575         memset(page_83, 0, 256);
576         retval = scsi_inquiry(scsi_dev, fd, 1, 0x83, page_83, 255);
577         if (retval < 0)
578                 return 1;
579
580         if (page_83[1] != 0x83) {
581                 log_message(LOG_WARNING, "%s: Invalid page 0x83\n",
582                             scsi_dev->name);
583                 return 1;
584         }
585         
586         /*
587          * XXX Some devices (IBM 3542) return all spaces for an identifier if
588          * the LUN is not actually configured. This leads to identifers of
589          * the form: "1            ".
590          */
591
592         /*
593          * Search for a match in the prioritized id_search_list.
594          */
595         for (id_ind = 0;
596              id_ind < sizeof(id_search_list)/sizeof(id_search_list[0]);
597              id_ind++) {
598                 /*
599                  * Examine each descriptor returned. There is normally only
600                  * one or a small number of descriptors.
601                  */
602                 for (j = 4; j <= page_83[3] + 3;
603                         j += page_83[j + 3] + 4) {
604                         retval = check_fill_0x83_id(scsi_dev, &page_83[j],
605                                                     &id_search_list[id_ind],
606                                                     serial, len);
607                         dprintf("%s id desc %d/%d/%d\n", scsi_dev->name,
608                                 id_search_list[id_ind].id_type,
609                                 id_search_list[id_ind].naa_type,
610                                 id_search_list[id_ind].code_set);
611                         if (!retval) {
612                                 dprintf("       used\n");
613                                 return retval;
614                         } else if (retval < 0) {
615                                 dprintf("       failed\n");
616                                 return retval;
617                         } else {
618                                 dprintf("       not used\n");
619                         }
620                 }
621         }
622         return 1;
623 }
624
625 static int do_scsi_page80_inquiry(struct sysfs_class_device *scsi_dev, int fd,
626                                   char *serial, int max_len)
627 {
628         int retval;
629         int ser_ind;
630         int i;
631         int len;
632         unsigned char buf[256];
633
634         memset(buf, 0, 256);
635         retval = scsi_inquiry(scsi_dev, fd, 1, 0x80, buf, 255);
636         if (retval < 0)
637                 return retval;
638
639         if (buf[1] != 0x80) {
640                 log_message(LOG_WARNING, "%s: Invalid page 0x80\n",
641                             scsi_dev->name);
642                 return 1;
643         }
644
645         len = 1 + VENDOR_LENGTH + MODEL_LENGTH + buf[3];
646         if (max_len < len) {
647                 log_message(LOG_WARNING, "%s: length %d too short - need %d\n",
648                             scsi_dev->name, max_len, len);
649                 return 1;
650         }
651         /*
652          * Prepend 'S' to avoid unlikely collision with page 0x83 vendor
653          * specific type where we prepend '0' + vendor + model.
654          */
655         serial[0] = 'S';
656         ser_ind = prepend_vendor_model(scsi_dev, &serial[1]);
657         if (ser_ind < 0)
658                 return 1;
659         len = buf[3];
660         for (i = 4; i < len + 4; i++, ser_ind++)
661                 serial[ser_ind] = buf[i];
662         return 0;
663 }
664
665 int scsi_get_serial (struct sysfs_class_device *scsi_dev, const char *devname,
666                      int page_code, char *serial, int len)
667 {
668         unsigned char page0[256];
669         int fd;
670         int ind;
671         int retval;
672
673         if (len > 255) {
674         }
675         memset(serial, 0, len);
676         dprintf("opening %s\n", devname);
677         fd = open(devname, O_RDONLY);
678         if (fd < 0) {
679                 log_message(LOG_WARNING, "%s cannot open %s: %s\n",
680                             scsi_dev->name, devname, strerror(errno));
681                 return 1;
682         }
683
684         if (page_code == 0x80) {
685                 if (do_scsi_page80_inquiry(scsi_dev, fd, serial, len)) {
686                         retval = 1;
687                         goto completed;
688                 } else  {
689                         retval = 0;
690                         goto completed;
691                 }
692         } else if (page_code == 0x83) {
693                 if (do_scsi_page83_inquiry(scsi_dev, fd, serial, len)) {
694                         retval = 1;
695                         goto completed;
696                 } else  {
697                         retval = 0;
698                         goto completed;
699                 }
700         } else if (page_code != 0x00) {
701                 log_message(LOG_WARNING, "%s unsupported page code 0x%d\n",
702                             scsi_dev->name, page_code);
703                 return 1;
704         }
705
706         /*
707          * Get page 0, the page of the pages. By default, try from best to
708          * worst of supported pages: 0x83 then 0x80.
709          */
710         if (do_scsi_page0_inquiry(scsi_dev, fd, page0, 255)) {
711                 /*
712                  * Don't try anything else. Black list if a specific page
713                  * should be used for this vendor+model, or maybe have an
714                  * optional fall-back to page 0x80 or page 0x83.
715                  */
716                 retval = 1;
717                 goto completed;
718         }
719
720         dprintf("%s: Checking page0\n", scsi_dev->name);
721
722         for (ind = 4; ind <= page0[3] + 3; ind++)
723                 if (page0[ind] == 0x83)
724                         if (!do_scsi_page83_inquiry(scsi_dev, fd, serial,
725                                                     len)) {
726                                 /*
727                                  * Success
728                                  */
729                                 retval = 0;
730                                 goto completed;
731                         }
732
733         for (ind = 4; ind <= page0[3] + 3; ind++)
734                 if (page0[ind] == 0x80)
735                         if (!do_scsi_page80_inquiry(scsi_dev, fd, serial,
736                                                     len)) {
737                                 /*
738                                  * Success
739                                  */
740                                 retval = 0;
741                                 goto completed;
742                         }
743         retval = 1;
744 completed:
745         if (close(fd) < 0)
746                 log_message(LOG_WARNING, "close failed: %s", strerror(errno));
747         return retval;
748 }