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 <glib.h>
30 
31 #include "../osspec.h"
32 #include "../logger.h"
33 #include "../hald.h"
34 #include "../hald_dbus.h"
35 #include "../device_info.h"
36 #include "../util.h"
37 #include "osspec_solaris.h"
38 #include "hotplug.h"
39 #include "devinfo.h"
40 #include "devinfo_storage.h"
41 #include "sysevent.h"
42 
43 #ifndef ESC_LOFI
44 #define ESC_LOFI "lofi"
45 #endif
46 
47 static void	sysevent_dev_handler(sysevent_t *);
48 static gboolean sysevent_iochannel_data(GIOChannel *, GIOCondition, gpointer);
49 static void	sysevent_dev_add(gchar *, gchar *);
50 static void	sysevent_dev_remove(gchar *, gchar *);
51 static void	sysevent_dev_branch(gchar *);
52 static void	sysevent_lofi_add(gchar *, gchar *);
53 static void	sysevent_lofi_remove(gchar *, gchar *);
54 
55 static sysevent_handle_t	*shp;
56 
57 static int sysevent_pipe_fds[2];
58 static GIOChannel *sysevent_iochannel;
59 static guint sysevent_source_id;
60 
61 gboolean
62 sysevent_init(void)
63 {
64 	GError *err = NULL;
65 	const char	*subcl[3];
66 
67         /*
68 	 * pipe used to serialize sysevents through the main loop
69  	 */
70         if (pipe (sysevent_pipe_fds) != 0) {
71                 HAL_INFO (("pipe() failed errno=%d", errno));
72 		return (FALSE);
73         }
74         sysevent_iochannel = g_io_channel_unix_new (sysevent_pipe_fds[0]);
75 	if (sysevent_iochannel == NULL) {
76                 HAL_INFO (("g_io_channel_unix_new failed"));
77 		return (FALSE);
78 	}
79 	g_io_channel_set_flags (sysevent_iochannel, G_IO_FLAG_NONBLOCK, &err);
80         sysevent_source_id = g_io_add_watch (
81                 sysevent_iochannel, G_IO_IN, sysevent_iochannel_data, NULL);
82 
83 	shp = sysevent_bind_handle(sysevent_dev_handler);
84 	if (shp == NULL) {
85 		HAL_INFO (("sysevent_bind_handle failed %d", errno));
86 		return (FALSE);
87 	}
88 
89 	subcl[0] = ESC_DISK;
90 	subcl[1] = ESC_LOFI;
91 	subcl[2] = ESC_PRINTER;
92 	if (sysevent_subscribe_event(shp, EC_DEV_ADD, subcl, 3) != 0) {
93 		HAL_INFO (("subscribe(dev_add) failed %d", errno));
94 		sysevent_unbind_handle(shp);
95 		return (FALSE);
96 	}
97 	if (sysevent_subscribe_event(shp, EC_DEV_REMOVE, subcl, 3) != 0) {
98 		HAL_INFO (("subscribe(dev_remove) failed %d", errno));
99 		sysevent_unbind_handle(shp);
100 		return (FALSE);
101 	}
102 
103 	subcl[0] = ESC_DEV_BRANCH_REMOVE;
104 	if (sysevent_subscribe_event(shp, EC_DEV_BRANCH, subcl, 1) != 0) {
105 		HAL_INFO (("subscribe(dev_branch) failed %d", errno));
106 		sysevent_unbind_handle(shp);
107 		return (FALSE);
108 	}
109 
110 	return (B_TRUE);
111 }
112 
113 void
114 sysevent_fini(void)
115 {
116 	sysevent_unbind_handle(shp);
117 	shp = NULL;
118 }
119 
120 static void
121 sysevent_dev_handler(sysevent_t *ev)
122 {
123 	char		*class;
124 	char		*subclass;
125 	nvlist_t	*attr_list;
126 	char		*phys_path;
127 	char		*dev_name;
128 	char		s[1024];
129 	ssize_t		nwritten;
130 
131 	if ((class = sysevent_get_class_name(ev)) == NULL)
132 		return;
133 
134 	if ((subclass = sysevent_get_subclass_name(ev)) == NULL)
135 		return;
136 
137 	if (sysevent_get_attr_list(ev, &attr_list) != 0)
138 		return;
139 
140 	if (nvlist_lookup_string(attr_list, DEV_PHYS_PATH, &phys_path) != 0)
141 		goto out;
142 
143 	if (nvlist_lookup_string(attr_list, DEV_NAME, &dev_name) != 0)
144 		dev_name = "";
145 
146 	snprintf(s, sizeof (s), "%s %s %s %s\n",
147 		class, subclass, phys_path, dev_name);
148 	nwritten = write(sysevent_pipe_fds[1], s, strlen(s) + 1);
149 
150 	HAL_INFO (("sysevent_dev_handler: wrote %d bytes", nwritten));
151 
152 out:
153 	nvlist_free(attr_list);
154 }
155 
156 static gboolean
157 sysevent_iochannel_data (GIOChannel *source,
158                     GIOCondition condition,
159                     gpointer user_data)
160 {
161         GError *err = NULL;
162 	gchar *s = NULL;
163 	gsize len;
164 	int matches;
165 	gchar class[1024];
166 	gchar subclass[1024];
167 	gchar phys_path[1024];
168 	gchar dev_name[1024];
169 
170 	HAL_INFO (("sysevent_iochannel_data"));
171 
172 	while (g_io_channel_read_line (sysevent_iochannel, &s, &len, NULL,
173 					&err) == G_IO_STATUS_NORMAL) {
174 		if (len == 0) {
175 			break;
176 		}
177 
178 		class[0] = subclass[0] = phys_path[0] = dev_name[0] = '\0';
179 		matches = sscanf(s, "%s %s %s %s", class, subclass, phys_path, dev_name);
180 		g_free (s);
181 		s = NULL;
182 		if (matches < 3) {
183 			continue;
184 		}
185 		HAL_INFO (("sysevent: class=%s, sub=%s", class, subclass));
186 
187 		if (strcmp(class, EC_DEV_ADD) == 0) {
188 			if ((strcmp(subclass, ESC_DISK) == 0) ||
189 			    (strcmp(subclass, ESC_PRINTER) == 0)) {
190 				sysevent_dev_add(phys_path, dev_name);
191 			} else if (strcmp(subclass, ESC_LOFI) == 0) {
192 				sysevent_lofi_add(phys_path, dev_name);
193 			}
194 		} else if (strcmp(class, EC_DEV_REMOVE) == 0) {
195 			if ((strcmp(subclass, ESC_DISK) == 0) ||
196 			    (strcmp(subclass, ESC_PRINTER) == 0)) {
197 				sysevent_dev_remove(phys_path, dev_name);
198 			} else if (strcmp(subclass, ESC_LOFI) == 0) {
199 				sysevent_lofi_remove(phys_path, dev_name);
200 			}
201 		} else if (strcmp(class, EC_DEV_BRANCH) == 0) {
202 			sysevent_dev_branch(phys_path);
203 		}
204 	}
205 
206 	if (err) {
207 		g_error_free (err);
208 	}
209 
210 	return (TRUE);
211 }
212 
213 static void
214 sysevent_dev_add(gchar *devfs_path, gchar *name)
215 {
216 	gchar	*parent_devfs_path, *hotplug_devfs_path;
217 	HalDevice *parent;
218 
219 	HAL_INFO (("dev_add: %s %s", name, devfs_path));
220 
221         parent = hal_util_find_closest_ancestor (devfs_path, &parent_devfs_path, &hotplug_devfs_path);
222 	if (parent == NULL) {
223 		return;
224 	}
225 
226 	HAL_INFO (("dev_add: parent=%s", parent_devfs_path));
227 	HAL_INFO (("dev_add: real=%s", hotplug_devfs_path));
228 
229 	devinfo_add (parent, hotplug_devfs_path);
230 
231 	g_free (parent_devfs_path);
232 	g_free (hotplug_devfs_path);
233 
234 	hotplug_event_process_queue ();
235 }
236 
237 static void
238 sysevent_dev_remove(gchar *devfs_path, gchar *name)
239 {
240 	HAL_INFO (("dev_remove: %s %s", name, devfs_path));
241 
242 	devinfo_remove_branch (devfs_path, NULL);
243 	hotplug_event_process_queue ();
244 }
245 
246 static void
247 sysevent_dev_branch(gchar *devfs_path)
248 {
249 	HAL_INFO (("branch_remove: %s", devfs_path));
250 
251 	devinfo_remove_branch (devfs_path, NULL);
252 	hotplug_event_process_queue ();
253 }
254 
255 static void
256 sysevent_lofi_add(gchar *devfs_path, gchar *name)
257 {
258 	di_node_t node;
259 	const char *parent_udi;
260 	HalDevice *d, *parent;
261 
262 	HAL_INFO (("lofi_add: %s %s", name, devfs_path));
263 
264 	if ((d = hal_device_store_match_key_value_string (hald_get_gdl (),
265 	    "solaris.devfs_path", devfs_path)) == NULL) {
266 		HAL_INFO (("device not found in GDL %s", devfs_path));
267 		return;
268 	}
269 	parent_udi = hal_device_property_get_string (d, "info.parent");
270 	if ((parent_udi == NULL) || (strlen(parent_udi) == 0)) {
271 		HAL_INFO (("parent not found in GDL %s", parent_udi));
272 		return;
273 	}
274 	if ((parent = hal_device_store_match_key_value_string (hald_get_gdl (),
275 	    "info.udi", parent_udi)) == NULL) {
276 		HAL_INFO (("parent not found in GDL %s", parent_udi));
277 		return;
278 	}
279 
280 	if ((node = di_init (devfs_path, DINFOCPYALL)) == DI_NODE_NIL) {
281 		HAL_INFO (("device not found in devinfo %s", devfs_path));
282 		return;
283 	}
284 
285 	HAL_INFO (("device %s parent %s", hal_device_get_udi (d), parent_udi));
286 	devinfo_lofi_add_major (parent, node, devfs_path, NULL, TRUE, d);
287 
288 	di_fini (node);
289 
290 	hotplug_event_process_queue ();
291 }
292 
293 static void
294 sysevent_lofi_remove(gchar *parent_devfs_path, gchar *name)
295 {
296 	devinfo_lofi_remove_minor(parent_devfs_path, name);
297 	hotplug_event_process_queue ();
298 }
299