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
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.
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]
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
32 #include <libsysevent.h>
33 #include <sys/sysevent/eventdefs.h>
34 #include <sys/sysevent/dev.h>
39 #include "libdiskmgt.h"
40 #include "disks_private.h"
43 struct event_list
*next
;
47 static struct event_list
*events
= NULL
;
48 static int event_error
= 0;
49 static int event_break
= 0;
50 static mutex_t queue_lock
;
51 static sema_t semaphore
;
54 * When we add a controller we get an add event for each drive on the
55 * controller. We don't want to walk the devtree for each drive since
56 * we will get the same information each time. So, the solution is to
57 * wait for a few seconds for all of the add events to come in and then
58 * do a single walk. If an add event comes in after we start the walk, we
59 * need to do another walk since we might have missed that drive.
61 * State: 0 - no walker; 1 - walker waiting; 2 - walker running
62 * 0 -> 1; wait a few seconds
63 * 1 -> 2; walking the devtree
64 * 2 -> either 0 or 1 (see below)
65 * While running (state 2), if event comes in, go back to waiting (state 1)
66 * after the walk otherwise go back to none (state 0).
68 * walker_lock protects walker_state & events_pending
71 #define WALK_WAITING 1
72 #define WALK_RUNNING 2
73 #define WALK_WAIT_TIME 60 /* wait 60 seconds */
75 static mutex_t walker_lock
;
76 static int walker_state
= WALK_NONE
;
77 static int events_pending
= 0;
79 static int sendevents
= 0;
81 static void add_event_to_queue(nvlist_t
*event
);
82 static void cb_watch_events();
83 static void event_handler(sysevent_t
*ev
);
84 static void print_nvlist(char *prefix
, nvlist_t
*list
);
85 static void walk_devtree();
86 static void walker(void *arg
);
88 static void(*callback
)(nvlist_t
*, int) = NULL
;
91 dm_get_event(int *errp
)
93 nvlist_t
*event
= NULL
;
97 /* wait until there is an event in the queue */
100 (void) sema_wait(&semaphore
);
108 (void) mutex_lock(&queue_lock
);
110 /* first see if we ran out of memory since the last call */
111 if (event_error
!= 0) {
115 } else if (events
!= NULL
) {
116 struct event_list
*tmpp
;
118 event
= events
->event
;
124 (void) mutex_unlock(&queue_lock
);
126 if (*errp
!= 0 || event
!= NULL
) {
135 dm_init_event_queue(void (*cb
)(nvlist_t
*, int), int *errp
)
137 if (sendevents
== 1) {
138 /* we were already initialized, see what changes to make */
140 if (cb
!= callback
) {
144 /* clearing the cb so shutdown the internal cb thread */
146 (void) sema_post(&semaphore
);
149 /* installing a cb; we didn't have one before */
150 thread_t watch_thread
;
152 *errp
= thr_create(NULL
, 0,
153 (void *(*)(void *))cb_watch_events
, NULL
, THR_DAEMON
,
159 /* first time to initialize */
162 *errp
= sema_init(&semaphore
, 0, USYNC_THREAD
, NULL
);
168 thread_t watch_thread
;
172 *errp
= thr_create(NULL
, 0,
173 (void *(*)(void *))cb_watch_events
, NULL
, THR_DAEMON
,
180 events_new_event(char *name
, int dtype
, char *etype
)
182 nvlist_t
*event
= NULL
;
188 if (nvlist_alloc(&event
, NVATTRS
, 0) != 0) {
195 nvlist_add_string(event
, DM_EV_NAME
, name
) != 0) {
200 nvlist_add_uint32(event
, DM_EV_DTYPE
, dtype
) != 0) {
204 if (nvlist_add_string(event
, DM_EV_TYPE
, etype
) != 0) {
214 add_event_to_queue(event
);
218 events_new_slice_event(char *dev
, char *type
)
220 events_new_event(basename(dev
), DM_SLICE
, type
);
224 events_start_event_watcher()
226 sysevent_handle_t
*shp
;
227 const char *subclass_list
[1];
229 /* Bind event handler and create subscriber handle */
230 shp
= sysevent_bind_handle(event_handler
);
233 /* keep going when we're debugging */
234 (void) fprintf(stderr
, "ERROR: bind failed %d\n", errno
);
240 subclass_list
[0] = ESC_DISK
;
241 if (sysevent_subscribe_event(shp
, EC_DEV_ADD
, subclass_list
, 1) != 0) {
243 /* keep going when we're debugging */
244 (void) fprintf(stderr
, "ERROR: subscribe failed\n");
249 if (sysevent_subscribe_event(shp
, EC_DEV_REMOVE
, subclass_list
, 1)
252 /* keep going when we're debugging */
253 (void) fprintf(stderr
, "ERROR: subscribe failed\n");
263 add_event_to_queue(nvlist_t
*event
)
265 (void) mutex_lock(&queue_lock
);
268 event_error
= ENOMEM
;
269 (void) mutex_unlock(&queue_lock
);
273 if (events
== NULL
) {
275 events
= (struct event_list
*)malloc(sizeof (struct event_list
));
276 if (events
== NULL
) {
277 event_error
= ENOMEM
;
281 events
->event
= event
;
285 /* already have events in the queue */
286 struct event_list
*ep
;
287 struct event_list
*new_event
;
289 /* find the last element in the list */
290 for (ep
= events
; ep
->next
!= NULL
; ep
= ep
->next
);
292 new_event
= (struct event_list
*)malloc(sizeof (struct event_list
));
293 if (new_event
== NULL
) {
294 event_error
= ENOMEM
;
297 new_event
->next
= NULL
;
298 new_event
->event
= event
;
299 ep
->next
= new_event
;
303 (void) mutex_unlock(&queue_lock
);
305 (void) sema_post(&semaphore
);
316 event
= dm_get_event(&error
);
317 if (callback
== NULL
) {
321 callback(event
, error
);
326 event_handler(sysevent_t
*ev
)
331 class_name
= sysevent_get_class_name(ev
);
333 (void) fprintf(stderr
, "****EVENT: %s %s ", class_name
,
334 sysevent_get_subclass_name(ev
));
335 if ((pub
= sysevent_get_pub_name(ev
)) != NULL
) {
336 (void) fprintf(stderr
, "%s\n", pub
);
339 (void) fprintf(stderr
, "\n");
343 if (libdiskmgt_str_eq(class_name
, EC_DEV_ADD
)) {
344 /* batch up the adds into a single devtree walk */
347 } else if (libdiskmgt_str_eq(class_name
, EC_DEV_REMOVE
)) {
348 nvlist_t
*nvlist
= NULL
;
349 char *dev_name
= NULL
;
351 (void) sysevent_get_attr_list(ev
, &nvlist
);
352 if (nvlist
!= NULL
) {
353 (void) nvlist_lookup_string(nvlist
, DEV_NAME
, &dev_name
);
356 print_nvlist("**** ", nvlist
);
360 if (dev_name
!= NULL
) {
361 cache_update(DM_EV_DISK_DELETE
, dev_name
);
369 * This is a debugging function only.
372 print_nvlist(char *prefix
, nvlist_t
*list
)
376 nvp
= nvlist_next_nvpair(list
, NULL
);
377 while (nvp
!= NULL
) {
386 attrname
= nvpair_name(nvp
);
387 switch (nvpair_type(nvp
)) {
388 case DATA_TYPE_STRING
:
389 (void) nvpair_value_string(nvp
, &str
);
390 (void) fprintf(stderr
, "%s%s: %s\n", prefix
, attrname
, str
);
393 case DATA_TYPE_STRING_ARRAY
:
394 (void) nvpair_value_string_array(nvp
, &str_array
, &cnt
);
395 (void) fprintf(stderr
, "%s%s:\n", prefix
, attrname
);
396 for (i
= 0; i
< cnt
; i
++) {
397 (void) fprintf(stderr
, "%s %s\n", prefix
, str_array
[i
]);
401 case DATA_TYPE_UINT32
:
402 (void) nvpair_value_uint32(nvp
, &ui32
);
403 (void) fprintf(stderr
, "%s%s: %u\n", prefix
, attrname
, ui32
);
406 case DATA_TYPE_UINT64
:
407 (void) nvpair_value_uint64(nvp
, &ui64
);
409 (void) fprintf(stderr
, "%s%s: %lu\n", prefix
, attrname
, ui64
);
411 (void) fprintf(stderr
, "%s%s: %llu\n", prefix
, attrname
, ui64
);
416 case DATA_TYPE_BOOLEAN
:
417 (void) fprintf(stderr
, "%s%s: true\n", prefix
, attrname
);
421 (void) fprintf(stderr
, "%s%s: UNSUPPORTED TYPE\n", prefix
,
426 nvp
= nvlist_next_nvpair(list
, nvp
);
431 * Batch up the adds into a single devtree walk. We can get a bunch of
432 * adds when we add a controller since we will get an add event for each
438 thread_t walk_thread
;
440 (void) mutex_lock(&walker_lock
);
442 switch (walker_state
) {
444 if (thr_create(NULL
, 0, (void *(*)(void *))walker
, NULL
,
445 THR_DAEMON
, &walk_thread
) == 0) {
446 walker_state
= WALK_WAITING
;
451 /* absorb the event and do nothing */
459 (void) mutex_unlock(&walker_lock
);
469 /* start by wating for a few seconds to absorb extra events */
470 (void) sleep(WALK_WAIT_TIME
);
472 (void) mutex_lock(&walker_lock
);
473 walker_state
= WALK_RUNNING
;
474 (void) mutex_unlock(&walker_lock
);
476 cache_update(DM_EV_DISK_ADD
, NULL
);
478 (void) mutex_lock(&walker_lock
);
480 if (events_pending
) {
482 walker_state
= WALK_WAITING
;
485 walker_state
= WALK_NONE
;
489 (void) mutex_unlock(&walker_lock
);
491 } while (walk_again
);