xref: /illumos-gate/usr/src/cmd/hal/hald/solaris/devinfo.c (revision 69a119caa6570c7077699161b7c28b6ee9f8b0f4)
1 /***************************************************************************
2  *
3  * devinfo.c : main file for libdevinfo-based device enumeration
4  *
5  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
6  *
7  * Licensed under the Academic Free License version 2.1
8  *
9  **************************************************************************/
10 
11 #ifdef HAVE_CONFIG_H
12 #  include <config.h>
13 #endif
14 
15 #include <stdio.h>
16 #include <string.h>
17 #include <libdevinfo.h>
18 
19 #include "../osspec.h"
20 #include "../logger.h"
21 #include "../hald.h"
22 #include "../hald_dbus.h"
23 #include "../device_info.h"
24 #include "../util.h"
25 #include "../hald_runner.h"
26 #include "osspec_solaris.h"
27 #include "hotplug.h"
28 #include "devinfo.h"
29 #include "devinfo_pci.h"
30 #include "devinfo_storage.h"
31 #include "devinfo_ieee1394.h"
32 #include "devinfo_usb.h"
33 #include "devinfo_misc.h"
34 #include "devinfo_acpi.h"
35 #include "devinfo_cpu.h"
36 
37 void devinfo_add_subtree(HalDevice *parent, di_node_t node, gboolean is_root);
38 HalDevice *devinfo_add_node(HalDevice *parent, di_node_t node);
39 
40 void
41 devinfo_add(HalDevice *parent, gchar *path)
42 {
43 	di_node_t	root;
44 
45 	if (strcmp (path, "/") == 0) {
46 		if ((root = di_init(path, DINFOCACHE)) == DI_NODE_NIL) {
47 			HAL_INFO (("di_init() failed %d", errno));
48 			return;
49 		}
50 	} else {
51 		if ((root = di_init(path, DINFOCPYALL)) == DI_NODE_NIL) {
52 			HAL_INFO (("di_init() failed %d", errno));
53 			return;
54 		}
55 	}
56 
57 	devinfo_add_subtree(parent, root, TRUE);
58 
59 	di_fini (root);
60 }
61 
62 void
63 devinfo_add_subtree(HalDevice *parent, di_node_t node, gboolean is_root)
64 {
65 	HalDevice *d;
66 	di_node_t root_node, child_node;
67 
68 	HAL_INFO (("add_subtree: %s", di_node_name (node)));
69 
70 	root_node = node;
71 	do {
72 		d = devinfo_add_node (parent, node);
73 
74 		if ((d != NULL) &&
75 		    (child_node = di_child_node (node)) != DI_NODE_NIL) {
76 			devinfo_add_subtree (d, child_node, FALSE);
77 		}
78 
79 		node = di_sibling_node (node);
80 	} while ((node != DI_NODE_NIL) &&
81 		(!is_root || di_parent_node (node) == root_node));
82 }
83 
84 void
85 devinfo_set_default_properties (HalDevice *d, HalDevice *parent, di_node_t node, char *devfs_path)
86 {
87 	char	*driver_name, *s;
88 	const char *s1;
89 	char	udi[HAL_PATH_MAX];
90 
91 	if (parent != NULL) {
92 		hal_device_property_set_string (d, "info.parent", hal_device_get_udi (parent));
93 	} else {
94 		hal_device_property_set_string (d, "info.parent", "/org/freedesktop/Hal/devices/local");
95 	}
96 
97 	hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi),
98 				"/org/freedesktop/Hal/devices%s_%d",
99 				devfs_path,
100 				di_instance (node));
101 	hal_device_set_udi (d, udi);
102 	hal_device_property_set_string (d, "info.udi", udi);
103 
104 	if (di_prop_lookup_strings (DDI_DEV_T_ANY, node, "model", &s) > 0) {
105 		hal_device_property_set_string (d, "info.product", s);
106 	} else {
107 		hal_device_property_set_string (d, "info.product", di_node_name (node));
108 	}
109 
110 	hal_device_property_set_string (d, "solaris.devfs_path", devfs_path);
111 
112 	if ((driver_name = di_driver_name (node)) != NULL) {
113 		hal_device_property_set_string (d, "info.solaris.driver",
114 						driver_name);
115 	}
116 
117 
118 	/* inherit parent's claim attributes */
119 	if (hal_device_property_get_bool (parent, "info.claimed")) {
120 		s1 = hal_device_property_get_string (parent, "info.claimed.service");
121 		if (s1 != NULL) {
122 			hal_device_property_set_bool (d, "info.claimed", TRUE);
123 			hal_device_property_set_string (d, "info.claimed.service", s1);
124 		}
125 	}
126 }
127 
128 /* device handlers, ordered specific to generic */
129 static DevinfoDevHandler *devinfo_handlers[] = {
130 	&devinfo_computer_handler,
131 	&devinfo_cpu_handler,
132 	&devinfo_ide_handler,
133 	&devinfo_scsi_handler,
134 	&devinfo_pcata_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 *
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 *
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