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