X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=device.c;h=0a7c967fabb230d280935d4c3b933b94c1491ea7;hp=c5901c95ff67697973ed4d22bc7bb65ddb1f5a9b;hb=c3b3c274cf7911121f84746cd80a152455f7ec97;hpb=2e478a46c3754ede1a7af57b961056a2a375a799 diff --git a/device.c b/device.c index c5901c95f..0a7c967fa 100644 --- a/device.c +++ b/device.c @@ -42,7 +42,18 @@ static void device_done(Unit *u) { Device *d = DEVICE(u); assert(d); + free(d->sysfs); + d->sysfs = NULL; +} + +static void device_init(Unit *u) { + Device *d = DEVICE(u); + + assert(d); + assert(u->meta.load_state == UNIT_STUB); + + d->state = 0; } static void device_set_state(Device *d, DeviceState state) { @@ -52,7 +63,8 @@ static void device_set_state(Device *d, DeviceState state) { old_state = d->state; d->state = state; - log_debug("%s changed %s → %s", unit_id(UNIT(d)), state_string_table[old_state], state_string_table[state]); + if (state != old_state) + log_debug("%s changed %s → %s", unit_id(UNIT(d)), state_string_table[old_state], state_string_table[state]); unit_notify(UNIT(d), state_translation_table[old_state], state_translation_table[state]); } @@ -111,67 +123,133 @@ static int device_add_escaped_name(Unit *u, const char *dn, bool make_id) { return 0; } +static int device_find_escape_name(Manager *m, const char *dn, Unit **_u) { + char *e; + Unit *u; + + assert(m); + assert(dn); + assert(dn[0] == '/'); + assert(_u); + + if (!(e = unit_name_escape_path(dn+1, ".device"))) + return -ENOMEM; + + u = manager_get_unit(m, e); + free(e); + + if (u) { + *_u = u; + return 1; + } + + return 0; +} + +static bool devnode_is_api(const char *node) { + unsigned i; + + static const char * const table[] = { + "/dev/null", + "/dev/zero", + "/dev/urandom", + "/dev/random", + "/dev/port", + "/dev/oldmem", + "/dev/full", + "/dev/kmsg", + "/dev/mem" + }; + + for (i = 0; i < ELEMENTSOF(table); i++) + if (streq(table[i], node)) + return true; + + return false; +} + static int device_process_new_device(Manager *m, struct udev_device *dev, bool update_state) { - const char *dn, *names, *wants, *sysfs; + const char *dn, *names, *wants, *sysfs, *expose, *model; Unit *u = NULL; int r; - char *e, *w, *state; + char *w, *state; size_t l; bool delete; struct udev_list_entry *item = NULL, *first = NULL; assert(m); + if (!(sysfs = udev_device_get_syspath(dev))) + return -ENOMEM; + /* Check whether this entry is even relevant for us. */ dn = udev_device_get_devnode(dev); + expose = udev_device_get_property_value(dev, "SYSTEMD_EXPOSE"); names = udev_device_get_property_value(dev, "SYSTEMD_NAMES"); wants = udev_device_get_property_value(dev, "SYSTEMD_WANTS"); - if (!dn && !names && !wants) - return 0; + if (expose) { + int b; + + if ((b = parse_boolean(expose)) < 0) { + log_error("Failed to parse SYSTEMD_EXPOSE udev property for device %s: %s", sysfs, expose); + return 0; + } + + if (!b) + return 0; + } else + if ((!dn || devnode_is_api(dn)) && !names && !wants) + return 0; /* Ok, seems kinda interesting. Now, let's see if this one * already exists. */ - if (!(sysfs = udev_device_get_syspath(dev))) - return -ENOMEM; + if ((r = device_find_escape_name(m, sysfs, &u)) < 0) + return r; - assert(sysfs[0] == '/'); - if (!(e = unit_name_escape_path(sysfs+1, ".device"))) - return -ENOMEM; + if (r == 0 && dn) + if ((r = device_find_escape_name(m, dn, &u)) < 0) + return r; - if (!(u = manager_get_unit(m, e))) { - const char *model; + if (r == 0) { + first = udev_device_get_devlinks_list_entry(dev); + udev_list_entry_foreach(item, first) { + if ((r = device_find_escape_name(m, udev_list_entry_get_name(item), &u)) < 0) + return r; + if (r > 0) + break; + } + } + + /* FIXME: this needs proper merging */ + + assert((r > 0) == !!u); + + /* If this is a different unit, then let's not merge things */ + if (u && DEVICE(u)->sysfs && !streq(DEVICE(u)->sysfs, sysfs)) + u = NULL; + + if (!u) { delete = true; - if (!(u = unit_new(m))) { - free(e); + if (!(u = unit_new(m))) return -ENOMEM; - } - r = unit_add_name(u, e); - free(e); - - if (r < 0) + if ((r = device_add_escaped_name(u, sysfs, true)) < 0) goto fail; + unit_add_to_load_queue(u); + } else + delete = false; + + if (!(DEVICE(u)->sysfs)) if (!(DEVICE(u)->sysfs = strdup(sysfs))) { r = -ENOMEM; goto fail; } - if ((model = udev_device_get_property_value(dev, "ID_MODEL_FROM_DATABASE")) || - (model = udev_device_get_property_value(dev, "ID_MODEL"))) - if ((r = unit_set_description(u, model)) < 0) - goto fail; - - unit_add_to_load_queue(u); - } else { - delete = false; - free(e); - } - if (dn) if ((r = device_add_escaped_name(u, dn, true)) < 0) goto fail; @@ -181,10 +259,26 @@ static int device_process_new_device(Manager *m, struct udev_device *dev, bool u if ((r = device_add_escaped_name(u, udev_list_entry_get_name(item), false)) < 0) goto fail; + if ((model = udev_device_get_property_value(dev, "ID_MODEL_FROM_DATABASE")) || + (model = udev_device_get_property_value(dev, "ID_MODEL"))) { + if ((r = unit_set_description(u, model)) < 0) + goto fail; + } else if (dn) + if ((r = unit_set_description(u, dn)) < 0) + goto fail; + + /* We don't remove names that are gone. But that should be + * fine and should probably be fixed only on a configuration + * refresh. */ + if (names) { FOREACH_WORD(w, l, names, state) { - if (!(e = strndup(w, l))) + char *e; + + if (!(e = strndup(w, l))) { + r = -ENOMEM; goto fail; + } r = unit_add_name(u, e); free(e); @@ -196,8 +290,12 @@ static int device_process_new_device(Manager *m, struct udev_device *dev, bool u if (wants) { FOREACH_WORD(w, l, wants, state) { - if (!(e = strndup(w, l))) + char *e; + + if (!(e = strndup(w, l))) { + r = -ENOMEM; goto fail; + } r = unit_add_dependency_by_name(u, UNIT_WANTS, e); free(e); @@ -343,8 +441,6 @@ void device_fd_event(Manager *m, int events) { assert(m); assert(events == EPOLLIN); - log_debug("got udev event"); - if (!(dev = udev_monitor_receive_device(m->udev_monitor))) { log_error("Failed to receive device."); return; @@ -374,7 +470,8 @@ fail: const UnitVTable device_vtable = { .suffix = ".device", - .init = unit_load_fragment_and_dropin, + .init = device_init, + .load = unit_load_fragment_and_dropin_optional, .done = device_done, .coldplug = device_coldplug,