device.c 12.8 KB
Newer Older
1
2
/*-*- Mode: C; c-basic-offset: 8 -*-*/

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/***
  This file is part of systemd.

  Copyright 2010 Lennart Poettering

  systemd is free software; you can redistribute it and/or modify it
  under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  systemd is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/

22
#include <errno.h>
23
#include <sys/epoll.h>
24
25
#include <libudev.h>

Lennart Poettering's avatar
Lennart Poettering committed
26
#include "unit.h"
27
28
#include "device.h"
#include "strv.h"
29
#include "log.h"
30
#include "unit-name.h"
31
#include "dbus-device.h"
32

Lennart Poettering's avatar
Lennart Poettering committed
33
34
static const UnitActiveState state_translation_table[_DEVICE_STATE_MAX] = {
        [DEVICE_DEAD] = UNIT_INACTIVE,
35
        [DEVICE_PLUGGED] = UNIT_ACTIVE
Lennart Poettering's avatar
Lennart Poettering committed
36
37
};

38
39
40
41
42
43
44
45
46
static void device_init(Unit *u) {
        Device *d = DEVICE(u);

        assert(d);
        assert(d->meta.load_state == UNIT_STUB);

        d->meta.job_timeout = DEFAULT_TIMEOUT_USEC;
}

Lennart Poettering's avatar
Lennart Poettering committed
47
48
static void device_done(Unit *u) {
        Device *d = DEVICE(u);
49
50

        assert(d);
51

52
        free(d->sysfs);
53
54
55
        d->sysfs = NULL;
}

Lennart Poettering's avatar
Lennart Poettering committed
56
57
58
static void device_set_state(Device *d, DeviceState state) {
        DeviceState old_state;
        assert(d);
59

Lennart Poettering's avatar
Lennart Poettering committed
60
61
        old_state = d->state;
        d->state = state;
62

63
        if (state != old_state)
64
                log_debug("%s changed %s -> %s",
65
                          d->meta.id,
66
67
                          device_state_to_string(old_state),
                          device_state_to_string(state));
Lennart Poettering's avatar
Lennart Poettering committed
68
69
70
71
72
73
74
75
76
77
78

        unit_notify(UNIT(d), state_translation_table[old_state], state_translation_table[state]);
}

static int device_coldplug(Unit *u) {
        Device *d = DEVICE(u);

        assert(d);
        assert(d->state == DEVICE_DEAD);

        if (d->sysfs)
79
                device_set_state(d, DEVICE_PLUGGED);
Lennart Poettering's avatar
Lennart Poettering committed
80
81
82
83
84

        return 0;
}

static void device_dump(Unit *u, FILE *f, const char *prefix) {
85
        Device *d = DEVICE(u);
86

87
        assert(d);
88
89

        fprintf(f,
90
91
                "%sDevice State: %s\n"
                "%sSysfs Path: %s\n",
92
                prefix, device_state_to_string(d->state),
Lennart Poettering's avatar
Lennart Poettering committed
93
94
95
96
97
98
99
                prefix, strna(d->sysfs));
}

static UnitActiveState device_active_state(Unit *u) {
        assert(u);

        return state_translation_table[DEVICE(u)->state];
100
101
}

102
103
104
static const char *device_sub_state_to_string(Unit *u) {
        assert(u);

105
        return device_state_to_string(DEVICE(u)->state);
106
107
}

108
static int device_add_escaped_name(Unit *u, const char *dn, bool make_id) {
109
110
111
112
113
114
115
        char *e;
        int r;

        assert(u);
        assert(dn);
        assert(dn[0] == '/');

116
        if (!(e = unit_name_from_path(dn, ".device")))
117
118
119
                return -ENOMEM;

        r = unit_add_name(u, e);
Lennart Poettering's avatar
Lennart Poettering committed
120
121
122
123

        if (r >= 0 && make_id)
                unit_choose_id(u, e);

124
125
126
127
128
129
130
131
        free(e);

        if (r < 0 && r != -EEXIST)
                return r;

        return 0;
}

132
133
134
135
136
137
138
139
140
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);

141
        if (!(e = unit_name_from_path(dn, ".device")))
142
143
144
145
146
147
148
149
150
151
152
153
154
                return -ENOMEM;

        u = manager_get_unit(m, e);
        free(e);

        if (u) {
                *_u = u;
                return 1;
        }

        return 0;
}

155
static int device_process_new_device(Manager *m, struct udev_device *dev, bool update_state) {
156
        const char *dn, *wants, *sysfs, *model, *alias;
157
158
        Unit *u = NULL;
        int r;
159
        char *w, *state;
160
161
162
163
164
165
        size_t l;
        bool delete;
        struct udev_list_entry *item = NULL, *first = NULL;

        assert(m);

166
167
168
        if (!(sysfs = udev_device_get_syspath(dev)))
                return -ENOMEM;

169
170
171
        /* Check whether this entry is even relevant for us. */
        dn = udev_device_get_devnode(dev);
        wants = udev_device_get_property_value(dev, "SYSTEMD_WANTS");
