]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCDNotifierInformViaCallback.c
configd-24.1.tar.gz
[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 #include <mach/mach.h>
24 #include <mach/mach_error.h>
25
26 #include <SystemConfiguration/SCD.h>
27 #include "config.h" /* MiG generated file */
28 #include "SCDPrivate.h"
29
30
31 static void
32 informCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
33 {
34 SCDSessionRef session = (SCDSessionRef)info;
35 SCDSessionPrivateRef sessionPrivate = (SCDSessionPrivateRef)session;
36 mach_msg_empty_rcv_t *buf = msg;
37 mach_msg_id_t msgid = buf->header.msgh_id;
38 SCDCallbackRoutine_t cbFunc = sessionPrivate->callbackFunction;
39 void *cbArg = sessionPrivate->callbackArgument;
40
41 if (msgid == MACH_NOTIFY_NO_SENDERS) {
42 /* the server died, disable additional callbacks */
43 SCDLog(LOG_DEBUG, CFSTR(" notifier port closed, disabling notifier"));
44 } else if (cbFunc == NULL) {
45 /* there is no (longer) a callback function, disable additional callbacks */
46 SCDLog(LOG_DEBUG, CFSTR(" no callback function, disabling notifier"));
47 } else {
48 SCDLog(LOG_DEBUG, CFSTR(" executing notifiction function"));
49 if ((*cbFunc)(session, cbArg)) {
50 /*
51 * callback function returned success.
52 */
53 return;
54 } else {
55 SCDLog(LOG_DEBUG, CFSTR(" callback returned error, disabling notifier"));
56 }
57 }
58
59 #ifdef DEBUG
60 if (port != sessionPrivate->callbackPort) {
61 SCDLog(LOG_DEBUG, CFSTR("informCallback, why is port != callbackPort?"));
62 }
63 #endif /* DEBUG */
64
65 /* we have encountered some type of error, disable additional callbacks */
66
67 /* XXX invalidating the port is not sufficient, remove the run loop source */
68 CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
69 sessionPrivate->callbackRunLoopSource,
70 kCFRunLoopDefaultMode);
71 CFRelease(sessionPrivate->callbackRunLoopSource);
72
73 /* invalidate port */
74 CFMachPortInvalidate(port);
75 CFRelease(port);
76
77 sessionPrivate->notifyStatus = NotifierNotRegistered;
78 sessionPrivate->callbackFunction = NULL;
79 sessionPrivate->callbackArgument = NULL;
80 sessionPrivate->callbackPort = NULL;
81 sessionPrivate->callbackRunLoopSource = NULL; /* XXX */
82
83 return;
84 }
85
86
87 static void
88 cleanupMachPort(void *ptr)
89 {
90 mach_port_t *port = (mach_port_t *)ptr;
91
92 SCDLog(LOG_DEBUG, CFSTR(" cleaning up notification port %d"), *port);
93 if (*port != MACH_PORT_NULL) {
94 (void) mach_port_destroy(mach_task_self(), *port);
95 free(port);
96 }
97
98 return;
99 }
100
101
102 static void *
103 watcherThread(void *arg)
104 {
105 SCDSessionRef session = (SCDSessionRef)arg;
106 SCDSessionPrivateRef sessionPrivate = (SCDSessionPrivateRef)session;
107 SCDCallbackRoutine_t cbFunc = sessionPrivate->callbackFunction;
108 void *cbArg = sessionPrivate->callbackArgument;
109 mach_port_t *port = malloc(sizeof(mach_port_t));
110
111 *port = CFMachPortGetPort(sessionPrivate->callbackPort);
112 pthread_cleanup_push(cleanupMachPort, (void *)port);
113
114 while (TRUE) {
115 mach_msg_id_t msgid;
116
117 SCDLog(LOG_DEBUG, CFSTR("Callback thread waiting, port=%d, tid=0x%08x"),
118 *port, pthread_self());
119
120 msgid = _waitForMachMessage(*port);
121
122 if (msgid == MACH_NOTIFY_NO_SENDERS) {
123 /* the server closed the notifier port, disable additional callbacks */
124 SCDLog(LOG_DEBUG, CFSTR(" notifier port closed, disabling notifier"));
125 break;
126 }
127
128 if (msgid == -1) {
129 mach_port_type_t pt;
130
131 /* an error was detected, disable additional callbacks */
132 SCDLog(LOG_DEBUG, CFSTR(" server failure, disabling notifier"));
133
134 /* check if the server connection is not valid, close if necessary */
135 if ((mach_port_type(mach_task_self(), sessionPrivate->server, &pt) == KERN_SUCCESS) &&
136 (pt & MACH_PORT_TYPE_DEAD_NAME)) {
137 SCDLog(LOG_DEBUG, CFSTR(" server process died, destroying (dead) port"));
138 (void) mach_port_destroy(mach_task_self(), sessionPrivate->server);
139 sessionPrivate->server = MACH_PORT_NULL;
140 }
141 break;
142 }
143
144 if (cbFunc == NULL) {
145 /* there is no (longer) a callback function, disable additional callbacks */
146 SCDLog(LOG_DEBUG, CFSTR(" no callback function, disabling notifier"));
147 break;
148 }
149
150 SCDLog(LOG_DEBUG, CFSTR(" executing notifiction function"));
151
152 if (!(*cbFunc)(session, cbArg)) {
153 /*
154 * callback function returned an error, exit the thread
155 */
156 break;
157 }
158
159 }
160
161 /*
162 * pop the cleanup routine for the "port" mach port. We end up calling
163 * mach_port_destroy() in the process.
164 */
165 pthread_cleanup_pop(1);
166
167 pthread_exit (NULL);
168 return NULL;
169 }
170
171
172 SCDStatus
173 SCDNotifierInformViaCallback(SCDSessionRef session, SCDCallbackRoutine_t func, void *arg)
174 {
175 SCDSessionPrivateRef sessionPrivate = (SCDSessionPrivateRef)session;
176 kern_return_t status;
177 mach_port_t port;
178 mach_port_t oldNotify;
179 SCDStatus scd_status;
180 CFMachPortContext context = { 0, (void *)session, NULL, NULL, NULL };
181
182 SCDLog(LOG_DEBUG, CFSTR("SCDNotifierInformViaCallback:"));
183
184 if ((session == NULL) || (sessionPrivate->server == MACH_PORT_NULL)) {
185 return SCD_NOSESSION; /* you must have an open session to play */
186 }
187
188 if (sessionPrivate->notifyStatus != NotifierNotRegistered) {
189 /* sorry, you can only have one notification registered at once */
190 return SCD_NOTIFIERACTIVE;
191 }
192
193 if (func == NULL) {
194 /* sorry, you must specify a callback function */
195 return SCD_INVALIDARGUMENT;
196 }
197
198 /* Allocating port (for server response) */
199 sessionPrivate->callbackPort = CFMachPortCreate(NULL,
200 informCallback,
201 &context,
202 NULL);
203
204 /* Request a notification when/if the server dies */
205 port = CFMachPortGetPort(sessionPrivate->callbackPort);
206 status = mach_port_request_notification(mach_task_self(),
207 port,
208 MACH_NOTIFY_NO_SENDERS,
209 1,
210 port,
211 MACH_MSG_TYPE_MAKE_SEND_ONCE,
212 &oldNotify);
213 if (status != KERN_SUCCESS) {
214 SCDLog(LOG_DEBUG, CFSTR("mach_port_request_notification(): %s"), mach_error_string(status));
215 CFMachPortInvalidate(sessionPrivate->callbackPort);
216 CFRelease(sessionPrivate->callbackPort);
217 return SCD_FAILED;
218 }
219
220 #ifdef DEBUG
221 if (oldNotify != MACH_PORT_NULL) {
222 SCDLog(LOG_DEBUG, CFSTR("SCDNotifierInformViaCallback(): why is oldNotify != MACH_PORT_NULL?"));
223 }
224 #endif /* DEBUG */
225
226 /* Requesting notification via mach port */
227 status = notifyviaport(sessionPrivate->server,
228 port,
229 0,
230 (int *)&scd_status);
231
232 if (status != KERN_SUCCESS) {
233 if (status != MACH_SEND_INVALID_DEST)
234 SCDLog(LOG_DEBUG, CFSTR("notifyviaport(): %s"), mach_error_string(status));
235 CFMachPortInvalidate(sessionPrivate->callbackPort);
236 CFRelease(sessionPrivate->callbackPort);
237 (void) mach_port_destroy(mach_task_self(), sessionPrivate->server);
238 sessionPrivate->server = MACH_PORT_NULL;
239 return SCD_NOSERVER;
240 }
241
242 if (scd_status != SCD_OK) {
243 return scd_status;
244 }
245
246 /* set notifier active */
247 sessionPrivate->notifyStatus = Using_NotifierInformViaCallback;
248 sessionPrivate->callbackFunction = func;
249 sessionPrivate->callbackArgument = arg;
250
251 if (SCDOptionGet(session, kSCDOptionUseCFRunLoop)) {
252 /* Creating/adding a run loop source for the port */
253 sessionPrivate->callbackRunLoopSource =
254 CFMachPortCreateRunLoopSource(NULL, sessionPrivate->callbackPort, 0);
255 CFRunLoopAddSource(CFRunLoopGetCurrent(),
256 sessionPrivate->callbackRunLoopSource,
257 kCFRunLoopDefaultMode);
258 } else {
259 pthread_attr_t tattr;
260
261 SCDLog(LOG_DEBUG, CFSTR("Starting background thread to watch for notifications..."));
262 pthread_attr_init(&tattr);
263 pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM);
264 pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
265 pthread_attr_setstacksize(&tattr, 96 * 1024); // each thread gets a 96K stack
266 pthread_create(&sessionPrivate->callbackHelper,
267 &tattr,
268 watcherThread,
269 (void *)session);
270 pthread_attr_destroy(&tattr);
271 SCDLog(LOG_DEBUG, CFSTR(" thread id=0x%08x"), sessionPrivate->callbackHelper);
272 }
273
274 return SCD_OK;
275 }