]> git.saurik.com Git - apple/system_cmds.git/blob - mach_init.tproj/bootstrap.c
15d09a1888ae9cc82d4745b2d8168a2f157c78fa
[apple/system_cmds.git] / mach_init.tproj / bootstrap.c
1 /*
2 * Copyright (c) 1999-2003 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * file.
14 *
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25 /*
26 * bootstrap -- fundamental service initiator and port server
27 * Mike DeMoney, NeXT, Inc.
28 * Copyright, 1990. All rights reserved.
29 *
30 * bootstrap.c -- implementation of bootstrap main service loop
31 */
32
33 /*
34 * Imports
35 */
36 #import <mach/mach.h>
37 #import <mach/boolean.h>
38 #import <mach/message.h>
39 #import <mach/notify.h>
40 #import <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 #import <sys/ioctl.h>
49 #import <sys/types.h>
50 #import <sys/time.h>
51 #import <sys/resource.h>
52 #import <sys/wait.h>
53 #import <pthread.h>
54 #import <string.h>
55 #import <ctype.h>
56 #import <stdio.h>
57 #import <libc.h>
58 #import <paths.h>
59
60 #import "bootstrap.h"
61
62 #import "bootstrap_internal.h"
63 #import "lists.h"
64 #import "error_log.h"
65
66 /* Mig should produce a declaration for this, but doesn't */
67 extern boolean_t bootstrap_server(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP);
68
69 /*
70 * Exports
71 */
72 const char *program_name; /* our name for error messages */
73
74 #ifndef INIT_PATH
75 #define INIT_PATH "/sbin/init" /* default init path */
76 #endif INIT_PATH
77
78 uid_t inherited_uid;
79 mach_port_t inherited_bootstrap_port = MACH_PORT_NULL;
80 boolean_t forward_ok = FALSE;
81 boolean_t shutdown_in_progress = FALSE;
82 boolean_t debugging = FALSE;
83 boolean_t register_self = FALSE;
84 boolean_t force_fork = FALSE;
85 const char *register_name;
86 task_t bootstrap_self;
87
88 #ifndef ASSERT
89 #define ASSERT(p)
90 #endif
91
92 /*
93 * Private macros
94 */
95 #define NELEM(x) (sizeof(x)/sizeof(x[0]))
96 #define END_OF(x) (&(x)[NELEM(x)])
97 #define streq(a,b) (strcmp(a,b) == 0)
98
99 /*
100 * Private declarations
101 */
102 static void wait_for_go(mach_port_t init_notify_port);
103 static void init_ports(void);
104 static void start_server(server_t *serverp);
105 static void unblock_init(mach_port_t init_notify_port, mach_port_t newBootstrap);
106 static void exec_server(server_t *serverp);
107 static char **argvize(const char *string);
108 static void *demand_loop(void *arg);
109 static void server_loop(void);
110 extern kern_return_t bootstrap_register
111 (
112 mach_port_t bootstrap_port,
113 name_t service_name,
114 mach_port_t service_port
115 );
116
117 /*
118 * Private ports we hold receive rights for. We also hold receive rights
119 * for all the privileged ports. Those are maintained in the server
120 * structs.
121 */
122 mach_port_t bootstrap_port_set;
123 mach_port_t demand_port_set;
124 pthread_t demand_thread;
125
126 mach_port_t notify_port;
127 mach_port_t backup_port;
128
129
130 static void
131 enablecoredumps(boolean_t enabled)
132 {
133 struct rlimit rlimit;
134
135 getrlimit(RLIMIT_CORE, &rlimit);
136 rlimit.rlim_cur = (enabled) ? rlimit.rlim_max : 0;
137 setrlimit(RLIMIT_CORE, &rlimit);
138 }
139
140 static void
141 toggle_debug(int signal)
142 {
143
144 debugging = (debugging) ? FALSE : TRUE;
145 enablecoredumps(debugging);
146 }
147
148 static mach_msg_return_t
149 inform_server_loop(
150 mach_port_name_t about,
151 mach_msg_option_t options)
152 {
153 mach_port_destroyed_notification_t not;
154 mach_msg_size_t size = sizeof(not) - sizeof(not.trailer);
155
156 not.not_header.msgh_id = DEMAND_REQUEST;
157 not.not_header.msgh_remote_port = backup_port;
158 not.not_header.msgh_local_port = MACH_PORT_NULL;
159 not.not_header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
160 not.not_header.msgh_size = size;
161 not.not_body.msgh_descriptor_count = 1;
162 not.not_port.type = MACH_MSG_PORT_DESCRIPTOR;
163 not.not_port.disposition = MACH_MSG_TYPE_PORT_NAME;
164 not.not_port.name = about;
165 return mach_msg(&not.not_header, MACH_SEND_MSG|options, size,
166 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
167 }
168
169 static void
170 notify_server_loop(mach_port_name_t about)
171 {
172 mach_msg_return_t result;
173
174 result = inform_server_loop(about, MACH_MSG_OPTION_NONE);
175 if (result != MACH_MSG_SUCCESS)
176 kern_error(result, "notify_server_loop: mach_msg()");
177 }
178
179 void start_shutdown(int signal)
180 {
181 shutdown_in_progress = TRUE;
182 (void) inform_server_loop(MACH_PORT_NULL, MACH_SEND_TIMEOUT);
183 }
184
185 int
186 main(int argc, char * argv[])
187 {
188 const char *argp;
189 char c;
190 kern_return_t result;
191 mach_port_t init_notify_port;
192 pthread_attr_t attr;
193 sigset_t mask;
194 int pid;
195
196 /*
197 * If we are pid one, we have to exec init. Before doing so, we'll
198 * fork a child, and that will become the true mach_init. But we have
199 * to be very careful about ports. They aren't inherited across fork,
200 * so we have to avoid storing port names in memory before the fork that
201 * might not be valid after.
202 */
203 pid = getpid();
204 if (pid == 1)
205 {
206 close(0);
207 freopen("/dev/console", "r", stdin);
208 setbuf(stdin, NULL);
209 close(1);
210 freopen("/dev/console", "w", stdout);
211 setbuf(stdout, NULL);
212 close(2);
213 freopen("/dev/console", "w", stderr);
214 setbuf(stderr, NULL);
215
216 result = mach_port_allocate(
217 mach_task_self(),
218 MACH_PORT_RIGHT_RECEIVE,
219 &init_notify_port);
220 if (result != KERN_SUCCESS)
221 kern_fatal(result, "mach_port_allocate");
222
223 result = mach_port_insert_right(
224 mach_task_self(),
225 init_notify_port,
226 init_notify_port,
227 MACH_MSG_TYPE_MAKE_SEND);
228 if (result != KERN_SUCCESS)
229 kern_fatal(result, "mach_port_insert_right");
230
231 result = task_set_bootstrap_port(
232 mach_task_self(),
233 init_notify_port);
234 if (result != KERN_SUCCESS)
235 kern_fatal(result, "task_set_bootstrap_port");
236
237 pid = fork();
238
239 if (pid < 0)
240 unix_fatal("fork");
241
242 else if (pid != 0) { /* PARENT - will become init when ready */
243 int fd;
244
245 /*
246 * Wait for mach_init ot give us a real bootstrap port
247 */
248 wait_for_go(init_notify_port);
249
250 close(0);
251 close(1);
252 close(2);
253 fd = open("/dev/tty", O_RDONLY);
254 if (fd >= 0) {
255 ioctl(fd, TIOCNOTTY, 0);
256 close(fd);
257 }
258
259 /* pass our arguments on to init */
260 argv[0] = INIT_PATH;
261 execv(argv[0], argv);
262 exit(1); /* will likely trigger a panic */
263
264 }
265
266 /*
267 * Child - will continue along as mach_init. Save off
268 * the init_notify_port and put back a NULL bootstrap
269 * port for ourselves.
270 */
271 init_notify_port = bootstrap_port;
272 bootstrap_port = MACH_PORT_NULL;
273 (void)task_set_bootstrap_port(
274 mach_task_self(),
275 bootstrap_port);
276 if (result != KERN_SUCCESS)
277 kern_fatal(result, "task_get_bootstrap_port");
278 } else
279 init_notify_port = MACH_PORT_NULL;
280
281 /* Initialize error handling */
282 program_name = rindex(*argv, '/');
283 if (program_name)
284 program_name++;
285 else
286 program_name = *argv;
287 argv++; argc--;
288
289 /* Parse command line args */
290 while (argc > 0 && **argv == '-') {
291 argp = *argv++ + 1; argc--;
292 while (*argp) {
293 switch (c = *argp++) {
294 case 'd':
295 debugging = TRUE;
296 break;
297 case 'D':
298 debugging = FALSE;
299 break;
300 case 'F':
301 if (init_notify_port != MACH_PORT_NULL)
302 force_fork = TRUE;
303 break;
304 case 'r':
305 register_self = forward_ok = TRUE;
306 if (argc > 0) {
307 register_name = *argv++; argc--;
308 } else
309 fatal("-r requires name");
310 break;
311 case '-':
312 default:
313 break;
314 }
315 }
316 }
317
318 /*
319 * If we must fork, do it now before we get Mach ports in use
320 */
321 if (force_fork) {
322 pid = fork();
323 if (pid < 0)
324 unix_fatal("fork");
325 else if (pid != 0) /* PARENT: just exit */
326 exit(0);
327 }
328
329 /*
330 * This task will become the bootstrap task, initialize the ports.
331 */
332 bootstrap_self = mach_task_self();
333 inherited_uid = getuid();
334 init_lists();
335 init_ports();
336
337 if (init_notify_port != MACH_PORT_NULL) {
338 /* send init a real bootstrap port to use */
339 unblock_init(init_notify_port, bootstraps.bootstrap_port);
340
341 result = mach_port_deallocate(
342 bootstrap_self,
343 init_notify_port);
344 if (result != KERN_SUCCESS)
345 kern_fatal(result, "mach_port_deallocate");
346
347 forward_ok = FALSE;
348 inherited_bootstrap_port = MACH_PORT_NULL;
349
350 } else {
351 /* get inherited bootstrap port */
352 result = task_get_bootstrap_port(
353 bootstrap_self,
354 &inherited_bootstrap_port);
355 if (result != KERN_SUCCESS)
356 kern_fatal(result, "task_get_bootstrap_port");
357
358 /* We set this explicitly as we start each child */
359 task_set_bootstrap_port(bootstrap_self, MACH_PORT_NULL);
360 if (inherited_bootstrap_port == MACH_PORT_NULL)
361 forward_ok = FALSE;
362
363 /* register "self" port with anscestor */
364 if (register_self && forward_ok) {
365 result = bootstrap_register(
366 inherited_bootstrap_port,
367 (char *)register_name,
368 bootstraps.bootstrap_port);
369 if (result != KERN_SUCCESS)
370 kern_fatal(result, "register self");
371 }
372 }
373
374 pthread_attr_init (&attr);
375 pthread_attr_setdetachstate ( &attr, PTHREAD_CREATE_DETACHED );
376 result = pthread_create(
377 &demand_thread,
378 &attr,
379 demand_loop,
380 NULL);
381 if (result) {
382 unix_error("pthread_create()");
383 exit(1);
384 }
385
386 /* block all but SIGHUP and SIGTERM */
387 sigfillset(&mask);
388 sigdelset(&mask, SIGHUP);
389 signal(SIGHUP, toggle_debug);
390 sigdelset(&mask, SIGTERM);
391 signal(SIGTERM, start_shutdown);
392 (void) sigprocmask(SIG_SETMASK, &mask, (sigset_t *)NULL);
393
394 /*
395 * Construct a very basic environment - as much as if we
396 * were actually forked from init (instead of the other
397 * way around):
398 *
399 * Set up the PATH to be approriate for the root user.
400 * Create an initial session.
401 * Establish an initial user.
402 * Disbale core dumps.
403 */
404 setsid();
405 setlogin("root");
406 enablecoredumps(debugging);
407 setenv("PATH", _PATH_STDPATH, 1);
408
409 init_errlog(pid == 0); /* are we a daemon? */
410 notice("Started with uid=%d%s%s%s",
411 inherited_uid,
412 (register_self) ? " registered-as=" : "",
413 (register_self) ? register_name : "",
414 (debugging) ? " in debug-mode" : "");
415
416 /* Process bootstrap service requests */
417 server_loop(); /* Should never return */
418 exit(1);
419 }
420
421 static void
422 wait_for_go(mach_port_t init_notify_port)
423 {
424 struct {
425 mach_msg_header_t hdr;
426 mach_msg_trailer_t trailer;
427 } init_go_msg;
428 kern_return_t result;
429
430 /*
431 * For now, we just blindly wait until we receive a message or
432 * timeout. We don't expect any notifications, and if we get one,
433 * it probably means something dire has happened; so we might as
434 * well give a shot at letting init run.
435 */
436 result = mach_msg(
437 &init_go_msg.hdr, MACH_RCV_MSG,
438 0, sizeof(init_go_msg), init_notify_port,
439 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
440 if (result != KERN_SUCCESS) {
441 kern_error(result, "mach_msg(receive) failed in wait_for_go");
442 }
443 bootstrap_port = init_go_msg.hdr.msgh_remote_port;
444 result = task_set_bootstrap_port(
445 mach_task_self(),
446 bootstrap_port);
447 if (result != KERN_SUCCESS) {
448 kern_error(result, "task_get_bootstrap_port()");
449 }
450 }
451
452
453 static void
454 unblock_init(mach_port_t init_notify_port,
455 mach_port_t newBootstrap)
456 {
457 mach_msg_header_t init_go_msg;
458 kern_return_t result;
459
460 /*
461 * Proc 1 is blocked in a msg_receive on its notify port, this lets
462 * it continue, and we hand off its new bootstrap port
463 */
464 init_go_msg.msgh_remote_port = init_notify_port;
465 init_go_msg.msgh_local_port = newBootstrap;
466 init_go_msg.msgh_bits = MACH_MSGH_BITS(
467 MACH_MSG_TYPE_COPY_SEND,
468 MACH_MSG_TYPE_MAKE_SEND);
469 init_go_msg.msgh_size = sizeof(init_go_msg);
470 result = mach_msg_send(&init_go_msg);
471 if (result != KERN_SUCCESS)
472 kern_fatal(result, "unblock_init mach_msg_send() failed");
473 debug("sent go message");
474 }
475
476
477 static void
478 init_ports(void)
479 {
480 kern_return_t result;
481
482 /*
483 * This task will become the bootstrap task.
484 */
485 /* Create port set that server loop listens to */
486 result = mach_port_allocate(
487 bootstrap_self,
488 MACH_PORT_RIGHT_PORT_SET,
489 &bootstrap_port_set);
490 if (result != KERN_SUCCESS)
491 kern_fatal(result, "port_set_allocate");
492
493 /* Create demand port set that second thread listens to */
494 result = mach_port_allocate(
495 bootstrap_self,
496 MACH_PORT_RIGHT_PORT_SET,
497 &demand_port_set);
498 if (result != KERN_SUCCESS)
499 kern_fatal(result, "port_set_allocate");
500
501 /* Create notify port and add to server port set */
502 result = mach_port_allocate(
503 bootstrap_self,
504 MACH_PORT_RIGHT_RECEIVE,
505 &notify_port);
506 if (result != KERN_SUCCESS)
507 kern_fatal(result, "mach_port_allocate");
508
509 result = mach_port_move_member(
510 bootstrap_self,
511 notify_port,
512 bootstrap_port_set);
513 if (result != KERN_SUCCESS)
514 kern_fatal(result, "mach_port_move_member");
515
516 /* Create backup port and add to server port set */
517 result = mach_port_allocate(
518 bootstrap_self,
519 MACH_PORT_RIGHT_RECEIVE,
520 &backup_port);
521 if (result != KERN_SUCCESS)
522 kern_fatal(result, "mach_port_allocate");
523
524 result = mach_port_move_member(
525 bootstrap_self,
526 backup_port,
527 bootstrap_port_set);
528 if (result != KERN_SUCCESS)
529 kern_fatal(result, "mach_port_move_member");
530
531 /* Create "self" port and add to server port set */
532 result = mach_port_allocate(
533 bootstrap_self,
534 MACH_PORT_RIGHT_RECEIVE,
535 &bootstraps.bootstrap_port);
536 if (result != KERN_SUCCESS)
537 kern_fatal(result, "mach_port_allocate");
538 result = mach_port_insert_right(
539 bootstrap_self,
540 bootstraps.bootstrap_port,
541 bootstraps.bootstrap_port,
542 MACH_MSG_TYPE_MAKE_SEND);
543 if (result != KERN_SUCCESS)
544 kern_fatal(result, "mach_port_insert_right");
545
546 /* keep the root bootstrap port "active" */
547 bootstraps.requestor_port = bootstraps.bootstrap_port;
548
549 result = mach_port_move_member(
550 bootstrap_self,
551 bootstraps.bootstrap_port,
552 bootstrap_port_set);
553 if (result != KERN_SUCCESS)
554 kern_fatal(result, "mach_port_move_member");
555 }
556
557 boolean_t
558 active_bootstrap(bootstrap_info_t *bootstrap)
559 {
560 return (bootstrap->requestor_port != MACH_PORT_NULL);
561 }
562
563 boolean_t
564 useless_server(server_t *serverp)
565 {
566 return ( !active_bootstrap(serverp->bootstrap) ||
567 !lookup_service_by_server(serverp) ||
568 !serverp->activity);
569 }
570
571 boolean_t
572 active_server(server_t *serverp)
573 {
574 return ( serverp->port ||
575 serverp->task_port || serverp->active_services);
576 }
577
578 static void
579 reap_server(server_t *serverp)
580 {
581 kern_return_t result;
582 pid_t presult;
583 int wstatus;
584
585 /*
586 * Reap our children.
587 */
588 presult = waitpid(serverp->pid, &wstatus, WNOHANG);
589 if (presult != serverp->pid) {
590 unix_error("waitpid: cmd = %s", serverp->cmd);
591 } else if (wstatus) {
592 notice("Server %x in bootstrap %x uid %d: \"%s\": %s %d [pid %d]",
593 serverp->port, serverp->bootstrap->bootstrap_port,
594 serverp->uid, serverp->cmd,
595 ((WIFEXITED(wstatus)) ?
596 "exited with non-zero status" :
597 "exited as a result of signal"),
598 ((WIFEXITED(wstatus)) ? WEXITSTATUS(wstatus) : WTERMSIG(wstatus)),
599 serverp->pid);
600 }
601 serverp->pid = 0;
602
603 /*
604 * Release the server task port reference, if we ever
605 * got it in the first place.
606 */
607 if (serverp->task_port != MACH_PORT_NULL) {
608 result = mach_port_deallocate(
609 mach_task_self(),
610 serverp->task_port);
611 if (result != KERN_SUCCESS)
612 kern_error(result, "mach_port_deallocate");
613 serverp->task_port = MACH_PORT_NULL;
614 }
615 }
616
617 static void
618 demand_server(server_t *serverp)
619 {
620 service_t *servicep;
621 kern_return_t result;
622
623 /*
624 * For on-demand servers, make sure that the service ports are
625 * back in on-demand portset. Active service ports should come
626 * back through a PORT_DESTROYED notification. We only have to
627 * worry about the inactive ports that may have been previously
628 * pulled from the set but never checked-in by the server.
629 */
630
631 for ( servicep = FIRST(services)
632 ; !IS_END(servicep, services)
633 ; servicep = NEXT(servicep))
634 {
635 if (serverp == servicep->server && !servicep->isActive) {
636 result = mach_port_move_member(
637 mach_task_self(),
638 servicep->port,
639 demand_port_set);
640 if (result != KERN_SUCCESS)
641 kern_fatal(result, "mach_port_move_member");
642 }
643 }
644 }
645
646 static
647 void dispatch_server(server_t *serverp)
648 {
649 if (!active_server(serverp)) {
650 if (useless_server(serverp))
651 delete_server(serverp);
652 else if (serverp->servertype == RESTARTABLE)
653 start_server(serverp);
654 else if (serverp->servertype == DEMAND)
655 demand_server(serverp);
656 }
657 }
658
659 void
660 setup_server(server_t *serverp)
661 {
662 kern_return_t result;
663 mach_port_t old_port;
664
665 /* Allocate privileged port for requests from service */
666 result = mach_port_allocate(mach_task_self(),
667 MACH_PORT_RIGHT_RECEIVE ,
668 &serverp->port);
669 info("Allocating port %x for server %s", serverp->port, serverp->cmd);
670 if (result != KERN_SUCCESS)
671 kern_fatal(result, "port_allocate");
672
673 /* Request no-senders notification so we can tell when server dies */
674 result = mach_port_request_notification(mach_task_self(),
675 serverp->port,
676 MACH_NOTIFY_NO_SENDERS,
677 1,
678 serverp->port,
679 MACH_MSG_TYPE_MAKE_SEND_ONCE,
680 &old_port);
681 if (result != KERN_SUCCESS)
682 kern_fatal(result, "mach_port_request_notification");
683
684 /* Add privileged server port to bootstrap port set */
685 result = mach_port_move_member(mach_task_self(),
686 serverp->port,
687 bootstrap_port_set);
688 if (result != KERN_SUCCESS)
689 kern_fatal(result, "mach_port_move_member");
690 }
691
692 static void
693 start_server(server_t *serverp)
694 {
695 kern_return_t result;
696 mach_port_t old_port;
697 int pid;
698
699 /*
700 * Do what's appropriate to get bootstrap port setup in server task
701 */
702 switch (serverp->servertype) {
703
704 case MACHINIT:
705 break;
706
707 case SERVER:
708 case DEMAND:
709 case RESTARTABLE:
710 if (!serverp->port)
711 setup_server(serverp);
712
713 serverp->activity = 0;
714
715 /* Insert a send right */
716 result = mach_port_insert_right(mach_task_self(),
717 serverp->port,
718 serverp->port,
719 MACH_MSG_TYPE_MAKE_SEND);
720 if (result != KERN_SUCCESS)
721 kern_fatal(result, "mach_port_insert_right");
722
723 /* Give trusted service a unique bootstrap port */
724 result = task_set_bootstrap_port(mach_task_self(),
725 serverp->port);
726 if (result != KERN_SUCCESS)
727 kern_fatal(result, "task_set_bootstrap_port");
728
729 result = mach_port_deallocate(mach_task_self(),
730 serverp->port);
731 if (result != KERN_SUCCESS)
732 kern_fatal(result, "mach_port_deallocate");
733
734 pid = fork();
735 if (pid < 0) {
736 unix_error("fork");
737 } else if (pid == 0) { /* CHILD */
738 exec_server(serverp);
739 exit(1);
740 } else { /* PARENT */
741
742 result = task_set_bootstrap_port(
743 mach_task_self(),
744 MACH_PORT_NULL);
745 if (result != KERN_SUCCESS)
746 kern_fatal(result, "task_set_bootstrap_port");
747
748 info("Launched server %x in bootstrap %x uid %d: \"%s\": [pid %d]",
749 serverp->port, serverp->bootstrap->bootstrap_port,
750 serverp->uid, serverp->cmd, pid);
751 serverp->pid = pid;
752 result = task_for_pid(
753 mach_task_self(),
754 pid,
755 &serverp->task_port);
756 if (result != KERN_SUCCESS) {
757 kern_error(result, "getting server task port");
758 reap_server(serverp);
759 dispatch_server(serverp);
760 break;
761 }
762
763 /* Request dead name notification to tell when task dies */
764 result = mach_port_request_notification(
765 mach_task_self(),
766 serverp->task_port,
767 MACH_NOTIFY_DEAD_NAME,
768 0,
769 notify_port,
770 MACH_MSG_TYPE_MAKE_SEND_ONCE,
771 &old_port);
772 if (result != KERN_SUCCESS) {
773 kern_error(result, "mach_port_request_notification");
774 reap_server(serverp);
775 dispatch_server(serverp);
776 }
777 }
778 break;
779 }
780 }
781
782 static void
783 exec_server(server_t *serverp)
784 {
785 char **argv;
786 sigset_t mask;
787
788 /*
789 * Setup environment for server, someday this should be Mach stuff
790 * rather than Unix crud
791 */
792 argv = argvize(serverp->cmd);
793 close_errlog();
794
795 if (serverp->uid != inherited_uid)
796 if (setuid(serverp->uid) < 0)
797 unix_fatal("Disabled server %x bootstrap %x: \"%s\": setuid(%d)",
798 serverp->port, serverp->bootstrap->bootstrap_port,
799 serverp->cmd, serverp->uid);
800
801 if (setsid() < 0) {
802 /*
803 * We can't keep this from happening, but we shouldn't start
804 * the server not as a process group leader. So, just fake like
805 * there was real activity, and exit the child. If needed,
806 * we'll re-launch it under another pid.
807 */
808 serverp->activity = 1;
809 unix_fatal("Temporary failure server %x bootstrap %x: \"%s\": setsid()",
810 serverp->port, serverp->bootstrap->bootstrap_port,
811 serverp->cmd);
812 }
813
814 sigemptyset(&mask);
815 (void) sigprocmask(SIG_SETMASK, &mask, (sigset_t *)NULL);
816
817 execv(argv[0], argv);
818 unix_fatal("Disabled server %x bootstrap %x: \"%s\": exec()",
819 serverp->port,
820 serverp->bootstrap->bootstrap_port,
821 serverp->cmd);
822 }
823
824 static char **
825 argvize(const char *string)
826 {
827 static char *argv[100], args[1000];
828 const char *cp;
829 char *argp, term;
830 int nargs;
831
832 /*
833 * Convert a command line into an argv for execv
834 */
835 nargs = 0;
836 argp = args;
837
838 for (cp = string; *cp;) {
839 while (isspace(*cp))
840 cp++;
841 term = (*cp == '"') ? *cp++ : '\0';
842 if (nargs < NELEM(argv))
843 argv[nargs++] = argp;
844 while (*cp && (term ? *cp != term : !isspace(*cp))
845 && argp < END_OF(args)) {
846 if (*cp == '\\')
847 cp++;
848 *argp++ = *cp;
849 if (*cp)
850 cp++;
851 }
852 *argp++ = '\0';
853 }
854 argv[nargs] = NULL;
855 return argv;
856 }
857
858 static void *
859 demand_loop(void *arg)
860 {
861 mach_msg_empty_rcv_t dummy;
862 kern_return_t dresult;
863
864
865 for(;;) {
866 mach_port_name_array_t members;
867 mach_msg_type_number_t membersCnt;
868 mach_port_status_t status;
869 mach_msg_type_number_t statusCnt;
870 int i;
871
872 /*
873 * Receive indication of message on demand service
874 * ports without actually receiving the message (we'll
875 * let the actual server do that.
876 */
877 dresult = mach_msg(
878 &dummy.header,
879 MACH_RCV_MSG|MACH_RCV_LARGE,
880 0,
881 0,
882 demand_port_set,
883 0,
884 MACH_PORT_NULL);
885 if (dresult != MACH_RCV_TOO_LARGE) {
886 kern_error(dresult, "demand_loop: mach_msg()");
887 continue;
888 }
889
890 /*
891 * If we are shutting down, there is no use processing
892 * any more of these messages.
893 */
894 if (shutdown_in_progress == TRUE)
895 return arg;
896
897 /*
898 * Some port(s) now have messages on them, find out
899 * which ones (there is no indication of which port
900 * triggered in the MACH_RCV_TOO_LARGE indication).
901 */
902 dresult = mach_port_get_set_status(
903 mach_task_self(),
904 demand_port_set,
905 &members,
906 &membersCnt);
907 if (dresult != KERN_SUCCESS) {
908 kern_error(dresult, "demand_loop: mach_port_get_set_status()");
909 continue;
910 }
911
912 for (i = 0; i < membersCnt; i++) {
913 statusCnt = MACH_PORT_RECEIVE_STATUS_COUNT;
914 dresult = mach_port_get_attributes(
915 mach_task_self(),
916 members[i],
917 MACH_PORT_RECEIVE_STATUS,
918 (mach_port_info_t)&status,
919 &statusCnt);
920 if (dresult != KERN_SUCCESS) {
921 kern_error(dresult, "demand_loop: mach_port_get_attributes()");
922 continue;
923 }
924
925 /*
926 * For each port with messages, take it out of the
927 * demand service portset, and inform the main thread
928 * that it might have to start the server responsible
929 * for it.
930 */
931 if (status.mps_msgcount) {
932 dresult = mach_port_move_member(
933 mach_task_self(),
934 members[i],
935 MACH_PORT_NULL);
936 if (dresult != KERN_SUCCESS) {
937 kern_error(dresult, "demand_loop: mach_port_move_member()");
938 continue;
939 }
940 notify_server_loop(members[i]);
941 }
942 }
943
944 dresult = vm_deallocate(
945 mach_task_self(),
946 (vm_address_t) members,
947 (vm_size_t) membersCnt * sizeof(mach_port_name_t));
948 if (dresult != KERN_SUCCESS) {
949 kern_error(dresult, "demand_loop: vm_deallocate()");
950 continue;
951 }
952 }
953 return NULL;
954 }
955
956 /*
957 * server_demux -- processes requests off our service port
958 * Also handles notifications
959 */
960
961 static boolean_t
962 server_demux(
963 mach_msg_header_t *Request,
964 mach_msg_header_t *Reply)
965 {
966 bootstrap_info_t *bootstrap;
967 service_t *servicep;
968 server_t *serverp;
969 kern_return_t result;
970 mig_reply_error_t *reply;
971
972 debug("received message on port %x\n", Request->msgh_local_port);
973
974 /*
975 * Do minimal cleanup and then exit.
976 */
977 if (shutdown_in_progress == TRUE) {
978 notice("Shutting down. Deactivating root bootstrap (%x) ...",
979 bootstraps.bootstrap_port);
980 deactivate_bootstrap(&bootstraps);
981 notice("Done.");
982 exit(0);
983 }
984
985 reply = (mig_reply_error_t *)Reply;
986
987 /*
988 * Pick off notification messages
989 */
990 if (Request->msgh_local_port == notify_port) {
991 mach_port_name_t np;
992
993 memset(reply, 0, sizeof(*reply));
994 switch (Request->msgh_id) {
995 case MACH_NOTIFY_DEAD_NAME:
996 np = ((mach_dead_name_notification_t *)Request)->not_port;
997 debug("Notified dead name %x", np);
998
999 if (np == inherited_bootstrap_port) {
1000 inherited_bootstrap_port = MACH_PORT_NULL;
1001 forward_ok = FALSE;
1002 }
1003
1004 /*
1005 * Check to see if a subset requestor port was deleted.
1006 */
1007 while (bootstrap = lookup_bootstrap_by_req_port(np)) {
1008 debug("Received dead name notification for bootstrap subset %x requestor port %x",
1009 bootstrap->bootstrap_port, bootstrap->requestor_port);
1010 mach_port_deallocate(
1011 mach_task_self(),
1012 bootstrap->requestor_port);
1013 bootstrap->requestor_port = MACH_PORT_NULL;
1014 deactivate_bootstrap(bootstrap);
1015 }
1016
1017 /*
1018 * Check to see if a defined service has gone
1019 * away.
1020 */
1021 while (servicep = lookup_service_by_port(np)) {
1022 /*
1023 * Port gone, registered service died.
1024 */
1025 debug("Received dead name notification for service %s "
1026 "on bootstrap port %x\n",
1027 servicep->name, servicep->bootstrap);
1028 debug("Service %s failed - deallocate", servicep->name);
1029 delete_service(servicep);
1030 }
1031
1032 /*
1033 * Check to see if a launched server task has gone
1034 * away.
1035 */
1036 if (serverp = lookup_server_by_task_port(np)) {
1037 /*
1038 * Port gone, server died.
1039 */
1040 debug("Received task death notification for server %s ",
1041 serverp->cmd);
1042 reap_server(serverp);
1043 dispatch_server(serverp);
1044 }
1045
1046 mach_port_deallocate(mach_task_self(), np);
1047 reply->RetCode = KERN_SUCCESS;
1048 break;
1049
1050 case MACH_NOTIFY_PORT_DELETED:
1051 np = ((mach_port_deleted_notification_t *)Request)->not_port;
1052 debug("port deleted notification on 0x%x\n", np);
1053 reply->RetCode = KERN_SUCCESS;
1054 break;
1055
1056 case MACH_NOTIFY_SEND_ONCE:
1057 debug("notification send-once right went unused\n");
1058 reply->RetCode = KERN_SUCCESS;
1059 break;
1060
1061 default:
1062 error("Unexpected notification: %d", Request->msgh_id);
1063 reply->RetCode = KERN_FAILURE;
1064 break;
1065 }
1066 }
1067
1068 else if (Request->msgh_local_port == backup_port) {
1069 mach_port_name_t np;
1070
1071 memset(reply, 0, sizeof(*reply));
1072
1073 np = ((mach_port_destroyed_notification_t *)Request)->not_port.name;
1074 servicep = lookup_service_by_port(np);
1075 if (servicep != NULL) {
1076 server_t *serverp = servicep->server;
1077
1078 switch (Request->msgh_id) {
1079
1080 case MACH_NOTIFY_PORT_DESTROYED:
1081 /*
1082 * Port sent back to us, server died.
1083 */
1084 debug("Received destroyed notification for service %s",
1085 servicep->name);
1086 debug("Service %x bootstrap %x backed up: %s",
1087 servicep->port, servicep->bootstrap->bootstrap_port,
1088 servicep->name);
1089 ASSERT(canReceive(servicep->port));
1090 servicep->isActive = FALSE;
1091 serverp->active_services--;
1092 dispatch_server(serverp);
1093 reply->RetCode = KERN_SUCCESS;
1094 break;
1095
1096 case DEMAND_REQUEST:
1097 /* message reflected over from demand start thread */
1098 if (!active_server(serverp))
1099 start_server(serverp);
1100 reply->RetCode = KERN_SUCCESS;
1101 break;
1102
1103 default:
1104 debug("Mysterious backup_port notification %d", Request->msgh_id);
1105 reply->RetCode = KERN_FAILURE;
1106 break;
1107 }
1108 } else {
1109 debug("Backup_port notification - previously deleted service");
1110 reply->RetCode = KERN_FAILURE;
1111 }
1112 }
1113
1114 else if (Request->msgh_id == MACH_NOTIFY_NO_SENDERS) {
1115 mach_port_t ns = Request->msgh_local_port;
1116
1117 if ((serverp = lookup_server_by_port(ns)) != NULL_SERVER) {
1118 /*
1119 * A server we launched has released his bootstrap
1120 * port send right. We won't re-launch him unless
1121 * his services came back to roost. But we need to
1122 * destroy the bootstrap port for fear of leaking.
1123 */
1124 debug("server %s dropped server port", serverp->cmd);
1125 serverp->port = MACH_PORT_NULL;
1126 dispatch_server(serverp);
1127 } else if (bootstrap = lookup_bootstrap_by_port(ns)) {
1128 /*
1129 * The last direct user of a deactivated bootstrap went away.
1130 * We can finally free it.
1131 */
1132 debug("Deallocating bootstrap %x: no more clients", ns);
1133 bootstrap->bootstrap_port = MACH_PORT_NULL;
1134 deallocate_bootstrap(bootstrap);
1135 }
1136
1137 result = mach_port_mod_refs(
1138 mach_task_self(),
1139 ns,
1140 MACH_PORT_RIGHT_RECEIVE,
1141 -1);
1142 if (result != KERN_SUCCESS)
1143 kern_fatal(result, "mach_port_mod_refs");
1144
1145 memset(reply, 0, sizeof(*reply));
1146 reply->RetCode = KERN_SUCCESS;
1147 }
1148
1149 else { /* must be a service request */
1150 debug("Handled request.");
1151 return bootstrap_server(Request, Reply);
1152 }
1153 return TRUE;
1154 }
1155
1156 /*
1157 * server_loop -- pick requests off our service port and process them
1158 * Also handles notifications
1159 */
1160 #define bootstrapMaxRequestSize 1024
1161 #define bootstrapMaxReplySize 1024
1162
1163 static void
1164 server_loop(void)
1165 {
1166 mach_msg_return_t mresult;
1167
1168 for (;;) {
1169 mresult = mach_msg_server(
1170 server_demux,
1171 bootstrapMaxRequestSize,
1172 bootstrap_port_set,
1173 MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_SENDER)|
1174 MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0));
1175 if (mresult != MACH_MSG_SUCCESS)
1176 kern_error(mresult, "mach_msg_server");
1177 }
1178 }
1179
1180 boolean_t
1181 canReceive(mach_port_t port)
1182 {
1183 mach_port_type_t p_type;
1184 kern_return_t result;
1185
1186 result = mach_port_type(mach_task_self(), port, &p_type);
1187 if (result != KERN_SUCCESS) {
1188 kern_error(result, "port_type");
1189 return FALSE;
1190 }
1191 return ((p_type & MACH_PORT_TYPE_RECEIVE) != 0);
1192 }
1193
1194
1195 boolean_t
1196 canSend(mach_port_t port)
1197 {
1198 mach_port_type_t p_type;
1199 kern_return_t result;
1200
1201 result = mach_port_type(mach_task_self(), port, &p_type);
1202 if (result != KERN_SUCCESS) {
1203 kern_error(result, "port_type");
1204 return FALSE;
1205 }
1206 return ((p_type & MACH_PORT_TYPE_PORT_RIGHTS) != 0);
1207 }