]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCDNotifierInformViaCallback.c
d64572bf29087fa83c752098e0de62bf0da00274
[apple/configd.git] / SystemConfiguration.fproj / SCDNotifierInformViaCallback.c
1 /*
2 * Copyright (c) 2000-2003 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*
25 * Modification History
26 *
27 * June 1, 2001 Allan Nathanson <ajn@apple.com>
28 * - public API conversion
29 *
30 * March 31, 2000 Allan Nathanson <ajn@apple.com>
31 * - initial revision
32 */
33
34 #include <mach/mach.h>
35 #include <mach/mach_error.h>
36
37 #include <SystemConfiguration/SystemConfiguration.h>
38 #include <SystemConfiguration/SCPrivate.h>
39 #include "SCDynamicStoreInternal.h"
40 #include "config.h" /* MiG generated file */
41
42 static void
43 informCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
44 {
45 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
46 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
47 mach_msg_empty_rcv_t *buf = msg;
48 mach_msg_id_t msgid = buf->header.msgh_id;
49 SCDynamicStoreCallBack_v1 cbFunc = storePrivate->callbackFunction;
50 void *cbArg = storePrivate->callbackArgument;
51
52 if (msgid == MACH_NOTIFY_NO_SENDERS) {
53 /* the server died, disable additional callbacks */
54 #ifdef DEBUG
55 SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" notifier port closed, disabling notifier"));
56 #endif /* DEBUG */
57 } else if (cbFunc == NULL) {
58 /* there is no (longer) a callback function, disable additional callbacks */
59 #ifdef DEBUG
60 SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" no callback function, disabling notifier"));
61 #endif /* DEBUG */
62 } else {
63 #ifdef DEBUG
64 SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" executing notification function"));
65 #endif /* DEBUG */
66 if ((*cbFunc)(store, cbArg)) {
67 /*
68 * callback function returned success.
69 */
70 return;
71 } else {
72 #ifdef DEBUG
73 SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" callback returned error, disabling notifier"));
74 #endif /* DEBUG */
75 }
76 }
77
78 #ifdef DEBUG
79 if (port != storePrivate->callbackPort) {
80 SCLog(_sc_verbose, LOG_DEBUG, CFSTR("informCallback, why is port != callbackPort?"));
81 }
82 #endif /* DEBUG */
83
84 /* invalidate the run loop source */
85 CFRunLoopSourceInvalidate(storePrivate->callbackRLS);
86 CFRelease(storePrivate->callbackRLS);
87 storePrivate->callbackRLS = NULL;
88
89 /* invalidate port */
90 CFMachPortInvalidate(storePrivate->callbackPort);
91 CFRelease(storePrivate->callbackPort);
92 storePrivate->callbackPort = NULL;
93
94 /* disable notifier */
95 storePrivate->notifyStatus = NotifierNotRegistered;
96 storePrivate->callbackArgument = NULL;
97 storePrivate->callbackFunction = NULL;
98
99 return;
100 }
101
102
103 Boolean
104 SCDynamicStoreNotifyCallback(SCDynamicStoreRef store,
105 CFRunLoopRef runLoop,
106 SCDynamicStoreCallBack_v1 func,
107 void *arg)
108 {
109 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
110 kern_return_t status;
111 mach_port_t port;
112 mach_port_t oldNotify;
113 int sc_status;
114 CFMachPortContext context = { 0
115 , (void *)store
116 , CFRetain
117 , CFRelease
118 , NULL
119 };
120
121 if (store == NULL) {
122 /* sorry, you must provide a session */
123 _SCErrorSet(kSCStatusNoStoreSession);
124 return FALSE;
125 }
126
127 if (storePrivate->server == MACH_PORT_NULL) {
128 /* sorry, you must have an open session to play */
129 _SCErrorSet(kSCStatusNoStoreServer);
130 return FALSE;
131 }
132
133 if (storePrivate->notifyStatus != NotifierNotRegistered) {
134 /* sorry, you can only have one notification registered at once */
135 _SCErrorSet(kSCStatusNotifierActive);
136 return FALSE;
137 }
138
139 /* Allocating port (for server response) */
140 storePrivate->callbackPort = CFMachPortCreate(NULL,
141 informCallback,
142 &context,
143 NULL);
144
145 /* Request a notification when/if the server dies */
146 port = CFMachPortGetPort(storePrivate->callbackPort);
147 status = mach_port_request_notification(mach_task_self(),
148 port,
149 MACH_NOTIFY_NO_SENDERS,
150 1,
151 port,
152 MACH_MSG_TYPE_MAKE_SEND_ONCE,
153 &oldNotify);
154 if (status != KERN_SUCCESS) {
155 SCLog(TRUE, LOG_DEBUG, CFSTR("SCDynamicStoreNotifyCallback mach_port_request_notification(): %s"), mach_error_string(status));
156 CFMachPortInvalidate(storePrivate->callbackPort);
157 CFRelease(storePrivate->callbackPort);
158 _SCErrorSet(status);
159 return FALSE;
160 }
161
162 #ifdef DEBUG
163 if (oldNotify != MACH_PORT_NULL) {
164 SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStoreNotifyCallback(): why is oldNotify != MACH_PORT_NULL?"));
165 }
166 #endif /* DEBUG */
167
168 /* Requesting notification via mach port */
169 status = notifyviaport(storePrivate->server,
170 port,
171 0,
172 (int *)&sc_status);
173
174 if (status != KERN_SUCCESS) {
175 #ifdef DEBUG
176 if (status != MACH_SEND_INVALID_DEST)
177 SCLog(_sc_verbose, LOG_DEBUG, CFSTR("SCDynamicStoreNotifyCallback notifyviaport(): %s"), mach_error_string(status));
178 #endif /* DEBUG */
179 CFMachPortInvalidate(storePrivate->callbackPort);
180 CFRelease(storePrivate->callbackPort);
181 (void) mach_port_destroy(mach_task_self(), storePrivate->server);
182 storePrivate->server = MACH_PORT_NULL;
183 _SCErrorSet(status);
184 return FALSE;
185 }
186
187 if (sc_status != kSCStatusOK) {
188 _SCErrorSet(sc_status);
189 return FALSE;
190 }
191
192 /* set notifier active */
193 storePrivate->notifyStatus = Using_NotifierInformViaCallback;
194
195 /* Creating/adding a run loop source for the port */
196 storePrivate->callbackArgument = arg;
197 storePrivate->callbackFunction = func;
198 storePrivate->callbackRLS = CFMachPortCreateRunLoopSource(NULL, storePrivate->callbackPort, 0);
199 CFRunLoopAddSource(runLoop, storePrivate->callbackRLS, kCFRunLoopDefaultMode);
200
201 return TRUE;
202 }
203
204
205 static void
206 rlsCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
207 {
208 mach_msg_empty_rcv_t *buf = msg;
209 mach_msg_id_t msgid = buf->header.msgh_id;
210 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
211 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
212
213 if (msgid == MACH_NOTIFY_NO_SENDERS) {
214 /* the server died, disable additional callbacks */
215 #ifdef DEBUG
216 SCLog(_sc_verbose, LOG_INFO, CFSTR(" rlsCallback(), notifier port closed"));
217 #endif /* DEBUG */
218
219 #ifdef DEBUG
220 if (port != storePrivate->callbackPort) {
221 SCLog(_sc_verbose, LOG_DEBUG, CFSTR("rlsCallback(), why is port != callbackPort?"));
222 }
223 #endif /* DEBUG */
224
225 /* invalidate the run loop source(s) */
226 CFRunLoopSourceInvalidate(storePrivate->callbackRLS);
227 CFRelease(storePrivate->callbackRLS);
228 storePrivate->callbackRLS = NULL;
229
230 /* invalidate port */
231 CFMachPortInvalidate(storePrivate->callbackPort);
232 CFRelease(storePrivate->callbackPort);
233 storePrivate->callbackPort = NULL;
234
235 return;
236 }
237
238 /* signal the real runloop source */
239 CFRunLoopSourceSignal(storePrivate->rls);
240 return;
241 }
242
243
244 static void
245 rlsPortInvalidate(CFMachPortRef mp, void *info) {
246 mach_port_t port = CFMachPortGetPort(mp);
247
248 // A simple deallocate won't get rid of all the references we've accumulated
249 #ifdef DEBUG
250 SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" invalidate = %d"), port);
251 #endif /* DEBUG */
252 (void)mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1);
253 }
254
255
256 static void
257 rlsSchedule(void *info, CFRunLoopRef rl, CFStringRef mode)
258 {
259 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
260 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
261
262 #ifdef DEBUG
263 SCLog(_sc_verbose, LOG_DEBUG, CFSTR("schedule notifications for mode %@"), mode);
264 #endif /* DEBUG */
265
266 if (storePrivate->rlsRefs++ == 0) {
267 CFMachPortContext context = { 0
268 , (void *)store
269 , CFRetain
270 , CFRelease
271 , NULL
272 };
273 mach_port_t oldNotify;
274 mach_port_t port;
275 int sc_status;
276 kern_return_t status;
277
278 #ifdef DEBUG
279 SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" activate callback runloop source"));
280 #endif /* DEBUG */
281
282 /* Allocating port (for server response) */
283 status = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
284 if (status != KERN_SUCCESS) {
285 SCLog(TRUE, LOG_DEBUG, CFSTR("mach_port_allocate(): %s"), mach_error_string(status));
286 return;
287 }
288
289 status = mach_port_insert_right(mach_task_self(),
290 port,
291 port,
292 MACH_MSG_TYPE_MAKE_SEND);
293 if (status != KERN_SUCCESS) {
294 SCLog(TRUE, LOG_DEBUG, CFSTR("mach_port_insert_right(): %s"), mach_error_string(status));
295 (void) mach_port_destroy(mach_task_self(), port);
296 return;
297 }
298
299 /* Request a notification when/if the server dies */
300 status = mach_port_request_notification(mach_task_self(),
301 port,
302 MACH_NOTIFY_NO_SENDERS,
303 1,
304 port,
305 MACH_MSG_TYPE_MAKE_SEND_ONCE,
306 &oldNotify);
307 if (status != KERN_SUCCESS) {
308 SCLog(TRUE, LOG_DEBUG, CFSTR("mach_port_request_notification(): %s"), mach_error_string(status));
309 (void) mach_port_destroy(mach_task_self(), port);
310 return;
311 }
312
313 #ifdef DEBUG
314 if (oldNotify != MACH_PORT_NULL) {
315 SCLog(TRUE, LOG_ERR, CFSTR("rlsSchedule(): why is oldNotify != MACH_PORT_NULL?"));
316 }
317 #endif /* DEBUG */
318
319 status = notifyviaport(storePrivate->server, port, 0, (int *)&sc_status);
320 if (status != KERN_SUCCESS) {
321 #ifdef DEBUG
322 if (status != MACH_SEND_INVALID_DEST)
323 SCLog(_sc_verbose, LOG_DEBUG, CFSTR("notifyviaport(): %s"), mach_error_string(status));
324 #endif /* DEBUG */
325 (void) mach_port_destroy(mach_task_self(), port);
326 port = MACH_PORT_NULL;
327 (void) mach_port_destroy(mach_task_self(), storePrivate->server);
328 storePrivate->server = MACH_PORT_NULL;
329 return;
330 }
331
332 storePrivate->callbackPort = CFMachPortCreateWithPort(NULL, port, rlsCallback, &context, NULL);
333 CFMachPortSetInvalidationCallBack(storePrivate->callbackPort, rlsPortInvalidate);
334 storePrivate->callbackRLS = CFMachPortCreateRunLoopSource(NULL, storePrivate->callbackPort, 0);
335 }
336
337 if (storePrivate->callbackRLS != NULL) {
338 CFRunLoopAddSource(rl, storePrivate->callbackRLS, mode);
339 }
340
341 return;
342 }
343
344
345 static void
346 rlsCancel(void *info, CFRunLoopRef rl, CFStringRef mode)
347 {
348 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
349 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
350
351 #ifdef DEBUG
352 SCLog(_sc_verbose, LOG_DEBUG, CFSTR("cancel notifications for mode %@"), mode);
353 #endif /* DEBUG */
354
355 if (storePrivate->callbackRLS != NULL) {
356 CFRunLoopRemoveSource(rl, storePrivate->callbackRLS, mode);
357 }
358
359 if (--storePrivate->rlsRefs == 0) {
360 int sc_status;
361 kern_return_t status;
362
363 #ifdef DEBUG
364 SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" cancel callback runloop source"));
365 #endif /* DEBUG */
366
367 if (storePrivate->callbackRLS != NULL) {
368 /* invalidate & remove the run loop source */
369 CFRunLoopSourceInvalidate(storePrivate->callbackRLS);
370 CFRelease(storePrivate->callbackRLS);
371 storePrivate->callbackRLS = NULL;
372 }
373
374 if (storePrivate->callbackPort != NULL) {
375 /* invalidate port */
376 CFMachPortInvalidate(storePrivate->callbackPort);
377 CFRelease(storePrivate->callbackPort);
378 storePrivate->callbackPort = NULL;
379 }
380
381 if (storePrivate->server != MACH_PORT_NULL) {
382 status = notifycancel(storePrivate->server, (int *)&sc_status);
383 if (status != KERN_SUCCESS) {
384 #ifdef DEBUG
385 if (status != MACH_SEND_INVALID_DEST)
386 SCLog(_sc_verbose, LOG_INFO, CFSTR("notifycancel(): %s"), mach_error_string(status));
387 #endif /* DEBUG */
388 (void) mach_port_destroy(mach_task_self(), storePrivate->server);
389 storePrivate->server = MACH_PORT_NULL;
390 return;
391 }
392 }
393 }
394 return;
395 }
396
397
398 static void
399 rlsPerform(void *info)
400 {
401 CFArrayRef changedKeys;
402 void *context_info;
403 void (*context_release)(const void *);
404 SCDynamicStoreCallBack rlsFunction;
405 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
406 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
407
408 #ifdef DEBUG
409 SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" executing notification function"));
410 #endif /* DEBUG */
411
412 changedKeys = SCDynamicStoreCopyNotifiedKeys(store);
413 if (changedKeys == NULL) {
414 /* if no changes or something happened to the server */
415 return;
416 }
417
418 if (CFArrayGetCount(changedKeys) == 0) {
419 goto done;
420 }
421
422 rlsFunction = storePrivate->rlsFunction;
423
424 if (NULL != storePrivate->rlsContext.retain) {
425 context_info = (void *)storePrivate->rlsContext.retain(storePrivate->rlsContext.info);
426 context_release = storePrivate->rlsContext.release;
427 } else {
428 context_info = storePrivate->rlsContext.info;
429 context_release = NULL;
430 }
431 (*rlsFunction)(store, changedKeys, context_info);
432 if (context_release) {
433 context_release(context_info);
434 }
435
436 done :
437
438 CFRelease(changedKeys);
439 return;
440 }
441
442
443 static CFTypeRef
444 rlsRetain(CFTypeRef cf)
445 {
446 SCDynamicStoreRef store = (SCDynamicStoreRef)cf;
447 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
448
449 if (storePrivate->notifyStatus != Using_NotifierInformViaRunLoop) {
450 /* mark RLS active */
451 storePrivate->notifyStatus = Using_NotifierInformViaRunLoop;
452 /* keep a reference to the store */
453 CFRetain(store);
454 }
455
456 return cf;
457 }
458
459
460 static void
461 rlsRelease(CFTypeRef cf)
462 {
463 SCDynamicStoreRef store = (SCDynamicStoreRef)cf;
464 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
465
466 /* mark RLS inactive */
467 storePrivate->notifyStatus = NotifierNotRegistered;
468 storePrivate->rls = NULL;
469
470 /* release our reference to the store */
471 CFRelease(store);
472
473 return;
474 }
475
476
477 CFRunLoopSourceRef
478 SCDynamicStoreCreateRunLoopSource(CFAllocatorRef allocator,
479 SCDynamicStoreRef store,
480 CFIndex order)
481 {
482 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
483
484 if (store == NULL) {
485 /* sorry, you must provide a session */
486 _SCErrorSet(kSCStatusNoStoreSession);
487 return NULL;
488 }
489
490 if (storePrivate->server == MACH_PORT_NULL) {
491 /* sorry, you must have an open session to play */
492 _SCErrorSet(kSCStatusNoStoreServer);
493 return NULL;
494 }
495
496 switch (storePrivate->notifyStatus) {
497 case NotifierNotRegistered :
498 case Using_NotifierInformViaRunLoop :
499 /* OK to enable runloop notification */
500 break;
501 default :
502 /* sorry, you can only have one notification registered at once */
503 _SCErrorSet(kSCStatusNotifierActive);
504 return NULL;
505 }
506
507 if (storePrivate->rls != NULL) {
508 CFRetain(storePrivate->rls);
509 } else {
510 CFRunLoopSourceContext context = { 0 // version
511 , (void *)store // info
512 , rlsRetain // retain
513 , rlsRelease // release
514 , CFCopyDescription // copyDescription
515 , CFEqual // equal
516 , CFHash // hash
517 , rlsSchedule // schedule
518 , rlsCancel // cancel
519 , rlsPerform // perform
520 };
521
522 storePrivate->rls = CFRunLoopSourceCreate(allocator, order, &context);
523 }
524
525 if (storePrivate->rls == NULL) {
526 _SCErrorSet(kSCStatusFailed);
527 return NULL;
528 }
529
530 return storePrivate->rls;
531 }