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