Line data Source code
1 : /*
2 : *
3 : * registrycb.c
4 : *
5 : * $Source: /Users/ekr/tmp/nrappkit-dump/nrappkit/src/registry/registrycb.c,v $
6 : * $Revision: 1.3 $
7 : * $Date: 2007/06/26 22:37:51 $
8 : *
9 : * Callback-related functions
10 : *
11 : *
12 : * Copyright (C) 2005, Network Resonance, Inc.
13 : * Copyright (C) 2006, Network Resonance, Inc.
14 : * All Rights Reserved
15 : *
16 : * Redistribution and use in source and binary forms, with or without
17 : * modification, are permitted provided that the following conditions
18 : * are met:
19 : *
20 : * 1. Redistributions of source code must retain the above copyright
21 : * notice, this list of conditions and the following disclaimer.
22 : * 2. Redistributions in binary form must reproduce the above copyright
23 : * notice, this list of conditions and the following disclaimer in the
24 : * documentation and/or other materials provided with the distribution.
25 : * 3. Neither the name of Network Resonance, Inc. nor the name of any
26 : * contributors to this software may be used to endorse or promote
27 : * products derived from this software without specific prior written
28 : * permission.
29 : *
30 : * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
31 : * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32 : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33 : * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
34 : * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
35 : * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
36 : * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
37 : * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
38 : * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
39 : * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
40 : * POSSIBILITY OF SUCH DAMAGE.
41 : *
42 : *
43 : */
44 :
45 : #include <assert.h>
46 : #include <string.h>
47 : #include "registry.h"
48 : #include "registry_int.h"
49 : #include "r_assoc.h"
50 : #include "r_errors.h"
51 : #include "nr_common.h"
52 : #include "r_log.h"
53 : #include "r_macros.h"
54 :
55 : static char CB_ACTIONS[] = { NR_REG_CB_ACTION_ADD,
56 : NR_REG_CB_ACTION_DELETE,
57 : NR_REG_CB_ACTION_CHANGE,
58 : NR_REG_CB_ACTION_FINAL };
59 :
60 : typedef struct nr_reg_cb_info_ {
61 : char action;
62 : void (*cb)(void *cb_arg, char action, NR_registry name);
63 : void *cb_arg;
64 : NR_registry name;
65 : } nr_reg_cb_info;
66 :
67 : /* callbacks that are registered, a mapping from names like "foo.bar.baz"
68 : * to an r_assoc which contains possibly several nr_reg_cb_info*'s */
69 : static r_assoc *nr_registry_callbacks = 0;
70 :
71 : //static size_t SIZEOF_CB_ID = (sizeof(void (*)()) + 1);
72 : #define SIZEOF_CB_ID (sizeof(void (*)()) + 1)
73 :
74 : static int nr_reg_validate_action(char action);
75 : static int nr_reg_assoc_destroy(void *ptr);
76 : static int compute_cb_id(void *cb, char action, unsigned char cb_id[SIZEOF_CB_ID]);
77 : static int nr_reg_info_free(void *ptr);
78 : static int nr_reg_raise_event_recurse(char *name, char *tmp, int action);
79 : static int nr_reg_register_callback(NR_registry name, char action, void (*cb)(void *cb_arg, char action, NR_registry name), void *cb_arg);
80 : static int nr_reg_unregister_callback(char *name, char action, void (*cb)(void *cb_arg, char action, NR_registry name));
81 :
82 : int
83 0 : nr_reg_cb_init()
84 : {
85 : int r, _status;
86 :
87 0 : if (nr_registry_callbacks == 0) {
88 0 : if ((r=r_assoc_create(&nr_registry_callbacks, r_assoc_crc32_hash_compute, 12)))
89 0 : ABORT(r);
90 : }
91 :
92 0 : _status=0;
93 : abort:
94 0 : if (_status) {
95 0 : r_log(NR_LOG_REGISTRY, LOG_DEBUG, "Couldn't init notifications: %s", nr_strerror(_status));
96 : }
97 0 : return(_status);
98 : }
99 :
100 : int
101 0 : nr_reg_validate_action(char action)
102 : {
103 : int _status;
104 : int i;
105 :
106 0 : for (i = 0; i < sizeof(CB_ACTIONS); ++i) {
107 0 : if (action == CB_ACTIONS[i])
108 0 : return 0;
109 : }
110 0 : ABORT(R_BAD_ARGS);
111 :
112 : _status=0;
113 : abort:
114 0 : return(_status);
115 : }
116 :
117 : int
118 0 : nr_reg_register_callback(NR_registry name, char action, void (*cb)(void *cb_arg, char action, NR_registry name), void *cb_arg)
119 : {
120 : int r, _status;
121 : r_assoc *assoc;
122 0 : int create_assoc = 0;
123 : nr_reg_cb_info *info;
124 0 : int create_info = 0;
125 : unsigned char cb_id[SIZEOF_CB_ID];
126 :
127 0 : if (name == 0 || cb == 0)
128 0 : ABORT(R_BAD_ARGS);
129 :
130 0 : if (nr_registry_callbacks == 0)
131 0 : ABORT(R_FAILED);
132 :
133 0 : if ((r=nr_reg_is_valid(name)))
134 0 : ABORT(r);
135 :
136 0 : if ((r=nr_reg_validate_action(action)))
137 0 : ABORT(r);
138 :
139 0 : if ((r=r_assoc_fetch(nr_registry_callbacks, name, strlen(name)+1, (void*)&assoc))) {
140 0 : if (r == R_NOT_FOUND)
141 0 : create_assoc = 1;
142 : else
143 0 : ABORT(r);
144 : }
145 :
146 0 : if (create_assoc) {
147 0 : if ((r=r_assoc_create(&assoc, r_assoc_crc32_hash_compute, 5)))
148 0 : ABORT(r);
149 :
150 0 : if ((r=r_assoc_insert(nr_registry_callbacks, name, strlen(name)+1, assoc, 0, nr_reg_assoc_destroy, R_ASSOC_NEW)))
151 0 : ABORT(r);
152 : }
153 :
154 0 : if ((r=compute_cb_id(cb, action, cb_id)))
155 0 : ABORT(r);
156 :
157 0 : if ((r=r_assoc_fetch(assoc, (char*)cb_id, SIZEOF_CB_ID, (void*)&info))) {
158 0 : if (r == R_NOT_FOUND)
159 0 : create_info = 1;
160 : else
161 0 : ABORT(r);
162 : }
163 :
164 0 : if (create_info) {
165 0 : if (!(info=(void*)RCALLOC(sizeof(*info))))
166 0 : ABORT(R_NO_MEMORY);
167 : }
168 :
169 0 : strncpy(info->name, name, sizeof(info->name));
170 0 : info->action = action;
171 0 : info->cb = cb;
172 0 : info->cb_arg = cb_arg;
173 :
174 0 : if (create_info) {
175 0 : if ((r=r_assoc_insert(assoc, (char*)cb_id, SIZEOF_CB_ID, info, 0, nr_reg_info_free, R_ASSOC_NEW)))
176 0 : ABORT(r);
177 : }
178 :
179 0 : _status=0;
180 : abort:
181 0 : r_log(NR_LOG_REGISTRY, LOG_DEBUG, "register callback %p on '%s' for '%s' %s", cb, name, nr_reg_action_name(action), (_status ? "FAILED" : "succeeded"));
182 :
183 0 : if (_status) {
184 0 : if (create_info && info) RFREE(info);
185 0 : if (create_assoc && assoc) nr_reg_assoc_destroy(&assoc);
186 : }
187 0 : return(_status);
188 : }
189 :
190 : int
191 0 : nr_reg_unregister_callback(char *name, char action, void (*cb)(void *cb_arg, char action, NR_registry name))
192 : {
193 : int r, _status;
194 : r_assoc *assoc;
195 : int size;
196 : unsigned char cb_id[SIZEOF_CB_ID];
197 :
198 0 : if (name == 0 || cb == 0)
199 0 : ABORT(R_BAD_ARGS);
200 :
201 0 : if (nr_registry_callbacks == 0)
202 0 : ABORT(R_FAILED);
203 :
204 0 : if ((r=nr_reg_is_valid(name)))
205 0 : ABORT(r);
206 :
207 0 : if ((r=nr_reg_validate_action(action)))
208 0 : ABORT(r);
209 :
210 0 : if ((r=r_assoc_fetch(nr_registry_callbacks, name, strlen(name)+1, (void*)&assoc))) {
211 0 : if (r != R_NOT_FOUND)
212 0 : ABORT(r);
213 : }
214 : else {
215 0 : if ((r=compute_cb_id(cb, action, cb_id)))
216 0 : ABORT(r);
217 :
218 0 : if ((r=r_assoc_delete(assoc, (char*)cb_id, SIZEOF_CB_ID))) {
219 0 : if (r != R_NOT_FOUND)
220 0 : ABORT(r);
221 : }
222 :
223 0 : if ((r=r_assoc_num_elements(assoc, &size)))
224 0 : ABORT(r);
225 :
226 0 : if (size == 0) {
227 0 : if ((r=r_assoc_delete(nr_registry_callbacks, name, strlen(name)+1)))
228 0 : ABORT(r);
229 : }
230 : }
231 :
232 0 : _status=0;
233 : abort:
234 0 : r_log(NR_LOG_REGISTRY, LOG_DEBUG, "unregister callback %p on '%s' for '%s' %s", cb, name, nr_reg_action_name(action), (_status ? "FAILED" : "succeeded"));
235 :
236 0 : return(_status);
237 : }
238 :
239 : int
240 0 : compute_cb_id(void *cb, char action, unsigned char cb_id[SIZEOF_CB_ID])
241 : {
242 : /* callbacks are identified by the pointer to the cb function plus
243 : * the action being watched */
244 : assert(sizeof(cb) == sizeof(void (*)()));
245 : assert(sizeof(cb) == (SIZEOF_CB_ID - 1));
246 :
247 0 : memcpy(cb_id, &(cb), sizeof(cb));
248 0 : cb_id[SIZEOF_CB_ID-1] = action;
249 :
250 0 : return 0;
251 : }
252 :
253 : char *
254 0 : nr_reg_action_name(int action)
255 : {
256 0 : char *name = "*Unknown*";
257 :
258 0 : switch (action) {
259 0 : case NR_REG_CB_ACTION_ADD: name = "add"; break;
260 0 : case NR_REG_CB_ACTION_DELETE: name = "delete"; break;
261 0 : case NR_REG_CB_ACTION_CHANGE: name = "change"; break;
262 0 : case NR_REG_CB_ACTION_FINAL: name = "final"; break;
263 : }
264 :
265 0 : return name;
266 : }
267 :
268 : int
269 0 : nr_reg_assoc_destroy(void *ptr)
270 : {
271 0 : return r_assoc_destroy((r_assoc**)&ptr);
272 : }
273 :
274 : int
275 0 : nr_reg_info_free(void *ptr)
276 : {
277 0 : RFREE(ptr);
278 0 : return 0;
279 : }
280 :
281 : /* call with tmp=0 */
282 : int
283 0 : nr_reg_raise_event_recurse(char *name, char *tmp, int action)
284 : {
285 : int r, _status;
286 : r_assoc *assoc;
287 : nr_reg_cb_info *info;
288 : r_assoc_iterator iter;
289 : char *key;
290 : int keyl;
291 : char *c;
292 0 : int free_tmp = 0;
293 : int count;
294 :
295 0 : if (tmp == 0) {
296 0 : if (!(tmp = (char*)r_strdup(name)))
297 0 : ABORT(R_NO_MEMORY);
298 0 : free_tmp = 1;
299 : }
300 :
301 0 : if ((r=r_assoc_fetch(nr_registry_callbacks, tmp, strlen(tmp)+1, (void*)&assoc))) {
302 0 : if (r != R_NOT_FOUND)
303 0 : ABORT(r);
304 :
305 0 : r_log(NR_LOG_REGISTRY, LOG_DEBUG, "No callbacks found on '%s'", tmp);
306 : }
307 : else {
308 0 : if (!r_assoc_num_elements(assoc, &count)) {
309 0 : r_log(NR_LOG_REGISTRY, LOG_DEBUG, "%d callback%s found on '%s'",
310 0 : count, ((count == 1) ? "" : "s"), tmp);
311 : }
312 :
313 0 : if ((r=r_assoc_init_iter(assoc, &iter)))
314 0 : ABORT(r);
315 :
316 : for (;;) {
317 0 : if ((r=r_assoc_iter(&iter, (void*)&key, &keyl, (void*)&info))) {
318 0 : if (r == R_EOD)
319 0 : break;
320 : else
321 0 : ABORT(r);
322 : }
323 :
324 0 : if (info->action == action) {
325 0 : r_log(NR_LOG_REGISTRY, LOG_DEBUG,
326 : "Invoking callback %p for '%s'",
327 0 : info->cb,
328 0 : nr_reg_action_name(info->action));
329 :
330 0 : (void)info->cb(info->cb_arg, action, name);
331 : }
332 : else {
333 0 : r_log(NR_LOG_REGISTRY, LOG_DEBUG,
334 : "Skipping callback %p for '%s'",
335 0 : info->cb,
336 0 : nr_reg_action_name(info->action));
337 : }
338 : }
339 : }
340 :
341 0 : if (strlen(tmp) > 0) {
342 0 : c = strrchr(tmp, '.');
343 0 : if (c != 0)
344 0 : *c = '\0';
345 : else
346 0 : tmp[0] = '\0';
347 :
348 0 : if ((r=nr_reg_raise_event_recurse(name, tmp, action)))
349 0 : ABORT(r);
350 : }
351 :
352 0 : _status=0;
353 : abort:
354 0 : if (free_tmp && tmp != 0) RFREE(tmp);
355 0 : return(_status);
356 : }
357 :
358 :
359 : /* NON-STATIC METHODS */
360 :
361 : int
362 0 : nr_reg_raise_event(char *name, int action)
363 : {
364 : int r, _status;
365 : int count;
366 0 : char *event = nr_reg_action_name(action);
367 :
368 0 : r_log(NR_LOG_REGISTRY, LOG_DEBUG, "raising event '%s' on '%s'", event, name);
369 :
370 0 : if (name == 0)
371 0 : ABORT(R_BAD_ARGS);
372 :
373 0 : if ((r=nr_reg_validate_action(action)))
374 0 : ABORT(r);
375 :
376 0 : if ((r=r_assoc_num_elements(nr_registry_callbacks, &count)))
377 0 : ABORT(r);
378 :
379 0 : if (count > 0) {
380 0 : if ((r=nr_reg_raise_event_recurse(name, 0, action)))
381 0 : ABORT(r);
382 : }
383 : else {
384 0 : r_log(NR_LOG_REGISTRY, LOG_DEBUG, "No callbacks found");
385 0 : return 0;
386 : }
387 :
388 0 : _status=0;
389 : abort:
390 0 : return(_status);
391 : }
392 :
393 :
394 : /* PUBLIC METHODS */
395 :
396 : int
397 0 : NR_reg_register_callback(NR_registry name, char action, void (*cb)(void *cb_arg, char action, NR_registry name), void *cb_arg)
398 : {
399 : int r, _status;
400 : int i;
401 :
402 0 : for (i = 0; i < sizeof(CB_ACTIONS); ++i) {
403 0 : if (action & CB_ACTIONS[i]) {
404 0 : if ((r=nr_reg_register_callback(name, CB_ACTIONS[i], cb, cb_arg)))
405 0 : ABORT(r);
406 :
407 0 : action &= ~(CB_ACTIONS[i]);
408 : }
409 : }
410 :
411 0 : if (action)
412 0 : ABORT(R_BAD_ARGS);
413 :
414 0 : _status=0;
415 : abort:
416 0 : return(_status);
417 : }
418 :
419 : int
420 0 : NR_reg_unregister_callback(char *name, char action, void (*cb)(void *cb_arg, char action, NR_registry name))
421 : {
422 : int r, _status;
423 : int i;
424 :
425 0 : for (i = 0; i < sizeof(CB_ACTIONS); ++i) {
426 0 : if (action & CB_ACTIONS[i]) {
427 0 : if ((r=nr_reg_unregister_callback(name, CB_ACTIONS[i], cb)))
428 0 : ABORT(r);
429 :
430 0 : action &= ~(CB_ACTIONS[i]);
431 : }
432 : }
433 :
434 0 : if (action)
435 0 : ABORT(R_BAD_ARGS);
436 :
437 0 : _status=0;
438 : abort:
439 0 : return(_status);
440 : }
|