/* * Copyright (c) 1999-2002 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights * Reserved. This file contains Original Code and/or Modifications of * Original Code as defined in and that are subject to the Apple Public * Source License Version 1.0 (the 'License'). You may not use this file * except in compliance with the License. Please obtain a copy of the * License at http://www.apple.com/publicsource and read it before using * this file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License." * * @APPLE_LICENSE_HEADER_END@ */ /* * bootstrap -- fundamental service initiator and port server * Mike DeMoney, NeXT, Inc. * Copyright, 1990. All rights reserved. * * bootstrap.c -- implementation of bootstrap main service loop */ /* * Imports */ #import #import #import #import #import #include #include #include #include #include #include #import #import #import #import #import #import #import #import #import "bootstrap.h" #import "bootstrap_internal.h" #import "lists.h" #import "error_log.h" /* Mig should produce a declaration for this, but doesn't */ extern boolean_t bootstrap_server(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP); /* * Exports */ const char *program_name; /* our name for error messages */ #ifndef INIT_PATH #define INIT_PATH "/sbin/init" /* default init path */ #endif INIT_PATH uid_t inherited_uid; mach_port_t inherited_bootstrap_port = MACH_PORT_NULL; boolean_t forward_ok = FALSE; boolean_t shutdown_in_progress = FALSE; boolean_t debugging = FALSE; boolean_t register_self = FALSE; boolean_t force_fork = FALSE; const char *register_name; task_t bootstrap_self; #ifndef ASSERT #define ASSERT(p) #endif /* * Private macros */ #define NELEM(x) (sizeof(x)/sizeof(x[0])) #define END_OF(x) (&(x)[NELEM(x)]) #define streq(a,b) (strcmp(a,b) == 0) /* * Private declarations */ static void wait_for_go(mach_port_t init_notify_port); static void init_ports(void); static void start_server(server_t *serverp); static void unblock_init(mach_port_t init_notify_port, mach_port_t newBootstrap); static void exec_server(server_t *serverp); static char **argvize(const char *string); static void *demand_loop(void *arg); static void server_loop(void); extern kern_return_t bootstrap_register ( mach_port_t bootstrap_port, name_t service_name, mach_port_t service_port ); /* * Private ports we hold receive rights for. We also hold receive rights * for all the privileged ports. Those are maintained in the server * structs. */ mach_port_t bootstrap_port_set; mach_port_t demand_port_set; pthread_t demand_thread; mach_port_t notify_port; mach_port_t backup_port; static void notify_server_loop(mach_port_name_t about) { mach_port_destroyed_notification_t not; mach_msg_return_t nresult; not.not_header.msgh_id = DEMAND_REQUEST; not.not_header.msgh_remote_port = backup_port; not.not_header.msgh_local_port = MACH_PORT_NULL; not.not_header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0); not.not_header.msgh_size = sizeof(not) - sizeof(not.trailer); not.not_body.msgh_descriptor_count = 1; not.not_port.type = MACH_MSG_PORT_DESCRIPTOR; not.not_port.disposition = MACH_MSG_TYPE_PORT_NAME; not.not_port.name = about; nresult = mach_msg_send(¬.not_header); if (nresult != MACH_MSG_SUCCESS) { kern_error(nresult, "notify_server: mach_msg_send()"); } } void _myExit(int arg) { exit(arg); } void toggle_debug(int signal) { debugging = (debugging) ? FALSE : TRUE; } void start_shutdown(int signal) { debug("received SIGTERM"); shutdown_in_progress = TRUE; notify_server_loop(MACH_PORT_DEAD); } int main(int argc, char * argv[]) { const char *argp; char c; server_t *serverp; kern_return_t result; mach_port_t init_notify_port; pthread_attr_t attr; sigset_t mask; int pid; /* * If we are pid one, we have to exec init. Before doing so, we'll * fork a child, and that will become the true mach_init. But we have * to be very careful about ports. They aren't inherited across fork, * so we have to avoid storing port names in memory before the fork that * might not be valid after. */ pid = getpid(); if (pid == 1) { close(0); freopen("/dev/console", "r", stdin); setbuf(stdin, NULL); close(1); freopen("/dev/console", "w", stdout); setbuf(stdout, NULL); close(2); freopen("/dev/console", "w", stderr); setbuf(stderr, NULL); result = mach_port_allocate( mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &init_notify_port); if (result != KERN_SUCCESS) kern_fatal(result, "mach_port_allocate"); result = mach_port_insert_right( mach_task_self(), init_notify_port, init_notify_port, MACH_MSG_TYPE_MAKE_SEND); if (result != KERN_SUCCESS) kern_fatal(result, "mach_port_insert_right"); result = task_set_bootstrap_port( mach_task_self(), init_notify_port); if (result != KERN_SUCCESS) kern_fatal(result, "task_set_bootstrap_port"); pid = fork(); if (pid < 0) unix_fatal("fork"); else if (pid != 0) { /* PARENT - will become init when ready */ int fd; /* * Wait for mach_init ot give us a real bootstrap port */ wait_for_go(init_notify_port); info("Execing init"); close(0); close(1); close(2); fd = open("/dev/tty", O_RDONLY); if (fd >= 0) { ioctl(fd, TIOCNOTTY, 0); close(fd); } /* pass our arguments on to init */ argv[0] = INIT_PATH; execv(argv[0], argv); exit(1); /* will likely trigger a panic */ } } else init_notify_port = MACH_PORT_NULL; /* Initialize error handling */ program_name = rindex(*argv, '/'); if (program_name) program_name++; else program_name = *argv; argv++; argc--; /* Parse command line args */ while (argc > 0 && **argv == '-') { argp = *argv++ + 1; argc--; while (*argp) { switch (c = *argp++) { case 'd': debugging = TRUE; break; case 'D': debugging = FALSE; break; case 'F': force_fork = TRUE; break; case 'r': register_self = forward_ok = TRUE; if (argc > 0) { register_name = *argv++; argc--; } else fatal("-r requires name"); break; case '-': default: break; } } } /* * If we must fork, do it now before we get Mach ports in use */ if (force_fork) { pid = fork(); if (pid < 0) unix_fatal("fork"); else if (pid != 0) /* PARENT: just exit */ exit(0); } /* block all but SIGHUP and SIGTERM and mark us as an init process */ setsid(); sigfillset(&mask); sigdelset(&mask, SIGHUP); signal(SIGHUP, toggle_debug); sigdelset(&mask, SIGTERM); signal(SIGTERM, start_shutdown); (void) sigprocmask(SIG_SETMASK, &mask, (sigset_t *)NULL); init_errlog(pid == 0); /* are we a daemon? */ init_lists(); /* * This task will become the bootstrap task, so go ahead and * initialize the ports now. */ bootstrap_self = mach_task_self(); inherited_uid = getuid(); init_ports(); log("Started with uid=%d%s%s%s", inherited_uid, (register_self) ? " registered-as=" : "", (register_self) ? register_name : "", (debugging) ? " in debug-mode" : ""); /* * If we are supposed to coordinate with init, we have to * get that port again, because we only have a (probably wrong) * name in memory, not a proper right. */ if (init_notify_port != MACH_PORT_NULL) { result = task_get_bootstrap_port( bootstrap_self, &init_notify_port); if (result != KERN_SUCCESS) kern_fatal(result, "task_get_bootstrap_port"); unblock_init(init_notify_port, bootstraps.bootstrap_port); result = task_set_bootstrap_port( bootstrap_self, MACH_PORT_NULL); if (result != KERN_SUCCESS) kern_fatal(result, "task_set_bootstrap_port"); result = mach_port_deallocate( bootstrap_self, init_notify_port); if (result != KERN_SUCCESS) kern_fatal(result, "mach_port_deallocate"); forward_ok = FALSE; inherited_bootstrap_port = MACH_PORT_NULL; } else { /* get inherited bootstrap port */ result = task_get_bootstrap_port( bootstrap_self, &inherited_bootstrap_port); if (result != KERN_SUCCESS) kern_fatal(result, "task_get_bootstrap_port"); /* We set this explicitly as we start each child */ task_set_bootstrap_port(bootstrap_self, MACH_PORT_NULL); if (inherited_bootstrap_port == MACH_PORT_NULL) forward_ok = FALSE; /* register "self" port with anscestor */ if (register_self && forward_ok) { result = bootstrap_register( inherited_bootstrap_port, (char *)register_name, bootstraps.bootstrap_port); if (result != KERN_SUCCESS) kern_fatal(result, "register self"); } } /* Kick off all continuously running server processes */ for ( serverp = FIRST(servers) ; !IS_END(serverp, servers) ; serverp = NEXT(serverp)) if (serverp->servertype != DEMAND) start_server(serverp); pthread_attr_init (&attr); pthread_attr_setdetachstate ( &attr, PTHREAD_CREATE_DETACHED ); result = pthread_create( &demand_thread, &attr, demand_loop, NULL); if (result) { unix_error("pthread_create()"); exit(1); } /* Process bootstrap service requests */ server_loop(); /* Should never return */ exit(1); } static void wait_for_go(mach_port_t init_notify_port) { struct { mach_msg_header_t hdr; mach_msg_trailer_t trailer; } init_go_msg; kern_return_t result; /* * For now, we just blindly wait until we receive a message or * timeout. We don't expect any notifications, and if we get one, * it probably means something dire has happened; so we might as * well give a shot at letting init run. */ result = mach_msg( &init_go_msg.hdr, MACH_RCV_MSG, 0, sizeof(init_go_msg), init_notify_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); if (result != KERN_SUCCESS) { kern_error(result, "mach_msg(receive) failed in wait_for_go"); } result = task_set_bootstrap_port( mach_task_self(), init_go_msg.hdr.msgh_remote_port); if (result != KERN_SUCCESS) { kern_error(result, "task_get_bootstrap_port()"); } } static void unblock_init(mach_port_t init_notify_port, mach_port_t newBootstrap) { mach_msg_header_t init_go_msg; kern_return_t result; /* * Proc 1 is blocked in a msg_receive on its notify port, this lets * it continue, and we hand off its new bootstrap port */ init_go_msg.msgh_remote_port = init_notify_port; init_go_msg.msgh_local_port = newBootstrap; init_go_msg.msgh_bits = MACH_MSGH_BITS( MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND); init_go_msg.msgh_size = sizeof(init_go_msg); result = mach_msg_send(&init_go_msg); if (result != KERN_SUCCESS) kern_fatal(result, "unblock_init mach_msg_send() failed"); debug("sent go message"); } static void init_ports(void) { kern_return_t result; service_t *servicep; /* * This task will become the bootstrap task. */ /* Create port set that server loop listens to */ result = mach_port_allocate( bootstrap_self, MACH_PORT_RIGHT_PORT_SET, &bootstrap_port_set); if (result != KERN_SUCCESS) kern_fatal(result, "port_set_allocate"); /* Create demand port set that second thread listens to */ result = mach_port_allocate( bootstrap_self, MACH_PORT_RIGHT_PORT_SET, &demand_port_set); if (result != KERN_SUCCESS) kern_fatal(result, "port_set_allocate"); /* Create notify port and add to server port set */ result = mach_port_allocate( bootstrap_self, MACH_PORT_RIGHT_RECEIVE, ¬ify_port); if (result != KERN_SUCCESS) kern_fatal(result, "mach_port_allocate"); result = mach_port_move_member( bootstrap_self, notify_port, bootstrap_port_set); if (result != KERN_SUCCESS) kern_fatal(result, "mach_port_move_member"); /* Create backup port and add to server port set */ result = mach_port_allocate( bootstrap_self, MACH_PORT_RIGHT_RECEIVE, &backup_port); if (result != KERN_SUCCESS) kern_fatal(result, "mach_port_allocate"); result = mach_port_move_member( bootstrap_self, backup_port, bootstrap_port_set); if (result != KERN_SUCCESS) kern_fatal(result, "mach_port_move_member"); /* Create "self" port and add to server port set */ result = mach_port_allocate( bootstrap_self, MACH_PORT_RIGHT_RECEIVE, &bootstraps.bootstrap_port); if (result != KERN_SUCCESS) kern_fatal(result, "mach_port_allocate"); result = mach_port_insert_right( bootstrap_self, bootstraps.bootstrap_port, bootstraps.bootstrap_port, MACH_MSG_TYPE_MAKE_SEND); if (result != KERN_SUCCESS) kern_fatal(result, "mach_port_insert_right"); /* keep the root bootstrap port "active" */ bootstraps.requestor_port = bootstraps.bootstrap_port; result = mach_port_move_member( bootstrap_self, bootstraps.bootstrap_port, bootstrap_port_set); if (result != KERN_SUCCESS) kern_fatal(result, "mach_port_move_member"); /* * Allocate service ports for declared services. */ for ( servicep = FIRST(services) ; ! IS_END(servicep, services) ; servicep = NEXT(servicep)) { switch (servicep->servicetype) { case DECLARED: result = mach_port_allocate( bootstrap_self, MACH_PORT_RIGHT_RECEIVE, &(servicep->port)); if (result != KERN_SUCCESS) kern_fatal(result, "mach_port_allocate"); result = mach_port_insert_right( bootstrap_self, servicep->port, servicep->port, MACH_MSG_TYPE_MAKE_SEND); if (result != KERN_SUCCESS) kern_fatal(result, "mach_port_insert_right"); info("Declared port %x for service %s", servicep->port, servicep->name); if (servicep->server != NULL_SERVER && servicep->server->servertype == DEMAND) { result = mach_port_move_member( bootstrap_self, servicep->port, demand_port_set); if (result != KERN_SUCCESS) kern_fatal(result, "mach_port_move_member"); } break; case SELF: servicep->port = bootstraps.bootstrap_port; servicep->server = new_server(&bootstraps, program_name, inherited_uid, MACHINIT); info("Set port %x for self port", bootstraps.bootstrap_port); break; case REGISTERED: fatal("Can't allocate REGISTERED port!?!"); break; } } } boolean_t active_bootstrap(bootstrap_info_t *bootstrap) { return (bootstrap->requestor_port != MACH_PORT_NULL); } boolean_t useless_server(server_t *serverp) { return ( !active_bootstrap(serverp->bootstrap) || !lookup_service_by_server(serverp) || !serverp->activity); } boolean_t active_server(server_t *serverp) { return ( #ifdef DELAYED_BOOTSTRAP_DESTROY serverp->port || #else (serverp->port && serverp->pid == NO_PID) || #endif serverp->task_port || serverp->active_services); } static void reap_server(server_t *serverp) { kern_return_t result; pid_t presult; int wstatus; /* * Reap our children. */ presult = waitpid(serverp->pid, &wstatus, WNOHANG); if (presult != serverp->pid) { unix_error("waitpid: cmd = %s", serverp->cmd); } else if (wstatus) { log("Server %x in bootstrap %x uid %d: \"%s\": %s %d [pid %d]", serverp->port, serverp->bootstrap->bootstrap_port, serverp->uid, serverp->cmd, ((WIFEXITED(wstatus)) ? "exited with non-zero status" : "exited as a result of signal"), ((WIFEXITED(wstatus)) ? WEXITSTATUS(wstatus) : WTERMSIG(wstatus)), serverp->pid); } serverp->pid = 0; /* * Release the server task port reference, if we ever * got it in the first place. */ if (serverp->task_port != MACH_PORT_NULL) { result = mach_port_deallocate( mach_task_self(), serverp->task_port); if (result != KERN_SUCCESS) kern_error(result, "mach_port_deallocate"); serverp->task_port = MACH_PORT_NULL; } } static void demand_server(server_t *serverp) { service_t *servicep; kern_return_t result; /* * For on-demand servers, make sure that the service ports are * back in on-demand portset. Active service ports should come * back through a PORT_DESTROYED notification. We only have to * worry about the inactive ports that may have been previously * pulled from the set but never checked-in by the server. */ for ( servicep = FIRST(services) ; !IS_END(servicep, services) ; servicep = NEXT(servicep)) { if (serverp == servicep->server && !servicep->isActive) { result = mach_port_move_member( mach_task_self(), servicep->port, demand_port_set); if (result != KERN_SUCCESS) kern_fatal(result, "mach_port_move_member"); } } } static void dispatch_server(server_t *serverp) { if (!active_server(serverp)) { if (useless_server(serverp)) delete_server(serverp); else if (serverp->servertype == RESTARTABLE) start_server(serverp); else if (serverp->servertype == DEMAND) demand_server(serverp); } } void setup_server(server_t *serverp) { kern_return_t result; mach_port_t old_port; /* Allocate privileged port for requests from service */ result = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE , &serverp->port); info("Allocating port %x for server %s", serverp->port, serverp->cmd); if (result != KERN_SUCCESS) kern_fatal(result, "port_allocate"); /* Request no-senders notification so we can tell when server dies */ result = mach_port_request_notification(mach_task_self(), serverp->port, MACH_NOTIFY_NO_SENDERS, 1, serverp->port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &old_port); if (result != KERN_SUCCESS) kern_fatal(result, "mach_port_request_notification"); /* Add privileged server port to bootstrap port set */ result = mach_port_move_member(mach_task_self(), serverp->port, bootstrap_port_set); if (result != KERN_SUCCESS) kern_fatal(result, "mach_port_move_member"); } static void start_server(server_t *serverp) { kern_return_t result; mach_port_t old_port; int pid; /* * Do what's appropriate to get bootstrap port setup in server task */ switch (serverp->servertype) { case MACHINIT: break; case SERVER: case DEMAND: case RESTARTABLE: #ifdef DELAYED_BOOTSTRAP_DESTROY if (!serverp->port) #endif setup_server(serverp); serverp->activity = 0; /* Insert a send right */ result = mach_port_insert_right(mach_task_self(), serverp->port, serverp->port, MACH_MSG_TYPE_MAKE_SEND); if (result != KERN_SUCCESS) kern_fatal(result, "mach_port_insert_right"); /* Give trusted service a unique bootstrap port */ result = task_set_bootstrap_port(mach_task_self(), serverp->port); if (result != KERN_SUCCESS) kern_fatal(result, "task_set_bootstrap_port"); result = mach_port_deallocate(mach_task_self(), serverp->port); if (result != KERN_SUCCESS) kern_fatal(result, "mach_port_deallocate"); pid = fork(); if (pid < 0) { unix_error("fork"); } else if (pid == 0) { /* CHILD */ exec_server(serverp); exit(1); } else { /* PARENT */ result = task_set_bootstrap_port( mach_task_self(), MACH_PORT_NULL); if (result != KERN_SUCCESS) kern_fatal(result, "task_set_bootstrap_port"); info("Launched server %x in bootstrap %x uid %d: \"%s\": [pid %d]", serverp->port, serverp->bootstrap->bootstrap_port, serverp->uid, serverp->cmd, pid); serverp->pid = pid; result = task_for_pid( mach_task_self(), pid, &serverp->task_port); if (result != KERN_SUCCESS) { kern_error(result, "getting server task port"); reap_server(serverp); dispatch_server(serverp); break; } /* Request dead name notification to tell when task dies */ result = mach_port_request_notification( mach_task_self(), serverp->task_port, MACH_NOTIFY_DEAD_NAME, 0, notify_port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &old_port); if (result != KERN_SUCCESS) { kern_error(result, "mach_port_request_notification"); reap_server(serverp); dispatch_server(serverp); } } break; } } static void exec_server(server_t *serverp) { char **argv; sigset_t mask; /* * Setup environment for server, someday this should be Mach stuff * rather than Unix crud */ argv = argvize(serverp->cmd); close_errlog(); if (serverp->uid != inherited_uid) if (setuid(serverp->uid) < 0) unix_fatal("Disabled server %x bootstrap %x: \"%s\": setuid(%d)", serverp->port, serverp->bootstrap->bootstrap_port, serverp->cmd, serverp->uid); if (setsid() < 0) { /* * We can't keep this from happening, but we shouldn't start * the server not as a process group leader. So, just fake like * there was real activity, and exit the child. If needed, * we'll re-launch it under another pid. */ serverp->activity = 1; unix_fatal("Temporary failure server %x bootstrap %x: \"%s\": setsid()", serverp->port, serverp->bootstrap->bootstrap_port, serverp->cmd); } sigemptyset(&mask); (void) sigprocmask(SIG_SETMASK, &mask, (sigset_t *)NULL); execv(argv[0], argv); unix_fatal("Disabled server %x bootstrap %x: \"%s\": exec()", serverp->port, serverp->bootstrap->bootstrap_port, serverp->cmd); } static char ** argvize(const char *string) { static char *argv[100], args[1000]; const char *cp; char *argp, term; int nargs; /* * Convert a command line into an argv for execv */ nargs = 0; argp = args; for (cp = string; *cp;) { while (isspace(*cp)) cp++; term = (*cp == '"') ? *cp++ : '\0'; if (nargs < NELEM(argv)) argv[nargs++] = argp; while (*cp && (term ? *cp != term : !isspace(*cp)) && argp < END_OF(args)) { if (*cp == '\\') cp++; *argp++ = *cp; if (*cp) cp++; } *argp++ = '\0'; } argv[nargs] = NULL; return argv; } static void * demand_loop(void *arg) { mach_msg_empty_rcv_t dummy; kern_return_t dresult; for(;;) { mach_port_name_array_t members; mach_msg_type_number_t membersCnt; mach_port_status_t status; mach_msg_type_number_t statusCnt; int i; /* * Receive indication of message on demand service * ports without actually receiving the message (we'll * let the actual server do that. */ dresult = mach_msg( &dummy.header, MACH_RCV_MSG|MACH_RCV_LARGE, 0, 0, demand_port_set, 0, MACH_PORT_NULL); if (dresult != MACH_RCV_TOO_LARGE) { kern_error(dresult, "demand_loop: mach_msg()"); continue; } /* * If we are shutting down, there is no use processing * any more of these messages. */ if (shutdown_in_progress == TRUE) return arg; /* * Some port(s) now have messages on them, find out * which ones (there is no indication of which port * triggered in the MACH_RCV_TOO_LARGE indication). */ dresult = mach_port_get_set_status( mach_task_self(), demand_port_set, &members, &membersCnt); if (dresult != KERN_SUCCESS) { kern_error(dresult, "demand_loop: mach_port_get_set_status()"); continue; } for (i = 0; i < membersCnt; i++) { statusCnt = MACH_PORT_RECEIVE_STATUS_COUNT; dresult = mach_port_get_attributes( mach_task_self(), members[i], MACH_PORT_RECEIVE_STATUS, (mach_port_info_t)&status, &statusCnt); if (dresult != KERN_SUCCESS) { kern_error(dresult, "demand_loop: mach_port_get_attributes()"); continue; } /* * For each port with messages, take it out of the * demand service portset, and inform the main thread * that it might have to start the server responsible * for it. */ if (status.mps_msgcount) { dresult = mach_port_move_member( mach_task_self(), members[i], MACH_PORT_NULL); if (dresult != KERN_SUCCESS) { kern_error(dresult, "demand_loop: mach_port_move_member()"); continue; } notify_server_loop(members[i]); } } dresult = vm_deallocate( mach_task_self(), (vm_address_t) members, (vm_size_t) membersCnt * sizeof(mach_port_name_t)); if (dresult != KERN_SUCCESS) { kern_error(dresult, "demand_loop: vm_deallocate()"); continue; } } return NULL; } /* * server_demux -- processes requests off our service port * Also handles notifications */ static boolean_t server_demux( mach_msg_header_t *Request, mach_msg_header_t *Reply) { bootstrap_info_t *bootstrap; service_t *servicep; server_t *serverp; kern_return_t result; mig_reply_error_t *reply; debug("received message on port %x\n", Request->msgh_local_port); /* * Do minimal cleanup and then exit. */ if (shutdown_in_progress == TRUE) { log("Shutting down. Deactivating root bootstrap (%x) ...", bootstraps.bootstrap_port); deactivate_bootstrap(&bootstraps); log("Done."); exit(0); } reply = (mig_reply_error_t *)Reply; /* * Pick off notification messages */ if (Request->msgh_local_port == notify_port) { mach_port_name_t np; memset(reply, 0, sizeof(*reply)); switch (Request->msgh_id) { case MACH_NOTIFY_DEAD_NAME: np = ((mach_dead_name_notification_t *)Request)->not_port; debug("Notified dead name %x", np); if (np == inherited_bootstrap_port) { inherited_bootstrap_port = MACH_PORT_NULL; forward_ok = FALSE; } /* * Check to see if a subset requestor port was deleted. */ while (bootstrap = lookup_bootstrap_by_req_port(np)) { debug("Received dead name notification for bootstrap subset %x requestor port %x", bootstrap->bootstrap_port, bootstrap->requestor_port); mach_port_deallocate( mach_task_self(), bootstrap->requestor_port); bootstrap->requestor_port = MACH_PORT_NULL; deactivate_bootstrap(bootstrap); } /* * Check to see if a defined service has gone * away. */ while (servicep = lookup_service_by_port(np)) { /* * Port gone, registered service died. */ debug("Received dead name notification for service %s " "on bootstrap port %x\n", servicep->name, servicep->bootstrap); debug("Service %s failed - deallocate", servicep->name); delete_service(servicep); } /* * Check to see if a launched server task has gone * away. */ if (serverp = lookup_server_by_task_port(np)) { /* * Port gone, server died. */ debug("Received task death notification for server %s ", serverp->cmd); reap_server(serverp); dispatch_server(serverp); } mach_port_deallocate(mach_task_self(), np); reply->RetCode = KERN_SUCCESS; break; case MACH_NOTIFY_PORT_DELETED: np = ((mach_port_deleted_notification_t *)Request)->not_port; debug("port deleted notification on 0x%x\n", np); reply->RetCode = KERN_SUCCESS; break; case MACH_NOTIFY_SEND_ONCE: debug("notification send-once right went unused\n"); reply->RetCode = KERN_SUCCESS; break; default: error("Unexpected notification: %d", Request->msgh_id); reply->RetCode = KERN_FAILURE; break; } } else if (Request->msgh_local_port == backup_port) { mach_port_name_t np; memset(reply, 0, sizeof(*reply)); np = ((mach_port_destroyed_notification_t *)Request)->not_port.name; servicep = lookup_service_by_port(np); if (servicep != NULL) { server_t *serverp = servicep->server; switch (Request->msgh_id) { case MACH_NOTIFY_PORT_DESTROYED: /* * Port sent back to us, server died. */ debug("Received destroyed notification for service %s", servicep->name); debug("Service %x bootstrap %x backed up: %s", servicep->port, servicep->bootstrap->bootstrap_port, servicep->name); ASSERT(canReceive(servicep->port)); servicep->isActive = FALSE; serverp->active_services--; dispatch_server(serverp); reply->RetCode = KERN_SUCCESS; break; case DEMAND_REQUEST: /* message reflected over from demand start thread */ if (!active_server(serverp)) start_server(serverp); reply->RetCode = KERN_SUCCESS; break; default: debug("Mysterious backup_port notification %d", Request->msgh_id); reply->RetCode = KERN_FAILURE; break; } } else { debug("Backup_port notification - previously deleted service"); reply->RetCode = KERN_FAILURE; } } else if (Request->msgh_id == MACH_NOTIFY_NO_SENDERS) { mach_port_t ns = Request->msgh_local_port; if ((serverp = lookup_server_by_port(ns)) != NULL_SERVER) { /* * A server we launched has released his bootstrap * port send right. We won't re-launch him unless * his services came back to roost. But we need to * destroy the bootstrap port for fear of leaking. */ debug("server %s dropped server port", serverp->cmd); serverp->port = MACH_PORT_NULL; dispatch_server(serverp); } else if (bootstrap = lookup_bootstrap_by_port(ns)) { /* * The last direct user of a deactivated bootstrap went away. * We can finally free it. */ debug("Deallocating bootstrap %x: no more clients", ns); bootstrap->bootstrap_port = MACH_PORT_NULL; deallocate_bootstrap(bootstrap); } result = mach_port_mod_refs( mach_task_self(), ns, MACH_PORT_RIGHT_RECEIVE, -1); if (result != KERN_SUCCESS) kern_fatal(result, "mach_port_mod_refs"); memset(reply, 0, sizeof(*reply)); reply->RetCode = KERN_SUCCESS; } else { /* must be a service request */ debug("Handled request."); return bootstrap_server(Request, Reply); } return TRUE; } /* * server_loop -- pick requests off our service port and process them * Also handles notifications */ #define bootstrapMaxRequestSize 1024 #define bootstrapMaxReplySize 1024 static void server_loop(void) { mach_msg_return_t mresult; for (;;) { mresult = mach_msg_server( server_demux, bootstrapMaxRequestSize, bootstrap_port_set, MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_SENDER)| MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0)); if (mresult != MACH_MSG_SUCCESS) kern_error(mresult, "mach_msg_server"); } } boolean_t canReceive(mach_port_t port) { mach_port_type_t p_type; kern_return_t result; result = mach_port_type(mach_task_self(), port, &p_type); if (result != KERN_SUCCESS) { kern_error(result, "port_type"); return FALSE; } return ((p_type & MACH_PORT_TYPE_RECEIVE) != 0); } boolean_t canSend(mach_port_t port) { mach_port_type_t p_type; kern_return_t result; result = mach_port_type(mach_task_self(), port, &p_type); if (result != KERN_SUCCESS) { kern_error(result, "port_type"); return FALSE; } return ((p_type & MACH_PORT_TYPE_PORT_RIGHTS) != 0); }