Bug 336193 - P1: Use SignalPipeWatcher to detect signals SIGTERM/SIGINT/SIGHUP and quit application gracefully.
MozReview-Commit-ID: HH1B7UFhWqY
--- a/toolkit/xre/nsNativeAppSupportUnix.cpp
+++ b/toolkit/xre/nsNativeAppSupportUnix.cpp
@@ -9,16 +9,17 @@
#include "nsXPCOM.h"
#include "nsISupportsPrimitives.h"
#include "nsIObserverService.h"
#include "nsIAppStartup.h"
#include "nsServiceManagerUtils.h"
#include "prlink.h"
#include "nsXREDirProvider.h"
#include "nsReadableUtils.h"
+#include "nsDumpUtils.h"
#include "nsIFile.h"
#include "nsDirectoryServiceDefs.h"
#include "nsICommandLineRunner.h"
#include "nsIWindowMediator.h"
#include "nsPIDOMWindow.h"
#include "nsIDocShell.h"
#include "nsIBaseWindow.h"
@@ -156,16 +157,18 @@ private:
{
mClientState = aState;
MOZ_LOG(sMozSMLog, LogLevel::Debug, ("New state = %s\n", gClientStateTable[aState]));
}
SmcConn mSessionConnection;
ClientState mClientState;
#endif
+ static void TerminationHandler(const uint8_t aRecvSig);
+ static void ForceAppQuitAsync();
};
#if MOZ_X11
static gboolean
process_ice_messages(IceConn connection)
{
IceProcessMessagesStatus status;
@@ -374,22 +377,17 @@ nsNativeAppSupportUnix::SaveYourselfCB(S
} else {
SmcSaveYourselfDone(smc_conn, True);
}
}
void
nsNativeAppSupportUnix::DieCB(SmcConn smc_conn, SmPointer client_data)
{
- nsCOMPtr<nsIAppStartup> appService =
- do_GetService("@mozilla.org/toolkit/app-startup;1");
-
- if (appService) {
- appService->Quit(nsIAppStartup::eForceQuit);
- }
+ ForceAppQuitAsync();
// Quit causes the shutdown to begin but the shutdown process is asynchronous
// so we can't DisconnectFromSM() yet
}
void
nsNativeAppSupportUnix::ShutdownCancelledCB(SmcConn smc_conn,
SmPointer client_data)
{
@@ -669,19 +667,56 @@ nsNativeAppSupportUnix::Stop(bool *aResu
NS_ENSURE_ARG(aResult);
*aResult = true;
return NS_OK;
}
NS_IMETHODIMP
nsNativeAppSupportUnix::Enable()
{
+ // Detect termination signals with a pipe so they can be handled cleanly.
+ // Note that SignalPipeWatcher does not chain to previous handler, this
+ // prevents nsProfileLock from interfering until we unregister later. SIGINT
+ // may be set to SIG_IGN by non-interactive shell which will not be registered.
+ SignalPipeWatcher* sw = SignalPipeWatcher::GetSingleton();
+ sw->RegisterCallback(SIGTERM, nsNativeAppSupportUnix::TerminationHandler);
+ sw->RegisterCallback(SIGINT, nsNativeAppSupportUnix::TerminationHandler);
+ sw->RegisterCallback(SIGHUP, nsNativeAppSupportUnix::TerminationHandler);
return NS_OK;
}
+void
+nsNativeAppSupportUnix::TerminationHandler(const uint8_t aRecvSig)
+{
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "nsNativeAppSupportUnix::TerminationHandler", []() -> void {
+ // Try to perform a normal clean quit, needs to be executed on
+ // the main thread.
+ ForceAppQuitAsync();
+
+ // Restore previous signal handlers so subsequent termination attempts
+ // can kill process, can't be part of immediate callback due to mutex lock.
+ SignalPipeWatcher* sw = SignalPipeWatcher::GetSingleton();
+ sw->UnregisterCallback(SIGTERM);
+ sw->UnregisterCallback(SIGINT);
+ sw->UnregisterCallback(SIGHUP);
+ }));
+}
+
+void
+nsNativeAppSupportUnix::ForceAppQuitAsync()
+{
+ nsCOMPtr<nsIAppStartup> appService =
+ do_GetService("@mozilla.org/toolkit/app-startup;1");
+
+ if (appService) {
+ appService->Quit(nsIAppStartup::eForceQuit);
+ }
+}
+
nsresult
NS_CreateNativeAppSupport(nsINativeAppSupport **aResult)
{
nsNativeAppSupportBase* native = new nsNativeAppSupportUnix();
if (!native)
return NS_ERROR_OUT_OF_MEMORY;
*aResult = native;
--- a/toolkit/xre/nsUpdateDriver.cpp
+++ b/toolkit/xre/nsUpdateDriver.cpp
@@ -44,16 +44,17 @@
#elif defined(XP_UNIX)
# include <unistd.h>
# include <sys/wait.h>
#endif
using namespace mozilla;
static LazyLogModule sUpdateLog("updatedriver");
+#undef LOG
#define LOG(args) MOZ_LOG(sUpdateLog, mozilla::LogLevel::Debug, args)
#ifdef XP_WIN
#define UPDATER_BIN "updater.exe"
#elif XP_MACOSX
#define UPDATER_BIN "org.mozilla.updater"
#else
#define UPDATER_BIN "updater"
--- a/xpcom/base/nsDumpUtils.cpp
+++ b/xpcom/base/nsDumpUtils.cpp
@@ -133,22 +133,50 @@ SignalPipeWatcher::RegisterCallback(uint
MutexAutoLock lock(mSignalInfoLock);
for (SignalInfoArray::index_type i = 0; i < mSignalInfo.Length(); ++i) {
if (mSignalInfo[i].mSignal == aSignal) {
LOG("Register Signal(%d) callback failed! (DUPLICATE)", aSignal);
return;
}
}
- SignalInfo signalInfo = { aSignal, aCallback };
+
+ // Save current signal action so it can be restored later. Respect handlers
+ // that have been set to SIG_IGN by non-job-control/non-interactive shell by
+ // not changing their disposition.
+ struct sigaction oldAction = { 0 };
+ if (sigaction(aSignal, nullptr, &oldAction) ||
+ oldAction.sa_handler == SIG_IGN) {
+ LOG("Register Signal(%d) callback failed! (OLDACTION)", aSignal);
+ return;
+ }
+
+ SignalInfo signalInfo = { aSignal, aCallback, oldAction };
mSignalInfo.AppendElement(signalInfo);
RegisterSignalHandler(signalInfo.mSignal);
}
void
+SignalPipeWatcher::UnregisterCallback(uint8_t aSignal)
+{
+ // Restore previous signal action
+ MutexAutoLock lock(mSignalInfoLock);
+ for (SignalInfoArray::index_type i = 0; i < mSignalInfo.Length(); i++) {
+ if (aSignal == mSignalInfo[i].mSignal) {
+ if (sigaction(aSignal, &mSignalInfo[i].mOldAction, nullptr) == 0) {
+ mSignalInfo.RemoveElementAt(i);
+ } else {
+ LOG("SignalPipeWatcher failed to unregister sig %d.", aSignal);
+ }
+ return;
+ }
+ }
+}
+
+void
SignalPipeWatcher::RegisterSignalHandler(uint8_t aSignal)
{
struct sigaction action;
memset(&action, 0, sizeof(action));
sigemptyset(&action.sa_mask);
action.sa_handler = DumpSignalHandler;
if (aSignal) {
--- a/xpcom/base/nsDumpUtils.h
+++ b/xpcom/base/nsDumpUtils.h
@@ -139,26 +139,29 @@ private:
FifoInfoArray mFifoInfo;
};
typedef void (*PipeCallback)(const uint8_t aRecvSig);
struct SignalInfo
{
uint8_t mSignal;
PipeCallback mCallback;
+ struct sigaction mOldAction;
};
typedef nsTArray<SignalInfo> SignalInfoArray;
class SignalPipeWatcher : public FdWatcher
{
public:
static SignalPipeWatcher* GetSingleton();
void RegisterCallback(uint8_t aSignal, PipeCallback aCallback);
+ void UnregisterCallback(uint8_t aSignal);
+
void RegisterSignalHandler(uint8_t aSignal = 0);
virtual ~SignalPipeWatcher();
virtual int OpenFd() override;
virtual void StopWatching() override;