]>
git.ipfire.org Git - thirdparty/util-linux.git/blob - lib/sysfs.c
9e973a4f76ee252d12def747f5b93bfe4974d042
2 * No copyright is claimed. This code is in the public domain; do with
5 * Written by Karel Zak <kzak@redhat.com>
14 #include "pathnames.h"
16 #include "fileutils.h"
19 char *sysfs_devno_attribute_path(dev_t devno
, char *buf
,
20 size_t bufsiz
, const char *attr
)
25 len
= snprintf(buf
, bufsiz
, _PATH_SYS_DEVBLOCK
"/%d:%d/%s",
26 major(devno
), minor(devno
), attr
);
28 len
= snprintf(buf
, bufsiz
, _PATH_SYS_DEVBLOCK
"/%d:%d",
29 major(devno
), minor(devno
));
31 return (len
< 0 || (size_t) len
>= bufsiz
) ? NULL
: buf
;
34 int sysfs_devno_has_attribute(dev_t devno
, const char *attr
)
39 if (!sysfs_devno_attribute_path(devno
, path
, sizeof(path
), attr
))
41 if (stat(path
, &info
) == 0)
46 char *sysfs_devno_path(dev_t devno
, char *buf
, size_t bufsiz
)
48 return sysfs_devno_attribute_path(devno
, buf
, bufsiz
, NULL
);
51 dev_t
sysfs_devname_to_devno(const char *name
, const char *parent
)
53 char buf
[PATH_MAX
], *path
= NULL
;
56 if (strncmp("/dev/", name
, 5) == 0) {
62 if (stat(name
, &st
) == 0)
65 name
+= 5; /* unaccesible, or not node in /dev */
68 if (!dev
&& parent
&& strncmp("dm-", name
, 3)) {
70 * Create path to /sys/block/<parent>/<name>/dev
72 char *_name
= strdup(name
), *_parent
= strdup(parent
);
75 if (!_name
|| !_parent
) {
80 sysfs_devname_dev_to_sys(_name
);
81 sysfs_devname_dev_to_sys(_parent
);
83 len
= snprintf(buf
, sizeof(buf
),
84 _PATH_SYS_BLOCK
"/%s/%s/dev", _parent
, _name
);
87 if (len
< 0 || (size_t) len
>= sizeof(buf
))
93 * Create path to /sys/block/<sysname>/dev
95 char *_name
= strdup(name
);
101 sysfs_devname_dev_to_sys(_name
);
102 len
= snprintf(buf
, sizeof(buf
),
103 _PATH_SYS_BLOCK
"/%s/dev", _name
);
105 if (len
< 0 || (size_t) len
>= sizeof(buf
))
112 * read devno from sysfs
115 int maj
= 0, min
= 0;
117 f
= fopen(path
, "r" UL_CLOEXECSTR
);
121 if (fscanf(f
, "%d:%d", &maj
, &min
) == 2)
122 dev
= makedev(maj
, min
);
129 * Returns devname (e.g. "/dev/sda1") for the given devno.
131 * Please, use more robust blkid_devno_to_devname() in your applications.
133 char *sysfs_devno_to_devpath(dev_t devno
, char *buf
, size_t bufsiz
)
135 struct sysfs_cxt cxt
;
140 if (sysfs_init(&cxt
, devno
, NULL
))
143 name
= sysfs_get_devname(&cxt
, buf
, bufsiz
);
151 if (sz
+ sizeof("/dev/") > bufsiz
)
154 /* create the final "/dev/<name>" string */
155 memmove(buf
+ 5, name
, sz
+ 1);
156 memcpy(buf
, "/dev/", 5);
158 if (!stat(buf
, &st
) && S_ISBLK(st
.st_mode
) && st
.st_rdev
== devno
)
164 int sysfs_init(struct sysfs_cxt
*cxt
, dev_t devno
, struct sysfs_cxt
*parent
)
169 memset(cxt
, 0, sizeof(*cxt
));
172 if (!sysfs_devno_path(devno
, path
, sizeof(path
)))
175 fd
= open(path
, O_RDONLY
|O_CLOEXEC
);
180 cxt
->dir_path
= strdup(path
);
184 cxt
->parent
= parent
;
187 rc
= errno
> 0 ? -errno
: -1;
192 void sysfs_deinit(struct sysfs_cxt
*cxt
)
197 if (cxt
->dir_fd
>= 0)
201 memset(cxt
, 0, sizeof(*cxt
));
206 int sysfs_stat(struct sysfs_cxt
*cxt
, const char *attr
, struct stat
*st
)
208 int rc
= fstatat(cxt
->dir_fd
, attr
, st
, 0);
210 if (rc
!= 0 && errno
== ENOENT
&&
211 strncmp(attr
, "queue/", 6) == 0 && cxt
->parent
) {
213 /* Exception for "queue/<attr>". These attributes are available
214 * for parental devices only
216 return fstatat(cxt
->parent
->dir_fd
, attr
, st
, 0);
221 int sysfs_has_attribute(struct sysfs_cxt
*cxt
, const char *attr
)
225 return sysfs_stat(cxt
, attr
, &st
) == 0;
228 static int sysfs_open(struct sysfs_cxt
*cxt
, const char *attr
, int flags
)
230 int fd
= openat(cxt
->dir_fd
, attr
, flags
);
232 if (fd
== -1 && errno
== ENOENT
&&
233 strncmp(attr
, "queue/", 6) == 0 && cxt
->parent
) {
235 /* Exception for "queue/<attr>". These attributes are available
236 * for parental devices only
238 fd
= openat(cxt
->parent
->dir_fd
, attr
, flags
);
243 ssize_t
sysfs_readlink(struct sysfs_cxt
*cxt
, const char *attr
,
244 char *buf
, size_t bufsiz
)
250 return readlinkat(cxt
->dir_fd
, attr
, buf
, bufsiz
);
252 /* read /sys/dev/block/<maj:min> link */
253 return readlink(cxt
->dir_path
, buf
, bufsiz
);
256 DIR *sysfs_opendir(struct sysfs_cxt
*cxt
, const char *attr
)
262 fd
= sysfs_open(cxt
, attr
, O_RDONLY
|O_CLOEXEC
);
264 else if (cxt
->dir_fd
>= 0)
265 /* request to open root of device in sysfs (/sys/block/<dev>)
266 * -- we cannot use cxt->sysfs_fd directly, because closedir()
267 * will close this our persistent file descriptor.
269 fd
= dup_fd_cloexec(cxt
->dir_fd
, STDERR_FILENO
+ 1);
285 static FILE *sysfs_fopen(struct sysfs_cxt
*cxt
, const char *attr
)
287 int fd
= sysfs_open(cxt
, attr
, O_RDONLY
|O_CLOEXEC
);
289 return fd
< 0 ? NULL
: fdopen(fd
, "r" UL_CLOEXECSTR
);
293 static struct dirent
*xreaddir(DIR *dp
)
297 while ((d
= readdir(dp
))) {
298 if (!strcmp(d
->d_name
, ".") ||
299 !strcmp(d
->d_name
, ".."))
302 /* blacklist here? */
308 int sysfs_is_partition_dirent(DIR *dir
, struct dirent
*d
, const char *parent_name
)
312 #ifdef _DIRENT_HAVE_D_TYPE
313 if (d
->d_type
!= DT_DIR
&&
314 d
->d_type
!= DT_LNK
&&
315 d
->d_type
!= DT_UNKNOWN
)
319 const char *p
= parent_name
;
322 /* /dev/sda --> "sda" */
323 if (*parent_name
== '/') {
324 p
= strrchr(parent_name
, '/');
331 if (strlen(d
->d_name
) <= len
)
334 /* partitions subdir name is
335 * "<parent>[:digit:]" or "<parent>p[:digit:]"
337 return strncmp(p
, d
->d_name
, len
) == 0 &&
338 ((*(d
->d_name
+ len
) == 'p' && isdigit(*(d
->d_name
+ len
+ 1)))
339 || isdigit(*(d
->d_name
+ len
)));
342 /* Cannot use /partition file, not supported on old sysfs */
343 snprintf(path
, sizeof(path
), "%s/start", d
->d_name
);
345 return faccessat(dirfd(dir
), path
, R_OK
, 0) == 0;
349 * Converts @partno (partition number) to devno of the partition.
350 * The @cxt handles wholedisk device.
352 * Note that this code does not expect any special format of the
353 * partitions devnames.
355 dev_t
sysfs_partno_to_devno(struct sysfs_cxt
*cxt
, int partno
)
362 dir
= sysfs_opendir(cxt
, NULL
);
366 while ((d
= xreaddir(dir
))) {
369 if (!sysfs_is_partition_dirent(dir
, d
, NULL
))
372 snprintf(path
, sizeof(path
), "%s/partition", d
->d_name
);
373 if (sysfs_read_int(cxt
, path
, &n
))
377 snprintf(path
, sizeof(path
), "%s/dev", d
->d_name
);
378 if (sysfs_scanf(cxt
, path
, "%d:%d", &maj
, &min
) == 2)
379 devno
= makedev(maj
, min
);
389 int sysfs_scanf(struct sysfs_cxt
*cxt
, const char *attr
, const char *fmt
, ...)
391 FILE *f
= sysfs_fopen(cxt
, attr
);
398 rc
= vfscanf(f
, fmt
, ap
);
406 int sysfs_read_s64(struct sysfs_cxt
*cxt
, const char *attr
, int64_t *res
)
410 if (sysfs_scanf(cxt
, attr
, "%"SCNd64
, &x
) == 1) {
418 int sysfs_read_u64(struct sysfs_cxt
*cxt
, const char *attr
, uint64_t *res
)
422 if (sysfs_scanf(cxt
, attr
, "%"SCNu64
, &x
) == 1) {
430 int sysfs_read_int(struct sysfs_cxt
*cxt
, const char *attr
, int *res
)
434 if (sysfs_scanf(cxt
, attr
, "%d", &x
) == 1) {
442 int sysfs_write_string(struct sysfs_cxt
*cxt
, const char *attr
, const char *str
)
444 int fd
= sysfs_open(cxt
, attr
, O_WRONLY
|O_CLOEXEC
);
449 rc
= write_all(fd
, str
, strlen(str
));
457 int sysfs_write_u64(struct sysfs_cxt
*cxt
, const char *attr
, uint64_t num
)
459 char buf
[sizeof(stringify_value(ULLONG_MAX
))];
460 int fd
, rc
= 0, len
, errsv
;
462 fd
= sysfs_open(cxt
, attr
, O_WRONLY
|O_CLOEXEC
);
466 len
= snprintf(buf
, sizeof(buf
), "%" PRIu64
, num
);
467 if (len
< 0 || (size_t) len
>= sizeof(buf
))
468 rc
= len
< 0 ? -errno
: -E2BIG
;
470 rc
= write_all(fd
, buf
, len
);
478 char *sysfs_strdup(struct sysfs_cxt
*cxt
, const char *attr
)
481 return sysfs_scanf(cxt
, attr
, "%1023[^\n]", buf
) == 1 ?
486 int sysfs_count_dirents(struct sysfs_cxt
*cxt
, const char *attr
)
491 if (!(dir
= sysfs_opendir(cxt
, attr
)))
494 while (xreaddir(dir
)) r
++;
500 int sysfs_count_partitions(struct sysfs_cxt
*cxt
, const char *devname
)
506 if (!(dir
= sysfs_opendir(cxt
, NULL
)))
509 while ((d
= xreaddir(dir
))) {
510 if (sysfs_is_partition_dirent(dir
, d
, devname
))
519 * Returns slave name if there is only one slave, otherwise returns NULL.
520 * The result should be deallocated by free().
522 char *sysfs_get_slave(struct sysfs_cxt
*cxt
)
528 if (!(dir
= sysfs_opendir(cxt
, "slaves")))
531 while ((d
= xreaddir(dir
))) {
533 goto err
; /* more slaves */
535 name
= strdup(d
->d_name
);
546 char *sysfs_get_devname(struct sysfs_cxt
*cxt
, char *buf
, size_t bufsiz
)
548 char linkpath
[PATH_MAX
];
552 sz
= sysfs_readlink(cxt
, NULL
, linkpath
, sizeof(linkpath
) - 1);
557 name
= strrchr(linkpath
, '/');
564 if ((size_t) sz
+ 1 > bufsiz
)
567 memcpy(buf
, name
, sz
+ 1);
568 sysfs_devname_sys_to_dev(buf
);
573 #define SUBSYSTEM_LINKNAME "/subsystem"
578 * chain: /sys/dev/block/../../devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/ \
579 * 1-1.2:1.0/host65/target65:0:0/65:0:0:0/block/sdb
581 * The function check if <chain>/subsystem symlink exists, if yes then returns
582 * basename of the readlink result, and remove the last subdirectory from the
585 static char *get_subsystem(char *chain
, char *buf
, size_t bufsz
)
590 if (!chain
|| !*chain
)
594 if (len
+ sizeof(SUBSYSTEM_LINKNAME
) > PATH_MAX
)
600 /* append "/subsystem" to the path */
601 memcpy(chain
+ len
, SUBSYSTEM_LINKNAME
, sizeof(SUBSYSTEM_LINKNAME
));
603 /* try if subsystem symlink exists */
604 sz
= readlink(chain
, buf
, bufsz
- 1);
606 /* remove last subsystem from chain */
608 p
= strrchr(chain
, '/');
615 /* we found symlink to subsystem, return basename */
617 return basename(buf
);
626 * Returns complete path to the device, the patch contains all all sybsystems
627 * used for the device.
629 char *sysfs_get_devchain(struct sysfs_cxt
*cxt
, char *buf
, size_t bufsz
)
631 /* read /sys/dev/block/<maj>:<min> symlink */
632 ssize_t sz
= sysfs_readlink(cxt
, NULL
, buf
, bufsz
);
633 if (sz
<= 0 || sz
+ sizeof(_PATH_SYS_DEVBLOCK
"/") > bufsz
)
638 /* create absolute patch from the link */
639 memmove(buf
+ sizeof(_PATH_SYS_DEVBLOCK
"/") - 1, buf
, sz
);
640 memcpy(buf
, _PATH_SYS_DEVBLOCK
"/", sizeof(_PATH_SYS_DEVBLOCK
"/") - 1);
646 * The @subsys returns the next subsystem in the chain. Function modifies
649 * Returns: 0 in success, <0 on error, 1 on end of chain
651 int sysfs_next_subsystem(struct sysfs_cxt
*cxt
__attribute__((unused
)),
652 char *devchain
, char **subsys
)
654 char subbuf
[PATH_MAX
];
657 if (!subsys
|| !devchain
)
662 while ((sub
= get_subsystem(devchain
, subbuf
, sizeof(subbuf
)))) {
663 *subsys
= strdup(sub
);
673 static int is_hotpluggable_subsystem(const char *name
)
675 static const char * const hotplug_subsystems
[] = {
684 for (i
= 0; i
< ARRAY_SIZE(hotplug_subsystems
); i
++)
685 if (strcmp(name
, hotplug_subsystems
[i
]) == 0)
691 int sysfs_is_hotpluggable(struct sysfs_cxt
*cxt
)
693 char buf
[PATH_MAX
], *chain
, *sub
;
697 /* check /sys/dev/block/<maj>:<min>/removable attribute */
698 if (sysfs_read_int(cxt
, "removable", &rc
) == 0 && rc
== 1)
701 chain
= sysfs_get_devchain(cxt
, buf
, sizeof(buf
));
703 while (chain
&& sysfs_next_subsystem(cxt
, chain
, &sub
) == 0) {
704 rc
= is_hotpluggable_subsystem(sub
);
715 static int get_dm_wholedisk(struct sysfs_cxt
*cxt
, char *diskname
,
716 size_t len
, dev_t
*diskdevno
)
721 /* Note, sysfs_get_slave() returns the first slave only,
722 * if there is more slaves, then return NULL
724 name
= sysfs_get_slave(cxt
);
728 if (diskname
&& len
) {
729 strncpy(diskname
, name
, len
);
730 diskname
[len
- 1] = '\0';
734 *diskdevno
= sysfs_devname_to_devno(name
, NULL
);
744 * Returns by @diskdevno whole disk device devno and (optionaly) by
745 * @diskname the whole disk device name.
747 int sysfs_devno_to_wholedisk(dev_t dev
, char *diskname
,
748 size_t len
, dev_t
*diskdevno
)
750 struct sysfs_cxt cxt
;
753 if (!dev
|| sysfs_init(&cxt
, dev
, NULL
) != 0)
756 is_part
= sysfs_has_attribute(&cxt
, "partition");
759 * Extra case for partitions mapped by device-mapper.
761 * All regualar partitions (added by BLKPG ioctl or kernel PT
762 * parser) have the /sys/.../partition file. The partitions
763 * mapped by DM don't have such file, but they have "part"
766 char *uuid
= sysfs_strdup(&cxt
, "dm/uuid");
768 char *prefix
= uuid
? strsep(&tmp
, "-") : NULL
;
770 if (prefix
&& strncasecmp(prefix
, "part", 4) == 0)
775 get_dm_wholedisk(&cxt
, diskname
, len
, diskdevno
) == 0)
777 * partitioned device, mapped by DM
786 * unpartitioned device
788 if (diskname
&& len
) {
789 if (!sysfs_get_devname(&cxt
, diskname
, len
))
798 * - readlink /sys/dev/block/8:1 = ../../block/sda/sda1
799 * - dirname ../../block/sda/sda1 = ../../block/sda
800 * - basename ../../block/sda = sda
802 char linkpath
[PATH_MAX
];
806 linklen
= sysfs_readlink(&cxt
, NULL
, linkpath
, sizeof(linkpath
) - 1);
809 linkpath
[linklen
] = '\0';
811 stripoff_last_component(linkpath
); /* dirname */
812 name
= stripoff_last_component(linkpath
); /* basename */
816 sysfs_devname_sys_to_dev(name
);
817 if (diskname
&& len
) {
818 strncpy(diskname
, name
, len
);
819 diskname
[len
- 1] = '\0';
823 *diskdevno
= sysfs_devname_to_devno(name
, NULL
);
838 * Returns 1 if the device is private LVM device.
840 int sysfs_devno_is_lvm_private(dev_t devno
)
842 struct sysfs_cxt cxt
= UL_SYSFSCXT_EMPTY
;
846 if (sysfs_init(&cxt
, devno
, NULL
) != 0)
849 uuid
= sysfs_strdup(&cxt
, "dm/uuid");
851 /* Private LVM devices use "LVM-<uuid>-<name>" uuid format (important
852 * is the "LVM" prefix and "-<name>" postfix).
854 if (uuid
&& strncmp(uuid
, "LVM-", 4) == 0) {
855 char *p
= strrchr(uuid
+ 4, '-');
867 * Return 0 or 1, or < 0 in case of error
869 int sysfs_devno_is_wholedisk(dev_t devno
)
873 if (sysfs_devno_to_wholedisk(devno
, NULL
, 0, &disk
) != 0)
876 return devno
== disk
;
880 int sysfs_scsi_get_hctl(struct sysfs_cxt
*cxt
, int *h
, int *c
, int *t
, int *l
)
882 char buf
[PATH_MAX
], *hctl
;
890 len
= sysfs_readlink(cxt
, "device", buf
, sizeof(buf
) - 1);
895 hctl
= strrchr(buf
, '/');
900 if (sscanf(hctl
, "%u:%u:%u:%u", &cxt
->scsi_host
, &cxt
->scsi_channel
,
901 &cxt
->scsi_target
, &cxt
->scsi_lun
) != 4)
909 *c
= cxt
->scsi_channel
;
911 *t
= cxt
->scsi_target
;
918 static char *sysfs_scsi_host_attribute_path(struct sysfs_cxt
*cxt
,
919 const char *type
, char *buf
, size_t bufsz
, const char *attr
)
924 if (sysfs_scsi_get_hctl(cxt
, &host
, NULL
, NULL
, NULL
))
928 len
= snprintf(buf
, bufsz
, _PATH_SYS_CLASS
"/%s_host/host%d/%s",
931 len
= snprintf(buf
, bufsz
, _PATH_SYS_CLASS
"/%s_host/host%d",
934 return (len
< 0 || (size_t) len
>= bufsz
) ? NULL
: buf
;
937 char *sysfs_scsi_host_strdup_attribute(struct sysfs_cxt
*cxt
,
938 const char *type
, const char *attr
)
944 if (!attr
|| !type
||
945 !sysfs_scsi_host_attribute_path(cxt
, type
, buf
, sizeof(buf
), attr
))
948 if (!(f
= fopen(buf
, "r" UL_CLOEXECSTR
)))
951 rc
= fscanf(f
, "%1023[^\n]", buf
);
954 return rc
== 1 ? strdup(buf
) : NULL
;
957 int sysfs_scsi_host_is(struct sysfs_cxt
*cxt
, const char *type
)
962 if (!type
|| !sysfs_scsi_host_attribute_path(cxt
, type
,
963 buf
, sizeof(buf
), NULL
))
966 return stat(buf
, &st
) == 0 && S_ISDIR(st
.st_mode
);
969 static char *sysfs_scsi_attribute_path(struct sysfs_cxt
*cxt
,
970 char *buf
, size_t bufsz
, const char *attr
)
974 if (sysfs_scsi_get_hctl(cxt
, &h
, &c
, &t
, &l
) != 0)
978 len
= snprintf(buf
, bufsz
, _PATH_SYS_SCSI
"/devices/%d:%d:%d:%d/%s",
981 len
= snprintf(buf
, bufsz
, _PATH_SYS_SCSI
"/devices/%d:%d:%d:%d",
983 return (len
< 0 || (size_t) len
>= bufsz
) ? NULL
: buf
;
986 int sysfs_scsi_has_attribute(struct sysfs_cxt
*cxt
, const char *attr
)
991 if (!sysfs_scsi_attribute_path(cxt
, path
, sizeof(path
), attr
))
994 return stat(path
, &st
) == 0;
997 int sysfs_scsi_path_contains(struct sysfs_cxt
*cxt
, const char *pattern
)
999 char path
[PATH_MAX
], linkc
[PATH_MAX
];
1003 if (!sysfs_scsi_attribute_path(cxt
, path
, sizeof(path
), NULL
))
1006 if (stat(path
, &st
) != 0)
1009 len
= readlink(path
, linkc
, sizeof(linkc
) - 1);
1014 return strstr(linkc
, pattern
) != NULL
;
1017 #ifdef TEST_PROGRAM_SYSFS
1022 int main(int argc
, char *argv
[])
1024 struct sysfs_cxt cxt
= UL_SYSFSCXT_EMPTY
;
1026 dev_t devno
, disk_devno
;
1027 char path
[PATH_MAX
], *sub
, *chain
;
1034 errx(EXIT_FAILURE
, "usage: %s <devname>", argv
[0]);
1037 devno
= sysfs_devname_to_devno(devname
, NULL
);
1040 err(EXIT_FAILURE
, "failed to read devno");
1042 if (sysfs_init(&cxt
, devno
, NULL
))
1043 return EXIT_FAILURE
;
1045 printf("NAME: %s\n", devname
);
1046 printf("DEVNAME: %s\n", sysfs_get_devname(&cxt
, path
, sizeof(path
)));
1047 printf("DEVPATH: %s\n", sysfs_devno_to_devpath(devno
, path
, sizeof(path
)));
1048 printf("DEVNO: %u (%d:%d)\n", (unsigned int) devno
, major(devno
), minor(devno
));
1049 printf("DEVNO-PATH: %s\n", sysfs_devno_path(devno
, path
, sizeof(path
)));
1051 sysfs_devno_to_wholedisk(devno
, diskname
, sizeof(diskname
), &disk_devno
);
1052 printf("WHOLEDISK-DEVNO: %u (%d:%d)\n", (unsigned int) disk_devno
, major(disk_devno
), minor(disk_devno
));
1053 printf("WHOLEDISK-DEVNAME: %s\n", diskname
);
1055 is_part
= sysfs_devno_has_attribute(devno
, "partition");
1056 printf("PARTITION: %s\n", is_part
? "YES" : "NOT");
1058 printf("HOTPLUG: %s\n", sysfs_is_hotpluggable(&cxt
) ? "yes" : "no");
1059 printf("SLAVES: %d\n", sysfs_count_dirents(&cxt
, "slaves"));
1061 len
= sysfs_readlink(&cxt
, NULL
, path
, sizeof(path
) - 1);
1064 printf("DEVNOLINK: %s\n", path
);
1068 printf("First 5 partitions:\n");
1069 for (i
= 1; i
<= 5; i
++) {
1070 dev_t dev
= sysfs_partno_to_devno(&cxt
, i
);
1072 printf("\t#%d %d:%d\n", i
, major(dev
), minor(dev
));
1076 if (sysfs_read_u64(&cxt
, "size", &u64
))
1077 printf("read SIZE failed\n");
1079 printf("SIZE: %jd\n", u64
);
1081 if (sysfs_read_int(&cxt
, "queue/hw_sector_size", &i
))
1082 printf("read SECTOR failed\n");
1084 printf("SECTOR: %d\n", i
);
1087 chain
= sysfs_get_devchain(&cxt
, path
, sizeof(path
));
1088 printf("SUBSUSTEMS:\n");
1090 while (chain
&& sysfs_next_subsystem(&cxt
, chain
, &sub
) == 0) {
1091 printf("\t%s\n", sub
);
1097 return EXIT_SUCCESS
;