1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
3 #include <errno.h>
4 
5 #include "alloc-util.h"
6 #include "bus-error.h"
7 #include "bus-util.h"
8 #include "device-private.h"
9 #include "path-util.h"
10 #include "udevadm-util.h"
11 #include "unit-name.h"
12 
find_device_from_path(const char * path,sd_device ** ret)13 static int find_device_from_path(const char *path, sd_device **ret) {
14         if (path_startswith(path, "/sys/"))
15                 return sd_device_new_from_syspath(ret, path);
16 
17         if (path_startswith(path, "/dev/")) {
18                 struct stat st;
19 
20                 if (stat(path, &st) < 0)
21                         return -errno;
22 
23                 return sd_device_new_from_stat_rdev(ret, &st);
24         }
25 
26         return -EINVAL;
27 }
28 
find_device_from_unit(const char * unit_name,sd_device ** ret)29 static int find_device_from_unit(const char *unit_name, sd_device **ret) {
30         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
31         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
32         _cleanup_free_ char *unit_path = NULL, *syspath = NULL;
33         int r;
34 
35         if (!unit_name_is_valid(unit_name, UNIT_NAME_PLAIN))
36                 return -EINVAL;
37 
38         if (unit_name_to_type(unit_name) != UNIT_DEVICE)
39                 return -EINVAL;
40 
41         r = bus_connect_system_systemd(&bus);
42         if (r < 0) {
43                 _cleanup_free_ char *path = NULL;
44 
45                 log_debug_errno(r, "Failed to open connection to systemd, using unit name as syspath: %m");
46 
47                 r = unit_name_to_path(unit_name, &path);
48                 if (r < 0)
49                         return log_debug_errno(r, "Failed to convert \"%s\" to a device path: %m", unit_name);
50 
51                 return find_device_from_path(path, ret);
52         }
53 
54         unit_path = unit_dbus_path_from_name(unit_name);
55         if (!unit_path)
56                 return -ENOMEM;
57 
58         r = sd_bus_get_property_string(
59                         bus,
60                         "org.freedesktop.systemd1",
61                         unit_path,
62                         "org.freedesktop.systemd1.Device",
63                         "SysFSPath",
64                         &error,
65                         &syspath);
66         if (r < 0)
67                 return log_debug_errno(r, "Failed to get SysFSPath= dbus property for %s: %s",
68                                        unit_name, bus_error_message(&error, r));
69 
70         return sd_device_new_from_syspath(ret, syspath);
71 }
72 
find_device(const char * id,const char * prefix,sd_device ** ret)73 int find_device(const char *id, const char *prefix, sd_device **ret) {
74         assert(id);
75         assert(ret);
76 
77         if (find_device_from_path(id, ret) >= 0)
78                 return 0;
79 
80         if (prefix && !path_startswith(id, prefix)) {
81                 _cleanup_free_ char *path = NULL;
82 
83                 path = path_join(prefix, id);
84                 if (!path)
85                         return -ENOMEM;
86 
87                 if (find_device_from_path(path, ret) >= 0)
88                         return 0;
89         }
90 
91         /* Check if the argument looks like a device unit name. */
92         return find_device_from_unit(id, ret);
93 }
94 
find_device_with_action(const char * id,sd_device_action_t action,sd_device ** ret)95 int find_device_with_action(const char *id, sd_device_action_t action, sd_device **ret) {
96         _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
97         int r;
98 
99         assert(id);
100         assert(ret);
101         assert(action >= 0 && action < _SD_DEVICE_ACTION_MAX);
102 
103         r = find_device(id, "/sys", &dev);
104         if (r < 0)
105                 return r;
106 
107         r = device_read_uevent_file(dev);
108         if (r < 0)
109                 return r;
110 
111         r = device_set_action(dev, action);
112         if (r < 0)
113                 return r;
114 
115         *ret = TAKE_PTR(dev);
116         return 0;
117 }
118 
parse_device_action(const char * str,sd_device_action_t * action)119 int parse_device_action(const char *str, sd_device_action_t *action) {
120         sd_device_action_t a;
121 
122         assert(str);
123         assert(action);
124 
125         if (streq(str, "help")) {
126                 dump_device_action_table();
127                 return 0;
128         }
129 
130         a = device_action_from_string(str);
131         if (a < 0)
132                 return a;
133 
134         *action = a;
135         return 1;
136 }
137