1 // Copyright (c) 2007, Google Inc. 2 // All rights reserved. 3 // 4 // Redistribution and use in source and binary forms, with or without 5 // modification, are permitted provided that the following conditions are 6 // met: 7 // 8 // * Redistributions of source code must retain the above copyright 9 // notice, this list of conditions and the following disclaimer. 10 // * Redistributions in binary form must reproduce the above 11 // copyright notice, this list of conditions and the following disclaimer 12 // in the documentation and/or other materials provided with the 13 // distribution. 14 // * Neither the name of Google Inc. nor the names of its 15 // contributors may be used to endorse or promote products derived from 16 // this software without specific prior written permission. 17 // 18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 30 #import "OnDemandServer.h" 31 32 #import "Breakpad.h" 33 #include "common/mac/bootstrap_compat.h" 34 35 #if DEBUG 36 #define PRINT_MACH_RESULT(result_, message_) \ 37 printf(message_"%s (%d)\n", mach_error_string(result_), result_ ); 38 #if defined(MAC_OS_X_VERSION_10_5) && \ 39 MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 40 #define PRINT_BOOTSTRAP_RESULT(result_, message_) \ 41 printf(message_"%s (%d)\n", bootstrap_strerror(result_), result_ ); 42 #else 43 #define PRINT_BOOTSTRAP_RESULT(result_, message_) \ 44 PRINT_MACH_RESULT(result_, message_) 45 #endif 46 #else 47 #define PRINT_MACH_RESULT(result_, message_) 48 #define PRINT_BOOTSTRAP_RESULT(result_, message_) 49 #endif 50 51 //============================================================================== 52 OnDemandServer *OnDemandServer::Create(const char *server_command, 53 const char *service_name, 54 bool unregister_on_cleanup, 55 kern_return_t *out_result) { 56 OnDemandServer *server = new OnDemandServer(); 57 58 if (!server) return NULL; 59 60 kern_return_t result = server->Initialize(server_command, 61 service_name, 62 unregister_on_cleanup); 63 64 if (out_result) { 65 *out_result = result; 66 } 67 68 if (result == KERN_SUCCESS) { 69 return server; 70 } 71 72 delete server; 73 return NULL; 74 }; 75 76 //============================================================================== 77 kern_return_t OnDemandServer::Initialize(const char *server_command, 78 const char *service_name, 79 bool unregister_on_cleanup) { 80 unregister_on_cleanup_ = unregister_on_cleanup; 81 82 mach_port_t self_task = mach_task_self(); 83 84 mach_port_t bootstrap_port; 85 kern_return_t kr = task_get_bootstrap_port(self_task, &bootstrap_port); 86 if (kr != KERN_SUCCESS) { 87 PRINT_MACH_RESULT(kr, "task_get_bootstrap_port(): "); 88 return kr; 89 } 90 91 mach_port_t bootstrap_subset_port; 92 kr = bootstrap_subset(bootstrap_port, self_task, &bootstrap_subset_port); 93 if (kr != BOOTSTRAP_SUCCESS) { 94 PRINT_BOOTSTRAP_RESULT(kr, "bootstrap_subset(): "); 95 return kr; 96 } 97 98 // The inspector will be invoked with its bootstrap port set to the subset, 99 // but the sender will need access to the original bootstrap port. Although 100 // the original port is the subset's parent, bootstrap_parent can't be used 101 // because it requires extra privileges. Stash the original bootstrap port 102 // in the subset by registering it under a known name. The inspector will 103 // recover this port and set it as its own bootstrap port in Inspector.mm 104 // Inspector::ResetBootstrapPort. 105 kr = breakpad::BootstrapRegister( 106 bootstrap_subset_port, 107 const_cast<char*>(BREAKPAD_BOOTSTRAP_PARENT_PORT), 108 bootstrap_port); 109 if (kr != BOOTSTRAP_SUCCESS) { 110 PRINT_BOOTSTRAP_RESULT(kr, "bootstrap_register(): "); 111 return kr; 112 } 113 114 kr = bootstrap_create_server(bootstrap_subset_port, 115 const_cast<char*>(server_command), 116 geteuid(), // server uid 117 true, 118 &server_port_); 119 if (kr != BOOTSTRAP_SUCCESS) { 120 PRINT_BOOTSTRAP_RESULT(kr, "bootstrap_create_server(): "); 121 return kr; 122 } 123 124 strlcpy(service_name_, service_name, sizeof(service_name_)); 125 126 #pragma clang diagnostic push 127 #pragma clang diagnostic ignored "-Wdeprecated-declarations" 128 // Create a service called service_name, and return send rights to 129 // that port in service_port_. 130 kr = bootstrap_create_service(server_port_, 131 const_cast<char*>(service_name), 132 &service_port_); 133 #pragma clang diagnostic pop 134 if (kr != BOOTSTRAP_SUCCESS) { 135 PRINT_BOOTSTRAP_RESULT(kr, "bootstrap_create_service(): "); 136 137 // perhaps the service has already been created - try to look it up 138 kr = bootstrap_look_up(bootstrap_port, (char*)service_name, &service_port_); 139 140 if (kr != BOOTSTRAP_SUCCESS) { 141 PRINT_BOOTSTRAP_RESULT(kr, "bootstrap_look_up(): "); 142 Unregister(); // clean up server port 143 return kr; 144 } 145 } 146 147 return KERN_SUCCESS; 148 } 149 150 //============================================================================== 151 OnDemandServer::~OnDemandServer() { 152 if (unregister_on_cleanup_) { 153 Unregister(); 154 } 155 } 156 157 //============================================================================== 158 void OnDemandServer::LaunchOnDemand() { 159 // We need to do this, since the launched server is another process 160 // and holding on to this port delays launching until the current process 161 // exits! 162 mach_port_deallocate(mach_task_self(), server_port_); 163 server_port_ = MACH_PORT_DEAD; 164 165 // Now, the service is still registered and all we need to do is send 166 // a mach message to the service port in order to launch the server. 167 } 168 169 //============================================================================== 170 void OnDemandServer::Unregister() { 171 if (service_port_ != MACH_PORT_NULL) { 172 mach_port_deallocate(mach_task_self(), service_port_); 173 service_port_ = MACH_PORT_NULL; 174 } 175 176 if (server_port_ != MACH_PORT_NULL) { 177 // unregister the service 178 kern_return_t kr = breakpad::BootstrapRegister(server_port_, 179 service_name_, 180 MACH_PORT_NULL); 181 182 if (kr != KERN_SUCCESS) { 183 PRINT_MACH_RESULT(kr, "Breakpad UNREGISTER : bootstrap_register() : "); 184 } 185 186 mach_port_deallocate(mach_task_self(), server_port_); 187 server_port_ = MACH_PORT_NULL; 188 } 189 } 190