]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCDNotifierInformViaCallback.c
aa1faefa97065fa6a71402c35444781108d359a1
[apple/configd.git] / SystemConfiguration.fproj / SCDNotifierInformViaCallback.c
1 /*
2 * Copyright (c) 2000-2005 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 static CFStringRef
104 notifyMPCopyDescription(const void *info)
105 {
106 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
107
108 return CFStringCreateWithFormat(NULL,
109 NULL,
110 CFSTR("<SCDynamicStore notification MP> {store = %p}"),
111 store);
112 }
113
114
115 Boolean
116 SCDynamicStoreNotifyCallback(SCDynamicStoreRef store,
117 CFRunLoopRef runLoop,
118 SCDynamicStoreCallBack_v1 func,
119 void *arg)
120 {
121 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
122 kern_return_t status;
123 mach_port_t port;
124 mach_port_t oldNotify;
125 int sc_status;
126 CFMachPortContext context = { 0
127 , (void *)store
128 , CFRetain
129 , CFRelease
130 , notifyMPCopyDescription
131 };
132
133 if (store == NULL) {
134 /* sorry, you must provide a session */
135 _SCErrorSet(kSCStatusNoStoreSession);
136 return FALSE;
137 }
138
139 if (storePrivate->server == MACH_PORT_NULL) {
140 /* sorry, you must have an open session to play */
141 _SCErrorSet(kSCStatusNoStoreServer);
142 return FALSE;
143 }
144
145 if (storePrivate->notifyStatus != NotifierNotRegistered) {
146 /* sorry, you can only have one notification registered at once */
147 _SCErrorSet(kSCStatusNotifierActive);
148 return FALSE;
149 }
150
151 /* Allocating port (for server response) */
152 storePrivate->callbackPort = CFMachPortCreate(NULL,
153 informCallback,
154 &context,
155 NULL);
156
157 /* Request a notification when/if the server dies */
158 port = CFMachPortGetPort(storePrivate->callbackPort);
159 status = mach_port_request_notification(mach_task_self(),
160 port,
161 MACH_NOTIFY_NO_SENDERS,
162 1,
163 port,
164 MACH_MSG_TYPE_MAKE_SEND_ONCE,
165 &oldNotify);
166 if (status != KERN_SUCCESS) {
167 SCLog(TRUE, LOG_DEBUG, CFSTR("SCDynamicStoreNotifyCallback mach_port_request_notification(): %s"), mach_error_string(status));
168 CFMachPortInvalidate(storePrivate->callbackPort);
169 CFRelease(storePrivate->callbackPort);
170 _SCErrorSet(status);
171 return FALSE;
172 }
173
174 #ifdef DEBUG
175 if (oldNotify != MACH_PORT_NULL) {
176 SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStoreNotifyCallback(): why is oldNotify != MACH_PORT_NULL?"));
177 }
178 #endif /* DEBUG */
179
180 /* Requesting notification via mach port */
181 status = notifyviaport(storePrivate->server,
182 port,
183 0,
184 (int *)&sc_status);
185
186 if (status != KERN_SUCCESS) {
187 #ifdef DEBUG
188 if (status != MACH_SEND_INVALID_DEST)
189 SCLog(_sc_verbose, LOG_DEBUG, CFSTR("SCDynamicStoreNotifyCallback notifyviaport(): %s"), mach_error_string(status));
190 #endif /* DEBUG */
191 CFMachPortInvalidate(storePrivate->callbackPort);
192 CFRelease(storePrivate->callbackPort);
193 (void) mach_port_destroy(mach_task_self(), storePrivate->server);
194 storePrivate->server = MACH_PORT_NULL;
195 _SCErrorSet(status);
196 return FALSE;
197 }
198
199 if (sc_status != kSCStatusOK) {
200 _SCErrorSet(sc_status);
201 return FALSE;
202 }
203
204 /* set notifier active */
205 storePrivate->notifyStatus = Using_NotifierInformViaCallback;
206
207 /* Creating/adding a run loop source for the port */
208 storePrivate->callbackArgument = arg;
209 storePrivate->callbackFunction = func;
210 storePrivate->callbackRLS = CFMachPortCreateRunLoopSource(NULL, storePrivate->callbackPort, 0);
211 CFRunLoopAddSource(runLoop, storePrivate->callbackRLS, kCFRunLoopDefaultMode);
212
213 return TRUE;
214 }
215
216
217 static void
218 rlsCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
219 {
220 mach_msg_empty_rcv_t *buf = msg;
221 mach_msg_id_t msgid = buf->header.msgh_id;
222 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
223 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
224
225 if (msgid == MACH_NOTIFY_NO_SENDERS) {
226 /* the server died, disable additional callbacks */
227 #ifdef DEBUG
228 SCLog(_sc_verbose, LOG_INFO, CFSTR(" rlsCallback(), notifier port closed"));
229 #endif /* DEBUG */
230
231 #ifdef DEBUG
232 if (port != storePrivate->callbackPort) {
233 SCLog(_sc_verbose, LOG_DEBUG, CFSTR("rlsCallback(), why is port != callbackPort?"));
234 }
235 #endif /* DEBUG */
236
237 /* invalidate the run loop source(s) */
238 CFRunLoopSourceInvalidate(storePrivate->callbackRLS);
239 CFRelease(storePrivate->callbackRLS);
240 storePrivate->callbackRLS = NULL;
241
242 /* invalidate port */
243 CFMachPortInvalidate(storePrivate->callbackPort);
244 CFRelease(storePrivate->callbackPort);
245 storePrivate->callbackPort = NULL;
246
247 return;
248 }
249
250 /* signal the real runloop source */
251 CFRunLoopSourceSignal(storePrivate->rls);
252 return;
253 }
254
255
256 static void
257 rlsPortInvalidate(CFMachPortRef mp, void *info) {
258 mach_port_t port = CFMachPortGetPort(mp);
259
260 // A simple deallocate won't get rid of all the references we've accumulated
261 #ifdef DEBUG
262 SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" invalidate = %d"), port);
263 #endif /* DEBUG */
264 (void)mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1);
265 }
266
267
268 static void
269 rlsSchedule(void *info, CFRunLoopRef rl, CFStringRef mode)
270 {
271 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
272 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
273
274 #ifdef DEBUG
275 SCLog(_sc_verbose, LOG_DEBUG, CFSTR("schedule notifications for mode %@"), mode);
276 #endif /* DEBUG */
277
278 if (storePrivate->rlsRefs++ == 0) {
279 CFMachPortContext context = { 0
280 , (void *)store
281 , CFRetain
282 , CFRelease
283 , notifyMPCopyDescription
284 };
285 mach_port_t oldNotify;
286 mach_port_t port;
287 int sc_status;
288 kern_return_t status;
289
290 #ifdef DEBUG
291 SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" activate callback runloop source"));
292 #endif /* DEBUG */
293
294 /* Allocating port (for server response) */
295 status = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
296 if (status != KERN_SUCCESS) {
297 SCLog(TRUE, LOG_DEBUG, CFSTR("mach_port_allocate(): %s"), mach_error_string(status));
298 return;
299 }
300
301 status = mach_port_insert_right(mach_task_self(),
302 port,
303 port,
304 MACH_MSG_TYPE_MAKE_SEND);
305 if (status != KERN_SUCCESS) {
306 SCLog(TRUE, LOG_DEBUG, CFSTR("mach_port_insert_right(): %s"), mach_error_string(status));
307 (void) mach_port_destroy(mach_task_self(), port);
308 return;
309 }
310
311 /* Request a notification when/if the server dies */
312 status = mach_port_request_notification(mach_task_self(),
313 port,
314 MACH_NOTIFY_NO_SENDERS,
315 1,
316 port,
317 MACH_MSG_TYPE_MAKE_SEND_ONCE,
318 &oldNotify);
319 if (status != KERN_SUCCESS) {
320 SCLog(TRUE, LOG_DEBUG, CFSTR("mach_port_request_notification(): %s"), mach_error_string(status));
321 (void) mach_port_destroy(mach_task_self(), port);
322 return;
323 }
324
325 #ifdef DEBUG
326 if (oldNotify != MACH_PORT_NULL) {
327 SCLog(TRUE, LOG_ERR, CFSTR("rlsSchedule(): why is oldNotify != MACH_PORT_NULL?"));
328 }
329 #endif /* DEBUG */
330
331 status = notifyviaport(storePrivate->server, port, 0, (int *)&sc_status);
332 if (status != KERN_SUCCESS) {
333 #ifdef DEBUG
334 if (status != MACH_SEND_INVALID_DEST)
335 SCLog(_sc_verbose, LOG_DEBUG, CFSTR("notifyviaport(): %s"), mach_error_string(status));
336 #endif /* DEBUG */
337 (void) mach_port_destroy(mach_task_self(), port);
338 port = MACH_PORT_NULL;
339 (void) mach_port_destroy(mach_task_self(), storePrivate->server);
340 storePrivate->server = MACH_PORT_NULL;
341 return;
342 }
343
344 storePrivate->callbackPort = CFMachPortCreateWithPort(NULL, port, rlsCallback, &context, NULL);
345 CFMachPortSetInvalidationCallBack(storePrivate->callbackPort, rlsPortInvalidate);
346 storePrivate->callbackRLS = CFMachPortCreateRunLoopSource(NULL, storePrivate->callbackPort, 0);
347 }
348
349 if (storePrivate->callbackRLS != NULL) {
350 CFRunLoopAddSource(rl, storePrivate->callbackRLS, mode);
351 }
352
353 return;
354 }
355
356
357 static void
358 rlsCancel(void *info, CFRunLoopRef rl, CFStringRef mode)
359 {
360 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
361 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
362
363 #ifdef DEBUG
364 SCLog(_sc_verbose, LOG_DEBUG, CFSTR("cancel notifications for mode %@"), mode);
365 #endif /* DEBUG */
366
367 if (storePrivate->callbackRLS != NULL) {
368 CFRunLoopRemoveSource(rl, storePrivate->callbackRLS, mode);
369 }
370
371 if (--storePrivate->rlsRefs == 0) {
372 int sc_status;
373 kern_return_t status;
374
375 #ifdef DEBUG
376 SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" cancel callback runloop source"));
377 #endif /* DEBUG */
378
379 if (storePrivate->callbackRLS != NULL) {
380 /* invalidate & remove the run loop source */
381 CFRunLoopSourceInvalidate(storePrivate->callbackRLS);
382 CFRelease(storePrivate->callbackRLS);
383 storePrivate->callbackRLS = NULL;
384 }
385
386 if (storePrivate->callbackPort != NULL) {
387 /* invalidate port */
388 CFMachPortInvalidate(storePrivate->callbackPort);
389 CFRelease(storePrivate->callbackPort);
390 storePrivate->callbackPort = NULL;
391 }
392
393 if (storePrivate->server != MACH_PORT_NULL) {
394 status = notifycancel(storePrivate->server, (int *)&sc_status);
395 if (status != KERN_SUCCESS) {
396 #ifdef DEBUG
397 if (status != MACH_SEND_INVALID_DEST)
398 SCLog(_sc_verbose, LOG_INFO, CFSTR("notifycancel(): %s"), mach_error_string(status));
399 #endif /* DEBUG */
400 (void) mach_port_destroy(mach_task_self(), storePrivate->server);
401 storePrivate->server = MACH_PORT_NULL;
402 return;
403 }
404 }
405 }
406 return;
407 }
408
409
410 static void
411 rlsPerform(void *info)
412 {
413 CFArrayRef changedKeys;
414 void *context_info;
415 void (*context_release)(const void *);
416 SCDynamicStoreCallBack rlsFunction;
417 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
418 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
419
420 #ifdef DEBUG
421 SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" executing notification function"));
422 #endif /* DEBUG */
423
424 changedKeys = SCDynamicStoreCopyNotifiedKeys(store);
425 if (changedKeys == NULL) {
426 /* if no changes or something happened to the server */
427 return;
428 }
429
430 if (CFArrayGetCount(changedKeys) == 0) {
431 goto done;
432 }
433
434 rlsFunction = storePrivate->rlsFunction;
435
436 if (NULL != storePrivate->rlsContext.retain) {
437 context_info = (void *)storePrivate->rlsContext.retain(storePrivate->rlsContext.info);
438 context_release = storePrivate->rlsContext.release;
439 } else {
440 context_info = storePrivate->rlsContext.info;
441 context_release = NULL;
442 }
443 (*rlsFunction)(store, changedKeys, context_info);
444 if (context_release) {
445 context_release(context_info);
446 }
447
448 done :
449
450 CFRelease(changedKeys);
451 return;
452 }
453
454
455 static CFTypeRef
456 rlsRetain(CFTypeRef cf)
457 {
458 SCDynamicStoreRef store = (SCDynamicStoreRef)cf;
459 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
460
461 if (storePrivate->notifyStatus != Using_NotifierInformViaRunLoop) {
462 /* mark RLS active */
463 storePrivate->notifyStatus = Using_NotifierInformViaRunLoop;
464 /* keep a reference to the store */
465 CFRetain(store);
466 }
467
468 return cf;
469 }
470
471
472 static void
473 rlsRelease(CFTypeRef cf)
474 {
475 SCDynamicStoreRef store = (SCDynamicStoreRef)cf;
476 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
477
478 /* mark RLS inactive */
479 storePrivate->notifyStatus = NotifierNotRegistered;
480 storePrivate->rls = NULL;
481
482 /* release our reference to the store */
483 CFRelease(store);
484
485 return;
486 }
487
488
489 static CFStringRef
490 rlsCopyDescription(const void *info)
491 {
492 CFMutableStringRef result;
493 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
494 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
495
496 result = CFStringCreateMutable(NULL, 0);
497 CFStringAppendFormat(result, NULL, CFSTR("<SCDynamicStore RLS> {"));
498 CFStringAppendFormat(result, NULL, CFSTR("store = %p"), store);
499 if (storePrivate->notifyStatus == Using_NotifierInformViaRunLoop) {
500 CFStringRef description = NULL;
501
502 CFStringAppendFormat(result, NULL, CFSTR(", callout = %p"), storePrivate->rlsFunction);
503
504 if ((storePrivate->rlsContext.info != NULL) && (storePrivate->rlsContext.copyDescription != NULL)) {
505 description = (*storePrivate->rlsContext.copyDescription)(storePrivate->rlsContext.info);
506 }
507 if (description == NULL) {
508 description = CFStringCreateWithFormat(NULL, NULL, CFSTR("<SCDynamicStore context %p>"), storePrivate->rlsContext.info);
509 }
510 if (description == NULL) {
511 description = CFRetain(CFSTR("<no description>"));
512 }
513 CFStringAppendFormat(result, NULL, CFSTR(", context = %@"), description);
514 CFRelease(description);
515 } else {
516 CFStringAppendFormat(result, NULL, CFSTR(", callout = %p"), storePrivate->callbackFunction);
517 CFStringAppendFormat(result, NULL, CFSTR(", info = %p"), storePrivate->callbackArgument);
518 }
519 CFStringAppendFormat(result, NULL, CFSTR("}"));
520
521 return result;
522 }
523
524
525 CFRunLoopSourceRef
526 SCDynamicStoreCreateRunLoopSource(CFAllocatorRef allocator,
527 SCDynamicStoreRef store,
528 CFIndex order)
529 {
530 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
531
532 if (store == NULL) {
533 /* sorry, you must provide a session */
534 _SCErrorSet(kSCStatusNoStoreSession);
535 return NULL;
536 }
537
538 if (storePrivate->server == MACH_PORT_NULL) {
539 /* sorry, you must have an open session to play */
540 _SCErrorSet(kSCStatusNoStoreServer);
541 return NULL;
542 }
543
544 switch (storePrivate->notifyStatus) {
545 case NotifierNotRegistered :
546 case Using_NotifierInformViaRunLoop :
547 /* OK to enable runloop notification */
548 break;
549 default :
550 /* sorry, you can only have one notification registered at once */
551 _SCErrorSet(kSCStatusNotifierActive);
552 return NULL;
553 }
554
555 if (storePrivate->rls != NULL) {
556 CFRetain(storePrivate->rls);
557 } else {
558 CFRunLoopSourceContext context = { 0 // version
559 , (void *)store // info
560 , rlsRetain // retain
561 , rlsRelease // release
562 , rlsCopyDescription // copyDescription
563 , CFEqual // equal
564 , CFHash // hash
565 , rlsSchedule // schedule
566 , rlsCancel // cancel
567 , rlsPerform // perform
568 };
569
570 storePrivate->rls = CFRunLoopSourceCreate(allocator, order, &context);
571 }
572
573 if (storePrivate->rls == NULL) {
574 _SCErrorSet(kSCStatusFailed);
575 return NULL;
576 }
577
578 return storePrivate->rls;
579 }