2 * Copyright (c) 2000-2003 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
27 * Modification History
29 * June 1, 2001 Allan Nathanson <ajn@apple.com>
30 * - public API conversion
32 * March 24, 2000 Allan Nathanson <ajn@apple.com>
36 #include <servers/bootstrap.h>
40 #include "configd_server.h"
41 #include "notify_server.h"
45 /* MiG generated externals and functions */
46 extern struct rpc_subsystem _config_subsystem
;
47 extern boolean_t
config_server(mach_msg_header_t
*, mach_msg_header_t
*);
49 /* configd server port (for new session requests) */
50 static CFMachPortRef configd_port
= NULL
;
52 /* priviledged bootstrap port (for registering/unregistering w/mach_init) */
53 static mach_port_t priv_bootstrap_port
= MACH_PORT_NULL
;
57 config_demux(mach_msg_header_t
*request
, mach_msg_header_t
*reply
)
59 Boolean processed
= FALSE
;
60 serverSessionRef thisSession
;
61 mach_msg_format_0_trailer_t
*trailer
;
63 thisSession
= getSession(request
->msgh_local_port
);
66 * Get the caller's credentials (eUID/eGID) from the message trailer.
68 trailer
= (mach_msg_security_trailer_t
*)((vm_offset_t
)request
+
69 round_msg(request
->msgh_size
));
71 if ((trailer
->msgh_trailer_type
== MACH_MSG_TRAILER_FORMAT_0
) &&
72 (trailer
->msgh_trailer_size
>= MACH_MSG_TRAILER_FORMAT_0_SIZE
)) {
73 thisSession
->callerEUID
= trailer
->msgh_sender
.val
[0];
74 thisSession
->callerEGID
= trailer
->msgh_sender
.val
[1];
75 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("caller has eUID = %d, eGID = %d"),
76 thisSession
->callerEUID
,
77 thisSession
->callerEGID
);
79 static Boolean warned
= FALSE
;
82 SCLog(_configd_verbose
, LOG_WARNING
, CFSTR("caller's credentials not available."));
85 thisSession
->callerEUID
= 0;
86 thisSession
->callerEGID
= 0;
91 * (attemp to) process configd requests.
93 processed
= config_server(request
, reply
);
97 * (attempt to) process (NO MORE SENDERS) notification messages.
99 processed
= notify_server(request
, reply
);
103 SCLog(TRUE
, LOG_ERR
, CFSTR("unknown message ID (%d) received"), request
->msgh_id
);
105 reply
->msgh_bits
= MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(request
->msgh_bits
), 0);
106 reply
->msgh_remote_port
= request
->msgh_remote_port
;
107 reply
->msgh_size
= sizeof(mig_reply_error_t
); /* Minimal size */
108 reply
->msgh_local_port
= MACH_PORT_NULL
;
109 reply
->msgh_id
= request
->msgh_id
+ 100;
110 ((mig_reply_error_t
*)reply
)->NDR
= NDR_record
;
111 ((mig_reply_error_t
*)reply
)->RetCode
= MIG_BAD_ID
;
118 #define MACH_MSG_BUFFER_SIZE 128
123 configdCallback(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
125 mig_reply_error_t
* bufRequest
= msg
;
126 uint32_t bufReply_q
[MACH_MSG_BUFFER_SIZE
/sizeof(uint32_t)];
127 mig_reply_error_t
* bufReply
= (mig_reply_error_t
*)bufReply_q
;
128 mach_msg_return_t mr
;
131 if (_config_subsystem
.maxsize
> sizeof(bufReply_q
)) {
132 static Boolean warned
= FALSE
;
135 SCLog(_configd_verbose
, LOG_NOTICE
,
136 CFSTR("configdCallback(): buffer size should be increased > %d"),
137 _config_subsystem
.maxsize
);
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
)
211 mach_port_t bootstrap_port
;
213 kern_return_t status
;
215 service_name
= getenv("SCD_SERVER");
217 service_name
= SCD_SERVER
;
220 /* Getting bootstrap server port */
221 status
= task_get_bootstrap_port(mach_task_self(), &bootstrap_port
);
222 if (status
!= KERN_SUCCESS
) {
223 fprintf(stderr
, "task_get_bootstrap_port(): %s\n",
224 mach_error_string(status
));
225 exit (EX_UNAVAILABLE
);
228 /* Check "configd" server status */
229 status
= bootstrap_check_in(bootstrap_port
, service_name
, restart_service_port
);
231 case BOOTSTRAP_SUCCESS
:
232 /* if we are being restarted by mach_init */
233 priv_bootstrap_port
= bootstrap_port
;
235 case BOOTSTRAP_SERVICE_ACTIVE
:
236 case BOOTSTRAP_NOT_PRIVILEGED
:
237 /* if another instance of the server is active (or starting) */
238 fprintf(stderr
, "'%s' server already active\n",
241 case BOOTSTRAP_UNKNOWN_SERVICE
:
242 /* if the server is not currently registered/active */
243 *restart_service_port
= MACH_PORT_NULL
;
246 fprintf(stderr
, "bootstrap_check_in() failed: status=%d\n", status
);
247 exit (EX_UNAVAILABLE
);
256 server_init(mach_port_t restart_service_port
, Boolean enableRestart
)
258 mach_port_t bootstrap_port
;
259 CFRunLoopSourceRef rls
;
261 mach_port_t service_port
= restart_service_port
;
262 kern_return_t status
;
264 service_name
= getenv("SCD_SERVER");
266 service_name
= SCD_SERVER
;
269 /* Getting bootstrap server port */
270 status
= task_get_bootstrap_port(mach_task_self(), &bootstrap_port
);
271 if (status
!= KERN_SUCCESS
) {
272 SCLog(TRUE
, LOG_ERR
, CFSTR("task_get_bootstrap_port(): %s"), mach_error_string(status
));
273 exit (EX_UNAVAILABLE
);
276 if (service_port
== MACH_PORT_NULL
) {
277 mach_port_t service_send_port
;
279 /* Check "configd" server status */
280 status
= bootstrap_check_in(bootstrap_port
, service_name
, &service_port
);
282 case BOOTSTRAP_SUCCESS
:
283 /* if we are being restarted by mach_init */
284 priv_bootstrap_port
= bootstrap_port
;
286 case BOOTSTRAP_NOT_PRIVILEGED
:
287 /* if another instance of the server is starting */
288 SCLog(TRUE
, LOG_ERR
, CFSTR("'%s' server already starting"), service_name
);
289 exit (EX_UNAVAILABLE
);
290 case BOOTSTRAP_UNKNOWN_SERVICE
:
291 /* service not currently registered, "a good thing" (tm) */
293 status
= bootstrap_create_server(bootstrap_port
,
296 FALSE
, /* not onDemand == restart now */
297 &priv_bootstrap_port
);
298 if (status
!= BOOTSTRAP_SUCCESS
) {
299 SCLog(TRUE
, LOG_ERR
, CFSTR("bootstrap_create_server() failed: status=%d"), status
);
300 exit (EX_UNAVAILABLE
);
303 priv_bootstrap_port
= bootstrap_port
;
306 status
= bootstrap_create_service(priv_bootstrap_port
, service_name
, &service_send_port
);
307 if (status
!= BOOTSTRAP_SUCCESS
) {
308 SCLog(TRUE
, LOG_ERR
, CFSTR("bootstrap_create_service() failed: status=%d"), status
);
309 exit (EX_UNAVAILABLE
);
312 status
= bootstrap_check_in(priv_bootstrap_port
, service_name
, &service_port
);
313 if (status
!= BOOTSTRAP_SUCCESS
) {
314 SCLog(TRUE
, LOG_ERR
, CFSTR("bootstrap_check_in() failed: status=%d"), status
);
315 exit (EX_UNAVAILABLE
);
318 case BOOTSTRAP_SERVICE_ACTIVE
:
319 /* if another instance of the server is active */
320 SCLog(TRUE
, LOG_ERR
, CFSTR("'%s' server already active"), service_name
);
321 exit (EX_UNAVAILABLE
);
323 SCLog(TRUE
, LOG_ERR
, CFSTR("bootstrap_check_in() failed: status=%d"), status
);
324 exit (EX_UNAVAILABLE
);
329 /* we don't want to pass our priviledged bootstrap port along to any spawned helpers so... */
330 status
= bootstrap_unprivileged(priv_bootstrap_port
, &bootstrap_port
);
331 if (status
!= BOOTSTRAP_SUCCESS
) {
332 SCLog(TRUE
, LOG_ERR
, CFSTR("bootstrap_unprivileged() failed: status=%d"), status
);
333 exit (EX_UNAVAILABLE
);
336 status
= task_set_bootstrap_port(mach_task_self(), bootstrap_port
);
337 if (status
!= BOOTSTRAP_SUCCESS
) {
338 SCLog(TRUE
, LOG_ERR
, CFSTR("task_set_bootstrap_port(): %s"),
339 mach_error_string(status
));
340 exit (EX_UNAVAILABLE
);
343 /* Create the primary / new connection port */
344 configd_port
= CFMachPortCreateWithPort(NULL
, service_port
, configdCallback
, NULL
, NULL
);
347 * Create and add a run loop source for the port and add this source
348 * to both the default run loop mode and the "locked" mode. These two
349 * modes will be used for normal (unlocked) communication with the
350 * server and when multiple (locked) updates are requested by a single
353 rls
= CFMachPortCreateRunLoopSource(NULL
, configd_port
, 0);
354 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
355 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, CFSTR("locked"));
358 /* Create a session for the primary / new connection port */
359 (void) addSession(configd_port
);
370 mach_port_t service_port
;
371 kern_return_t status
;
374 * Note: we can't use SCLog() since the signal may be received while the
375 * logging thread lock is held.
377 if ((priv_bootstrap_port
== MACH_PORT_NULL
) || (configd_port
== NULL
)) {
381 service_name
= getenv("SCD_SERVER");
383 service_name
= SCD_SERVER
;
386 service_port
= CFMachPortGetPort(configd_port
);
387 if (service_port
!= MACH_PORT_NULL
) {
388 (void) mach_port_destroy(mach_task_self(), service_port
);
391 status
= bootstrap_register(priv_bootstrap_port
, service_name
, MACH_PORT_NULL
);
393 case BOOTSTRAP_SUCCESS
:
395 case MACH_SEND_INVALID_DEST
:
396 case MIG_SERVER_DIED
:
397 /* something happened to mach_init */
400 if (_configd_verbose
) {
401 syslog (LOG_ERR
, "bootstrap_register() failed: status=%d" , status
);
403 fprintf(stderr
, "bootstrap_register() failed: status=%d\n", status
);
406 exit (EX_UNAVAILABLE
);
422 * if linked with a DEBUG version of the framework, display some
423 * debugging information
425 __showMachPortStatus();
428 * process one run loop event
430 rlMode
= (storeLocked
> 0) ? CFSTR("locked") : kCFRunLoopDefaultMode
;
431 rlStatus
= CFRunLoopRunInMode(rlMode
, 1.0e10
, TRUE
);
434 * check for, and if necessary, push out change notifications
435 * to other processes.