2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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.
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
20 * @APPLE_LICENSE_HEADER_END@
23 #include <mach/mach.h>
24 #include <mach/mach_error.h>
26 #include <SystemConfiguration/SCD.h>
27 #include "config.h" /* MiG generated file */
28 #include "SCDPrivate.h"
32 informCallback(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
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
;
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"));
48 SCDLog(LOG_DEBUG
, CFSTR(" executing notifiction function"));
49 if ((*cbFunc
)(session
, cbArg
)) {
51 * callback function returned success.
55 SCDLog(LOG_DEBUG
, CFSTR(" callback returned error, disabling notifier"));
60 if (port
!= sessionPrivate
->callbackPort
) {
61 SCDLog(LOG_DEBUG
, CFSTR("informCallback, why is port != callbackPort?"));
65 /* we have encountered some type of error, disable additional callbacks */
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
);
74 CFMachPortInvalidate(port
);
77 sessionPrivate
->notifyStatus
= NotifierNotRegistered
;
78 sessionPrivate
->callbackFunction
= NULL
;
79 sessionPrivate
->callbackArgument
= NULL
;
80 sessionPrivate
->callbackPort
= NULL
;
81 sessionPrivate
->callbackRunLoopSource
= NULL
; /* XXX */
88 cleanupMachPort(void *ptr
)
90 mach_port_t
*port
= (mach_port_t
*)ptr
;
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
);
103 watcherThread(void *arg
)
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
));
111 *port
= CFMachPortGetPort(sessionPrivate
->callbackPort
);
112 pthread_cleanup_push(cleanupMachPort
, (void *)port
);
117 SCDLog(LOG_DEBUG
, CFSTR("Callback thread waiting, port=%d, tid=0x%08x"),
118 *port
, pthread_self());
120 msgid
= _waitForMachMessage(*port
);
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"));
131 /* an error was detected, disable additional callbacks */
132 SCDLog(LOG_DEBUG
, CFSTR(" server failure, disabling notifier"));
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
;
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"));
150 SCDLog(LOG_DEBUG
, CFSTR(" executing notifiction function"));
152 if (!(*cbFunc
)(session
, cbArg
)) {
154 * callback function returned an error, exit the thread
162 * pop the cleanup routine for the "port" mach port. We end up calling
163 * mach_port_destroy() in the process.
165 pthread_cleanup_pop(1);
173 SCDNotifierInformViaCallback(SCDSessionRef session
, SCDCallbackRoutine_t func
, void *arg
)
175 SCDSessionPrivateRef sessionPrivate
= (SCDSessionPrivateRef
)session
;
176 kern_return_t status
;
178 mach_port_t oldNotify
;
179 SCDStatus scd_status
;
180 CFMachPortContext context
= { 0, (void *)session
, NULL
, NULL
, NULL
};
182 SCDLog(LOG_DEBUG
, CFSTR("SCDNotifierInformViaCallback:"));
184 if ((session
== NULL
) || (sessionPrivate
->server
== MACH_PORT_NULL
)) {
185 return SCD_NOSESSION
; /* you must have an open session to play */
188 if (sessionPrivate
->notifyStatus
!= NotifierNotRegistered
) {
189 /* sorry, you can only have one notification registered at once */
190 return SCD_NOTIFIERACTIVE
;
194 /* sorry, you must specify a callback function */
195 return SCD_INVALIDARGUMENT
;
198 /* Allocating port (for server response) */
199 sessionPrivate
->callbackPort
= CFMachPortCreate(NULL
,
204 /* Request a notification when/if the server dies */
205 port
= CFMachPortGetPort(sessionPrivate
->callbackPort
);
206 status
= mach_port_request_notification(mach_task_self(),
208 MACH_NOTIFY_NO_SENDERS
,
211 MACH_MSG_TYPE_MAKE_SEND_ONCE
,
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
);
221 if (oldNotify
!= MACH_PORT_NULL
) {
222 SCDLog(LOG_DEBUG
, CFSTR("SCDNotifierInformViaCallback(): why is oldNotify != MACH_PORT_NULL?"));
226 /* Requesting notification via mach port */
227 status
= notifyviaport(sessionPrivate
->server
,
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
;
242 if (scd_status
!= SCD_OK
) {
246 /* set notifier active */
247 sessionPrivate
->notifyStatus
= Using_NotifierInformViaCallback
;
248 sessionPrivate
->callbackFunction
= func
;
249 sessionPrivate
->callbackArgument
= arg
;
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
);
259 pthread_attr_t tattr
;
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
,
270 pthread_attr_destroy(&tattr
);
271 SCDLog(LOG_DEBUG
, CFSTR(" thread id=0x%08x"), sessionPrivate
->callbackHelper
);