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