]>
Commit | Line | Data |
---|---|---|
e91b9f68 A |
1 | /* |
2 | * Copyright (c) 1999-2004 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights | |
7 | * Reserved. This file contains Original Code and/or Modifications of | |
8 | * Original Code as defined in and that are subject to the Apple Public | |
9 | * Source License Version 1.0 (the 'License'). You may not use this file | |
10 | * except in compliance with the License. Please obtain a copy of the | |
11 | * License at http://www.apple.com/publicsource and read it before using | |
12 | * this file. | |
13 | * | |
14 | * The Original Code and all software distributed under the License are | |
15 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the | |
19 | * License for the specific language governing rights and limitations | |
20 | * under the License." | |
21 | * | |
22 | * @APPLE_LICENSE_HEADER_END@ | |
23 | */ | |
24 | /* | |
25 | * bootstrap -- fundamental service initiator and port server | |
26 | * Mike DeMoney, NeXT, Inc. | |
27 | * Copyright, 1990. All rights reserved. | |
28 | * | |
29 | * bootstrap.c -- implementation of bootstrap main service loop | |
30 | */ | |
31 | ||
32 | /* | |
33 | * Imports | |
34 | */ | |
35 | #include <mach/mach.h> | |
36 | #include <mach/mach_error.h> | |
37 | #include <mach/boolean.h> | |
38 | #include <mach/message.h> | |
39 | #include <mach/notify.h> | |
40 | #include <mach/mig_errors.h> | |
41 | #include <mach/mach_traps.h> | |
42 | #include <mach/mach_interface.h> | |
43 | #include <mach/bootstrap.h> | |
44 | #include <mach/host_info.h> | |
45 | #include <mach/mach_host.h> | |
46 | #include <mach/exception.h> | |
47 | ||
48 | #include <sys/ioctl.h> | |
49 | #include <sys/types.h> | |
50 | #include <sys/time.h> | |
51 | #include <sys/resource.h> | |
52 | #include <sys/wait.h> | |
53 | #include <pthread.h> | |
54 | #include <errno.h> | |
55 | #include <string.h> | |
56 | #include <ctype.h> | |
57 | #include <stdio.h> | |
58 | #include <stdbool.h> | |
59 | #include <libc.h> | |
60 | #include <paths.h> | |
61 | #include <syslog.h> | |
62 | #include <pwd.h> | |
63 | ||
64 | #include <bsm/audit.h> | |
65 | #include <bsm/libbsm.h> | |
66 | ||
67 | #include "bootstrap.h" | |
68 | #include "bootstrap_internal.h" | |
69 | #include "lists.h" | |
70 | #include "launchd.h" | |
71 | ||
72 | /* Mig should produce a declaration for this, but doesn't */ | |
73 | extern boolean_t bootstrap_server(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP); | |
74 | ||
75 | auditinfo_t inherited_audit; | |
76 | mach_port_t inherited_bootstrap_port = MACH_PORT_NULL; | |
77 | bool forward_ok = false; | |
78 | bool debugging = false; | |
79 | bool register_self = false; | |
80 | const char *register_name = NULL; | |
81 | task_t bootstrap_self = MACH_PORT_NULL; | |
82 | ||
83 | static uid_t inherited_uid = 0; | |
84 | static bool shutdown_in_progress = false; | |
85 | ||
86 | #ifndef ASSERT | |
87 | #define ASSERT(p) | |
88 | #endif | |
89 | ||
90 | /* | |
91 | * Private macros | |
92 | */ | |
93 | #define NELEM(x) (sizeof(x)/sizeof(x[0])) | |
94 | #define END_OF(x) (&(x)[NELEM(x)]) | |
95 | #define streq(a,b) (strcmp(a,b) == 0) | |
96 | ||
97 | /* | |
98 | * Private declarations | |
99 | */ | |
100 | static void init_ports(void); | |
101 | static void start_server(server_t *serverp); | |
102 | static void exec_server(server_t *serverp); | |
103 | static char **argvize(const char *string); | |
104 | static void *demand_loop(void *arg); | |
105 | void *mach_server_loop(void *); | |
106 | extern kern_return_t bootstrap_register | |
107 | ( | |
108 | mach_port_t bootstrapport, | |
109 | name_t servicename, | |
110 | mach_port_t serviceport | |
111 | ); | |
112 | ||
113 | /* | |
114 | * Private ports we hold receive rights for. We also hold receive rights | |
115 | * for all the privileged ports. Those are maintained in the server | |
116 | * structs. | |
117 | */ | |
118 | mach_port_t bootstrap_port_set; | |
119 | mach_port_t demand_port_set; | |
120 | pthread_t demand_thread; | |
121 | ||
122 | mach_port_t notify_port; | |
123 | mach_port_t backup_port; | |
124 | ||
125 | ||
126 | static mach_msg_return_t | |
127 | inform_server_loop( | |
128 | mach_port_name_t about, | |
129 | mach_msg_option_t options) | |
130 | { | |
131 | mach_port_destroyed_notification_t not; | |
132 | mach_msg_size_t size = sizeof(not) - sizeof(not.trailer); | |
133 | ||
134 | not.not_header.msgh_id = DEMAND_REQUEST; | |
135 | not.not_header.msgh_remote_port = backup_port; | |
136 | not.not_header.msgh_local_port = MACH_PORT_NULL; | |
137 | not.not_header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0); | |
138 | not.not_header.msgh_size = size; | |
139 | not.not_body.msgh_descriptor_count = 1; | |
140 | not.not_port.type = MACH_MSG_PORT_DESCRIPTOR; | |
141 | not.not_port.disposition = MACH_MSG_TYPE_PORT_NAME; | |
142 | not.not_port.name = about; | |
143 | return mach_msg(¬.not_header, MACH_SEND_MSG|options, size, | |
144 | 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); | |
145 | } | |
146 | ||
147 | static void | |
148 | notify_server_loop(mach_port_name_t about) | |
149 | { | |
150 | mach_msg_return_t result; | |
151 | ||
152 | result = inform_server_loop(about, MACH_MSG_OPTION_NONE); | |
153 | if (result != MACH_MSG_SUCCESS) | |
154 | syslog(LOG_ERR, "notify_server_loop: mach_msg(): %s", mach_error_string(result)); | |
155 | } | |
156 | ||
157 | void mach_start_shutdown(__unused int signalnum) | |
158 | { | |
159 | shutdown_in_progress = TRUE; | |
160 | (void) inform_server_loop(MACH_PORT_NULL, MACH_SEND_TIMEOUT); | |
161 | } | |
162 | ||
163 | mach_port_t mach_init_init(void) | |
164 | { | |
165 | kern_return_t result; | |
166 | pthread_attr_t attr; | |
167 | ||
168 | bootstrap_self = mach_task_self(); | |
169 | inherited_uid = getuid(); | |
170 | getaudit(&inherited_audit); | |
171 | init_lists(); | |
172 | init_ports(); | |
173 | ||
174 | result = task_get_bootstrap_port(bootstrap_self, &inherited_bootstrap_port); | |
175 | if (result != KERN_SUCCESS) { | |
176 | syslog(LOG_ALERT, "task_get_bootstrap_port(): %s", mach_error_string(result)); | |
177 | exit(EXIT_FAILURE); | |
178 | } | |
179 | if (inherited_bootstrap_port == MACH_PORT_NULL) | |
180 | forward_ok = FALSE; | |
181 | ||
182 | /* We set this explicitly as we start each child */ | |
183 | task_set_bootstrap_port(bootstrap_self, MACH_PORT_NULL); | |
184 | ||
185 | /* register "self" port with anscestor */ | |
186 | if (register_self && forward_ok) { | |
187 | result = bootstrap_register(inherited_bootstrap_port, | |
188 | (char *)register_name, | |
189 | bootstraps.bootstrap_port); | |
190 | if (result != KERN_SUCCESS) | |
191 | panic("register self(): %s", mach_error_string(result)); | |
192 | } | |
193 | ||
194 | pthread_attr_init(&attr); | |
195 | pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); | |
196 | result = pthread_create(&demand_thread, &attr, demand_loop, NULL); | |
197 | if (result) | |
198 | panic("pthread_create(): %s", strerror(result)); | |
199 | pthread_attr_destroy(&attr); | |
200 | ||
201 | return bootstraps.bootstrap_port; | |
202 | } | |
203 | ||
204 | static void | |
205 | init_ports(void) | |
206 | { | |
207 | kern_return_t result; | |
208 | ||
209 | /* | |
210 | * This task will become the bootstrap task. | |
211 | */ | |
212 | /* Create port set that server loop listens to */ | |
213 | result = mach_port_allocate( | |
214 | bootstrap_self, | |
215 | MACH_PORT_RIGHT_PORT_SET, | |
216 | &bootstrap_port_set); | |
217 | if (result != KERN_SUCCESS) | |
218 | panic("port_set_allocate(): %s", mach_error_string(result)); | |
219 | ||
220 | /* Create demand port set that second thread listens to */ | |
221 | result = mach_port_allocate( | |
222 | bootstrap_self, | |
223 | MACH_PORT_RIGHT_PORT_SET, | |
224 | &demand_port_set); | |
225 | if (result != KERN_SUCCESS) | |
226 | panic("port_set_allocate(): %s", mach_error_string(result)); | |
227 | ||
228 | /* Create notify port and add to server port set */ | |
229 | result = mach_port_allocate( | |
230 | bootstrap_self, | |
231 | MACH_PORT_RIGHT_RECEIVE, | |
232 | ¬ify_port); | |
233 | if (result != KERN_SUCCESS) | |
234 | panic("mach_port_allocate(): %s", mach_error_string(result)); | |
235 | ||
236 | result = mach_port_move_member( | |
237 | bootstrap_self, | |
238 | notify_port, | |
239 | bootstrap_port_set); | |
240 | if (result != KERN_SUCCESS) | |
241 | panic("mach_port_move_member(): %s", mach_error_string(result)); | |
242 | ||
243 | /* Create backup port and add to server port set */ | |
244 | result = mach_port_allocate( | |
245 | bootstrap_self, | |
246 | MACH_PORT_RIGHT_RECEIVE, | |
247 | &backup_port); | |
248 | if (result != KERN_SUCCESS) | |
249 | panic("mach_port_allocate(): %s", mach_error_string(result)); | |
250 | ||
251 | result = mach_port_move_member( | |
252 | bootstrap_self, | |
253 | backup_port, | |
254 | bootstrap_port_set); | |
255 | if (result != KERN_SUCCESS) | |
256 | panic("mach_port_move_member(): %s", mach_error_string(result)); | |
257 | ||
258 | /* Create "self" port and add to server port set */ | |
259 | result = mach_port_allocate( | |
260 | bootstrap_self, | |
261 | MACH_PORT_RIGHT_RECEIVE, | |
262 | &bootstraps.bootstrap_port); | |
263 | if (result != KERN_SUCCESS) | |
264 | panic("mach_port_allocate(): %s", mach_error_string(result)); | |
265 | result = mach_port_insert_right( | |
266 | bootstrap_self, | |
267 | bootstraps.bootstrap_port, | |
268 | bootstraps.bootstrap_port, | |
269 | MACH_MSG_TYPE_MAKE_SEND); | |
270 | if (result != KERN_SUCCESS) | |
271 | panic("mach_port_insert_right(): %s", mach_error_string(result)); | |
272 | ||
273 | /* keep the root bootstrap port "active" */ | |
274 | bootstraps.requestor_port = bootstraps.bootstrap_port; | |
275 | ||
276 | result = mach_port_move_member( | |
277 | bootstrap_self, | |
278 | bootstraps.bootstrap_port, | |
279 | bootstrap_port_set); | |
280 | if (result != KERN_SUCCESS) | |
281 | panic("mach_port_move_member(): %s", mach_error_string(result)); | |
282 | } | |
283 | ||
284 | boolean_t | |
285 | active_bootstrap(bootstrap_info_t *bootstrap) | |
286 | { | |
287 | return (bootstrap->requestor_port != MACH_PORT_NULL); | |
288 | } | |
289 | ||
290 | boolean_t | |
291 | useless_server(server_t *serverp) | |
292 | { | |
293 | return ( !active_bootstrap(serverp->bootstrap) || | |
294 | !lookup_service_by_server(serverp) || | |
295 | !serverp->activity); | |
296 | } | |
297 | ||
298 | boolean_t | |
299 | active_server(server_t *serverp) | |
300 | { | |
301 | return ( serverp->port || | |
302 | serverp->task_port || serverp->active_services); | |
303 | } | |
304 | ||
305 | static void | |
306 | reap_server(server_t *serverp) | |
307 | { | |
308 | kern_return_t result; | |
309 | pid_t presult; | |
310 | int wstatus; | |
311 | ||
312 | /* | |
313 | * Reap our children. | |
314 | */ | |
315 | presult = waitpid(serverp->pid, &wstatus, WNOHANG); | |
316 | switch (presult) { | |
317 | case -1: | |
318 | syslog(LOG_DEBUG, "waitpid: cmd = %s: %m", serverp->cmd); | |
319 | break; | |
320 | ||
321 | case 0: | |
322 | { | |
323 | /* process must have switched mach tasks */ | |
324 | mach_port_t old_port; | |
325 | ||
326 | old_port = serverp->task_port; | |
327 | mach_port_deallocate(mach_task_self(), old_port); | |
328 | serverp->task_port = MACH_PORT_NULL; | |
329 | ||
330 | result = task_for_pid( mach_task_self(), | |
331 | serverp->pid, | |
332 | &serverp->task_port); | |
333 | if (result != KERN_SUCCESS) { | |
334 | syslog(LOG_INFO, "race getting new server task port for pid[%d]: %s", | |
335 | serverp->pid, mach_error_string(result)); | |
336 | break; | |
337 | } | |
338 | ||
339 | /* Request dead name notification to tell when new task dies */ | |
340 | result = mach_port_request_notification( | |
341 | mach_task_self(), | |
342 | serverp->task_port, | |
343 | MACH_NOTIFY_DEAD_NAME, | |
344 | 0, | |
345 | notify_port, | |
346 | MACH_MSG_TYPE_MAKE_SEND_ONCE, | |
347 | &old_port); | |
348 | if (result != KERN_SUCCESS) { | |
349 | syslog(LOG_INFO, "race setting up notification for new server task port for pid[%d]: %s", | |
350 | serverp->pid, mach_error_string(result)); | |
351 | break; | |
352 | } | |
353 | return; | |
354 | } | |
355 | ||
356 | default: | |
357 | if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus)) { | |
358 | syslog(LOG_NOTICE, "Server %x in bootstrap %x uid %d: \"%s\"[%d]: exited with status: %d", | |
359 | serverp->port, serverp->bootstrap->bootstrap_port, | |
360 | serverp->uid, serverp->cmd, serverp->pid, WEXITSTATUS(wstatus)); | |
361 | } else if (WIFSIGNALED(wstatus)) { | |
362 | syslog(LOG_NOTICE, "Server %x in bootstrap %x uid %d: \"%s\"[%d]: exited abnormally: %s", | |
363 | serverp->port, serverp->bootstrap->bootstrap_port, | |
364 | serverp->uid, serverp->cmd, serverp->pid, strsignal(WTERMSIG(wstatus))); | |
365 | } | |
366 | break; | |
367 | } | |
368 | ||
369 | ||
370 | serverp->pid = 0; | |
371 | ||
372 | /* | |
373 | * Release the server task port reference, if we ever | |
374 | * got it in the first place. | |
375 | */ | |
376 | if (serverp->task_port != MACH_PORT_NULL) { | |
377 | result = mach_port_deallocate( | |
378 | mach_task_self(), | |
379 | serverp->task_port); | |
380 | if (result != KERN_SUCCESS) | |
381 | syslog(LOG_ERR, "mach_port_deallocate(): %s", mach_error_string(result)); | |
382 | serverp->task_port = MACH_PORT_NULL; | |
383 | } | |
384 | } | |
385 | ||
386 | static void | |
387 | demand_server(server_t *serverp) | |
388 | { | |
389 | service_t *servicep; | |
390 | kern_return_t result; | |
391 | ||
392 | /* | |
393 | * For on-demand servers, make sure that the service ports are | |
394 | * back in on-demand portset. Active service ports should come | |
395 | * back through a PORT_DESTROYED notification. We only have to | |
396 | * worry about the inactive ports that may have been previously | |
397 | * pulled from the set but never checked-in by the server. | |
398 | */ | |
399 | ||
400 | for ( servicep = FIRST(services) | |
401 | ; !IS_END(servicep, services) | |
402 | ; servicep = NEXT(servicep)) | |
403 | { | |
404 | if (serverp == servicep->server && !servicep->isActive) { | |
405 | result = mach_port_move_member( | |
406 | mach_task_self(), | |
407 | servicep->port, | |
408 | demand_port_set); | |
409 | if (result != KERN_SUCCESS) | |
410 | panic("mach_port_move_member(): %s", mach_error_string(result)); | |
411 | } | |
412 | } | |
413 | } | |
414 | ||
415 | static | |
416 | void dispatch_server(server_t *serverp) | |
417 | { | |
418 | if (!active_server(serverp)) { | |
419 | if (useless_server(serverp) || shutdown_in_progress) | |
420 | delete_server(serverp); | |
421 | else if (serverp->servertype == RESTARTABLE) | |
422 | start_server(serverp); | |
423 | else if (serverp->servertype == DEMAND) | |
424 | demand_server(serverp); | |
425 | } | |
426 | } | |
427 | ||
428 | void | |
429 | setup_server(server_t *serverp) | |
430 | { | |
431 | kern_return_t result; | |
432 | mach_port_t old_port; | |
433 | ||
434 | /* Allocate privileged port for requests from service */ | |
435 | result = mach_port_allocate(mach_task_self(), | |
436 | MACH_PORT_RIGHT_RECEIVE , | |
437 | &serverp->port); | |
438 | syslog(LOG_INFO, "Allocating port %x for server %s", serverp->port, serverp->cmd); | |
439 | if (result != KERN_SUCCESS) | |
440 | panic("port_allocate(): %s", mach_error_string(result)); | |
441 | ||
442 | /* Request no-senders notification so we can tell when server dies */ | |
443 | result = mach_port_request_notification(mach_task_self(), | |
444 | serverp->port, | |
445 | MACH_NOTIFY_NO_SENDERS, | |
446 | 1, | |
447 | serverp->port, | |
448 | MACH_MSG_TYPE_MAKE_SEND_ONCE, | |
449 | &old_port); | |
450 | if (result != KERN_SUCCESS) | |
451 | panic("mach_port_request_notification(): %s", mach_error_string(result)); | |
452 | ||
453 | /* Add privileged server port to bootstrap port set */ | |
454 | result = mach_port_move_member(mach_task_self(), | |
455 | serverp->port, | |
456 | bootstrap_port_set); | |
457 | if (result != KERN_SUCCESS) | |
458 | panic("mach_port_move_member(): %s", mach_error_string(result)); | |
459 | } | |
460 | ||
461 | pid_t | |
462 | fork_with_bootstrap_port(mach_port_t p) | |
463 | { | |
464 | static pthread_mutex_t forklock = PTHREAD_MUTEX_INITIALIZER; | |
465 | kern_return_t result; | |
466 | pid_t r; | |
467 | size_t i; | |
468 | ||
469 | pthread_mutex_lock(&forklock); | |
470 | ||
471 | sigprocmask(SIG_BLOCK, &blocked_signals, NULL); | |
472 | ||
473 | result = task_set_bootstrap_port(mach_task_self(), p); | |
474 | if (result != KERN_SUCCESS) | |
475 | panic("task_set_bootstrap_port(): %s", mach_error_string(result)); | |
476 | ||
477 | if (launchd_bootstrap_port != p) { | |
478 | result = mach_port_deallocate(mach_task_self(), p); | |
479 | if (result != KERN_SUCCESS) | |
480 | panic("mach_port_deallocate(): %s", mach_error_string(result)); | |
481 | } | |
482 | ||
483 | r = fork(); | |
484 | ||
485 | if (r > 0) { | |
486 | /* Post Tiger: | |
487 | * | |
488 | * We should set the bootstrap back to MACH_PORT_NULL instead | |
489 | * of launchd_bootstrap_port. This will expose rare latent race | |
490 | * condition bugs, given that some programs assume that the PID | |
491 | * 1's bootstrap port is constant. This function clearly | |
492 | * demonstrates that is no longer true. | |
493 | * | |
494 | * Those programs should be calling bootstrap_parent(), and not | |
495 | * task_for_pid(1) followed by a call to get the bootstrap port | |
496 | * on the task. | |
497 | */ | |
498 | result = task_set_bootstrap_port(mach_task_self(), launchd_bootstrap_port); | |
499 | if (result != KERN_SUCCESS) | |
500 | panic("task_set_bootstrap_port(): %s", mach_error_string(result)); | |
501 | } else { | |
502 | for (i = 0; i <= NSIG; i++) { | |
503 | if (sigismember(&blocked_signals, i)) | |
504 | signal(i, SIG_DFL); | |
505 | } | |
506 | } | |
507 | ||
508 | sigprocmask(SIG_UNBLOCK, &blocked_signals, NULL); | |
509 | ||
510 | pthread_mutex_unlock(&forklock); | |
511 | ||
512 | return r; | |
513 | } | |
514 | ||
515 | static void | |
516 | start_server(server_t *serverp) | |
517 | { | |
518 | kern_return_t result; | |
519 | mach_port_t old_port; | |
520 | int pid; | |
521 | ||
522 | /* | |
523 | * Do what's appropriate to get bootstrap port setup in server task | |
524 | */ | |
525 | switch (serverp->servertype) { | |
526 | ||
527 | case MACHINIT: | |
528 | break; | |
529 | ||
530 | case SERVER: | |
531 | case DEMAND: | |
532 | case RESTARTABLE: | |
533 | if (!serverp->port) | |
534 | setup_server(serverp); | |
535 | ||
536 | serverp->activity = 0; | |
537 | ||
538 | /* Insert a send right */ | |
539 | result = mach_port_insert_right(mach_task_self(), | |
540 | serverp->port, | |
541 | serverp->port, | |
542 | MACH_MSG_TYPE_MAKE_SEND); | |
543 | if (result != KERN_SUCCESS) | |
544 | panic("mach_port_insert_right(): %s", mach_error_string(result)); | |
545 | ||
546 | pid = fork_with_bootstrap_port(serverp->port); | |
547 | if (pid < 0) { | |
548 | syslog(LOG_WARNING, "fork(): %m"); | |
549 | } else if (pid == 0) { /* CHILD */ | |
550 | exec_server(serverp); | |
551 | exit(EXIT_FAILURE); | |
552 | } else { /* PARENT */ | |
553 | syslog(LOG_INFO, "Launched server %x in bootstrap %x uid %d: \"%s\": [pid %d]", | |
554 | serverp->port, serverp->bootstrap->bootstrap_port, | |
555 | serverp->uid, serverp->cmd, pid); | |
556 | serverp->pid = pid; | |
557 | result = task_for_pid( | |
558 | mach_task_self(), | |
559 | pid, | |
560 | &serverp->task_port); | |
561 | if (result != KERN_SUCCESS) { | |
562 | syslog(LOG_ERR, "getting server task port(): %s", mach_error_string(result)); | |
563 | reap_server(serverp); | |
564 | dispatch_server(serverp); | |
565 | break; | |
566 | } | |
567 | ||
568 | /* Request dead name notification to tell when task dies */ | |
569 | result = mach_port_request_notification( | |
570 | mach_task_self(), | |
571 | serverp->task_port, | |
572 | MACH_NOTIFY_DEAD_NAME, | |
573 | 0, | |
574 | notify_port, | |
575 | MACH_MSG_TYPE_MAKE_SEND_ONCE, | |
576 | &old_port); | |
577 | if (result != KERN_SUCCESS) { | |
578 | syslog(LOG_ERR, "mach_port_request_notification(): %s", mach_error_string(result)); | |
579 | reap_server(serverp); | |
580 | dispatch_server(serverp); | |
581 | } | |
582 | } | |
583 | break; | |
584 | } | |
585 | } | |
586 | ||
587 | static void | |
588 | exec_server(server_t *serverp) | |
589 | { | |
590 | char **argv; | |
591 | sigset_t mask; | |
592 | ||
593 | /* | |
594 | * Setup environment for server, someday this should be Mach stuff | |
595 | * rather than Unix crud | |
596 | */ | |
597 | argv = argvize(serverp->cmd); | |
598 | closelog(); | |
599 | ||
600 | /* | |
601 | * Set up the audit state for the user (if necessesary). | |
602 | */ | |
603 | if (inherited_uid == 0 && | |
604 | (serverp->auinfo.ai_auid != inherited_uid || | |
605 | serverp->auinfo.ai_asid != inherited_audit.ai_asid)) { | |
606 | struct passwd *pwd = NULL; | |
607 | ||
608 | pwd = getpwuid(serverp->auinfo.ai_auid); | |
609 | if (pwd == NULL) { | |
610 | panic("Disabled server %x bootstrap %x: \"%s\": getpwuid(%d) failed", | |
611 | serverp->port, serverp->bootstrap->bootstrap_port, | |
612 | serverp->cmd, serverp->auinfo.ai_auid); | |
613 | ||
614 | } else if (au_user_mask(pwd->pw_name, &serverp->auinfo.ai_mask) != 0) { | |
615 | panic("Disabled server %x bootstrap %x: \"%s\": au_user_mask(%s) failed", | |
616 | serverp->port, serverp->bootstrap->bootstrap_port, | |
617 | serverp->cmd, pwd->pw_name); | |
618 | } else if (setaudit(&serverp->auinfo) != 0) | |
619 | panic("Disabled server %x bootstrap %x: \"%s\": setaudit()", | |
620 | serverp->port, serverp->bootstrap->bootstrap_port, | |
621 | serverp->cmd); | |
622 | } | |
623 | ||
aa59983a A |
624 | if (serverp->uid != inherited_uid) { |
625 | struct passwd *pwd = getpwuid(serverp->uid); | |
626 | gid_t g; | |
627 | ||
628 | if (NULL == pwd) { | |
629 | panic("Disabled server %x bootstrap %x: \"%s\": getpwuid(%d) failed", | |
630 | serverp->port, serverp->bootstrap->bootstrap_port, | |
631 | serverp->cmd, serverp->uid); | |
632 | } | |
633 | ||
634 | g = pwd->pw_gid; | |
635 | ||
636 | if (-1 == setgroups(1, &g)) { | |
637 | panic("Disabled server %x bootstrap %x: \"%s\": setgroups(1, %d): %s", | |
638 | serverp->port, serverp->bootstrap->bootstrap_port, | |
639 | serverp->cmd, g, strerror(errno)); | |
640 | } | |
641 | ||
642 | if (-1 == setgid(g)) { | |
643 | panic("Disabled server %x bootstrap %x: \"%s\": setgid(%d): %s", | |
644 | serverp->port, serverp->bootstrap->bootstrap_port, | |
645 | serverp->cmd, g, strerror(errno)); | |
646 | } | |
647 | ||
648 | if (-1 == setuid(serverp->uid)) { | |
e91b9f68 A |
649 | panic("Disabled server %x bootstrap %x: \"%s\": setuid(%d): %s", |
650 | serverp->port, serverp->bootstrap->bootstrap_port, | |
651 | serverp->cmd, serverp->uid, strerror(errno)); | |
aa59983a A |
652 | } |
653 | } | |
654 | ||
e91b9f68 A |
655 | |
656 | if (setsid() < 0) { | |
657 | /* | |
658 | * We can't keep this from happening, but we shouldn't start | |
659 | * the server not as a process group leader. So, just fake like | |
660 | * there was real activity, and exit the child. If needed, | |
661 | * we'll re-launch it under another pid. | |
662 | */ | |
663 | serverp->activity = 1; | |
664 | panic("Temporary failure server %x bootstrap %x: \"%s\": setsid(): %s", | |
665 | serverp->port, serverp->bootstrap->bootstrap_port, | |
666 | serverp->cmd, strerror(errno)); | |
667 | } | |
668 | ||
669 | sigemptyset(&mask); | |
670 | (void) sigprocmask(SIG_SETMASK, &mask, (sigset_t *)NULL); | |
671 | ||
672 | setpriority(PRIO_PROCESS, 0, 0); | |
673 | execv(argv[0], argv); | |
674 | panic("Disabled server %x bootstrap %x: \"%s\": exec(): %s", | |
675 | serverp->port, | |
676 | serverp->bootstrap->bootstrap_port, | |
677 | serverp->cmd, | |
678 | strerror(errno)); | |
679 | } | |
680 | ||
681 | static char ** | |
682 | argvize(const char *string) | |
683 | { | |
684 | static char *argv[100], args[1000]; | |
685 | const char *cp; | |
686 | char *argp, term; | |
687 | unsigned int nargs; | |
688 | ||
689 | /* | |
690 | * Convert a command line into an argv for execv | |
691 | */ | |
692 | nargs = 0; | |
693 | argp = args; | |
694 | ||
695 | for (cp = string; *cp;) { | |
696 | while (isspace(*cp)) | |
697 | cp++; | |
698 | term = (*cp == '"') ? *cp++ : '\0'; | |
699 | if (nargs < NELEM(argv)) | |
700 | argv[nargs++] = argp; | |
701 | while (*cp && (term ? *cp != term : !isspace(*cp)) | |
702 | && argp < END_OF(args)) { | |
703 | if (*cp == '\\') | |
704 | cp++; | |
705 | *argp++ = *cp; | |
706 | if (*cp) | |
707 | cp++; | |
708 | } | |
709 | *argp++ = '\0'; | |
710 | } | |
711 | argv[nargs] = NULL; | |
712 | return argv; | |
713 | } | |
714 | ||
715 | static void * | |
716 | demand_loop(void *arg __attribute__((unused))) | |
717 | { | |
718 | mach_msg_empty_rcv_t dummy; | |
719 | kern_return_t dresult; | |
720 | ||
721 | ||
722 | for(;;) { | |
723 | mach_port_name_array_t members; | |
724 | mach_msg_type_number_t membersCnt; | |
725 | mach_port_status_t status; | |
726 | mach_msg_type_number_t statusCnt; | |
727 | unsigned int i; | |
728 | ||
729 | /* | |
730 | * Receive indication of message on demand service | |
731 | * ports without actually receiving the message (we'll | |
732 | * let the actual server do that. | |
733 | */ | |
734 | dresult = mach_msg( | |
735 | &dummy.header, | |
736 | MACH_RCV_MSG|MACH_RCV_LARGE, | |
737 | 0, | |
738 | 0, | |
739 | demand_port_set, | |
740 | 0, | |
741 | MACH_PORT_NULL); | |
742 | if (dresult != MACH_RCV_TOO_LARGE) { | |
743 | syslog(LOG_ERR, "demand_loop: mach_msg(): %s", mach_error_string(dresult)); | |
744 | continue; | |
745 | } | |
746 | ||
747 | /* | |
748 | * Some port(s) now have messages on them, find out | |
749 | * which ones (there is no indication of which port | |
750 | * triggered in the MACH_RCV_TOO_LARGE indication). | |
751 | */ | |
752 | dresult = mach_port_get_set_status( | |
753 | mach_task_self(), | |
754 | demand_port_set, | |
755 | &members, | |
756 | &membersCnt); | |
757 | if (dresult != KERN_SUCCESS) { | |
758 | syslog(LOG_ERR, "demand_loop: mach_port_get_set_status(): %s", mach_error_string(dresult)); | |
759 | continue; | |
760 | } | |
761 | ||
762 | for (i = 0; i < membersCnt; i++) { | |
763 | statusCnt = MACH_PORT_RECEIVE_STATUS_COUNT; | |
764 | dresult = mach_port_get_attributes( | |
765 | mach_task_self(), | |
766 | members[i], | |
767 | MACH_PORT_RECEIVE_STATUS, | |
768 | (mach_port_info_t)&status, | |
769 | &statusCnt); | |
770 | if (dresult != KERN_SUCCESS) { | |
771 | syslog(LOG_ERR, "demand_loop: mach_port_get_attributes(): %s", mach_error_string(dresult)); | |
772 | continue; | |
773 | } | |
774 | ||
775 | /* | |
776 | * For each port with messages, take it out of the | |
777 | * demand service portset, and inform the main thread | |
778 | * that it might have to start the server responsible | |
779 | * for it. | |
780 | */ | |
781 | if (status.mps_msgcount) { | |
782 | dresult = mach_port_move_member( | |
783 | mach_task_self(), | |
784 | members[i], | |
785 | MACH_PORT_NULL); | |
786 | if (dresult != KERN_SUCCESS) { | |
787 | syslog(LOG_ERR, "demand_loop: mach_port_move_member(): %s", mach_error_string(dresult)); | |
788 | continue; | |
789 | } | |
790 | notify_server_loop(members[i]); | |
791 | } | |
792 | } | |
793 | ||
794 | dresult = vm_deallocate( | |
795 | mach_task_self(), | |
796 | (vm_address_t) members, | |
797 | (vm_size_t) membersCnt * sizeof(mach_port_name_t)); | |
798 | if (dresult != KERN_SUCCESS) { | |
799 | syslog(LOG_ERR, "demand_loop: vm_deallocate(): %s", mach_error_string(dresult)); | |
800 | continue; | |
801 | } | |
802 | } | |
803 | return NULL; | |
804 | } | |
805 | ||
806 | /* | |
807 | * server_demux -- processes requests off our service port | |
808 | * Also handles notifications | |
809 | */ | |
810 | ||
811 | static boolean_t | |
812 | server_demux( | |
813 | mach_msg_header_t *Request, | |
814 | mach_msg_header_t *Reply) | |
815 | { | |
816 | bootstrap_info_t *bootstrap; | |
817 | service_t *servicep; | |
818 | server_t *serverp; | |
819 | kern_return_t result; | |
820 | mig_reply_error_t *reply; | |
821 | ||
822 | syslog(LOG_DEBUG, "received message on port %x", Request->msgh_local_port); | |
823 | ||
824 | reply = (mig_reply_error_t *)Reply; | |
825 | ||
826 | /* | |
827 | * Pick off notification messages | |
828 | */ | |
829 | if (Request->msgh_local_port == notify_port) { | |
830 | mach_port_name_t np; | |
831 | ||
832 | memset(reply, 0, sizeof(*reply)); | |
833 | switch (Request->msgh_id) { | |
834 | case MACH_NOTIFY_DEAD_NAME: | |
835 | np = ((mach_dead_name_notification_t *)Request)->not_port; | |
836 | syslog(LOG_DEBUG, "Notified dead name %x", np); | |
837 | ||
838 | if (np == inherited_bootstrap_port) { | |
839 | inherited_bootstrap_port = MACH_PORT_NULL; | |
840 | forward_ok = FALSE; | |
841 | } | |
842 | ||
843 | /* | |
844 | * Check to see if a subset requestor port was deleted. | |
845 | */ | |
846 | while ((bootstrap = lookup_bootstrap_by_req_port(np)) != NULL) { | |
847 | syslog(LOG_DEBUG, "Received dead name notification for bootstrap subset %x requestor port %x", | |
848 | bootstrap->bootstrap_port, bootstrap->requestor_port); | |
849 | mach_port_deallocate( | |
850 | mach_task_self(), | |
851 | bootstrap->requestor_port); | |
852 | bootstrap->requestor_port = MACH_PORT_NULL; | |
853 | deactivate_bootstrap(bootstrap); | |
854 | } | |
855 | ||
856 | /* | |
857 | * Check to see if a defined service has gone | |
858 | * away. | |
859 | */ | |
860 | while ((servicep = lookup_service_by_port(np)) != NULL) { | |
861 | /* | |
862 | * Port gone, registered service died. | |
863 | */ | |
864 | syslog(LOG_DEBUG, "Received dead name notification for service %s " | |
865 | "on bootstrap port %x\n", | |
866 | servicep->name, servicep->bootstrap); | |
867 | syslog(LOG_DEBUG, "Service %s failed - deallocate", servicep->name); | |
868 | delete_service(servicep); | |
869 | } | |
870 | ||
871 | /* | |
872 | * Check to see if a launched server task has gone | |
873 | * away. | |
874 | */ | |
875 | if ((serverp = lookup_server_by_task_port(np)) != NULL) { | |
876 | /* | |
877 | * Port gone, server died or picked up new task. | |
878 | */ | |
879 | syslog(LOG_DEBUG, "Received task death notification for server %s ", | |
880 | serverp->cmd); | |
881 | reap_server(serverp); | |
882 | dispatch_server(serverp); | |
883 | } | |
884 | ||
885 | mach_port_deallocate(mach_task_self(), np); | |
886 | reply->RetCode = KERN_SUCCESS; | |
887 | break; | |
888 | ||
889 | case MACH_NOTIFY_PORT_DELETED: | |
890 | np = ((mach_port_deleted_notification_t *)Request)->not_port; | |
891 | syslog(LOG_DEBUG, "port deleted notification on 0x%x", np); | |
892 | reply->RetCode = KERN_SUCCESS; | |
893 | break; | |
894 | ||
895 | case MACH_NOTIFY_SEND_ONCE: | |
896 | syslog(LOG_DEBUG, "notification send-once right went unused"); | |
897 | reply->RetCode = KERN_SUCCESS; | |
898 | break; | |
899 | ||
900 | default: | |
901 | syslog(LOG_ERR, "Unexpected notification: %d", Request->msgh_id); | |
902 | reply->RetCode = KERN_FAILURE; | |
903 | break; | |
904 | } | |
905 | } | |
906 | ||
907 | else if (Request->msgh_local_port == backup_port) { | |
908 | mach_port_name_t np; | |
909 | ||
910 | memset(reply, 0, sizeof(*reply)); | |
911 | ||
912 | np = ((mach_port_destroyed_notification_t *)Request)->not_port.name; | |
913 | servicep = lookup_service_by_port(np); | |
914 | if (servicep != NULL) { | |
915 | serverp = servicep->server; | |
916 | ||
917 | switch (Request->msgh_id) { | |
918 | ||
919 | case MACH_NOTIFY_PORT_DESTROYED: | |
920 | /* | |
921 | * Port sent back to us, server died. | |
922 | */ | |
923 | syslog(LOG_DEBUG, "Received destroyed notification for service %s", | |
924 | servicep->name); | |
925 | syslog(LOG_DEBUG, "Service %x bootstrap %x backed up: %s", | |
926 | servicep->port, servicep->bootstrap->bootstrap_port, | |
927 | servicep->name); | |
928 | ASSERT(canReceive(servicep->port)); | |
929 | servicep->isActive = FALSE; | |
930 | serverp->active_services--; | |
931 | dispatch_server(serverp); | |
932 | reply->RetCode = KERN_SUCCESS; | |
933 | break; | |
934 | ||
935 | case DEMAND_REQUEST: | |
936 | /* message reflected over from demand start thread */ | |
937 | if (!active_server(serverp)) | |
938 | start_server(serverp); | |
939 | reply->RetCode = KERN_SUCCESS; | |
940 | break; | |
941 | ||
942 | default: | |
943 | syslog(LOG_DEBUG, "Mysterious backup_port notification %d", Request->msgh_id); | |
944 | reply->RetCode = KERN_FAILURE; | |
945 | break; | |
946 | } | |
947 | } else { | |
948 | syslog(LOG_DEBUG, "Backup_port notification - previously deleted service"); | |
949 | reply->RetCode = KERN_FAILURE; | |
950 | } | |
951 | } | |
952 | ||
953 | else if (Request->msgh_id == MACH_NOTIFY_NO_SENDERS) { | |
954 | mach_port_t ns = Request->msgh_local_port; | |
955 | ||
956 | if ((serverp = lookup_server_by_port(ns)) != NULL_SERVER) { | |
957 | /* | |
958 | * A server we launched has released his bootstrap | |
959 | * port send right. We won't re-launch him unless | |
960 | * his services came back to roost. But we need to | |
961 | * destroy the bootstrap port for fear of leaking. | |
962 | */ | |
963 | syslog(LOG_DEBUG, "server %s dropped server port", serverp->cmd); | |
964 | serverp->port = MACH_PORT_NULL; | |
965 | dispatch_server(serverp); | |
966 | } else if ((bootstrap = lookup_bootstrap_by_port(ns)) != NULL) { | |
967 | /* | |
968 | * The last direct user of a deactivated bootstrap went away. | |
969 | * We can finally free it. | |
970 | */ | |
971 | syslog(LOG_DEBUG, "Deallocating bootstrap %x: no more clients", ns); | |
972 | bootstrap->bootstrap_port = MACH_PORT_NULL; | |
973 | deallocate_bootstrap(bootstrap); | |
974 | } | |
975 | ||
976 | result = mach_port_mod_refs( | |
977 | mach_task_self(), | |
978 | ns, | |
979 | MACH_PORT_RIGHT_RECEIVE, | |
980 | -1); | |
981 | if (result != KERN_SUCCESS) | |
982 | panic("mach_port_mod_refs(): %s", mach_error_string(result)); | |
983 | ||
984 | memset(reply, 0, sizeof(*reply)); | |
985 | reply->RetCode = KERN_SUCCESS; | |
986 | } | |
987 | ||
988 | else { /* must be a service request */ | |
989 | syslog(LOG_DEBUG, "Handled request."); | |
990 | return bootstrap_server(Request, Reply); | |
991 | } | |
992 | return TRUE; | |
993 | } | |
994 | ||
995 | /* | |
996 | * server_loop -- pick requests off our service port and process them | |
997 | * Also handles notifications | |
998 | */ | |
999 | #define bootstrapMaxRequestSize 1024 | |
1000 | #define bootstrapMaxReplySize 1024 | |
1001 | ||
1002 | void * | |
1003 | mach_server_loop(void *arg __attribute__((unused))) | |
1004 | { | |
1005 | mach_msg_return_t mresult; | |
1006 | ||
1007 | for (;;) { | |
1008 | mresult = mach_msg_server( | |
1009 | server_demux, | |
1010 | bootstrapMaxRequestSize, | |
1011 | bootstrap_port_set, | |
1012 | MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_SENDER)| | |
1013 | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0)); | |
1014 | if (mresult != MACH_MSG_SUCCESS) | |
1015 | syslog(LOG_ERR, "mach_msg_server(): %s", mach_error_string(mresult)); | |
1016 | } | |
1017 | return NULL; | |
1018 | } | |
1019 | ||
1020 | bool | |
1021 | canReceive(mach_port_t port) | |
1022 | { | |
1023 | mach_port_type_t p_type; | |
1024 | kern_return_t result; | |
1025 | ||
1026 | result = mach_port_type(mach_task_self(), port, &p_type); | |
1027 | if (result != KERN_SUCCESS) { | |
1028 | syslog(LOG_ERR, "port_type(): %s", mach_error_string(result)); | |
1029 | return FALSE; | |
1030 | } | |
1031 | return ((p_type & MACH_PORT_TYPE_RECEIVE) != 0); | |
1032 | } | |
1033 | ||
1034 | ||
1035 | bool | |
1036 | canSend(mach_port_t port) | |
1037 | { | |
1038 | mach_port_type_t p_type; | |
1039 | kern_return_t result; | |
1040 | ||
1041 | result = mach_port_type(mach_task_self(), port, &p_type); | |
1042 | if (result != KERN_SUCCESS) { | |
1043 | syslog(LOG_ERR, "port_type(): %s", mach_error_string(result)); | |
1044 | return FALSE; | |
1045 | } | |
1046 | return ((p_type & MACH_PORT_TYPE_PORT_RIGHTS) != 0); | |
1047 | } |