- remove some obsolete code
[opensuse:hwinfo.git] / src / hd / block.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <errno.h>
6 #include <fcntl.h>
7 #include <ctype.h>
8 #include <sys/stat.h>
9 #include <sys/types.h>
10 #include <sys/ioctl.h>
11 #include <linux/iso_fs.h>
12 #include <scsi/sg.h>
13
14 #include "hd.h"
15 #include "hd_int.h"
16 #include "hddb.h"
17 #include "block.h"
18
19 /**
20  * @defgroup BLOCKint Block devices
21  * @ingroup  libhdDEVint
22  * @brief Block device functions
23  *
24  * @{
25  */
26
27 static void get_block_devs(hd_data_t *hd_data);
28 static void add_partitions(hd_data_t *hd_data, hd_t *hd, char *path);
29 static void add_cdrom_info(hd_data_t *hd_data, hd_t *hd);
30 static void add_other_sysfs_info(hd_data_t *hd_data, hd_t *hd);
31 static void add_ide_sysfs_info(hd_data_t *hd_data, hd_t *hd);
32 static void add_scsi_sysfs_info(hd_data_t *hd_data, hd_t *hd, char *sf_dev);
33 static void read_partitions(hd_data_t *hd_data);
34 static void read_cdroms(hd_data_t *hd_data);
35 static cdrom_info_t *new_cdrom_entry(cdrom_info_t **ci);
36 static cdrom_info_t *get_cdrom_entry(cdrom_info_t *ci, int n);
37 static void get_scsi_tape(hd_data_t *hd_data);
38 static void get_generic_scsi_devs(hd_data_t *hd_data);
39 static void add_disk_size(hd_data_t *hd_data, hd_t *hd);
40
41
42 void hd_scan_sysfs_block(hd_data_t *hd_data)
43 {
44   if(!hd_probe_feature(hd_data, pr_block)) return;
45
46   hd_data->module = mod_block;
47
48   /* some clean-up */
49   remove_hd_entries(hd_data);
50
51   hd_data->disks = free_str_list(hd_data->disks);
52   hd_data->partitions = free_str_list(hd_data->partitions);
53   hd_data->cdroms = free_str_list(hd_data->cdroms);
54
55   if(hd_probe_feature(hd_data, pr_block_mods)) {
56     PROGRESS(1, 0, "block modules");
57     // load_module(hd_data, "ide-cd");
58     load_module(hd_data, "ide-cd_mod");
59     load_module(hd_data, "ide-disk");
60     load_module(hd_data, "sr_mod");
61     load_module(hd_data, "sd_mod");
62 #if !defined(__s390__) && !defined(__s390x__)
63     load_module(hd_data, "st");
64 #endif
65   }
66
67   PROGRESS(2, 0, "sysfs drivers");
68
69   hd_sysfs_driver_list(hd_data);
70
71   PROGRESS(3, 0, "cdrom");
72
73   read_cdroms(hd_data);
74
75   PROGRESS(4, 0, "partition");
76
77   read_partitions(hd_data);
78
79   PROGRESS(5, 0, "get sysfs block dev data");
80
81   get_block_devs(hd_data);
82
83   if(hd_data->cdrom) {
84     ADD2LOG("oops: cdrom list not empty\n");
85   }
86 }
87
88
89 void get_block_devs(hd_data_t *hd_data)
90 {
91   str_list_t *sl;
92   char *s, *t;
93   unsigned u1, u2, u3;
94   uint64_t ul0;
95   hd_t *hd, *hd1;
96   hd_dev_num_t dev_num;
97   str_list_t *sf_class, *sf_class_e;
98   char *sf_cdev = NULL, *sf_dev = NULL, *sf_dev_ide;
99   char *sf_drv_name, *sf_drv, *bus_id, *bus_name, *ide_bus_id;
100   str_list_t *sf_bus, *sf_bus_e;
101   char *sf_block_dir;
102
103   hd_data->lsscsi = free_str_list(hd_data->lsscsi);
104   hd_data->lsscsi = read_file("|/usr/bin/lsscsi -t 2>/dev/null", 0, 0);
105
106   ADD2LOG("-----  lsscsi -----\n");
107   for(sl = hd_data->lsscsi; sl; sl = sl->next) {
108     ADD2LOG("  %s", sl->str);
109   }
110   ADD2LOG("-----  lsscsi end -----\n");
111
112   sf_bus = read_dir("/sys/bus/ide/devices", 'l');
113
114   if(sf_bus) {
115     for(sf_bus_e = sf_bus; sf_bus_e; sf_bus_e = sf_bus_e->next) {
116       sf_dev = new_str(hd_read_sysfs_link("/sys/bus/ide/devices", sf_bus_e->str));
117       ADD2LOG(
118         "  ide: bus_id = %s path = %s\n",
119         sf_bus_e->str,
120         hd_sysfs_id(sf_dev)
121       );
122       free_mem(sf_dev);
123     }
124   }
125
126   sf_block_dir = "/sys/subsystem/block";
127   sf_class = read_dir(sf_block_dir, 'l');
128
129   if (!sf_class) {
130     sf_block_dir = "/sys/class/block";
131     sf_class = read_dir(sf_block_dir, 'l');
132   }
133
134   if (!sf_class) {
135     sf_block_dir = "/sys/block";
136     sf_class = read_dir(sf_block_dir, 'd');
137   }
138
139   if(!sf_class) {
140     ADD2LOG("sysfs: no such class: block\n");
141     return;
142   }
143
144   for(sf_class_e = sf_class; sf_class_e; sf_class_e = sf_class_e->next) {
145     str_printf(&sf_cdev, 0, "%s/%s", sf_block_dir, sf_class_e->str);
146     ADD2LOG(
147       "  block: name = %s, path = %s\n",
148       sf_class_e->str,
149       hd_sysfs_id(sf_cdev)
150     );
151
152     memset(&dev_num, 0, sizeof dev_num);
153
154     if((s = get_sysfs_attr_by_path(sf_cdev, "dev"))) {
155       if(sscanf(s, "%u:%u", &u1, &u2) == 2) {
156         dev_num.type = 'b';
157         dev_num.major = u1;
158         dev_num.minor = u2;
159         dev_num.range = 1;
160       }
161       ADD2LOG("    dev = %u:%u\n", u1, u2);
162     }
163
164     if(hd_attr_uint(get_sysfs_attr_by_path(sf_cdev, "range"), &ul0, 0)) {
165       dev_num.range = ul0;
166       ADD2LOG("    range = %u\n", dev_num.range);
167     }
168
169     sf_dev = new_str(hd_read_sysfs_link(sf_cdev, "device"));
170     sf_drv_name = NULL;
171     sf_drv = hd_read_sysfs_link(sf_dev, "driver");
172     if(!sf_drv) {
173       /* maybe older kernel */
174       sf_drv = hd_read_sysfs_link(sf_cdev, "driver");
175     }
176     if(sf_drv) {
177       sf_drv_name = strrchr(sf_drv, '/');
178       if(sf_drv_name) sf_drv_name++;
179     }
180
181     sf_drv_name = new_str(sf_drv_name);
182
183     bus_id = sf_dev ? strrchr(sf_dev, '/') : NULL;
184     if(bus_id) bus_id++;
185
186     bus_name = NULL;
187     if(
188       (s = hd_read_sysfs_link(sf_dev, "subsystem")) ||
189       (s = hd_read_sysfs_link(sf_dev, "bus"))
190     ) {
191       bus_name = strrchr(s, '/');
192       if(bus_name) bus_name++;
193       bus_name = new_str(bus_name);
194     }
195
196     if(sf_dev) {
197       ADD2LOG(
198         "    block device: bus = %s, bus_id = %s driver = %s\n      path = %s\n",
199         bus_name,
200         bus_id,
201         sf_drv_name,
202         hd_sysfs_id(sf_dev)
203       );
204     }
205
206     hd = NULL;
207
208 #if defined(__s390x__) || defined(__s390__)
209     /* check if disk is DASD and has already been found by s390.c */
210     if(sf_drv_name && strstr(sf_drv_name,"dasd"))
211     {
212       char bid[9];
213       hd_res_t* res;
214       //fprintf(stderr,"dn %s bi %s\n",sf_drv_name,bus_id);
215       for(hd=hd_data->hd;hd;hd=hd->next)
216       {
217         //fprintf(stderr,"bcid %d\n",hd->base_class.id);
218         if(hd->base_class.id == bc_storage_device
219            && hd->detail
220            && hd->detail->ccw.type == hd_detail_ccw)
221         {
222           for(res=hd->res;res;res=res->next)
223           {
224             if(res->io.type==res_io)
225             {
226               sprintf(bid,"%01x.%01x.%04x",
227                       hd->detail->ccw.data->lcss >> 8,
228                       hd->detail->ccw.data->lcss & 0xff,
229                       (unsigned short)res->io.base);
230               //fprintf(stderr,"bid %s\n",bid);
231               if (strcmp(bid,bus_id)==0) goto out;
232             }
233           }
234         }
235       }
236       hd=NULL;
237       out:;
238     }
239     else
240 #endif
241
242     if((sl = search_str_list(hd_data->disks, hd_sysfs_name2_dev(sf_class_e->str)))) {
243       hd = add_hd_entry(hd_data, __LINE__, 0);
244       hd->sub_class.id = sc_sdev_disk;
245     }
246     else if((sl = search_str_list(hd_data->cdroms, hd_sysfs_name2_dev(sf_class_e->str)))) {
247       hd = add_hd_entry(hd_data, __LINE__, 0);
248       hd->sub_class.id = sc_sdev_cdrom;
249     }
250     else if(
251       bus_name &&
252       (!strcmp(bus_name, "scsi") || !strcmp(bus_name, "ide"))
253     ) {
254       hd = add_hd_entry(hd_data, __LINE__, 0);
255       hd->sub_class.id = sc_sdev_other;
256     }
257
258     if(hd) {
259       str_printf(&hd->unix_dev_name, 0, "/dev/%s", hd_sysfs_name2_dev(sf_class_e->str));
260
261       hd->base_class.id = bc_storage_device;
262
263       hd->sysfs_id = new_str(hd_sysfs_id(sf_cdev));
264
265       hd->sysfs_device_link = new_str(hd_sysfs_id(sf_dev));
266
267       hd->unix_dev_num = dev_num;
268
269       hd->bus.id = bus_none;
270
271       if(bus_name) {
272         if(!strcmp(bus_name, "ide")) hd->bus.id = bus_ide;
273         else if(!strcmp(bus_name, "scsi")) hd->bus.id = bus_scsi;
274       }
275       hd->sysfs_bus_id = new_str(bus_id);
276
277       if((s = hd_sysfs_id(sf_dev))) {
278
279         /* parent has longest matching sysfs id */
280         u2 = strlen(s);
281         for(u3 = 0, hd1 = hd_data->hd; hd1; hd1 = hd1->next) {
282           if(hd1->sysfs_id) {
283             u1 = strlen(hd1->sysfs_id);
284             if(u1 > u3 && u1 <= u2 && !strncmp(s, hd1->sysfs_id, u1)) {
285               u3 = u1;
286               hd->attached_to = hd1->idx;
287             }
288           }
289         }
290
291         /* find longest matching sysfs id we have a driver for */
292         s = new_str(s);
293         t = strrchr(s, '/');
294         if(t) *t = 0;
295         t = hd_sysfs_find_driver(hd_data, s, 0);
296         if(t) {
297           add_str_list(&hd->drivers, t);
298         }
299         s = free_mem(s);
300
301         /* look for ide-scsi handled devices */
302         if(hd->bus.id == bus_scsi) {
303           for(sf_bus_e = sf_bus; sf_bus_e; sf_bus_e = sf_bus_e->next) {
304             sf_dev_ide = new_str(hd_read_sysfs_link("/sys/bus/ide/devices", sf_bus_e->str));
305             ide_bus_id = sf_dev_ide ? strrchr(sf_dev_ide, '/') : NULL;
306             if(ide_bus_id) ide_bus_id++;
307
308             if(
309               strcmp(sf_dev, sf_dev_ide) &&
310               !strncmp(sf_dev, sf_dev_ide, strlen(sf_dev_ide)) &&
311               sscanf(ide_bus_id, "%u.%u", &u1, &u2) == 2
312             ) {
313               str_printf(&hd->unix_dev_name2, 0, "/dev/hd%c", 'a' + (u1 << 1) + u2);
314             }
315
316             sf_dev_ide = free_mem(sf_dev_ide);
317           }
318         }
319       }
320
321       /*
322        * set hd->drivers before calling any of add_xxx_sysfs_info()
323        */
324       if(sf_drv_name) {
325         add_str_list(&hd->drivers, sf_drv_name);
326       }
327
328       if(hd->bus.id == bus_ide) {
329         add_ide_sysfs_info(hd_data, hd);
330       }
331       else if(hd->bus.id == bus_scsi) {
332         add_scsi_sysfs_info(hd_data, hd, sf_dev);
333       }
334       else {
335         add_other_sysfs_info(hd_data, hd);
336       }
337       
338       if(hd->sub_class.id == sc_sdev_cdrom) {
339         add_cdrom_info(hd_data, hd);
340       }
341
342       if(
343         hd->sub_class.id == sc_sdev_disk &&
344         hd_probe_feature(hd_data, pr_block_part)
345       ) {
346         add_partitions(hd_data, hd, sf_cdev);
347       }
348     }
349
350     sf_drv_name = free_mem(sf_drv_name);
351     bus_name = free_mem(bus_name);
352     sf_dev = free_mem(sf_dev);
353   }
354
355   sf_cdev = free_mem(sf_cdev);
356   sf_class = free_str_list(sf_class);
357   sf_bus = free_str_list(sf_bus);
358 }
359
360
361 /*
362  * Find driver for sysfs_id.
363  *
364  * Return driver for id (exact = 1) or longest matching id (exact = 0).
365  */
366 char *hd_sysfs_find_driver(hd_data_t *hd_data, char *sysfs_id, int exact)
367 {
368   hd_sysfsdrv_t *sf;
369   char *t;
370   unsigned u1, u2, u3;
371
372   if(!sysfs_id || !*sysfs_id) return NULL;
373
374   t = NULL;
375
376   if(exact) {
377     for(sf = hd_data->sysfsdrv; sf; sf = sf->next) {
378       if(sf->device && !strcmp(sysfs_id, sf->device)) {
379         t = sf->driver;
380         break;
381       }
382     }
383   }
384   else {
385     u2 = strlen(sysfs_id);
386     u3 = 0;
387     for(sf = hd_data->sysfsdrv; sf; sf = sf->next) {
388       if(!sf->device) continue;
389       u1 = strlen(sf->device);
390       if(u1 > u3 && u1 <= u2 && !strncmp(sysfs_id, sf->device, u1)) {
391         u3 = u1;
392         t = sf->driver;
393       }
394     }
395   }
396
397   return t;
398 }
399
400
401 void add_partitions(hd_data_t *hd_data, hd_t *hd, char *path)
402 {
403   hd_t *hd1;
404   str_list_t *sl;
405   char *s;
406   size_t len;
407
408   s = hd->unix_dev_name + sizeof "/dev/" - 1;
409   len = strlen(s);
410   for(sl = hd_data->partitions; sl; sl = sl->next) {
411     if(!strncmp(sl->str, s, len)) {
412       hd1 = add_hd_entry(hd_data, __LINE__, 0);
413       hd1->base_class.id = bc_partition;
414       str_printf(&hd1->unix_dev_name, 0, "/dev/%s", sl->str);
415       hd1->attached_to = hd->idx;
416
417       str_printf(&hd1->sysfs_id, 0, "%s/%s", hd->sysfs_id, hd_sysfs_dev2_name(sl->str));
418     }
419   }
420 }
421
422
423 void add_cdrom_info(hd_data_t *hd_data, hd_t *hd)
424 {
425   cdrom_info_t *ci, **prev;
426
427   hd->detail = free_hd_detail(hd->detail);
428   hd->detail = new_mem(sizeof *hd->detail);
429   hd->detail->type = hd_detail_cdrom;
430
431   for(ci = *(prev = &hd_data->cdrom); ci; ci = *(prev = &ci->next)) {
432     if(!strcmp(hd->unix_dev_name + sizeof "/dev/" - 1, ci->name)) {
433       hd->detail->cdrom.data = ci;
434       *prev = ci->next;
435       hd->detail->cdrom.data->next = NULL;
436       break;
437     }
438   }
439
440   if((ci = hd->detail->cdrom.data)) {
441     /* update prog_if: cdr, cdrw, ... */
442     if(
443       /* ###### FIXME: dosn't work anyway: ide-scsi doesn't support sysfs */
444       hd->bus.id == bus_scsi &&
445       !search_str_list(hd->drivers, "ide-scsi")         /* could be ide, though */
446     ) {
447       /* scsi devs lie */
448       if(ci->dvd && (ci->cdrw || ci->dvdr || ci->dvdram)) {
449         ci->dvd = ci->dvdr = ci->dvdram = 0;
450       }
451       ci->dvdr = ci->dvdram = 0;
452       ci->cdr = ci->cdrw = 0;
453       if(hd->prog_if.id == pif_cdr) ci->cdr = 1;
454     }
455
456     /* trust ide info */
457     if(ci->dvd) {
458       hd->is.dvd = 1;
459       hd->prog_if.id = pif_dvd;
460     }
461     if(ci->cdr) {
462       hd->is.cdr = 1;
463       hd->prog_if.id = pif_cdr;
464     }
465     if(ci->cdrw) {
466       hd->is.cdrw = 1;
467       hd->prog_if.id = pif_cdrw;
468     }
469     if(ci->dvdr) {
470       hd->is.dvdr = 1;
471       hd->prog_if.id = pif_dvdr;
472     }
473     if(ci->dvdram) {
474       hd->is.dvdram = 1;
475       hd->prog_if.id = pif_dvdram;
476     }
477   }
478
479   if(
480     hd_probe_feature(hd_data, pr_block_cdrom) &&
481     hd_report_this(hd_data, hd)
482   ) {
483     hd_read_cdrom_info(hd_data, hd);
484   }
485 }
486
487
488 void add_other_sysfs_info(hd_data_t *hd_data, hd_t *hd)
489 {
490   unsigned u0, u1;
491   char c;
492
493   if(hd->sysfs_id) {
494     if(
495       sscanf(hd->sysfs_id, "/block/cciss!c%ud%u", &u0, &u1) == 2
496     ) {
497       hd->slot = (u0 << 8) + u1;
498       str_printf(&hd->device.name, 0, "CCISS disk %u/%u", u0, u1);
499     }
500     else if(
501       sscanf(hd->sysfs_id, "/block/ida!c%ud%u", &u0, &u1) == 2
502     ) {
503       hd->slot = (u0 << 8) + u1;
504       str_printf(&hd->device.name, 0, "SMART Array %u/%u", u0, u1);
505     }
506     else if(
507       sscanf(hd->sysfs_id, "/block/rd!c%ud%u", &u0, &u1) == 2
508     ) {
509       hd->slot = (u0 << 8) + u1;
510       str_printf(&hd->device.name, 0, "DAC960 RAID Array %u/%u", u0, u1);
511     }
512     else if(
513       sscanf(hd->sysfs_id, "/block/i2o!hd%c", &c) == 1 &&
514       c >= 'a'
515     ) {
516       hd->slot = c - 'a';
517       str_printf(&hd->device.name, 0, "I2O disk %u", hd->slot);
518     }
519     else if(
520       sscanf(hd->sysfs_id, "/block/dasd%c", &c) == 1 &&
521       c >= 'a'
522     ) {
523       hd->slot = c - 'a';
524       hd->device.name = new_str("S390 Disk");
525     }
526   }
527
528   add_disk_size(hd_data, hd);
529 }
530
531
532 void add_ide_sysfs_info(hd_data_t *hd_data, hd_t *hd)
533 {
534   char *fname = NULL, buf[256], *dev_name, *s;
535   unsigned u0, u1, u2, size = 0;
536   str_list_t *sl, *sl0;
537   hd_res_t *res;
538   FILE *f;
539
540   if(!hd_report_this(hd_data, hd)) return;
541
542   if(hd->sysfs_bus_id && sscanf(hd->sysfs_bus_id, "%u.%u", &u0, &u1) == 2) {
543     /* host.master/slave */
544     hd->slot = (u0 << 1) + u1;
545   }
546
547   if(
548     hd->unix_dev_name &&
549     strlen(hd->unix_dev_name) > 5
550   ) {
551     dev_name = hd->unix_dev_name + 5;
552
553     str_printf(&fname, 0, PROC_IDE "/%s/media", dev_name);
554     if((sl = read_file(fname, 0, 1))) {
555
556       if(strstr(sl->str, "floppy"))
557         hd->sub_class.id = sc_sdev_floppy;
558       else if(strstr(sl->str, "cdrom"))
559         hd->sub_class.id = sc_sdev_cdrom;
560       else if(strstr(sl->str, "tape"))
561         hd->sub_class.id = sc_sdev_tape;
562
563       free_str_list(sl);
564     }
565
566     str_printf(&fname, 0, PROC_IDE "/%s/model", dev_name);
567     if((sl = read_file(fname, 0, 1))) {
568       hd->vendor.name = canon_str(sl->str, strlen(sl->str));
569       if((s = strchr(hd->vendor.name, ' '))) {
570         hd->device.name = canon_str(s, strlen(s));
571         if(*hd->device.name) {
572           *s = 0;
573         }
574         else {
575           hd->device.name = free_mem(hd->device.name);
576         }
577       }
578       if(!hd->device.name) {
579         hd->device.name = hd->vendor.name;
580         hd->vendor.name = NULL;
581       }
582
583       free_str_list(sl);
584     }
585
586     str_printf(&fname, 0, PROC_IDE "/%s/driver", dev_name);
587     if((sl = read_file(fname, 0, 1))) {
588       if((s = strchr(sl->str, ' '))) *s = 0;
589       s = canon_str(sl->str, strlen(sl->str));
590       if(!search_str_list(hd->drivers, s)) add_str_list(&hd->drivers, s);
591       s = free_mem(s);
592       free_str_list(sl);
593     }
594
595     str_printf(&fname, 0, PROC_IDE "/%s/capacity", dev_name);
596     if((sl = read_file(fname, 0, 1))) {
597       if(sscanf(sl->str, "%u", &u0) == 1 && u0 != 0x7fffffff) {
598         res = add_res_entry(&hd->res, new_mem(sizeof *res));
599         res->size.type = res_size;
600         res->size.unit = size_unit_sectors;
601         res->size.val1 = size = u0;
602         res->size.val2 = 512;           // ####### FIXME: sector size?
603       }
604       free_str_list(sl);
605     }
606
607     str_printf(&fname, 0, PROC_IDE "/%s/geometry", dev_name);
608     if((sl0 = read_file(fname, 0, 2))) {
609       for(sl = sl0; sl; sl = sl->next) {
610         if(sscanf(sl->str, " physical %u / %u / %u", &u0, &u1, &u2) == 3) {
611           if(u0 || u1 || u2) {
612             if(size && u1 && u2) {
613               u0 = size / (u1 * u2);
614             }
615             res = add_res_entry(&hd->res, new_mem(sizeof *res));
616             res->disk_geo.type = res_disk_geo;
617             res->disk_geo.cyls = u0;
618             res->disk_geo.heads = u1;
619             res->disk_geo.sectors = u2;
620             res->disk_geo.geotype = geo_physical;
621           }
622           continue;
623         }
624
625         if(sscanf(sl->str, " logical %u / %u / %u", &u0, &u1, &u2) == 3) {
626           if(size && u1 && u2) {
627             u0 = size / (u1 * u2);
628           }
629           res = add_res_entry(&hd->res, new_mem(sizeof *res));
630           res->disk_geo.type = res_disk_geo;
631           res->disk_geo.cyls = u0;
632           res->disk_geo.heads = u1;
633           res->disk_geo.sectors = u2;
634           res->disk_geo.geotype = geo_logical;
635         }
636       }
637       free_str_list(sl0);
638     }
639
640     str_printf(&fname, 0, PROC_IDE "/%s/cache", dev_name);
641     if((sl = read_file(fname, 0, 1))) {
642       if(sscanf(sl->str, "%u", &u0) == 1 && u0) {
643         res = add_res_entry(&hd->res, new_mem(sizeof *res));
644         res->cache.type = res_cache;
645         res->cache.size = u0;
646       }
647       free_str_list(sl);
648     }
649
650     str_printf(&fname, 0, PROC_IDE "/%s/identify", dev_name);
651     if((f = fopen(fname, "r"))) {
652       u1 = 0;
653       memset(buf, 0, sizeof buf);
654       while(u1 < sizeof buf - 1 && fscanf(f, "%x", &u0) == 1) {
655         buf[u1++] = u0 >> 8; buf[u1++] = u0;
656       }
657       fclose(f);
658
659       /* ok, we now have the ATA/ATAPI ident block */
660
661       if(buf[0x14] || buf[0x15]) {      /* has serial id */
662         hd->serial = canon_str(buf + 0x14, 20);
663       }
664       if(buf[0x2e] || buf[0x2f]) {      /* has revision id */
665         hd->revision.name = canon_str(buf + 0x2e, 8);
666       }
667     }
668
669     free_mem(fname);
670   }
671
672   if(!size) add_disk_size(hd_data, hd);
673 }
674
675
676 /*
677  * assumes hd->drivers aleady includes scsi device drivers (like 'sd')
678  */
679 void add_scsi_sysfs_info(hd_data_t *hd_data, hd_t *hd, char *sf_dev)
680 {
681   hd_t *hd1;
682   char *s, *t, *cs, *pr_str;
683   unsigned u0, u1, u2, u3;
684   int fd, k;
685   unsigned char scsi_cmd_buf[0x300];
686   struct sg_io_hdr hdr;
687   unsigned char *uc;
688   scsi_t *scsi;
689   hd_res_t *geo, *size;
690   uint64_t ul0;
691   str_list_t *sl;
692   char buf[16];
693   hd_res_t *res;
694
695   if(!hd_report_this(hd_data, hd)) return;
696
697   hd->detail = new_mem(sizeof *hd->detail);
698   hd->detail->type = hd_detail_scsi;
699   hd->detail->scsi.data = scsi = new_mem(sizeof *scsi);
700
701   if(hd->sysfs_bus_id && sscanf(hd->sysfs_bus_id, "%u:%u:%u:%u", &u0, &u1, &u2, &u3) == 4) {
702     /* host:channel:id:lun */
703     hd->slot = (u0 << 8) + (u1 << 4) + u2;
704     hd->func = u3;
705   }
706
707   if((s = get_sysfs_attr_by_path(sf_dev, "vendor"))) {
708     cs = canon_str(s, strlen(s));
709     ADD2LOG("    vendor = %s\n", cs);
710     if(*cs) {
711       hd->vendor.name = cs;
712     }
713     else {
714       free_mem(cs);
715     }
716   }
717
718   if((s = get_sysfs_attr_by_path(sf_dev, "model"))) {
719     cs = canon_str(s, strlen(s));
720     ADD2LOG("    model = %s\n", cs);
721     if(*cs) {
722       hd->device.name = cs;
723     }
724     else {
725       free_mem(cs);
726     }
727
728     /* sata entries are somewhat strange... */
729     if(
730       hd->vendor.name &&
731       !strcmp(hd->vendor.name, "ATA") &&
732       hd->device.name
733     ) {
734       hd->bus.id = bus_ide;
735
736       if((cs = strchr(hd->device.name, ' '))) {
737         t = canon_str(cs, strlen(cs));
738         if(*t) {
739           *cs = 0;
740           free_mem(hd->vendor.name);
741           hd->vendor.name = hd->device.name;
742           hd->device.name = t;
743         }
744         else {
745           t = free_mem(t);
746         }
747       }
748
749       if(!strcmp(hd->vendor.name, "ATA")) {
750         hd->vendor.name = free_mem(hd->vendor.name);
751       }
752     }
753   }
754
755   if((s = get_sysfs_attr_by_path(sf_dev, "rev"))) {
756     cs = canon_str(s, strlen(s));
757     ADD2LOG("    rev = %s\n", cs);
758     if(*cs) {
759       hd->revision.name = cs;
760     }
761     else {
762       free_mem(cs);
763     }
764   }
765
766   if(hd_attr_uint(get_sysfs_attr_by_path(sf_dev, "type"), &ul0, 0)) {
767     ADD2LOG("    type = %u\n", (unsigned) ul0);
768     if(ul0 == 6 /* scanner */) {
769       hd->sub_class.id = sc_sdev_scanner;
770     }
771     else if(ul0 == 3 /* processor */ && hd->vendor.name) {
772       if(
773         !strncmp(hd->vendor.name, "HP", sizeof "HP" - 1) ||
774         !strncmp(hd->vendor.name, "EPSON", sizeof "EPSON" - 1)
775       ) {
776         hd->sub_class.id = sc_sdev_scanner;
777       }
778     }
779
780     /*
781      * typically needed for usb card readers (unused slots)
782      */
783     if(
784       hd->base_class.id == bc_storage_device &&
785       hd->sub_class.id == sc_sdev_other
786     ) {
787       switch(ul0) {
788         case 0:
789           if(search_str_list(hd->drivers, "sd")) {
790             hd->sub_class.id = sc_sdev_disk;
791           }
792           break;
793
794         case 5:
795           if(search_str_list(hd->drivers, "sr")) {
796             hd->sub_class.id = sc_sdev_cdrom;
797           }
798           break;
799       }
800     }
801   }
802
803   res = new_mem(sizeof *res);
804   res->any.type = res_fc;
805
806   if(hd->sysfs_bus_id) {
807     for(sl = hd_data->lsscsi; sl; sl = sl->next) {
808       if(
809         sscanf(sl->str, "[%15[^]]] %*s fc:%"SCNx64" , %x ", buf, &ul0, &u0) == 3 &&
810         !strcmp(hd->sysfs_bus_id, buf)
811       ) {
812         ADD2LOG("    lsscsi: wwpn = 0x%"PRIx64", port_id = 0x%x\n", ul0, u0);
813         res->fc.wwpn = ul0;
814         res->fc.wwpn_ok = 1;
815         res->fc.port_id = u0;
816         res->fc.port_id_ok = 1;
817       }
818     }
819   }
820
821   /* s390: wwpn & fcp lun */
822   if(hd_attr_uint(get_sysfs_attr_by_path(sf_dev, "wwpn"), &ul0, 0)) {
823     ADD2LOG("    wwpn = 0x%016"PRIx64"\n", ul0);
824     res->fc.wwpn = ul0;
825     res->fc.wwpn_ok = 1;
826     scsi->wwpn = ul0;
827     scsi->wwpn_ok = 1;
828
829     /* it's a bit of a hack, actually */
830     t = new_str(hd_sysfs_id(sf_dev));
831     if(t) {
832       if((s = strrchr(t, '/'))) *s = 0;
833       if((s = strrchr(t, '/'))) *s = 0;
834       if((s = strrchr(t, '/'))) *s = 0;
835       if((s = strrchr(t, '/'))) *s = 0;
836       if((s = strrchr(t, '/'))) {
837         scsi->controller_id = new_str(s + 1);
838         res->fc.controller_id = new_str(s + 1);
839       }
840     }
841     t = free_mem(t);
842   }
843
844   if(hd_attr_uint(get_sysfs_attr_by_path(sf_dev, "fcp_lun"), &ul0, 0)) {
845     ADD2LOG("    fcp_lun = 0x%016"PRIx64"\n", ul0);
846     res->fc.fcp_lun = ul0;
847     res->fc.fcp_lun_ok = 1;
848     scsi->fcp_lun = ul0;
849     scsi->fcp_lun_ok = 1;
850   }
851
852   if(res->fc.wwpn_ok || res->fc.port_id_ok || res->fc.fcp_lun_ok || res->fc.controller_id) {
853     add_res_entry(&hd->res, res);
854   }
855   else {
856     free_res_list(res);
857   }
858
859   /* ppc: get rom id */
860   if((hd1 = hd_get_device_by_idx(hd_data, hd->attached_to)) && hd1->rom_id) {
861     str_printf(&hd->rom_id, 0, "%s/@%u", hd1->rom_id, (hd->slot & 0xf));
862   }
863
864   pr_str = NULL;
865
866   if(
867     hd_report_this(hd_data, hd) &&
868     hd->unix_dev_name &&
869     hd->sub_class.id == sc_sdev_cdrom &&
870     !hd_data->flags.vmware              /* VMWare doesn't like it */
871   ) {
872     PROGRESS(5, 0, hd->unix_dev_name);
873     fd = open(hd->unix_dev_name, O_RDONLY | O_NONBLOCK);
874     if(fd >= 0) {
875
876       str_printf(&pr_str, 0, "%s cache", hd->unix_dev_name);
877       PROGRESS(5, 1, pr_str);
878
879       memset(scsi_cmd_buf, 0, sizeof scsi_cmd_buf);
880       memset(&hdr, 0, sizeof(hdr));
881
882       hdr.interface_id = 'S';
883       hdr.cmd_len = 6;
884       hdr.dxfer_direction = SG_DXFER_FROM_DEV;
885       hdr.dxferp = scsi_cmd_buf + 8 + 6;
886       hdr.dxfer_len = 0xff;
887       hdr.cmdp = scsi_cmd_buf + 8;
888       hdr.cmdp[0] = 0x1a;
889       hdr.cmdp[2] = 0x08;
890       hdr.cmdp[4] = 0xff;
891
892       k = ioctl(fd, SG_IO, &hdr);
893
894       if(k) {
895         ADD2LOG("%s status(0x1a:8) 0x%x\n", hd->unix_dev_name, k);
896       }
897       else {
898         unsigned char *ptr = hdr.dxferp;
899
900         uc = ptr + 4 + ptr[3] + 2;
901         scsi->cache = uc[0];
902         ADD2LOG("  scsi cache: 0x%02x\n", uc[0]);
903     
904         if((scsi->cache & 4)) {
905           hd->prog_if.id = pif_cdr;
906         }
907       }
908
909       close(fd);
910     }
911   }
912
913
914   if(
915     hd_report_this(hd_data, hd) &&
916     hd->unix_dev_name &&
917     hd->sub_class.id == sc_sdev_disk &&
918     !hd_probe_feature(hd_data, pr_scsi_noserial)
919   ) {
920     PROGRESS(5, 0, hd->unix_dev_name);
921     fd = open(hd->unix_dev_name, O_RDONLY | O_NONBLOCK);
922     if(fd >= 0) {
923
924       str_printf(&pr_str, 0, "%s geo", hd->unix_dev_name);
925       PROGRESS(5, 1, pr_str);
926
927       if(hd_getdisksize(hd_data, hd->unix_dev_name, fd, &geo, &size) == 1) {
928         /* (low-level) unformatted disk */
929         hd->is.notready = 1;
930       }
931
932       if(geo) add_res_entry(&hd->res, geo);
933       if(size) add_res_entry(&hd->res, size);
934
935       str_printf(&pr_str, 0, "%s serial", hd->unix_dev_name);
936       PROGRESS(5, 2, pr_str);
937
938       memset(scsi_cmd_buf, 0, sizeof scsi_cmd_buf);
939       memset(&hdr, 0, sizeof(hdr));
940
941       hdr.interface_id = 'S';
942       hdr.cmd_len = 6;
943       hdr.dxfer_direction = SG_DXFER_FROM_DEV;
944       hdr.dxferp = scsi_cmd_buf + 8 + 6;
945       hdr.dxfer_len = 0x24;
946       hdr.cmdp = scsi_cmd_buf + 8;
947       hdr.cmdp[0] = 0x12;
948       hdr.cmdp[1] = 0x01;
949       hdr.cmdp[2] = 0x80;
950       hdr.cmdp[4] = 0x24;
951  
952       k = ioctl(fd, SG_IO, &hdr);
953
954       if(k) {
955         ADD2LOG("%s status(0x12) 0x%x\n", scsi->dev_name, k);
956       }
957       else {
958         unsigned char *ptr = hdr.dxferp;
959
960         ADD2LOG("  serial id len: %u\n", ptr[3]);
961
962         if((hd->serial = canon_str(ptr + 4, ptr[3]))) {
963           if(!*hd->serial) hd->serial = free_mem(hd->serial);
964         }
965       }
966
967       close(fd);
968     }
969   }
970
971   pr_str = free_mem(pr_str);
972
973
974   if(
975     hd->base_class.id == bc_storage_device &&
976     hd->sub_class.id == sc_sdev_scanner
977   ) {
978     hd->base_class.id = bc_scanner;
979   }
980
981   // ###### FIXME: usb-storage: disk vs. floppy?
982  
983 }
984
985
986 void read_partitions(hd_data_t *hd_data)
987 {
988   str_list_t *sl, *sl0, *pl0 = NULL;
989   char buf[256], *s1, *name, *base;
990   char *last_base = new_str(" ");
991   char *last_name = new_str(" ");
992   int l, is_disk;
993
994   if(!(sl0 = read_file(PROC_PARTITIONS, 2, 0))) return;
995
996   if(hd_data->debug) {
997     ADD2LOG("----- "PROC_PARTITIONS" -----\n");
998     for(sl = sl0; sl; sl = sl->next) {
999       ADD2LOG("  %s", sl->str);
1000     }
1001     ADD2LOG("----- "PROC_PARTITIONS" end -----\n");
1002   }
1003
1004   for(sl = sl0; sl; sl = sl->next) {
1005     *buf = 0;
1006     if(sscanf(sl->str, "%*s %*s %*s %255s", buf) > 0) {
1007       if(*buf) add_str_list(&pl0, buf);
1008     }
1009   }
1010
1011   free_str_list(sl0);
1012
1013   for(is_disk = 1, sl = pl0; sl; sl = sl->next) {
1014     base = sl->str;
1015     l = strlen(base);
1016     if(!l) continue;
1017
1018     s1 = base + l - 1;
1019     while(isdigit(*s1) && s1 > base) s1--;
1020     if(s1 == base) continue;
1021
1022     name = new_str(base);
1023     s1[1] = 0;
1024
1025     if(!strcmp(last_base, base)) {
1026       if(!strcmp(last_name, base)) is_disk = 0;
1027     }
1028     else {
1029       is_disk = strncmp(last_name, base, strlen(last_name)) ? 1 : 0;
1030     }
1031
1032     if(!search_str_list(hd_data->cdroms, name)) {
1033       if(
1034         strncmp(name, "loop", sizeof "loop" - 1) &&
1035         (
1036           hd_data->flags.list_md ||
1037           (
1038             strncmp(name, "md", sizeof "md" - 1) &&
1039             strncmp(name, "dm-", sizeof "dm-" - 1)
1040           )
1041         )
1042       ) {
1043         add_str_list(is_disk ? &hd_data->disks : &hd_data->partitions, name);
1044       }
1045     }
1046     free_mem(last_base);
1047     free_mem(last_name);
1048
1049     last_base = new_str(base);
1050     last_name = name; name = NULL;
1051   }
1052
1053   free_mem(last_base);
1054   free_mem(last_name);
1055
1056   free_str_list(pl0);
1057
1058   if(hd_data->debug) {
1059     ADD2LOG("disks:\n");
1060     for(sl = hd_data->disks; sl; sl = sl->next) ADD2LOG("  %s\n", sl->str);
1061     ADD2LOG("partitions:\n");
1062     for(sl = hd_data->partitions; sl; sl = sl->next) ADD2LOG("  %s\n", sl->str);
1063   }
1064 }
1065
1066
1067 /*
1068  * Read iso9660/el torito info, if there is a CD inserted.
1069  * Returns NULL if nothing was found
1070  */
1071 cdrom_info_t *hd_read_cdrom_info(hd_data_t *hd_data, hd_t *hd)
1072 {
1073   int fd;
1074   char *s;
1075   cdrom_info_t *ci;
1076   struct iso_primary_descriptor iso_desc;
1077   unsigned char sector[0x800];
1078   unsigned et;
1079   unsigned u0, u1, u2;
1080
1081   /* free existing entry */
1082   if(hd->detail && hd->detail->type != hd_detail_cdrom) {
1083     hd->detail = free_hd_detail(hd->detail);
1084   }
1085
1086   if(!hd->detail) {
1087     hd->detail = new_mem(sizeof *hd->detail);
1088     hd->detail->type = hd_detail_cdrom;
1089     hd->detail->cdrom.data = new_mem(sizeof *hd->detail->cdrom.data);
1090   }
1091
1092   ci = hd->detail->cdrom.data;
1093
1094   hd->is.notready = 0;
1095
1096   if((fd = open(hd->unix_dev_name, O_RDONLY)) < 0) {
1097     /* we are here if there is no CD in the drive */
1098     hd->is.notready = 1;
1099     return NULL;
1100   }
1101
1102   ci->iso9660.ok = 0;
1103   if(
1104     lseek(fd, 0x8000, SEEK_SET) >= 0 &&
1105     read(fd, &iso_desc, sizeof iso_desc) == sizeof iso_desc
1106   ) {
1107     ci->cdrom = 1;
1108     if(!memcmp(iso_desc.id, "CD001", 5)) {
1109       ci->iso9660.ok = 1;
1110       /* now, fill in the fields */
1111       s = canon_str(iso_desc.volume_id, sizeof iso_desc.volume_id);
1112       if(!*s) s = free_mem(s);
1113       ci->iso9660.volume = s;
1114
1115       s = canon_str(iso_desc.publisher_id, sizeof iso_desc.publisher_id);
1116       if(!*s) s = free_mem(s);
1117       ci->iso9660.publisher = s;
1118
1119       s = canon_str(iso_desc.preparer_id, sizeof iso_desc.preparer_id);
1120       if(!*s) s = free_mem(s);
1121       ci->iso9660.preparer = s;
1122
1123       s = canon_str(iso_desc.application_id, sizeof iso_desc.application_id);
1124       if(!*s) s = free_mem(s);
1125       ci->iso9660.application = s;
1126
1127       s = canon_str(iso_desc.creation_date, sizeof iso_desc.creation_date);
1128       if(!*s) s = free_mem(s);
1129       ci->iso9660.creation_date = s;
1130     }
1131   }
1132
1133   if(
1134     ci->iso9660.ok &&
1135     lseek(fd, 0x8800, SEEK_SET) >= 0 &&
1136     read(fd, &sector, sizeof sector) == sizeof sector
1137   ) {
1138     if(
1139       sector[0] == 0 && sector[6] == 1 &&
1140       !memcmp(sector + 1, "CD001", 5) &&
1141       !memcmp(sector + 7, "EL TORITO SPECIFICATION", 23)
1142     ) {
1143       et = sector[0x47] + (sector[0x48] << 8) + (sector[0x49] << 16) + (sector[0x4a] << 24);
1144       ADD2LOG("  %s: el torito boot catalog at 0x%04x\n", ci->name, et);
1145       if(
1146         lseek(fd, et * 0x800, SEEK_SET) >= 0 &&
1147         read(fd, &sector, sizeof sector) == sizeof sector &&
1148         sector[0] == 1
1149       ) {
1150         ci->el_torito.ok = 1;
1151         ci->el_torito.catalog = et;
1152         ci->el_torito.platform = sector[1];
1153         s = canon_str(sector + 4, 24);
1154         if(!*s) s = free_mem(s);
1155         ci->el_torito.id_string = s;
1156         ci->el_torito.bootable = sector[0x20] == 0x88 ? 1 : 0;
1157         ci->el_torito.media_type = sector[0x21];
1158         ADD2LOG("    media type: %u\n",  ci->el_torito.media_type);
1159         ci->el_torito.load_address = (sector[0x22] + (sector[0x23] << 8)) << 4;
1160         ADD2LOG("    load address: 0x%04x\n",  ci->el_torito.load_address);
1161 #if 0
1162         if(ci->el_torito.platform == 0 && ci->el_torito.load_address == 0)
1163           ci->el_torito.load_address = 0x7c00;
1164 #endif
1165         ci->el_torito.load_count = sector[0x26] + (sector[0x27] << 8);
1166         ci->el_torito.start = sector[0x28] + (sector[0x29] << 8) + (sector[0x2a] << 16) + (sector[0x2b] << 24);
1167         if(ci->el_torito.media_type >= 1 && ci->el_torito.media_type <= 3) {
1168           ci->el_torito.geo.c = 80;
1169           ci->el_torito.geo.h = 2;
1170         }
1171         switch(ci->el_torito.media_type) {
1172           case 1:
1173             ci->el_torito.geo.s = 15;
1174             break;
1175           case 2:
1176             ci->el_torito.geo.s = 18;
1177             break;
1178           case 3:
1179             ci->el_torito.geo.s = 36;
1180             break;
1181         }
1182         if(
1183           lseek(fd, ci->el_torito.start * 0x800, SEEK_SET) >= 0 &&
1184           read(fd, &sector, sizeof sector) == sizeof sector
1185         ) {
1186           if(ci->el_torito.media_type == 4) {
1187             /* ##### we should go on and read the 1st partition sector in this case... */
1188             ci->el_torito.geo.h = (unsigned) sector[0x1be + 5] + 1;
1189             ci->el_torito.geo.s = sector[0x1be + 6] & 0x3f;
1190             ci->el_torito.geo.c = sector[0x1be + 7] + (((unsigned) sector[0x1be + 6] >> 6) << 8);
1191           }
1192           if(
1193             sector[0x1fe] == 0x55 && sector[0x1ff] == 0xaa &&
1194             sector[0x0b] == 0 && sector[0x0c] == 2 &&
1195             sector[0x0e] == 1 && sector[0x0f] == 0
1196           ) {
1197             u0 = sector[0x13] + (sector[0x14] << 8);    /* partition size */
1198             u1 = sector[0x18] + (sector[0x19] << 8);    /* sectors per track */
1199             u2 = sector[0x1a] + (sector[0x1b] << 8);    /* heads */
1200             u0 = u0 ? u0 : sector[0x20] + (sector[0x21] << 8) + (sector[0x22] << 16) + ((unsigned) sector[0x23] << 24);
1201             if(sector[0x26] == 0x29) {
1202               s = canon_str(sector + 0x2b, 11);
1203               if(!*s) s = free_mem(s);
1204               ci->el_torito.label = s;
1205             }
1206             if(!ci->el_torito.label) {
1207               s = canon_str(sector + 3, 8);
1208               if(!*s) s = free_mem(s);
1209               ci->el_torito.label = s;
1210             }
1211             if(
1212               (ci->el_torito.media_type == 0 || ci->el_torito.media_type > 3) &&
1213               u0 && u1 && u2
1214             ) {
1215               ci->el_torito.geo.h = u2;
1216               ci->el_torito.geo.s = u1;
1217               ci->el_torito.geo.size = u0;
1218               ci->el_torito.geo.c = ci->el_torito.geo.size / (u1 * u2);
1219             }
1220           }
1221         }
1222
1223         ci->el_torito.geo.size = ci->el_torito.geo.s * ci->el_torito.geo.c * ci->el_torito.geo.h;
1224       }
1225     }
1226   }
1227
1228   close(fd);
1229
1230   return ci;
1231 }
1232
1233
1234 /*
1235  * Read the list of CDROM devices known to the kernel. The info is taken
1236  * from /proc/sys/dev/cdrom/info.
1237  */
1238 void read_cdroms(hd_data_t *hd_data)
1239 {
1240   char *s, *t, *v;
1241   str_list_t *sl, *sl0;
1242   cdrom_info_t *ci;
1243   int i, line, entries = 0;
1244   unsigned val;
1245
1246   if(!(sl0 = read_file(PROC_CDROM_INFO, 2, 0))) return;
1247
1248   if((hd_data->debug & HD_DEB_CDROM)) {
1249     ADD2LOG("----- "PROC_CDROM_INFO" -----\n");
1250     for(sl = sl0; sl; sl = sl->next) {
1251       if(*sl->str != '\n') ADD2LOG("%s", sl->str);
1252     }
1253     ADD2LOG("----- "PROC_CDROM_INFO" end -----\n");
1254   }
1255
1256   for(sl = sl0; sl; sl = sl->next) {
1257     if(
1258       (line = 0, strstr(sl->str, "drive name:") == sl->str) ||
1259       (line++, strstr(sl->str, "drive speed:") == sl->str) ||
1260       (line++, strstr(sl->str, "Can write CD-R:") == sl->str) ||
1261       (line++, strstr(sl->str, "Can write CD-RW:") == sl->str) ||
1262       (line++, strstr(sl->str, "Can read DVD:") == sl->str) ||
1263       (line++, strstr(sl->str, "Can write DVD-R:") == sl->str) ||
1264       (line++, strstr(sl->str, "Can write DVD-RAM:") == sl->str)
1265     ) {
1266       s = strchr(sl->str, ':') + 1;
1267       i = 0;
1268       while((t = strsep(&s, " \t\n"))) {
1269         if(!*t) continue;
1270         i++;
1271         switch(line) {
1272           case 0:       /* drive name */
1273             ci = new_cdrom_entry(&hd_data->cdrom);
1274             entries++;
1275             add_str_list(&hd_data->cdroms, t);
1276             ci->name = new_str(t);
1277             break;
1278
1279           case 1:       /* drive speed */
1280           case 2:       /* Can write CD-R */
1281           case 3:       /* Can write CD-RW */
1282           case 4:       /* Can read DVD */
1283           case 5:       /* Can write DVD-R */
1284           case 6:       /* Can write DVD-RAM */
1285             ci = get_cdrom_entry(hd_data->cdrom, entries - i);
1286             if(ci) {
1287               val = strtoul(t, &v, 10);
1288               if(!*v) {
1289                 switch(line) {
1290                   case 1:
1291                     ci->speed = val;
1292                     break;
1293                   case 2:
1294                     ci->cdr = val;
1295                     break;
1296                   case 3:
1297                     ci->cdrw = val;
1298                     break;
1299                   case 4:
1300                     ci->dvd = val;
1301                     break;
1302                   case 5:
1303                     ci->dvdr = val;
1304                     break;
1305                   case 6:
1306                     ci->dvdram = val;
1307                     break;
1308                 }
1309               }
1310             }
1311             break;
1312         }
1313       }
1314     }  
1315   }
1316
1317   free_str_list(sl0);
1318 }
1319
1320
1321 /* add new entries at the _start_ of the list */
1322 cdrom_info_t *new_cdrom_entry(cdrom_info_t **ci)
1323 {
1324   cdrom_info_t *new_ci = new_mem(sizeof *new_ci);
1325
1326   new_ci->next = *ci;
1327   return *ci = new_ci;
1328 }
1329
1330
1331 /* return nth entry */
1332 cdrom_info_t *get_cdrom_entry(cdrom_info_t *ci, int n)
1333 {
1334   if(n < 0) return NULL;
1335
1336   while(n--) {
1337     if(!ci) return NULL;
1338     ci = ci->next;
1339   }
1340
1341   return ci;
1342 }
1343
1344
1345 /*
1346  * Add generic scsi devs.
1347  */
1348 void hd_scan_sysfs_scsi(hd_data_t *hd_data)
1349 {
1350   if(!hd_probe_feature(hd_data, pr_scsi)) return;
1351
1352   hd_data->module = mod_scsi;
1353
1354   /* some clean-up */
1355   remove_hd_entries(hd_data);
1356
1357   PROGRESS(1, 0, "scsi modules");
1358
1359   load_module(hd_data, "sg");
1360
1361   PROGRESS(2, 0, "scsi tape");
1362
1363   get_scsi_tape(hd_data);
1364
1365   PROGRESS(3, 0, "scsi generic");
1366
1367   get_generic_scsi_devs(hd_data);
1368 }
1369
1370
1371 void get_scsi_tape(hd_data_t *hd_data)
1372 {
1373   char *s, *t;
1374   unsigned u1, u2, u3;
1375   uint64_t ul0;
1376   hd_t *hd, *hd1;
1377   hd_dev_num_t dev_num;
1378   str_list_t *sf_class, *sf_class_e;
1379   char *sf_cdev = NULL, *sf_dev = NULL;
1380   char *sf_drv_name, *sf_drv, *bus_id;
1381
1382   sf_class = read_dir("/sys/class/scsi_tape", 'D');
1383
1384   if(!sf_class) {
1385     ADD2LOG("sysfs: no such class: scsi_tape\n");
1386     return;
1387   }
1388
1389   for(sf_class_e = sf_class; sf_class_e; sf_class_e = sf_class_e->next) {
1390     str_printf(&sf_cdev, 0, "/sys/class/scsi_tape/%s", sf_class_e->str);
1391
1392     ADD2LOG(
1393       "  scsi tape: name = %s, path = %s\n",
1394       sf_class_e->str,
1395       hd_sysfs_id(sf_cdev)
1396     );
1397
1398     memset(&dev_num, 0, sizeof dev_num);
1399
1400     if((s = get_sysfs_attr_by_path(sf_cdev, "dev"))) {
1401       if(sscanf(s, "%u:%u", &u1, &u2) == 2) {
1402         dev_num.type = 'c';
1403         dev_num.major = u1;
1404         dev_num.minor = u2;
1405         dev_num.range = 1;
1406       }
1407       ADD2LOG("    dev = %u:%u\n", u1, u2);
1408     }
1409
1410     if(hd_attr_uint(get_sysfs_attr_by_path(sf_cdev, "range"), &ul0, 0)) {
1411       dev_num.range = ul0;
1412       ADD2LOG("    range = %u\n", dev_num.range);
1413     }
1414
1415     sf_dev = new_str(hd_read_sysfs_link(sf_cdev, "device"));
1416     sf_drv_name = NULL;
1417     sf_drv = hd_read_sysfs_link(sf_dev, "driver");
1418     if(!sf_drv) {
1419       /* maybe older kernel */
1420       sf_drv = hd_read_sysfs_link(sf_cdev, "driver");
1421     }
1422     if(sf_drv) {
1423       sf_drv_name = strrchr(sf_drv, '/');
1424       if(sf_drv_name) sf_drv_name++;
1425     }
1426
1427     bus_id = sf_dev ? strrchr(sf_dev, '/') : NULL;
1428     if(bus_id) bus_id++;
1429
1430     if(sf_dev) {
1431       s = hd_sysfs_id(sf_dev);
1432
1433       ADD2LOG(
1434         "    scsi device: bus_id = %s driver = %s\n      path = %s\n",
1435         bus_id,
1436         sf_drv_name,
1437         s
1438       );
1439
1440       for(hd = hd_data->hd; hd; hd = hd->next) {
1441         if(
1442           hd->module == hd_data->module &&
1443           hd->sysfs_device_link &&
1444           hd->base_class.id == bc_storage_device &&
1445           hd->sub_class.id == sc_sdev_tape &&
1446           s &&
1447           !strcmp(hd->sysfs_device_link, s)
1448         ) break;
1449       }
1450
1451       if(!hd) {
1452         hd = add_hd_entry(hd_data, __LINE__, 0);
1453         hd->base_class.id = bc_storage_device;
1454         hd->sub_class.id = sc_sdev_tape;
1455
1456         hd->bus.id = bus_scsi;
1457
1458         hd->sysfs_device_link = new_str(s);
1459
1460         hd->sysfs_bus_id = new_str(bus_id);
1461
1462         /* parent has longest matching sysfs id */
1463         u2 = strlen(s);
1464         for(u3 = 0, hd1 = hd_data->hd; hd1; hd1 = hd1->next) {
1465           if(hd1->sysfs_id) {
1466             u1 = strlen(hd1->sysfs_id);
1467             if(u1 > u3 && u1 <= u2 && !strncmp(s, hd1->sysfs_id, u1)) {
1468               u3 = u1;
1469               hd->attached_to = hd1->idx;
1470             }
1471           }
1472         }
1473
1474         /* find longest matching sysfs id we have a driver for */
1475         s = new_str(s);
1476         t = strrchr(s, '/');
1477         if(t) *t = 0;
1478         t = hd_sysfs_find_driver(hd_data, s, 0);
1479         if(t) {
1480           add_str_list(&hd->drivers, t);
1481         }
1482         s = free_mem(s);
1483
1484         if(sf_drv_name) {
1485           add_str_list(&hd->drivers, sf_drv_name);
1486         }
1487
1488         add_scsi_sysfs_info(hd_data, hd, sf_dev);
1489       }
1490
1491       s = hd_sysfs_name2_dev(sf_class_e->str);
1492
1493       if(!hd->unix_dev_name || strlen(s) + sizeof "/dev/" - 1 < strlen(hd->unix_dev_name)) {
1494         str_printf(&hd->unix_dev_name, 0, "/dev/%s", s);
1495         hd->unix_dev_num = dev_num;
1496         free_mem(hd->sysfs_id);
1497         hd->sysfs_id = new_str(hd_sysfs_id(sf_cdev));
1498       }
1499     }
1500   }
1501
1502   sf_cdev = free_mem(sf_cdev);
1503   sf_class = free_str_list(sf_class);
1504 }
1505
1506
1507 void get_generic_scsi_devs(hd_data_t *hd_data)
1508 {
1509   char *s, *t;
1510   unsigned u1, u2, u3;
1511   uint64_t ul0;
1512   hd_t *hd, *hd1;
1513   hd_dev_num_t dev_num;
1514   str_list_t *sf_class, *sf_class_e;
1515   char *sf_cdev = NULL, *sf_dev = NULL;
1516   char *sf_drv_name, *sf_drv, *bus_id;
1517
1518   sf_class = read_dir("/sys/class/scsi_generic", 'D');
1519
1520   if(!sf_class) {
1521     ADD2LOG("sysfs: no such class: scsi_generic\n");
1522     return;
1523   }
1524
1525   for(sf_class_e = sf_class; sf_class_e; sf_class_e = sf_class_e->next) {
1526     str_printf(&sf_cdev, 0, "/sys/class/scsi_generic/%s", sf_class_e->str);
1527
1528     ADD2LOG(
1529       "  scsi: name = %s, path = %s\n",
1530       sf_class_e->str,
1531       hd_sysfs_id(sf_cdev)
1532     );
1533
1534     memset(&dev_num, 0, sizeof dev_num);
1535
1536     if((s = get_sysfs_attr_by_path(sf_cdev, "dev"))) {
1537       if(sscanf(s, "%u:%u", &u1, &u2) == 2) {
1538         dev_num.type = 'c';
1539         dev_num.major = u1;
1540         dev_num.minor = u2;
1541         dev_num.range = 1;
1542       }
1543       ADD2LOG("    dev = %u:%u\n", u1, u2);
1544     }
1545
1546     if(hd_attr_uint(get_sysfs_attr_by_path(sf_cdev, "range"), &ul0, 0)) {
1547       dev_num.range = ul0;
1548       ADD2LOG("    range = %u\n", dev_num.range);
1549     }
1550
1551     sf_dev = new_str(hd_read_sysfs_link(sf_cdev, "device"));
1552     sf_drv_name = NULL;
1553     sf_drv = hd_read_sysfs_link(sf_dev, "driver");
1554     if(!sf_drv) {
1555       /* maybe older kernel */
1556       sf_drv = hd_read_sysfs_link(sf_cdev, "driver");
1557     }
1558     if(sf_drv) {
1559       sf_drv_name = strrchr(sf_drv, '/');
1560       if(sf_drv_name) sf_drv_name++;
1561     }
1562
1563     bus_id = sf_dev ? strrchr(sf_dev, '/') : NULL;
1564     if(bus_id) bus_id++;
1565
1566     s = NULL;
1567
1568     if(sf_dev) {
1569       s = hd_sysfs_id(sf_dev);
1570
1571       ADD2LOG(
1572         "    scsi device: bus_id = %s driver = %s\n      path = %s\n",
1573         bus_id,
1574         sf_drv_name,
1575         s
1576       );
1577     }
1578
1579     for(hd = hd_data->hd; hd; hd = hd->next) {
1580       if(
1581         hd->sysfs_device_link &&
1582         hd->bus.id == bus_scsi &&
1583         s &&
1584         !strcmp(hd->sysfs_device_link, s)
1585       ) break;
1586     }
1587
1588     if(hd) {
1589       if(!hd->unix_dev_name2) {
1590         str_printf(&hd->unix_dev_name2, 0, "/dev/%s", hd_sysfs_name2_dev(sf_class_e->str));
1591         hd->unix_dev_num2 = dev_num;
1592       }
1593     }
1594
1595     hd = NULL;
1596
1597     if(sf_dev && !sf_drv_name) {
1598       hd = add_hd_entry(hd_data, __LINE__, 0);
1599       hd->base_class.id = bc_storage_device;
1600       hd->sub_class.id = sc_sdev_other;
1601
1602       str_printf(&hd->unix_dev_name, 0, "/dev/%s", hd_sysfs_name2_dev(sf_class_e->str));
1603
1604       hd->bus.id = bus_scsi;
1605
1606       hd->sysfs_id = new_str(hd_sysfs_id(sf_cdev));
1607
1608       hd->unix_dev_num = dev_num;
1609
1610       hd->sysfs_bus_id = new_str(bus_id);
1611
1612       if((s = hd_sysfs_id(sf_dev))) {
1613
1614         /* parent has longest matching sysfs id */
1615         u2 = strlen(s);
1616         for(u3 = 0, hd1 = hd_data->hd; hd1; hd1 = hd1->next) {
1617           if(hd1->sysfs_id) {
1618             u1 = strlen(hd1->sysfs_id);
1619             if(u1 > u3 && u1 <= u2 && !strncmp(s, hd1->sysfs_id, u1)) {
1620               u3 = u1;
1621               hd->attached_to = hd1->idx;
1622             }
1623           }
1624         }
1625
1626         /* find longest matching sysfs id we have a driver for */
1627         s = new_str(s);
1628         t = strrchr(s, '/');
1629         if(t) *t = 0;
1630         t = hd_sysfs_find_driver(hd_data, s, 0);
1631         if(t) {
1632           add_str_list(&hd->drivers, t);
1633         }
1634         s = free_mem(s);
1635
1636       }
1637
1638       add_scsi_sysfs_info(hd_data, hd, sf_dev);
1639     }
1640
1641     sf_dev = free_mem(sf_dev);
1642   }
1643
1644   sf_cdev = free_mem(sf_cdev);
1645   sf_class = free_str_list(sf_class);
1646 }
1647
1648
1649 void add_disk_size(hd_data_t *hd_data, hd_t *hd)
1650 {
1651   hd_res_t *geo, *size;
1652   int fd;
1653   char *pr_str;
1654
1655   pr_str = NULL;
1656
1657   if(
1658     hd->unix_dev_name &&
1659     hd->sub_class.id == sc_sdev_disk
1660   ) {
1661     PROGRESS(5, 0, hd->unix_dev_name);
1662     fd = open(hd->unix_dev_name, O_RDONLY | O_NONBLOCK);
1663     if(fd >= 0) {
1664
1665       str_printf(&pr_str, 0, "%s geo", hd->unix_dev_name);
1666       PROGRESS(5, 1, pr_str);
1667
1668       if(hd_getdisksize(hd_data, hd->unix_dev_name, fd, &geo, &size) == 1) {
1669         /* (low-level) unformatted disk */
1670         hd->is.notready = 1;
1671       }
1672
1673       if(geo) add_res_entry(&hd->res, geo);
1674       if(size) add_res_entry(&hd->res, size);
1675
1676       close(fd);
1677     }
1678   }
1679
1680   pr_str = free_mem(pr_str);
1681 }
1682
1683 /** @} */
1684