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