]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCDNotifierInformViaCallback.c
9b1cf56f3ad09c5cccd073aee3812249b7988ab7
[apple/configd.git] / SystemConfiguration.fproj / SCDNotifierInformViaCallback.c
1 /*
2 * Copyright (c) 2000 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, (void *)store, NULL, NULL, NULL };
111
112 SCLog(_sc_verbose, LOG_DEBUG, CFSTR("SCDynamicStoreNotifyCallback:"));
113
114 if (!store) {
115 /* sorry, you must provide a session */
116 _SCErrorSet(kSCStatusNoStoreSession);
117 return FALSE;
118 }
119
120 if (storePrivate->server == MACH_PORT_NULL) {
121 /* sorry, you must have an open session to play */
122 _SCErrorSet(kSCStatusNoStoreServer);
123 return FALSE;
124 }
125
126 if (storePrivate->notifyStatus != NotifierNotRegistered) {
127 /* sorry, you can only have one notification registered at once */
128 _SCErrorSet(kSCStatusNotifierActive);
129 return FALSE;
130 }
131
132 /* Allocating port (for server response) */
133 storePrivate->callbackPort = CFMachPortCreate(NULL,
134 informCallback,
135 &context,
136 NULL);
137
138 /* Request a notification when/if the server dies */
139 port = CFMachPortGetPort(storePrivate->callbackPort);
140 status = mach_port_request_notification(mach_task_self(),
141 port,
142 MACH_NOTIFY_NO_SENDERS,
143 1,
144 port,
145 MACH_MSG_TYPE_MAKE_SEND_ONCE,
146 &oldNotify);
147 if (status != KERN_SUCCESS) {
148 SCLog(_sc_verbose, LOG_DEBUG, CFSTR("mach_port_request_notification(): %s"), mach_error_string(status));
149 CFMachPortInvalidate(storePrivate->callbackPort);
150 CFRelease(storePrivate->callbackPort);
151 _SCErrorSet(status);
152 return FALSE;
153 }
154
155 if (oldNotify != MACH_PORT_NULL) {
156 SCLog(_sc_verbose, LOG_ERR, CFSTR("SCDynamicStoreNotifyCallback(): why is oldNotify != MACH_PORT_NULL?"));
157 }
158
159 /* Requesting notification via mach port */
160 status = notifyviaport(storePrivate->server,
161 port,
162 0,
163 (int *)&sc_status);
164
165 if (status != KERN_SUCCESS) {
166 if (status != MACH_SEND_INVALID_DEST)
167 SCLog(_sc_verbose, LOG_DEBUG, CFSTR("notifyviaport(): %s"), mach_error_string(status));
168 CFMachPortInvalidate(storePrivate->callbackPort);
169 CFRelease(storePrivate->callbackPort);
170 (void) mach_port_destroy(mach_task_self(), storePrivate->server);
171 storePrivate->server = MACH_PORT_NULL;
172 _SCErrorSet(status);
173 return FALSE;
174 }
175
176 if (sc_status != kSCStatusOK) {
177 _SCErrorSet(sc_status);
178 return FALSE;
179 }
180
181 /* set notifier active */
182 storePrivate->notifyStatus = Using_NotifierInformViaCallback;
183
184 /* Creating/adding a run loop source for the port */
185 storePrivate->callbackArgument = arg;
186 storePrivate->callbackFunction = func;
187 storePrivate->callbackRunLoop = runLoop;
188 storePrivate->callbackRunLoopSource =
189 CFMachPortCreateRunLoopSource(NULL, storePrivate->callbackPort, 0);
190
191 CFRunLoopAddSource(storePrivate->callbackRunLoop,
192 storePrivate->callbackRunLoopSource,
193 kCFRunLoopDefaultMode);
194
195 return TRUE;
196 }
197
198
199 static void
200 rlsCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
201 {
202 mach_msg_empty_rcv_t *buf = msg;
203 mach_msg_id_t msgid = buf->header.msgh_id;
204 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
205 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
206
207 if (msgid == MACH_NOTIFY_NO_SENDERS) {
208 /* the server died, disable additional callbacks */
209 SCLog(_sc_verbose, LOG_INFO, CFSTR(" rlsCallback(), notifier port closed"));
210
211 #ifdef DEBUG
212 if (port != storePrivate->callbackPort) {
213 SCLog(_sc_verbose, LOG_DEBUG, CFSTR("rlsCallback(), why is port != callbackPort?"));
214 }
215 #endif /* DEBUG */
216
217 /* remove the run loop source(s) */
218 CFRunLoopSourceInvalidate(storePrivate->callbackRunLoopSource);
219 CFRelease(storePrivate->callbackRunLoopSource);
220
221 /* invalidate port */
222 CFMachPortInvalidate(storePrivate->callbackPort);
223 CFRelease(storePrivate->callbackPort);
224
225 return;
226 }
227
228 /* signal the real runloop source */
229 CFRunLoopSourceSignal(storePrivate->rls);
230 return;
231 }
232
233
234 static void
235 rlsSchedule(void *info, CFRunLoopRef rl, CFStringRef mode)
236 {
237 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
238 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
239
240 SCLog(_sc_verbose, LOG_DEBUG, CFSTR("schedule notifications for mode %@"), mode);
241
242 if (storePrivate->rlsRefs++ == 0) {
243 CFMachPortContext context = { 0, (void *)store, NULL, NULL, NULL };
244 mach_port_t oldNotify;
245 mach_port_t port;
246 int sc_status;
247 kern_return_t status;
248
249 SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" activate callback runloop source"));
250
251 /* Allocating port (for server response) */
252 status = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
253 if (status != KERN_SUCCESS) {
254 SCLog(_sc_verbose, LOG_DEBUG, CFSTR("mach_port_allocate(): %s"), mach_error_string(status));
255 return;
256 }
257 SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" port = %d"), port);
258
259 status = mach_port_insert_right(mach_task_self(),
260 port,
261 port,
262 MACH_MSG_TYPE_MAKE_SEND);
263 if (status != KERN_SUCCESS) {
264 SCLog(_sc_verbose, LOG_DEBUG, CFSTR("mach_port_insert_right(): %s"), mach_error_string(status));
265 (void) mach_port_destroy(mach_task_self(), port);
266 return;
267 }
268
269 /* Request a notification when/if the server dies */
270 status = mach_port_request_notification(mach_task_self(),
271 port,
272 MACH_NOTIFY_NO_SENDERS,
273 1,
274 port,
275 MACH_MSG_TYPE_MAKE_SEND_ONCE,
276 &oldNotify);
277 if (status != KERN_SUCCESS) {
278 SCLog(_sc_verbose, LOG_DEBUG, CFSTR("mach_port_request_notification(): %s"), mach_error_string(status));
279 (void) mach_port_destroy(mach_task_self(), port);
280 return;
281 }
282
283 if (oldNotify != MACH_PORT_NULL) {
284 SCLog(_sc_verbose, LOG_ERR, CFSTR("rlsSchedule(): why is oldNotify != MACH_PORT_NULL?"));
285 }
286
287 status = notifyviaport(storePrivate->server, port, 0, (int *)&sc_status);
288 if (status != KERN_SUCCESS) {
289 if (status != MACH_SEND_INVALID_DEST)
290 SCLog(_sc_verbose, LOG_DEBUG, CFSTR("notifyviaport(): %s"), mach_error_string(status));
291 (void) mach_port_destroy(mach_task_self(), port);
292 port = MACH_PORT_NULL;
293 (void) mach_port_destroy(mach_task_self(), storePrivate->server);
294 storePrivate->server = MACH_PORT_NULL;
295 return;
296 }
297
298 storePrivate->callbackPort = CFMachPortCreateWithPort(NULL, port, rlsCallback, &context, NULL);
299 storePrivate->callbackRunLoopSource = CFMachPortCreateRunLoopSource(NULL, storePrivate->callbackPort, 0);
300 }
301
302 CFRunLoopAddSource(rl, storePrivate->callbackRunLoopSource, mode);
303 return;
304 }
305
306
307 static void
308 rlsCancel(void *info, CFRunLoopRef rl, CFStringRef mode)
309 {
310 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
311 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
312
313 SCLog(_sc_verbose, LOG_DEBUG, CFSTR("cancel notifications for mode %@"), mode);
314
315 CFRunLoopRemoveSource(rl, storePrivate->callbackRunLoopSource, mode);
316 if (--storePrivate->rlsRefs == 0) {
317 int sc_status;
318 kern_return_t status;
319
320 SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" cancel callback runloop source"));
321
322 /* remove the run loop source */
323 CFRelease(storePrivate->callbackRunLoopSource);
324
325 /* invalidate port */
326 CFMachPortInvalidate(storePrivate->callbackPort);
327 CFRelease(storePrivate->callbackPort);
328
329 status = notifycancel(storePrivate->server, (int *)&sc_status);
330 if (status != KERN_SUCCESS) {
331 if (status != MACH_SEND_INVALID_DEST)
332 SCLog(_sc_verbose, LOG_INFO, CFSTR("notifycancel(): %s"), mach_error_string(status));
333 (void) mach_port_destroy(mach_task_self(), storePrivate->server);
334 storePrivate->server = MACH_PORT_NULL;
335 return;
336 }
337 }
338 return;
339 }
340
341 static void
342 rlsPerform(void *info)
343 {
344 CFArrayRef changedKeys;
345 void *context_info;
346 void (*context_release)(const void *);
347 SCDynamicStoreCallBack rlsFunction;
348 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
349 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
350
351 SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" executing notifiction function"));
352
353 changedKeys = SCDynamicStoreCopyNotifiedKeys(store);
354 rlsFunction = storePrivate->rlsFunction;
355
356 if (NULL != storePrivate->rlsContext.retain) {
357 context_info = (void *)storePrivate->rlsContext.retain(storePrivate->rlsContext.info);
358 context_release = storePrivate->rlsContext.release;
359 } else {
360 context_info = storePrivate->rlsContext.info;
361 context_release = NULL;
362 }
363 (*rlsFunction)(store, changedKeys, storePrivate->rlsContext.info);
364 if (context_release) {
365 context_release(context_info);
366 }
367
368 CFRelease(changedKeys);
369 return;
370 }
371
372
373 CFRunLoopSourceRef
374 SCDynamicStoreCreateRunLoopSource(CFAllocatorRef allocator,
375 SCDynamicStoreRef store,
376 CFIndex order)
377 {
378 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
379
380 SCLog(_sc_verbose, LOG_DEBUG, CFSTR("SCDynamicStoreCreateRunLoopSource:"));
381
382 if (!store) {
383 /* sorry, you must provide a session */
384 _SCErrorSet(kSCStatusNoStoreSession);
385 return NULL;
386 }
387
388 if (storePrivate->server == MACH_PORT_NULL) {
389 /* sorry, you must have an open session to play */
390 _SCErrorSet(kSCStatusNoStoreServer);
391 return NULL;
392 }
393
394 switch (storePrivate->notifyStatus) {
395 case NotifierNotRegistered :
396 case Using_NotifierInformViaRunLoop :
397 /* set notifier active */
398 storePrivate->notifyStatus = Using_NotifierInformViaRunLoop;
399 break;
400 default :
401 /* sorry, you can only have one notification registered at once */
402 _SCErrorSet(kSCStatusNotifierActive);
403 return NULL;
404 }
405
406 if (!storePrivate->rls) {
407 CFRunLoopSourceContext context;
408
409 context.version = 0;
410 context.info = (void *)store;
411 context.retain = (const void *(*)(const void *))CFRetain;
412 context.release = (void (*)(const void *))CFRelease;
413 context.copyDescription = (CFStringRef (*)(const void *))CFCopyDescription;
414 context.equal = (Boolean (*)(const void *, const void *))CFEqual;
415 context.hash = (CFHashCode (*)(const void *))CFHash;
416 context.schedule = rlsSchedule;
417 context.cancel = rlsCancel;
418 context.perform = rlsPerform;
419
420 storePrivate->rls = CFRunLoopSourceCreate(allocator, order, &context);
421 }
422
423 return (CFRunLoopSourceRef)CFRetain(storePrivate->rls);
424 }
425