1 /*************************************************************************** 2 * 3 * sysevent.c : Solaris sysevents 4 * 5 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 6 * Use is subject to license terms. 7 * 8 * Licensed under the Academic Free License version 2.1 9 * 10 **************************************************************************/ 11 12 #pragma ident "%Z%%M% %I% %E% SMI" 13 14 #ifdef HAVE_CONFIG_H 15 # include <config.h> 16 #endif 17 18 #include <stdio.h> 19 #include <unistd.h> 20 #include <stdlib.h> 21 #include <string.h> 22 #include <errno.h> 23 #include <fcntl.h> 24 #include <sys/dkio.h> 25 #include <sys/stat.h> 26 #include <libdevinfo.h> 27 #include <libsysevent.h> 28 #include <sys/sysevent/dev.h> 29 #include <sys/sysevent/acpiev.h> 30 #include <glib.h> 31 32 #include "../osspec.h" 33 #include "../logger.h" 34 #include "../hald.h" 35 #include "../hald_dbus.h" 36 #include "../device_info.h" 37 #include "../util.h" 38 #include "osspec_solaris.h" 39 #include "hotplug.h" 40 #include "devinfo.h" 41 #include "devinfo_storage.h" 42 #include "devinfo_acpi.h" 43 #include "devinfo_usb.h" 44 #include "sysevent.h" 45 46 #ifndef ESC_LOFI 47 #define ESC_LOFI "lofi" 48 #endif 49 50 static void sysevent_dev_handler(sysevent_t *); 51 static gboolean sysevent_iochannel_data(GIOChannel *, GIOCondition, gpointer); 52 static void sysevent_dev_add(gchar *, gchar *); 53 static void sysevent_dev_remove(gchar *, gchar *); 54 static void sysevent_dev_branch(gchar *); 55 static void sysevent_lofi_add(gchar *, gchar *); 56 static void sysevent_lofi_remove(gchar *, gchar *); 57 static void sysevent_devfs_add(gchar *); 58 59 static sysevent_handle_t *shp; 60 61 static int sysevent_pipe_fds[2]; 62 static GIOChannel *sysevent_iochannel; 63 static guint sysevent_source_id; 64 65 gboolean 66 sysevent_init(void) 67 { 68 GError *err = NULL; 69 const char *subcl[3]; 70 71 /* 72 * pipe used to serialize sysevents through the main loop 73 */ 74 if (pipe (sysevent_pipe_fds) != 0) { 75 HAL_INFO (("pipe() failed errno=%d", errno)); 76 return (FALSE); 77 } 78 sysevent_iochannel = g_io_channel_unix_new (sysevent_pipe_fds[0]); 79 if (sysevent_iochannel == NULL) { 80 HAL_INFO (("g_io_channel_unix_new failed")); 81 return (FALSE); 82 } 83 g_io_channel_set_flags (sysevent_iochannel, G_IO_FLAG_NONBLOCK, &err); 84 sysevent_source_id = g_io_add_watch ( 85 sysevent_iochannel, G_IO_IN, sysevent_iochannel_data, NULL); 86 87 shp = sysevent_bind_handle(sysevent_dev_handler); 88 if (shp == NULL) { 89 HAL_INFO (("sysevent_bind_handle failed %d", errno)); 90 return (FALSE); 91 } 92 93 subcl[0] = ESC_DISK; 94 subcl[1] = ESC_LOFI; 95 subcl[2] = ESC_PRINTER; 96 if (sysevent_subscribe_event(shp, EC_DEV_ADD, subcl, 3) != 0) { 97 HAL_INFO (("subscribe(dev_add) failed %d", errno)); 98 sysevent_unbind_handle(shp); 99 return (FALSE); 100 } 101 if (sysevent_subscribe_event(shp, EC_DEV_REMOVE, subcl, 3) != 0) { 102 HAL_INFO (("subscribe(dev_remove) failed %d", errno)); 103 sysevent_unbind_handle(shp); 104 return (FALSE); 105 } 106 107 subcl[0] = ESC_DEV_BRANCH_REMOVE; 108 if (sysevent_subscribe_event(shp, EC_DEV_BRANCH, subcl, 1) != 0) { 109 HAL_INFO (("subscribe(dev_branch) failed %d", errno)); 110 sysevent_unbind_handle(shp); 111 return (FALSE); 112 } 113 114 subcl[0] = ESC_ACPIEV_ADD; 115 subcl[1] = ESC_ACPIEV_REMOVE; 116 subcl[2] = ESC_ACPIEV_STATE_CHANGE; 117 if (sysevent_subscribe_event(shp, EC_ACPIEV, subcl, 3) != 0) { 118 HAL_INFO(("subscribe(dev_add) failed %d", errno)); 119 sysevent_unbind_handle(shp); 120 return (FALSE); 121 } 122 123 subcl[0] = ESC_DEVFS_DEVI_ADD; 124 if (sysevent_subscribe_event(shp, EC_DEVFS, subcl, 1) != 0) { 125 HAL_INFO (("subscribe(EC_DEVFS) failed %d", errno)); 126 sysevent_unbind_handle(shp); 127 return (FALSE); 128 } 129 130 return (B_TRUE); 131 } 132 133 void 134 sysevent_fini(void) 135 { 136 sysevent_unbind_handle(shp); 137 shp = NULL; 138 } 139 140 static void 141 sysevent_dev_handler(sysevent_t *ev) 142 { 143 char *class; 144 char *subclass; 145 nvlist_t *attr_list; 146 char *phys_path; 147 char *dev_name; 148 char *dev_hid; 149 char *dev_uid; 150 uint_t dev_index; 151 char s[1024]; 152 ssize_t nwritten; 153 154 if ((class = sysevent_get_class_name(ev)) == NULL) 155 return; 156 157 if ((subclass = sysevent_get_subclass_name(ev)) == NULL) 158 return; 159 160 if (sysevent_get_attr_list(ev, &attr_list) != 0) 161 return; 162 163 if (strcmp(class, EC_DEVFS) == 0) { 164 if (nvlist_lookup_string(attr_list, DEVFS_PATHNAME, &phys_path) != 0) { 165 goto out; 166 } 167 168 snprintf(s, sizeof (s), "%s %s %s\n", 169 class, subclass, phys_path); 170 nwritten = write(sysevent_pipe_fds[1], s, strlen(s) + 1); 171 172 HAL_INFO (("sysevent_dev_handler: wrote %d bytes", nwritten)); 173 goto out; 174 } 175 176 if (strcmp(class, EC_ACPIEV) == 0) { 177 if (nvlist_lookup_string(attr_list, ACPIEV_DEV_PHYS_PATH, 178 &phys_path) != 0) { 179 goto out; 180 } 181 } else if (nvlist_lookup_string(attr_list, DEV_PHYS_PATH, &phys_path) 182 != 0) { 183 goto out; 184 } 185 186 if (nvlist_lookup_string(attr_list, DEV_NAME, &dev_name) != 0) { 187 if (strcmp(class, EC_ACPIEV) == 0) { 188 dev_name = "noname"; 189 } else { 190 dev_name = ""; 191 } 192 } 193 194 if (nvlist_lookup_string(attr_list, ACPIEV_DEV_HID, &dev_hid) != 0) { 195 dev_hid = ""; 196 } 197 if (nvlist_lookup_string(attr_list, ACPIEV_DEV_UID, &dev_uid) != 0) { 198 dev_uid = ""; 199 } 200 if (nvlist_lookup_uint32(attr_list, ACPIEV_DEV_INDEX, &dev_index) 201 != 0) { 202 dev_index = 0; 203 } 204 205 snprintf(s, sizeof (s), "%s %s %s %s %s %s %d\n", 206 class, subclass, phys_path, dev_name, dev_hid, dev_uid, dev_index); 207 nwritten = write(sysevent_pipe_fds[1], s, strlen(s) + 1); 208 209 HAL_INFO (("sysevent_dev_handler: wrote %d bytes", nwritten)); 210 211 out: 212 nvlist_free(attr_list); 213 } 214 215 static gboolean 216 sysevent_iochannel_data (GIOChannel *source, 217 GIOCondition condition, 218 gpointer user_data) 219 { 220 GError *err = NULL; 221 gchar *s = NULL; 222 gsize len; 223 int matches; 224 gchar class[1024]; 225 gchar subclass[1024]; 226 gchar phys_path[1024]; 227 gchar dev_name[1024]; 228 gchar dev_uid[1024]; 229 gchar dev_hid[1024]; 230 gchar udi[1024]; 231 uint_t dev_index; 232 233 HAL_INFO (("sysevent_iochannel_data")); 234 235 while (g_io_channel_read_line (sysevent_iochannel, &s, &len, NULL, 236 &err) == G_IO_STATUS_NORMAL) { 237 if (len == 0) { 238 break; 239 } 240 241 class[0] = subclass[0] = phys_path[0] = dev_name[0] = 242 dev_hid[0] = dev_uid[0] = '\0'; 243 matches = sscanf(s, "%s %s %s %s %s %s %d", class, subclass, 244 phys_path, dev_name, dev_hid, dev_uid, &dev_index); 245 g_free (s); 246 s = NULL; 247 if (matches < 3) { 248 continue; 249 } 250 HAL_INFO (("sysevent: class=%s, sub=%s", class, subclass)); 251 252 if (strcmp(class, EC_DEV_ADD) == 0) { 253 if ((strcmp(subclass, ESC_DISK) == 0) || 254 (strcmp(subclass, ESC_PRINTER) == 0)) { 255 sysevent_dev_add(phys_path, dev_name); 256 } else if (strcmp(subclass, ESC_LOFI) == 0) { 257 sysevent_lofi_add(phys_path, dev_name); 258 } 259 } else if (strcmp(class, EC_DEV_REMOVE) == 0) { 260 if ((strcmp(subclass, ESC_DISK) == 0) || 261 (strcmp(subclass, ESC_PRINTER) == 0)) { 262 sysevent_dev_remove(phys_path, dev_name); 263 } else if (strcmp(subclass, ESC_LOFI) == 0) { 264 sysevent_lofi_remove(phys_path, dev_name); 265 } 266 } else if (strcmp(class, EC_DEV_BRANCH) == 0) { 267 sysevent_dev_branch(phys_path); 268 } else if (strcmp(class, EC_ACPIEV) == 0) { 269 if (strcmp(dev_hid, "PNP0C0A") == 0) { 270 snprintf(udi, sizeof(udi), 271 "/org/freedesktop/Hal/devices/pseudo/" 272 "battery_0_battery%d_0", dev_index); 273 } else if (strcmp(dev_hid, "ACPI0003") == 0) { 274 snprintf(udi, sizeof(udi), 275 "/org/freedesktop/Hal/devices/pseudo/" 276 "battery_0_ac%d_0", dev_index); 277 } else { 278 HAL_INFO(("dev_hid %s unknown", dev_hid)); 279 continue; 280 } 281 devinfo_battery_device_rescan(phys_path, udi); 282 } else if (strcmp(class, EC_DEVFS) == 0) { 283 if (strcmp(subclass, ESC_DEVFS_DEVI_ADD) == 0) { 284 sysevent_devfs_add(phys_path); 285 } 286 } 287 } 288 289 if (err) { 290 g_error_free (err); 291 } 292 293 return (TRUE); 294 } 295 296 static void 297 sysevent_dev_add(gchar *devfs_path, gchar *name) 298 { 299 gchar *parent_devfs_path, *hotplug_devfs_path; 300 HalDevice *parent; 301 302 HAL_INFO (("dev_add: %s %s", name, devfs_path)); 303 304 parent = hal_util_find_closest_ancestor (devfs_path, &parent_devfs_path, &hotplug_devfs_path); 305 if (parent == NULL) { 306 return; 307 } 308 309 HAL_INFO (("dev_add: parent=%s", parent_devfs_path)); 310 HAL_INFO (("dev_add: real=%s", hotplug_devfs_path)); 311 312 devinfo_add (parent, hotplug_devfs_path); 313 314 g_free (parent_devfs_path); 315 g_free (hotplug_devfs_path); 316 317 hotplug_event_process_queue (); 318 } 319 320 static void 321 sysevent_dev_remove(gchar *devfs_path, gchar *name) 322 { 323 HAL_INFO (("dev_remove: %s %s", name, devfs_path)); 324 325 devinfo_remove_branch (devfs_path, NULL); 326 hotplug_event_process_queue (); 327 } 328 329 static void 330 sysevent_dev_branch(gchar *devfs_path) 331 { 332 HAL_INFO (("branch_remove: %s", devfs_path)); 333 334 devinfo_remove_branch (devfs_path, NULL); 335 hotplug_event_process_queue (); 336 } 337 338 static void 339 sysevent_lofi_add(gchar *devfs_path, gchar *name) 340 { 341 di_node_t node; 342 const char *parent_udi; 343 HalDevice *d, *parent; 344 345 HAL_INFO (("lofi_add: %s %s", name, devfs_path)); 346 347 if ((d = hal_device_store_match_key_value_string (hald_get_gdl (), 348 "solaris.devfs_path", devfs_path)) == NULL) { 349 HAL_INFO (("device not found in GDL %s", devfs_path)); 350 return; 351 } 352 parent_udi = hal_device_property_get_string (d, "info.parent"); 353 if ((parent_udi == NULL) || (strlen(parent_udi) == 0)) { 354 HAL_INFO (("parent not found in GDL %s", parent_udi)); 355 return; 356 } 357 if ((parent = hal_device_store_match_key_value_string (hald_get_gdl (), 358 "info.udi", parent_udi)) == NULL) { 359 HAL_INFO (("parent not found in GDL %s", parent_udi)); 360 return; 361 } 362 363 if ((node = di_init (devfs_path, DINFOCPYALL)) == DI_NODE_NIL) { 364 HAL_INFO (("device not found in devinfo %s", devfs_path)); 365 return; 366 } 367 368 HAL_INFO (("device %s parent %s", hal_device_get_udi (d), parent_udi)); 369 devinfo_lofi_add_major (parent, node, devfs_path, NULL, TRUE, d); 370 371 di_fini (node); 372 373 hotplug_event_process_queue (); 374 } 375 376 static void 377 sysevent_lofi_remove(gchar *parent_devfs_path, gchar *name) 378 { 379 devinfo_lofi_remove_minor(parent_devfs_path, name); 380 hotplug_event_process_queue (); 381 } 382 383 static HalDevice * 384 lookup_parent(char *devfs_path) 385 { 386 gchar *path = NULL; 387 HalDevice *parent = NULL; 388 char *p; 389 390 path = strdup (devfs_path); 391 p = strrchr (path, '/'); 392 if (p == NULL) { 393 free (path); 394 return (NULL); 395 } 396 *p = '\0'; 397 398 /* Look up the parent node in the gdl. */ 399 parent = hal_device_store_match_key_value_string (hald_get_gdl (), 400 "solaris.devfs_path", path); 401 402 if (parent == NULL) { 403 /* Look up the parent node in the tdl. */ 404 parent = hal_device_store_match_key_value_string (hald_get_tdl (), 405 "solaris.devfs_path", path); 406 } 407 408 free (path); 409 return (parent); 410 } 411 412 /* 413 * Handle the USB bus devices hot plugging events. 414 */ 415 static void 416 sysevent_devfs_add(gchar *devfs_path) 417 { 418 di_node_t node; 419 HalDevice *parent; 420 char *driver_name; 421 422 HAL_INFO (("devfs_handle: %s", devfs_path)); 423 424 if ((node = di_init (devfs_path, DINFOCPYALL)) == DI_NODE_NIL) { 425 HAL_INFO (("device not found in devinfo %s", devfs_path)); 426 return; 427 } 428 429 if ((driver_name = di_driver_name (node)) == NULL) 430 goto out; 431 432 /* The disk and printer devices are handled by EC_DEV_ADD class. */ 433 if ((strcmp (driver_name, "scsa2usb") == 0) || 434 (strcmp (driver_name, "usbprn") == 0)) 435 goto out; 436 437 if ((parent = lookup_parent (devfs_path)) == NULL) 438 goto out; 439 440 devinfo_usb_add (parent, node, devfs_path, NULL); 441 442 di_fini (node); 443 444 hotplug_event_process_queue (); 445 446 return; 447 448 out: 449 di_fini (node); 450 } 451