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