device.c revision b92bea5d2a9481de69bb627a7b442a9f58fca43d
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering This file is part of systemd.
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering Copyright 2010 Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering systemd is free software; you can redistribute it and/or modify it
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering under the terms of the GNU Lesser General Public License as published by
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering (at your option) any later version.
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering systemd is distributed in the hope that it will be useful, but
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering Lesser General Public License for more details.
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering You should have received a copy of the GNU Lesser General Public License
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
3ffd4af22052963e7a29431721ee204e634bea75Lennart Poetteringstatic const UnitActiveState state_translation_table[_DEVICE_STATE_MAX] = {
15a5e95075a7f6007dd97b2a165c8ed16fe683dfLennart Poetteringstatic void device_unset_sysfs(Device *d) {
b92bea5d2a9481de69bb627a7b442a9f58fca43dZbigniew Jędrzejewski-Szmek /* Remove this unit from the chain of devices which share the
46b131574fdd7d77c15a0919ca9010cad7aa6ac7Lennart Poettering * same sysfs path. */
b92bea5d2a9481de69bb627a7b442a9f58fca43dZbigniew Jędrzejewski-Szmek first = hashmap_get(UNIT(d)->manager->devices_by_sysfs, d->sysfs);
b92bea5d2a9481de69bb627a7b442a9f58fca43dZbigniew Jędrzejewski-Szmek LIST_REMOVE(Device, same_sysfs, first, d);
b92bea5d2a9481de69bb627a7b442a9f58fca43dZbigniew Jędrzejewski-Szmek hashmap_remove_and_replace(UNIT(d)->manager->devices_by_sysfs, d->sysfs, first->sysfs, first);
46b131574fdd7d77c15a0919ca9010cad7aa6ac7Lennart Poettering hashmap_remove(UNIT(d)->manager->devices_by_sysfs, d->sysfs);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering assert(UNIT(d)->load_state == UNIT_STUB);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering /* In contrast to all other unit types we timeout jobs waiting
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * for devices by default. This is because they otherwise wait
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * indefinitely for plugged in devices, something which cannot
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * happen for the other units since their operations time out
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering UNIT(d)->job_timeout = DEFAULT_TIMEOUT_USEC;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic void device_set_state(Device *d, DeviceState state) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering unit_notify(UNIT(d), state_translation_table[old_state], state_translation_table[state], true);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic void device_dump(Unit *u, FILE *f, const char *prefix) {
3b3154df7e2773332bb814e167187367a0ccae4aLennart Poettering "%sDevice State: %s\n"
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering "%sSysfs Path: %s\n",
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering prefix, device_state_to_string(d->state),
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic UnitActiveState device_active_state(Unit *u) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return state_translation_table[DEVICE(u)->state];
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic const char *device_sub_state_to_string(Unit *u) {
3b97fcbd28f92a1e51887fef5de8844a89bde523Lennart Poettering return device_state_to_string(DEVICE(u)->state);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic int device_add_escaped_name(Unit *u, const char *dn) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (r < 0 && r != -EEXIST)
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic int device_find_escape_name(Manager *m, const char *dn, Unit **_u) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic int device_update_unit(Manager *m, struct udev_device *dev, const char *path, bool main) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (!(sysfs = udev_device_get_syspath(dev)))
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if ((r = device_find_escape_name(m, path, &u)) < 0)
e88baee88fad8bc59d33b55a7a2d640ef9e16cd6Zbigniew Jędrzejewski-Szmek if (u && DEVICE(u)->sysfs && !path_equal(DEVICE(u)->sysfs, sysfs))
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering /* If this was created via some dependency and has not
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * actually been seen yet ->sysfs will not be
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * initialized. Hence initialize it if necessary. */
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (!(DEVICE(u)->sysfs = strdup(sysfs))) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (!(m->devices_by_sysfs = hashmap_new(string_hash_func, string_compare_func))) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering first = hashmap_get(m->devices_by_sysfs, sysfs);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering LIST_PREPEND(Device, same_sysfs, first, DEVICE(u));
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if ((r = hashmap_replace(m->devices_by_sysfs, DEVICE(u)->sysfs, first)) < 0)
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if ((model = udev_device_get_property_value(dev, "ID_MODEL_FROM_DATABASE")) ||
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering (model = udev_device_get_property_value(dev, "ID_MODEL"))) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if ((r = unit_set_description(u, model)) < 0)
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if ((r = unit_set_description(u, path)) < 0)
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering /* The additional systemd udev properties we only
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering * interpret for the main object */
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering alias = udev_device_get_property_value(dev, "SYSTEMD_ALIAS");
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering FOREACH_WORD_QUOTED(w, l, alias, state) {
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering log_warning("SYSTEMD_ALIAS for %s is not a path, ignoring: %s", sysfs, e);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering wants = udev_device_get_property_value(dev, "SYSTEMD_WANTS");
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering FOREACH_WORD_QUOTED(w, l, wants, state) {
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering r = unit_add_dependency_by_name(u, UNIT_WANTS, e, NULL, true);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering log_warning("Failed to load device unit: %s", strerror(-r));
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poetteringstatic int device_process_new_device(Manager *m, struct udev_device *dev, bool update_state) {
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering struct udev_list_entry *item = NULL, *first = NULL;
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering if (!(sysfs = udev_device_get_syspath(dev)))
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering /* Add the main unit named after the sysfs path */
3b3154df7e2773332bb814e167187367a0ccae4aLennart Poettering /* Add an additional unit for the device node */
8457f8d6ac7adc6c6ef31378e6e7761cce522141Lennart Poettering /* Add additional units for all symlinks */
8457f8d6ac7adc6c6ef31378e6e7761cce522141Lennart Poettering first = udev_device_get_devlinks_list_entry(dev);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering const char *p;
8457f8d6ac7adc6c6ef31378e6e7761cce522141Lennart Poettering /* Don't bother with the /dev/block links */
e3bfb7be07d9b1f4ebb12eb22c4c8bcd2a988d51Zbigniew Jędrzejewski-Szmek /* Verify that the symlink in the FS actually belongs
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * to this device. This is useful to deal with
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * conflicting devices, e.g. when two disks want the
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * same /dev/disk/by-label/xxx link because they have
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * the same label. We want to make sure that the same
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * device that won the symlink wins in systemd, so we
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering * check the device node major/minor*/
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if ((!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode)) ||
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering st.st_rdev != udev_device_get_devnum(dev))
8457f8d6ac7adc6c6ef31378e6e7761cce522141Lennart Poettering l = hashmap_get(m->devices_by_sysfs, sysfs);
63c372cb9df3bee01e3bf8cd7f96f336bddda846Lennart Poetteringstatic int device_process_path(Manager *m, const char *path, bool update_state) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (!(dev = udev_device_new_from_syspath(m->udev, path))) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering log_warning("Failed to get udev device object from udev for path %s.", path);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = device_process_new_device(m, dev, update_state);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic int device_process_removed_device(Manager *m, struct udev_device *dev) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (!(sysfs = udev_device_get_syspath(dev)))
03ee5c38cb0da193dd08733fb4c0c2809cee6a99Lennart Poettering /* Remove all units of this sysfs path */
b92bea5d2a9481de69bb627a7b442a9f58fca43dZbigniew Jędrzejewski-Szmek while ((d = hashmap_get(m->devices_by_sysfs, sysfs))) {
4a62c710b62a5a3c7a8a278b810b9d5b5a0c8f4fMichal Schmidt /* Make everybody follow the unit that's named after the sysfs path */
4a62c710b62a5a3c7a8a278b810b9d5b5a0c8f4fMichal Schmidt for (other = d->same_sysfs_next; other; other = other->same_sysfs_next)
d682b3a7e7c7c2941a4d3e193f1e330dbc9fae89Lennart Poettering for (other = d->same_sysfs_prev; other; other = other->same_sysfs_prev) {
8531ae707d4d0203e83304d4af948b8169a5fce1Lennart Poetteringstatic int device_following_set(Unit *u, Set **_s) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (!d->same_sysfs_prev && !d->same_sysfs_next) {
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering for (other = d->same_sysfs_next; other; other = other->same_sysfs_next)
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering for (other = d->same_sysfs_prev; other; other = other->same_sysfs_prev)
fail:
set_free(s);
assert(m);
if (m->udev_monitor) {
if (m->udev) {
assert(m);
if (!m->udev) {
return -ENOMEM;
r = -ENOMEM;
goto fail;
r = -ENOMEM;
goto fail;
r = -EIO;
goto fail;
return -errno;
r = -ENOMEM;
goto fail;
r = -EIO;
goto fail;
if (udev_enumerate_scan_devices(e) < 0) {
r = -EIO;
goto fail;
fail:
device_shutdown(m);
assert(m);
goto fail;
goto fail;
goto fail;
fail:
.sections =
.no_instances = true,
.starting_stopping = {
.finished_start_job = {