]> git.saurik.com Git - apple/configd.git/blob - configd.tproj/configd_server.c
configd-24.tar.gz
[apple/configd.git] / configd.tproj / configd_server.c
1 /*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
23 #include <servers/bootstrap.h>
24 #include <sysexits.h>
25
26 #include "configd.h"
27 #include "configd_server.h"
28 #include "notify_server.h"
29 #include "session.h"
30 #include "notify.h"
31
32 /* MiG generated externals and functions */
33 extern struct rpc_subsystem _config_subsystem;
34 extern boolean_t config_server(mach_msg_header_t *, mach_msg_header_t *);
35
36 /* server state information */
37 CFMachPortRef configd_port; /* configd server port (for new session requests) */
38
39
40 boolean_t
41 config_demux(mach_msg_header_t *request, mach_msg_header_t *reply)
42 {
43 boolean_t processed = FALSE;
44
45 mach_msg_format_0_trailer_t *trailer;
46
47 /* Feed the request into the ("MiG" generated) server */
48 if (!processed &&
49 (request->msgh_id >= _config_subsystem.start && request->msgh_id < _config_subsystem.end)) {
50 serverSessionRef thisSession;
51
52 thisSession = getSession(request->msgh_local_port);
53 if (thisSession) {
54 /*
55 * Get the caller's credentials (eUID/eGID) from the message trailer.
56 */
57 trailer = (mach_msg_security_trailer_t *)((vm_offset_t)request +
58 round_msg(request->msgh_size));
59
60 if ((trailer->msgh_trailer_type == MACH_MSG_TRAILER_FORMAT_0) &&
61 (trailer->msgh_trailer_size >= MACH_MSG_TRAILER_FORMAT_0_SIZE)) {
62 thisSession->callerEUID = trailer->msgh_sender.val[0];
63 thisSession->callerEGID = trailer->msgh_sender.val[1];
64 SCDLog(LOG_DEBUG, CFSTR("caller has eUID = %d, eGID = %d"),
65 thisSession->callerEUID,
66 thisSession->callerEGID);
67 } else {
68 static boolean_t warned = FALSE;
69
70 if (!warned) {
71 SCDLog(LOG_WARNING, CFSTR("caller's credentials not available."));
72 warned = TRUE;
73 }
74 thisSession->callerEUID = 0;
75 thisSession->callerEGID = 0;
76 }
77 }
78
79 /*
80 * Process configd requests.
81 */
82 processed = config_server(request, reply);
83 }
84
85 if (!processed &&
86 (request->msgh_id >= MACH_NOTIFY_FIRST && request->msgh_id < MACH_NOTIFY_LAST)) {
87 processed = notify_server(request, reply);
88 }
89
90 if (!processed) {
91 SCDLog(LOG_WARNING, CFSTR("unknown message received"));
92 exit (EX_OSERR);
93 }
94
95 return processed;
96 }
97
98
99 void
100 configdCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
101 {
102 mig_reply_error_t *bufRequest = msg;
103 mig_reply_error_t *bufReply = CFAllocatorAllocate(NULL, _config_subsystem.maxsize, 0);
104 mach_msg_return_t mr;
105 int options;
106
107 /* we have a request message */
108 (void) config_demux(&bufRequest->Head, &bufReply->Head);
109
110 if (!(bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) && (bufReply->RetCode != KERN_SUCCESS)) {
111
112 if (bufReply->RetCode == MIG_NO_REPLY) {
113 /*
114 * This return code is a little tricky -- it appears that the
115 * demux routine found an error of some sort, but since that
116 * error would not normally get returned either to the local
117 * user or the remote one, we pretend it's ok.
118 */
119 CFAllocatorDeallocate(NULL, bufReply);
120 return;
121 }
122
123 /*
124 * destroy any out-of-line data in the request buffer but don't destroy
125 * the reply port right (since we need that to send an error message).
126 */
127 bufRequest->Head.msgh_remote_port = MACH_PORT_NULL;
128 mach_msg_destroy(&bufRequest->Head);
129 }
130
131 if (bufReply->Head.msgh_remote_port == MACH_PORT_NULL) {
132 /* no reply port, so destroy the reply */
133 if (bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) {
134 mach_msg_destroy(&bufReply->Head);
135 }
136 CFAllocatorDeallocate(NULL, bufReply);
137 return;
138 }
139
140 /*
141 * send reply.
142 *
143 * We don't want to block indefinitely because the client
144 * isn't receiving messages from the reply port.
145 * If we have a send-once right for the reply port, then
146 * this isn't a concern because the send won't block.
147 * If we have a send right, we need to use MACH_SEND_TIMEOUT.
148 * To avoid falling off the kernel's fast RPC path unnecessarily,
149 * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
150 */
151
152 options = MACH_SEND_MSG;
153 if (MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) == MACH_MSG_TYPE_MOVE_SEND_ONCE) {
154 options |= MACH_SEND_TIMEOUT;
155 }
156 mr = mach_msg(&bufReply->Head, /* msg */
157 options, /* option */
158 bufReply->Head.msgh_size, /* send_size */
159 0, /* rcv_size */
160 MACH_PORT_NULL, /* rcv_name */
161 MACH_MSG_TIMEOUT_NONE, /* timeout */
162 MACH_PORT_NULL); /* notify */
163
164
165 /* Has a message error occurred? */
166 switch (mr) {
167 case MACH_SEND_INVALID_DEST:
168 case MACH_SEND_TIMED_OUT:
169 /* the reply can't be delivered, so destroy it */
170 mach_msg_destroy(&bufReply->Head);
171 break;
172
173 default :
174 /* Includes success case. */
175 break;
176 }
177
178 CFAllocatorDeallocate(NULL, bufReply);
179 }
180
181
182 boolean_t
183 server_active()
184 {
185 kern_return_t status;
186 mach_port_t bootstrap_port;
187 boolean_t active;
188
189 /* Getting bootstrap server port */
190 status = task_get_bootstrap_port(mach_task_self(), &bootstrap_port);
191 if (status != KERN_SUCCESS) {
192 fprintf(stderr, "task_get_bootstrap_port(): %s\n",
193 mach_error_string(status));
194 exit (EX_UNAVAILABLE);
195 }
196
197 /* Check "configd" server status */
198 status = bootstrap_status(bootstrap_port, SCD_SERVER, &active);
199 switch (status) {
200 case BOOTSTRAP_SUCCESS :
201 if (active) {
202 fprintf(stderr, "configd: '%s' server already active\n",
203 SCD_SERVER);
204 return TRUE;
205 }
206 break;
207 case BOOTSTRAP_UNKNOWN_SERVICE :
208 break;
209 default :
210 fprintf(stderr, "bootstrap_status(): %s\n",
211 mach_error_string(status));
212 exit (EX_UNAVAILABLE);
213 }
214 return FALSE;
215 }
216
217 void
218 server_init()
219 {
220 kern_return_t status;
221 mach_port_t bootstrap_port;
222 boolean_t active;
223 CFRunLoopSourceRef rls;
224
225 /* Getting bootstrap server port */
226 status = task_get_bootstrap_port(mach_task_self(), &bootstrap_port);
227 if (status != KERN_SUCCESS) {
228 SCDLog(LOG_DEBUG, CFSTR("task_get_bootstrap_port(): %s"), mach_error_string(status));
229 exit (EX_UNAVAILABLE);
230 }
231
232 /* Check "configd" server status */
233 status = bootstrap_status(bootstrap_port, SCD_SERVER, &active);
234 switch (status) {
235 case BOOTSTRAP_SUCCESS :
236 if (active) {
237 SCDLog(LOG_DEBUG, CFSTR("\"%s\" is currently active, exiting."), SCD_SERVER);
238 exit (EX_UNAVAILABLE);
239 }
240 break;
241 case BOOTSTRAP_UNKNOWN_SERVICE :
242 /* service not currently registered, "a good thing" (tm) */
243 break;
244 default :
245 fprintf(stderr, "bootstrap_status(): %s\n", mach_error_string(status));
246 exit (EX_UNAVAILABLE);
247 }
248
249 /* Create the primary / new connection port */
250 configd_port = CFMachPortCreate(NULL, configdCallback, NULL, NULL);
251
252 /*
253 * Create and add a run loop source for the port and add this source
254 * to both the default run loop mode and the "locked" mode. These two
255 * modes will be used for normal (unlocked) communication with the
256 * server and when multiple (locked) updates are requested by a single
257 * session.
258 */
259 rls = CFMachPortCreateRunLoopSource(NULL, configd_port, 0);
260 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
261 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, CFSTR("locked"));
262 CFRelease(rls);
263
264 /* Create a session for the primary / new connection port */
265 (void) addSession(configd_port);
266
267 SCDLog(LOG_DEBUG, CFSTR("Registering service \"%s\""), SCD_SERVER);
268 status = bootstrap_register(bootstrap_port, SCD_SERVER, CFMachPortGetPort(configd_port));
269 switch (status) {
270 case BOOTSTRAP_SUCCESS :
271 /* service not currently registered, "a good thing" (tm) */
272 break;
273 case BOOTSTRAP_NOT_PRIVILEGED :
274 SCDLog(LOG_ERR, CFSTR("bootstrap_register(): bootstrap not privileged"));
275 exit (EX_OSERR);
276 case BOOTSTRAP_SERVICE_ACTIVE :
277 SCDLog(LOG_ERR, CFSTR("bootstrap_register(): bootstrap service active"));
278 exit (EX_OSERR);
279 default :
280 SCDLog(LOG_ERR, CFSTR("bootstrap_register(): %s"), mach_error_string(status));
281 exit (EX_OSERR);
282 }
283
284 return;
285 }
286
287
288 void
289 server_loop()
290 {
291 CFStringRef rlMode;
292 int rlStatus;
293
294 while (TRUE) {
295 boolean_t isLocked = SCDOptionGet(NULL, kSCDOptionIsLocked);
296
297 /*
298 * if linked with a DEBUG version of the framework, display some
299 * debugging information
300 */
301 _showMachPortStatus();
302
303 /*
304 * process one run loop event
305 */
306 rlMode = isLocked ? CFSTR("locked") : kCFRunLoopDefaultMode;
307 rlStatus = CFRunLoopRunInMode(rlMode, 1.0e10, TRUE);
308
309 /*
310 * check for, and if necessary, push out change notifications
311 * to other processes.
312 */
313 pushNotifications();
314 }
315 }