]> git.ipfire.org Git - thirdparty/util-linux.git/blob - lib/sysfs.c
lib/sysfs: fix compiler warning [-Wunused-variable]
[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 /*
543 * Returns by @diskdevno whole disk device devno and (optionaly) by
544 * @diskname the whole disk device name.
545 */
546 int sysfs_devno_to_wholedisk(dev_t dev, char *diskname,
547 size_t len, dev_t *diskdevno)
548 {
549 struct sysfs_cxt cxt;
550 int is_part = 0;
551
552 if (!dev || sysfs_init(&cxt, dev, NULL) != 0)
553 return -1;
554
555 is_part = sysfs_has_attribute(&cxt, "partition");
556 if (!is_part) {
557 /*
558 * Extra case for partitions mapped by device-mapper.
559 *
560 * All regualar partitions (added by BLKPG ioctl or kernel PT
561 * parser) have the /sys/.../partition file. The partitions
562 * mapped by DM don't have such file, but they have "part"
563 * prefix in DM UUID.
564 */
565 char *uuid = sysfs_strdup(&cxt, "dm/uuid");
566 char *tmp = uuid;
567 char *prefix = uuid ? strsep(&tmp, "-") : NULL;
568
569 if (prefix && strncasecmp(prefix, "part", 4) == 0)
570 is_part = 1;
571 free(uuid);
572
573 if (is_part &&
574 get_dm_wholedisk(&cxt, diskname, len, diskdevno) == 0)
575 /*
576 * partitioned device, mapped by DM
577 */
578 goto done;
579
580 is_part = 0;
581 }
582
583 if (!is_part) {
584 /*
585 * unpartitioned device
586 */
587 if (diskname && len) {
588 if (!sysfs_get_devname(&cxt, diskname, len))
589 goto err;
590 }
591 if (diskdevno)
592 *diskdevno = dev;
593
594 } else {
595 /*
596 * partitioned device
597 * - readlink /sys/dev/block/8:1 = ../../block/sda/sda1
598 * - dirname ../../block/sda/sda1 = ../../block/sda
599 * - basename ../../block/sda = sda
600 */
601 char linkpath[PATH_MAX];
602 char *name;
603 int linklen;
604
605 linklen = sysfs_readlink(&cxt, NULL,
606 linkpath, sizeof(linkpath) - 1);
607 if (linklen < 0)
608 goto err;
609 linkpath[linklen] = '\0';
610
611 stripoff_last_component(linkpath); /* dirname */
612 name = stripoff_last_component(linkpath); /* basename */
613 if (!name)
614 goto err;
615
616 if (diskname && len) {
617 strncpy(diskname, name, len);
618 diskname[len - 1] = '\0';
619 }
620
621 if (diskdevno) {
622 *diskdevno = sysfs_devname_to_devno(name, NULL);
623 if (!*diskdevno)
624 goto err;
625 }
626 }
627
628 done:
629 sysfs_deinit(&cxt);
630 return 0;
631 err:
632 sysfs_deinit(&cxt);
633 return -1;
634 }
635
636 /*
637 * Returns 1 if the device is private LVM device.
638 */
639 int sysfs_devno_is_lvm_private(dev_t devno)
640 {
641 struct sysfs_cxt cxt = UL_SYSFSCXT_EMPTY;
642 char *uuid = NULL;
643 int rc = 0;
644
645 if (sysfs_init(&cxt, devno, NULL) != 0)
646 return 0;
647
648 uuid = sysfs_strdup(&cxt, "dm/uuid");
649
650 /* Private LVM devices use "LVM-<uuid>-<name>" uuid format (important
651 * is the "LVM" prefix and "-<name>" postfix).
652 */
653 if (uuid && strncmp(uuid, "LVM-", 4) == 0) {
654 char *p = strrchr(uuid + 4, '-');
655
656 if (p && *(p + 1))
657 rc = 1;
658 }
659
660 sysfs_deinit(&cxt);
661 free(uuid);
662 return rc;
663 }
664
665 /*
666 * Return 0 or 1, or < 0 in case of error
667 */
668 int sysfs_devno_is_wholedisk(dev_t devno)
669 {
670 dev_t disk;
671
672 if (sysfs_devno_to_wholedisk(devno, NULL, 0, &disk) != 0)
673 return -1;
674
675 return devno == disk;
676 }
677
678 int sysfs_scsi_get_hctl(struct sysfs_cxt *cxt, int *h, int *c, int *t, int *l)
679 {
680 char buf[PATH_MAX], *hctl;
681 ssize_t len;
682
683 if (!cxt)
684 return -EINVAL;
685 if (cxt->has_hctl)
686 goto done;
687
688 len = sysfs_readlink(cxt, "device", buf, sizeof(buf) - 1);
689 if (len < 0)
690 return len;
691
692 buf[len] = '\0';
693 hctl = strrchr(buf, '/');
694 if (!hctl)
695 return -1;
696 hctl++;
697
698 if (sscanf(hctl, "%u:%u:%u:%u", &cxt->scsi_host, &cxt->scsi_channel,
699 &cxt->scsi_target, &cxt->scsi_lun) != 4)
700 return -1;
701
702 cxt->has_hctl = 1;
703 done:
704 if (h)
705 *h = cxt->scsi_host;
706 if (c)
707 *c = cxt->scsi_channel;
708 if (t)
709 *t = cxt->scsi_target;
710 if (l)
711 *l = cxt->scsi_lun;
712 return 0;
713 }
714
715
716 static char *sysfs_scsi_host_attribute_path(struct sysfs_cxt *cxt,
717 const char *type, char *buf, size_t bufsz, const char *attr)
718 {
719 int len;
720 int host;
721
722 if (sysfs_scsi_get_hctl(cxt, &host, NULL, NULL, NULL))
723 return NULL;
724
725 if (attr)
726 len = snprintf(buf, bufsz, _PATH_SYS_CLASS "/%s_host/host%d/%s",
727 type, host, attr);
728 else
729 len = snprintf(buf, bufsz, _PATH_SYS_CLASS "/%s_host/host%d",
730 type, host);
731
732 return (len < 0 || (size_t) len + 1 > bufsz) ? NULL : buf;
733 }
734
735 char *sysfs_scsi_host_strdup_attribute(struct sysfs_cxt *cxt,
736 const char *type, const char *attr)
737 {
738 char buf[1024];
739 int rc;
740 FILE *f;
741
742 if (!attr || !type ||
743 !sysfs_scsi_host_attribute_path(cxt, type, buf, sizeof(buf), attr))
744 return NULL;
745
746 if (!(f = fopen(buf, "r" UL_CLOEXECSTR)))
747 return NULL;
748
749 rc = fscanf(f, "%1023[^\n]", buf);
750 fclose(f);
751
752 return rc == 1 ? strdup(buf) : NULL;
753 }
754
755 int sysfs_scsi_host_is(struct sysfs_cxt *cxt, const char *type)
756 {
757 char buf[PATH_MAX];
758 struct stat st;
759
760 if (!type || !sysfs_scsi_host_attribute_path(cxt, type,
761 buf, sizeof(buf), NULL))
762 return 0;
763
764 return stat(buf, &st) == 0 && S_ISDIR(st.st_mode);
765 }
766
767 static char *sysfs_scsi_attribute_path(struct sysfs_cxt *cxt,
768 char *buf, size_t bufsz, const char *attr)
769 {
770 int len, h, c, t, l;
771
772 if (sysfs_scsi_get_hctl(cxt, &h, &c, &t, &l) != 0)
773 return NULL;
774
775 if (attr)
776 len = snprintf(buf, bufsz, _PATH_SYS_SCSI "/devices/%d:%d:%d:%d/%s",
777 h,c,t,l, attr);
778 else
779 len = snprintf(buf, bufsz, _PATH_SYS_SCSI "/devices/%d:%d:%d:%d",
780 h,c,t,l);
781 return (len < 0 || (size_t) len + 1 > bufsz) ? NULL : buf;
782 }
783
784 int sysfs_scsi_has_attribute(struct sysfs_cxt *cxt, const char *attr)
785 {
786 char path[PATH_MAX];
787 struct stat st;
788
789 if (!sysfs_scsi_attribute_path(cxt, path, sizeof(path), attr))
790 return 0;
791
792 return stat(path, &st) == 0;
793 }
794
795 int sysfs_scsi_path_contains(struct sysfs_cxt *cxt, const char *pattern)
796 {
797 char path[PATH_MAX], linkc[PATH_MAX];
798 struct stat st;
799 ssize_t len;
800
801 if (!sysfs_scsi_attribute_path(cxt, path, sizeof(path), NULL))
802 return 0;
803
804 if (stat(path, &st) != 0)
805 return 0;
806
807 len = readlink(path, linkc, sizeof(linkc) - 1);
808 if (len < 0)
809 return 0;
810
811 linkc[len] = '\0';
812 return strstr(linkc, pattern) != NULL;
813 }
814
815 #ifdef TEST_PROGRAM_SYSFS
816 #include <errno.h>
817 #include <err.h>
818 #include <stdlib.h>
819
820 int main(int argc, char *argv[])
821 {
822 struct sysfs_cxt cxt = UL_SYSFSCXT_EMPTY;
823 char *devname;
824 dev_t devno;
825 char path[PATH_MAX];
826 int i, is_part;
827 uint64_t u64;
828 ssize_t len;
829
830 if (argc != 2)
831 errx(EXIT_FAILURE, "usage: %s <devname>", argv[0]);
832
833 devname = argv[1];
834 devno = sysfs_devname_to_devno(devname, NULL);
835
836 if (!devno)
837 err(EXIT_FAILURE, "failed to read devno");
838
839 is_part = sysfs_devno_has_attribute(devno, "partition");
840
841 printf("NAME: %s\n", devname);
842 printf("DEVNO: %u (%d:%d)\n", (unsigned int) devno, major(devno), minor(devno));
843 printf("DEVNOPATH: %s\n", sysfs_devno_path(devno, path, sizeof(path)));
844 printf("DEVPATH: %s\n", sysfs_devno_to_devpath(devno, path, sizeof(path)));
845 printf("PARTITION: %s\n", is_part ? "YES" : "NOT");
846
847 if (sysfs_init(&cxt, devno, NULL))
848 return EXIT_FAILURE;
849
850 len = sysfs_readlink(&cxt, NULL, path, sizeof(path) - 1);
851 if (len > 0) {
852 path[len] = '\0';
853 printf("DEVNOLINK: %s\n", path);
854 }
855
856 if (!is_part) {
857 printf("First 5 partitions:\n");
858 for (i = 1; i <= 5; i++) {
859 dev_t dev = sysfs_partno_to_devno(&cxt, i);
860 if (dev)
861 printf("\t#%d %d:%d\n", i, major(dev), minor(dev));
862 }
863 }
864
865 printf("SLAVES: %d\n", sysfs_count_dirents(&cxt, "slaves"));
866
867 if (sysfs_read_u64(&cxt, "size", &u64))
868 printf("read SIZE failed\n");
869 else
870 printf("SIZE: %jd\n", u64);
871
872 if (sysfs_read_int(&cxt, "queue/hw_sector_size", &i))
873 printf("read SECTOR failed\n");
874 else
875 printf("SECTOR: %d\n", i);
876
877 printf("DEVNAME: %s\n", sysfs_get_devname(&cxt, path, sizeof(path)));
878
879 sysfs_deinit(&cxt);
880 return EXIT_SUCCESS;
881 }
882 #endif