2 * Copyright (c) 2000-2003 Apple Computer, 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 * June 1, 2001 Allan Nathanson <ajn@apple.com>
28 * - public API conversion
30 * March 24, 2000 Allan Nathanson <ajn@apple.com>
34 #include <servers/bootstrap.h>
38 #include "configd_server.h"
39 #include "notify_server.h"
43 /* MiG generated externals and functions */
44 extern struct rpc_subsystem _config_subsystem
;
45 extern boolean_t
config_server(mach_msg_header_t
*, mach_msg_header_t
*);
47 /* configd server port (for new session requests) */
48 static CFMachPortRef configd_port
= NULL
;
50 /* priviledged bootstrap port (for registering/unregistering w/mach_init) */
51 static mach_port_t priv_bootstrap_port
= MACH_PORT_NULL
;
55 config_demux(mach_msg_header_t
*request
, mach_msg_header_t
*reply
)
57 Boolean processed
= FALSE
;
58 serverSessionRef thisSession
;
59 mach_msg_format_0_trailer_t
*trailer
;
61 thisSession
= getSession(request
->msgh_local_port
);
64 * Get the caller's credentials (eUID/eGID) from the message trailer.
66 trailer
= (mach_msg_security_trailer_t
*)((vm_offset_t
)request
+
67 round_msg(request
->msgh_size
));
69 if ((trailer
->msgh_trailer_type
== MACH_MSG_TRAILER_FORMAT_0
) &&
70 (trailer
->msgh_trailer_size
>= MACH_MSG_TRAILER_FORMAT_0_SIZE
)) {
71 thisSession
->callerEUID
= trailer
->msgh_sender
.val
[0];
72 thisSession
->callerEGID
= trailer
->msgh_sender
.val
[1];
73 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("caller has eUID = %d, eGID = %d"),
74 thisSession
->callerEUID
,
75 thisSession
->callerEGID
);
77 static Boolean warned
= FALSE
;
80 SCLog(_configd_verbose
, LOG_WARNING
, CFSTR("caller's credentials not available."));
83 thisSession
->callerEUID
= 0;
84 thisSession
->callerEGID
= 0;
89 * (attemp to) process configd requests.
91 processed
= config_server(request
, reply
);
95 * (attempt to) process (NO MORE SENDERS) notification messages.
97 processed
= notify_server(request
, reply
);
101 SCLog(TRUE
, LOG_ERR
, CFSTR("unknown message ID (%d) received"), request
->msgh_id
);
103 reply
->msgh_bits
= MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(request
->msgh_bits
), 0);
104 reply
->msgh_remote_port
= request
->msgh_remote_port
;
105 reply
->msgh_size
= sizeof(mig_reply_error_t
); /* Minimal size */
106 reply
->msgh_local_port
= MACH_PORT_NULL
;
107 reply
->msgh_id
= request
->msgh_id
+ 100;
108 ((mig_reply_error_t
*)reply
)->NDR
= NDR_record
;
109 ((mig_reply_error_t
*)reply
)->RetCode
= MIG_BAD_ID
;
116 #define MACH_MSG_BUFFER_SIZE 128
121 configdCallback(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
123 mig_reply_error_t
* bufRequest
= msg
;
124 uint32_t bufReply_q
[MACH_MSG_BUFFER_SIZE
/sizeof(uint32_t)];
125 mig_reply_error_t
* bufReply
= (mig_reply_error_t
*)bufReply_q
;
126 mach_msg_return_t mr
;
129 if (_config_subsystem
.maxsize
> sizeof(bufReply_q
)) {
130 static Boolean warned
= FALSE
;
133 SCLog(_configd_verbose
, LOG_NOTICE
,
134 CFSTR("configdCallback(): buffer size should be increased > %d"),
135 _config_subsystem
.maxsize
);
138 bufReply
= CFAllocatorAllocate(NULL
, _config_subsystem
.maxsize
, 0);
141 /* we have a request message */
142 (void) config_demux(&bufRequest
->Head
, &bufReply
->Head
);
144 if (!(bufReply
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
)) {
145 if (bufReply
->RetCode
== MIG_NO_REPLY
) {
146 bufReply
->Head
.msgh_remote_port
= MACH_PORT_NULL
;
147 } else if ((bufReply
->RetCode
!= KERN_SUCCESS
) &&
148 (bufRequest
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
)) {
150 * destroy the request - but not the reply port
152 bufRequest
->Head
.msgh_remote_port
= MACH_PORT_NULL
;
153 mach_msg_destroy(&bufRequest
->Head
);
157 if (bufReply
->Head
.msgh_remote_port
!= MACH_PORT_NULL
) {
161 * We don't want to block indefinitely because the client
162 * isn't receiving messages from the reply port.
163 * If we have a send-once right for the reply port, then
164 * this isn't a concern because the send won't block.
165 * If we have a send right, we need to use MACH_SEND_TIMEOUT.
166 * To avoid falling off the kernel's fast RPC path unnecessarily,
167 * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
170 options
= MACH_SEND_MSG
;
171 if (MACH_MSGH_BITS_REMOTE(bufReply
->Head
.msgh_bits
) != MACH_MSG_TYPE_MOVE_SEND_ONCE
) {
172 options
|= MACH_SEND_TIMEOUT
;
174 mr
= mach_msg(&bufReply
->Head
, /* msg */
175 options
, /* option */
176 bufReply
->Head
.msgh_size
, /* send_size */
178 MACH_PORT_NULL
, /* rcv_name */
179 MACH_MSG_TIMEOUT_NONE
, /* timeout */
180 MACH_PORT_NULL
); /* notify */
182 /* Has a message error occurred? */
184 case MACH_SEND_INVALID_DEST
:
185 case MACH_SEND_TIMED_OUT
:
188 /* Includes success case. */
193 if (bufReply
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
) {
194 mach_msg_destroy(&bufReply
->Head
);
199 if (bufReply
!= (mig_reply_error_t
*)bufReply_q
)
200 CFAllocatorDeallocate(NULL
, bufReply
);
207 server_active(mach_port_t
*restart_service_port
)
209 mach_port_t bootstrap_port
;
211 kern_return_t status
;
213 service_name
= getenv("SCD_SERVER");
215 service_name
= SCD_SERVER
;
218 /* Getting bootstrap server port */
219 status
= task_get_bootstrap_port(mach_task_self(), &bootstrap_port
);
220 if (status
!= KERN_SUCCESS
) {
221 fprintf(stderr
, "task_get_bootstrap_port(): %s\n",
222 mach_error_string(status
));
223 exit (EX_UNAVAILABLE
);
226 /* Check "configd" server status */
227 status
= bootstrap_check_in(bootstrap_port
, service_name
, restart_service_port
);
229 case BOOTSTRAP_SUCCESS
:
230 /* if we are being restarted by mach_init */
231 priv_bootstrap_port
= bootstrap_port
;
233 case BOOTSTRAP_SERVICE_ACTIVE
:
234 case BOOTSTRAP_NOT_PRIVILEGED
:
235 /* if another instance of the server is active (or starting) */
236 fprintf(stderr
, "'%s' server already active\n",
239 case BOOTSTRAP_UNKNOWN_SERVICE
:
240 /* if the server is not currently registered/active */
241 *restart_service_port
= MACH_PORT_NULL
;
244 fprintf(stderr
, "bootstrap_check_in() failed: status=%d\n", status
);
245 exit (EX_UNAVAILABLE
);
254 server_init(mach_port_t restart_service_port
, Boolean enableRestart
)
256 mach_port_t bootstrap_port
;
257 CFRunLoopSourceRef rls
;
259 mach_port_t service_port
= restart_service_port
;
260 kern_return_t status
;
262 service_name
= getenv("SCD_SERVER");
264 service_name
= SCD_SERVER
;
267 /* Getting bootstrap server port */
268 status
= task_get_bootstrap_port(mach_task_self(), &bootstrap_port
);
269 if (status
!= KERN_SUCCESS
) {
270 SCLog(TRUE
, LOG_ERR
, CFSTR("task_get_bootstrap_port(): %s"), mach_error_string(status
));
271 exit (EX_UNAVAILABLE
);
274 if (service_port
== MACH_PORT_NULL
) {
275 mach_port_t service_send_port
;
277 /* Check "configd" server status */
278 status
= bootstrap_check_in(bootstrap_port
, service_name
, &service_port
);
280 case BOOTSTRAP_SUCCESS
:
281 /* if we are being restarted by mach_init */
282 priv_bootstrap_port
= bootstrap_port
;
284 case BOOTSTRAP_NOT_PRIVILEGED
:
285 /* if another instance of the server is starting */
286 SCLog(TRUE
, LOG_ERR
, CFSTR("'%s' server already starting"), service_name
);
287 exit (EX_UNAVAILABLE
);
288 case BOOTSTRAP_UNKNOWN_SERVICE
:
289 /* service not currently registered, "a good thing" (tm) */
291 status
= bootstrap_create_server(bootstrap_port
,
294 FALSE
, /* not onDemand == restart now */
295 &priv_bootstrap_port
);
296 if (status
!= BOOTSTRAP_SUCCESS
) {
297 SCLog(TRUE
, LOG_ERR
, CFSTR("bootstrap_create_server() failed: status=%d"), status
);
298 exit (EX_UNAVAILABLE
);
301 priv_bootstrap_port
= bootstrap_port
;
304 status
= bootstrap_create_service(priv_bootstrap_port
, service_name
, &service_send_port
);
305 if (status
!= BOOTSTRAP_SUCCESS
) {
306 SCLog(TRUE
, LOG_ERR
, CFSTR("bootstrap_create_service() failed: status=%d"), status
);
307 exit (EX_UNAVAILABLE
);
310 status
= bootstrap_check_in(priv_bootstrap_port
, service_name
, &service_port
);
311 if (status
!= BOOTSTRAP_SUCCESS
) {
312 SCLog(TRUE
, LOG_ERR
, CFSTR("bootstrap_check_in() failed: status=%d"), status
);
313 exit (EX_UNAVAILABLE
);
316 case BOOTSTRAP_SERVICE_ACTIVE
:
317 /* if another instance of the server is active */
318 SCLog(TRUE
, LOG_ERR
, CFSTR("'%s' server already active"), service_name
);
319 exit (EX_UNAVAILABLE
);
321 SCLog(TRUE
, LOG_ERR
, CFSTR("bootstrap_check_in() failed: status=%d"), status
);
322 exit (EX_UNAVAILABLE
);
327 /* we don't want to pass our priviledged bootstrap port along to any spawned helpers so... */
328 status
= bootstrap_unprivileged(priv_bootstrap_port
, &bootstrap_port
);
329 if (status
!= BOOTSTRAP_SUCCESS
) {
330 SCLog(TRUE
, LOG_ERR
, CFSTR("bootstrap_unprivileged() failed: status=%d"), status
);
331 exit (EX_UNAVAILABLE
);
334 status
= task_set_bootstrap_port(mach_task_self(), bootstrap_port
);
335 if (status
!= BOOTSTRAP_SUCCESS
) {
336 SCLog(TRUE
, LOG_ERR
, CFSTR("task_set_bootstrap_port(): %s"),
337 mach_error_string(status
));
338 exit (EX_UNAVAILABLE
);
341 /* Create the primary / new connection port */
342 configd_port
= CFMachPortCreateWithPort(NULL
, service_port
, configdCallback
, NULL
, NULL
);
345 * Create and add a run loop source for the port and add this source
346 * to both the default run loop mode and the "locked" mode. These two
347 * modes will be used for normal (unlocked) communication with the
348 * server and when multiple (locked) updates are requested by a single
351 rls
= CFMachPortCreateRunLoopSource(NULL
, configd_port
, 0);
352 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
353 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, CFSTR("locked"));
356 /* Create a session for the primary / new connection port */
357 (void) addSession(configd_port
);
368 mach_port_t service_port
;
369 kern_return_t status
;
372 * Note: we can't use SCLog() since the signal may be received while the
373 * logging thread lock is held.
375 if ((priv_bootstrap_port
== MACH_PORT_NULL
) || (configd_port
== NULL
)) {
379 service_name
= getenv("SCD_SERVER");
381 service_name
= SCD_SERVER
;
384 service_port
= CFMachPortGetPort(configd_port
);
385 if (service_port
!= MACH_PORT_NULL
) {
386 (void) mach_port_destroy(mach_task_self(), service_port
);
389 status
= bootstrap_register(priv_bootstrap_port
, service_name
, MACH_PORT_NULL
);
391 case BOOTSTRAP_SUCCESS
:
393 case MACH_SEND_INVALID_DEST
:
394 case MIG_SERVER_DIED
:
395 /* something happened to mach_init */
398 if (_configd_verbose
) {
399 syslog (LOG_ERR
, "bootstrap_register() failed: status=%d" , status
);
401 fprintf(stderr
, "bootstrap_register() failed: status=%d\n", status
);
404 exit (EX_UNAVAILABLE
);
420 * if linked with a DEBUG version of the framework, display some
421 * debugging information
423 __showMachPortStatus();
426 * process one run loop event
428 rlMode
= (storeLocked
> 0) ? CFSTR("locked") : kCFRunLoopDefaultMode
;
429 rlStatus
= CFRunLoopRunInMode(rlMode
, 1.0e10
, TRUE
);
432 * check for, and if necessary, push out change notifications
433 * to other processes.