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