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/pwrctl.h> 30 #include <sys/sysevent/dr.h> 31 #include <glib.h> 32 #include <config_admin.h> 33 #include <kstat.h> 34 35 #include "../osspec.h" 36 #include "../logger.h" 37 #include "../hald.h" 38 #include "../hald_dbus.h" 39 #include "../device_info.h" 40 #include "../util.h" 41 #include "osspec_solaris.h" 42 #include "hotplug.h" 43 #include "devinfo.h" 44 #include "devinfo_storage.h" 45 #include "devinfo_acpi.h" 46 #include "devinfo_usb.h" 47 #include "sysevent.h" 48 #include "devinfo_misc.h" 49 #include "devinfo_cpu.h" 50 51 #ifndef ESC_LOFI 52 #define ESC_LOFI "lofi" 53 #endif 54 55 static void sysevent_dev_handler(sysevent_t *); 56 static gboolean sysevent_iochannel_data(GIOChannel *, GIOCondition, gpointer); 57 static void sysevent_dev_add(gchar *, gchar *); 58 static void sysevent_dev_remove(gchar *, gchar *); 59 static void sysevent_dev_branch(gchar *); 60 static void sysevent_lofi_add(gchar *, gchar *); 61 static void sysevent_lofi_remove(gchar *, gchar *); 62 static void sysevent_devfs_add(gchar *); 63 static void sysevent_pwrctl(gchar *, gchar *, gchar *, gchar *, gchar *, 64 gchar *, uint_t); 65 static void sysevent_process_dr(gchar *, gchar *); 66 67 static sysevent_handle_t *shp; 68 69 static int sysevent_pipe_fds[2]; 70 static GIOChannel *sysevent_iochannel; 71 static guint sysevent_source_id; 72 73 gboolean 74 sysevent_init(void) 75 { 76 GError *err = NULL; 77 const char *subcl[6]; 78 79 /* 80 * pipe used to serialize sysevents through the main loop 81 */ 82 if (pipe (sysevent_pipe_fds) != 0) { 83 HAL_INFO (("pipe() failed errno=%d", errno)); 84 return (FALSE); 85 } 86 sysevent_iochannel = g_io_channel_unix_new (sysevent_pipe_fds[0]); 87 if (sysevent_iochannel == NULL) { 88 HAL_INFO (("g_io_channel_unix_new failed")); 89 return (FALSE); 90 } 91 g_io_channel_set_flags (sysevent_iochannel, G_IO_FLAG_NONBLOCK, &err); 92 sysevent_source_id = g_io_add_watch ( 93 sysevent_iochannel, G_IO_IN, sysevent_iochannel_data, NULL); 94 95 shp = sysevent_bind_handle(sysevent_dev_handler); 96 if (shp == NULL) { 97 HAL_INFO (("sysevent_bind_handle failed %d", errno)); 98 return (FALSE); 99 } 100 101 subcl[0] = ESC_DISK; 102 subcl[1] = ESC_LOFI; 103 subcl[2] = ESC_PRINTER; 104 if (sysevent_subscribe_event(shp, EC_DEV_ADD, subcl, 3) != 0) { 105 HAL_INFO (("subscribe(dev_add) failed %d", errno)); 106 sysevent_unbind_handle(shp); 107 return (FALSE); 108 } 109 if (sysevent_subscribe_event(shp, EC_DEV_REMOVE, subcl, 3) != 0) { 110 HAL_INFO (("subscribe(dev_remove) failed %d", errno)); 111 sysevent_unbind_handle(shp); 112 return (FALSE); 113 } 114 115 subcl[0] = ESC_DEV_BRANCH_REMOVE; 116 if (sysevent_subscribe_event(shp, EC_DEV_BRANCH, subcl, 1) != 0) { 117 HAL_INFO (("subscribe(dev_branch) failed %d", errno)); 118 sysevent_unbind_handle(shp); 119 return (FALSE); 120 } 121 122 subcl[0] = ESC_PWRCTL_ADD; 123 subcl[1] = ESC_PWRCTL_REMOVE; 124 subcl[2] = ESC_PWRCTL_STATE_CHANGE; 125 subcl[3] = ESC_PWRCTL_BRIGHTNESS_UP; 126 subcl[4] = ESC_PWRCTL_BRIGHTNESS_DOWN; 127 subcl[5] = ESC_PWRCTL_POWER_BUTTON; 128 if (sysevent_subscribe_event(shp, EC_PWRCTL, subcl, 6) != 0) { 129 HAL_INFO(("subscribe(dev_add) failed %d", errno)); 130 sysevent_unbind_handle(shp); 131 return (FALSE); 132 } 133 134 subcl[0] = ESC_DEVFS_DEVI_ADD; 135 if (sysevent_subscribe_event(shp, EC_DEVFS, subcl, 1) != 0) { 136 HAL_INFO (("subscribe(EC_DEVFS) failed %d", errno)); 137 sysevent_unbind_handle(shp); 138 return (FALSE); 139 } 140 141 subcl[0] = ESC_DR_AP_STATE_CHANGE; 142 if (sysevent_subscribe_event(shp, EC_DR, subcl, 1) != 0) { 143 HAL_INFO (("subscribe(dynamic reconfiguration) failed %d", 144 errno)); 145 sysevent_unbind_handle(shp); 146 return (FALSE); 147 } 148 149 return (B_TRUE); 150 } 151 152 void 153 sysevent_fini(void) 154 { 155 sysevent_unbind_handle(shp); 156 shp = NULL; 157 } 158 159 static void 160 sysevent_dev_handler(sysevent_t *ev) 161 { 162 char *class; 163 char *subclass; 164 nvlist_t *attr_list; 165 char *phys_path; 166 char *dev_name; 167 char *dev_hid; 168 char *dev_uid; 169 uint_t dev_index; 170 char s[1024]; 171 ssize_t nwritten; 172 173 if ((class = sysevent_get_class_name(ev)) == NULL) 174 return; 175 176 if ((subclass = sysevent_get_subclass_name(ev)) == NULL) 177 return; 178 179 if (sysevent_get_attr_list(ev, &attr_list) != 0) 180 return; 181 182 if (strcmp(class, EC_DEVFS) == 0) { 183 if (nvlist_lookup_string(attr_list, DEVFS_PATHNAME, &phys_path) != 0) { 184 goto out; 185 } 186 187 snprintf(s, sizeof (s), "%s %s %s\n", 188 class, subclass, phys_path); 189 nwritten = write(sysevent_pipe_fds[1], s, strlen(s) + 1); 190 191 HAL_INFO (("sysevent_dev_handler: wrote %d bytes", nwritten)); 192 goto out; 193 } 194 195 if (strcmp(class, EC_PWRCTL) == 0) { 196 if (nvlist_lookup_string(attr_list, PWRCTL_DEV_PHYS_PATH, 197 &phys_path) != 0) { 198 goto out; 199 } 200 } else if (strcmp(class, EC_DR) == 0) { 201 if (nvlist_lookup_string(attr_list, DR_AP_ID, 202 &phys_path) != 0) { 203 goto out; 204 } 205 } else if (nvlist_lookup_string(attr_list, DEV_PHYS_PATH, &phys_path) 206 != 0) { 207 goto out; 208 } 209 210 /* 211 * In case of EC_DR, use dev_name to store DR_HINT val 212 */ 213 if (strcmp(class, EC_DR) == 0) { 214 if (nvlist_lookup_string(attr_list, DR_HINT, &dev_name) != 0) { 215 goto out; 216 } 217 } else if (nvlist_lookup_string(attr_list, DEV_NAME, &dev_name) != 0) { 218 if (strcmp(class, EC_PWRCTL) == 0) { 219 dev_name = "noname"; 220 } else { 221 dev_name = ""; 222 } 223 } 224 225 if (nvlist_lookup_string(attr_list, PWRCTL_DEV_HID, &dev_hid) != 0) { 226 dev_hid = ""; 227 } 228 if (nvlist_lookup_string(attr_list, PWRCTL_DEV_UID, &dev_uid) != 0) { 229 dev_uid = ""; 230 } 231 if (nvlist_lookup_uint32(attr_list, PWRCTL_DEV_INDEX, &dev_index) 232 != 0) { 233 dev_index = 0; 234 } 235 236 snprintf(s, sizeof (s), "%s %s %s %s %s %s %d\n", 237 class, subclass, phys_path, dev_name, dev_hid, dev_uid, dev_index); 238 nwritten = write(sysevent_pipe_fds[1], s, strlen(s) + 1); 239 240 HAL_INFO (("sysevent_dev_handler: wrote %d bytes", nwritten)); 241 242 out: 243 nvlist_free(attr_list); 244 } 245 246 static gboolean 247 sysevent_iochannel_data (GIOChannel *source, 248 GIOCondition condition, 249 gpointer user_data) 250 { 251 GError *err = NULL; 252 gchar *s = NULL; 253 gsize len; 254 int matches; 255 gchar class[1024]; 256 gchar subclass[1024]; 257 gchar phys_path[1024]; 258 gchar dev_name[1024]; 259 gchar dev_uid[1024]; 260 gchar dev_hid[1024]; 261 uint_t dev_index; 262 263 HAL_INFO (("sysevent_iochannel_data")); 264 265 while (g_io_channel_read_line (sysevent_iochannel, &s, &len, NULL, 266 &err) == G_IO_STATUS_NORMAL) { 267 if (len == 0) { 268 break; 269 } 270 HAL_INFO (("IOChannel val => %s", s)); 271 class[0] = subclass[0] = phys_path[0] = dev_name[0] = 272 dev_hid[0] = dev_uid[0] = '\0'; 273 matches = sscanf(s, "%s %s %s %s %s %s %d", class, subclass, 274 phys_path, dev_name, dev_hid, dev_uid, &dev_index); 275 g_free (s); 276 s = NULL; 277 if (matches < 3) { 278 continue; 279 } 280 HAL_INFO (("sysevent: class=%s, sub=%s", class, subclass)); 281 282 if (strcmp(class, EC_DEV_ADD) == 0) { 283 if ((strcmp(subclass, ESC_DISK) == 0) || 284 (strcmp(subclass, ESC_PRINTER) == 0)) { 285 sysevent_dev_add(phys_path, dev_name); 286 } else if (strcmp(subclass, ESC_LOFI) == 0) { 287 sysevent_lofi_add(phys_path, dev_name); 288 } 289 } else if (strcmp(class, EC_DEV_REMOVE) == 0) { 290 if ((strcmp(subclass, ESC_DISK) == 0) || 291 (strcmp(subclass, ESC_PRINTER) == 0)) { 292 sysevent_dev_remove(phys_path, dev_name); 293 } else if (strcmp(subclass, ESC_LOFI) == 0) { 294 sysevent_lofi_remove(phys_path, dev_name); 295 } 296 } else if (strcmp(class, EC_DEV_BRANCH) == 0) { 297 sysevent_dev_branch(phys_path); 298 } else if (strcmp(class, EC_PWRCTL) == 0) { 299 sysevent_pwrctl(class, subclass, phys_path, 300 dev_name, dev_hid, dev_uid, dev_index); 301 } else if (strcmp(class, EC_DEVFS) == 0) { 302 if (strcmp(subclass, ESC_DEVFS_DEVI_ADD) == 0) { 303 sysevent_devfs_add(phys_path); 304 } 305 } else if (strcmp(class, EC_DR) == 0) { 306 /* 307 * Note: AP_ID is stored in phys_path and HINT is 308 * stored in dev_name, to avoid creating seperate 309 * variables and multiple conditions checking 310 */ 311 HAL_DEBUG (("In %s, AP_ID-> %s, Hint-> %s", class, 312 phys_path, dev_name)); 313 if (strcmp(subclass, ESC_DR_AP_STATE_CHANGE) == 0) { 314 sysevent_process_dr(phys_path, dev_name); 315 } 316 } 317 } 318 319 if (err) { 320 g_error_free (err); 321 } 322 323 return (TRUE); 324 } 325 326 static void 327 sysevent_dev_add(gchar *devfs_path, gchar *name) 328 { 329 gchar *parent_devfs_path, *hotplug_devfs_path; 330 HalDevice *parent; 331 332 HAL_INFO (("dev_add: %s %s", name, devfs_path)); 333 334 parent = hal_util_find_closest_ancestor (devfs_path, &parent_devfs_path, &hotplug_devfs_path); 335 if (parent == NULL) { 336 return; 337 } 338 339 HAL_INFO (("dev_add: parent=%s", parent_devfs_path)); 340 HAL_INFO (("dev_add: real=%s", hotplug_devfs_path)); 341 342 devinfo_add (parent, hotplug_devfs_path); 343 344 g_free (parent_devfs_path); 345 g_free (hotplug_devfs_path); 346 347 hotplug_event_process_queue (); 348 } 349 350 static void 351 sysevent_dev_remove(gchar *devfs_path, gchar *name) 352 { 353 HAL_INFO (("dev_remove: %s %s", name, devfs_path)); 354 355 devinfo_remove_branch (devfs_path, NULL); 356 hotplug_event_process_queue (); 357 } 358 359 static void 360 sysevent_dev_branch(gchar *devfs_path) 361 { 362 HAL_INFO (("branch_remove: %s", devfs_path)); 363 364 devinfo_remove_branch (devfs_path, NULL); 365 hotplug_event_process_queue (); 366 } 367 368 static void 369 sysevent_lofi_add(gchar *devfs_path, gchar *name) 370 { 371 di_node_t node; 372 const char *parent_udi; 373 HalDevice *d, *parent; 374 375 HAL_INFO (("lofi_add: %s %s", name, devfs_path)); 376 377 if ((d = hal_device_store_match_key_value_string (hald_get_gdl (), 378 "solaris.devfs_path", devfs_path)) == NULL) { 379 HAL_INFO (("device not found in GDL %s", devfs_path)); 380 return; 381 } 382 parent_udi = hal_device_property_get_string (d, "info.parent"); 383 if ((parent_udi == NULL) || (strlen(parent_udi) == 0)) { 384 HAL_INFO (("parent not found in GDL %s", parent_udi)); 385 return; 386 } 387 if ((parent = hal_device_store_match_key_value_string (hald_get_gdl (), 388 "info.udi", parent_udi)) == NULL) { 389 HAL_INFO (("parent not found in GDL %s", parent_udi)); 390 return; 391 } 392 393 if ((node = di_init (devfs_path, DINFOCPYALL)) == DI_NODE_NIL) { 394 HAL_INFO (("device not found in devinfo %s", devfs_path)); 395 return; 396 } 397 398 HAL_INFO (("device %s parent %s", hal_device_get_udi (d), parent_udi)); 399 devinfo_lofi_add_major (parent, node, devfs_path, NULL, TRUE, d); 400 401 di_fini (node); 402 403 hotplug_event_process_queue (); 404 } 405 406 static void 407 sysevent_lofi_remove(gchar *parent_devfs_path, gchar *name) 408 { 409 devinfo_lofi_remove_minor(parent_devfs_path, name); 410 hotplug_event_process_queue (); 411 } 412 413 static HalDevice * 414 lookup_parent(char *devfs_path) 415 { 416 gchar *path = NULL; 417 HalDevice *parent = NULL; 418 char *p; 419 420 path = strdup (devfs_path); 421 p = strrchr (path, '/'); 422 if (p == NULL) { 423 free (path); 424 return (NULL); 425 } 426 *p = '\0'; 427 428 /* Look up the parent node in the gdl. */ 429 parent = hal_device_store_match_key_value_string (hald_get_gdl (), 430 "solaris.devfs_path", path); 431 432 if (parent == NULL) { 433 /* Look up the parent node in the tdl. */ 434 parent = hal_device_store_match_key_value_string (hald_get_tdl (), 435 "solaris.devfs_path", path); 436 } 437 438 free (path); 439 return (parent); 440 } 441 442 /* 443 * Handle the USB bus devices hot plugging events. 444 */ 445 static void 446 sysevent_devfs_add(gchar *devfs_path) 447 { 448 di_node_t node; 449 HalDevice *parent; 450 char *driver_name; 451 452 HAL_INFO (("devfs_handle: %s", devfs_path)); 453 454 if ((node = di_init (devfs_path, DINFOCPYALL)) == DI_NODE_NIL) { 455 HAL_INFO (("device not found in devinfo %s", devfs_path)); 456 return; 457 } 458 459 if ((driver_name = di_driver_name (node)) == NULL) 460 goto out; 461 462 /* The disk and printer devices are handled by EC_DEV_ADD class. */ 463 if ((strcmp (driver_name, "scsa2usb") == 0) || 464 (strcmp (driver_name, "usbprn") == 0)) 465 goto out; 466 467 if ((parent = lookup_parent (devfs_path)) == NULL) 468 goto out; 469 470 devinfo_usb_add (parent, node, devfs_path, NULL); 471 472 di_fini (node); 473 474 hotplug_event_process_queue (); 475 476 return; 477 478 out: 479 di_fini (node); 480 } 481 482 static void 483 sysevent_pwrctl(gchar *class, gchar *subclass, gchar *phys_path, 484 gchar *dev_name, gchar *dev_hid, gchar *dev_uid, uint_t dev_index) 485 { 486 const gchar prefix[] = "/org/freedesktop/Hal/devices/pseudo/acpi_drv_0"; 487 gchar udi[HAL_PATH_MAX]; 488 489 if (strcmp(dev_hid, "PNP0C0A") == 0) { 490 snprintf(udi, sizeof(udi), "%s_battery%d_0", prefix, dev_index); 491 devinfo_battery_device_rescan(phys_path, udi); 492 } else if (strcmp(dev_hid, "ACPI0003") == 0) { 493 snprintf(udi, sizeof (udi), "%s_ac%d_0", prefix, dev_index); 494 devinfo_battery_device_rescan(phys_path, udi); 495 } else if (strcmp(dev_hid, "PNP0C0D") == 0) { 496 snprintf(udi, sizeof (udi), "%s_lid_0", prefix); 497 devinfo_lid_device_rescan(subclass, udi); 498 } else if (strcmp(subclass, ESC_PWRCTL_POWER_BUTTON) == 0) { 499 devinfo_power_button_rescan(); 500 } else if ((strcmp(subclass, ESC_PWRCTL_BRIGHTNESS_UP) == 0) || 501 (strcmp(subclass, ESC_PWRCTL_BRIGHTNESS_DOWN) == 0)) { 502 devinfo_brightness_hotkeys_rescan(subclass); 503 } else { 504 HAL_INFO(("Unmatched EC_PWRCTL")); 505 } 506 } 507 508 static void 509 sysevent_dr_remove_cpu() 510 { 511 512 HalDeviceStore *gdl; 513 GSList *iter; 514 HalDevice *d, *del_dev; 515 int cpu_id, del_cpuid; 516 kstat_ctl_t *kc; 517 kstat_t *ksp; 518 kstat_named_t *ksdata; 519 const char *cpu_devfs_path; 520 /* 521 * Find the CPU's that are DR removed. For each "processor" device in 522 * HAL device tree, check if it has its corresponding kstat_info. If 523 * not, then, that cpu has been removed and can remove the entry from 524 * HAL entry 525 */ 526 527 HAL_DEBUG (("sysevent_dr_remove_cpu()")); 528 kc = kstat_open (); 529 if (kc == NULL) { 530 HAL_INFO (("Error in removing HAL cpu entry during DR. Could" 531 " not open kstat to get cpu info: %s", strerror (errno))); 532 return; 533 } 534 535 /* 536 * Iterate through the HAL device list to get the processor devices 537 */ 538 gdl = hald_get_gdl (); 539 iter = gdl->devices; 540 541 while (iter != NULL) { 542 d = HAL_DEVICE (iter->data); 543 544 if (!hal_device_has_property (d, "processor.number")) { 545 iter = iter->next; 546 continue; 547 } 548 549 cpu_id = hal_device_property_get_int (d, "processor.number"); 550 551 /* 552 * Check if the above cpu_id has its info in kstat 553 */ 554 555 ksp = kstat_lookup (kc, "cpu_info", cpu_id, NULL); 556 if (ksp != NULL) { 557 iter = iter->next; 558 continue; 559 } 560 /* 561 * kstat info not found. Delete the device entry 562 */ 563 HAL_INFO ((" Remove CPU entry: %d", cpu_id)); 564 iter = iter->next; 565 cpu_devfs_path = hal_device_property_get_string (d, 566 "solaris.devfs_path"); 567 if (cpu_devfs_path == NULL) { 568 HAL_INFO (("Could not get cpu_devfs_path to " 569 "remove for cpu_id %d", cpu_id)); 570 } else { 571 /* 572 * Remove the cpu device 573 */ 574 HAL_DEBUG (("Queue %s for removal", cpu_devfs_path)); 575 devinfo_remove_enqueue ((char *)cpu_devfs_path, NULL); 576 hotplug_event_process_queue (); 577 } 578 } 579 580 if (kc) { 581 kstat_close (kc); 582 } 583 } 584 585 int 586 sysevent_dr_insert_cpu(di_node_t node, void *arg) 587 { 588 char *devfs_path; 589 char *device_type = NULL; 590 DevinfoDevHandler *dh; 591 592 dh = &devinfo_cpu_handler; 593 devfs_path = di_devfs_path (node); 594 595 (void) di_prop_lookup_strings (DDI_DEV_T_ANY, node, "device_type", 596 &device_type); 597 598 dh->add (NULL, node, devfs_path, device_type); 599 600 di_devfs_path_free (devfs_path); 601 return (DI_WALK_CONTINUE); 602 } 603 604 /* 605 * Remove/Add the DR event device 606 * Note: Currently it supports only CPU DR events 607 */ 608 static void 609 sysevent_process_dr(gchar *ap_id, gchar *hint_val) 610 { 611 cfga_err_t cfgerr; 612 cfga_list_data_t *cfg_stat; 613 int nlist; 614 char *errstr; 615 di_node_t root_node; 616 617 if ((ap_id == NULL) || (hint_val == NULL)) 618 return; 619 HAL_DEBUG (("sysevent_process_dr: %s", ap_id)); 620 621 cfgerr = config_list_ext (1, (char *const *)&ap_id, &cfg_stat, &nlist, 622 NULL, NULL, &errstr, 0); 623 624 if (cfgerr != CFGA_OK) { 625 HAL_INFO (("DR sysevent process %d config_list_ext error: %s", 626 ap_id, errstr)); 627 goto out; 628 } 629 /* 630 * Check if the device type is CPU 631 */ 632 HAL_DEBUG ((" Ap-Type: %s, State: %d", cfg_stat->ap_type, 633 cfg_stat->ap_r_state)); 634 if (strcmp (cfg_stat->ap_type, "CPU") == 0) { 635 if (strcmp (hint_val, DR_HINT_REMOVE) == 0) { 636 sysevent_dr_remove_cpu(); 637 } else if (strcmp (hint_val, DR_HINT_INSERT) == 0) { 638 /* 639 * Go through the device list and add the new cpu 640 * entries into HAL 641 */ 642 if ((root_node = 643 di_init ("/", DINFOCPYALL)) == DI_NODE_NIL) { 644 HAL_INFO (("di_init failed. "\ 645 "Cannot insert CPU")); 646 goto out; 647 } 648 di_walk_node (root_node, DI_WALK_CLDFIRST, NULL, 649 sysevent_dr_insert_cpu); 650 di_fini (root_node); 651 hotplug_event_process_queue (); 652 } 653 } else { 654 HAL_INFO (("Not a CPU, so cannot DR")); 655 } 656 657 out: 658 if (cfg_stat) 659 free (cfg_stat); 660 if (errstr) 661 free (errstr); 662 } 663