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