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.
27 #pragma ident "%Z%%M% %I% %E% SMI"
34 #include <libsysevent.h>
35 #include <sys/sysevent/eventdefs.h>
36 #include <sys/sysevent/dev.h>
41 #include "libdiskmgt.h"
42 #include "disks_private.h"
45 struct event_list
*next
;
49 static struct event_list
*events
= NULL
;
50 static int event_error
= 0;
51 static int event_break
= 0;
52 static mutex_t queue_lock
;
53 static sema_t semaphore
;
56 * When we add a controller we get an add event for each drive on the
57 * controller. We don't want to walk the devtree for each drive since
58 * we will get the same information each time. So, the solution is to
59 * wait for a few seconds for all of the add events to come in and then
60 * do a single walk. If an add event comes in after we start the walk, we
61 * need to do another walk since we might have missed that drive.
63 * State: 0 - no walker; 1 - walker waiting; 2 - walker running
64 * 0 -> 1; wait a few seconds
65 * 1 -> 2; walking the devtree
66 * 2 -> either 0 or 1 (see below)
67 * While running (state 2), if event comes in, go back to waiting (state 1)
68 * after the walk otherwise go back to none (state 0).
70 * walker_lock protects walker_state & events_pending
73 #define WALK_WAITING 1
74 #define WALK_RUNNING 2
75 #define WALK_WAIT_TIME 60 /* wait 60 seconds */
77 static mutex_t walker_lock
;
78 static int walker_state
= WALK_NONE
;
79 static int events_pending
= 0;
81 static int sendevents
= 0;
83 static void add_event_to_queue(nvlist_t
*event
);
84 static void cb_watch_events();
85 static void event_handler(sysevent_t
*ev
);
86 static void print_nvlist(char *prefix
, nvlist_t
*list
);
87 static void walk_devtree();
88 static void walker(void *arg
);
90 static void(*callback
)(nvlist_t
*, int) = NULL
;
93 dm_get_event(int *errp
)
95 nvlist_t
*event
= NULL
;
99 /* wait until there is an event in the queue */
102 (void) sema_wait(&semaphore
);
110 (void) mutex_lock(&queue_lock
);
112 /* first see if we ran out of memory since the last call */
113 if (event_error
!= 0) {
117 } else if (events
!= NULL
) {
118 struct event_list
*tmpp
;
120 event
= events
->event
;
126 (void) mutex_unlock(&queue_lock
);
128 if (*errp
!= 0 || event
!= NULL
) {
137 dm_init_event_queue(void (*cb
)(nvlist_t
*, int), int *errp
)
139 if (sendevents
== 1) {
140 /* we were already initialized, see what changes to make */
142 if (cb
!= callback
) {
146 /* clearing the cb so shutdown the internal cb thread */
148 (void) sema_post(&semaphore
);
151 /* installing a cb; we didn't have one before */
152 thread_t watch_thread
;
154 *errp
= thr_create(NULL
, NULL
,
155 (void *(*)(void *))cb_watch_events
, NULL
, THR_DAEMON
,
161 /* first time to initialize */
164 *errp
= sema_init(&semaphore
, 0, USYNC_THREAD
, NULL
);
170 thread_t watch_thread
;
174 *errp
= thr_create(NULL
, NULL
,
175 (void *(*)(void *))cb_watch_events
, NULL
, THR_DAEMON
,
182 events_new_event(char *name
, int dtype
, char *etype
)
184 nvlist_t
*event
= NULL
;
190 if (nvlist_alloc(&event
, NVATTRS
, 0) != 0) {
197 nvlist_add_string(event
, DM_EV_NAME
, name
) != 0) {
202 nvlist_add_uint32(event
, DM_EV_DTYPE
, dtype
) != 0) {
206 if (nvlist_add_string(event
, DM_EV_TYPE
, etype
) != 0) {
216 add_event_to_queue(event
);
220 events_new_slice_event(char *dev
, char *type
)
222 events_new_event(basename(dev
), DM_SLICE
, type
);
226 events_start_event_watcher()
228 sysevent_handle_t
*shp
;
229 const char *subclass_list
[1];
231 /* Bind event handler and create subscriber handle */
232 shp
= sysevent_bind_handle(event_handler
);
235 /* keep going when we're debugging */
236 (void) fprintf(stderr
, "ERROR: bind failed %d\n", errno
);
242 subclass_list
[0] = ESC_DISK
;
243 if (sysevent_subscribe_event(shp
, EC_DEV_ADD
, subclass_list
, 1) != 0) {
245 /* keep going when we're debugging */
246 (void) fprintf(stderr
, "ERROR: subscribe failed\n");
251 if (sysevent_subscribe_event(shp
, EC_DEV_REMOVE
, subclass_list
, 1)
254 /* keep going when we're debugging */
255 (void) fprintf(stderr
, "ERROR: subscribe failed\n");
265 add_event_to_queue(nvlist_t
*event
)
267 (void) mutex_lock(&queue_lock
);
270 event_error
= ENOMEM
;
271 (void) mutex_unlock(&queue_lock
);
275 if (events
== NULL
) {
277 events
= (struct event_list
*)malloc(sizeof (struct event_list
));
278 if (events
== NULL
) {
279 event_error
= ENOMEM
;
283 events
->event
= event
;
287 /* already have events in the queue */
288 struct event_list
*ep
;
289 struct event_list
*new_event
;
291 /* find the last element in the list */
292 for (ep
= events
; ep
->next
!= NULL
; ep
= ep
->next
);
294 new_event
= (struct event_list
*)malloc(sizeof (struct event_list
));
295 if (new_event
== NULL
) {
296 event_error
= ENOMEM
;
299 new_event
->next
= NULL
;
300 new_event
->event
= event
;
301 ep
->next
= new_event
;
305 (void) mutex_unlock(&queue_lock
);
307 (void) sema_post(&semaphore
);
318 event
= dm_get_event(&error
);
319 if (callback
== NULL
) {
323 callback(event
, error
);
328 event_handler(sysevent_t
*ev
)
333 class_name
= sysevent_get_class_name(ev
);
335 (void) fprintf(stderr
, "****EVENT: %s %s ", class_name
,
336 sysevent_get_subclass_name(ev
));
337 if ((pub
= sysevent_get_pub_name(ev
)) != NULL
) {
338 (void) fprintf(stderr
, "%s\n", pub
);
341 (void) fprintf(stderr
, "\n");
345 if (libdiskmgt_str_eq(class_name
, EC_DEV_ADD
)) {
346 /* batch up the adds into a single devtree walk */
349 } else if (libdiskmgt_str_eq(class_name
, EC_DEV_REMOVE
)) {
350 nvlist_t
*nvlist
= NULL
;
351 char *dev_name
= NULL
;
353 (void) sysevent_get_attr_list(ev
, &nvlist
);
354 if (nvlist
!= NULL
) {
355 (void) nvlist_lookup_string(nvlist
, DEV_NAME
, &dev_name
);
358 print_nvlist("**** ", nvlist
);
362 if (dev_name
!= NULL
) {
363 cache_update(DM_EV_DISK_DELETE
, dev_name
);
366 if (nvlist
!= NULL
) {
373 * This is a debugging function only.
376 print_nvlist(char *prefix
, nvlist_t
*list
)
380 nvp
= nvlist_next_nvpair(list
, NULL
);
381 while (nvp
!= NULL
) {
390 attrname
= nvpair_name(nvp
);
391 switch (nvpair_type(nvp
)) {
392 case DATA_TYPE_STRING
:
393 (void) nvpair_value_string(nvp
, &str
);
394 (void) fprintf(stderr
, "%s%s: %s\n", prefix
, attrname
, str
);
397 case DATA_TYPE_STRING_ARRAY
:
398 (void) nvpair_value_string_array(nvp
, &str_array
, &cnt
);
399 (void) fprintf(stderr
, "%s%s:\n", prefix
, attrname
);
400 for (i
= 0; i
< cnt
; i
++) {
401 (void) fprintf(stderr
, "%s %s\n", prefix
, str_array
[i
]);
405 case DATA_TYPE_UINT32
:
406 (void) nvpair_value_uint32(nvp
, &ui32
);
407 (void) fprintf(stderr
, "%s%s: %u\n", prefix
, attrname
, ui32
);
410 case DATA_TYPE_UINT64
:
411 (void) nvpair_value_uint64(nvp
, &ui64
);
413 (void) fprintf(stderr
, "%s%s: %lu\n", prefix
, attrname
, ui64
);
415 (void) fprintf(stderr
, "%s%s: %llu\n", prefix
, attrname
, ui64
);
420 case DATA_TYPE_BOOLEAN
:
421 (void) fprintf(stderr
, "%s%s: true\n", prefix
, attrname
);
425 (void) fprintf(stderr
, "%s%s: UNSUPPORTED TYPE\n", prefix
,
430 nvp
= nvlist_next_nvpair(list
, nvp
);
435 * Batch up the adds into a single devtree walk. We can get a bunch of
436 * adds when we add a controller since we will get an add event for each
442 thread_t walk_thread
;
444 (void) mutex_lock(&walker_lock
);
446 switch (walker_state
) {
448 if (thr_create(NULL
, NULL
, (void *(*)(void *))walker
, NULL
,
449 THR_DAEMON
, &walk_thread
) == 0) {
450 walker_state
= WALK_WAITING
;
455 /* absorb the event and do nothing */
463 (void) mutex_unlock(&walker_lock
);
473 /* start by wating for a few seconds to absorb extra events */
474 (void) sleep(WALK_WAIT_TIME
);
476 (void) mutex_lock(&walker_lock
);
477 walker_state
= WALK_RUNNING
;
478 (void) mutex_unlock(&walker_lock
);
480 cache_update(DM_EV_DISK_ADD
, NULL
);
482 (void) mutex_lock(&walker_lock
);
484 if (events_pending
) {
486 walker_state
= WALK_WAITING
;
489 walker_state
= WALK_NONE
;
493 (void) mutex_unlock(&walker_lock
);
495 } while (walk_again
);