diff options
author | Karel Zak | 2011-05-16 14:08:50 +0200 |
---|---|---|
committer | Karel Zak | 2011-05-16 20:22:52 +0200 |
commit | 7fe16fdaf40283cd8ff6fd05958f9f9aa3811409 (patch) | |
tree | 73c7924833bc8514a7f91e7ece4b9f01ae8115bf | |
parent | libblkid: use cached buffers for nested PT probing (diff) | |
download | kernel-qcow2-util-linux-7fe16fdaf40283cd8ff6fd05958f9f9aa3811409.tar.gz kernel-qcow2-util-linux-7fe16fdaf40283cd8ff6fd05958f9f9aa3811409.tar.xz kernel-qcow2-util-linux-7fe16fdaf40283cd8ff6fd05958f9f9aa3811409.zip |
lib: add generic sysfs utils
Signed-off-by: Karel Zak <kzak@redhat.com>
-rw-r--r-- | include/at.h | 4 | ||||
-rw-r--r-- | include/sysfs.h | 57 | ||||
-rw-r--r-- | lib/Makefile.am | 3 | ||||
-rw-r--r-- | lib/at.c | 2 | ||||
-rw-r--r-- | lib/sysfs.c | 348 |
5 files changed, 412 insertions, 2 deletions
diff --git a/include/at.h b/include/at.h index 17c70d1ed..8e0a78db6 100644 --- a/include/at.h +++ b/include/at.h @@ -10,6 +10,10 @@ #define UTIL_LINUX_AT_H #include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + #include "c.h" extern int fstat_at(int dir, const char *dirname, diff --git a/include/sysfs.h b/include/sysfs.h new file mode 100644 index 000000000..bbb3816c2 --- /dev/null +++ b/include/sysfs.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2011 Karel Zak <kzak@redhat.com> + */ +#ifndef UTIL_LINUX_SYSFS_H +#define UTIL_LINUX_SYSFS_H + + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdint.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> +#include <fcntl.h> +#include <string.h> +#include <inttypes.h> +#include <sys/types.h> +#include <dirent.h> + +struct sysfs_cxt { + dev_t devno; + int dir_fd; /* /sys/block/<name> */ + char *dir_path; + struct sysfs_cxt *parent; +}; + + +extern char *sysfs_devno_attribute_path(dev_t devno, char *buf, + size_t buflen, const char *attr); +extern int sysfs_devno_has_attribute(dev_t devno, const char *attr); +extern char *sysfs_devno_path(dev_t devno, char *buf, size_t buflen); +extern dev_t sysfs_devname_to_devno(const char *name, const char *parent); + +extern int sysfs_init(struct sysfs_cxt *cxt, dev_t devno, + struct sysfs_cxt *parent); +extern void sysfs_deinit(struct sysfs_cxt *cxt); + +extern DIR *sysfs_opendir(struct sysfs_cxt *cxt, const char *attr); + +extern int sysfs_stat(struct sysfs_cxt *cxt, const char *attr, struct stat *st); + +extern int sysfs_scanf(struct sysfs_cxt *cxt, const char *attr, + const char *fmt, ...) + __attribute__ ((format (scanf, 3, 4))); +extern int64_t sysfs_read_s64(struct sysfs_cxt *cxt, const char *attr); +extern uint64_t sysfs_read_u64(struct sysfs_cxt *cxt, const char *attr); +extern int sysfs_read_int(struct sysfs_cxt *cxt, const char *attr); +extern char *sysfs_strdup(struct sysfs_cxt *cxt, const char *attr); + +extern int sysfs_count_dirents(struct sysfs_cxt *cxt, const char *attr); +extern int sysfs_count_partitions(struct sysfs_cxt *cxt, const char *devname); + +extern int sysfs_is_partition_dirent(DIR *dir, struct dirent *d, + const char *parent_name); + +#endif /* UTIL_LINUX_SYSFS_H */ diff --git a/lib/Makefile.am b/lib/Makefile.am index 7d966ff99..ebf1a52c8 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -6,7 +6,7 @@ noinst_PROGRAMS = test_blkdev test_ismounted test_wholedisk test_mangle \ test_tt test_canonicalize test_at test_strutils test_procutils if LINUX if HAVE_CPU_SET_T -noinst_PROGRAMS += test_cpuset +noinst_PROGRAMS += test_cpuset test_sysfs endif endif @@ -19,6 +19,7 @@ test_strutils_SOURCES = strutils.c test_procutils_SOURCES = procutils.c if LINUX test_cpuset_SOURCES = cpuset.c +test_sysfs_SOURCES = sysfs.c at.c endif test_tt_SOURCES = tt.c $(top_srcdir)/lib/mbsalign.c test_canonicalize_SOURCES = canonicalize.c @@ -56,7 +56,7 @@ FILE *fopen_at(int dir, const char *dirname, const char *filename, int flags, return fdopen(fd, mode); } -#ifdef TEST_PROGRAM +#ifdef TEST_PROGRAM__DISABLED_FOR_NOW #include <errno.h> #include <sys/types.h> #include <dirent.h> diff --git a/lib/sysfs.c b/lib/sysfs.c new file mode 100644 index 000000000..aba12864e --- /dev/null +++ b/lib/sysfs.c @@ -0,0 +1,348 @@ +/* + * Copyright (C) 2011 Karel Zak <kzak@redhat.com> + */ + +#include "c.h" +#include "at.h" +#include "pathnames.h" +#include "sysfs.h" + +char *sysfs_devno_attribute_path(dev_t devno, char *buf, + size_t buflen, const char *attr) +{ + int len; + + if (attr) + len = snprintf(buf, buflen, _PATH_SYS_DEVBLOCK "/%d:%d/%s", + major(devno), minor(devno), attr); + else + len = snprintf(buf, buflen, _PATH_SYS_DEVBLOCK "/%d:%d", + major(devno), minor(devno)); + + return (len < 0 || len + 1 > buflen) ? NULL : buf; +} + +int sysfs_devno_has_attribute(dev_t devno, const char *attr) +{ + char path[PATH_MAX]; + struct stat info; + + if (!sysfs_devno_attribute_path(devno, path, sizeof(path), attr)) + return 0; + if (stat(path, &info) == 0) + return 1; + return 0; +} + +char *sysfs_devno_path(dev_t devno, char *buf, size_t buflen) +{ + return sysfs_devno_attribute_path(devno, buf, buflen, NULL); +} + +dev_t sysfs_devname_to_devno(const char *name, const char *parent) +{ + char buf[PATH_MAX], *path = NULL; + dev_t dev = 0; + + if (strncmp("/dev/", name, 5) == 0) { + /* + * Read from /dev + */ + struct stat st; + + if (stat(name, &st) == 0) + dev = st.st_rdev; + else + name += 5; /* unaccesible, or not node in /dev */ + } + + if (!dev && parent) { + /* + * Create path to /sys/block/<parent>/<name>/dev + */ + int len = snprintf(buf, sizeof(buf), + _PATH_SYS_BLOCK "/%s/%s/dev", parent, name); + if (len < 0 || len + 1 > sizeof(buf)) + return 0; + path = buf; + + } else if (!dev) { + /* + * Create path to /sys/block/<name>/dev + */ + int len = snprintf(buf, sizeof(buf), + _PATH_SYS_BLOCK "/%s/dev", name); + if (len < 0 || len + 1 > sizeof(buf)) + return 0; + path = buf; + } + + if (path) { + /* + * read devno from sysfs + */ + FILE *f; + int maj = 0, min = 0; + + f = fopen(path, "r"); + if (!f) + return 0; + + if (fscanf(f, "%u:%u", &maj, &min) == 2) + dev = makedev(maj, min); + fclose(f); + } + return dev; +} + +int sysfs_init(struct sysfs_cxt *cxt, dev_t devno, struct sysfs_cxt *parent) +{ + char path[PATH_MAX]; + int fd, rc = 0; + + if (!sysfs_devno_path(devno, path, sizeof(path))) + goto err; + + fd = open(path, O_RDONLY); + if (fd < 0) + goto err; +#ifndef HAVE_FSTATAT + cxt->dir_path = strdup(path); + if (!cxt->dir_path) + goto err; +#endif + cxt->devno = devno; + cxt->dir_fd = fd; + cxt->parent = parent; + return 0; +err: + rc = -errno; + sysfs_deinit(cxt); + return rc; +} + +void sysfs_deinit(struct sysfs_cxt *cxt) +{ + if (!cxt) + return; + + if (cxt->dir_fd >= 0) + close(cxt->dir_fd); + cxt->devno = 0; + cxt->dir_fd = -1; + cxt->parent = NULL; +#ifndef HAVE_FSTATAT + free(cxt->dir_path); +#endif +} + +int sysfs_stat(struct sysfs_cxt *cxt, const char *attr, struct stat *st) +{ + int rc = fstat_at(cxt->dir_fd, cxt->dir_path, attr, st, 0); + + if (rc != 0 && errno == ENOENT && + strncmp(attr, "queue/", 6) == 0 && cxt->parent) { + + /* Exception for "queue/<attr>". These attributes are available + * for parental devices only + */ + return fstat_at(cxt->parent->dir_fd, + cxt->parent->dir_path, attr, st, 0); + } + return rc; +} + +static int sysfs_open(struct sysfs_cxt *cxt, const char *attr) +{ + int fd = open_at(cxt->dir_fd, cxt->dir_path, attr, O_RDONLY); + + if (fd == -1 && errno == ENOENT && + strncmp(attr, "queue/", 6) == 0 && cxt->parent) { + + /* Exception for "queue/<attr>". These attributes are available + * for parental devices only + */ + fd = open_at(cxt->parent->dir_fd, cxt->dir_path, attr, O_RDONLY); + } + return fd; +} + +DIR *sysfs_opendir(struct sysfs_cxt *cxt, const char *attr) +{ + DIR *dir; + int fd; + + if (attr) + fd = sysfs_open(cxt, attr); + else { + /* request to open root of device in sysfs (/sys/block/<dev>) + * -- we cannot use cxt->sysfs_fd directly, because closedir() + * will close this our persistent file descriptor. + */ + fd = dup(cxt->dir_fd); + } + + if (fd < 0) + return NULL; + + dir = fdopendir(fd); + if (!dir) { + close(fd); + return NULL; + } + if (!attr) + rewinddir(dir); + return dir; +} + + +static FILE *sysfs_fopen(struct sysfs_cxt *cxt, const char *attr) +{ + int fd = sysfs_open(cxt, attr); + + return fd < 0 ? NULL : fdopen(fd, "r"); +} + + +static struct dirent *xreaddir(DIR *dp) +{ + struct dirent *d; + + while ((d = readdir(dp))) { + if (!strcmp(d->d_name, ".") || + !strcmp(d->d_name, "..")) + continue; + + /* blacklist here? */ + break; + } + return d; +} + +int sysfs_is_partition_dirent(DIR *dir, struct dirent *d, const char *parent_name) +{ + char path[256]; + +#ifdef _DIRENT_HAVE_D_TYPE + if (d->d_type != DT_DIR) + return 0; +#endif + if (strncmp(parent_name, d->d_name, strlen(parent_name))) + return 0; + + /* Cannot use /partition file, not supported on old sysfs */ + snprintf(path, sizeof(path), "%s/start", d->d_name); + + return faccessat(dirfd(dir), path, R_OK, 0) == 0; +} + +int sysfs_scanf(struct sysfs_cxt *cxt, const char *attr, const char *fmt, ...) +{ + FILE *f = sysfs_fopen(cxt, attr); + va_list ap; + int rc; + + if (!f) + return -EINVAL; + va_start(ap, fmt); + rc = vfscanf(f, fmt, ap); + va_end(ap); + + fclose(f); + return rc; +} + +int64_t sysfs_read_s64(struct sysfs_cxt *cxt, const char *attr) +{ + uint64_t x; + return sysfs_scanf(cxt, attr, "%"SCNd64, &x) == 1 ? x : 0; +} + +uint64_t sysfs_read_u64(struct sysfs_cxt *cxt, const char *attr) +{ + uint64_t x; + return sysfs_scanf(cxt, attr, "%"SCNu64, &x) == 1 ? x : 0; +} + +int sysfs_read_int(struct sysfs_cxt *cxt, const char *attr) +{ + int x; + return sysfs_scanf(cxt, attr, "%d", &x) == 1 ? x : 0; +} + +char *sysfs_strdup(struct sysfs_cxt *cxt, const char *attr) +{ + char buf[1024]; + return sysfs_scanf(cxt, attr, "%1024[^\n]", buf) == 1 ? + strdup(buf) : NULL; +} + +int sysfs_count_dirents(struct sysfs_cxt *cxt, const char *attr) +{ + DIR *dir; + int r = 0; + + if (!(dir = sysfs_opendir(cxt, attr))) + return 0; + + while (xreaddir(dir)) r++; + + closedir(dir); + return r; +} + +int sysfs_count_partitions(struct sysfs_cxt *cxt, const char *devname) +{ + DIR *dir; + struct dirent *d; + int r = 0; + + if (!(dir = sysfs_opendir(cxt, NULL))) + return 0; + + while ((d = xreaddir(dir))) { + if (sysfs_is_partition_dirent(dir, d, devname)) + r++; + } + + closedir(dir); + return r; +} + + +#ifdef TEST_PROGRAM +#include <errno.h> +#include <err.h> +#include <stdlib.h> + +int main(int argc, char *argv[]) +{ + struct sysfs_cxt cxt; + char *devname; + dev_t devno; + char path[PATH_MAX]; + + if (argc != 2) + errx(EXIT_FAILURE, "usage: %s <devname>", argv[0]); + + devname = argv[1]; + devno = sysfs_devname_to_devno(devname, NULL); + + if (!devno) + err(EXIT_FAILURE, "failed to read devno"); + + printf("NAME: %s\n", devname); + printf("DEVNO: %u\n", (unsigned int) devno); + printf("DEVNOPATH: %s\n", sysfs_devno_path(devno, path, sizeof(path))); + printf("PARTITION: %s\n", + sysfs_devno_has_attribute(devno, "partition") ? "YES" : "NOT"); + + sysfs_init(&cxt, devno, NULL); + + printf("SLAVES: %d\n", sysfs_count_dirents(&cxt, "slaves")); + printf("SIZE: %jd\n", sysfs_read_u64(&cxt, "size")); + printf("SECTOR: %d\n", sysfs_read_int(&cxt, "queue/hw_sector_size")); + + return EXIT_SUCCESS; +} +#endif |