]>
git.ipfire.org Git - thirdparty/util-linux.git/blob - lib/sysfs.c
2 * No copyright is claimed. This code is in the public domain; do with
5 * Written by Karel Zak <kzak@redhat.com>
11 #include "pathnames.h"
14 char *sysfs_devno_attribute_path(dev_t devno
, char *buf
,
15 size_t bufsiz
, const char *attr
)
20 len
= snprintf(buf
, bufsiz
, _PATH_SYS_DEVBLOCK
"/%d:%d/%s",
21 major(devno
), minor(devno
), attr
);
23 len
= snprintf(buf
, bufsiz
, _PATH_SYS_DEVBLOCK
"/%d:%d",
24 major(devno
), minor(devno
));
26 return (len
< 0 || (size_t) len
+ 1 > bufsiz
) ? NULL
: buf
;
29 int sysfs_devno_has_attribute(dev_t devno
, const char *attr
)
34 if (!sysfs_devno_attribute_path(devno
, path
, sizeof(path
), attr
))
36 if (stat(path
, &info
) == 0)
41 char *sysfs_devno_path(dev_t devno
, char *buf
, size_t bufsiz
)
43 return sysfs_devno_attribute_path(devno
, buf
, bufsiz
, NULL
);
46 dev_t
sysfs_devname_to_devno(const char *name
, const char *parent
)
48 char buf
[PATH_MAX
], *path
= NULL
;
51 if (strncmp("/dev/", name
, 5) == 0) {
57 if (stat(name
, &st
) == 0)
60 name
+= 5; /* unaccesible, or not node in /dev */
63 if (!dev
&& parent
&& strncmp("dm-", name
, 3)) {
65 * Create path to /sys/block/<parent>/<name>/dev
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
))
75 * Create path to /sys/block/<name>/dev
77 int len
= snprintf(buf
, sizeof(buf
),
78 _PATH_SYS_BLOCK
"/%s/dev", name
);
79 if (len
< 0 || (size_t) len
+ 1 > sizeof(buf
))
86 * read devno from sysfs
91 f
= fopen(path
, "r" UL_CLOEXECSTR
);
95 if (fscanf(f
, "%d:%d", &maj
, &min
) == 2)
96 dev
= makedev(maj
, min
);
103 * Returns devname (e.g. "/dev/sda1") for the given devno.
105 * Note that the @buf has to be large enough to store /sys/dev/block/<maj:min>
108 * Please, use more robust blkid_devno_to_devname() in your applications.
110 char *sysfs_devno_to_devpath(dev_t devno
, char *buf
, size_t bufsiz
)
112 struct sysfs_cxt cxt
;
117 if (sysfs_init(&cxt
, devno
, NULL
))
120 name
= sysfs_get_devname(&cxt
, buf
, bufsiz
);
128 if (sz
+ sizeof("/dev/") > bufsiz
)
131 /* create the final "/dev/<name>" string */
132 memmove(buf
+ 5, name
, sz
+ 1);
133 memcpy(buf
, "/dev/", 5);
135 if (!stat(buf
, &st
) && S_ISBLK(st
.st_mode
) && st
.st_rdev
== devno
)
141 int sysfs_init(struct sysfs_cxt
*cxt
, dev_t devno
, struct sysfs_cxt
*parent
)
146 memset(cxt
, 0, sizeof(*cxt
));
149 if (!sysfs_devno_path(devno
, path
, sizeof(path
)))
152 fd
= open(path
, O_RDONLY
|O_CLOEXEC
);
157 cxt
->dir_path
= strdup(path
);
161 cxt
->parent
= parent
;
164 rc
= errno
> 0 ? -errno
: -1;
169 void sysfs_deinit(struct sysfs_cxt
*cxt
)
174 if (cxt
->dir_fd
>= 0)
178 memset(cxt
, 0, sizeof(*cxt
));
183 int sysfs_stat(struct sysfs_cxt
*cxt
, const char *attr
, struct stat
*st
)
185 int rc
= fstat_at(cxt
->dir_fd
, cxt
->dir_path
, attr
, st
, 0);
187 if (rc
!= 0 && errno
== ENOENT
&&
188 strncmp(attr
, "queue/", 6) == 0 && cxt
->parent
) {
190 /* Exception for "queue/<attr>". These attributes are available
191 * for parental devices only
193 return fstat_at(cxt
->parent
->dir_fd
,
194 cxt
->parent
->dir_path
, attr
, st
, 0);
199 int sysfs_has_attribute(struct sysfs_cxt
*cxt
, const char *attr
)
203 return sysfs_stat(cxt
, attr
, &st
) == 0;
206 static int sysfs_open(struct sysfs_cxt
*cxt
, const char *attr
)
208 int fd
= open_at(cxt
->dir_fd
, cxt
->dir_path
, attr
, O_RDONLY
|O_CLOEXEC
);
210 if (fd
== -1 && 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 fd
= open_at(cxt
->parent
->dir_fd
, cxt
->dir_path
, attr
,
222 ssize_t
sysfs_readlink(struct sysfs_cxt
*cxt
, const char *attr
,
223 char *buf
, size_t bufsiz
)
229 return readlink_at(cxt
->dir_fd
, cxt
->dir_path
, attr
, buf
, bufsiz
);
231 /* read /sys/dev/block/<maj:min> link */
232 return readlink(cxt
->dir_path
, buf
, bufsiz
);
235 DIR *sysfs_opendir(struct sysfs_cxt
*cxt
, const char *attr
)
241 fd
= sysfs_open(cxt
, attr
);
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.
248 fd
= dup(cxt
->dir_fd
);
264 static FILE *sysfs_fopen(struct sysfs_cxt
*cxt
, const char *attr
)
266 int fd
= sysfs_open(cxt
, attr
);
268 return fd
< 0 ? NULL
: fdopen(fd
, "r" UL_CLOEXECSTR
);
272 static struct dirent
*xreaddir(DIR *dp
)
276 while ((d
= readdir(dp
))) {
277 if (!strcmp(d
->d_name
, ".") ||
278 !strcmp(d
->d_name
, ".."))
281 /* blacklist here? */
287 int sysfs_is_partition_dirent(DIR *dir
, struct dirent
*d
, const char *parent_name
)
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
)
298 const char *p
= parent_name
;
301 /* /dev/sda --> "sda" */
302 if (*parent_name
== '/') {
303 p
= strrchr(parent_name
, '/');
310 if (strlen(d
->d_name
) <= len
)
313 /* partitions subdir name is
314 * "<parent>[:digit:]" or "<parent>p[:digit:]"
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
)));
321 /* Cannot use /partition file, not supported on old sysfs */
322 snprintf(path
, sizeof(path
), "%s/start", d
->d_name
);
324 return faccessat(dirfd(dir
), path
, R_OK
, 0) == 0;
328 * Converts @partno (partition number) to devno of the partition.
329 * The @cxt handles wholedisk device.
331 * Note that this code does not expect any special format of the
332 * partitions devnames.
334 dev_t
sysfs_partno_to_devno(struct sysfs_cxt
*cxt
, int partno
)
341 dir
= sysfs_opendir(cxt
, NULL
);
345 while ((d
= xreaddir(dir
))) {
348 if (!sysfs_is_partition_dirent(dir
, d
, NULL
))
351 snprintf(path
, sizeof(path
), "%s/partition", d
->d_name
);
352 if (sysfs_read_int(cxt
, path
, &n
))
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
);
368 int sysfs_scanf(struct sysfs_cxt
*cxt
, const char *attr
, const char *fmt
, ...)
370 FILE *f
= sysfs_fopen(cxt
, attr
);
377 rc
= vfscanf(f
, fmt
, ap
);
385 int sysfs_read_s64(struct sysfs_cxt
*cxt
, const char *attr
, int64_t *res
)
389 if (sysfs_scanf(cxt
, attr
, "%"SCNd64
, &x
) == 1) {
397 int sysfs_read_u64(struct sysfs_cxt
*cxt
, const char *attr
, uint64_t *res
)
401 if (sysfs_scanf(cxt
, attr
, "%"SCNu64
, &x
) == 1) {
409 int sysfs_read_int(struct sysfs_cxt
*cxt
, const char *attr
, int *res
)
413 if (sysfs_scanf(cxt
, attr
, "%d", &x
) == 1) {
421 char *sysfs_strdup(struct sysfs_cxt
*cxt
, const char *attr
)
424 return sysfs_scanf(cxt
, attr
, "%1023[^\n]", buf
) == 1 ?
428 int sysfs_count_dirents(struct sysfs_cxt
*cxt
, const char *attr
)
433 if (!(dir
= sysfs_opendir(cxt
, attr
)))
436 while (xreaddir(dir
)) r
++;
442 int sysfs_count_partitions(struct sysfs_cxt
*cxt
, const char *devname
)
448 if (!(dir
= sysfs_opendir(cxt
, NULL
)))
451 while ((d
= xreaddir(dir
))) {
452 if (sysfs_is_partition_dirent(dir
, d
, devname
))
461 * Returns slave name if there is only one slave, otherwise returns NULL.
462 * The result should be deallocated by free().
464 char *sysfs_get_slave(struct sysfs_cxt
*cxt
)
470 if (!(dir
= sysfs_opendir(cxt
, "slaves")))
473 while ((d
= xreaddir(dir
))) {
475 goto err
; /* more slaves */
477 name
= strdup(d
->d_name
);
489 * Note that the @buf has to be large enough to store /sys/dev/block/<maj:min>
492 char *sysfs_get_devname(struct sysfs_cxt
*cxt
, char *buf
, size_t bufsiz
)
497 sz
= sysfs_readlink(cxt
, NULL
, buf
, bufsiz
- 1);
502 name
= strrchr(buf
, '/');
509 memmove(buf
, name
, sz
+ 1);
513 /* returns basename and keeps dirname in the @path */
514 static char *stripoff_last_component(char *path
)
516 char *p
= strrchr(path
, '/');
524 static int get_dm_wholedisk(struct sysfs_cxt
*cxt
, char *diskname
,
525 size_t len
, dev_t
*diskdevno
)
530 /* Note, sysfs_get_slave() returns the first slave only,
531 * if there is more slaves, then return NULL
533 name
= sysfs_get_slave(cxt
);
537 if (diskname
&& len
) {
538 strncpy(diskname
, name
, len
);
539 diskname
[len
- 1] = '\0';
543 *diskdevno
= sysfs_devname_to_devno(name
, NULL
);
552 int sysfs_devno_to_wholedisk(dev_t dev
, char *diskname
,
553 size_t len
, dev_t
*diskdevno
)
555 struct sysfs_cxt cxt
;
558 if (!dev
|| sysfs_init(&cxt
, dev
, NULL
) != 0)
561 is_part
= sysfs_has_attribute(&cxt
, "partition");
564 * Extra case for partitions mapped by device-mapper.
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"
571 char *uuid
= sysfs_strdup(&cxt
, "dm/uuid");
573 char *prefix
= uuid
? strsep(&tmp
, "-") : NULL
;
575 if (prefix
&& strncasecmp(prefix
, "part", 4) == 0)
580 get_dm_wholedisk(&cxt
, diskname
, len
, diskdevno
) == 0)
582 * partitioned device, mapped by DM
591 * unpartitioned device
593 if (diskname
&& len
) {
594 if (!sysfs_get_devname(&cxt
, diskname
, len
))
603 * - readlink /sys/dev/block/8:1 = ../../block/sda/sda1
604 * - dirname ../../block/sda/sda1 = ../../block/sda
605 * - basename ../../block/sda = sda
607 char linkpath
[PATH_MAX
];
611 linklen
= sysfs_readlink(&cxt
, NULL
,
612 linkpath
, sizeof(linkpath
) - 1);
615 linkpath
[linklen
] = '\0';
617 stripoff_last_component(linkpath
); /* dirname */
618 name
= stripoff_last_component(linkpath
); /* basename */
622 if (diskname
&& len
) {
623 strncpy(diskname
, name
, len
);
624 diskname
[len
- 1] = '\0';
628 *diskdevno
= sysfs_devname_to_devno(name
, NULL
);
643 int sysfs_scsi_get_hctl(struct sysfs_cxt
*cxt
, int *h
, int *c
, int *t
, int *l
)
645 char buf
[PATH_MAX
], *hctl
;
653 len
= sysfs_readlink(cxt
, "device", buf
, sizeof(buf
) - 1);
658 hctl
= strrchr(buf
, '/');
663 if (sscanf(hctl
, "%u:%u:%u:%u", &cxt
->scsi_host
, &cxt
->scsi_channel
,
664 &cxt
->scsi_target
, &cxt
->scsi_lun
) != 4)
672 *c
= cxt
->scsi_channel
;
674 *t
= cxt
->scsi_target
;
681 static char *sysfs_scsi_host_attribute_path(struct sysfs_cxt
*cxt
,
682 const char *type
, char *buf
, size_t bufsz
, const char *attr
)
687 if (sysfs_scsi_get_hctl(cxt
, &host
, NULL
, NULL
, NULL
))
691 len
= snprintf(buf
, bufsz
, _PATH_SYS_CLASS
"/%s_host/host%d/%s",
694 len
= snprintf(buf
, bufsz
, _PATH_SYS_CLASS
"/%s_host/host%d",
697 return (len
< 0 || (size_t) len
+ 1 > bufsz
) ? NULL
: buf
;
700 char *sysfs_scsi_host_strdup_attribute(struct sysfs_cxt
*cxt
,
701 const char *type
, const char *attr
)
707 if (!attr
|| !type
||
708 !sysfs_scsi_host_attribute_path(cxt
, type
, buf
, sizeof(buf
), attr
))
711 if (!(f
= fopen(buf
, "r" UL_CLOEXECSTR
)))
714 rc
= fscanf(f
, "%1023[^\n]", buf
);
717 return rc
== 1 ? strdup(buf
) : NULL
;
720 int sysfs_scsi_host_is(struct sysfs_cxt
*cxt
, const char *type
)
725 if (!type
|| !sysfs_scsi_host_attribute_path(cxt
, type
,
726 buf
, sizeof(buf
), NULL
))
729 return stat(buf
, &st
) == 0 && S_ISDIR(st
.st_mode
);
732 static char *sysfs_scsi_attribute_path(struct sysfs_cxt
*cxt
,
733 char *buf
, size_t bufsz
, const char *attr
)
737 if (sysfs_scsi_get_hctl(cxt
, &h
, &c
, &t
, &l
) != 0)
741 len
= snprintf(buf
, bufsz
, _PATH_SYS_SCSI
"/devices/%d:%d:%d:%d/%s",
744 len
= snprintf(buf
, bufsz
, _PATH_SYS_SCSI
"/devices/%d:%d:%d:%d",
746 return (len
< 0 || (size_t) len
+ 1 > bufsz
) ? NULL
: buf
;
749 int sysfs_scsi_has_attribute(struct sysfs_cxt
*cxt
, const char *attr
)
754 if (!sysfs_scsi_attribute_path(cxt
, path
, sizeof(path
), attr
))
757 return stat(path
, &st
) == 0;
760 int sysfs_scsi_path_contains(struct sysfs_cxt
*cxt
, const char *pattern
)
762 char path
[PATH_MAX
], linkc
[PATH_MAX
];
766 if (!sysfs_scsi_attribute_path(cxt
, path
, sizeof(path
), NULL
))
769 if (stat(path
, &st
) != 0)
772 len
= readlink(path
, linkc
, sizeof(linkc
) - 1);
777 return strstr(linkc
, pattern
) != NULL
;
780 #ifdef TEST_PROGRAM_SYSFS
785 int main(int argc
, char *argv
[])
787 struct sysfs_cxt cxt
= UL_SYSFSCXT_EMPTY
;
796 errx(EXIT_FAILURE
, "usage: %s <devname>", argv
[0]);
799 devno
= sysfs_devname_to_devno(devname
, NULL
);
802 err(EXIT_FAILURE
, "failed to read devno");
804 is_part
= sysfs_devno_has_attribute(devno
, "partition");
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");
812 if (sysfs_init(&cxt
, devno
, NULL
))
815 len
= sysfs_readlink(&cxt
, NULL
, path
, sizeof(path
) - 1);
818 printf("DEVNOLINK: %s\n", path
);
822 printf("First 5 partitions:\n");
823 for (i
= 1; i
<= 5; i
++) {
824 dev_t dev
= sysfs_partno_to_devno(&cxt
, i
);
826 printf("\t#%d %d:%d\n", i
, major(dev
), minor(dev
));
830 printf("SLAVES: %d\n", sysfs_count_dirents(&cxt
, "slaves"));
832 if (sysfs_read_u64(&cxt
, "size", &u64
))
833 printf("read SIZE failed\n");
835 printf("SIZE: %jd\n", u64
);
837 if (sysfs_read_int(&cxt
, "queue/hw_sector_size", &i
))
838 printf("read SECTOR failed\n");
840 printf("SECTOR: %d\n", i
);
842 printf("DEVNAME: %s\n", sysfs_get_devname(&cxt
, path
, sizeof(path
)));