172
173
174
175
176
177
178
179
180
        alias = udev_device_get_property_value(dev, "SYSTEMD_ALIAS");

        /* We allow exactly one alias to be configured a this time and
         * it must be a path */

        if (alias && !is_path(alias)) {
                log_warning("SYSTEMD_ALIAS for %s is not a path, ignoring: %s", sysfs, alias);
                alias = NULL;
        }
181

182
183
        if ((r = device_find_escape_name(m, sysfs, &u)) < 0)
                return r;
184

185
186
187
        if (r == 0 && dn)
                if ((r = device_find_escape_name(m, dn, &u)) < 0)
                        return r;
188

189
190
191
192
193
        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;
194

195
196
                        if (r > 0)
                                break;
197
                }
198
199
        }

200
201
202
203
        if (r == 0 && alias)
                if ((r = device_find_escape_name(m, alias, &u)) < 0)
                        return r;

204
205
206
207
208
        /* FIXME: this needs proper merging */

        assert((r > 0) == !!u);

        /* If this is a different unit, then let's not merge things */
209
        if (u && DEVICE(u)->sysfs && !path_equal(DEVICE(u)->sysfs, sysfs))
210
                u = NULL;
211

212
213
214
215
216
        if (!u) {
                delete = true;

                if (!(u = unit_new(m)))
                        return -ENOMEM;
217

218
                if ((r = device_add_escaped_name(u, sysfs, true)) < 0)
219
220
                        goto fail;

221
222
223
224
225
                unit_add_to_load_queue(u);
        } else
                delete = false;

        if (!(DEVICE(u)->sysfs))
226
227
228
229
230
                if (!(DEVICE(u)->sysfs = strdup(sysfs))) {
                        r = -ENOMEM;
                        goto fail;
                }

231
232
233
234
        if (alias)
                if ((r = device_add_escaped_name(u, alias, true)) < 0)
                        goto fail;

235
        if (dn)
236
                if ((r = device_add_escaped_name(u, dn, true)) < 0)
237
238
239
240
                        goto fail;

        first = udev_device_get_devlinks_list_entry(dev);
        udev_list_entry_foreach(item, first)
241
                if ((r = device_add_escaped_name(u, udev_list_entry_get_name(item), false)) < 0)
242
243
                        goto fail;

244
245
246
247
        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;
Lennart Poettering's avatar
Lennart Poettering committed
248
        } else if (dn) {
249
250
                if ((r = unit_set_description(u, dn)) < 0)
                        goto fail;
Lennart Poettering's avatar
Lennart Poettering committed
251
252
253
        } else
                if ((r = unit_set_description(u, sysfs)) < 0)
                        goto fail;
254

255
        if (wants) {
256
                FOREACH_WORD_QUOTED(w, l, wants, state) {
257
258
                        char *e;

259
260
                        if (!(e = strndup(w, l))) {
                                r = -ENOMEM;
261
                                goto fail;
262
                        }
263

264
                        r = unit_add_dependency_by_name(u, UNIT_WANTS, e, NULL, true);
265
266
267
268
269
270
271
                        free(e);

                        if (r < 0)
                                goto fail;
                }
        }

272
273
        if (update_state) {
                manager_dispatch_load_queue(u->meta.manager);
274
                device_set_state(DEVICE(u), DEVICE_PLUGGED);
275
276
        }

277
278
        unit_add_to_dbus_queue(u);

279
280
281
        return 0;

fail:
282
283
284

        log_warning("Failed to load device unit: %s", strerror(-r));

285
286
        if (delete && u)
                unit_free(u);
287

288
289
290
        return r;
}

291
static int device_process_path(Manager *m, const char *path, bool update_state) {
292
293
294
295
296
297
298
299
300
301
302
        int r;
        struct udev_device *dev;

        assert(m);
        assert(path);

        if (!(dev = udev_device_new_from_syspath(m->udev, path))) {
                log_warning("Failed to get udev device object from udev for path %s.", path);
                return -ENOMEM;
        }

303
        r = device_process_new_device(m, dev, update_state);
304
305
306
307
        udev_device_unref(dev);
        return r;
}

308
309
310
311
312
313
314
315
316
317
318
319
320
static int device_process_removed_device(Manager *m, struct udev_device *dev) {
        const char *sysfs;
        char *e;
        Unit *u;
        Device *d;

        assert(m);
        assert(dev);

        if (!(sysfs = udev_device_get_syspath(dev)))
                return -ENOMEM;

        assert(sysfs[0] == '/');
321
        if (!(e = unit_name_from_path(sysfs, ".device")))
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
                return -ENOMEM;

        u = manager_get_unit(m, e);
        free(e);

        if (!u)
                return 0;

        d = DEVICE(u);
        free(d->sysfs);
        d->sysfs = NULL;

        device_set_state(d, DEVICE_DEAD);
        return 0;
}

