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