]> git.saurik.com Git - apple/configd.git/blob - configd.tproj/configd_server.c
configd-130.tar.gz
[apple/configd.git] / configd.tproj / configd_server.c
1 /*
2 * Copyright (c) 2000-2004 Apple Computer, 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/mach_init) */
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 serverSessionRef thisSession;
70 mach_msg_format_0_trailer_t *trailer;
71
72 thisSession = getSession(request->msgh_local_port);
73 if (thisSession) {
74 /*
75 * Get the caller's credentials (eUID/eGID) from the message trailer.
76 */
77 trailer = (mach_msg_security_trailer_t *)((vm_offset_t)request +
78 round_msg(request->msgh_size));
79
80 if ((trailer->msgh_trailer_type == MACH_MSG_TRAILER_FORMAT_0) &&
81 (trailer->msgh_trailer_size >= MACH_MSG_TRAILER_FORMAT_0_SIZE)) {
82 thisSession->callerEUID = trailer->msgh_sender.val[0];
83 thisSession->callerEGID = trailer->msgh_sender.val[1];
84 } else {
85 static Boolean warned = FALSE;
86
87 if (!warned) {
88 SCLog(TRUE, LOG_WARNING, CFSTR("caller's credentials not available."));
89 warned = TRUE;
90 }
91 thisSession->callerEUID = 0;
92 thisSession->callerEGID = 0;
93 }
94 }
95
96 /*
97 * (attempt to) process SCDynamicStore requests.
98 */
99 processed = config_server(request, reply);
100 if (processed) {
101 return TRUE;
102 }
103
104 /*
105 * (attempt to) process DNS configuration requests.
106 */
107 processed = shared_dns_info_server(request, reply);
108 if (processed) {
109 return TRUE;
110 }
111
112 /*
113 * (attempt to) process (NO MORE SENDERS) notification messages.
114 */
115 processed = notify_server(request, reply);
116 if (processed) {
117 return TRUE;
118 }
119
120 /*
121 * unknown message ID, log and return an error.
122 */
123 SCLog(TRUE, LOG_ERR, CFSTR("config_demux(): unknown message ID (%d) received"), request->msgh_id);
124 reply->msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(request->msgh_bits), 0);
125 reply->msgh_remote_port = request->msgh_remote_port;
126 reply->msgh_size = sizeof(mig_reply_error_t); /* Minimal size */
127 reply->msgh_local_port = MACH_PORT_NULL;
128 reply->msgh_id = request->msgh_id + 100;
129 ((mig_reply_error_t *)reply)->NDR = NDR_record;
130 ((mig_reply_error_t *)reply)->RetCode = MIG_BAD_ID;
131
132 return FALSE;
133 }
134
135
136 #define MACH_MSG_BUFFER_SIZE 128
137
138
139 __private_extern__
140 void
141 configdCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
142 {
143 mig_reply_error_t * bufRequest = msg;
144 uint32_t bufReply_q[MACH_MSG_BUFFER_SIZE/sizeof(uint32_t)];
145 mig_reply_error_t * bufReply = (mig_reply_error_t *)bufReply_q;
146 static CFIndex bufSize = 0;
147 mach_msg_return_t mr;
148 int options;
149
150 if (bufSize == 0) {
151 // get max size for MiG reply buffers
152 bufSize = _config_subsystem.maxsize;
153 if (_shared_dns_info_subsystem.maxsize > bufSize) {
154 bufSize = _shared_dns_info_subsystem.maxsize;
155 }
156
157 // check if our on-the-stack reply buffer will be big enough
158 if (bufSize > sizeof(bufReply_q)) {
159 SCLog(TRUE, LOG_NOTICE,
160 CFSTR("configdCallback(): buffer size should be increased > %d"),
161 _config_subsystem.maxsize);
162 }
163 }
164
165 if (bufSize > sizeof(bufReply_q)) {
166 bufReply = CFAllocatorAllocate(NULL, _config_subsystem.maxsize, 0);
167 }
168
169 /* we have a request message */
170 (void) config_demux(&bufRequest->Head, &bufReply->Head);
171
172 if (!(bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
173 if (bufReply->RetCode == MIG_NO_REPLY) {
174 bufReply->Head.msgh_remote_port = MACH_PORT_NULL;
175 } else if ((bufReply->RetCode != KERN_SUCCESS) &&
176 (bufRequest->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
177 /*
178 * destroy the request - but not the reply port
179 */
180 bufRequest->Head.msgh_remote_port = MACH_PORT_NULL;
181 mach_msg_destroy(&bufRequest->Head);
182 }
183 }
184
185 if (bufReply->Head.msgh_remote_port != MACH_PORT_NULL) {
186 /*
187 * send reply.
188 *
189 * We don't want to block indefinitely because the client
190 * isn't receiving messages from the reply port.
191 * If we have a send-once right for the reply port, then
192 * this isn't a concern because the send won't block.
193 * If we have a send right, we need to use MACH_SEND_TIMEOUT.
194 * To avoid falling off the kernel's fast RPC path unnecessarily,
195 * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
196 */
197
198 options = MACH_SEND_MSG;
199 if (MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) != MACH_MSG_TYPE_MOVE_SEND_ONCE) {
200 options |= MACH_SEND_TIMEOUT;
201 }
202 mr = mach_msg(&bufReply->Head, /* msg */
203 options, /* option */
204 bufReply->Head.msgh_size, /* send_size */
205 0, /* rcv_size */
206 MACH_PORT_NULL, /* rcv_name */
207 MACH_MSG_TIMEOUT_NONE, /* timeout */
208 MACH_PORT_NULL); /* notify */
209
210 /* Has a message error occurred? */
211 switch (mr) {
212 case MACH_SEND_INVALID_DEST:
213 case MACH_SEND_TIMED_OUT:
214 break;
215 default :
216 /* Includes success case. */
217 goto done;
218 }
219 }
220
221 if (bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) {
222 mach_msg_destroy(&bufReply->Head);
223 }
224
225 done :
226
227 if (bufReply != (mig_reply_error_t *)bufReply_q)
228 CFAllocatorDeallocate(NULL, bufReply);
229 return;
230 }
231
232
233 __private_extern__
234 boolean_t
235 server_active(mach_port_t *restart_service_port)
236 {
237 char *service_name;
238 kern_return_t status;
239
240 service_name = getenv("SCD_SERVER");
241 if (!service_name) {
242 service_name = SCD_SERVER;
243 }
244
245 /* Check "configd" server status */
246 status = bootstrap_check_in(bootstrap_port, service_name, restart_service_port);
247 switch (status) {
248 case BOOTSTRAP_SUCCESS :
249 /* if we are being restarted by mach_init */
250 priv_bootstrap_port = bootstrap_port;
251 break;
252 case BOOTSTRAP_SERVICE_ACTIVE :
253 case BOOTSTRAP_NOT_PRIVILEGED :
254 /* if another instance of the server is active (or starting) */
255 fprintf(stderr, "'%s' server already active\n",
256 service_name);
257 return TRUE;
258 case BOOTSTRAP_UNKNOWN_SERVICE :
259 /* if the server is not currently registered/active */
260 *restart_service_port = MACH_PORT_NULL;
261 break;
262 default :
263 fprintf(stderr, "bootstrap_check_in() failed: status=%d\n", status);
264 exit (EX_UNAVAILABLE);
265 }
266
267 return FALSE;
268 }
269
270
271 __private_extern__
272 void
273 server_init(mach_port_t restart_service_port, Boolean enableRestart)
274 {
275 CFRunLoopSourceRef rls;
276 char *service_name;
277 mach_port_t service_port = restart_service_port;
278 kern_return_t status;
279 mach_port_t unpriv_bootstrap_port;
280
281 service_name = getenv("SCD_SERVER");
282 if (!service_name) {
283 service_name = SCD_SERVER;
284 }
285
286 if (service_port == MACH_PORT_NULL) {
287 mach_port_t service_send_port;
288
289 /* Check "configd" server status */
290 status = bootstrap_check_in(bootstrap_port, service_name, &service_port);
291 switch (status) {
292 case BOOTSTRAP_SUCCESS :
293 /* if we are being restarted by mach_init */
294 priv_bootstrap_port = bootstrap_port;
295 break;
296 case BOOTSTRAP_NOT_PRIVILEGED :
297 /* if another instance of the server is starting */
298 SCLog(TRUE, LOG_ERR, CFSTR("'%s' server already starting"), service_name);
299 exit (EX_UNAVAILABLE);
300 case BOOTSTRAP_UNKNOWN_SERVICE :
301 /* service not currently registered, "a good thing" (tm) */
302 if (enableRestart) {
303 status = bootstrap_create_server(bootstrap_port,
304 "/usr/sbin/configd",
305 geteuid(),
306 FALSE, /* not onDemand == restart now */
307 &priv_bootstrap_port);
308 if (status != BOOTSTRAP_SUCCESS) {
309 SCLog(TRUE, LOG_ERR, CFSTR("server_init bootstrap_create_server() failed: status=%d"), status);
310 exit (EX_UNAVAILABLE);
311 }
312 } else {
313 priv_bootstrap_port = bootstrap_port;
314 }
315
316 status = bootstrap_create_service(priv_bootstrap_port, service_name, &service_send_port);
317 if (status != BOOTSTRAP_SUCCESS) {
318 SCLog(TRUE, LOG_ERR, CFSTR("server_init bootstrap_create_service() failed: status=%d"), status);
319 exit (EX_UNAVAILABLE);
320 }
321
322 status = bootstrap_check_in(priv_bootstrap_port, service_name, &service_port);
323 if (status != BOOTSTRAP_SUCCESS) {
324 SCLog(TRUE, LOG_ERR, CFSTR("server_init bootstrap_check_in() failed: status=%d"), status);
325 exit (EX_UNAVAILABLE);
326 }
327 break;
328 case BOOTSTRAP_SERVICE_ACTIVE :
329 /* if another instance of the server is active */
330 SCLog(TRUE, LOG_ERR, CFSTR("'%s' server already active"), service_name);
331 exit (EX_UNAVAILABLE);
332 default :
333 SCLog(TRUE, LOG_ERR, CFSTR("server_init bootstrap_check_in() failed: status=%d"), status);
334 exit (EX_UNAVAILABLE);
335 }
336
337 }
338
339 /* we don't want to pass our priviledged bootstrap port along to any spawned helpers so... */
340 status = bootstrap_unprivileged(priv_bootstrap_port, &unpriv_bootstrap_port);
341 if (status != BOOTSTRAP_SUCCESS) {
342 SCLog(TRUE, LOG_ERR, CFSTR("server_init bootstrap_unprivileged() failed: status=%d"), status);
343 exit (EX_UNAVAILABLE);
344 }
345
346 status = task_set_bootstrap_port(mach_task_self(), unpriv_bootstrap_port);
347 if (status != BOOTSTRAP_SUCCESS) {
348 SCLog(TRUE, LOG_ERR, CFSTR("server_init task_set_bootstrap_port(): %s"),
349 mach_error_string(status));
350 exit (EX_UNAVAILABLE);
351 }
352
353 /* Create the primary / new connection port */
354 configd_port = CFMachPortCreateWithPort(NULL, service_port, configdCallback, NULL, NULL);
355
356 /*
357 * Create and add a run loop source for the port and add this source
358 * to both the default run loop mode and the "locked" mode. These two
359 * modes will be used for normal (unlocked) communication with the
360 * server and when multiple (locked) updates are requested by a single
361 * session.
362 */
363 rls = CFMachPortCreateRunLoopSource(NULL, configd_port, 0);
364 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
365 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, CFSTR("locked"));
366 CFRelease(rls);
367
368 /* Create a session for the primary / new connection port */
369 (void) addSession(configd_port);
370
371 return;
372 }
373
374
375 __private_extern__
376 int
377 server_shutdown()
378 {
379 char *service_name;
380 mach_port_t service_port;
381 kern_return_t status;
382
383 /*
384 * Note: we can't use SCLog() since the signal may be received while the
385 * logging thread lock is held.
386 */
387 if ((priv_bootstrap_port == MACH_PORT_NULL) || (configd_port == NULL)) {
388 return EX_OK;
389 }
390
391 service_name = getenv("SCD_SERVER");
392 if (!service_name) {
393 service_name = SCD_SERVER;
394 }
395
396 service_port = CFMachPortGetPort(configd_port);
397 if (service_port != MACH_PORT_NULL) {
398 (void) mach_port_destroy(mach_task_self(), service_port);
399 }
400
401 status = bootstrap_register(priv_bootstrap_port, service_name, MACH_PORT_NULL);
402 switch (status) {
403 case BOOTSTRAP_SUCCESS :
404 break;
405 case MACH_SEND_INVALID_DEST :
406 case MIG_SERVER_DIED :
407 /* something happened to mach_init */
408 break;
409 default :
410 if (_configd_verbose) {
411 syslog (LOG_ERR, "bootstrap_register() failed: status=%d" , status);
412 } else {
413 fprintf(stderr, "bootstrap_register() failed: status=%d\n", status);
414 fflush (stderr);
415 }
416 return EX_UNAVAILABLE;
417 }
418
419 return EX_OK;
420 }
421
422
423 __private_extern__
424 void
425 server_loop()
426 {
427 CFStringRef rlMode;
428 int rlStatus;
429
430 while (TRUE) {
431 /*
432 * if linked with a DEBUG version of the framework, display some
433 * debugging information
434 */
435 __showMachPortStatus();
436
437 /*
438 * process one run loop event
439 */
440 rlMode = (storeLocked > 0) ? CFSTR("locked") : kCFRunLoopDefaultMode;
441 rlStatus = CFRunLoopRunInMode(rlMode, 1.0e10, TRUE);
442
443 /*
444 * check for, and if necessary, push out change notifications
445 * to other processes.
446 */
447 pushNotifications();
448 }
449 }