mirror of
https://github.com/troglobit/finit.git
synced 2026-10-23 11:44:37 +00:00
keventd: add class/<subsystem>/<name> and driver/<name> conditions
dev/<X> only fires when a device gets a /dev node, which leaves a lot of
embedded-relevant hardware uncoverable: DSA switch cores, IIO sensors,
LEDs, backlight, PHYs, regulators -- all live purely under sysfs.
Two new condition namespaces:
class/<subsystem>/<sysname> asserted on every sysfs class device add
(e.g. <class/leds/blue>)
driver/<name> asserted while the driver is bound to at
least one device (e.g. <driver/mt7530>)
A driver can bind to several devices, so driver/ conditions are
refcounted: asserted on first bind, cleared when the last device is
unbound.
dev_cond() is generalized into a static cond_emit(prefix, rel, set) so
class_cond() and driver_cond() share the same mkpath + symlink/erase
code. The name avoids colliding with src/cond.h's public cond_path()
helper (unrelated function that returns a condition's filesystem path).
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
6 files changed
+174
-13
No files matched your search
+12
-1
@@ -334,11 +334,17 @@ static void handle_uevent(char *buf, size_t len)
|
||||
|
||||
/* Create symlinks */
|
||||
symlink_add(&ev);
|
||||
|
||||
/* class/<subsystem>/<sysname> -- covers sysfs-only
|
||||
* devices that don't get a /dev node. */
|
||||
class_cond(&ev, 1);
|
||||
}
|
||||
break;
|
||||
|
||||
case ACT_REMOVE:
|
||||
if (!passive) {
|
||||
class_cond(&ev, 0);
|
||||
|
||||
/* Remove symlinks first */
|
||||
symlink_del(&ev);
|
||||
|
||||
@@ -356,8 +362,13 @@ static void handle_uevent(char *buf, size_t len)
|
||||
break;
|
||||
|
||||
case ACT_BIND:
|
||||
if (!passive)
|
||||
driver_cond(&ev, 1);
|
||||
break;
|
||||
|
||||
case ACT_UNBIND:
|
||||
/* Driver bind/unbind - could trigger conditions */
|
||||
if (!passive)
|
||||
driver_cond(&ev, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@@ -149,6 +149,9 @@ int devnode_add (struct uevent *ev);
|
||||
int devnode_del (struct uevent *ev);
|
||||
int netdev_add (struct uevent *ev);
|
||||
|
||||
void class_cond (const struct uevent *ev, int set);
|
||||
void driver_cond (const struct uevent *ev, int set);
|
||||
|
||||
int symlink_add (struct uevent *ev);
|
||||
int symlink_del (struct uevent *ev);
|
||||
void symlink_write_db (const char *devpath, FILE *fp);
|
||||
|
||||
+95
-11
@@ -58,37 +58,121 @@
|
||||
void logit(int prio, const char *fmt, ...);
|
||||
|
||||
/*
|
||||
* Set/clear a device condition by creating/removing a symlink.
|
||||
* Set/clear a Finit condition by creating/removing a symlink.
|
||||
* keventd is a standalone daemon, so we manipulate the filesystem directly
|
||||
* rather than using Finit's internal cond_set()/cond_clear() API.
|
||||
*
|
||||
* prefix is the namespace path fragment (COND_DEV, COND_CLASS,
|
||||
* COND_DRIVER, ...) ending in '/'. rel is the relative path under that
|
||||
* namespace (e.g. "sda", "input/event0", "net/eth0", "mt7530").
|
||||
*/
|
||||
static void dev_cond(const char *devname, int set)
|
||||
static void cond_emit(const char *prefix, const char *rel, int set)
|
||||
{
|
||||
char cond[PATH_MAX];
|
||||
char *dir;
|
||||
|
||||
if (!devname)
|
||||
if (!prefix || !rel || !*rel)
|
||||
return;
|
||||
|
||||
snprintf(cond, sizeof(cond), "%s%s", _PATH_CONDDEV, devname);
|
||||
snprintf(cond, sizeof(cond), "%s%s%s", _PATH_COND, prefix, rel);
|
||||
|
||||
/* Create parent directory if needed (e.g., dev/input/) */
|
||||
/* mkpath is idempotent (EEXIST-tolerant) so just always call it. */
|
||||
dir = strdupa(cond);
|
||||
dir = dirname(dir);
|
||||
if (strcmp(dir, _PATH_CONDDEV)) {
|
||||
if (mkpath(dir, 0755) && errno != EEXIST)
|
||||
logit(LOG_WARNING, "Failed creating condition dir %s", dir);
|
||||
}
|
||||
if (mkpath(dir, 0755) && errno != EEXIST)
|
||||
logit(LOG_WARNING, "Failed creating condition dir %s", dir);
|
||||
|
||||
if (set) {
|
||||
if (symlink(_PATH_RECONF, cond) && errno != EEXIST)
|
||||
logit(LOG_WARNING, "Failed setting dev/%s condition", devname);
|
||||
logit(LOG_WARNING, "Failed setting %s%s condition", prefix, rel);
|
||||
} else {
|
||||
if (erase(cond) && errno != ENOENT)
|
||||
logit(LOG_WARNING, "Failed clearing dev/%s condition", devname);
|
||||
logit(LOG_WARNING, "Failed clearing %s%s condition", prefix, rel);
|
||||
}
|
||||
}
|
||||
|
||||
static void dev_cond(const char *devname, int set)
|
||||
{
|
||||
cond_emit(COND_DEV, devname, set);
|
||||
}
|
||||
|
||||
/*
|
||||
* class/<subsystem>/<sysname> -- fires for any sysfs class device add,
|
||||
* regardless of whether the device gets a /dev node. Lets services
|
||||
* depend on sysfs-only hardware (DSA switch ports, LEDs, IIO sensors,
|
||||
* PHYs, etc.).
|
||||
*/
|
||||
void class_cond(const struct uevent *ev, int set)
|
||||
{
|
||||
const char *sysname;
|
||||
char rel[256];
|
||||
|
||||
if (!ev->subsystem || !ev->devpath)
|
||||
return;
|
||||
|
||||
sysname = strrchr(ev->devpath, '/');
|
||||
sysname = sysname ? sysname + 1 : ev->devpath;
|
||||
if (!*sysname)
|
||||
return;
|
||||
|
||||
snprintf(rel, sizeof(rel), "%s/%s", ev->subsystem, sysname);
|
||||
cond_emit(COND_CLASS, rel, set);
|
||||
}
|
||||
|
||||
/*
|
||||
* driver/<name> -- asserted while the driver is bound to at least one
|
||||
* device, from the kernel's bind/unbind uevents. Lets services depend
|
||||
* on a specific driver having probed its hardware (e.g. <driver/mt7530>)
|
||||
* even when no class device or /dev node marks the moment.
|
||||
*
|
||||
* A driver can bind to several devices, so the condition is refcounted:
|
||||
* asserted on the first bind, cleared when the last device is unbound.
|
||||
*/
|
||||
struct drv_bind {
|
||||
TAILQ_ENTRY(drv_bind) link;
|
||||
char *name;
|
||||
size_t count;
|
||||
};
|
||||
|
||||
static TAILQ_HEAD(, drv_bind) drv_binds = TAILQ_HEAD_INITIALIZER(drv_binds);
|
||||
|
||||
void driver_cond(const struct uevent *ev, int set)
|
||||
{
|
||||
struct drv_bind *db;
|
||||
|
||||
if (!ev->driver)
|
||||
return;
|
||||
|
||||
TAILQ_FOREACH(db, &drv_binds, link) {
|
||||
if (!strcmp(db->name, ev->driver))
|
||||
break;
|
||||
}
|
||||
|
||||
if (set) {
|
||||
if (!db) {
|
||||
db = calloc(1, sizeof(*db));
|
||||
if (!db)
|
||||
goto emit;
|
||||
db->name = strdup(ev->driver);
|
||||
if (!db->name) {
|
||||
free(db);
|
||||
goto emit;
|
||||
}
|
||||
TAILQ_INSERT_TAIL(&drv_binds, db, link);
|
||||
}
|
||||
if (db->count++)
|
||||
return;
|
||||
} else if (db) {
|
||||
if (--db->count)
|
||||
return;
|
||||
TAILQ_REMOVE(&drv_binds, db, link);
|
||||
free(db->name);
|
||||
free(db);
|
||||
}
|
||||
emit:
|
||||
cond_emit(COND_DRIVER, ev->driver, set);
|
||||
}
|
||||
|
||||
/* Symlink tracking for cleanup on device removal */
|
||||
static TAILQ_HEAD(, dev_symlink) symlinks = TAILQ_HEAD_INITIALIZER(symlinks);
|
||||
|
||||
|
||||
Reference in new issue
Block a user