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