eject: use sysfs API to detect hotplug

Signed-off-by: Karel Zak <kzak@redhat.com>
This commit is contained in:
Karel Zak 2014-12-02 11:00:19 +01:00
parent e017ef8bb9
commit bc265f7cfa
1 changed files with 1 additions and 105 deletions

View File

@ -71,17 +71,6 @@
*/
#define TRAY_WAS_ALREADY_OPEN_USECS 200000 /* about 0.2 seconds */
/* eject(1) is able to eject only 'removable' devices (attribute in /sys)
* _or_ devices connected by hotplug subsystem.
*/
static const char * const hotplug_subsystems[] = {
"usb",
"ieee1394",
"pcmcia",
"mmc",
"ccw"
};
struct eject_control {
struct libmnt_table *mtab;
char *device; /* device or mount point to be ejected */
@ -820,111 +809,18 @@ done:
return count;
}
static int is_hotpluggable_subsystem(const char *name)
{
size_t i;
for (i = 0; i < ARRAY_SIZE(hotplug_subsystems); i++)
if (strcmp(name, hotplug_subsystems[i]) == 0)
return 1;
return 0;
}
#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 <chain>/subsystem symlink exists, if yes then returns
* basename of the readlink result, and remove the last subdirectory from the
* <chain> 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;
}
static int is_hotpluggable(const struct eject_control *ctl)
{
struct sysfs_cxt cxt = UL_SYSFSCXT_EMPTY;
char devchain[PATH_MAX];
char subbuf[PATH_MAX];
dev_t devno;
int rc = 0;
ssize_t sz;
char *sub;
devno = sysfs_devname_to_devno(ctl->device, NULL);
if (sysfs_init(&cxt, devno, NULL) != 0)
return 0;
/* check /sys/dev/block/<maj>:<min>/removable attribute */
if (sysfs_read_int(&cxt, "removable", &rc) == 0 && rc == 1) {
verbose(ctl, _("%s: is removable device"), ctl->device);
goto done;
}
rc = sysfs_is_hotpluggable(&cxt);
/* read /sys/dev/block/<maj>:<min> symlink */
sz = sysfs_readlink(&cxt, NULL, devchain, sizeof(devchain));
if (sz <= 0 || sz + sizeof(_PATH_SYS_DEVBLOCK "/") > sizeof(devchain))
goto done;
devchain[sz++] = '\0';
/* create absolute patch from the link */
memmove(devchain + sizeof(_PATH_SYS_DEVBLOCK "/") - 1, devchain, sz);
memcpy(devchain, _PATH_SYS_DEVBLOCK "/",
sizeof(_PATH_SYS_DEVBLOCK "/") - 1);
while ((sub = get_subsystem(devchain, subbuf, sizeof(subbuf)))) {
rc = is_hotpluggable_subsystem(sub);
if (rc) {
verbose(ctl, _("%s: connected by hotplug subsystem: %s"),
ctl->device, sub);
break;
}
}
done:
sysfs_deinit(&cxt);
return rc;
}