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