]>
git.ipfire.org Git - thirdparty/util-linux.git/blob - lib/sysfs.c
2 * Copyright (C) 2011 Karel Zak <kzak@redhat.com>
10 char *sysfs_devno_attribute_path(dev_t devno
, char *buf
,
11 size_t bufsiz
, const char *attr
)
16 len
= snprintf(buf
, bufsiz
, _PATH_SYS_DEVBLOCK
"/%d:%d/%s",
17 major(devno
), minor(devno
), attr
);
19 len
= snprintf(buf
, bufsiz
, _PATH_SYS_DEVBLOCK
"/%d:%d",
20 major(devno
), minor(devno
));
22 return (len
< 0 || (size_t) len
+ 1 > bufsiz
) ? NULL
: buf
;
25 int sysfs_devno_has_attribute(dev_t devno
, const char *attr
)
30 if (!sysfs_devno_attribute_path(devno
, path
, sizeof(path
), attr
))
32 if (stat(path
, &info
) == 0)
37 char *sysfs_devno_path(dev_t devno
, char *buf
, size_t bufsiz
)
39 return sysfs_devno_attribute_path(devno
, buf
, bufsiz
, NULL
);
42 dev_t
sysfs_devname_to_devno(const char *name
, const char *parent
)
44 char buf
[PATH_MAX
], *path
= NULL
;
47 if (strncmp("/dev/", name
, 5) == 0) {
53 if (stat(name
, &st
) == 0)
56 name
+= 5; /* unaccesible, or not node in /dev */
61 * Create path to /sys/block/<parent>/<name>/dev
63 int len
= snprintf(buf
, sizeof(buf
),
64 _PATH_SYS_BLOCK
"/%s/%s/dev", parent
, name
);
65 if (len
< 0 || (size_t) len
+ 1 > sizeof(buf
))
71 * Create path to /sys/block/<name>/dev
73 int len
= snprintf(buf
, sizeof(buf
),
74 _PATH_SYS_BLOCK
"/%s/dev", name
);
75 if (len
< 0 || (size_t) len
+ 1 > sizeof(buf
))
82 * read devno from sysfs
91 if (fscanf(f
, "%u:%u", &maj
, &min
) == 2)
92 dev
= makedev(maj
, min
);
99 * Returns devname (e.g. "/dev/sda1") for the given devno.
101 * Note that the @buf has to be large enough to store /sys/dev/block/<maj:min>
104 * Please, use more robust blkid_devno_to_devname() in your applications.
106 char *sysfs_devno_to_devpath(dev_t devno
, char *buf
, size_t bufsiz
)
108 struct sysfs_cxt cxt
;
113 if (sysfs_init(&cxt
, devno
, NULL
))
116 name
= sysfs_get_devname(&cxt
, buf
, bufsiz
);
124 if (sz
+ sizeof("/dev/") > bufsiz
)
127 /* create the final "/dev/<name>" string */
128 memmove(buf
+ 5, name
, sz
+ 1);
129 memcpy(buf
, "/dev/", 5);
131 if (!stat(buf
, &st
) && S_ISBLK(st
.st_mode
) && st
.st_rdev
== devno
)
137 int sysfs_init(struct sysfs_cxt
*cxt
, dev_t devno
, struct sysfs_cxt
*parent
)
142 memset(cxt
, 0, sizeof(*cxt
));
145 if (!sysfs_devno_path(devno
, path
, sizeof(path
)))
148 fd
= open(path
, O_RDONLY
);
153 cxt
->dir_path
= strdup(path
);
157 cxt
->parent
= parent
;
165 void sysfs_deinit(struct sysfs_cxt
*cxt
)
170 if (cxt
->dir_fd
>= 0)
177 cxt
->dir_path
= NULL
;
180 int sysfs_stat(struct sysfs_cxt
*cxt
, const char *attr
, struct stat
*st
)
182 int rc
= fstat_at(cxt
->dir_fd
, cxt
->dir_path
, attr
, st
, 0);
184 if (rc
!= 0 && errno
== ENOENT
&&
185 strncmp(attr
, "queue/", 6) == 0 && cxt
->parent
) {
187 /* Exception for "queue/<attr>". These attributes are available
188 * for parental devices only
190 return fstat_at(cxt
->parent
->dir_fd
,
191 cxt
->parent
->dir_path
, attr
, st
, 0);
196 int sysfs_has_attribute(struct sysfs_cxt
*cxt
, const char *attr
)
200 return sysfs_stat(cxt
, attr
, &st
) == 0;
203 static int sysfs_open(struct sysfs_cxt
*cxt
, const char *attr
)
205 int fd
= open_at(cxt
->dir_fd
, cxt
->dir_path
, attr
, O_RDONLY
);
207 if (fd
== -1 && errno
== ENOENT
&&
208 strncmp(attr
, "queue/", 6) == 0 && cxt
->parent
) {
210 /* Exception for "queue/<attr>". These attributes are available
211 * for parental devices only
213 fd
= open_at(cxt
->parent
->dir_fd
, cxt
->dir_path
, attr
, O_RDONLY
);
218 ssize_t
sysfs_readlink(struct sysfs_cxt
*cxt
, const char *attr
,
219 char *buf
, size_t bufsiz
)
222 return readlink_at(cxt
->dir_fd
, cxt
->dir_path
, attr
, buf
, bufsiz
);
224 /* read /sys/dev/block/<maj:min> link */
225 return readlink(cxt
->dir_path
, buf
, bufsiz
);
228 DIR *sysfs_opendir(struct sysfs_cxt
*cxt
, const char *attr
)
234 fd
= sysfs_open(cxt
, attr
);
236 /* request to open root of device in sysfs (/sys/block/<dev>)
237 * -- we cannot use cxt->sysfs_fd directly, because closedir()
238 * will close this our persistent file descriptor.
240 fd
= dup(cxt
->dir_fd
);
256 static FILE *sysfs_fopen(struct sysfs_cxt
*cxt
, const char *attr
)
258 int fd
= sysfs_open(cxt
, attr
);
260 return fd
< 0 ? NULL
: fdopen(fd
, "r");
264 static struct dirent
*xreaddir(DIR *dp
)
268 while ((d
= readdir(dp
))) {
269 if (!strcmp(d
->d_name
, ".") ||
270 !strcmp(d
->d_name
, ".."))
273 /* blacklist here? */
279 int sysfs_is_partition_dirent(DIR *dir
, struct dirent
*d
, const char *parent_name
)
283 #ifdef _DIRENT_HAVE_D_TYPE
284 if (d
->d_type
!= DT_DIR
)
287 if (strncmp(parent_name
, d
->d_name
, strlen(parent_name
)))
290 /* Cannot use /partition file, not supported on old sysfs */
291 snprintf(path
, sizeof(path
), "%s/start", d
->d_name
);
293 return faccessat(dirfd(dir
), path
, R_OK
, 0) == 0;
296 int sysfs_scanf(struct sysfs_cxt
*cxt
, const char *attr
, const char *fmt
, ...)
298 FILE *f
= sysfs_fopen(cxt
, attr
);
305 rc
= vfscanf(f
, fmt
, ap
);
313 int sysfs_read_s64(struct sysfs_cxt
*cxt
, const char *attr
, int64_t *res
)
317 if (sysfs_scanf(cxt
, attr
, "%"SCNd64
, &x
) == 1) {
325 int sysfs_read_u64(struct sysfs_cxt
*cxt
, const char *attr
, uint64_t *res
)
329 if (sysfs_scanf(cxt
, attr
, "%"SCNu64
, &x
) == 1) {
337 int sysfs_read_int(struct sysfs_cxt
*cxt
, const char *attr
, int *res
)
341 if (sysfs_scanf(cxt
, attr
, "%d", &x
) == 1) {
349 char *sysfs_strdup(struct sysfs_cxt
*cxt
, const char *attr
)
352 return sysfs_scanf(cxt
, attr
, "%1024[^\n]", buf
) == 1 ?
356 int sysfs_count_dirents(struct sysfs_cxt
*cxt
, const char *attr
)
361 if (!(dir
= sysfs_opendir(cxt
, attr
)))
364 while (xreaddir(dir
)) r
++;
370 int sysfs_count_partitions(struct sysfs_cxt
*cxt
, const char *devname
)
376 if (!(dir
= sysfs_opendir(cxt
, NULL
)))
379 while ((d
= xreaddir(dir
))) {
380 if (sysfs_is_partition_dirent(dir
, d
, devname
))
389 * Returns slave name if there is only one slave, otherwise returns NULL.
390 * The result should be deallocated by free().
392 char *sysfs_get_slave(struct sysfs_cxt
*cxt
)
398 if (!(dir
= sysfs_opendir(cxt
, "slaves")))
401 while ((d
= xreaddir(dir
))) {
403 goto err
; /* more slaves */
405 name
= strdup(d
->d_name
);
417 * Note that the @buf has to be large enough to store /sys/dev/block/<maj:min>
420 char *sysfs_get_devname(struct sysfs_cxt
*cxt
, char *buf
, size_t bufsiz
)
425 sz
= sysfs_readlink(cxt
, NULL
, buf
, bufsiz
- 1);
430 name
= strrchr(buf
, '/');
437 memmove(buf
, name
, sz
+ 1);
441 #ifdef TEST_PROGRAM_SYSFS
446 int main(int argc
, char *argv
[])
448 struct sysfs_cxt cxt
;
457 errx(EXIT_FAILURE
, "usage: %s <devname>", argv
[0]);
460 devno
= sysfs_devname_to_devno(devname
, NULL
);
463 err(EXIT_FAILURE
, "failed to read devno");
465 printf("NAME: %s\n", devname
);
466 printf("DEVNO: %u\n", (unsigned int) devno
);
467 printf("DEVNOPATH: %s\n", sysfs_devno_path(devno
, path
, sizeof(path
)));
468 printf("DEVPATH: %s\n", sysfs_devno_to_devpath(devno
, path
, sizeof(path
)));
469 printf("PARTITION: %s\n",
470 sysfs_devno_has_attribute(devno
, "partition") ? "YES" : "NOT");
472 sysfs_init(&cxt
, devno
, NULL
);
474 len
= sysfs_readlink(&cxt
, NULL
, path
, sizeof(path
) - 1);
477 printf("DEVNOLINK: %s\n", path
);
480 printf("SLAVES: %d\n", sysfs_count_dirents(&cxt
, "slaves"));
482 if (sysfs_read_u64(&cxt
, "size", &u64
))
483 printf("read SIZE failed\n");
485 printf("SIZE: %jd\n", u64
);
487 if (sysfs_read_int(&cxt
, "queue/hw_sector_size", &i
))
488 printf("read SECTOR failed\n");
490 printf("SECTOR: %d\n", i
);
492 printf("DEVNAME: %s\n", sysfs_get_devname(&cxt
, path
, sizeof(path
)));