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