338
339
340
static void device_shutdown(Manager *m) {
        assert(m);

341
        if (m->udev_monitor) {
342
                udev_monitor_unref(m->udev_monitor);
343
344
                m->udev_monitor = NULL;
        }
345

346
        if (m->udev) {
347
                udev_unref(m->udev);
348
349
                m->udev = NULL;
        }
350
351
352
}

static int device_enumerate(Manager *m) {
353
        struct epoll_event ev;
354
355
356
357
358
359
        int r;
        struct udev_enumerate *e = NULL;
        struct udev_list_entry *item = NULL, *first = NULL;

        assert(m);

360
361
362
        if (!m->udev) {
                if (!(m->udev = udev_new()))
                        return -ENOMEM;
363

364
365
366
367
                if (!(m->udev_monitor = udev_monitor_new_from_netlink(m->udev, "udev"))) {
                        r = -ENOMEM;
                        goto fail;
                }
368

369
370
371
372
373
                if (udev_monitor_filter_add_match_tag(m->udev_monitor, "systemd") < 0) {
                        r = -ENOMEM;
                        goto fail;
                }

374
375
376
377
                if (udev_monitor_enable_receiving(m->udev_monitor) < 0) {
                        r = -EIO;
                        goto fail;
                }
378

379
380
                m->udev_watch.type = WATCH_UDEV;
                m->udev_watch.fd = udev_monitor_get_fd(m->udev_monitor);
381

382
383
384
                zero(ev);
                ev.events = EPOLLIN;
                ev.data.ptr = &m->udev_watch;
385

386
387
388
                if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->udev_watch.fd, &ev) < 0)
                        return -errno;
        }
389

390
391
392
393
        if (!(e = udev_enumerate_new(m->udev))) {
                r = -ENOMEM;
                goto fail;
        }
394
395
396
397
        if (udev_enumerate_add_match_tag(e, "systemd") < 0) {
                r = -EIO;
                goto fail;
        }
398

399
400
401
402
        if (udev_enumerate_scan_devices(e) < 0) {
                r = -EIO;
                goto fail;
        }
403

404
405
406
        first = udev_enumerate_get_list_entry(e);
        udev_list_entry_foreach(item, first)
                device_process_path(m, udev_list_entry_get_name(item), false);
407

408
        udev_enumerate_unref(e);
409
410
411
412
413
414
415
416
        return 0;

fail:
        if (e)
                udev_enumerate_unref(e);

        device_shutdown(m);
        return r;
417
418
}

419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
void device_fd_event(Manager *m, int events) {
        struct udev_device *dev;
        int r;
        const char *action;

        assert(m);
        assert(events == EPOLLIN);

        if (!(dev = udev_monitor_receive_device(m->udev_monitor))) {
                log_error("Failed to receive device.");
                return;
        }

        if (!(action = udev_device_get_action(dev))) {
                log_error("Failed to get udev action string.");
                goto fail;
        }

        if (streq(action, "remove")) {
                if ((r = device_process_removed_device(m, dev)) < 0) {
                        log_error("Failed to process udev device event: %s", strerror(-r));
                        goto fail;
                }
        } else {
                if ((r = device_process_new_device(m, dev, true)) < 0) {
                        log_error("Failed to process udev device event: %s", strerror(-r));
                        goto fail;
                }
        }

fail:
        udev_device_unref(dev);
}

453
454
static const char* const device_state_table[_DEVICE_STATE_MAX] = {
        [DEVICE_DEAD] = "dead",
455
        [DEVICE_PLUGGED] = "plugged"
456
457
458
459
};

DEFINE_STRING_TABLE_LOOKUP(device_state, DeviceState);

Lennart Poettering's avatar
Lennart Poettering committed
460
const UnitVTable device_vtable = {
461
462
        .suffix = ".device",

463
464
        .no_requires = true,
        .no_instances = true,
465
        .no_snapshots = true,
466
        .no_isolate = true,
467

468
469
        .init = device_init,

470
        .load = unit_load_fragment_and_dropin_optional,
471
        .done = device_done,
Lennart Poettering's avatar
Lennart Poettering committed
472
473
        .coldplug = device_coldplug,

474
475
        .dump = device_dump,

Lennart Poettering's avatar
Lennart Poettering committed
476
        .active_state = device_active_state,
477
        .sub_state_to_string = device_sub_state_to_string,
478

479
480
        .bus_message_handler = bus_device_message_handler,

Lennart Poettering's avatar
Lennart Poettering committed
481
482
        .enumerate = device_enumerate,
        .shutdown = device_shutdown
483
};