]> git.saurik.com Git - apple/configd.git/blob - configd.tproj/configd_server.c
configd-84.tar.gz
[apple/configd.git] / configd.tproj / configd_server.c
1 /*
2 * Copyright (c) 2000-2003 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
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
13 * file.
14 *
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.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25
26 /*
27 * Modification History
28 *
29 * June 1, 2001 Allan Nathanson <ajn@apple.com>
30 * - public API conversion
31 *
32 * March 24, 2000 Allan Nathanson <ajn@apple.com>
33 * - initial revision
34 */
35
36 #include <servers/bootstrap.h>
37 #include <sysexits.h>
38
39 #include "configd.h"
40 #include "configd_server.h"
41 #include "notify_server.h"
42 #include "session.h"
43 #include "notify.h"
44
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 *);
48
49 /* configd server port (for new session requests) */
50 static CFMachPortRef configd_port = NULL;
51
52 /* priviledged bootstrap port (for registering/unregistering w/mach_init) */
53 static mach_port_t priv_bootstrap_port = MACH_PORT_NULL;
54
55 __private_extern__
56 boolean_t
57 config_demux(mach_msg_header_t *request, mach_msg_header_t *reply)
58 {
59 Boolean processed = FALSE;
60 serverSessionRef thisSession;
61 mach_msg_format_0_trailer_t *trailer;
62
63 thisSession = getSession(request->msgh_local_port);
64 if (thisSession) {
65 /*
66 * Get the caller's credentials (eUID/eGID) from the message trailer.
67 */
68 trailer = (mach_msg_security_trailer_t *)((vm_offset_t)request +
69 round_msg(request->msgh_size));
70
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);
78 } else {
79 static Boolean warned = FALSE;
80
81 if (!warned) {
82 SCLog(_configd_verbose, LOG_WARNING, CFSTR("caller's credentials not available."));
83 warned = TRUE;
84 }
85 thisSession->callerEUID = 0;
86 thisSession->callerEGID = 0;
87 }
88 }
89
90 /*
91 * (attemp to) process configd requests.
92 */
93 processed = config_server(request, reply);
94
95 if (!processed) {
96 /*
97 * (attempt to) process (NO MORE SENDERS) notification messages.
98 */
99 processed = notify_server(request, reply);
100 }
101
102 if (!processed) {
103 SCLog(TRUE, LOG_ERR, CFSTR("unknown message ID (%d) received"), request->msgh_id);
104
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;
112 }
113
114 return processed;
115 }
116
117
118 #define MACH_MSG_BUFFER_SIZE 128
119
120
121 __private_extern__
122 void
123 configdCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
124 {
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;
129 int options;
130
131 if (_config_subsystem.maxsize > sizeof(bufReply_q)) {
132 static Boolean warned = FALSE;
133
134 if (!warned) {
135 SCLog(_configd_verbose, LOG_NOTICE,
136 CFSTR("configdCallback(): buffer size should be increased > %d"),
137 _config_subsystem.maxsize);
138 warned = TRUE;
139 }
140 bufReply = CFAllocatorAllocate(NULL, _config_subsystem.maxsize, 0);
141 }
142
143 /* we have a request message */
144 (void) config_demux(&bufRequest->Head, &bufReply->Head);
145
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)) {
151 /*
152 * destroy the request - but not the reply port
153 */
154 bufRequest->Head.msgh_remote_port = MACH_PORT_NULL;
155 mach_msg_destroy(&bufRequest->Head);
156 }
157 }
158
159 if (bufReply->Head.msgh_remote_port != MACH_PORT_NULL) {
160 /*
161 * send reply.
162 *
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.
170 */
171
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;
175 }
176 mr = mach_msg(&bufReply->Head, /* msg */
177 options, /* option */
178 bufReply->Head.msgh_size, /* send_size */
179 0, /* rcv_size */
180 MACH_PORT_NULL, /* rcv_name */
181 MACH_MSG_TIMEOUT_NONE, /* timeout */
182 MACH_PORT_NULL); /* notify */
183
184 /* Has a message error occurred? */
185 switch (mr) {
186 case MACH_SEND_INVALID_DEST:
187 case MACH_SEND_TIMED_OUT:
188 break;
189 default :
190 /* Includes success case. */
191 goto done;
192 }
193 }
194
195 if (bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) {
196 mach_msg_destroy(&bufReply->Head);
197 }
198
199 done :
200
201 if (bufReply != (mig_reply_error_t *)bufReply_q)
202 CFAllocatorDeallocate(NULL, bufReply);
203 return;
204 }
205
206
207 __private_extern__
208 boolean_t
209 server_active(mach_port_t *restart_service_port)
210 {
211 mach_port_t bootstrap_port;
212 char *service_name;
213 kern_return_t status;
214
215 service_name = getenv("SCD_SERVER");
216 if (!service_name) {
217 service_name = SCD_SERVER;
218 }
219
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);
226 }
227
228 /* Check "configd" server status */
229 status = bootstrap_check_in(bootstrap_port, service_name, restart_service_port);
230 switch (status) {
231 case BOOTSTRAP_SUCCESS :
232 /* if we are being restarted by mach_init */
233 priv_bootstrap_port = bootstrap_port;
234 break;
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",
239 service_name);
240 return TRUE;
241 case BOOTSTRAP_UNKNOWN_SERVICE :
242 /* if the server is not currently registered/active */
243 *restart_service_port = MACH_PORT_NULL;
244 break;
245 default :
246 fprintf(stderr, "bootstrap_check_in() failed: status=%d\n", status);
247 exit (EX_UNAVAILABLE);
248 }
249
250 return FALSE;
251 }
252
253
254 __private_extern__
255 void
256 server_init(mach_port_t restart_service_port, Boolean enableRestart)
257 {
258 mach_port_t bootstrap_port;
259 CFRunLoopSourceRef rls;
260 char *service_name;
261 mach_port_t service_port = restart_service_port;
262 kern_return_t status;
263
264 service_name = getenv("SCD_SERVER");
265 if (!service_name) {
266 service_name = SCD_SERVER;
267 }
268
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);
274 }
275
276 if (service_port == MACH_PORT_NULL) {
277 mach_port_t service_send_port;
278
279 /* Check "configd" server status */
280 status = bootstrap_check_in(bootstrap_port, service_name, &service_port);
281 switch (status) {
282 case BOOTSTRAP_SUCCESS :
283 /* if we are being restarted by mach_init */
284 priv_bootstrap_port = bootstrap_port;
285 break;
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) */
292 if (enableRestart) {
293 status = bootstrap_create_server(bootstrap_port,
294 "/usr/sbin/configd",
295 geteuid(),
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);
301 }
302 } else {
303 priv_bootstrap_port = bootstrap_port;
304 }
305
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);
310 }
311
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);
316 }
317 break;
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);
322 default :
323 SCLog(TRUE, LOG_ERR, CFSTR("bootstrap_check_in() failed: status=%d"), status);
324 exit (EX_UNAVAILABLE);
325 }
326
327 }
328
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);
334 }
335
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);
341 }
342
343 /* Create the primary / new connection port */
344 configd_port = CFMachPortCreateWithPort(NULL, service_port, configdCallback, NULL, NULL);
345
346 /*
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
351 * session.
352 */
353 rls = CFMachPortCreateRunLoopSource(NULL, configd_port, 0);
354 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
355 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, CFSTR("locked"));
356 CFRelease(rls);
357
358 /* Create a session for the primary / new connection port */
359 (void) addSession(configd_port);
360
361 return;
362 }
363
364
365 __private_extern__
366 void
367 server_shutdown()
368 {
369 char *service_name;
370 mach_port_t service_port;
371 kern_return_t status;
372
373 /*
374 * Note: we can't use SCLog() since the signal may be received while the
375 * logging thread lock is held.
376 */
377 if ((priv_bootstrap_port == MACH_PORT_NULL) || (configd_port == NULL)) {
378 return;
379 }
380
381 service_name = getenv("SCD_SERVER");
382 if (!service_name) {
383 service_name = SCD_SERVER;
384 }
385
386 service_port = CFMachPortGetPort(configd_port);
387 if (service_port != MACH_PORT_NULL) {
388 (void) mach_port_destroy(mach_task_self(), service_port);
389 }
390
391 status = bootstrap_register(priv_bootstrap_port, service_name, MACH_PORT_NULL);
392 switch (status) {
393 case BOOTSTRAP_SUCCESS :
394 break;
395 case MACH_SEND_INVALID_DEST :
396 case MIG_SERVER_DIED :
397 /* something happened to mach_init */
398 break;
399 default :
400 if (_configd_verbose) {
401 syslog (LOG_ERR, "bootstrap_register() failed: status=%d" , status);
402 } else {
403 fprintf(stderr, "bootstrap_register() failed: status=%d\n", status);
404 fflush (stderr);
405 }
406 exit (EX_UNAVAILABLE);
407 }
408
409 exit(EX_OK);
410 }
411
412
413 __private_extern__
414 void
415 server_loop()
416 {
417 CFStringRef rlMode;
418 int rlStatus;
419
420 while (TRUE) {
421 /*
422 * if linked with a DEBUG version of the framework, display some
423 * debugging information
424 */
425 __showMachPortStatus();
426
427 /*
428 * process one run loop event
429 */
430 rlMode = (storeLocked > 0) ? CFSTR("locked") : kCFRunLoopDefaultMode;
431 rlStatus = CFRunLoopRunInMode(rlMode, 1.0e10, TRUE);
432
433 /*
434 * check for, and if necessary, push out change notifications
435 * to other processes.
436 */
437 pushNotifications();
438 }
439 }