dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / libdiskmgt / common / events.c
blob2da6d40d381eb81851e06e9049a9900a1ab4562f
1 /*
2 * CDDL HEADER START
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.
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]
20 * CDDL HEADER END
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <stropts.h>
30 #include <synch.h>
31 #include <thread.h>
32 #include <libsysevent.h>
33 #include <sys/sysevent/eventdefs.h>
34 #include <sys/sysevent/dev.h>
35 #include <errno.h>
36 #include <libgen.h>
37 #include <unistd.h>
39 #include "libdiskmgt.h"
40 #include "disks_private.h"
42 struct event_list {
43 struct event_list *next;
44 nvlist_t *event;
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
70 #define WALK_NONE 0
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;
90 nvlist_t *
91 dm_get_event(int *errp)
93 nvlist_t *event = NULL;
95 *errp = 0;
97 /* wait until there is an event in the queue */
98 /*CONSTCOND*/
99 while (1) {
100 (void) sema_wait(&semaphore);
102 if (event_break) {
103 event_break = 0;
104 *errp = EINTR;
105 break;
108 (void) mutex_lock(&queue_lock);
110 /* first see if we ran out of memory since the last call */
111 if (event_error != 0) {
112 *errp = event_error;
113 event_error = 0;
115 } else if (events != NULL) {
116 struct event_list *tmpp;
118 event = events->event;
119 tmpp = events->next;
120 free(events);
121 events = tmpp;
124 (void) mutex_unlock(&queue_lock);
126 if (*errp != 0 || event != NULL) {
127 break;
131 return (event);
134 void
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 */
139 *errp = 0;
140 if (cb != callback) {
142 callback = cb;
143 if (cb == NULL) {
144 /* clearing the cb so shutdown the internal cb thread */
145 event_break = 1;
146 (void) sema_post(&semaphore);
148 } else {
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,
154 &watch_thread);
158 } else {
159 /* first time to initialize */
160 sendevents = 1;
162 *errp = sema_init(&semaphore, 0, USYNC_THREAD, NULL);
163 if (*errp != 0) {
164 return;
167 if (cb != NULL) {
168 thread_t watch_thread;
170 callback = cb;
172 *errp = thr_create(NULL, 0,
173 (void *(*)(void *))cb_watch_events, NULL, THR_DAEMON,
174 &watch_thread);
179 void
180 events_new_event(char *name, int dtype, char *etype)
182 nvlist_t *event = NULL;
184 if (!sendevents) {
185 return;
188 if (nvlist_alloc(&event, NVATTRS, 0) != 0) {
189 event = NULL;
191 } else {
192 int error = 0;
194 if (name != NULL &&
195 nvlist_add_string(event, DM_EV_NAME, name) != 0) {
196 error = ENOMEM;
199 if (dtype != -1 &&
200 nvlist_add_uint32(event, DM_EV_DTYPE, dtype) != 0) {
201 error = ENOMEM;
204 if (nvlist_add_string(event, DM_EV_TYPE, etype) != 0) {
205 error = ENOMEM;
208 if (error != 0) {
209 nvlist_free(event);
210 event = NULL;
214 add_event_to_queue(event);
217 void
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);
231 if (shp == NULL) {
232 if (dm_debug) {
233 /* keep going when we're debugging */
234 (void) fprintf(stderr, "ERROR: bind failed %d\n", errno);
235 return (0);
237 return (errno);
240 subclass_list[0] = ESC_DISK;
241 if (sysevent_subscribe_event(shp, EC_DEV_ADD, subclass_list, 1) != 0) {
242 if (dm_debug) {
243 /* keep going when we're debugging */
244 (void) fprintf(stderr, "ERROR: subscribe failed\n");
245 return (0);
247 return (errno);
249 if (sysevent_subscribe_event(shp, EC_DEV_REMOVE, subclass_list, 1)
250 != 0) {
251 if (dm_debug) {
252 /* keep going when we're debugging */
253 (void) fprintf(stderr, "ERROR: subscribe failed\n");
254 return (0);
256 return (errno);
259 return (0);
262 static void
263 add_event_to_queue(nvlist_t *event)
265 (void) mutex_lock(&queue_lock);
267 if (event == NULL) {
268 event_error = ENOMEM;
269 (void) mutex_unlock(&queue_lock);
270 return;
273 if (events == NULL) {
275 events = (struct event_list *)malloc(sizeof (struct event_list));
276 if (events == NULL) {
277 event_error = ENOMEM;
278 nvlist_free(event);
279 } else {
280 events->next = NULL;
281 events->event = event;
284 } else {
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;
295 nvlist_free(event);
296 } else {
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);
308 static void
309 cb_watch_events()
311 nvlist_t *event;
312 int error;
314 /*CONSTCOND*/
315 while (1) {
316 event = dm_get_event(&error);
317 if (callback == NULL) {
318 /* end the thread */
319 return;
321 callback(event, error);
325 static void
326 event_handler(sysevent_t *ev)
328 char *class_name;
329 char *pub;
331 class_name = sysevent_get_class_name(ev);
332 if (dm_debug) {
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);
337 free(pub);
338 } else {
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 */
345 walk_devtree();
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);
355 if (dm_debug) {
356 print_nvlist("**** ", nvlist);
360 if (dev_name != NULL) {
361 cache_update(DM_EV_DISK_DELETE, dev_name);
364 nvlist_free(nvlist);
369 * This is a debugging function only.
371 static void
372 print_nvlist(char *prefix, nvlist_t *list)
374 nvpair_t *nvp;
376 nvp = nvlist_next_nvpair(list, NULL);
377 while (nvp != NULL) {
378 char *attrname;
379 char *str;
380 uint32_t ui32;
381 uint64_t ui64;
382 char **str_array;
383 uint_t cnt;
384 int i;
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);
391 break;
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]);
399 break;
401 case DATA_TYPE_UINT32:
402 (void) nvpair_value_uint32(nvp, &ui32);
403 (void) fprintf(stderr, "%s%s: %u\n", prefix, attrname, ui32);
404 break;
406 case DATA_TYPE_UINT64:
407 (void) nvpair_value_uint64(nvp, &ui64);
408 #ifdef _LP64
409 (void) fprintf(stderr, "%s%s: %lu\n", prefix, attrname, ui64);
410 #else
411 (void) fprintf(stderr, "%s%s: %llu\n", prefix, attrname, ui64);
412 #endif
413 break;
416 case DATA_TYPE_BOOLEAN:
417 (void) fprintf(stderr, "%s%s: true\n", prefix, attrname);
418 break;
420 default:
421 (void) fprintf(stderr, "%s%s: UNSUPPORTED TYPE\n", prefix,
422 attrname);
423 break;
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
433 * drive.
435 static void
436 walk_devtree()
438 thread_t walk_thread;
440 (void) mutex_lock(&walker_lock);
442 switch (walker_state) {
443 case WALK_NONE:
444 if (thr_create(NULL, 0, (void *(*)(void *))walker, NULL,
445 THR_DAEMON, &walk_thread) == 0) {
446 walker_state = WALK_WAITING;
448 break;
450 case WALK_WAITING:
451 /* absorb the event and do nothing */
452 break;
454 case WALK_RUNNING:
455 events_pending = 1;
456 break;
459 (void) mutex_unlock(&walker_lock);
462 /*ARGSUSED*/
463 static void
464 walker(void *arg)
466 int walk_again = 0;
468 do {
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) {
481 events_pending = 0;
482 walker_state = WALK_WAITING;
483 walk_again = 1;
484 } else {
485 walker_state = WALK_NONE;
486 walk_again = 0;
489 (void) mutex_unlock(&walker_lock);
491 } while (walk_again);