diff --git a/handler/handler_main.cc b/handler/handler_main.cc index 11f14ef1..578b8862 100644 --- a/handler/handler_main.cc +++ b/handler/handler_main.cc @@ -20,10 +20,12 @@ #include #include +#include "base/auto_reset.h" #include "base/files/file_path.h" #include "base/files/scoped_file.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" +#include "base/scoped_generic.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" #include "client/crash_report_database.h" @@ -38,6 +40,8 @@ #if defined(OS_MACOSX) #include +#include + #include "base/mac/scoped_mach_port.h" #include "handler/mac/crash_report_exception_handler.h" #include "handler/mac/exception_handler_server.h" @@ -46,6 +50,7 @@ #include "util/posix/close_stdio.h" #elif defined(OS_WIN) #include + #include "handler/win/crash_report_exception_handler.h" #include "util/win/exception_handler_server.h" #include "util/win/handle.h" @@ -80,6 +85,31 @@ void Usage(const base::FilePath& me) { ToolSupport::UsageTail(me); } +#if defined(OS_MACOSX) + +struct ResetSIGTERMTraits { + static struct sigaction* InvalidValue() { + return nullptr; + } + + static void Free(struct sigaction* sa) { + int rv = sigaction(SIGTERM, sa, nullptr); + PLOG_IF(ERROR, rv != 0) << "sigaction"; + } +}; +using ScopedResetSIGTERM = + base::ScopedGeneric; + +ExceptionHandlerServer* g_exception_handler_server; + +// This signal handler is only operative when being run from launchd. +void HandleSIGTERM(int sig, siginfo_t* siginfo, void* context) { + DCHECK(g_exception_handler_server); + g_exception_handler_server->Stop(); +} + +#endif // OS_MACOSX + } // namespace int HandlerMain(int argc, char* argv[]) { @@ -285,6 +315,26 @@ int HandlerMain(int argc, char* argv[]) { ExceptionHandlerServer exception_handler_server( receive_right.Pass(), !options.mach_service.empty()); + base::AutoReset reset_g_exception_handler_server( + &g_exception_handler_server, &exception_handler_server); + + struct sigaction old_sa; + ScopedResetSIGTERM reset_sigterm; + if (!options.mach_service.empty()) { + // When running from launchd, no no-senders notification could ever be + // triggered, because launchd maintains a send right to the service. When + // launchd wants the job to exit, it will send a SIGTERM. See + // launchd.plist(5). + // + // Set up a SIGTERM handler that will call exception_handler_server.Stop(). + struct sigaction sa = {}; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_SIGINFO; + sa.sa_sigaction = HandleSIGTERM; + int rv = sigaction(SIGTERM, &sa, &old_sa); + PCHECK(rv == 0) << "sigaction"; + reset_sigterm.reset(&old_sa); + } #elif defined(OS_WIN) ExceptionHandlerServer exception_handler_server(!options.pipe_name.empty()); diff --git a/handler/mac/exception_handler_server.cc b/handler/mac/exception_handler_server.cc index 19038293..845b34dd 100644 --- a/handler/mac/exception_handler_server.cc +++ b/handler/mac/exception_handler_server.cc @@ -14,12 +14,7 @@ #include "handler/mac/exception_handler_server.h" -#include - -#include "base/auto_reset.h" #include "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "base/scoped_generic.h" #include "base/mac/mach_logging.h" #include "util/mach/composite_mach_message_server.h" #include "util/mach/mach_extensions.h" @@ -31,58 +26,12 @@ namespace crashpad { namespace { -struct ResetSIGTERMTraits { - static struct sigaction* InvalidValue() { - return nullptr; - } - - static void Free(struct sigaction* sa) { - int rv = sigaction(SIGTERM, sa, nullptr); - PLOG_IF(ERROR, rv != 0) << "sigaction"; - } -}; -using ScopedResetSIGTERM = - base::ScopedGeneric; - -mach_port_t g_signal_notify_port; - -// This signal handler is only operative when being run from launchd. It causes -// the exception handler server to stop running by sending it a synthesized -// no-senders notification. -void HandleSIGTERM(int sig, siginfo_t* siginfo, void* context) { - DCHECK(g_signal_notify_port); - - // mach_no_senders_notification_t defines the receive side of this structure, - // with a trailer element that’s undesirable for the send side. - struct { - mach_msg_header_t header; - NDR_record_t ndr; - mach_msg_type_number_t mscount; - } no_senders_notification = {}; - no_senders_notification.header.msgh_bits = - MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND_ONCE, 0); - no_senders_notification.header.msgh_size = sizeof(no_senders_notification); - no_senders_notification.header.msgh_remote_port = g_signal_notify_port; - no_senders_notification.header.msgh_local_port = MACH_PORT_NULL; - no_senders_notification.header.msgh_id = MACH_NOTIFY_NO_SENDERS; - no_senders_notification.ndr = NDR_record; - no_senders_notification.mscount = 0; - - kern_return_t kr = mach_msg(&no_senders_notification.header, - MACH_SEND_MSG, - sizeof(no_senders_notification), - 0, - MACH_PORT_NULL, - MACH_MSG_TIMEOUT_NONE, - MACH_PORT_NULL); - MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_msg"; -} - class ExceptionHandlerServerRun : public UniversalMachExcServer::Interface, public NotifyServer::DefaultInterface { public: ExceptionHandlerServerRun( mach_port_t exception_port, + mach_port_t notify_port, bool launchd, UniversalMachExcServer::Interface* exception_interface) : UniversalMachExcServer::Interface(), @@ -92,11 +41,9 @@ class ExceptionHandlerServerRun : public UniversalMachExcServer::Interface, composite_mach_message_server_(), exception_interface_(exception_interface), exception_port_(exception_port), - notify_port_(NewMachPort(MACH_PORT_RIGHT_RECEIVE)), + notify_port_(notify_port), running_(true), launchd_(launchd) { - CHECK(notify_port_.is_valid()); - composite_mach_message_server_.AddHandler(&mach_exc_server_); composite_mach_message_server_.AddHandler(¬ify_server_); } @@ -108,9 +55,6 @@ class ExceptionHandlerServerRun : public UniversalMachExcServer::Interface, DCHECK(running_); kern_return_t kr; - scoped_ptr> reset_signal_notify_port; - struct sigaction old_sa; - ScopedResetSIGTERM reset_sigterm; if (!launchd_) { // Request that a no-senders notification for exception_port_ be sent to // notify_port_. @@ -119,33 +63,11 @@ class ExceptionHandlerServerRun : public UniversalMachExcServer::Interface, exception_port_, MACH_NOTIFY_NO_SENDERS, 0, - notify_port_.get(), + notify_port_, MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous); MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_request_notification"; - - if (previous != MACH_PORT_NULL) { - kr = mach_port_deallocate(mach_task_self(), previous); - MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_deallocate"; - } - } else { - // A real no-senders notification would never be triggered, because - // launchd maintains a send right to the service. When launchd wants the - // job to exit, it will send a SIGTERM. See launchd.plist(5). - // - // Set up a SIGTERM handler that will cause Run() to return (incidentally, - // by sending a synthetic no-senders notification). - struct sigaction sa = {}; - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_SIGINFO; - sa.sa_sigaction = HandleSIGTERM; - int rv = sigaction(SIGTERM, &sa, &old_sa); - PCHECK(rv == 0) << "sigaction"; - reset_sigterm.reset(&old_sa); - - DCHECK(!g_signal_notify_port); - reset_signal_notify_port.reset(new base::AutoReset( - &g_signal_notify_port, notify_port_.get())); + base::mac::ScopedMachSendRight previous_owner(previous); } // A single CompositeMachMessageServer will dispatch both exception messages @@ -165,7 +87,7 @@ class ExceptionHandlerServerRun : public UniversalMachExcServer::Interface, MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_insert_member"; kr = mach_port_insert_member( - mach_task_self(), notify_port_.get(), server_port_set.get()); + mach_task_self(), notify_port_, server_port_set.get()); MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_insert_member"; // Run the server in kOneShot mode so that running_ can be reevaluated after @@ -249,7 +171,7 @@ class ExceptionHandlerServerRun : public UniversalMachExcServer::Interface, CompositeMachMessageServer composite_mach_message_server_; UniversalMachExcServer::Interface* exception_interface_; // weak mach_port_t exception_port_; // weak - base::mac::ScopedMachReceiveRight notify_port_; + mach_port_t notify_port_; // weak bool running_; bool launchd_; @@ -262,8 +184,10 @@ ExceptionHandlerServer::ExceptionHandlerServer( base::mac::ScopedMachReceiveRight receive_port, bool launchd) : receive_port_(receive_port.Pass()), + notify_port_(NewMachPort(MACH_PORT_RIGHT_RECEIVE)), launchd_(launchd) { CHECK(receive_port_.is_valid()); + CHECK(notify_port_.is_valid()); } ExceptionHandlerServer::~ExceptionHandlerServer() { @@ -272,8 +196,38 @@ ExceptionHandlerServer::~ExceptionHandlerServer() { void ExceptionHandlerServer::Run( UniversalMachExcServer::Interface* exception_interface) { ExceptionHandlerServerRun run( - receive_port_.get(), launchd_, exception_interface); + receive_port_.get(), notify_port_.get(), launchd_, exception_interface); run.Run(); } +void ExceptionHandlerServer::Stop() { + // Cause the exception handler server to stop running by sending it a + // synthesized no-senders notification. + // + // mach_no_senders_notification_t defines the receive side of this structure, + // with a trailer element that’s undesirable for the send side. + struct { + mach_msg_header_t header; + NDR_record_t ndr; + mach_msg_type_number_t mscount; + } no_senders_notification = {}; + no_senders_notification.header.msgh_bits = + MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND_ONCE, 0); + no_senders_notification.header.msgh_size = sizeof(no_senders_notification); + no_senders_notification.header.msgh_remote_port = notify_port_.get(); + no_senders_notification.header.msgh_local_port = MACH_PORT_NULL; + no_senders_notification.header.msgh_id = MACH_NOTIFY_NO_SENDERS; + no_senders_notification.ndr = NDR_record; + no_senders_notification.mscount = 0; + + kern_return_t kr = mach_msg(&no_senders_notification.header, + MACH_SEND_MSG, + sizeof(no_senders_notification), + 0, + MACH_PORT_NULL, + MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_msg"; +} + } // namespace crashpad diff --git a/handler/mac/exception_handler_server.h b/handler/mac/exception_handler_server.h index df9d5a0d..421038e3 100644 --- a/handler/mac/exception_handler_server.h +++ b/handler/mac/exception_handler_server.h @@ -34,8 +34,8 @@ class ExceptionHandlerServer { //! notifications will be received on. //! \param[in] launchd If `true`, the exception handler is being run from //! launchd. \a receive_port is not monitored for no-senders - //! notifications, and instead, the expected “quit” signal is receipt of - //! `SIGTERM`. + //! notifications, and instead, Stop() must be called to provide a “quit” + //! signal. ExceptionHandlerServer(base::mac::ScopedMachReceiveRight receive_port, bool launchd); ~ExceptionHandlerServer(); @@ -47,11 +47,10 @@ class ExceptionHandlerServer { //! This method monitors the receive port for exception messages and, if //! not being run by launchd, no-senders notifications. It continues running //! until it has no more clients, indicated by the receipt of a no-senders - //! notification, or if being run by launchd, receipt of `SIGTERM`. When not - //! being run by launchd, it is important to assure that a send right exists - //! in a client (or has been queued by `mach_msg()` to be sent to a client) - //! prior to calling this method, or it will detect that it is sender-less and - //! return immediately. + //! notification, or until Stop() is called. When not being run by launchd, it + //! is important to assure that a send right exists in a client (or has been + //! queued by `mach_msg()` to be sent to a client) prior to calling this + //! method, or it will detect that it is sender-less and return immediately. //! //! All exception messages will be passed to \a exception_interface. //! @@ -63,8 +62,19 @@ class ExceptionHandlerServer { //! this method will continue running normally. void Run(UniversalMachExcServer::Interface* exception_interface); + //! \brief Stops a running exception-handling server. + //! + //! The normal mode of operation is to call Stop() while Run() is running. It + //! is expected that Stop() would be called from a signal handler. + //! + //! If Stop() is called before Run() it will cause Run() to return as soon as + //! it is called. It is harmless to call Stop() after Run() has already + //! returned, or to call Stop() after it has already been called. + void Stop(); + private: base::mac::ScopedMachReceiveRight receive_port_; + base::mac::ScopedMachReceiveRight notify_port_; bool launchd_; DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerServer);