]>
Commit | Line | Data |
---|---|---|
53e1b683 | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
57fa1d09 TG |
2 | |
3 | #include <ctype.h> | |
57fa1d09 | 4 | #include <net/if.h> |
07630cea | 5 | #include <sys/types.h> |
57fa1d09 TG |
6 | |
7 | #include "sd-device.h" | |
8 | ||
b5efdb8a | 9 | #include "alloc-util.h" |
57fa1d09 TG |
10 | #include "device-internal.h" |
11 | #include "device-private.h" | |
07630cea | 12 | #include "device-util.h" |
3ffd4af2 | 13 | #include "fd-util.h" |
07630cea | 14 | #include "fileio.h" |
f4f15635 | 15 | #include "fs-util.h" |
07630cea LP |
16 | #include "hashmap.h" |
17 | #include "macro.h" | |
18 | #include "mkdir.h" | |
6bedfcbb | 19 | #include "parse-util.h" |
07630cea | 20 | #include "path-util.h" |
07630cea | 21 | #include "set.h" |
8b43440b | 22 | #include "string-table.h" |
07630cea LP |
23 | #include "string-util.h" |
24 | #include "strv.h" | |
25 | #include "strxcpyx.h" | |
e4de7287 | 26 | #include "tmpfile-util.h" |
ee104e11 | 27 | #include "user-util.h" |
07630cea | 28 | #include "util.h" |
57fa1d09 TG |
29 | |
30 | int device_add_property(sd_device *device, const char *key, const char *value) { | |
31 | int r; | |
32 | ||
33 | assert(device); | |
34 | assert(key); | |
35 | ||
36 | r = device_add_property_aux(device, key, value, false); | |
37 | if (r < 0) | |
38 | return r; | |
39 | ||
40 | if (key[0] != '.') { | |
41 | r = device_add_property_aux(device, key, value, true); | |
42 | if (r < 0) | |
43 | return r; | |
44 | } | |
45 | ||
46 | return 0; | |
47 | } | |
48 | ||
57fa1d09 TG |
49 | void device_set_devlink_priority(sd_device *device, int priority) { |
50 | assert(device); | |
51 | ||
52 | device->devlink_priority = priority; | |
53 | } | |
54 | ||
55 | void device_set_is_initialized(sd_device *device) { | |
56 | assert(device); | |
57 | ||
58 | device->is_initialized = true; | |
59 | } | |
60 | ||
61 | int device_ensure_usec_initialized(sd_device *device, sd_device *device_old) { | |
dc5042c0 | 62 | usec_t when; |
57fa1d09 TG |
63 | |
64 | assert(device); | |
65 | ||
66 | if (device_old && device_old->usec_initialized > 0) | |
dc5042c0 | 67 | when = device_old->usec_initialized; |
57fa1d09 | 68 | else |
dc5042c0 | 69 | when = now(CLOCK_MONOTONIC); |
57fa1d09 | 70 | |
dc5042c0 | 71 | return device_set_usec_initialized(device, when); |
57fa1d09 TG |
72 | } |
73 | ||
57fa1d09 TG |
74 | uint64_t device_get_properties_generation(sd_device *device) { |
75 | assert(device); | |
76 | ||
77 | return device->properties_generation; | |
78 | } | |
79 | ||
80 | uint64_t device_get_tags_generation(sd_device *device) { | |
81 | assert(device); | |
82 | ||
83 | return device->tags_generation; | |
84 | } | |
85 | ||
86 | uint64_t device_get_devlinks_generation(sd_device *device) { | |
87 | assert(device); | |
88 | ||
89 | return device->devlinks_generation; | |
90 | } | |
91 | ||
92 | int device_get_devnode_mode(sd_device *device, mode_t *mode) { | |
93 | int r; | |
94 | ||
95 | assert(device); | |
57fa1d09 TG |
96 | |
97 | r = device_read_db(device); | |
98 | if (r < 0) | |
99 | return r; | |
100 | ||
dcfbde3a YW |
101 | if (device->devmode == (mode_t) -1) |
102 | return -ENOENT; | |
103 | ||
78ffb476 YW |
104 | if (mode) |
105 | *mode = device->devmode; | |
57fa1d09 TG |
106 | |
107 | return 0; | |
108 | } | |
109 | ||
110 | int device_get_devnode_uid(sd_device *device, uid_t *uid) { | |
111 | int r; | |
112 | ||
113 | assert(device); | |
57fa1d09 TG |
114 | |
115 | r = device_read_db(device); | |
116 | if (r < 0) | |
117 | return r; | |
118 | ||
dcfbde3a YW |
119 | if (device->devuid == (uid_t) -1) |
120 | return -ENOENT; | |
121 | ||
78ffb476 YW |
122 | if (uid) |
123 | *uid = device->devuid; | |
57fa1d09 TG |
124 | |
125 | return 0; | |
126 | } | |
127 | ||
128 | static int device_set_devuid(sd_device *device, const char *uid) { | |
129 | unsigned u; | |
130 | int r; | |
131 | ||
132 | assert(device); | |
133 | assert(uid); | |
134 | ||
135 | r = safe_atou(uid, &u); | |
136 | if (r < 0) | |
137 | return r; | |
138 | ||
139 | r = device_add_property_internal(device, "DEVUID", uid); | |
140 | if (r < 0) | |
141 | return r; | |
142 | ||
143 | device->devuid = u; | |
144 | ||
145 | return 0; | |
146 | } | |
147 | ||
148 | int device_get_devnode_gid(sd_device *device, gid_t *gid) { | |
149 | int r; | |
150 | ||
151 | assert(device); | |
57fa1d09 TG |
152 | |
153 | r = device_read_db(device); | |
154 | if (r < 0) | |
155 | return r; | |
156 | ||
dcfbde3a YW |
157 | if (device->devgid == (gid_t) -1) |
158 | return -ENOENT; | |
159 | ||
78ffb476 YW |
160 | if (gid) |
161 | *gid = device->devgid; | |
57fa1d09 TG |
162 | |
163 | return 0; | |
164 | } | |
165 | ||
166 | static int device_set_devgid(sd_device *device, const char *gid) { | |
167 | unsigned g; | |
168 | int r; | |
169 | ||
170 | assert(device); | |
171 | assert(gid); | |
172 | ||
173 | r = safe_atou(gid, &g); | |
174 | if (r < 0) | |
175 | return r; | |
176 | ||
177 | r = device_add_property_internal(device, "DEVGID", gid); | |
178 | if (r < 0) | |
179 | return r; | |
180 | ||
181 | device->devgid = g; | |
182 | ||
183 | return 0; | |
184 | } | |
185 | ||
401cb614 | 186 | static int device_amend(sd_device *device, const char *key, const char *value) { |
57fa1d09 TG |
187 | int r; |
188 | ||
189 | assert(device); | |
190 | assert(key); | |
191 | assert(value); | |
192 | ||
193 | if (streq(key, "DEVPATH")) { | |
194 | char *path; | |
195 | ||
196 | path = strjoina("/sys", value); | |
197 | ||
198 | /* the caller must verify or trust this data (e.g., if it comes from the kernel) */ | |
199 | r = device_set_syspath(device, path, false); | |
200 | if (r < 0) | |
c7d54dae | 201 | return log_device_debug_errno(device, r, "sd-device: Failed to set syspath to '%s': %m", path); |
57fa1d09 TG |
202 | } else if (streq(key, "SUBSYSTEM")) { |
203 | r = device_set_subsystem(device, value); | |
204 | if (r < 0) | |
c7d54dae | 205 | return log_device_debug_errno(device, r, "sd-device: Failed to set subsystem to '%s': %m", value); |
57fa1d09 TG |
206 | } else if (streq(key, "DEVTYPE")) { |
207 | r = device_set_devtype(device, value); | |
208 | if (r < 0) | |
c7d54dae | 209 | return log_device_debug_errno(device, r, "sd-device: Failed to set devtype to '%s': %m", value); |
57fa1d09 TG |
210 | } else if (streq(key, "DEVNAME")) { |
211 | r = device_set_devname(device, value); | |
212 | if (r < 0) | |
c7d54dae | 213 | return log_device_debug_errno(device, r, "sd-device: Failed to set devname to '%s': %m", value); |
57fa1d09 | 214 | } else if (streq(key, "USEC_INITIALIZED")) { |
dc5042c0 ZJS |
215 | usec_t t; |
216 | ||
217 | r = safe_atou64(value, &t); | |
218 | if (r < 0) | |
219 | return log_device_debug_errno(device, r, "sd-device: Failed to parse timestamp '%s': %m", value); | |
220 | ||
221 | r = device_set_usec_initialized(device, t); | |
57fa1d09 | 222 | if (r < 0) |
c7d54dae | 223 | return log_device_debug_errno(device, r, "sd-device: Failed to set usec-initialized to '%s': %m", value); |
57fa1d09 TG |
224 | } else if (streq(key, "DRIVER")) { |
225 | r = device_set_driver(device, value); | |
226 | if (r < 0) | |
c7d54dae | 227 | return log_device_debug_errno(device, r, "sd-device: Failed to set driver to '%s': %m", value); |
57fa1d09 TG |
228 | } else if (streq(key, "IFINDEX")) { |
229 | r = device_set_ifindex(device, value); | |
230 | if (r < 0) | |
c7d54dae | 231 | return log_device_debug_errno(device, r, "sd-device: Failed to set ifindex to '%s': %m", value); |
57fa1d09 TG |
232 | } else if (streq(key, "DEVMODE")) { |
233 | r = device_set_devmode(device, value); | |
234 | if (r < 0) | |
c7d54dae | 235 | return log_device_debug_errno(device, r, "sd-device: Failed to set devmode to '%s': %m", value); |
57fa1d09 TG |
236 | } else if (streq(key, "DEVUID")) { |
237 | r = device_set_devuid(device, value); | |
238 | if (r < 0) | |
c7d54dae | 239 | return log_device_debug_errno(device, r, "sd-device: Failed to set devuid to '%s': %m", value); |
57fa1d09 TG |
240 | } else if (streq(key, "DEVGID")) { |
241 | r = device_set_devgid(device, value); | |
242 | if (r < 0) | |
c7d54dae | 243 | return log_device_debug_errno(device, r, "sd-device: Failed to set devgid to '%s': %m", value); |
57fa1d09 | 244 | } else if (streq(key, "DEVLINKS")) { |
4df4fd11 TG |
245 | const char *word, *state; |
246 | size_t l; | |
57fa1d09 | 247 | |
4df4fd11 | 248 | FOREACH_WORD(word, l, value, state) { |
de9b34b6 | 249 | char devlink[l + 1]; |
57fa1d09 | 250 | |
de9b34b6 TG |
251 | strncpy(devlink, word, l); |
252 | devlink[l] = '\0'; | |
57fa1d09 | 253 | |
4df4fd11 | 254 | r = device_add_devlink(device, devlink); |
57fa1d09 | 255 | if (r < 0) |
c7d54dae | 256 | return log_device_debug_errno(device, r, "sd-device: Failed to add devlink '%s': %m", devlink); |
57fa1d09 TG |
257 | } |
258 | } else if (streq(key, "TAGS")) { | |
4df4fd11 TG |
259 | const char *word, *state; |
260 | size_t l; | |
57fa1d09 | 261 | |
4df4fd11 | 262 | FOREACH_WORD_SEPARATOR(word, l, value, ":", state) { |
de9b34b6 | 263 | char tag[l + 1]; |
57fa1d09 | 264 | |
db2f8a2e | 265 | (void) strncpy(tag, word, l); |
de9b34b6 | 266 | tag[l] = '\0'; |
57fa1d09 | 267 | |
4df4fd11 | 268 | r = device_add_tag(device, tag); |
57fa1d09 | 269 | if (r < 0) |
c7d54dae | 270 | return log_device_debug_errno(device, r, "sd-device: Failed to add tag '%s': %m", tag); |
57fa1d09 TG |
271 | } |
272 | } else { | |
273 | r = device_add_property_internal(device, key, value); | |
274 | if (r < 0) | |
c7d54dae | 275 | return log_device_debug_errno(device, r, "sd-device: Failed to add property '%s=%s': %m", key, value); |
57fa1d09 TG |
276 | } |
277 | ||
278 | return 0; | |
279 | } | |
280 | ||
281 | static const char* const device_action_table[_DEVICE_ACTION_MAX] = { | |
282 | [DEVICE_ACTION_ADD] = "add", | |
283 | [DEVICE_ACTION_REMOVE] = "remove", | |
284 | [DEVICE_ACTION_CHANGE] = "change", | |
285 | [DEVICE_ACTION_MOVE] = "move", | |
286 | [DEVICE_ACTION_ONLINE] = "online", | |
287 | [DEVICE_ACTION_OFFLINE] = "offline", | |
9a39e1ce LP |
288 | [DEVICE_ACTION_BIND] = "bind", |
289 | [DEVICE_ACTION_UNBIND] = "unbind", | |
57fa1d09 TG |
290 | }; |
291 | ||
292 | DEFINE_STRING_TABLE_LOOKUP(device_action, DeviceAction); | |
293 | ||
294 | static int device_append(sd_device *device, char *key, const char **_major, const char **_minor, uint64_t *_seqnum, | |
295 | DeviceAction *_action) { | |
296 | DeviceAction action = _DEVICE_ACTION_INVALID; | |
297 | uint64_t seqnum = 0; | |
298 | const char *major = NULL, *minor = NULL; | |
299 | char *value; | |
300 | int r; | |
301 | ||
302 | assert(device); | |
303 | assert(key); | |
304 | assert(_major); | |
305 | assert(_minor); | |
306 | assert(_seqnum); | |
307 | assert(_action); | |
308 | ||
309 | value = strchr(key, '='); | |
310 | if (!value) { | |
c7d54dae | 311 | log_device_debug(device, "sd-device: Not a key-value pair: '%s'", key); |
57fa1d09 TG |
312 | return -EINVAL; |
313 | } | |
314 | ||
315 | *value = '\0'; | |
316 | ||
317 | value++; | |
318 | ||
319 | if (streq(key, "MAJOR")) | |
320 | major = value; | |
321 | else if (streq(key, "MINOR")) | |
322 | minor = value; | |
323 | else { | |
324 | if (streq(key, "ACTION")) { | |
325 | action = device_action_from_string(value); | |
326 | if (action == _DEVICE_ACTION_INVALID) | |
327 | return -EINVAL; | |
328 | } else if (streq(key, "SEQNUM")) { | |
329 | r = safe_atou64(value, &seqnum); | |
330 | if (r < 0) | |
331 | return r; | |
332 | else if (seqnum == 0) | |
333 | /* kernel only sends seqnum > 0 */ | |
334 | return -EINVAL; | |
335 | } | |
336 | ||
401cb614 | 337 | r = device_amend(device, key, value); |
57fa1d09 TG |
338 | if (r < 0) |
339 | return r; | |
340 | } | |
341 | ||
342 | if (major != 0) | |
343 | *_major = major; | |
344 | ||
345 | if (minor != 0) | |
346 | *_minor = minor; | |
347 | ||
348 | if (action != _DEVICE_ACTION_INVALID) | |
349 | *_action = action; | |
350 | ||
351 | if (seqnum > 0) | |
352 | *_seqnum = seqnum; | |
353 | ||
354 | return 0; | |
355 | } | |
356 | ||
357 | void device_seal(sd_device *device) { | |
358 | assert(device); | |
359 | ||
360 | device->sealed = true; | |
361 | } | |
362 | ||
363 | static int device_verify(sd_device *device, DeviceAction action, uint64_t seqnum) { | |
364 | assert(device); | |
365 | ||
366 | if (!device->devpath || !device->subsystem || action == _DEVICE_ACTION_INVALID || seqnum == 0) { | |
c7d54dae | 367 | log_device_debug(device, "sd-device: Device created from strv or nulstr lacks devpath, subsystem, action or seqnum."); |
57fa1d09 TG |
368 | return -EINVAL; |
369 | } | |
370 | ||
371 | device->sealed = true; | |
372 | ||
373 | return 0; | |
374 | } | |
375 | ||
376 | int device_new_from_strv(sd_device **ret, char **strv) { | |
4afd3348 | 377 | _cleanup_(sd_device_unrefp) sd_device *device = NULL; |
57fa1d09 TG |
378 | char **key; |
379 | const char *major = NULL, *minor = NULL; | |
380 | DeviceAction action = _DEVICE_ACTION_INVALID; | |
8d578a2e | 381 | uint64_t seqnum = 0; |
57fa1d09 TG |
382 | int r; |
383 | ||
384 | assert(ret); | |
385 | assert(strv); | |
386 | ||
387 | r = device_new_aux(&device); | |
388 | if (r < 0) | |
389 | return r; | |
390 | ||
391 | STRV_FOREACH(key, strv) { | |
392 | r = device_append(device, *key, &major, &minor, &seqnum, &action); | |
393 | if (r < 0) | |
394 | return r; | |
395 | } | |
396 | ||
397 | if (major) { | |
398 | r = device_set_devnum(device, major, minor); | |
399 | if (r < 0) | |
c7d54dae | 400 | return log_device_debug_errno(device, r, "sd-device: Failed to set devnum %s:%s: %m", major, minor); |
57fa1d09 TG |
401 | } |
402 | ||
403 | r = device_verify(device, action, seqnum); | |
404 | if (r < 0) | |
405 | return r; | |
406 | ||
1cc6c93a | 407 | *ret = TAKE_PTR(device); |
57fa1d09 TG |
408 | |
409 | return 0; | |
410 | } | |
411 | ||
412 | int device_new_from_nulstr(sd_device **ret, uint8_t *nulstr, size_t len) { | |
4afd3348 | 413 | _cleanup_(sd_device_unrefp) sd_device *device = NULL; |
57fa1d09 TG |
414 | const char *major = NULL, *minor = NULL; |
415 | DeviceAction action = _DEVICE_ACTION_INVALID; | |
8d578a2e | 416 | uint64_t seqnum = 0; |
57fa1d09 TG |
417 | unsigned i = 0; |
418 | int r; | |
419 | ||
420 | assert(ret); | |
421 | assert(nulstr); | |
422 | assert(len); | |
423 | ||
424 | r = device_new_aux(&device); | |
425 | if (r < 0) | |
426 | return r; | |
427 | ||
428 | while (i < len) { | |
429 | char *key; | |
430 | const char *end; | |
431 | ||
432 | key = (char*)&nulstr[i]; | |
433 | end = memchr(key, '\0', len - i); | |
434 | if (!end) { | |
c7d54dae | 435 | log_device_debug(device, "sd-device: Failed to parse nulstr"); |
57fa1d09 TG |
436 | return -EINVAL; |
437 | } | |
438 | i += end - key + 1; | |
439 | ||
440 | r = device_append(device, key, &major, &minor, &seqnum, &action); | |
441 | if (r < 0) | |
442 | return r; | |
443 | } | |
444 | ||
445 | if (major) { | |
446 | r = device_set_devnum(device, major, minor); | |
447 | if (r < 0) | |
c7d54dae | 448 | return log_device_debug_errno(device, r, "sd-device: Failed to set devnum %s:%s: %m", major, minor); |
57fa1d09 TG |
449 | } |
450 | ||
451 | r = device_verify(device, action, seqnum); | |
452 | if (r < 0) | |
453 | return r; | |
454 | ||
1cc6c93a | 455 | *ret = TAKE_PTR(device); |
57fa1d09 TG |
456 | |
457 | return 0; | |
458 | } | |
459 | ||
460 | static int device_update_properties_bufs(sd_device *device) { | |
461 | const char *val, *prop; | |
ccc1002a TG |
462 | _cleanup_free_ char **buf_strv = NULL; |
463 | _cleanup_free_ uint8_t *buf_nulstr = NULL; | |
d854ba50 | 464 | size_t allocated_nulstr = 0; |
ccc1002a | 465 | size_t nulstr_len = 0, num = 0, i = 0; |
57fa1d09 TG |
466 | |
467 | assert(device); | |
468 | ||
aa20f49a TG |
469 | if (!device->properties_buf_outdated) |
470 | return 0; | |
471 | ||
57fa1d09 TG |
472 | FOREACH_DEVICE_PROPERTY(device, prop, val) { |
473 | size_t len = 0; | |
474 | ||
475 | len = strlen(prop) + 1 + strlen(val); | |
476 | ||
477 | buf_nulstr = GREEDY_REALLOC0(buf_nulstr, allocated_nulstr, nulstr_len + len + 2); | |
478 | if (!buf_nulstr) | |
479 | return -ENOMEM; | |
480 | ||
57fa1d09 TG |
481 | strscpyl((char *)buf_nulstr + nulstr_len, len + 1, prop, "=", val, NULL); |
482 | nulstr_len += len + 1; | |
d854ba50 | 483 | ++num; |
57fa1d09 TG |
484 | } |
485 | ||
ccc1002a TG |
486 | /* build buf_strv from buf_nulstr */ |
487 | buf_strv = new0(char *, num + 1); | |
488 | if (!buf_strv) | |
489 | return -ENOMEM; | |
d854ba50 | 490 | |
d854ba50 | 491 | NULSTR_FOREACH(val, (char*) buf_nulstr) { |
ccc1002a | 492 | buf_strv[i] = (char *) val; |
d854ba50 MP |
493 | assert(i < num); |
494 | i++; | |
495 | } | |
57fa1d09 | 496 | |
f9ecfd3b | 497 | free_and_replace(device->properties_nulstr, buf_nulstr); |
ccc1002a | 498 | device->properties_nulstr_len = nulstr_len; |
f9ecfd3b | 499 | free_and_replace(device->properties_strv, buf_strv); |
ccc1002a | 500 | |
57fa1d09 TG |
501 | device->properties_buf_outdated = false; |
502 | ||
503 | return 0; | |
504 | } | |
505 | ||
506 | int device_get_properties_nulstr(sd_device *device, const uint8_t **nulstr, size_t *len) { | |
507 | int r; | |
508 | ||
509 | assert(device); | |
510 | assert(nulstr); | |
511 | assert(len); | |
512 | ||
aa20f49a TG |
513 | r = device_update_properties_bufs(device); |
514 | if (r < 0) | |
515 | return r; | |
57fa1d09 TG |
516 | |
517 | *nulstr = device->properties_nulstr; | |
518 | *len = device->properties_nulstr_len; | |
519 | ||
520 | return 0; | |
521 | } | |
522 | ||
523 | int device_get_properties_strv(sd_device *device, char ***strv) { | |
524 | int r; | |
525 | ||
526 | assert(device); | |
527 | assert(strv); | |
528 | ||
529 | r = device_update_properties_bufs(device); | |
530 | if (r < 0) | |
531 | return r; | |
532 | ||
533 | *strv = device->properties_strv; | |
534 | ||
535 | return 0; | |
536 | } | |
537 | ||
538 | int device_get_devlink_priority(sd_device *device, int *priority) { | |
539 | int r; | |
540 | ||
541 | assert(device); | |
542 | assert(priority); | |
543 | ||
544 | r = device_read_db(device); | |
545 | if (r < 0) | |
546 | return r; | |
547 | ||
548 | *priority = device->devlink_priority; | |
549 | ||
550 | return 0; | |
551 | } | |
552 | ||
553 | int device_get_watch_handle(sd_device *device, int *handle) { | |
554 | int r; | |
555 | ||
556 | assert(device); | |
57fa1d09 TG |
557 | |
558 | r = device_read_db(device); | |
559 | if (r < 0) | |
560 | return r; | |
561 | ||
dcfbde3a YW |
562 | if (device->watch_handle < 0) |
563 | return -ENOENT; | |
564 | ||
78ffb476 YW |
565 | if (handle) |
566 | *handle = device->watch_handle; | |
57fa1d09 TG |
567 | |
568 | return 0; | |
569 | } | |
570 | ||
571 | void device_set_watch_handle(sd_device *device, int handle) { | |
572 | assert(device); | |
573 | ||
574 | device->watch_handle = handle; | |
575 | } | |
576 | ||
577 | int device_rename(sd_device *device, const char *name) { | |
578 | _cleanup_free_ char *dirname = NULL; | |
579 | char *new_syspath; | |
580 | const char *interface; | |
581 | int r; | |
582 | ||
583 | assert(device); | |
584 | assert(name); | |
585 | ||
586 | dirname = dirname_malloc(device->syspath); | |
587 | if (!dirname) | |
588 | return -ENOMEM; | |
589 | ||
590 | new_syspath = strjoina(dirname, "/", name); | |
591 | ||
592 | /* the user must trust that the new name is correct */ | |
593 | r = device_set_syspath(device, new_syspath, false); | |
594 | if (r < 0) | |
595 | return r; | |
596 | ||
597 | r = sd_device_get_property_value(device, "INTERFACE", &interface); | |
598 | if (r >= 0) { | |
f7b1c8d1 EV |
599 | /* like DEVPATH_OLD, INTERFACE_OLD is not saved to the db, but only stays around for the current event */ |
600 | r = device_add_property_internal(device, "INTERFACE_OLD", interface); | |
57fa1d09 TG |
601 | if (r < 0) |
602 | return r; | |
603 | ||
f7b1c8d1 | 604 | r = device_add_property_internal(device, "INTERFACE", name); |
57fa1d09 TG |
605 | if (r < 0) |
606 | return r; | |
607 | } else if (r != -ENOENT) | |
608 | return r; | |
609 | ||
610 | return 0; | |
611 | } | |
612 | ||
613 | int device_shallow_clone(sd_device *old_device, sd_device **new_device) { | |
4afd3348 | 614 | _cleanup_(sd_device_unrefp) sd_device *ret = NULL; |
57fa1d09 TG |
615 | int r; |
616 | ||
617 | assert(old_device); | |
618 | assert(new_device); | |
619 | ||
620 | r = device_new_aux(&ret); | |
621 | if (r < 0) | |
622 | return r; | |
623 | ||
624 | r = device_set_syspath(ret, old_device->syspath, false); | |
625 | if (r < 0) | |
626 | return r; | |
627 | ||
628 | r = device_set_subsystem(ret, old_device->subsystem); | |
629 | if (r < 0) | |
630 | return r; | |
631 | ||
632 | ret->devnum = old_device->devnum; | |
633 | ||
1cc6c93a | 634 | *new_device = TAKE_PTR(ret); |
57fa1d09 TG |
635 | |
636 | return 0; | |
637 | } | |
638 | ||
639 | int device_clone_with_db(sd_device *old_device, sd_device **new_device) { | |
4afd3348 | 640 | _cleanup_(sd_device_unrefp) sd_device *ret = NULL; |
57fa1d09 TG |
641 | int r; |
642 | ||
643 | assert(old_device); | |
644 | assert(new_device); | |
645 | ||
646 | r = device_shallow_clone(old_device, &ret); | |
647 | if (r < 0) | |
648 | return r; | |
649 | ||
650 | r = device_read_db(ret); | |
651 | if (r < 0) | |
652 | return r; | |
653 | ||
654 | ret->sealed = true; | |
655 | ||
1cc6c93a | 656 | *new_device = TAKE_PTR(ret); |
57fa1d09 TG |
657 | |
658 | return 0; | |
659 | } | |
660 | ||
661 | int device_new_from_synthetic_event(sd_device **new_device, const char *syspath, const char *action) { | |
4afd3348 | 662 | _cleanup_(sd_device_unrefp) sd_device *ret = NULL; |
57fa1d09 TG |
663 | int r; |
664 | ||
665 | assert(new_device); | |
666 | assert(syspath); | |
667 | assert(action); | |
668 | ||
669 | r = sd_device_new_from_syspath(&ret, syspath); | |
670 | if (r < 0) | |
671 | return r; | |
672 | ||
673 | r = device_read_uevent_file(ret); | |
674 | if (r < 0) | |
675 | return r; | |
676 | ||
677 | r = device_add_property_internal(ret, "ACTION", action); | |
678 | if (r < 0) | |
679 | return r; | |
680 | ||
1cc6c93a | 681 | *new_device = TAKE_PTR(ret); |
57fa1d09 TG |
682 | |
683 | return 0; | |
684 | } | |
685 | ||
ad5944d7 YW |
686 | int device_new_from_stat_rdev(sd_device **ret, const struct stat *st) { |
687 | char type; | |
688 | ||
689 | assert(ret); | |
690 | assert(st); | |
691 | ||
692 | if (S_ISBLK(st->st_mode)) | |
693 | type = 'b'; | |
694 | else if (S_ISCHR(st->st_mode)) | |
695 | type = 'c'; | |
696 | else | |
697 | return -ENOTTY; | |
698 | ||
699 | return sd_device_new_from_devnum(ret, type, st->st_rdev); | |
700 | } | |
701 | ||
57fa1d09 TG |
702 | int device_copy_properties(sd_device *device_dst, sd_device *device_src) { |
703 | const char *property, *value; | |
a3ce8136 | 704 | Iterator i; |
57fa1d09 TG |
705 | int r; |
706 | ||
707 | assert(device_dst); | |
708 | assert(device_src); | |
709 | ||
a3ce8136 YW |
710 | r = device_properties_prepare(device_src); |
711 | if (r < 0) | |
712 | return r; | |
713 | ||
5ce41697 | 714 | ORDERED_HASHMAP_FOREACH_KEY(value, property, device_src->properties_db, i) { |
a3ce8136 YW |
715 | r = device_add_property_aux(device_dst, property, value, true); |
716 | if (r < 0) | |
717 | return r; | |
718 | } | |
719 | ||
5ce41697 | 720 | ORDERED_HASHMAP_FOREACH_KEY(value, property, device_src->properties, i) { |
a3ce8136 | 721 | r = device_add_property_aux(device_dst, property, value, false); |
57fa1d09 TG |
722 | if (r < 0) |
723 | return r; | |
724 | } | |
725 | ||
726 | return 0; | |
727 | } | |
728 | ||
729 | void device_cleanup_tags(sd_device *device) { | |
730 | assert(device); | |
731 | ||
732 | set_free_free(device->tags); | |
733 | device->tags = NULL; | |
734 | device->property_tags_outdated = true; | |
313cefa1 | 735 | device->tags_generation++; |
57fa1d09 TG |
736 | } |
737 | ||
738 | void device_cleanup_devlinks(sd_device *device) { | |
739 | assert(device); | |
740 | ||
741 | set_free_free(device->devlinks); | |
742 | device->devlinks = NULL; | |
743 | device->property_devlinks_outdated = true; | |
313cefa1 | 744 | device->devlinks_generation++; |
57fa1d09 TG |
745 | } |
746 | ||
747 | void device_remove_tag(sd_device *device, const char *tag) { | |
748 | assert(device); | |
749 | assert(tag); | |
750 | ||
751 | free(set_remove(device->tags, tag)); | |
752 | device->property_tags_outdated = true; | |
313cefa1 | 753 | device->tags_generation++; |
57fa1d09 TG |
754 | } |
755 | ||
756 | static int device_tag(sd_device *device, const char *tag, bool add) { | |
757 | const char *id; | |
758 | char *path; | |
759 | int r; | |
760 | ||
761 | assert(device); | |
762 | assert(tag); | |
763 | ||
764 | r = device_get_id_filename(device, &id); | |
765 | if (r < 0) | |
766 | return r; | |
767 | ||
768 | path = strjoina("/run/udev/tags/", tag, "/", id); | |
769 | ||
770 | if (add) { | |
771 | r = touch_file(path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, 0444); | |
772 | if (r < 0) | |
773 | return r; | |
774 | } else { | |
775 | r = unlink(path); | |
776 | if (r < 0 && errno != ENOENT) | |
777 | return -errno; | |
778 | } | |
779 | ||
780 | return 0; | |
781 | } | |
782 | ||
783 | int device_tag_index(sd_device *device, sd_device *device_old, bool add) { | |
784 | const char *tag; | |
785 | int r = 0, k; | |
786 | ||
787 | if (add && device_old) { | |
788 | /* delete possible left-over tags */ | |
789 | FOREACH_DEVICE_TAG(device_old, tag) { | |
790 | if (!sd_device_has_tag(device, tag)) { | |
791 | k = device_tag(device_old, tag, false); | |
792 | if (r >= 0 && k < 0) | |
793 | r = k; | |
794 | } | |
795 | } | |
796 | } | |
797 | ||
798 | FOREACH_DEVICE_TAG(device, tag) { | |
799 | k = device_tag(device, tag, add); | |
800 | if (r >= 0 && k < 0) | |
801 | r = k; | |
802 | } | |
803 | ||
804 | return r; | |
805 | } | |
806 | ||
807 | static bool device_has_info(sd_device *device) { | |
808 | assert(device); | |
809 | ||
810 | if (!set_isempty(device->devlinks)) | |
811 | return true; | |
812 | ||
813 | if (device->devlink_priority != 0) | |
814 | return true; | |
815 | ||
816 | if (!ordered_hashmap_isempty(device->properties_db)) | |
817 | return true; | |
818 | ||
819 | if (!set_isempty(device->tags)) | |
820 | return true; | |
821 | ||
822 | if (device->watch_handle >= 0) | |
823 | return true; | |
824 | ||
825 | return false; | |
826 | } | |
827 | ||
828 | void device_set_db_persist(sd_device *device) { | |
829 | assert(device); | |
830 | ||
831 | device->db_persist = true; | |
832 | } | |
833 | ||
834 | int device_update_db(sd_device *device) { | |
835 | const char *id; | |
836 | char *path; | |
837 | _cleanup_fclose_ FILE *f = NULL; | |
838 | _cleanup_free_ char *path_tmp = NULL; | |
839 | bool has_info; | |
840 | int r; | |
841 | ||
842 | assert(device); | |
843 | ||
844 | has_info = device_has_info(device); | |
845 | ||
846 | r = device_get_id_filename(device, &id); | |
847 | if (r < 0) | |
848 | return r; | |
849 | ||
850 | path = strjoina("/run/udev/data/", id); | |
851 | ||
852 | /* do not store anything for otherwise empty devices */ | |
853 | if (!has_info && major(device->devnum) == 0 && device->ifindex == 0) { | |
854 | r = unlink(path); | |
855 | if (r < 0 && errno != ENOENT) | |
856 | return -errno; | |
857 | ||
858 | return 0; | |
859 | } | |
860 | ||
861 | /* write a database file */ | |
862 | r = mkdir_parents(path, 0755); | |
863 | if (r < 0) | |
864 | return r; | |
865 | ||
866 | r = fopen_temporary(path, &f, &path_tmp); | |
867 | if (r < 0) | |
868 | return r; | |
869 | ||
870 | /* | |
871 | * set 'sticky' bit to indicate that we should not clean the | |
872 | * database when we transition from initramfs to the real root | |
873 | */ | |
874 | if (device->db_persist) { | |
875 | r = fchmod(fileno(f), 01644); | |
876 | if (r < 0) { | |
877 | r = -errno; | |
878 | goto fail; | |
879 | } | |
880 | } else { | |
881 | r = fchmod(fileno(f), 0644); | |
882 | if (r < 0) { | |
883 | r = -errno; | |
884 | goto fail; | |
885 | } | |
886 | } | |
887 | ||
888 | if (has_info) { | |
889 | const char *property, *value, *tag; | |
890 | Iterator i; | |
891 | ||
892 | if (major(device->devnum) > 0) { | |
893 | const char *devlink; | |
894 | ||
895 | FOREACH_DEVICE_DEVLINK(device, devlink) | |
fbd0b64f | 896 | fprintf(f, "S:%s\n", devlink + STRLEN("/dev/")); |
57fa1d09 TG |
897 | |
898 | if (device->devlink_priority != 0) | |
899 | fprintf(f, "L:%i\n", device->devlink_priority); | |
900 | ||
901 | if (device->watch_handle >= 0) | |
902 | fprintf(f, "W:%i\n", device->watch_handle); | |
903 | } | |
904 | ||
905 | if (device->usec_initialized > 0) | |
906 | fprintf(f, "I:"USEC_FMT"\n", device->usec_initialized); | |
907 | ||
908 | ORDERED_HASHMAP_FOREACH_KEY(value, property, device->properties_db, i) | |
909 | fprintf(f, "E:%s=%s\n", property, value); | |
910 | ||
911 | FOREACH_DEVICE_TAG(device, tag) | |
912 | fprintf(f, "G:%s\n", tag); | |
913 | } | |
914 | ||
915 | r = fflush_and_check(f); | |
916 | if (r < 0) | |
917 | goto fail; | |
918 | ||
919 | r = rename(path_tmp, path); | |
920 | if (r < 0) { | |
921 | r = -errno; | |
922 | goto fail; | |
923 | } | |
924 | ||
c7d54dae YW |
925 | log_device_debug(device, "sd-device: Created %s file '%s' for '%s'", has_info ? "db" : "empty", |
926 | path, device->devpath); | |
57fa1d09 TG |
927 | |
928 | return 0; | |
929 | ||
930 | fail: | |
dacd6cee LP |
931 | (void) unlink(path); |
932 | (void) unlink(path_tmp); | |
57fa1d09 | 933 | |
c7d54dae | 934 | return log_device_debug_errno(device, r, "sd-device: Failed to create %s file '%s' for '%s'", has_info ? "db" : "empty", path, device->devpath); |
57fa1d09 TG |
935 | } |
936 | ||
937 | int device_delete_db(sd_device *device) { | |
938 | const char *id; | |
939 | char *path; | |
940 | int r; | |
941 | ||
942 | assert(device); | |
943 | ||
944 | r = device_get_id_filename(device, &id); | |
945 | if (r < 0) | |
946 | return r; | |
947 | ||
948 | path = strjoina("/run/udev/data/", id); | |
949 | ||
950 | r = unlink(path); | |
951 | if (r < 0 && errno != ENOENT) | |
952 | return -errno; | |
953 | ||
954 | return 0; | |
955 | } |