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 <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 static void sysevent_pwrctl(gchar *, gchar *, gchar *, gchar *, gchar *, 59 gchar *, uint_t); 60 61 static sysevent_handle_t *shp; 62 63 static int sysevent_pipe_fds[2]; 64 static GIOChannel *sysevent_iochannel; 65 static guint sysevent_source_id; 66 67 gboolean 68 sysevent_init(void) 69 { 70 GError *err = NULL; 71 const char *subcl[6]; 72 73 /* 74 * pipe used to serialize sysevents through the main loop 75 */ 76 if (pipe (sysevent_pipe_fds) != 0) { 77 HAL_INFO (("pipe() failed errno=%d", errno)); 78 return (FALSE); 79 } 80 sysevent_iochannel = g_io_channel_unix_new (sysevent_pipe_fds[0]); 81 if (sysevent_iochannel == NULL) { 82 HAL_INFO (("g_io_channel_unix_new failed")); 83 return (FALSE); 84 } 85 g_io_channel_set_flags (sysevent_iochannel, G_IO_FLAG_NONBLOCK, &err); 86 sysevent_source_id = g_io_add_watch ( 87 sysevent_iochannel, G_IO_IN, sysevent_iochannel_data, NULL); 88 89 shp = sysevent_bind_handle(sysevent_dev_handler); 90 if (shp == NULL) { 91 HAL_INFO (("sysevent_bind_handle failed %d", errno)); 92 return (FALSE); 93 } 94 95 subcl[0] = ESC_DISK; 96 subcl[1] = ESC_LOFI; 97 subcl[2] = ESC_PRINTER; 98 if (sysevent_subscribe_event(shp, EC_DEV_ADD, subcl, 3) != 0) { 99 HAL_INFO (("subscribe(dev_add) failed %d", errno)); 100 sysevent_unbind_handle(shp); 101 return (FALSE); 102 } 103 if (sysevent_subscribe_event(shp, EC_DEV_REMOVE, subcl, 3) != 0) { 104 HAL_INFO (("subscribe(dev_remove) failed %d", errno)); 105 sysevent_unbind_handle(shp); 106 return (FALSE); 107 } 108 109 subcl[0] = ESC_DEV_BRANCH_REMOVE; 110 if (sysevent_subscribe_event(shp, EC_DEV_BRANCH, subcl, 1) != 0) { 111 HAL_INFO (("subscribe(dev_branch) failed %d", errno)); 112 sysevent_unbind_handle(shp); 113 return (FALSE); 114 } 115 116 subcl[0] = ESC_PWRCTL_ADD; 117 subcl[1] = ESC_PWRCTL_REMOVE; 118 subcl[2] = ESC_PWRCTL_STATE_CHANGE; 119 subcl[3] = ESC_PWRCTL_BRIGHTNESS_UP; 120 subcl[4] = ESC_PWRCTL_BRIGHTNESS_DOWN; 121 subcl[5] = ESC_PWRCTL_POWER_BUTTON; 122 if (sysevent_subscribe_event(shp, EC_PWRCTL, subcl, 6) != 0) { 123 HAL_INFO(("subscribe(dev_add) failed %d", errno)); 124 sysevent_unbind_handle(shp); 125 return (FALSE); 126 } 127 128 subcl[0] = ESC_DEVFS_DEVI_ADD; 129 if (sysevent_subscribe_event(shp, EC_DEVFS, subcl, 1) != 0) { 130 HAL_INFO (("subscribe(EC_DEVFS) failed %d", errno)); 131 sysevent_unbind_handle(shp); 132 return (FALSE); 133 } 134 135 return (B_TRUE); 136 } 137 138 void 139 sysevent_fini(void) 140 { 141 sysevent_unbind_handle(shp); 142 shp = NULL; 143 } 144 145 static void 146 sysevent_dev_handler(sysevent_t *ev) 147 { 148 char *class; 149 char *subclass; 150 nvlist_t *attr_list; 151 char *phys_path; 152 char *dev_name; 153 char *dev_hid; 154 char *dev_uid; 155 uint_t dev_index; 156 char s[1024]; 157 ssize_t nwritten; 158 159 if ((class = sysevent_get_class_name(ev)) == NULL) 160 return; 161 162 if ((subclass = sysevent_get_subclass_name(ev)) == NULL) 163 return; 164 165 if (sysevent_get_attr_list(ev, &attr_list) != 0) 166 return; 167 168 if (strcmp(class, EC_DEVFS) == 0) { 169 if (nvlist_lookup_string(attr_list, DEVFS_PATHNAME, &phys_path) != 0) { 170 goto out; 171 } 172 173 snprintf(s, sizeof (s), "%s %s %s\n", 174 class, subclass, phys_path); 175 nwritten = write(sysevent_pipe_fds[1], s, strlen(s) + 1); 176 177 HAL_INFO (("sysevent_dev_handler: wrote %d bytes", nwritten)); 178 goto out; 179 } 180 181 if (strcmp(class, EC_PWRCTL) == 0) { 182 if (nvlist_lookup_string(attr_list, PWRCTL_DEV_PHYS_PATH, 183 &phys_path) != 0) { 184 goto out; 185 } 186 } else if (nvlist_lookup_string(attr_list, DEV_PHYS_PATH, &phys_path) 187 != 0) { 188 goto out; 189 } 190 191 if (nvlist_lookup_string(attr_list, DEV_NAME, &dev_name) != 0) { 192 if (strcmp(class, EC_PWRCTL) == 0) { 193 dev_name = "noname"; 194 } else { 195 dev_name = ""; 196 } 197 } 198 199 if (nvlist_lookup_string(attr_list, PWRCTL_DEV_HID, &dev_hid) != 0) { 200 dev_hid = ""; 201 } 202 if (nvlist_lookup_string(attr_list, PWRCTL_DEV_UID, &dev_uid) != 0) { 203 dev_uid = ""; 204 } 205 if (nvlist_lookup_uint32(attr_list, PWRCTL_DEV_INDEX, &dev_index) 206 != 0) { 207 dev_index = 0; 208 } 209 210 snprintf(s, sizeof (s), "%s %s %s %s %s %s %d\n", 211 class, subclass, phys_path, dev_name, dev_hid, dev_uid, dev_index); 212 nwritten = write(sysevent_pipe_fds[1], s, strlen(s) + 1); 213 214 HAL_INFO (("sysevent_dev_handler: wrote %d bytes", nwritten)); 215 216 out: 217 nvlist_free(attr_list); 218 } 219 220 static gboolean 221 sysevent_iochannel_data (GIOChannel *source, 222 GIOCondition condition, 223 gpointer user_data) 224 { 225 GError *err = NULL; 226 gchar *s = NULL; 227 gsize len; 228 int matches; 229 gchar class[1024]; 230 gchar subclass[1024]; 231 gchar phys_path[1024]; 232 gchar dev_name[1024]; 233 gchar dev_uid[1024]; 234 gchar dev_hid[1024]; 235 uint_t dev_index; 236 237 HAL_INFO (("sysevent_iochannel_data")); 238 239 while (g_io_channel_read_line (sysevent_iochannel, &s, &len, NULL, 240 &err) == G_IO_STATUS_NORMAL) { 241 if (len == 0) { 242 break; 243 } 244 245 class[0] = subclass[0] = phys_path[0] = dev_name[0] = 246 dev_hid[0] = dev_uid[0] = '\0'; 247 matches = sscanf(s, "%s %s %s %s %s %s %d", class, subclass, 248 phys_path, dev_name, dev_hid, dev_uid, &dev_index); 249 g_free (s); 250 s = NULL; 251 if (matches < 3) { 252 continue; 253 } 254 HAL_INFO (("sysevent: class=%s, sub=%s", class, subclass)); 255 256 if (strcmp(class, EC_DEV_ADD) == 0) { 257 if ((strcmp(subclass, ESC_DISK) == 0) || 258 (strcmp(subclass, ESC_PRINTER) == 0)) { 259 sysevent_dev_add(phys_path, dev_name); 260 } else if (strcmp(subclass, ESC_LOFI) == 0) { 261 sysevent_lofi_add(phys_path, dev_name); 262 } 263 } else if (strcmp(class, EC_DEV_REMOVE) == 0) { 264 if ((strcmp(subclass, ESC_DISK) == 0) || 265 (strcmp(subclass, ESC_PRINTER) == 0)) { 266 sysevent_dev_remove(phys_path, dev_name); 267 } else if (strcmp(subclass, ESC_LOFI) == 0) { 268 sysevent_lofi_remove(phys_path, dev_name); 269 } 270 } else if (strcmp(class, EC_DEV_BRANCH) == 0) { 271 sysevent_dev_branch(phys_path); 272 } else if (strcmp(class, EC_PWRCTL) == 0) { 273 sysevent_pwrctl(class, subclass, phys_path, 274 dev_name, dev_hid, dev_uid, dev_index); 275 } else if (strcmp(class, EC_DEVFS) == 0) { 276 if (strcmp(subclass, ESC_DEVFS_DEVI_ADD) == 0) { 277 sysevent_devfs_add(phys_path); 278 } 279 } 280 } 281 282 if (err) { 283 g_error_free (err); 284 } 285 286 return (TRUE); 287 } 288 289 static void 290 sysevent_dev_add(gchar *devfs_path, gchar *name) 291 { 292 gchar *parent_devfs_path, *hotplug_devfs_path; 293 HalDevice *parent; 294 295 HAL_INFO (("dev_add: %s %s", name, devfs_path)); 296 297 parent = hal_util_find_closest_ancestor (devfs_path, &parent_devfs_path, &hotplug_devfs_path); 298 if (parent == NULL) { 299 return; 300 } 301 302 HAL_INFO (("dev_add: parent=%s", parent_devfs_path)); 303 HAL_INFO (("dev_add: real=%s", hotplug_devfs_path)); 304 305 devinfo_add (parent, hotplug_devfs_path); 306 307 g_free (parent_devfs_path); 308 g_free (hotplug_devfs_path); 309 310 hotplug_event_process_queue (); 311 } 312 313 static void 314 sysevent_dev_remove(gchar *devfs_path, gchar *name) 315 { 316 HAL_INFO (("dev_remove: %s %s", name, devfs_path)); 317 318 devinfo_remove_branch (devfs_path, NULL); 319 hotplug_event_process_queue (); 320 } 321 322 static void 323 sysevent_dev_branch(gchar *devfs_path) 324 { 325 HAL_INFO (("branch_remove: %s", devfs_path)); 326 327 devinfo_remove_branch (devfs_path, NULL); 328 hotplug_event_process_queue (); 329 } 330 331 static void 332 sysevent_lofi_add(gchar *devfs_path, gchar *name) 333 { 334 di_node_t node; 335 const char *parent_udi; 336 HalDevice *d, *parent; 337 338 HAL_INFO (("lofi_add: %s %s", name, devfs_path)); 339 340 if ((d = hal_device_store_match_key_value_string (hald_get_gdl (), 341 "solaris.devfs_path", devfs_path)) == NULL) { 342 HAL_INFO (("device not found in GDL %s", devfs_path)); 343 return; 344 } 345 parent_udi = hal_device_property_get_string (d, "info.parent"); 346 if ((parent_udi == NULL) || (strlen(parent_udi) == 0)) { 347 HAL_INFO (("parent not found in GDL %s", parent_udi)); 348 return; 349 } 350 if ((parent = hal_device_store_match_key_value_string (hald_get_gdl (), 351 "info.udi", parent_udi)) == NULL) { 352 HAL_INFO (("parent not found in GDL %s", parent_udi)); 353 return; 354 } 355 356 if ((node = di_init (devfs_path, DINFOCPYALL)) == DI_NODE_NIL) { 357 HAL_INFO (("device not found in devinfo %s", devfs_path)); 358 return; 359 } 360 361 HAL_INFO (("device %s parent %s", hal_device_get_udi (d), parent_udi)); 362 devinfo_lofi_add_major (parent, node, devfs_path, NULL, TRUE, d); 363 364 di_fini (node); 365 366 hotplug_event_process_queue (); 367 } 368 369 static void 370 sysevent_lofi_remove(gchar *parent_devfs_path, gchar *name) 371 { 372 devinfo_lofi_remove_minor(parent_devfs_path, name); 373 hotplug_event_process_queue (); 374 } 375 376 static HalDevice * 377 lookup_parent(char *devfs_path) 378 { 379 gchar *path = NULL; 380 HalDevice *parent = NULL; 381 char *p; 382 383 path = strdup (devfs_path); 384 p = strrchr (path, '/'); 385 if (p == NULL) { 386 free (path); 387 return (NULL); 388 } 389 *p = '\0'; 390 391 /* Look up the parent node in the gdl. */ 392 parent = hal_device_store_match_key_value_string (hald_get_gdl (), 393 "solaris.devfs_path", path); 394 395 if (parent == NULL) { 396 /* Look up the parent node in the tdl. */ 397 parent = hal_device_store_match_key_value_string (hald_get_tdl (), 398 "solaris.devfs_path", path); 399 } 400 401 free (path); 402 return (parent); 403 } 404 405 /* 406 * Handle the USB bus devices hot plugging events. 407 */ 408 static void 409 sysevent_devfs_add(gchar *devfs_path) 410 { 411 di_node_t node; 412 HalDevice *parent; 413 char *driver_name; 414 415 HAL_INFO (("devfs_handle: %s", devfs_path)); 416 417 if ((node = di_init (devfs_path, DINFOCPYALL)) == DI_NODE_NIL) { 418 HAL_INFO (("device not found in devinfo %s", devfs_path)); 419 return; 420 } 421 422 if ((driver_name = di_driver_name (node)) == NULL) 423 goto out; 424 425 /* The disk and printer devices are handled by EC_DEV_ADD class. */ 426 if ((strcmp (driver_name, "scsa2usb") == 0) || 427 (strcmp (driver_name, "usbprn") == 0)) 428 goto out; 429 430 if ((parent = lookup_parent (devfs_path)) == NULL) 431 goto out; 432 433 devinfo_usb_add (parent, node, devfs_path, NULL); 434 435 di_fini (node); 436 437 hotplug_event_process_queue (); 438 439 return; 440 441 out: 442 di_fini (node); 443 } 444 445 static void 446 sysevent_pwrctl(gchar *class, gchar *subclass, gchar *phys_path, 447 gchar *dev_name, gchar *dev_hid, gchar *dev_uid, uint_t dev_index) 448 { 449 const gchar prefix[] = "/org/freedesktop/Hal/devices/pseudo/acpi_drv_0"; 450 gchar udi[HAL_PATH_MAX]; 451 452 if (strcmp(dev_hid, "PNP0C0A") == 0) { 453 snprintf(udi, sizeof(udi), "%s_battery%d_0", prefix, dev_index); 454 devinfo_battery_device_rescan(phys_path, udi); 455 } else if (strcmp(dev_hid, "ACPI0003") == 0) { 456 snprintf(udi, sizeof (udi), "%s_ac%d_0", prefix, dev_index); 457 devinfo_battery_device_rescan(phys_path, udi); 458 } else if (strcmp(dev_hid, "PNP0C0D") == 0) { 459 snprintf(udi, sizeof (udi), "%s_lid_0", prefix); 460 devinfo_lid_device_rescan(subclass, udi); 461 } else if (strcmp(subclass, ESC_PWRCTL_POWER_BUTTON) == 0) { 462 devinfo_power_button_rescan(); 463 } else if ((strcmp(subclass, ESC_PWRCTL_BRIGHTNESS_UP) == 0) || 464 (strcmp(subclass, ESC_PWRCTL_BRIGHTNESS_DOWN) == 0)) { 465 devinfo_brightness_hotkeys_rescan(subclass); 466 } else { 467 HAL_INFO(("Unmatched EC_PWRCTL")); 468 } 469 } 470