]> git.ipfire.org Git - thirdparty/util-linux.git/blob - lib/sysfs.c
lib/fileutils: add stripoff_last_component() from libmount
[thirdparty/util-linux.git] / lib / sysfs.c
1 /*
2 * No copyright is claimed. This code is in the public domain; do with
3 * it what you wish.
4 *
5 * Written by Karel Zak <kzak@redhat.com>
6 */
7 #include <ctype.h>
8
9 #include "c.h"
10 #include "at.h"
11 #include "pathnames.h"
12 #include "sysfs.h"
13 #include "fileutils.h"
14
15 char *sysfs_devno_attribute_path(dev_t devno, char *buf,
16 size_t bufsiz, const char *attr)
17 {
18 int len;
19
20 if (attr)
21 len = snprintf(buf, bufsiz, _PATH_SYS_DEVBLOCK "/%d:%d/%s",
22 major(devno), minor(devno), attr);
23 else
24 len = snprintf(buf, bufsiz, _PATH_SYS_DEVBLOCK "/%d:%d",
25 major(devno), minor(devno));
26
27 return (len < 0 || (size_t) len + 1 > bufsiz) ? NULL : buf;
28 }
29
30 int sysfs_devno_has_attribute(dev_t devno, const char *attr)
31 {
32 char path[PATH_MAX];
33 struct stat info;
34
35 if (!sysfs_devno_attribute_path(devno, path, sizeof(path), attr))
36 return 0;
37 if (stat(path, &info) == 0)
38 return 1;
39 return 0;
40 }
41
42 char *sysfs_devno_path(dev_t devno, char *buf, size_t bufsiz)
43 {
44 return sysfs_devno_attribute_path(devno, buf, bufsiz, NULL);
45 }
46
47 dev_t sysfs_devname_to_devno(const char *name, const char *parent)
48 {
49 char buf[PATH_MAX], *path = NULL;
50 dev_t dev = 0;
51
52 if (strncmp("/dev/", name, 5) == 0) {
53 /*
54 * Read from /dev
55 */
56 struct stat st;
57
58 if (stat(name, &st) == 0)
59 dev = st.st_rdev;
60 else
61 name += 5; /* unaccesible, or not node in /dev */
62 }
63
64 if (!dev && parent && strncmp("dm-", name, 3)) {
65 /*
66 * Create path to /sys/block/<parent>/<name>/dev
67 */
68 int len = snprintf(buf, sizeof(buf),
69 _PATH_SYS_BLOCK "/%s/%s/dev", parent, name);
70 if (len < 0 || (size_t) len + 1 > sizeof(buf))
71 return 0;
72 path = buf;
73
74 } else if (!dev) {
75 /*
76 * Create path to /sys/block/<name>/dev
77 */
78 int len = snprintf(buf, sizeof(buf),
79 _PATH_SYS_BLOCK "/%s/dev", name);
80 if (len < 0 || (size_t) len + 1 > sizeof(buf))
81 return 0;
82 path = buf;
83 }
84
85 if (path) {
86 /*
87 * read devno from sysfs
88 */
89 FILE *f;
90 int maj = 0, min = 0;
91
92 f = fopen(path, "r" UL_CLOEXECSTR);
93 if (!f)
94 return 0;
95
96 if (fscanf(f, "%d:%d", &maj, &min) == 2)
97 dev = makedev(maj, min);
98 fclose(f);
99 }
100 return dev;
101 }
102
103 /*
104 * Returns devname (e.g. "/dev/sda1") for the given devno.
105 *
106 * Note that the @buf has to be large enough to store /sys/dev/block/<maj:min>
107 * symlinks.
108 *
109 * Please, use more robust blkid_devno_to_devname() in your applications.
110 */
111 char *sysfs_devno_to_devpath(dev_t devno, char *buf, size_t bufsiz)
112 {
113 struct sysfs_cxt cxt;
114 char *name;
115 size_t sz;
116 struct stat st;
117
118 if (sysfs_init(&cxt, devno, NULL))
119 return NULL;
120
121 name = sysfs_get_devname(&cxt, buf, bufsiz);
122 sysfs_deinit(&cxt);
123
124 if (!name)
125 return NULL;
126
127 sz = strlen(name);
128
129 if (sz + sizeof("/dev/") > bufsiz)
130 return NULL;
131
132 /* create the final "/dev/<name>" string */
133 memmove(buf + 5, name, sz + 1);
134 memcpy(buf, "/dev/", 5);
135
136 if (!stat(buf, &st) && S_ISBLK(st.st_mode) && st.st_rdev == devno)
137 return buf;
138
139 return NULL;
140 }
141
142 int sysfs_init(struct sysfs_cxt *cxt, dev_t devno, struct sysfs_cxt *parent)
143 {
144 char path[PATH_MAX];
145 int fd, rc;
146
147 memset(cxt, 0, sizeof(*cxt));
148 cxt->dir_fd = -1;
149
150 if (!sysfs_devno_path(devno, path, sizeof(path)))
151 goto err;
152
153 fd = open(path, O_RDONLY|O_CLOEXEC);
154 if (fd < 0)
155 goto err;
156 cxt->dir_fd = fd;
157
158 cxt->dir_path = strdup(path);
159 if (!cxt->dir_path)
160 goto err;
161 cxt->devno = devno;
162 cxt->parent = parent;
163 return 0;
164 err:
165 rc = errno > 0 ? -errno : -1;
166 sysfs_deinit(cxt);
167 return rc;
168 }
169
170 void sysfs_deinit(struct sysfs_cxt *cxt)
171 {
172 if (!cxt)
173 return;
174
175 if (cxt->dir_fd >= 0)
176 close(cxt->dir_fd);
177 free(cxt->dir_path);
178
179 memset(cxt, 0, sizeof(*cxt));
180
181 cxt->dir_fd = -1;
182 }
183
184 int sysfs_stat(struct sysfs_cxt *cxt, const char *attr, struct stat *st)
185 {
186 int rc = fstat_at(cxt->dir_fd, cxt->dir_path, attr, st, 0);
187
188 if (rc != 0 && errno == ENOENT &&
189 strncmp(attr, "queue/", 6) == 0 && cxt->parent) {
190
191 /* Exception for "queue/<attr>". These attributes are available
192 * for parental devices only
193 */
194 return fstat_at(cxt->parent->dir_fd,
195 cxt->parent->dir_path, attr, st, 0);
196 }
197 return rc;
198 }
199
200 int sysfs_has_attribute(struct sysfs_cxt *cxt, const char *attr)
201 {
202 struct stat st;
203
204 return sysfs_stat(cxt, attr, &st) == 0;
205 }
206
207 static int sysfs_open(struct sysfs_cxt *cxt, const char *attr)
208 {
209 int fd = open_at(cxt->dir_fd, cxt->dir_path, attr, O_RDONLY|O_CLOEXEC);
210
211 if (fd == -1 && errno == ENOENT &&
212 strncmp(attr, "queue/", 6) == 0 && cxt->parent) {
213
214 /* Exception for "queue/<attr>". These attributes are available
215 * for parental devices only
216 */
217 fd = open_at(cxt->parent->dir_fd, cxt->dir_path, attr,
218 O_RDONLY|O_CLOEXEC);
219 }
220 return fd;
221 }
222
223 ssize_t sysfs_readlink(struct sysfs_cxt *cxt, const char *attr,
224 char *buf, size_t bufsiz)
225 {
226 if (!cxt->dir_path)
227 return -1;
228
229 if (attr)
230 return readlink_at(cxt->dir_fd, cxt->dir_path, attr, buf, bufsiz);
231
232 /* read /sys/dev/block/<maj:min> link */
233 return readlink(cxt->dir_path, buf, bufsiz);
234 }
235
236 DIR *sysfs_opendir(struct sysfs_cxt *cxt, const char *attr)
237 {
238 DIR *dir;
239 int fd = -1;
240
241 if (attr)
242 fd = sysfs_open(cxt, attr);
243
244 else if (cxt->dir_fd >= 0)
245 /* request to open root of device in sysfs (/sys/block/<dev>)
246 * -- we cannot use cxt->sysfs_fd directly, because closedir()
247 * will close this our persistent file descriptor.
248 */
249 fd = dup(cxt->dir_fd);
250
251 if (fd < 0)
252 return NULL;
253
254 dir = fdopendir(fd);
255 if (!dir) {
256 close(fd);
257 return NULL;
258 }
259 if (!attr)
260 rewinddir(dir);
261 return dir;
262 }
263
264
265 static FILE *sysfs_fopen(struct sysfs_cxt *cxt, const char *attr)
266 {
267 int fd = sysfs_open(cxt, attr);
268
269 return fd < 0 ? NULL : fdopen(fd, "r" UL_CLOEXECSTR);
270 }
271
272
273 static struct dirent *xreaddir(DIR *dp)
274 {
275 struct dirent *d;
276
277 while ((d = readdir(dp))) {
278 if (!strcmp(d->d_name, ".") ||
279 !strcmp(d->d_name, ".."))
280 continue;
281
282 /* blacklist here? */
283 break;
284 }
285 return d;
286 }
287
288 int sysfs_is_partition_dirent(DIR *dir, struct dirent *d, const char *parent_name)
289 {
290 char path[256];
291
292 #ifdef _DIRENT_HAVE_D_TYPE
293 if (d->d_type != DT_DIR &&
294 d->d_type != DT_LNK &&
295 d->d_type != DT_UNKNOWN)
296 return 0;
297 #endif
298 if (parent_name) {
299 const char *p = parent_name;
300 size_t len;
301
302 /* /dev/sda --> "sda" */
303 if (*parent_name == '/') {
304 p = strrchr(parent_name, '/');
305 if (!p)
306 return 0;
307 p++;
308 }
309
310 len = strlen(p);
311 if (strlen(d->d_name) <= len)
312 return 0;
313
314 /* partitions subdir name is
315 * "<parent>[:digit:]" or "<parent>p[:digit:]"
316 */
317 return strncmp(p, d->d_name, len) == 0 &&
318 ((*(d->d_name + len) == 'p' && isdigit(*(d->d_name + len + 1)))
319 || isdigit(*(d->d_name + len)));
320 }
321
322 /* Cannot use /partition file, not supported on old sysfs */
323 snprintf(path, sizeof(path), "%s/start", d->d_name);
324
325 return faccessat(dirfd(dir), path, R_OK, 0) == 0;
326 }
327
328 /*
329 * Converts @partno (partition number) to devno of the partition.
330 * The @cxt handles wholedisk device.
331 *
332 * Note that this code does not expect any special format of the
333 * partitions devnames.
334 */
335 dev_t sysfs_partno_to_devno(struct sysfs_cxt *cxt, int partno)
336 {
337 DIR *dir;
338 struct dirent *d;
339 char path[256];
340 dev_t devno = 0;
341
342 dir = sysfs_opendir(cxt, NULL);
343 if (!dir)
344 return 0;
345
346 while ((d = xreaddir(dir))) {
347 int n, maj, min;
348
349 if (!sysfs_is_partition_dirent(dir, d, NULL))
350 continue;
351
352 snprintf(path, sizeof(path), "%s/partition", d->d_name);
353 if (sysfs_read_int(cxt, path, &n))
354 continue;
355
356 if (n == partno) {
357 snprintf(path, sizeof(path), "%s/dev", d->d_name);
358 if (sysfs_scanf(cxt, path, "%d:%d", &maj, &min) == 2)
359 devno = makedev(maj, min);
360 break;
361 }
362 }
363
364 closedir(dir);
365 return devno;
366 }
367
368
369 int sysfs_scanf(struct sysfs_cxt *cxt, const char *attr, const char *fmt, ...)
370 {
371 FILE *f = sysfs_fopen(cxt, attr);
372 va_list ap;
373 int rc;
374
375 if (!f)
376 return -EINVAL;
377 va_start(ap, fmt);
378 rc = vfscanf(f, fmt, ap);
379 va_end(ap);
380
381 fclose(f);
382 return rc;
383 }
384
385
386 int sysfs_read_s64(struct sysfs_cxt *cxt, const char *attr, int64_t *res)
387 {
388 int64_t x = 0;
389
390 if (sysfs_scanf(cxt, attr, "%"SCNd64, &x) == 1) {
391 if (res)
392 *res = x;
393 return 0;
394 }
395 return -1;
396 }
397
398 int sysfs_read_u64(struct sysfs_cxt *cxt, const char *attr, uint64_t *res)
399 {
400 uint64_t x = 0;
401
402 if (sysfs_scanf(cxt, attr, "%"SCNu64, &x) == 1) {
403 if (res)
404 *res = x;
405 return 0;
406 }
407 return -1;
408 }
409
410 int sysfs_read_int(struct sysfs_cxt *cxt, const char *attr, int *res)
411 {
412 int x = 0;
413
414 if (sysfs_scanf(cxt, attr, "%d", &x) == 1) {
415 if (res)
416 *res = x;
417 return 0;
418 }
419 return -1;
420 }
421
422 char *sysfs_strdup(struct sysfs_cxt *cxt, const char *attr)
423 {
424 char buf[1024];
425 return sysfs_scanf(cxt, attr, "%1023[^\n]", buf) == 1 ?
426 strdup(buf) : NULL;
427 }
428
429 int sysfs_count_dirents(struct sysfs_cxt *cxt, const char *attr)
430 {
431 DIR *dir;
432 int r = 0;
433
434 if (!(dir = sysfs_opendir(cxt, attr)))
435 return 0;
436
437 while (xreaddir(dir)) r++;
438
439 closedir(dir);
440 return r;
441 }
442
443 int sysfs_count_partitions(struct sysfs_cxt *cxt, const char *devname)
444 {
445 DIR *dir;
446 struct dirent *d;
447 int r = 0;
448
449 if (!(dir = sysfs_opendir(cxt, NULL)))
450 return 0;
451
452 while ((d = xreaddir(dir))) {
453 if (sysfs_is_partition_dirent(dir, d, devname))
454 r++;
455 }
456
457 closedir(dir);
458 return r;
459 }
460
461 /*
462 * Returns slave name if there is only one slave, otherwise returns NULL.
463 * The result should be deallocated by free().
464 */
465 char *sysfs_get_slave(struct sysfs_cxt *cxt)
466 {
467 DIR *dir;
468 struct dirent *d;
469 char *name = NULL;
470
471 if (!(dir = sysfs_opendir(cxt, "slaves")))
472 return NULL;
473
474 while ((d = xreaddir(dir))) {
475 if (name)
476 goto err; /* more slaves */
477
478 name = strdup(d->d_name);
479 }
480
481 closedir(dir);
482 return name;
483 err:
484 free(name);
485 closedir(dir);
486 return NULL;
487 }
488
489 /*
490 * Note that the @buf has to be large enough to store /sys/dev/block/<maj:min>
491 * symlinks.
492 */
493 char *sysfs_get_devname(struct sysfs_cxt *cxt, char *buf, size_t bufsiz)
494 {
495 char *name = NULL;
496 ssize_t sz;
497
498 sz = sysfs_readlink(cxt, NULL, buf, bufsiz - 1);
499 if (sz < 0)
500 return NULL;
501
502 buf[sz] = '\0';
503 name = strrchr(buf, '/');
504 if (!name)
505 return NULL;
506
507 name++;
508 sz = strlen(name);
509
510 memmove(buf, name, sz + 1);
511 return buf;
512 }
513
514 static int get_dm_wholedisk(struct sysfs_cxt *cxt, char *diskname,
515 size_t len, dev_t *diskdevno)
516 {
517 int rc = 0;
518 char *name;
519
520 /* Note, sysfs_get_slave() returns the first slave only,
521 * if there is more slaves, then return NULL
522 */
523 name = sysfs_get_slave(cxt);
524 if (!name)
525 return -1;
526
527 if (diskname && len) {
528 strncpy(diskname, name, len);
529 diskname[len - 1] = '\0';
530 }
531
532 if (diskdevno) {
533 *diskdevno = sysfs_devname_to_devno(name, NULL);
534 if (!*diskdevno)
535 rc = -1;
536 }
537
538 free(name);
539 return rc;
540 }
541
542 int sysfs_devno_to_wholedisk(dev_t dev, char *diskname,
543 size_t len, dev_t *diskdevno)
544 {
545 struct sysfs_cxt cxt;
546 int is_part = 0;
547
548 if (!dev || sysfs_init(&cxt, dev, NULL) != 0)
549 return -1;
550
551 is_part = sysfs_has_attribute(&cxt, "partition");
552 if (!is_part) {
553 /*
554 * Extra case for partitions mapped by device-mapper.
555 *
556 * All regualar partitions (added by BLKPG ioctl or kernel PT
557 * parser) have the /sys/.../partition file. The partitions
558 * mapped by DM don't have such file, but they have "part"
559 * prefix in DM UUID.
560 */
561 char *uuid = sysfs_strdup(&cxt, "dm/uuid");
562 char *tmp = uuid;
563 char *prefix = uuid ? strsep(&tmp, "-") : NULL;
564
565 if (prefix && strncasecmp(prefix, "part", 4) == 0)
566 is_part = 1;
567 free(uuid);
568
569 if (is_part &&
570 get_dm_wholedisk(&cxt, diskname, len, diskdevno) == 0)
571 /*
572 * partitioned device, mapped by DM
573 */
574 goto done;
575
576 is_part = 0;
577 }
578
579 if (!is_part) {
580 /*
581 * unpartitioned device
582 */
583 if (diskname && len) {
584 if (!sysfs_get_devname(&cxt, diskname, len))
585 goto err;
586 }
587 if (diskdevno)
588 *diskdevno = dev;
589
590 } else {
591 /*
592 * partitioned device
593 * - readlink /sys/dev/block/8:1 = ../../block/sda/sda1
594 * - dirname ../../block/sda/sda1 = ../../block/sda
595 * - basename ../../block/sda = sda
596 */
597 char linkpath[PATH_MAX];
598 char *name;
599 int linklen;
600
601 linklen = sysfs_readlink(&cxt, NULL,
602 linkpath, sizeof(linkpath) - 1);
603 if (linklen < 0)
604 goto err;
605 linkpath[linklen] = '\0';
606
607 stripoff_last_component(linkpath); /* dirname */
608 name = stripoff_last_component(linkpath); /* basename */
609 if (!name)
610 goto err;
611
612 if (diskname && len) {
613 strncpy(diskname, name, len);
614 diskname[len - 1] = '\0';
615 }
616
617 if (diskdevno) {
618 *diskdevno = sysfs_devname_to_devno(name, NULL);
619 if (!*diskdevno)
620 goto err;
621 }
622 }
623
624 done:
625 sysfs_deinit(&cxt);
626 return 0;
627 err:
628 sysfs_deinit(&cxt);
629 return -1;
630 }
631
632
633 int sysfs_scsi_get_hctl(struct sysfs_cxt *cxt, int *h, int *c, int *t, int *l)
634 {
635 char buf[PATH_MAX], *hctl;
636 ssize_t len;
637
638 if (!cxt)
639 return -EINVAL;
640 if (cxt->has_hctl)
641 goto done;
642
643 len = sysfs_readlink(cxt, "device", buf, sizeof(buf) - 1);
644 if (len < 0)
645 return len;
646
647 buf[len] = '\0';
648 hctl = strrchr(buf, '/');
649 if (!hctl)
650 return -1;
651 hctl++;
652
653 if (sscanf(hctl, "%u:%u:%u:%u", &cxt->scsi_host, &cxt->scsi_channel,
654 &cxt->scsi_target, &cxt->scsi_lun) != 4)
655 return -1;
656
657 cxt->has_hctl = 1;
658 done:
659 if (h)
660 *h = cxt->scsi_host;
661 if (c)
662 *c = cxt->scsi_channel;
663 if (t)
664 *t = cxt->scsi_target;
665 if (l)
666 *l = cxt->scsi_lun;
667 return 0;
668 }
669
670
671 static char *sysfs_scsi_host_attribute_path(struct sysfs_cxt *cxt,
672 const char *type, char *buf, size_t bufsz, const char *attr)
673 {
674 int len;
675 int host;
676
677 if (sysfs_scsi_get_hctl(cxt, &host, NULL, NULL, NULL))
678 return NULL;
679
680 if (attr)
681 len = snprintf(buf, bufsz, _PATH_SYS_CLASS "/%s_host/host%d/%s",
682 type, host, attr);
683 else
684 len = snprintf(buf, bufsz, _PATH_SYS_CLASS "/%s_host/host%d",
685 type, host);
686
687 return (len < 0 || (size_t) len + 1 > bufsz) ? NULL : buf;
688 }
689
690 char *sysfs_scsi_host_strdup_attribute(struct sysfs_cxt *cxt,
691 const char *type, const char *attr)
692 {
693 char buf[1024];
694 int rc;
695 FILE *f;
696
697 if (!attr || !type ||
698 !sysfs_scsi_host_attribute_path(cxt, type, buf, sizeof(buf), attr))
699 return NULL;
700
701 if (!(f = fopen(buf, "r" UL_CLOEXECSTR)))
702 return NULL;
703
704 rc = fscanf(f, "%1023[^\n]", buf);
705 fclose(f);
706
707 return rc == 1 ? strdup(buf) : NULL;
708 }
709
710 int sysfs_scsi_host_is(struct sysfs_cxt *cxt, const char *type)
711 {
712 char buf[PATH_MAX];
713 struct stat st;
714
715 if (!type || !sysfs_scsi_host_attribute_path(cxt, type,
716 buf, sizeof(buf), NULL))
717 return 0;
718
719 return stat(buf, &st) == 0 && S_ISDIR(st.st_mode);
720 }
721
722 static char *sysfs_scsi_attribute_path(struct sysfs_cxt *cxt,
723 char *buf, size_t bufsz, const char *attr)
724 {
725 int len, h, c, t, l;
726
727 if (sysfs_scsi_get_hctl(cxt, &h, &c, &t, &l) != 0)
728 return NULL;
729
730 if (attr)
731 len = snprintf(buf, bufsz, _PATH_SYS_SCSI "/devices/%d:%d:%d:%d/%s",
732 h,c,t,l, attr);
733 else
734 len = snprintf(buf, bufsz, _PATH_SYS_SCSI "/devices/%d:%d:%d:%d",
735 h,c,t,l);
736 return (len < 0 || (size_t) len + 1 > bufsz) ? NULL : buf;
737 }
738
739 int sysfs_scsi_has_attribute(struct sysfs_cxt *cxt, const char *attr)
740 {
741 char path[PATH_MAX];
742 struct stat st;
743
744 if (!sysfs_scsi_attribute_path(cxt, path, sizeof(path), attr))
745 return 0;
746
747 return stat(path, &st) == 0;
748 }
749
750 int sysfs_scsi_path_contains(struct sysfs_cxt *cxt, const char *pattern)
751 {
752 char path[PATH_MAX], linkc[PATH_MAX];
753 struct stat st;
754 ssize_t len;
755
756 if (!sysfs_scsi_attribute_path(cxt, path, sizeof(path), NULL))
757 return 0;
758
759 if (stat(path, &st) != 0)
760 return 0;
761
762 len = readlink(path, linkc, sizeof(linkc) - 1);
763 if (len < 0)
764 return 0;
765
766 linkc[len] = '\0';
767 return strstr(linkc, pattern) != NULL;
768 }
769
770 #ifdef TEST_PROGRAM_SYSFS
771 #include <errno.h>
772 #include <err.h>
773 #include <stdlib.h>
774
775 int main(int argc, char *argv[])
776 {
777 struct sysfs_cxt cxt = UL_SYSFSCXT_EMPTY;
778 char *devname;
779 dev_t devno;
780 char path[PATH_MAX];
781 int i, is_part;
782 uint64_t u64;
783 ssize_t len;
784
785 if (argc != 2)
786 errx(EXIT_FAILURE, "usage: %s <devname>", argv[0]);
787
788 devname = argv[1];
789 devno = sysfs_devname_to_devno(devname, NULL);
790
791 if (!devno)
792 err(EXIT_FAILURE, "failed to read devno");
793
794 is_part = sysfs_devno_has_attribute(devno, "partition");
795
796 printf("NAME: %s\n", devname);
797 printf("DEVNO: %u (%d:%d)\n", (unsigned int) devno, major(devno), minor(devno));
798 printf("DEVNOPATH: %s\n", sysfs_devno_path(devno, path, sizeof(path)));
799 printf("DEVPATH: %s\n", sysfs_devno_to_devpath(devno, path, sizeof(path)));
800 printf("PARTITION: %s\n", is_part ? "YES" : "NOT");
801
802 if (sysfs_init(&cxt, devno, NULL))
803 return EXIT_FAILURE;
804
805 len = sysfs_readlink(&cxt, NULL, path, sizeof(path) - 1);
806 if (len > 0) {
807 path[len] = '\0';
808 printf("DEVNOLINK: %s\n", path);
809 }
810
811 if (!is_part) {
812 printf("First 5 partitions:\n");
813 for (i = 1; i <= 5; i++) {
814 dev_t dev = sysfs_partno_to_devno(&cxt, i);
815 if (dev)
816 printf("\t#%d %d:%d\n", i, major(dev), minor(dev));
817 }
818 }
819
820 printf("SLAVES: %d\n", sysfs_count_dirents(&cxt, "slaves"));
821
822 if (sysfs_read_u64(&cxt, "size", &u64))
823 printf("read SIZE failed\n");
824 else
825 printf("SIZE: %jd\n", u64);
826
827 if (sysfs_read_int(&cxt, "queue/hw_sector_size", &i))
828 printf("read SECTOR failed\n");
829 else
830 printf("SECTOR: %d\n", i);
831
832 printf("DEVNAME: %s\n", sysfs_get_devname(&cxt, path, sizeof(path)));
833
834 sysfs_deinit(&cxt);
835 return EXIT_SUCCESS;
836 }
837 #endif