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