]> git.saurik.com Git - apple/configd.git/blob - configd.tproj/configd_server.c
configd-204.tar.gz
[apple/configd.git] / configd.tproj / configd_server.c
1 /*
2 * Copyright (c) 2000-2007 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*
25 * Modification History
26 *
27 * March 9, 2004 Allan Nathanson <ajn@apple.com>
28 * - add DNS configuration server
29 *
30 * June 1, 2001 Allan Nathanson <ajn@apple.com>
31 * - public API conversion
32 *
33 * March 24, 2000 Allan Nathanson <ajn@apple.com>
34 * - initial revision
35 */
36
37 #include <sysexits.h>
38 #include <unistd.h>
39 #include <sys/types.h>
40 #include <servers/bootstrap.h>
41
42 #include "configd.h"
43 #include "configd_server.h"
44 #include "notify_server.h"
45 #include "session.h"
46
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 *);
50
51 #include "shared_dns_info_types.h"
52 #include "dnsinfo_server.h"
53
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 *);
57
58 /* configd server port (for new session requests) */
59 static CFMachPortRef configd_port = NULL;
60
61 /* priviledged bootstrap port (for registering/unregistering w/launchd) */
62 static mach_port_t priv_bootstrap_port = MACH_PORT_NULL;
63
64 __private_extern__
65 boolean_t
66 config_demux(mach_msg_header_t *request, mach_msg_header_t *reply)
67 {
68 Boolean processed = FALSE;
69
70 /*
71 * (attempt to) process SCDynamicStore requests.
72 */
73 processed = config_server(request, reply);
74 if (processed) {
75 return TRUE;
76 }
77
78 /*
79 * (attempt to) process DNS configuration requests.
80 */
81 processed = shared_dns_info_server(request, reply);
82 if (processed) {
83 return TRUE;
84 }
85
86 /*
87 * (attempt to) process (NO MORE SENDERS) notification messages.
88 */
89 processed = notify_server(request, reply);
90 if (processed) {
91 return TRUE;
92 }
93
94 /*
95 * unknown message ID, log and return an error.
96 */
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;
105
106 return FALSE;
107 }
108
109
110 #define MACH_MSG_BUFFER_SIZE 128
111
112
113 __private_extern__
114 void
115 configdCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
116 {
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;
122 int options;
123
124 if (bufSize == 0) {
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;
129 }
130
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);
136 }
137 }
138
139 if (bufSize > sizeof(bufReply_q)) {
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 char *service_name;
212 kern_return_t status;
213
214 service_name = getenv("SCD_SERVER");
215 if (!service_name) {
216 service_name = SCD_SERVER;
217 }
218
219 /* Check "configd" server status */
220 status = bootstrap_check_in(bootstrap_port, service_name, restart_service_port);
221 switch (status) {
222 case BOOTSTRAP_SUCCESS :
223 /* if we are being restarted by launchd */
224 priv_bootstrap_port = bootstrap_port;
225 break;
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",
230 service_name);
231 return TRUE;
232 case BOOTSTRAP_UNKNOWN_SERVICE :
233 /* if the server is not currently registered/active */
234 *restart_service_port = MACH_PORT_NULL;
235 break;
236 default :
237 fprintf(stderr,
238 "bootstrap_check_in() failed: %s\n",
239 bootstrap_strerror(status));
240 exit (EX_UNAVAILABLE);
241 }
242
243 return FALSE;
244 }
245
246
247 static CFStringRef
248 serverMPCopyDescription(const void *info)
249 {
250 return CFStringCreateWithFormat(NULL, NULL, CFSTR("<main DynamicStore MP>"));
251 }
252
253
254 __private_extern__
255 void
256 server_init(mach_port_t restart_service_port,
257 Boolean enableRestart)
258 {
259 CFMachPortContext context = { 0
260 , (void *)1
261 , NULL
262 , NULL
263 , serverMPCopyDescription
264 };
265
266 CFRunLoopSourceRef rls;
267 char *service_name;
268 mach_port_t service_port = restart_service_port;
269 kern_return_t status;
270 mach_port_t unpriv_bootstrap_port;
271
272 service_name = getenv("SCD_SERVER");
273 if (!service_name) {
274 service_name = SCD_SERVER;
275 }
276
277 if (service_port == MACH_PORT_NULL) {
278 mach_port_t service_send_port;
279
280 /* Check "configd" server status */
281 status = bootstrap_check_in(bootstrap_port, service_name, &service_port);
282 switch (status) {
283 case BOOTSTRAP_SUCCESS :
284 /* if we are being restarted by launchd */
285 priv_bootstrap_port = bootstrap_port;
286 break;
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) */
293 if (enableRestart) {
294 status = bootstrap_create_server(bootstrap_port,
295 "/usr/sbin/configd",
296 geteuid(),
297 FALSE, /* not onDemand == restart now */
298 &priv_bootstrap_port);
299 if (status != BOOTSTRAP_SUCCESS) {
300 SCLog(TRUE, LOG_ERR,
301 CFSTR("server_init bootstrap_create_server() failed: %s"),
302 bootstrap_strerror(status));
303 exit (EX_UNAVAILABLE);
304 }
305 } else {
306 priv_bootstrap_port = bootstrap_port;
307 }
308
309 status = bootstrap_create_service(priv_bootstrap_port, service_name, &service_send_port);
310 if (status != BOOTSTRAP_SUCCESS) {
311 SCLog(TRUE, LOG_ERR,
312 CFSTR("server_init bootstrap_create_service() failed: %s"),
313 bootstrap_strerror(status));
314 exit (EX_UNAVAILABLE);
315 }
316
317 status = bootstrap_check_in(priv_bootstrap_port, service_name, &service_port);
318 if (status != BOOTSTRAP_SUCCESS) {
319 SCLog(TRUE, LOG_ERR,
320 CFSTR("server_init bootstrap_check_in() failed: %s"),
321 bootstrap_strerror(status));
322 exit (EX_UNAVAILABLE);
323 }
324 break;
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);
329 default :
330 SCLog(TRUE, LOG_ERR,
331 CFSTR("server_init bootstrap_check_in() failed: %s"),
332 bootstrap_strerror(status));
333 exit (EX_UNAVAILABLE);
334 }
335
336 }
337
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) {
341 SCLog(TRUE, LOG_ERR,
342 CFSTR("server_init bootstrap_unprivileged() failed: %s"),
343 bootstrap_strerror(status));
344 exit (EX_UNAVAILABLE);
345 }
346
347 status = task_set_bootstrap_port(mach_task_self(), unpriv_bootstrap_port);
348 if (status != BOOTSTRAP_SUCCESS) {
349 SCLog(TRUE, LOG_ERR,
350 CFSTR("server_init task_set_bootstrap_port(): %s"),
351 mach_error_string(status));
352 exit (EX_UNAVAILABLE);
353 }
354
355 /* ... and make sure that the global "bootstrap_port" is also unpriviledged */
356 bootstrap_port = unpriv_bootstrap_port;
357
358 /* Create the primary / new connection port */
359 configd_port = CFMachPortCreateWithPort(NULL, service_port, configdCallback, &context, NULL);
360
361 /*
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
366 * session.
367 */
368 rls = CFMachPortCreateRunLoopSource(NULL, configd_port, 0);
369 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
370 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, CFSTR("locked"));
371 CFRelease(rls);
372
373 /* Create a session for the primary / new connection port */
374 (void) addSession(configd_port);
375
376 return;
377 }
378
379
380 __private_extern__
381 int
382 server_shutdown()
383 {
384 char *service_name;
385 mach_port_t service_port;
386 kern_return_t status;
387
388 /*
389 * Note: we can't use SCLog() since the signal may be received while the
390 * logging thread lock is held.
391 */
392 if ((priv_bootstrap_port == MACH_PORT_NULL) || (configd_port == NULL)) {
393 return EX_OK;
394 }
395
396 service_name = getenv("SCD_SERVER");
397 if (!service_name) {
398 service_name = SCD_SERVER;
399 }
400
401 service_port = CFMachPortGetPort(configd_port);
402 if (service_port != MACH_PORT_NULL) {
403 (void) mach_port_destroy(mach_task_self(), service_port);
404 }
405
406 status = bootstrap_register(priv_bootstrap_port, service_name, MACH_PORT_NULL);
407 switch (status) {
408 case BOOTSTRAP_SUCCESS :
409 break;
410 case MACH_SEND_INVALID_DEST :
411 case MIG_SERVER_DIED :
412 /* something happened to launchd */
413 break;
414 default :
415 SCLog(TRUE, LOG_ERR,
416 CFSTR("server_shutdown bootstrap_register(): %s"),
417 bootstrap_strerror(status));
418 return EX_UNAVAILABLE;
419 }
420
421 return EX_OK;
422 }
423
424
425 __private_extern__
426 void
427 server_loop()
428 {
429 CFStringRef rlMode;
430 int rlStatus;
431
432 while (TRUE) {
433 /*
434 * if linked with a DEBUG version of the framework, display some
435 * debugging information
436 */
437 __showMachPortStatus();
438
439 /*
440 * process one run loop event
441 */
442 rlMode = (storeLocked > 0) ? CFSTR("locked") : kCFRunLoopDefaultMode;
443 rlStatus = CFRunLoopRunInMode(rlMode, 1.0e10, TRUE);
444
445 /*
446 * check for, and if necessary, push out change notifications
447 * to other processes.
448 */
449 pushNotifications();
450 }
451 }