1 /*************************************************************************** 2 * 3 * sysevent.c : Solaris sysevents 4 * 5 * Copyright 2007 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 "sysevent.h" 44 45 #ifndef ESC_LOFI 46 #define ESC_LOFI "lofi" 47 #endif 48 49 static void sysevent_dev_handler(sysevent_t *); 50 static gboolean sysevent_iochannel_data(GIOChannel *, GIOCondition, gpointer); 51 static void sysevent_dev_add(gchar *, gchar *); 52 static void sysevent_dev_remove(gchar *, gchar *); 53 static void sysevent_dev_branch(gchar *); 54 static void sysevent_lofi_add(gchar *, gchar *); 55 static void sysevent_lofi_remove(gchar *, gchar *); 56 57 static sysevent_handle_t *shp; 58 59 static int sysevent_pipe_fds[2]; 60 static GIOChannel *sysevent_iochannel; 61 static guint sysevent_source_id; 62 63 gboolean 64 sysevent_init(void) 65 { 66 GError *err = NULL; 67 const char *subcl[3]; 68 69 /* 70 * pipe used to serialize sysevents through the main loop 71 */ 72 if (pipe (sysevent_pipe_fds) != 0) { 73 HAL_INFO (("pipe() failed errno=%d", errno)); 74 return (FALSE); 75 } 76 sysevent_iochannel = g_io_channel_unix_new (sysevent_pipe_fds[0]); 77 if (sysevent_iochannel == NULL) { 78 HAL_INFO (("g_io_channel_unix_new failed")); 79 return (FALSE); 80 } 81 g_io_channel_set_flags (sysevent_iochannel, G_IO_FLAG_NONBLOCK, &err); 82 sysevent_source_id = g_io_add_watch ( 83 sysevent_iochannel, G_IO_IN, sysevent_iochannel_data, NULL); 84 85 shp = sysevent_bind_handle(sysevent_dev_handler); 86 if (shp == NULL) { 87 HAL_INFO (("sysevent_bind_handle failed %d", errno)); 88 return (FALSE); 89 } 90 91 subcl[0] = ESC_DISK; 92 subcl[1] = ESC_LOFI; 93 subcl[2] = ESC_PRINTER; 94 if (sysevent_subscribe_event(shp, EC_DEV_ADD, subcl, 3) != 0) { 95 HAL_INFO (("subscribe(dev_add) failed %d", errno)); 96 sysevent_unbind_handle(shp); 97 return (FALSE); 98 } 99 if (sysevent_subscribe_event(shp, EC_DEV_REMOVE, subcl, 3) != 0) { 100 HAL_INFO (("subscribe(dev_remove) failed %d", errno)); 101 sysevent_unbind_handle(shp); 102 return (FALSE); 103 } 104 105 subcl[0] = ESC_DEV_BRANCH_REMOVE; 106 if (sysevent_subscribe_event(shp, EC_DEV_BRANCH, subcl, 1) != 0) { 107 HAL_INFO (("subscribe(dev_branch) failed %d", errno)); 108 sysevent_unbind_handle(shp); 109 return (FALSE); 110 } 111 112 subcl[0] = ESC_ACPIEV_ADD; 113 subcl[1] = ESC_ACPIEV_REMOVE; 114 subcl[2] = ESC_ACPIEV_STATE_CHANGE; 115 if (sysevent_subscribe_event(shp, EC_ACPIEV, subcl, 3) != 0) { 116 HAL_INFO(("subscribe(dev_add) failed %d", errno)); 117 sysevent_unbind_handle(shp); 118 return (FALSE); 119 } 120 121 return (B_TRUE); 122 } 123 124 void 125 sysevent_fini(void) 126 { 127 sysevent_unbind_handle(shp); 128 shp = NULL; 129 } 130 131 static void 132 sysevent_dev_handler(sysevent_t *ev) 133 { 134 char *class; 135 char *subclass; 136 nvlist_t *attr_list; 137 char *phys_path; 138 char *dev_name; 139 char *dev_hid; 140 char *dev_uid; 141 uint_t dev_index; 142 char s[1024]; 143 ssize_t nwritten; 144 145 if ((class = sysevent_get_class_name(ev)) == NULL) 146 return; 147 148 if ((subclass = sysevent_get_subclass_name(ev)) == NULL) 149 return; 150 151 if (sysevent_get_attr_list(ev, &attr_list) != 0) 152 return; 153 154 if (strcmp(class, EC_ACPIEV) == 0) { 155 if (nvlist_lookup_string(attr_list, ACPIEV_DEV_PHYS_PATH, 156 &phys_path) != 0) { 157 goto out; 158 } 159 } else if (nvlist_lookup_string(attr_list, DEV_PHYS_PATH, &phys_path) 160 != 0) { 161 goto out; 162 } 163 164 if (nvlist_lookup_string(attr_list, DEV_NAME, &dev_name) != 0) { 165 if (strcmp(class, EC_ACPIEV) == 0) { 166 dev_name = "noname"; 167 } else { 168 dev_name = ""; 169 } 170 } 171 172 if (nvlist_lookup_string(attr_list, ACPIEV_DEV_HID, &dev_hid) != 0) { 173 dev_hid = ""; 174 } 175 if (nvlist_lookup_string(attr_list, ACPIEV_DEV_UID, &dev_uid) != 0) { 176 dev_uid = ""; 177 } 178 if (nvlist_lookup_uint32(attr_list, ACPIEV_DEV_INDEX, &dev_index) 179 != 0) { 180 dev_index = 0; 181 } 182 183 snprintf(s, sizeof (s), "%s %s %s %s %s %s %d\n", 184 class, subclass, phys_path, dev_name, dev_hid, dev_uid, dev_index); 185 nwritten = write(sysevent_pipe_fds[1], s, strlen(s) + 1); 186 187 HAL_INFO (("sysevent_dev_handler: wrote %d bytes", nwritten)); 188 189 out: 190 nvlist_free(attr_list); 191 } 192 193 static gboolean 194 sysevent_iochannel_data (GIOChannel *source, 195 GIOCondition condition, 196 gpointer user_data) 197 { 198 GError *err = NULL; 199 gchar *s = NULL; 200 gsize len; 201 int matches; 202 gchar class[1024]; 203 gchar subclass[1024]; 204 gchar phys_path[1024]; 205 gchar dev_name[1024]; 206 gchar dev_uid[1024]; 207 gchar dev_hid[1024]; 208 gchar udi[1024]; 209 uint_t dev_index; 210 211 HAL_INFO (("sysevent_iochannel_data")); 212 213 while (g_io_channel_read_line (sysevent_iochannel, &s, &len, NULL, 214 &err) == G_IO_STATUS_NORMAL) { 215 if (len == 0) { 216 break; 217 } 218 219 class[0] = subclass[0] = phys_path[0] = dev_name[0] = 220 dev_hid[0] = dev_uid[0] = '\0'; 221 matches = sscanf(s, "%s %s %s %s %s %s %d", class, subclass, 222 phys_path, dev_name, dev_hid, dev_uid, &dev_index); 223 g_free (s); 224 s = NULL; 225 if (matches < 3) { 226 continue; 227 } 228 HAL_INFO (("sysevent: class=%s, sub=%s", class, subclass)); 229 230 if (strcmp(class, EC_DEV_ADD) == 0) { 231 if ((strcmp(subclass, ESC_DISK) == 0) || 232 (strcmp(subclass, ESC_PRINTER) == 0)) { 233 sysevent_dev_add(phys_path, dev_name); 234 } else if (strcmp(subclass, ESC_LOFI) == 0) { 235 sysevent_lofi_add(phys_path, dev_name); 236 } 237 } else if (strcmp(class, EC_DEV_REMOVE) == 0) { 238 if ((strcmp(subclass, ESC_DISK) == 0) || 239 (strcmp(subclass, ESC_PRINTER) == 0)) { 240 sysevent_dev_remove(phys_path, dev_name); 241 } else if (strcmp(subclass, ESC_LOFI) == 0) { 242 sysevent_lofi_remove(phys_path, dev_name); 243 } 244 } else if (strcmp(class, EC_DEV_BRANCH) == 0) { 245 sysevent_dev_branch(phys_path); 246 } else if (strcmp(class, EC_ACPIEV) == 0) { 247 if (strcmp(dev_hid, "PNP0C0A") == 0) { 248 snprintf(udi, sizeof(udi), 249 "/org/freedesktop/Hal/devices/pseudo/" 250 "battery_0_battery%d_0", dev_index); 251 } else if (strcmp(dev_hid, "ACPI0003") == 0) { 252 snprintf(udi, sizeof(udi), 253 "/org/freedesktop/Hal/devices/pseudo/" 254 "battery_0_ac%d_0", dev_index); 255 } else { 256 HAL_INFO(("dev_hid %s unknown", dev_hid)); 257 continue; 258 } 259 devinfo_battery_device_rescan(phys_path, udi); 260 } 261 } 262 263 if (err) { 264 g_error_free (err); 265 } 266 267 return (TRUE); 268 } 269 270 static void 271 sysevent_dev_add(gchar *devfs_path, gchar *name) 272 { 273 gchar *parent_devfs_path, *hotplug_devfs_path; 274 HalDevice *parent; 275 276 HAL_INFO (("dev_add: %s %s", name, devfs_path)); 277 278 parent = hal_util_find_closest_ancestor (devfs_path, &parent_devfs_path, &hotplug_devfs_path); 279 if (parent == NULL) { 280 return; 281 } 282 283 HAL_INFO (("dev_add: parent=%s", parent_devfs_path)); 284 HAL_INFO (("dev_add: real=%s", hotplug_devfs_path)); 285 286 devinfo_add (parent, hotplug_devfs_path); 287 288 g_free (parent_devfs_path); 289 g_free (hotplug_devfs_path); 290 291 hotplug_event_process_queue (); 292 } 293 294 static void 295 sysevent_dev_remove(gchar *devfs_path, gchar *name) 296 { 297 HAL_INFO (("dev_remove: %s %s", name, devfs_path)); 298 299 devinfo_remove_branch (devfs_path, NULL); 300 hotplug_event_process_queue (); 301 } 302 303 static void 304 sysevent_dev_branch(gchar *devfs_path) 305 { 306 HAL_INFO (("branch_remove: %s", devfs_path)); 307 308 devinfo_remove_branch (devfs_path, NULL); 309 hotplug_event_process_queue (); 310 } 311 312 static void 313 sysevent_lofi_add(gchar *devfs_path, gchar *name) 314 { 315 di_node_t node; 316 const char *parent_udi; 317 HalDevice *d, *parent; 318 319 HAL_INFO (("lofi_add: %s %s", name, devfs_path)); 320 321 if ((d = hal_device_store_match_key_value_string (hald_get_gdl (), 322 "solaris.devfs_path", devfs_path)) == NULL) { 323 HAL_INFO (("device not found in GDL %s", devfs_path)); 324 return; 325 } 326 parent_udi = hal_device_property_get_string (d, "info.parent"); 327 if ((parent_udi == NULL) || (strlen(parent_udi) == 0)) { 328 HAL_INFO (("parent not found in GDL %s", parent_udi)); 329 return; 330 } 331 if ((parent = hal_device_store_match_key_value_string (hald_get_gdl (), 332 "info.udi", parent_udi)) == NULL) { 333 HAL_INFO (("parent not found in GDL %s", parent_udi)); 334 return; 335 } 336 337 if ((node = di_init (devfs_path, DINFOCPYALL)) == DI_NODE_NIL) { 338 HAL_INFO (("device not found in devinfo %s", devfs_path)); 339 return; 340 } 341 342 HAL_INFO (("device %s parent %s", hal_device_get_udi (d), parent_udi)); 343 devinfo_lofi_add_major (parent, node, devfs_path, NULL, TRUE, d); 344 345 di_fini (node); 346 347 hotplug_event_process_queue (); 348 } 349 350 static void 351 sysevent_lofi_remove(gchar *parent_devfs_path, gchar *name) 352 { 353 devinfo_lofi_remove_minor(parent_devfs_path, name); 354 hotplug_event_process_queue (); 355 } 356