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