1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
23 #include <sys/epoll.h>
31 static const UnitActiveState state_translation_table
[_DEVICE_STATE_MAX
] = {
32 [DEVICE_DEAD
] = UNIT_INACTIVE
,
33 [DEVICE_AVAILABLE
] = UNIT_ACTIVE
36 static const char* const state_string_table
[_DEVICE_STATE_MAX
] = {
37 [DEVICE_DEAD
] = "dead",
38 [DEVICE_AVAILABLE
] = "available"
41 static void device_done(Unit
*u
) {
42 Device
*d
= DEVICE(u
);
48 static void device_set_state(Device
*d
, DeviceState state
) {
49 DeviceState old_state
;
55 log_debug("%s changed %s → %s", unit_id(UNIT(d
)), state_string_table
[old_state
], state_string_table
[state
]);
57 unit_notify(UNIT(d
), state_translation_table
[old_state
], state_translation_table
[state
]);
60 static int device_coldplug(Unit
*u
) {
61 Device
*d
= DEVICE(u
);
64 assert(d
->state
== DEVICE_DEAD
);
67 device_set_state(d
, DEVICE_AVAILABLE
);
72 static void device_dump(Unit
*u
, FILE *f
, const char *prefix
) {
73 Device
*d
= DEVICE(u
);
78 "%sDevice State: %s\n"
80 prefix
, state_string_table
[d
->state
],
81 prefix
, strna(d
->sysfs
));
84 static UnitActiveState
device_active_state(Unit
*u
) {
87 return state_translation_table
[DEVICE(u
)->state
];
90 static int device_add_escaped_name(Unit
*u
, const char *prefix
, const char *dn
, bool make_id
) {
98 if (!(e
= unit_name_escape_path(prefix
, dn
+1, ".device")))
101 r
= unit_add_name(u
, e
);
103 if (r
>= 0 && make_id
)
104 unit_choose_id(u
, e
);
108 if (r
< 0 && r
!= -EEXIST
)
114 static int device_process_new_device(Manager
*m
, struct udev_device
*dev
, bool update_state
) {
115 const char *dn
, *names
, *wants
, *sysfs
;
121 struct udev_list_entry
*item
= NULL
, *first
= NULL
;
125 /* Check whether this entry is even relevant for us. */
126 dn
= udev_device_get_devnode(dev
);
127 names
= udev_device_get_property_value(dev
, "SYSTEMD_NAMES");
128 wants
= udev_device_get_property_value(dev
, "SYSTEMD_WANTS");
130 if (!dn
&& !names
&& !wants
)
133 /* Ok, seems kinda interesting. Now, let's see if this one
136 if (!(sysfs
= udev_device_get_syspath(dev
)))
139 assert(sysfs
[0] == '/');
140 if (!(e
= unit_name_escape_path("sysfs-", sysfs
+1, ".device")))
143 if (!(u
= manager_get_unit(m
, e
))) {
148 if (!(u
= unit_new(m
))) {
153 r
= unit_add_name(u
, e
);
159 if (!(DEVICE(u
)->sysfs
= strdup(sysfs
))) {
164 if ((model
= udev_device_get_property_value(dev
, "ID_MODEL_FROM_DATABASE")) ||
165 (model
= udev_device_get_property_value(dev
, "ID_MODEL")))
166 if ((r
= unit_set_description(u
, model
)) < 0)
169 unit_add_to_load_queue(u
);
176 if ((r
= device_add_escaped_name(u
, "node-", dn
, true)) < 0)
179 first
= udev_device_get_devlinks_list_entry(dev
);
180 udev_list_entry_foreach(item
, first
)
181 if ((r
= device_add_escaped_name(u
, "node-", udev_list_entry_get_name(item
), false)) < 0)
185 FOREACH_WORD(w
, l
, names
, state
) {
186 if (!(e
= strndup(w
, l
)))
189 r
= unit_add_name(u
, e
);
192 if (r
< 0 && r
!= -EEXIST
)
198 FOREACH_WORD(w
, l
, wants
, state
) {
199 if (!(e
= strndup(w
, l
)))
202 r
= unit_add_dependency_by_name(u
, UNIT_WANTS
, e
);
211 manager_dispatch_load_queue(u
->meta
.manager
);
212 device_set_state(DEVICE(u
), DEVICE_AVAILABLE
);
223 static int device_process_path(Manager
*m
, const char *path
, bool update_state
) {
225 struct udev_device
*dev
;
230 if (!(dev
= udev_device_new_from_syspath(m
->udev
, path
))) {
231 log_warning("Failed to get udev device object from udev for path %s.", path
);
235 r
= device_process_new_device(m
, dev
, update_state
);
236 udev_device_unref(dev
);
240 static int device_process_removed_device(Manager
*m
, struct udev_device
*dev
) {
249 if (!(sysfs
= udev_device_get_syspath(dev
)))
252 assert(sysfs
[0] == '/');
253 if (!(e
= unit_name_escape_path("sysfs-", sysfs
+1, ".device")))
256 u
= manager_get_unit(m
, e
);
266 device_set_state(d
, DEVICE_DEAD
);
270 static void device_shutdown(Manager
*m
) {
274 udev_monitor_unref(m
->udev_monitor
);
280 static int device_enumerate(Manager
*m
) {
281 struct epoll_event ev
;
283 struct udev_enumerate
*e
= NULL
;
284 struct udev_list_entry
*item
= NULL
, *first
= NULL
;
288 if (!(m
->udev
= udev_new()))
291 if (!(m
->udev_monitor
= udev_monitor_new_from_netlink(m
->udev
, "udev"))) {
296 if (udev_monitor_enable_receiving(m
->udev_monitor
) < 0) {
301 m
->udev_watch
.type
= WATCH_UDEV
;
302 m
->udev_watch
.fd
= udev_monitor_get_fd(m
->udev_monitor
);
306 ev
.data
.ptr
= &m
->udev_watch
;
308 if (epoll_ctl(m
->epoll_fd
, EPOLL_CTL_ADD
, m
->udev_watch
.fd
, &ev
) < 0)
311 if (!(e
= udev_enumerate_new(m
->udev
))) {
316 if (udev_enumerate_scan_devices(e
) < 0) {
321 first
= udev_enumerate_get_list_entry(e
);
322 udev_list_entry_foreach(item
, first
)
323 device_process_path(m
, udev_list_entry_get_name(item
), false);
325 udev_enumerate_unref(e
);
330 udev_enumerate_unref(e
);
336 void device_fd_event(Manager
*m
, int events
) {
337 struct udev_device
*dev
;
342 assert(events
== EPOLLIN
);
344 log_debug("got udev event");
346 if (!(dev
= udev_monitor_receive_device(m
->udev_monitor
))) {
347 log_error("Failed to receive device.");
351 if (!(action
= udev_device_get_action(dev
))) {
352 log_error("Failed to get udev action string.");
356 if (streq(action
, "remove")) {
357 if ((r
= device_process_removed_device(m
, dev
)) < 0) {
358 log_error("Failed to process udev device event: %s", strerror(-r
));
362 if ((r
= device_process_new_device(m
, dev
, true)) < 0) {
363 log_error("Failed to process udev device event: %s", strerror(-r
));
369 udev_device_unref(dev
);
372 const UnitVTable device_vtable
= {
375 .init
= unit_load_fragment_and_dropin
,
377 .coldplug
= device_coldplug
,
381 .active_state
= device_active_state
,
383 .enumerate
= device_enumerate
,
384 .shutdown
= device_shutdown