]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSMacOSX/helper-main.c
mDNSResponder-561.1.1.tar.gz
[apple/mdnsresponder.git] / mDNSMacOSX / helper-main.c
1 /* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2007 Apple Inc. All rights reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 #define _FORTIFY_SOURCE 2
19
20 #include <CoreFoundation/CoreFoundation.h>
21 #include <sys/cdefs.h>
22 #include <sys/time.h>
23 #include <sys/types.h>
24 #include <mach/mach.h>
25 #include <mach/mach_error.h>
26 #include <servers/bootstrap.h>
27 #include <asl.h>
28 #include <launch.h>
29 #include <pwd.h>
30 #include <pthread.h>
31 #include <stdarg.h>
32 #include <stdbool.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <time.h>
36 #include <unistd.h>
37 #include <Security/Security.h>
38 #include "helper.h"
39 #include "helper-server.h"
40 #include "helpermsg.h"
41 #include "helpermsgServer.h"
42 #include <vproc.h>
43
44 #if TARGET_OS_EMBEDDED
45 #define NO_SECURITYFRAMEWORK 1
46 #endif
47
48 #ifndef LAUNCH_JOBKEY_MACHSERVICES
49 #define LAUNCH_JOBKEY_MACHSERVICES "MachServices"
50 #define LAUNCH_DATA_MACHPORT 10
51 #define launch_data_get_machport launch_data_get_fd
52 #endif
53
54 union max_msg_size
55 {
56 union __RequestUnion__proxy_helper_subsystem req;
57 union __ReplyUnion__proxy_helper_subsystem rep;
58 };
59
60 static const mach_msg_size_t MAX_MSG_SIZE = sizeof(union max_msg_size) + MAX_TRAILER_SIZE;
61 static aslclient logclient = NULL;
62 static int opt_debug;
63 static pthread_t idletimer_thread;
64
65 unsigned long maxidle = 15;
66 unsigned long actualidle = 3600;
67
68 CFRunLoopRef gRunLoop = NULL;
69 CFRunLoopTimerRef gTimer = NULL;
70
71 mach_port_t gPort = MACH_PORT_NULL;
72
73 static void helplogv(int level, const char *fmt, va_list ap)
74 {
75 if (NULL == logclient) { vfprintf(stderr, fmt, ap); fflush(stderr); }
76 else asl_vlog(logclient, NULL, level, fmt, ap);
77 }
78
79 void helplog(int level, const char *fmt, ...)
80 {
81 va_list ap;
82 va_start(ap, fmt);
83 helplogv(level, fmt, ap);
84 va_end(ap);
85 }
86
87 // for safe_vproc
88 void LogMsgWithLevel(mDNSLogLevel_t logLevel, const char *fmt, ...)
89 {
90 (void)logLevel;
91 va_list ap;
92 va_start(ap, fmt);
93 // safe_vproc only calls LogMsg, so assume logLevel maps to ASL_LEVEL_ERR
94 helplog(ASL_LEVEL_ERR, fmt, ap);
95 va_end(ap);
96 }
97
98 static void handle_sigterm(int sig)
99 {
100 // debug("entry sig=%d", sig); Can't use syslog from within a signal handler
101 assert(sig == SIGTERM);
102 (void)proxy_mDNSExit(gPort);
103 }
104
105 static void initialize_logging(void)
106 {
107 logclient = asl_open(NULL, kmDNSHelperServiceName, (opt_debug ? ASL_OPT_STDERR : 0));
108 if (NULL == logclient) { fprintf(stderr, "Could not initialize ASL logging.\n"); fflush(stderr); return; }
109 if (opt_debug) asl_set_filter(logclient, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG));
110 }
111
112 static void initialize_id(void)
113 {
114 static char login[] = "_mdnsresponder";
115 struct passwd hardcode;
116 struct passwd *pwd = &hardcode; // getpwnam(login);
117 hardcode.pw_uid = 65;
118 hardcode.pw_gid = 65;
119
120 if (!pwd) { helplog(ASL_LEVEL_ERR, "Could not find account name `%s'. I will only help root.", login); return; }
121 mDNSResponderUID = pwd->pw_uid;
122 mDNSResponderGID = pwd->pw_gid;
123 }
124
125 static void diediedie(CFRunLoopTimerRef timer, void *context)
126 {
127 debug("entry %p %p %d", timer, context, maxidle);
128 assert(gTimer == timer);
129 if (maxidle)
130 (void)proxy_mDNSExit(gPort);
131 }
132
133 void pause_idle_timer(void)
134 {
135 debug("entry");
136 assert(gTimer);
137 assert(gRunLoop);
138 CFRunLoopRemoveTimer(gRunLoop, gTimer, kCFRunLoopDefaultMode);
139 }
140
141 void unpause_idle_timer(void)
142 {
143 debug("entry");
144 assert(gRunLoop);
145 assert(gTimer);
146 CFRunLoopAddTimer(gRunLoop, gTimer, kCFRunLoopDefaultMode);
147 }
148
149 void update_idle_timer(void)
150 {
151 debug("entry");
152 assert(gTimer);
153 CFRunLoopTimerSetNextFireDate(gTimer, CFAbsoluteTimeGetCurrent() + actualidle);
154 }
155
156 static void *idletimer(void *context)
157 {
158 debug("entry context=%p", context);
159 gRunLoop = CFRunLoopGetCurrent();
160
161 unpause_idle_timer();
162
163 for (;;)
164 {
165 debug("Running CFRunLoop");
166 CFRunLoopRun();
167 sleep(1);
168 }
169
170 return NULL;
171 }
172
173 static int initialize_timer()
174 {
175 gTimer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + actualidle, actualidle, 0, 0, diediedie, NULL);
176 int err = 0;
177
178 debug("entry");
179 if (0 != (err = pthread_create(&idletimer_thread, NULL, idletimer, NULL)))
180 helplog(ASL_LEVEL_ERR, "Could not start idletimer thread: %s", strerror(err));
181
182 return err;
183 }
184
185 static mach_port_t register_service(const char *service_name)
186 {
187 mach_port_t port = MACH_PORT_NULL;
188 kern_return_t kr;
189
190 if (KERN_SUCCESS != (kr = bootstrap_check_in(bootstrap_port, (char *)service_name, &port)))
191 {
192 helplog(ASL_LEVEL_ERR, "bootstrap_check_in: %d %X %s", kr, kr, mach_error_string(kr));
193 return MACH_PORT_NULL;
194 }
195
196 if (KERN_SUCCESS != (kr = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND)))
197 {
198 helplog(ASL_LEVEL_ERR, "mach_port_insert_right: %d %X %s", kr, kr, mach_error_string(kr));
199 mach_port_deallocate(mach_task_self(), port);
200 return MACH_PORT_NULL;
201 }
202
203 return port;
204 }
205
206 int main(int ac, char *av[])
207 {
208 char *p = NULL;
209 kern_return_t kr = KERN_FAILURE;
210 long n;
211 int ch;
212 mach_msg_header_t hdr;
213
214 while ((ch = getopt(ac, av, "dt:")) != -1)
215 switch (ch)
216 {
217 case 'd': opt_debug = 1; break;
218 case 't':
219 n = strtol(optarg, &p, 0);
220 if ('\0' == optarg[0] || '\0' != *p || n > LONG_MAX || n < 0)
221 { fprintf(stderr, "Invalid idle timeout: %s\n", optarg); exit(EXIT_FAILURE); }
222 maxidle = n;
223 break;
224 case '?':
225 default:
226 fprintf(stderr, "Usage: mDNSResponderHelper [-d] [-t maxidle]\n");
227 exit(EXIT_FAILURE);
228 }
229 ac -= optind;
230 av += optind;
231
232 initialize_logging();
233 helplog(ASL_LEVEL_INFO, "Starting");
234 initialize_id();
235
236 #ifndef NO_SECURITYFRAMEWORK
237 // We should normally be running as a system daemon. However, that might not be the case in some scenarios (e.g. debugging).
238 // Explicitly ensure that our Keychain operations utilize the system domain.
239 if (opt_debug) SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem);
240 #endif
241 gPort = register_service(kmDNSHelperServiceName);
242 if (!gPort)
243 exit(EXIT_FAILURE);
244
245 if (maxidle) actualidle = maxidle;
246
247 signal(SIGTERM, handle_sigterm);
248
249 // We use BeginTransactionAtShutdown in the plist that ensures that we will
250 // receive a SIGTERM during shutdown rather than a SIGKILL. But launchd (due to some
251 // limitation) currently requires us to still start and end the transaction for
252 // its proper initialization.
253 vproc_transaction_t vt = vproc_transaction_begin(NULL);
254 if (vt) vproc_transaction_end(NULL, vt);
255
256 if (initialize_timer()) exit(EXIT_FAILURE);
257 for (n=0; n<100000; n++) if (!gRunLoop) usleep(100);
258 if (!gRunLoop)
259 {
260 helplog(ASL_LEVEL_ERR, "gRunLoop not set after waiting");
261 exit(EXIT_FAILURE);
262 }
263
264 for(;;)
265 {
266 hdr.msgh_bits = 0;
267 hdr.msgh_local_port = gPort;
268 hdr.msgh_remote_port = MACH_PORT_NULL;
269 hdr.msgh_size = sizeof(hdr);
270 hdr.msgh_id = 0;
271 kr = mach_msg(&hdr, MACH_RCV_LARGE | MACH_RCV_MSG, 0, hdr.msgh_size, gPort, 0, 0);
272 if (MACH_RCV_TOO_LARGE != kr)
273 helplog(ASL_LEVEL_ERR, "main MACH_RCV_MSG error: %d %X %s", kr, kr, mach_error_string(kr));
274
275 kr = mach_msg_server_once(helper_server, MAX_MSG_SIZE, gPort,
276 MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT) | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0));
277 if (KERN_SUCCESS != kr)
278 { helplog(ASL_LEVEL_ERR, "mach_msg_server: %d %X %s", kr, kr, mach_error_string(kr)); exit(EXIT_FAILURE); }
279
280 }
281 exit(EXIT_SUCCESS);
282 }
283
284 // Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion
285 // e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4"
286 // To expand "version" to its value before making the string, use STRINGIFY(version) instead
287 #define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) # s
288 #define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s)
289
290 // For convenience when using the "strings" command, this is the last thing in the file
291 // The "@(#) " pattern is a special prefix the "what" command looks for
292 const char VersionString_SCCS[] = "@(#) mDNSResponderHelper " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")";
293
294 #if _BUILDING_XCODE_PROJECT_
295 // If the process crashes, then this string will be magically included in the automatically-generated crash log
296 const char *__crashreporter_info__ = VersionString_SCCS + 5;
297 asm (".desc ___crashreporter_info__, 0x10");
298 #endif