]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCDNotifierInformViaCallback.c
02e7d48911fb7f11382af001d486c667a7197e71
[apple/configd.git] / SystemConfiguration.fproj / SCDNotifierInformViaCallback.c
1 /*
2 * Copyright (c) 2000-2005, 2008-2013 Apple 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 <Availability.h>
35 #include <TargetConditionals.h>
36 #include <sys/cdefs.h>
37 #include <dispatch/dispatch.h>
38 #include <mach/mach.h>
39 #include <mach/mach_error.h>
40
41 #include <SystemConfiguration/SystemConfiguration.h>
42 #include <SystemConfiguration/SCPrivate.h>
43 #include "SCDynamicStoreInternal.h"
44 #include "config.h" /* MiG generated file */
45
46
47 static CFStringRef
48 notifyMPCopyDescription(const void *info)
49 {
50 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
51
52 return CFStringCreateWithFormat(NULL,
53 NULL,
54 CFSTR("<SCDynamicStore notification MP> {store = %p}"),
55 store);
56 }
57
58
59 static void
60 rlsCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
61 {
62 mach_no_senders_notification_t *buf = msg;
63 mach_msg_id_t msgid = buf->not_header.msgh_id;
64 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
65 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
66
67 if (msgid == MACH_NOTIFY_NO_SENDERS) {
68 /* the server died, disable additional callbacks */
69 #ifdef DEBUG
70 SCLog(_sc_verbose, LOG_INFO, CFSTR(" rlsCallback(), notifier port closed"));
71 #endif /* DEBUG */
72
73 #ifdef DEBUG
74 if (port != storePrivate->rlsNotifyPort) {
75 SCLog(_sc_verbose, LOG_DEBUG, CFSTR("rlsCallback(), why is port != rlsNotifyPort?"));
76 }
77 #endif /* DEBUG */
78
79 /* re-establish notification and inform the client */
80 (void)__SCDynamicStoreReconnectNotifications(store);
81 }
82
83 /* signal the real runloop source */
84 if (storePrivate->rls != NULL) {
85 CFRunLoopSourceSignal(storePrivate->rls);
86 }
87 return;
88 }
89
90
91 static void
92 rlsSchedule(void *info, CFRunLoopRef rl, CFStringRef mode)
93 {
94 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
95 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
96
97 #ifdef DEBUG
98 SCLog(_sc_verbose, LOG_DEBUG,
99 CFSTR("schedule notifications for mode %@"),
100 (rl != NULL) ? mode : CFSTR("libdispatch"));
101 #endif /* DEBUG */
102
103 if (storePrivate->rlList == NULL) {
104 CFMachPortContext context = { 0
105 , (void *)store
106 , CFRetain
107 , CFRelease
108 , notifyMPCopyDescription
109 };
110 mach_port_t oldNotify;
111 mach_port_t port;
112 int sc_status;
113 kern_return_t status;
114
115 #ifdef DEBUG
116 SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" activate callback runloop source"));
117 #endif /* DEBUG */
118
119 /* Allocating port (for server response) */
120 status = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
121 if (status != KERN_SUCCESS) {
122 SCLog(TRUE, LOG_ERR, CFSTR("rlsSchedule mach_port_allocate(): %s"), mach_error_string(status));
123 return;
124 }
125
126 status = mach_port_insert_right(mach_task_self(),
127 port,
128 port,
129 MACH_MSG_TYPE_MAKE_SEND);
130 if (status != KERN_SUCCESS) {
131 /*
132 * We can't insert a send right into our own port! This should
133 * only happen if someone stomped on OUR port (so let's leave
134 * the port alone).
135 */
136 SCLog(TRUE, LOG_ERR, CFSTR("rlsSchedule mach_port_insert_right(): %s"), mach_error_string(status));
137 return;
138 }
139
140 /* Request a notification when/if the server dies */
141 status = mach_port_request_notification(mach_task_self(),
142 port,
143 MACH_NOTIFY_NO_SENDERS,
144 1,
145 port,
146 MACH_MSG_TYPE_MAKE_SEND_ONCE,
147 &oldNotify);
148 if (status != KERN_SUCCESS) {
149 /*
150 * We can't request a notification for our own port! This should
151 * only happen if someone stomped on OUR port (so let's leave
152 * the port alone).
153 */
154 SCLog(TRUE, LOG_ERR, CFSTR("rlsSchedule mach_port_request_notification(): %s"), mach_error_string(status));
155 return;
156 }
157
158 if (oldNotify != MACH_PORT_NULL) {
159 SCLog(TRUE, LOG_ERR, CFSTR("rlsSchedule(): oldNotify != MACH_PORT_NULL"));
160 }
161
162 retry :
163
164 __MACH_PORT_DEBUG(TRUE, "*** rlsSchedule", port);
165 status = notifyviaport(storePrivate->server, port, 0, (int *)&sc_status);
166
167 if (__SCDynamicStoreCheckRetryAndHandleError(store,
168 status,
169 &sc_status,
170 "rlsSchedule notifyviaport()")) {
171 goto retry;
172 }
173
174 if (status != KERN_SUCCESS) {
175 if ((status == MACH_SEND_INVALID_DEST) || (status == MIG_SERVER_DIED)) {
176 /* remove the send right that we tried (but failed) to pass to the server */
177 (void) mach_port_deallocate(mach_task_self(), port);
178 }
179
180 /* remove our receive right */
181 (void) mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1);
182 return;
183 }
184
185 if (sc_status != kSCStatusOK) {
186 /* something [else] didn't work, remove our receive right */
187 (void) mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1);
188 return;
189 }
190
191 __MACH_PORT_DEBUG(TRUE, "*** rlsSchedule (after notifyviaport)", port);
192 storePrivate->rlsNotifyPort = _SC_CFMachPortCreateWithPort("SCDynamicStore",
193 port,
194 rlsCallback,
195 &context);
196 storePrivate->rlsNotifyRLS = CFMachPortCreateRunLoopSource(NULL, storePrivate->rlsNotifyPort, 0);
197
198 storePrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
199 }
200
201 if ((rl != NULL) && (storePrivate->rlsNotifyRLS != NULL)) {
202 if (!_SC_isScheduled(store, rl, mode, storePrivate->rlList)) {
203 /*
204 * if we are not already scheduled with this runLoop / runLoopMode
205 */
206 CFRunLoopAddSource(rl, storePrivate->rlsNotifyRLS, mode);
207 __MACH_PORT_DEBUG(TRUE, "*** rlsSchedule (after CFRunLoopAddSource)", CFMachPortGetPort(storePrivate->rlsNotifyPort));
208 }
209
210 _SC_schedule(store, rl, mode, storePrivate->rlList);
211 }
212
213 return;
214 }
215
216
217 static void
218 rlsCancel(void *info, CFRunLoopRef rl, CFStringRef mode)
219 {
220 CFIndex n = 0;
221 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
222 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
223
224 #ifdef DEBUG
225 SCLog(_sc_verbose, LOG_DEBUG,
226 CFSTR("cancel notifications for mode %@"),
227 (rl != NULL) ? mode : CFSTR("libdispatch"));
228 #endif /* DEBUG */
229
230 if ((rl != NULL) && (storePrivate->rlsNotifyRLS != NULL)) {
231 if (_SC_unschedule(store, rl, mode, storePrivate->rlList, FALSE)) {
232 /*
233 * if currently scheduled on this runLoop / runLoopMode
234 */
235 n = CFArrayGetCount(storePrivate->rlList);
236 if (n == 0 || !_SC_isScheduled(store, rl, mode, storePrivate->rlList)) {
237 /*
238 * if we are no longer scheduled to receive notifications for
239 * this runLoop / runLoopMode
240 */
241 CFRunLoopRemoveSource(rl, storePrivate->rlsNotifyRLS, mode);
242 }
243 }
244 }
245
246 if (n == 0) {
247 int sc_status;
248 kern_return_t status;
249
250 #ifdef DEBUG
251 SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" cancel callback runloop source"));
252 #endif /* DEBUG */
253 __MACH_PORT_DEBUG((storePrivate->rlsNotifyPort != NULL),
254 "*** rlsCancel",
255 CFMachPortGetPort(storePrivate->rlsNotifyPort));
256
257 if (storePrivate->rlList != NULL) {
258 CFRelease(storePrivate->rlList);
259 storePrivate->rlList = NULL;
260 }
261
262 if (storePrivate->rlsNotifyRLS != NULL) {
263 /* invalidate & remove the run loop source */
264 CFRunLoopSourceInvalidate(storePrivate->rlsNotifyRLS);
265 CFRelease(storePrivate->rlsNotifyRLS);
266 storePrivate->rlsNotifyRLS = NULL;
267 }
268
269 if (storePrivate->rlsNotifyPort != NULL) {
270 mach_port_t mp;
271
272 mp = CFMachPortGetPort(storePrivate->rlsNotifyPort);
273 __MACH_PORT_DEBUG((storePrivate->rlsNotifyPort != NULL),
274 "*** rlsCancel (before invalidating/releasing CFMachPort)",
275 mp);
276
277 /* invalidate and release port */
278 CFMachPortInvalidate(storePrivate->rlsNotifyPort);
279 CFRelease(storePrivate->rlsNotifyPort);
280 storePrivate->rlsNotifyPort = NULL;
281
282 /* and, finally, remove our receive right */
283 (void)mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_RECEIVE, -1);
284 }
285
286 if (storePrivate->server != MACH_PORT_NULL) {
287 status = notifycancel(storePrivate->server, (int *)&sc_status);
288
289 (void) __SCDynamicStoreCheckRetryAndHandleError(store,
290 status,
291 &sc_status,
292 "rlsCancel notifycancel()");
293
294 if (status != KERN_SUCCESS) {
295 return;
296 }
297 }
298 }
299
300 return;
301 }
302
303
304 static void
305 rlsPerform(void *info)
306 {
307 CFArrayRef changedKeys;
308 void *context_info;
309 void (*context_release)(const void *);
310 SCDynamicStoreCallBack rlsFunction;
311 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
312 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
313
314 #ifdef DEBUG
315 SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" executing notification function"));
316 #endif /* DEBUG */
317
318 changedKeys = SCDynamicStoreCopyNotifiedKeys(store);
319 if (storePrivate->disconnectForceCallBack) {
320 storePrivate->disconnectForceCallBack = FALSE;
321 if (changedKeys == NULL) {
322 changedKeys = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks);
323 }
324 } else {
325 if (changedKeys == NULL) {
326 /* if no changes or something happened to the server */
327 return;
328 } else if (CFArrayGetCount(changedKeys) == 0) {
329 goto done;
330 }
331 }
332
333 rlsFunction = storePrivate->rlsFunction;
334
335 if (storePrivate->rlsContext.retain != NULL) {
336 context_info = (void *)storePrivate->rlsContext.retain(storePrivate->rlsContext.info);
337 context_release = storePrivate->rlsContext.release;
338 } else {
339 context_info = storePrivate->rlsContext.info;
340 context_release = NULL;
341 }
342 if (rlsFunction != NULL) {
343 (*rlsFunction)(store, changedKeys, context_info);
344 }
345 if (context_release != NULL) {
346 context_release(context_info);
347 }
348
349 done :
350
351 CFRelease(changedKeys);
352 return;
353 }
354
355
356 static CFTypeRef
357 rlsRetain(CFTypeRef cf)
358 {
359 SCDynamicStoreRef store = (SCDynamicStoreRef)cf;
360 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
361
362 switch (storePrivate->notifyStatus) {
363 case NotifierNotRegistered :
364 /* mark RLS active */
365 storePrivate->notifyStatus = Using_NotifierInformViaRunLoop;
366 /* keep a reference to the store */
367 CFRetain(store);
368 break;
369 case Using_NotifierInformViaRunLoop :
370 break;
371 default :
372 SCLog(TRUE, LOG_ERR, CFSTR("rlsRetain() error: notifyStatus=%d"), storePrivate->notifyStatus);
373 break;
374 }
375
376 return cf;
377 }
378
379
380 static void
381 rlsRelease(CFTypeRef cf)
382 {
383 SCDynamicStoreRef store = (SCDynamicStoreRef)cf;
384 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
385
386 switch (storePrivate->notifyStatus) {
387 case NotifierNotRegistered :
388 break;
389 case Using_NotifierInformViaRunLoop :
390 /* mark RLS inactive */
391 storePrivate->notifyStatus = NotifierNotRegistered;
392 storePrivate->rls = NULL;
393
394 /* release our reference to the store */
395 CFRelease(store);
396 break;
397 default :
398 SCLog(TRUE, LOG_ERR, CFSTR("rlsRelease() error: notifyStatus=%d"), storePrivate->notifyStatus);
399 break;
400 }
401
402 return;
403 }
404
405
406 static CFStringRef
407 rlsCopyDescription(const void *info)
408 {
409 CFMutableStringRef result;
410 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
411 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
412
413 result = CFStringCreateMutable(NULL, 0);
414 CFStringAppendFormat(result, NULL, CFSTR("<SCDynamicStore RLS> {"));
415 CFStringAppendFormat(result, NULL, CFSTR("store = %p"), store);
416 if (storePrivate->notifyStatus == Using_NotifierInformViaRunLoop) {
417 CFStringRef description = NULL;
418
419 CFStringAppendFormat(result, NULL, CFSTR(", callout = %p"), storePrivate->rlsFunction);
420
421 if ((storePrivate->rlsContext.info != NULL) && (storePrivate->rlsContext.copyDescription != NULL)) {
422 description = (*storePrivate->rlsContext.copyDescription)(storePrivate->rlsContext.info);
423 }
424 if (description == NULL) {
425 description = CFStringCreateWithFormat(NULL, NULL, CFSTR("<SCDynamicStore context %p>"), storePrivate->rlsContext.info);
426 }
427 if (description == NULL) {
428 description = CFRetain(CFSTR("<no description>"));
429 }
430 CFStringAppendFormat(result, NULL, CFSTR(", context = %@"), description);
431 CFRelease(description);
432 }
433 CFStringAppendFormat(result, NULL, CFSTR("}"));
434
435 return result;
436 }
437
438
439 CFRunLoopSourceRef
440 SCDynamicStoreCreateRunLoopSource(CFAllocatorRef allocator,
441 SCDynamicStoreRef store,
442 CFIndex order)
443 {
444 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
445
446 if (store == NULL) {
447 /* sorry, you must provide a session */
448 _SCErrorSet(kSCStatusNoStoreSession);
449 return NULL;
450 }
451
452 if (storePrivate->server == MACH_PORT_NULL) {
453 /* sorry, you must have an open session to play */
454 _SCErrorSet(kSCStatusNoStoreServer);
455 return NULL;
456 }
457
458 switch (storePrivate->notifyStatus) {
459 case NotifierNotRegistered :
460 case Using_NotifierInformViaRunLoop :
461 /* OK to enable runloop notification */
462 break;
463 default :
464 /* sorry, you can only have one notification registered at once */
465 _SCErrorSet(kSCStatusNotifierActive);
466 return NULL;
467 }
468
469 if (storePrivate->rls != NULL) {
470 CFRetain(storePrivate->rls);
471 } else {
472 CFRunLoopSourceContext context = { 0 // version
473 , (void *)store // info
474 , rlsRetain // retain
475 , rlsRelease // release
476 , rlsCopyDescription // copyDescription
477 , CFEqual // equal
478 , CFHash // hash
479 , rlsSchedule // schedule
480 , rlsCancel // cancel
481 , rlsPerform // perform
482 };
483
484 storePrivate->rls = CFRunLoopSourceCreate(allocator, order, &context);
485 if (storePrivate->rls == NULL) {
486 _SCErrorSet(kSCStatusFailed);
487 }
488 }
489
490 return storePrivate->rls;
491 }
492
493
494 Boolean
495 SCDynamicStoreSetDispatchQueue(SCDynamicStoreRef store, dispatch_queue_t queue)
496 {
497 dispatch_group_t drainGroup = NULL;
498 dispatch_queue_t drainQueue = NULL;
499 dispatch_group_t group = NULL;
500 mach_port_t mp;
501 Boolean ok = FALSE;
502 dispatch_source_t source;
503 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
504
505 if (store == NULL) {
506 // sorry, you must provide a session
507 _SCErrorSet(kSCStatusNoStoreSession);
508 return FALSE;
509 }
510
511 if (queue == NULL) {
512 if (storePrivate->dispatchQueue == NULL) {
513 _SCErrorSet(kSCStatusInvalidArgument);
514 return FALSE;
515 }
516
517 ok = TRUE;
518 goto cleanup;
519 }
520
521 if (storePrivate->server == MACH_PORT_NULL) {
522 // sorry, you must have an open session to play
523 _SCErrorSet(kSCStatusNoStoreServer);
524 return FALSE;
525 }
526
527 if ((storePrivate->dispatchQueue != NULL) || (storePrivate->rls != NULL)) {
528 _SCErrorSet(kSCStatusInvalidArgument);
529 return FALSE;
530 }
531
532 if (storePrivate->notifyStatus != NotifierNotRegistered) {
533 // sorry, you can only have one notification registered at once...
534 _SCErrorSet(kSCStatusNotifierActive);
535 return FALSE;
536 }
537
538 /*
539 * mark our using of the SCDynamicStore notifications, create and schedule
540 * the notification port (storePrivate->rlsNotifyPort), and a bunch of other
541 * "setup"
542 */
543 storePrivate->notifyStatus = Using_NotifierInformViaDispatch;
544 rlsSchedule((void*)store, NULL, NULL);
545 if (storePrivate->rlsNotifyPort == NULL) {
546 /* if we could not schedule the notification */
547 _SCErrorSet(kSCStatusFailed);
548 goto cleanup;
549 }
550
551 // retain the dispatch queue
552 storePrivate->dispatchQueue = queue;
553 dispatch_retain(storePrivate->dispatchQueue);
554
555 //
556 // We've taken a reference to the callers dispatch_queue and we
557 // want to hold on to that reference until we've processed any/all
558 // notifications. To facilitate this we create a group, dispatch
559 // any notification blocks to via that group, and when the caller
560 // has told us to stop the notifications (unschedule) we wait for
561 // the group to empty and use the group's finalizer to release
562 // our reference to the SCDynamicStore.
563 //
564 group = dispatch_group_create();
565 storePrivate->dispatchGroup = group;
566 CFRetain(store);
567 dispatch_set_context(storePrivate->dispatchGroup, (void *)store);
568 dispatch_set_finalizer_f(storePrivate->dispatchGroup, (dispatch_function_t)CFRelease);
569
570 // create a dispatch source for the mach notifications
571 mp = CFMachPortGetPort(storePrivate->rlsNotifyPort);
572 source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, mp, 0, queue);
573 if (source == NULL) {
574 SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStore dispatch_source_create() failed"));
575 _SCErrorSet(kSCStatusFailed);
576 goto cleanup;
577 }
578
579 dispatch_source_set_event_handler(source, ^{
580 kern_return_t kr;
581 mach_msg_id_t msgid;
582 union {
583 u_int8_t buf[sizeof(mach_msg_empty_t) + MAX_TRAILER_SIZE];
584 mach_msg_empty_rcv_t msg;
585 mach_no_senders_notification_t no_senders;
586 } notify_msg;
587
588 kr = mach_msg(&notify_msg.msg.header, // msg
589 MACH_RCV_MSG, // options
590 0, // send_size
591 sizeof(notify_msg), // rcv_size
592 mp, // rcv_name
593 MACH_MSG_TIMEOUT_NONE, // timeout
594 MACH_PORT_NULL); // notify
595 if (kr != KERN_SUCCESS) {
596 SCLog(TRUE, LOG_ERR,
597 CFSTR("SCDynamicStore notification handler, kr=0x%x"),
598 kr);
599 return;
600 }
601
602 msgid = notify_msg.msg.header.msgh_id;
603
604 CFRetain(store);
605 dispatch_group_async(group, queue, ^{
606 if (msgid == MACH_NOTIFY_NO_SENDERS) {
607 // re-establish notification and inform the client
608 (void)__SCDynamicStoreReconnectNotifications(store);
609 }
610 rlsPerform(storePrivate);
611 CFRelease(store);
612 });
613 });
614
615 dispatch_source_set_cancel_handler(source, ^{
616 dispatch_release(source);
617 });
618
619 storePrivate->dispatchSource = source;
620 dispatch_resume(source);
621
622 return TRUE;
623
624 cleanup :
625
626 CFRetain(store);
627
628 if (storePrivate->dispatchSource != NULL) {
629 dispatch_source_cancel(storePrivate->dispatchSource);
630 storePrivate->dispatchSource = NULL;
631 }
632 drainGroup = storePrivate->dispatchGroup;
633 storePrivate->dispatchGroup = NULL;
634 drainQueue = storePrivate->dispatchQueue;
635 storePrivate->dispatchQueue = NULL;
636
637 rlsCancel((void*)store, NULL, NULL);
638
639 if (drainGroup != NULL) {
640 dispatch_group_notify(drainGroup, drainQueue, ^{
641 // release group/queue references
642 dispatch_release(drainQueue);
643 dispatch_release(drainGroup); // releases our store reference
644 });
645 }
646
647 storePrivate->notifyStatus = NotifierNotRegistered;
648
649 CFRelease(store);
650
651 return ok;
652 }