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