1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <unistd.h>
28 #include <stdlib.h>
29 #include <errno.h>
30 #include <string.h>
31 #include <door.h>
32 #include <fcntl.h>
33 #include <syslog.h>
34 #include <sys/sunddi.h>
35 #include <libsysevent.h>
36 #include <picl.h>
37 #include <pthread.h>
38 #include "piclevent.h"
39 #include <sys/sysevent/eventdefs.h>
40 #include <sys/sysevent/dr.h>
41 
42 #define	PICLSLM_DOOR_FAILED	gettext("PICL SLM door create failed\n")
43 
44 /*
45  * syseventd event handler
46  */
47 static	int	piclslm_debug = 0;
48 static	int	piclslm_deliver_event(sysevent_t *ev, int flag);
49 static	int	door_fd = -1;
50 
51 typedef struct nvlist_queue {
52 	char			*nvq_item;	/* packed nvlist */
53 	size_t			nvq_sz;		/* buf size */
54 	struct nvlist_queue	*nvq_next;
55 } nvlist_queue_t;
56 
57 static nvlist_queue_t	*nvq_head;
58 static nvlist_queue_t	*nvq_tail;
59 
60 static mutex_t	nvq_lock;
61 static cond_t	nvq_cv;
62 static thread_t	piclslm_deliver_thr_id;
63 static int	cleanup;
64 
65 static struct slm_mod_ops piclslm_mod_ops = {
66 	SE_MAJOR_VERSION, SE_MINOR_VERSION, SE_MAX_RETRY_LIMIT,
67 	piclslm_deliver_event};
68 
69 
70 static void
init_queue(void)71 init_queue(void)
72 {
73 	nvq_head = NULL;
74 	nvq_tail = NULL;
75 }
76 
77 static int
add_to_queue(char * nvl,size_t sz)78 add_to_queue(char *nvl, size_t sz)
79 {
80 	nvlist_queue_t	*new_nvq;
81 
82 	new_nvq = malloc(sizeof (*new_nvq));
83 	if (new_nvq == NULL)
84 		return (-1);
85 
86 	new_nvq->nvq_item = nvl;
87 	new_nvq->nvq_sz = sz;
88 	new_nvq->nvq_next = NULL;
89 
90 	if (nvq_head == NULL)
91 		nvq_head = new_nvq;
92 	else
93 		nvq_tail->nvq_next = new_nvq;
94 	nvq_tail = new_nvq;
95 
96 	return (0);
97 }
98 
99 static nvlist_queue_t *
remove_from_queue(void)100 remove_from_queue(void)
101 {
102 	nvlist_queue_t	*nvqp;
103 
104 	if (nvq_head == NULL)
105 		return (NULL);
106 
107 	nvqp = nvq_head;
108 	nvq_head = nvq_head->nvq_next;
109 	if (nvq_head == NULL)
110 		nvq_tail = NULL;
111 	return (nvqp);
112 }
113 
114 static void
free_nvqueue(nvlist_queue_t * nvqp)115 free_nvqueue(nvlist_queue_t *nvqp)
116 {
117 	free(nvqp->nvq_item);
118 	free(nvqp);
119 }
120 
121 /*
122  * deliver the event to the plugin if the door exists
123  */
124 static void
post_piclevent(char * pack_buf,size_t nvl_size)125 post_piclevent(char *pack_buf, size_t nvl_size)
126 {
127 	door_arg_t		darg;
128 
129 	darg.data_ptr = pack_buf;
130 	darg.data_size = nvl_size;
131 	darg.desc_ptr = NULL;
132 	darg.desc_num = 0;
133 	darg.rbuf = NULL;
134 	darg.rsize = 0;
135 
136 	if (door_fd < 0 || door_call(door_fd, &darg) < 0) {
137 		if (door_fd >= 0) {
138 			if (errno != EBADF) {
139 				return;
140 			}
141 
142 			/*
143 			 * It's not a valid door file descriptor.
144 			 * Close and reopen the door and try again
145 			 * as "picld" may have restarted.
146 			 */
147 			(void) close(door_fd);
148 		}
149 
150 		door_fd = open(PICLEVENT_DOOR, O_RDONLY);
151 		if (piclslm_debug)
152 			syslog(LOG_INFO,
153 			    "picl_slm: opened door %s door_fd: %d\n",
154 			    PICLEVENT_DOOR, door_fd);
155 		if (door_fd < 0 || door_call(door_fd, &darg) < 0) {
156 			return;
157 		}
158 	}
159 	if (piclslm_debug)
160 		syslog(LOG_INFO,
161 		    "picl_slm: sent sysevent door:%d pack_buf:%p size:0x%x\n",
162 		    door_fd, pack_buf, nvl_size);
163 }
164 
165 /*ARGSUSED*/
166 static void *
piclslm_deliver_thr(void * args)167 piclslm_deliver_thr(void *args)
168 {
169 	nvlist_queue_t		*nvqp;
170 
171 	for (;;) {
172 		(void) mutex_lock(&nvq_lock);
173 		while (nvq_head == NULL && cleanup == 0) {
174 			(void) cond_wait(&nvq_cv, &nvq_lock);
175 		}
176 		nvqp = remove_from_queue();
177 		(void) mutex_unlock(&nvq_lock);
178 		while (nvqp) {
179 			post_piclevent(nvqp->nvq_item, nvqp->nvq_sz);
180 			free_nvqueue(nvqp);
181 			(void) mutex_lock(&nvq_lock);
182 			nvqp = remove_from_queue();
183 			(void) mutex_unlock(&nvq_lock);
184 		}
185 		if (cleanup)
186 			return (NULL);
187 	}
188 	/*NOTREACHED*/
189 }
190 
191 /*
192  * returns 0 if arguments successfully added to nvl, EINVAL if arguments missing
193  * from ev and EAGAIN if nvlist_add_string() fails
194  */
195 static int
piclslm_add_ec_devfs_args(nvlist_t * nvl,sysevent_t * ev)196 piclslm_add_ec_devfs_args(nvlist_t *nvl, sysevent_t *ev)
197 {
198 	sysevent_value_t se_val;
199 
200 	if (sysevent_lookup_attr(ev, DEVFS_PATHNAME, SE_DATA_TYPE_STRING,
201 	    &se_val) != 0 || se_val.value.sv_string == NULL) {
202 		return (EINVAL);
203 	}
204 	if (nvlist_add_string(nvl, PICLEVENTARG_DEVFS_PATH,
205 	    se_val.value.sv_string)) {
206 		return (EAGAIN);
207 	}
208 	return (0);
209 }
210 
211 /*
212  * returns 0 if arguments successfully added to nvl, EINVAL if arguments missing
213  * from ev and EAGAIN if nvlist_add_string() fails
214  */
215 static int
piclslm_add_ec_dr_args(nvlist_t * nvl,sysevent_t * ev)216 piclslm_add_ec_dr_args(nvlist_t *nvl, sysevent_t *ev)
217 {
218 	sysevent_value_t se_val;
219 
220 	if (sysevent_lookup_attr(ev, DR_AP_ID, SE_DATA_TYPE_STRING,
221 	    &se_val) != 0 || se_val.value.sv_string == NULL) {
222 		return (EINVAL);
223 	}
224 	if (nvlist_add_string(nvl, PICLEVENTARG_AP_ID,
225 	    se_val.value.sv_string)) {
226 		return (EAGAIN);
227 	}
228 	if (sysevent_lookup_attr(ev, DR_HINT, SE_DATA_TYPE_STRING,
229 	    &se_val) != 0 || se_val.value.sv_string == NULL) {
230 		if (nvlist_add_string(nvl, PICLEVENTARG_HINT, ""))
231 			return (EAGAIN);
232 	} else {
233 		if (nvlist_add_string(nvl, PICLEVENTARG_HINT,
234 		    se_val.value.sv_string))
235 			return (EAGAIN);
236 	}
237 	return (0);
238 }
239 
240 /*
241  * returns 0 if arguments successfully added to nvl, EINVAL if arguments missing
242  * from ev and EAGAIN if nvlist_add_string() fails
243  */
244 static int
piclslm_add_ec_dr_req_args(nvlist_t * nvl,sysevent_t * ev)245 piclslm_add_ec_dr_req_args(nvlist_t *nvl, sysevent_t *ev)
246 {
247 	nvlist_t *nvlist = NULL;
248 	char *ap_id = NULL;
249 	char *dr_req = NULL;
250 
251 	if (sysevent_get_attr_list(ev, &nvlist)) {
252 		return (EAGAIN);
253 	}
254 
255 	if (nvlist_lookup_string(nvlist, DR_AP_ID, &ap_id) != 0 ||
256 	    ap_id == NULL) {
257 		nvlist_free(nvlist);
258 		return (EINVAL);
259 	}
260 
261 	if (nvlist_add_string(nvl, PICLEVENTARG_AP_ID, ap_id)) {
262 		nvlist_free(nvlist);
263 		return (EAGAIN);
264 	}
265 
266 	dr_req = NULL;
267 	if (nvlist_lookup_string(nvlist, DR_REQ_TYPE, &dr_req) != 0)
268 		dr_req = "";
269 
270 	if (nvlist_add_string(nvl, PICLEVENTARG_DR_REQ_TYPE, dr_req)) {
271 		nvlist_free(nvlist);
272 		return (EAGAIN);
273 	}
274 
275 	if (piclslm_debug)
276 		syslog(LOG_DEBUG, "piclevent: dr_req_type = %s on %s\n",
277 		    (dr_req ? dr_req : "Investigate"), ap_id);
278 
279 	nvlist_free(nvlist);
280 	return (0);
281 }
282 
283 /*
284  * piclslm_deliver_event - called by syseventd to deliver an event buffer.
285  *			The event buffer is subsequently delivered to
286  *			picld.  If picld, is not responding to the
287  *			delivery attempt, we will ignore it.
288  */
289 /*ARGSUSED*/
290 static int
piclslm_deliver_event(sysevent_t * ev,int flag)291 piclslm_deliver_event(sysevent_t *ev, int flag)
292 {
293 	sysevent_t	*dupev;
294 	nvlist_t	*nvl;
295 	char		*ec;
296 	char		*esc;
297 	char		*ename;
298 	int		retval;
299 	char		*pack_buf;
300 	size_t		nvl_size;
301 	int		rval;
302 
303 	/*
304 	 * Filter out uninteresting events
305 	 */
306 	ec = sysevent_get_class_name(ev);
307 	esc = sysevent_get_subclass_name(ev);
308 	if (piclslm_debug)
309 		syslog(LOG_INFO,
310 		    "picl_slm: got sysevent  ev:%p class:%s subclass:%s\n",
311 		    ev, (ec) ? ec : "NULL", (esc) ? esc : "NULL");
312 	if ((ec == NULL) || (esc == NULL)) {
313 		return (0);
314 	} else if (strcmp(ec, EC_DEVFS) == 0) {
315 		if (strcmp(esc, ESC_DEVFS_DEVI_ADD) == 0)
316 			ename = strdup(PICLEVENT_SYSEVENT_DEVICE_ADDED);
317 		else if (strcmp(esc, ESC_DEVFS_DEVI_REMOVE) == 0)
318 			ename = strdup(PICLEVENT_SYSEVENT_DEVICE_REMOVED);
319 		else
320 			return (0);
321 	} else if (strcmp(ec, EC_DR) == 0) {
322 		if (strcmp(esc, ESC_DR_AP_STATE_CHANGE) == 0)
323 			ename = strdup(PICLEVENT_DR_AP_STATE_CHANGE);
324 		else if (strcmp(esc, ESC_DR_REQ) == 0)
325 			ename = strdup(PICLEVENT_DR_REQ);
326 		else
327 			return (0);
328 	} else {
329 		return (0);
330 	}
331 
332 	if (ename == NULL)
333 		return (EAGAIN);
334 
335 	/*
336 	 * Make a copy to expand attribute list
337 	 */
338 	dupev = sysevent_dup(ev);
339 	if (dupev == NULL) {
340 		free(ename);
341 		return (EAGAIN);
342 	}
343 
344 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME_TYPE, 0)) {
345 		free(ename);
346 		sysevent_free(dupev);
347 		return (EAGAIN);
348 	}
349 
350 	if (strcmp(ec, EC_DEVFS) == 0) {
351 		rval = piclslm_add_ec_devfs_args(nvl, dupev);
352 	} else if (strcmp(ec, EC_DR) == 0) {
353 		if (strcmp(esc, ESC_DR_REQ) == 0) {
354 			rval = piclslm_add_ec_dr_req_args(nvl, dupev);
355 		} else {
356 			rval = piclslm_add_ec_dr_args(nvl, dupev);
357 		}
358 	}
359 
360 	if (rval != 0) {
361 		free(ename);
362 		nvlist_free(nvl);
363 		sysevent_free(dupev);
364 		return ((rval == EAGAIN) ? EAGAIN : 0);
365 	}
366 
367 	pack_buf = NULL;
368 	if (nvlist_add_string(nvl, PICLEVENTARG_EVENT_NAME, ename) ||
369 	    nvlist_add_string(nvl, PICLEVENTARG_DATA_TYPE,
370 	    PICLEVENTARG_PICLEVENT_DATA) ||
371 	    nvlist_pack(nvl, &pack_buf, &nvl_size, NV_ENCODE_NATIVE, 0)) {
372 		free(ename);
373 		nvlist_free(nvl);
374 		sysevent_free(dupev);
375 		return (EAGAIN);
376 	}
377 
378 	/*
379 	 * Add nvlist_t to queue
380 	 */
381 	(void) mutex_lock(&nvq_lock);
382 	retval = add_to_queue(pack_buf, nvl_size);
383 	(void) cond_signal(&nvq_cv);
384 	(void) mutex_unlock(&nvq_lock);
385 
386 	nvlist_free(nvl);
387 	sysevent_free(dupev);
388 	free(ename);
389 	return (retval < 0 ? EAGAIN : 0);
390 }
391 
392 struct slm_mod_ops *
slm_init(void)393 slm_init(void)
394 {
395 	cleanup = 0;
396 
397 	init_queue();
398 
399 	(void) mutex_init(&nvq_lock, USYNC_THREAD, NULL);
400 	(void) cond_init(&nvq_cv, USYNC_THREAD, NULL);
401 
402 	if (thr_create(NULL, 0, piclslm_deliver_thr,
403 	    NULL, THR_BOUND, &piclslm_deliver_thr_id) != 0) {
404 		(void) mutex_destroy(&nvq_lock);
405 		(void) cond_destroy(&nvq_cv);
406 		return (NULL);
407 	}
408 	return (&piclslm_mod_ops);
409 }
410 
411 void
slm_fini(void)412 slm_fini(void)
413 {
414 	/*
415 	 * Wait for all events to be sent
416 	 */
417 	(void) mutex_lock(&nvq_lock);
418 	cleanup = 1;
419 	(void) cond_signal(&nvq_cv);
420 	(void) mutex_unlock(&nvq_lock);
421 
422 	/* Wait for delivery thread to exit */
423 	(void) thr_join(piclslm_deliver_thr_id, NULL, NULL);
424 
425 	(void) mutex_destroy(&nvq_lock);
426 	(void) cond_destroy(&nvq_cv);
427 
428 	if (door_fd >= 0)
429 		(void) close(door_fd);
430 	door_fd = -1;
431 }
432