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