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