From: Karel Zak Date: Tue, 2 Dec 2014 09:59:48 +0000 (+0100) Subject: lib/sysfs: add subsystem and hotplug detection X-Git-Tag: v2.26-rc1~157 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e017ef8bb921d8faa681dfefc58209c6d8b38de5;p=thirdparty%2Futil-linux.git lib/sysfs: add subsystem and hotplug detection Signed-off-by: Karel Zak --- diff --git a/include/sysfs.h b/include/sysfs.h index 3ff20cb270..1de624aadc 100644 --- a/include/sysfs.h +++ b/include/sysfs.h @@ -70,6 +70,10 @@ extern int sysfs_count_partitions(struct sysfs_cxt *cxt, const char *devname); extern dev_t sysfs_partno_to_devno(struct sysfs_cxt *cxt, int partno); extern char *sysfs_get_slave(struct sysfs_cxt *cxt); +extern char *sysfs_get_devchain(struct sysfs_cxt *cxt, char *buf, size_t bufsz); +extern int sysfs_next_subsystem(struct sysfs_cxt *cxt, char *devchain, char **subsys); +extern int sysfs_is_hotpluggable(struct sysfs_cxt *cxt); + extern int sysfs_is_partition_dirent(DIR *dir, struct dirent *d, const char *parent_name); diff --git a/lib/sysfs.c b/lib/sysfs.c index e2baf7fcd3..63a90dcbca 100644 --- a/lib/sysfs.c +++ b/lib/sysfs.c @@ -547,6 +547,146 @@ char *sysfs_get_devname(struct sysfs_cxt *cxt, char *buf, size_t bufsiz) return buf; } +#define SUBSYSTEM_LINKNAME "/subsystem" + +/* + * For example: + * + * chain: /sys/dev/block/../../devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/ \ + * 1-1.2:1.0/host65/target65:0:0/65:0:0:0/block/sdb + * + * The function check if /subsystem symlink exists, if yes then returns + * basename of the readlink result, and remove the last subdirectory from the + * path. + */ +static char *get_subsystem(char *chain, char *buf, size_t bufsz) +{ + size_t len; + char *p; + + if (!chain || !*chain) + return NULL; + + len = strlen(chain); + if (len + sizeof(SUBSYSTEM_LINKNAME) > PATH_MAX) + return NULL; + + do { + ssize_t sz; + + /* append "/subsystem" to the path */ + memcpy(chain + len, SUBSYSTEM_LINKNAME, sizeof(SUBSYSTEM_LINKNAME)); + + /* try if subsystem symlink exists */ + sz = readlink(chain, buf, bufsz - 1); + + /* remove last subsystem from chain */ + chain[len] = '\0'; + p = strrchr(chain, '/'); + if (p) { + *p = '\0'; + len = p - chain; + } + + if (sz > 0) { + /* we found symlink to subsystem, return basename */ + buf[sz] = '\0'; + return basename(buf); + } + + } while (p); + + return NULL; +} + +/* + * Returns complete path to the device, the patch contains all all sybsystems + * used for the device. + */ +char *sysfs_get_devchain(struct sysfs_cxt *cxt, char *buf, size_t bufsz) +{ + /* read /sys/dev/block/: symlink */ + size_t sz = sysfs_readlink(cxt, NULL, buf, bufsz); + if (sz <= 0 || sz + sizeof(_PATH_SYS_DEVBLOCK "/") > bufsz) + return NULL; + + buf[sz++] = '\0'; + + /* create absolute patch from the link */ + memmove(buf + sizeof(_PATH_SYS_DEVBLOCK "/") - 1, buf, sz); + memcpy(buf, _PATH_SYS_DEVBLOCK "/", sizeof(_PATH_SYS_DEVBLOCK "/") - 1); + + return buf; +} + +/* + * The @subsys returns the next subsystem in the chain. Function modifies + * @devchain string. + * + * Returns: 0 in success, <0 on error, 1 on end of chain + */ +int sysfs_next_subsystem(struct sysfs_cxt *cxt __attribute__((unused)), + char *devchain, char **subsys) +{ + char subbuf[PATH_MAX]; + char *sub; + + if (!subsys || !devchain) + return -EINVAL; + + while ((sub = get_subsystem(devchain, subbuf, sizeof(subbuf)))) { + *subsys = strdup(sub); + if (!*subsys) + return -ENOMEM; + return 0; + } + + return 1; +} + + +static int is_hotpluggable_subsystem(const char *name) +{ + static const char * const hotplug_subsystems[] = { + "usb", + "ieee1394", + "pcmcia", + "mmc", + "ccw" + }; + size_t i; + + for (i = 0; i < ARRAY_SIZE(hotplug_subsystems); i++) + if (strcmp(name, hotplug_subsystems[i]) == 0) + return 1; + + return 0; +} + +int sysfs_is_hotpluggable(struct sysfs_cxt *cxt) +{ + char buf[PATH_MAX], *chain, *sub; + int rc = 0; + + + /* check /sys/dev/block/:/removable attribute */ + if (sysfs_read_int(cxt, "removable", &rc) == 0 && rc == 1) + return 1; + + chain = sysfs_get_devchain(cxt, buf, sizeof(buf)); + + while (chain && sysfs_next_subsystem(cxt, chain, &sub) == 0) { + rc = is_hotpluggable_subsystem(sub); + if (rc) { + free(sub); + break; + } + free(sub); + } + + return rc; +} + static int get_dm_wholedisk(struct sysfs_cxt *cxt, char *diskname, size_t len, dev_t *diskdevno) { @@ -711,6 +851,7 @@ int sysfs_devno_is_wholedisk(dev_t devno) return devno == disk; } + int sysfs_scsi_get_hctl(struct sysfs_cxt *cxt, int *h, int *c, int *t, int *l) { char buf[PATH_MAX], *hctl; @@ -858,7 +999,7 @@ int main(int argc, char *argv[]) struct sysfs_cxt cxt = UL_SYSFSCXT_EMPTY; char *devname; dev_t devno; - char path[PATH_MAX]; + char path[PATH_MAX], *sub, *chain; int i, is_part; uint64_t u64; ssize_t len; @@ -911,6 +1052,16 @@ int main(int argc, char *argv[]) printf("SECTOR: %d\n", i); printf("DEVNAME: %s\n", sysfs_get_devname(&cxt, path, sizeof(path))); + printf("HOTPLUG: %s\n", sysfs_is_hotpluggable(&cxt) ? "yes" : "no"); + + chain = sysfs_get_devchain(&cxt, path, sizeof(path)); + printf("SUBSUSTEMS:\n"); + + while (chain && sysfs_next_subsystem(&cxt, chain, &sub) == 0) { + printf("\t%s\n", sub); + free(sub); + } + sysfs_deinit(&cxt); return EXIT_SUCCESS;