1 /***************************************************************************
2 *
3 * devinfo.c : main file for libdevinfo-based device enumeration
4 *
5 * Copyright 2013 Garrett D'Amore <garrett@damore.org>
6 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
7 *
8 * Licensed under the Academic Free License version 2.1
9 *
10 **************************************************************************/
11
12 #ifdef HAVE_CONFIG_H
13 # include <config.h>
14 #endif
15
16 #include <stdio.h>
17 #include <string.h>
18 #include <libdevinfo.h>
19
20 #include "../osspec.h"
21 #include "../logger.h"
22 #include "../hald.h"
23 #include "../hald_dbus.h"
24 #include "../device_info.h"
25 #include "../util.h"
26 #include "../hald_runner.h"
27 #include "osspec_solaris.h"
28 #include "hotplug.h"
29 #include "devinfo.h"
30 #include "devinfo_pci.h"
31 #include "devinfo_storage.h"
32 #include "devinfo_ieee1394.h"
33 #include "devinfo_usb.h"
34 #include "devinfo_misc.h"
35 #include "devinfo_acpi.h"
36 #include "devinfo_cpu.h"
37
38 void devinfo_add_subtree(HalDevice *parent, di_node_t node, gboolean is_root);
39 HalDevice *devinfo_add_node(HalDevice *parent, di_node_t node);
40
41 void
devinfo_add(HalDevice * parent,gchar * path)42 devinfo_add(HalDevice *parent, gchar *path)
43 {
44 di_node_t root;
45
46 if (strcmp (path, "/") == 0) {
47 if ((root = di_init(path, DINFOCACHE)) == DI_NODE_NIL) {
48 HAL_INFO (("di_init() failed %d", errno));
49 return;
50 }
51 } else {
52 if ((root = di_init(path, DINFOCPYALL)) == DI_NODE_NIL) {
53 HAL_INFO (("di_init() failed %d", errno));
54 return;
55 }
56 }
57
58 devinfo_add_subtree(parent, root, TRUE);
59
60 di_fini (root);
61 }
62
63 void
devinfo_add_subtree(HalDevice * parent,di_node_t node,gboolean is_root)64 devinfo_add_subtree(HalDevice *parent, di_node_t node, gboolean is_root)
65 {
66 HalDevice *d;
67 di_node_t root_node, child_node;
68
69 HAL_INFO (("add_subtree: %s", di_node_name (node)));
70
71 root_node = node;
72 do {
73 d = devinfo_add_node (parent, node);
74
75 if ((d != NULL) &&
76 (child_node = di_child_node (node)) != DI_NODE_NIL) {
77 devinfo_add_subtree (d, child_node, FALSE);
78 }
79
80 node = di_sibling_node (node);
81 } while ((node != DI_NODE_NIL) &&
82 (!is_root || di_parent_node (node) == root_node));
83 }
84
85 void
devinfo_set_default_properties(HalDevice * d,HalDevice * parent,di_node_t node,char * devfs_path)86 devinfo_set_default_properties (HalDevice *d, HalDevice *parent, di_node_t node, char *devfs_path)
87 {
88 char *driver_name, *s;
89 const char *s1;
90 char udi[HAL_PATH_MAX];
91
92 if (parent != NULL) {
93 hal_device_property_set_string (d, "info.parent", hal_device_get_udi (parent));
94 } else {
95 hal_device_property_set_string (d, "info.parent", "/org/freedesktop/Hal/devices/local");
96 }
97
98 hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi),
99 "/org/freedesktop/Hal/devices%s_%d",
100 devfs_path,
101 di_instance (node));
102 hal_device_set_udi (d, udi);
103 hal_device_property_set_string (d, "info.udi", udi);
104
105 if (di_prop_lookup_strings (DDI_DEV_T_ANY, node, "model", &s) > 0) {
106 hal_device_property_set_string (d, "info.product", s);
107 } else {
108 hal_device_property_set_string (d, "info.product", di_node_name (node));
109 }
110
111 hal_device_property_set_string (d, "solaris.devfs_path", devfs_path);
112
113 if ((driver_name = di_driver_name (node)) != NULL) {
114 hal_device_property_set_string (d, "info.solaris.driver",
115 driver_name);
116 }
117
118
119 /* inherit parent's claim attributes */
120 if (hal_device_property_get_bool (parent, "info.claimed")) {
121 s1 = hal_device_property_get_string (parent, "info.claimed.service");
122 if (s1 != NULL) {
123 hal_device_property_set_bool (d, "info.claimed", TRUE);
124 hal_device_property_set_string (d, "info.claimed.service", s1);
125 }
126 }
127 }
128
129 /* device handlers, ordered specific to generic */
130 static DevinfoDevHandler *devinfo_handlers[] = {
131 &devinfo_computer_handler,
132 &devinfo_cpu_handler,
133 &devinfo_ide_handler,
134 &devinfo_scsi_handler,
135 &devinfo_blkdev_handler,
136 &devinfo_floppy_handler,
137 &devinfo_usb_handler,
138 &devinfo_ieee1394_handler,
139 &devinfo_lofi_handler,
140 &devinfo_acpi_handler,
141 &devinfo_power_button_handler,
142 &devinfo_keyboard_handler,
143 &devinfo_mouse_handler,
144 &devinfo_pci_handler,
145 &devinfo_default_handler,
146 NULL
147 };
148
149 HalDevice *
devinfo_add_node(HalDevice * parent,di_node_t node)150 devinfo_add_node(HalDevice *parent, di_node_t node)
151 {
152 HalDevice *d = NULL;
153 char *devfs_path;
154 char *device_type = NULL;
155 DevinfoDevHandler *handler;
156 int i;
157
158 devfs_path = di_devfs_path (node);
159
160 (void) di_prop_lookup_strings (DDI_DEV_T_ANY, node, "device_type",
161 &device_type);
162
163 for (i = 0; (d == NULL) && (devinfo_handlers[i] != NULL); i++) {
164 handler = devinfo_handlers[i];
165 d = handler->add (parent, node, devfs_path, device_type);
166 }
167
168 di_devfs_path_free(devfs_path);
169
170 HAL_INFO (("add_node: %s", d ? hal_device_get_udi (d) : "none"));
171 return (d);
172 }
173
174 void
devinfo_hotplug_enqueue(HalDevice * d,gchar * devfs_path,DevinfoDevHandler * handler,int action,int front)175 devinfo_hotplug_enqueue(HalDevice *d, gchar *devfs_path, DevinfoDevHandler *handler, int action, int front)
176 {
177 HotplugEvent *hotplug_event;
178
179 hotplug_event = g_new0 (HotplugEvent, 1);
180 hotplug_event->action = action;
181 hotplug_event->type = HOTPLUG_EVENT_DEVFS;
182 hotplug_event->d = d;
183 strlcpy (hotplug_event->un.devfs.devfs_path, devfs_path,
184 sizeof (hotplug_event->un.devfs.devfs_path));
185 hotplug_event->un.devfs.handler = handler;
186
187 hotplug_event_enqueue (hotplug_event, front);
188 }
189
190 void
devinfo_add_enqueue(HalDevice * d,gchar * devfs_path,DevinfoDevHandler * handler)191 devinfo_add_enqueue(HalDevice *d, gchar *devfs_path, DevinfoDevHandler *handler)
192 {
193 devinfo_hotplug_enqueue (d, devfs_path, handler, HOTPLUG_ACTION_ADD, 0);
194 }
195
196 void
devinfo_add_enqueue_at_front(HalDevice * d,gchar * devfs_path,DevinfoDevHandler * handler)197 devinfo_add_enqueue_at_front(HalDevice *d, gchar *devfs_path, DevinfoDevHandler *handler)
198 {
199 devinfo_hotplug_enqueue (d, devfs_path, handler, HOTPLUG_ACTION_ADD, 1);
200 }
201
202 void
devinfo_remove_enqueue(gchar * devfs_path,DevinfoDevHandler * handler)203 devinfo_remove_enqueue(gchar *devfs_path, DevinfoDevHandler *handler)
204 {
205 devinfo_hotplug_enqueue (NULL, devfs_path, handler, HOTPLUG_ACTION_REMOVE, 0);
206 }
207
208 void
devinfo_callouts_add_done(HalDevice * d,gpointer userdata1,gpointer userdata2)209 devinfo_callouts_add_done (HalDevice *d, gpointer userdata1, gpointer userdata2)
210 {
211 void *end_token = (void *) userdata1;
212
213 /* Move from temporary to global device store */
214 hal_device_store_remove (hald_get_tdl (), d);
215 hal_device_store_add (hald_get_gdl (), d);
216
217 hotplug_event_end (end_token);
218 }
219
220 void
devinfo_callouts_probing_done(HalDevice * d,guint32 exit_type,gint return_code,char ** error,gpointer userdata1,gpointer userdata2)221 devinfo_callouts_probing_done (HalDevice *d, guint32 exit_type, gint return_code, char **error, gpointer userdata1, gpointer userdata2)
222 {
223 void *end_token = (void *) userdata1;
224
225 /* Discard device if probing reports failure */
226 if (exit_type != HALD_RUN_SUCCESS || (return_code != 0)) {
227 HAL_INFO (("Probing for %s failed %d", hal_device_get_udi (d), return_code));
228 hal_device_store_remove (hald_get_tdl (), d);
229 g_object_unref (d);
230 hotplug_event_end (end_token);
231 return;
232 }
233
234 /* Merge properties from .fdi files */
235 di_search_and_merge (d, DEVICE_INFO_TYPE_INFORMATION);
236 di_search_and_merge (d, DEVICE_INFO_TYPE_POLICY);
237
238 hal_util_callout_device_add (d, devinfo_callouts_add_done, end_token, NULL);
239 }
240
241 void
devinfo_callouts_preprobing_done(HalDevice * d,gpointer userdata1,gpointer userdata2)242 devinfo_callouts_preprobing_done (HalDevice *d, gpointer userdata1, gpointer userdata2)
243 {
244 void *end_token = (void *) userdata1;
245 DevinfoDevHandler *handler = (DevinfoDevHandler *) userdata2;
246 void (*probing_done) (HalDevice *, guint32, gint, char **, gpointer, gpointer);
247 const gchar *prober;
248 int prober_timeout;
249
250 if (hal_device_property_get_bool (d, "info.ignore")) {
251 HAL_INFO (("Preprobing merged info.ignore==TRUE"));
252
253 /* Leave device with info.ignore==TRUE so we won't pick up children */
254 hal_device_property_remove (d, "info.category");
255 hal_device_property_remove (d, "info.capabilities");
256
257 hal_device_store_remove (hald_get_tdl (), d);
258 hal_device_store_add (hald_get_gdl (), d);
259
260 hotplug_event_end (end_token);
261 return;
262 }
263
264 if (handler != NULL && handler->get_prober != NULL) {
265 prober = handler->get_prober (d, &prober_timeout);
266 } else {
267 prober = NULL;
268 }
269
270 if (handler->probing_done != NULL) {
271 probing_done = handler->probing_done;
272 } else {
273 probing_done = devinfo_callouts_probing_done;
274 }
275
276 if (prober != NULL) {
277 /* probe the device */
278 HAL_INFO(("Probing udi=%s", hal_device_get_udi (d)));
279 hald_runner_run (d,
280 prober, NULL,
281 prober_timeout,
282 probing_done,
283 (gpointer) end_token, (gpointer) handler);
284 } else {
285 probing_done (d, 0, 0, NULL, userdata1, userdata2);
286 }
287 }
288
289 /* This is the beginning of hotplug even handling */
290 void
hotplug_event_begin_add_devinfo(HalDevice * d,HalDevice * parent,DevinfoDevHandler * handler,void * end_token)291 hotplug_event_begin_add_devinfo (HalDevice *d, HalDevice *parent, DevinfoDevHandler *handler, void *end_token)
292 {
293 HotplugEvent *hotplug_event = (HotplugEvent *)end_token;
294
295 HAL_INFO(("Preprobing udi=%s", hal_device_get_udi (d)));
296
297 if (parent == NULL && (strcmp(hotplug_event->un.devfs.devfs_path, "/") != 0)) {
298 HAL_ERROR (("Parent is NULL, devfs_path=%s", hotplug_event->un.devfs.devfs_path));
299
300 goto skip;
301 }
302
303
304 if (parent != NULL && hal_device_property_get_bool (parent, "info.ignore")) {
305 HAL_INFO (("Ignoring device since parent has info.ignore==TRUE"));
306
307 goto skip;
308 }
309
310 if (hal_device_store_find (hald_get_tdl (), hal_device_get_udi (d)) == NULL) {
311
312 /* add to TDL so preprobing callouts and prober can access it */
313 hal_device_store_add (hald_get_tdl (), d);
314 }
315
316 /* Process preprobe fdi files */
317 di_search_and_merge (d, DEVICE_INFO_TYPE_PREPROBE);
318
319 /* Run preprobe callouts */
320 hal_util_callout_device_preprobe (d, devinfo_callouts_preprobing_done, end_token, handler);
321
322 return;
323
324 skip:
325 if (hal_device_store_find (hald_get_tdl (), hal_device_get_udi (d)))
326 hal_device_store_remove (hald_get_tdl (), d);
327
328 g_object_unref (d);
329 hotplug_event_end (end_token);
330
331 return;
332 }
333
334 void
devinfo_remove(gchar * devfs_path)335 devinfo_remove (gchar *devfs_path)
336 {
337 devinfo_remove_enqueue ((gchar *)devfs_path, NULL);
338 }
339
340 /* generate hotplug event for each device in this branch */
341 void
devinfo_remove_branch(gchar * devfs_path,HalDevice * d)342 devinfo_remove_branch (gchar *devfs_path, HalDevice *d)
343 {
344 GSList *i;
345 GSList *children;
346 HalDevice *child;
347 char *child_devfs_path;
348
349 if (d == NULL) {
350 d = hal_device_store_match_key_value_string (hald_get_gdl (),
351 "solaris.devfs_path", devfs_path);
352 if (d == NULL)
353 return;
354 }
355
356 HAL_INFO (("remove_branch: %s %s\n", devfs_path, hal_device_get_udi (d)));
357
358 /* first remove children */
359 children = hal_device_store_match_multiple_key_value_string (hald_get_gdl(),
360 "info.parent", hal_device_get_udi (d));
361 for (i = children; i != NULL; i = g_slist_next (i)) {
362 child = HAL_DEVICE (i->data);
363 HAL_INFO (("remove_branch: child %s\n", hal_device_get_udi (child)));
364 devinfo_remove_branch ((gchar *)hal_device_property_get_string (child, "solaris.devfs_path"), child);
365 }
366 g_slist_free (children);
367 HAL_INFO (("remove_branch: done with children"));
368
369 /* then remove self */
370 HAL_INFO (("remove_branch: queueing %s", devfs_path));
371 devinfo_remove_enqueue (devfs_path, NULL);
372 }
373
374 void
devinfo_callouts_remove_done(HalDevice * d,gpointer userdata1,gpointer userdata2)375 devinfo_callouts_remove_done (HalDevice *d, gpointer userdata1, gpointer userdata2)
376 {
377 void *end_token = (void *) userdata1;
378
379 HAL_INFO (("Remove callouts completed udi=%s", hal_device_get_udi (d)));
380
381 if (!hal_device_store_remove (hald_get_gdl (), d)) {
382 HAL_WARNING (("Error removing device"));
383 }
384 g_object_unref (d);
385
386 hotplug_event_end (end_token);
387 }
388
389 void
hotplug_event_begin_remove_devinfo(HalDevice * d,gchar * devfs_path,void * end_token)390 hotplug_event_begin_remove_devinfo (HalDevice *d, gchar *devfs_path, void *end_token)
391 {
392 if (hal_device_has_capability (d, "volume")) {
393 devinfo_volume_hotplug_begin_remove (d, devfs_path, end_token);
394 } else {
395 hal_util_callout_device_remove (d, devinfo_callouts_remove_done, end_token, NULL);
396 }
397 }
398
399 gboolean
devinfo_device_rescan(HalDevice * d)400 devinfo_device_rescan (HalDevice *d)
401 {
402 if (hal_device_has_capability (d, "block")) {
403 return (devinfo_storage_device_rescan (d));
404 } else if (hal_device_has_capability (d, "button")) {
405 return (devinfo_lid_rescan (d));
406 } else {
407 return (FALSE);
408 }
409 }
410
411 static int
walk_devlinks(di_devlink_t devlink,void * arg)412 walk_devlinks(di_devlink_t devlink, void *arg)
413 {
414 char **path= (char **)arg;
415
416 *path = strdup(di_devlink_path(devlink));
417
418 return (DI_WALK_TERMINATE);
419 }
420
421 char *
get_devlink(di_devlink_handle_t devlink_hdl,char * re,char * path)422 get_devlink(di_devlink_handle_t devlink_hdl, char *re, char *path)
423 {
424 char *devlink_path = NULL;
425
426 (void) di_devlink_walk(devlink_hdl, re, path,
427 DI_PRIMARY_LINK, &devlink_path, walk_devlinks);
428
429 return (devlink_path);
430 }
431