2 * Copyright (c) 2000-2007 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * Modification History
27 * March 9, 2004 Allan Nathanson <ajn@apple.com>
28 * - add DNS configuration server
30 * June 1, 2001 Allan Nathanson <ajn@apple.com>
31 * - public API conversion
33 * March 24, 2000 Allan Nathanson <ajn@apple.com>
39 #include <sys/types.h>
40 #include <servers/bootstrap.h>
43 #include "configd_server.h"
44 #include "notify_server.h"
47 /* MiG generated externals and functions */
48 extern struct mig_subsystem _config_subsystem
;
49 extern boolean_t
config_server(mach_msg_header_t
*, mach_msg_header_t
*);
51 #include "shared_dns_info_types.h"
52 #include "dnsinfo_server.h"
54 /* MiG generated externals and functions */
55 extern struct mig_subsystem _shared_dns_info_subsystem
;
56 extern boolean_t
shared_dns_info_server(mach_msg_header_t
*, mach_msg_header_t
*);
58 /* configd server port (for new session requests) */
59 static CFMachPortRef configd_port
= NULL
;
61 /* priviledged bootstrap port (for registering/unregistering w/launchd) */
62 static mach_port_t priv_bootstrap_port
= MACH_PORT_NULL
;
66 config_demux(mach_msg_header_t
*request
, mach_msg_header_t
*reply
)
68 Boolean processed
= FALSE
;
71 * (attempt to) process SCDynamicStore requests.
73 processed
= config_server(request
, reply
);
79 * (attempt to) process DNS configuration requests.
81 processed
= shared_dns_info_server(request
, reply
);
87 * (attempt to) process (NO MORE SENDERS) notification messages.
89 processed
= notify_server(request
, reply
);
95 * unknown message ID, log and return an error.
97 SCLog(TRUE
, LOG_ERR
, CFSTR("config_demux(): unknown message ID (%d) received"), request
->msgh_id
);
98 reply
->msgh_bits
= MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(request
->msgh_bits
), 0);
99 reply
->msgh_remote_port
= request
->msgh_remote_port
;
100 reply
->msgh_size
= sizeof(mig_reply_error_t
); /* Minimal size */
101 reply
->msgh_local_port
= MACH_PORT_NULL
;
102 reply
->msgh_id
= request
->msgh_id
+ 100;
103 ((mig_reply_error_t
*)reply
)->NDR
= NDR_record
;
104 ((mig_reply_error_t
*)reply
)->RetCode
= MIG_BAD_ID
;
110 #define MACH_MSG_BUFFER_SIZE 128
115 configdCallback(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
117 mig_reply_error_t
* bufRequest
= msg
;
118 uint32_t bufReply_q
[MACH_MSG_BUFFER_SIZE
/sizeof(uint32_t)];
119 mig_reply_error_t
* bufReply
= (mig_reply_error_t
*)bufReply_q
;
120 static CFIndex bufSize
= 0;
121 mach_msg_return_t mr
;
125 // get max size for MiG reply buffers
126 bufSize
= _config_subsystem
.maxsize
;
127 if (_shared_dns_info_subsystem
.maxsize
> bufSize
) {
128 bufSize
= _shared_dns_info_subsystem
.maxsize
;
131 // check if our on-the-stack reply buffer will be big enough
132 if (bufSize
> sizeof(bufReply_q
)) {
133 SCLog(TRUE
, LOG_NOTICE
,
134 CFSTR("configdCallback(): buffer size should be increased > %d"),
135 _config_subsystem
.maxsize
);
139 if (bufSize
> sizeof(bufReply_q
)) {
140 bufReply
= CFAllocatorAllocate(NULL
, _config_subsystem
.maxsize
, 0);
143 /* we have a request message */
144 (void) config_demux(&bufRequest
->Head
, &bufReply
->Head
);
146 if (!(bufReply
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
)) {
147 if (bufReply
->RetCode
== MIG_NO_REPLY
) {
148 bufReply
->Head
.msgh_remote_port
= MACH_PORT_NULL
;
149 } else if ((bufReply
->RetCode
!= KERN_SUCCESS
) &&
150 (bufRequest
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
)) {
152 * destroy the request - but not the reply port
154 bufRequest
->Head
.msgh_remote_port
= MACH_PORT_NULL
;
155 mach_msg_destroy(&bufRequest
->Head
);
159 if (bufReply
->Head
.msgh_remote_port
!= MACH_PORT_NULL
) {
163 * We don't want to block indefinitely because the client
164 * isn't receiving messages from the reply port.
165 * If we have a send-once right for the reply port, then
166 * this isn't a concern because the send won't block.
167 * If we have a send right, we need to use MACH_SEND_TIMEOUT.
168 * To avoid falling off the kernel's fast RPC path unnecessarily,
169 * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
172 options
= MACH_SEND_MSG
;
173 if (MACH_MSGH_BITS_REMOTE(bufReply
->Head
.msgh_bits
) != MACH_MSG_TYPE_MOVE_SEND_ONCE
) {
174 options
|= MACH_SEND_TIMEOUT
;
176 mr
= mach_msg(&bufReply
->Head
, /* msg */
177 options
, /* option */
178 bufReply
->Head
.msgh_size
, /* send_size */
180 MACH_PORT_NULL
, /* rcv_name */
181 MACH_MSG_TIMEOUT_NONE
, /* timeout */
182 MACH_PORT_NULL
); /* notify */
184 /* Has a message error occurred? */
186 case MACH_SEND_INVALID_DEST
:
187 case MACH_SEND_TIMED_OUT
:
190 /* Includes success case. */
195 if (bufReply
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
) {
196 mach_msg_destroy(&bufReply
->Head
);
201 if (bufReply
!= (mig_reply_error_t
*)bufReply_q
)
202 CFAllocatorDeallocate(NULL
, bufReply
);
209 server_active(mach_port_t
*restart_service_port
)
212 kern_return_t status
;
214 service_name
= getenv("SCD_SERVER");
216 service_name
= SCD_SERVER
;
219 /* Check "configd" server status */
220 status
= bootstrap_check_in(bootstrap_port
, service_name
, restart_service_port
);
222 case BOOTSTRAP_SUCCESS
:
223 /* if we are being restarted by launchd */
224 priv_bootstrap_port
= bootstrap_port
;
226 case BOOTSTRAP_SERVICE_ACTIVE
:
227 case BOOTSTRAP_NOT_PRIVILEGED
:
228 /* if another instance of the server is active (or starting) */
229 fprintf(stderr
, "'%s' server already active\n",
232 case BOOTSTRAP_UNKNOWN_SERVICE
:
233 /* if the server is not currently registered/active */
234 *restart_service_port
= MACH_PORT_NULL
;
238 "bootstrap_check_in() failed: %s\n",
239 bootstrap_strerror(status
));
240 exit (EX_UNAVAILABLE
);
248 serverMPCopyDescription(const void *info
)
250 return CFStringCreateWithFormat(NULL
, NULL
, CFSTR("<main DynamicStore MP>"));
256 server_init(mach_port_t restart_service_port
,
257 Boolean enableRestart
)
259 CFMachPortContext context
= { 0
263 , serverMPCopyDescription
266 CFRunLoopSourceRef rls
;
268 mach_port_t service_port
= restart_service_port
;
269 kern_return_t status
;
270 mach_port_t unpriv_bootstrap_port
;
272 service_name
= getenv("SCD_SERVER");
274 service_name
= SCD_SERVER
;
277 if (service_port
== MACH_PORT_NULL
) {
278 mach_port_t service_send_port
;
280 /* Check "configd" server status */
281 status
= bootstrap_check_in(bootstrap_port
, service_name
, &service_port
);
283 case BOOTSTRAP_SUCCESS
:
284 /* if we are being restarted by launchd */
285 priv_bootstrap_port
= bootstrap_port
;
287 case BOOTSTRAP_NOT_PRIVILEGED
:
288 /* if another instance of the server is starting */
289 SCLog(TRUE
, LOG_ERR
, CFSTR("'%s' server already starting"), service_name
);
290 exit (EX_UNAVAILABLE
);
291 case BOOTSTRAP_UNKNOWN_SERVICE
:
292 /* service not currently registered, "a good thing" (tm) */
294 status
= bootstrap_create_server(bootstrap_port
,
297 FALSE
, /* not onDemand == restart now */
298 &priv_bootstrap_port
);
299 if (status
!= BOOTSTRAP_SUCCESS
) {
301 CFSTR("server_init bootstrap_create_server() failed: %s"),
302 bootstrap_strerror(status
));
303 exit (EX_UNAVAILABLE
);
306 priv_bootstrap_port
= bootstrap_port
;
309 status
= bootstrap_create_service(priv_bootstrap_port
, service_name
, &service_send_port
);
310 if (status
!= BOOTSTRAP_SUCCESS
) {
312 CFSTR("server_init bootstrap_create_service() failed: %s"),
313 bootstrap_strerror(status
));
314 exit (EX_UNAVAILABLE
);
317 status
= bootstrap_check_in(priv_bootstrap_port
, service_name
, &service_port
);
318 if (status
!= BOOTSTRAP_SUCCESS
) {
320 CFSTR("server_init bootstrap_check_in() failed: %s"),
321 bootstrap_strerror(status
));
322 exit (EX_UNAVAILABLE
);
325 case BOOTSTRAP_SERVICE_ACTIVE
:
326 /* if another instance of the server is active */
327 SCLog(TRUE
, LOG_ERR
, CFSTR("'%s' server already active"), service_name
);
328 exit (EX_UNAVAILABLE
);
331 CFSTR("server_init bootstrap_check_in() failed: %s"),
332 bootstrap_strerror(status
));
333 exit (EX_UNAVAILABLE
);
338 /* we don't want to pass our priviledged bootstrap port along to any spawned helpers so... */
339 status
= bootstrap_unprivileged(priv_bootstrap_port
, &unpriv_bootstrap_port
);
340 if (status
!= BOOTSTRAP_SUCCESS
) {
342 CFSTR("server_init bootstrap_unprivileged() failed: %s"),
343 bootstrap_strerror(status
));
344 exit (EX_UNAVAILABLE
);
347 status
= task_set_bootstrap_port(mach_task_self(), unpriv_bootstrap_port
);
348 if (status
!= BOOTSTRAP_SUCCESS
) {
350 CFSTR("server_init task_set_bootstrap_port(): %s"),
351 mach_error_string(status
));
352 exit (EX_UNAVAILABLE
);
355 /* ... and make sure that the global "bootstrap_port" is also unpriviledged */
356 bootstrap_port
= unpriv_bootstrap_port
;
358 /* Create the primary / new connection port */
359 configd_port
= CFMachPortCreateWithPort(NULL
, service_port
, configdCallback
, &context
, NULL
);
362 * Create and add a run loop source for the port and add this source
363 * to both the default run loop mode and the "locked" mode. These two
364 * modes will be used for normal (unlocked) communication with the
365 * server and when multiple (locked) updates are requested by a single
368 rls
= CFMachPortCreateRunLoopSource(NULL
, configd_port
, 0);
369 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
370 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, CFSTR("locked"));
373 /* Create a session for the primary / new connection port */
374 (void) addSession(configd_port
);
385 mach_port_t service_port
;
386 kern_return_t status
;
389 * Note: we can't use SCLog() since the signal may be received while the
390 * logging thread lock is held.
392 if ((priv_bootstrap_port
== MACH_PORT_NULL
) || (configd_port
== NULL
)) {
396 service_name
= getenv("SCD_SERVER");
398 service_name
= SCD_SERVER
;
401 service_port
= CFMachPortGetPort(configd_port
);
402 if (service_port
!= MACH_PORT_NULL
) {
403 (void) mach_port_destroy(mach_task_self(), service_port
);
406 status
= bootstrap_register(priv_bootstrap_port
, service_name
, MACH_PORT_NULL
);
408 case BOOTSTRAP_SUCCESS
:
410 case MACH_SEND_INVALID_DEST
:
411 case MIG_SERVER_DIED
:
412 /* something happened to launchd */
416 CFSTR("server_shutdown bootstrap_register(): %s"),
417 bootstrap_strerror(status
));
418 return EX_UNAVAILABLE
;
434 * if linked with a DEBUG version of the framework, display some
435 * debugging information
437 __showMachPortStatus();
440 * process one run loop event
442 rlMode
= (storeLocked
> 0) ? CFSTR("locked") : kCFRunLoopDefaultMode
;
443 rlStatus
= CFRunLoopRunInMode(rlMode
, 1.0e10
, TRUE
);
446 * check for, and if necessary, push out change notifications
447 * to other processes.