1/* 2 * Copyright (c) 2000-2011, 2013 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 <TargetConditionals.h> 38#include <sysexits.h> 39#include <unistd.h> 40#include <sys/types.h> 41#include <servers/bootstrap.h> 42 43#include "configd.h" 44#include "configd_server.h" 45#include "notify_server.h" 46#include "session.h" 47 48/* MiG generated externals and functions */ 49extern struct mig_subsystem _config_subsystem; 50extern boolean_t config_server(mach_msg_header_t *, mach_msg_header_t *); 51 52/* configd server port (for new session requests) */ 53static CFMachPortRef configd_port = NULL; 54 55__private_extern__ 56boolean_t 57config_demux(mach_msg_header_t *request, mach_msg_header_t *reply) 58{ 59 Boolean processed = FALSE; 60 61 /* 62 * (attempt to) process SCDynamicStore requests. 63 */ 64 processed = config_server(request, reply); 65 if (processed) { 66 return TRUE; 67 } 68 69 /* 70 * (attempt to) process (NO MORE SENDERS) notification messages. 71 */ 72 processed = notify_server(request, reply); 73 if (processed) { 74 return TRUE; 75 } 76 77 /* 78 * unknown message ID, log and return an error. 79 */ 80 SCLog(TRUE, LOG_ERR, CFSTR("config_demux(): unknown message ID (%d) received"), request->msgh_id); 81 reply->msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(request->msgh_bits), 0); 82 reply->msgh_remote_port = request->msgh_remote_port; 83 reply->msgh_size = sizeof(mig_reply_error_t); /* Minimal size */ 84 reply->msgh_local_port = MACH_PORT_NULL; 85 reply->msgh_id = request->msgh_id + 100; 86 ((mig_reply_error_t *)reply)->NDR = NDR_record; 87 ((mig_reply_error_t *)reply)->RetCode = MIG_BAD_ID; 88 89 return FALSE; 90} 91 92 93#define MACH_MSG_BUFFER_SIZE 128 94 95 96__private_extern__ 97void 98configdCallback(CFMachPortRef port, void *msg, CFIndex size, void *info) 99{ 100 mig_reply_error_t * bufRequest = msg; 101 uint32_t bufReply_q[MACH_MSG_BUFFER_SIZE/sizeof(uint32_t)]; 102 mig_reply_error_t * bufReply = (mig_reply_error_t *)bufReply_q; 103 static CFIndex bufSize = 0; 104 mach_msg_return_t mr; 105 int options; 106 107 if (bufSize == 0) { 108 // get max size for MiG reply buffers 109 bufSize = _config_subsystem.maxsize; 110 111 // check if our on-the-stack reply buffer will be big enough 112 if (bufSize > sizeof(bufReply_q)) { 113 SCLog(TRUE, LOG_NOTICE, 114 CFSTR("configdCallback(): buffer size should be increased > %d"), 115 _config_subsystem.maxsize); 116 } 117 } 118 119 if (bufSize > sizeof(bufReply_q)) { 120 bufReply = CFAllocatorAllocate(NULL, _config_subsystem.maxsize, 0); 121 } 122 bufReply->RetCode = 0; 123 124 /* we have a request message */ 125 (void) config_demux(&bufRequest->Head, &bufReply->Head); 126 127 if (!(bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) { 128 if (bufReply->RetCode == MIG_NO_REPLY) { 129 bufReply->Head.msgh_remote_port = MACH_PORT_NULL; 130 } else if ((bufReply->RetCode != KERN_SUCCESS) && 131 (bufRequest->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) { 132 /* 133 * destroy the request - but not the reply port 134 */ 135 bufRequest->Head.msgh_remote_port = MACH_PORT_NULL; 136 mach_msg_destroy(&bufRequest->Head); 137 } 138 } 139 140 if (bufReply->Head.msgh_remote_port != MACH_PORT_NULL) { 141 /* 142 * send reply. 143 * 144 * We don't want to block indefinitely because the client 145 * isn't receiving messages from the reply port. 146 * If we have a send-once right for the reply port, then 147 * this isn't a concern because the send won't block. 148 * If we have a send right, we need to use MACH_SEND_TIMEOUT. 149 * To avoid falling off the kernel's fast RPC path unnecessarily, 150 * we only supply MACH_SEND_TIMEOUT when absolutely necessary. 151 */ 152 153 options = MACH_SEND_MSG; 154 if (MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) != MACH_MSG_TYPE_MOVE_SEND_ONCE) { 155 options |= MACH_SEND_TIMEOUT; 156 } 157 mr = mach_msg(&bufReply->Head, /* msg */ 158 options, /* option */ 159 bufReply->Head.msgh_size, /* send_size */ 160 0, /* rcv_size */ 161 MACH_PORT_NULL, /* rcv_name */ 162 MACH_MSG_TIMEOUT_NONE, /* timeout */ 163 MACH_PORT_NULL); /* notify */ 164 165 /* Has a message error occurred? */ 166 switch (mr) { 167 case MACH_SEND_INVALID_DEST: 168 case MACH_SEND_TIMED_OUT: 169 break; 170 default : 171 /* Includes success case. */ 172 goto done; 173 } 174 } 175 176 if (bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) { 177 mach_msg_destroy(&bufReply->Head); 178 } 179 180 done : 181 182 if (bufReply != (mig_reply_error_t *)bufReply_q) 183 CFAllocatorDeallocate(NULL, bufReply); 184 return; 185} 186 187 188static CFStringRef 189serverMPCopyDescription(const void *info) 190{ 191 return CFStringCreateWithFormat(NULL, NULL, CFSTR("<main DynamicStore MP>")); 192} 193 194 195__private_extern__ 196void 197server_init() 198{ 199 serverSessionRef mySession; 200 CFRunLoopSourceRef rls; 201 char *service_name; 202 mach_port_t service_port = MACH_PORT_NULL; 203 kern_return_t status; 204 205 service_name = getenv("SCD_SERVER"); 206 if (!service_name) { 207 service_name = SCD_SERVER; 208 } 209 210 /* Check "configd" server status */ 211 status = bootstrap_check_in(bootstrap_port, service_name, &service_port); 212 switch (status) { 213 case BOOTSTRAP_SUCCESS : 214 /* if we are being [re-]started by launchd */ 215 break; 216 case BOOTSTRAP_NOT_PRIVILEGED : 217 /* if another instance of the server is starting */ 218 SCLog(TRUE, LOG_ERR, CFSTR("'%s' server already starting"), service_name); 219 exit (EX_UNAVAILABLE); 220 case BOOTSTRAP_SERVICE_ACTIVE : 221 /* if another instance of the server is active */ 222 SCLog(TRUE, LOG_ERR, CFSTR("'%s' server already active"), service_name); 223 exit (EX_UNAVAILABLE); 224 default : 225 SCLog(TRUE, LOG_ERR, 226 CFSTR("server_init bootstrap_check_in(..., '%s', ...) failed: %s"), 227 service_name, 228 bootstrap_strerror(status)); 229 exit (EX_UNAVAILABLE); 230 } 231 232 /* Create the primary / new connection port and backing session */ 233 mySession = addSession(service_port, serverMPCopyDescription); 234 configd_port = mySession->serverPort; 235 236 /* 237 * Create and add a run loop source for the port and add this source 238 * to the default run loop mode. 239 */ 240 rls = CFMachPortCreateRunLoopSource(NULL, configd_port, 0); 241 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); 242 CFRelease(rls); 243 244 return; 245} 246 247 248__private_extern__ 249int 250server_shutdown() 251{ 252 if (configd_port != NULL) { 253 mach_port_t service_port = CFMachPortGetPort(configd_port); 254 255 CFMachPortInvalidate(configd_port); 256 CFRelease(configd_port); 257 configd_port = NULL; 258 259 if (service_port != MACH_PORT_NULL) { 260 (void) mach_port_mod_refs(mach_task_self(), 261 service_port, 262 MACH_PORT_RIGHT_RECEIVE, 263 -1); 264 } 265 } 266 267 return EX_OK; 268} 269 270 271__private_extern__ 272void 273server_loop() 274{ 275 pthread_setname_np("SCDynamicStore"); 276 277 while (TRUE) { 278 /* 279 * process one run loop event 280 */ 281 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e10, TRUE); 282 283 /* 284 * check for, and if necessary, push out change notifications 285 * to other processes. 286 */ 287 pushNotifications(_configd_trace); 288 } 289} 290