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//============================================================================== 52OnDemandServer *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//============================================================================== 77kern_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//============================================================================== 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