AU: Catch terminate signals and block exit if necessary.
Adds a global Terminator class to manage signals and exit blocking.
BUG=7392
TEST=unit tests, gmerged on device, initctl stop update-engine
Change-Id: I2291d4eb55240a6662b18ff834af161d957bce2f
Review URL: http://codereview.chromium.org/3608015
diff --git a/SConstruct b/SConstruct
index c42b528..1a73466 100644
--- a/SConstruct
+++ b/SConstruct
@@ -264,6 +264,7 @@
split_file_writer.cc
subprocess.cc
tarjan.cc
+ terminator.cc
topological_sort.cc
update_attempter.cc
update_check_scheduler.cc
diff --git a/delta_performer.cc b/delta_performer.cc
index f807665..10031eb 100644
--- a/delta_performer.cc
+++ b/delta_performer.cc
@@ -23,6 +23,7 @@
#include "update_engine/payload_signer.h"
#include "update_engine/prefs_interface.h"
#include "update_engine/subprocess.h"
+#include "update_engine/terminator.h"
using std::min;
using std::string;
@@ -209,6 +210,8 @@
next_operation_num_ - manifest_.install_operations_size());
if (!CanPerformInstallOperation(op))
break;
+ ScopedTerminatorExitUnblocker exit_unblocker =
+ ScopedTerminatorExitUnblocker(); // Avoids a compiler unused var bug.
// Log every thousandth operation, and also the first and last ones
if ((next_operation_num_ % 1000 == 0) ||
(next_operation_num_ + 1 == total_operations)) {
@@ -221,6 +224,7 @@
// that if the operation gets interrupted, we don't try to resume the
// update.
if (!IsIdempotentOperation(op)) {
+ Terminator::set_exit_blocked(true);
ResetUpdateProgress(prefs_);
}
if (op.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE ||
@@ -574,9 +578,10 @@
}
bool DeltaPerformer::CheckpointUpdateProgress() {
- // First reset the progress in case we die in the middle of the state update.
- ResetUpdateProgress(prefs_);
+ Terminator::set_exit_blocked(true);
if (last_updated_buffer_offset_ != buffer_offset_) {
+ // Resets the progress in case we die in the middle of the state update.
+ ResetUpdateProgress(prefs_);
TEST_AND_RETURN_FALSE(
prefs_->SetString(kPrefsUpdateStateSHA256Context,
hash_calculator_.GetContext()));
diff --git a/generate_delta_main.cc b/generate_delta_main.cc
index c66d9d1..8c5d6a9 100644
--- a/generate_delta_main.cc
+++ b/generate_delta_main.cc
@@ -21,6 +21,7 @@
#include "update_engine/delta_performer.h"
#include "update_engine/prefs.h"
#include "update_engine/subprocess.h"
+#include "update_engine/terminator.h"
#include "update_engine/update_metadata.pb.h"
#include "update_engine/utils.h"
@@ -61,6 +62,7 @@
g_thread_init(NULL);
google::ParseCommandLineFlags(&argc, &argv, true);
CommandLine::Init(argc, argv);
+ Terminator::Init();
Subprocess::Init();
logging::InitLogging("delta_generator.log",
logging::LOG_ONLY_TO_SYSTEM_DEBUG_LOG,
diff --git a/main.cc b/main.cc
index daa5eda..94393c3 100644
--- a/main.cc
+++ b/main.cc
@@ -20,6 +20,7 @@
#include "update_engine/dbus_service.h"
#include "update_engine/prefs.h"
#include "update_engine/subprocess.h"
+#include "update_engine/terminator.h"
#include "update_engine/update_attempter.h"
#include "update_engine/update_check_scheduler.h"
@@ -93,6 +94,7 @@
g_thread_init(NULL);
dbus_g_thread_init();
base::AtExitManager exit_manager; // Required for base/rand_util.h.
+ chromeos_update_engine::Terminator::Init();
chromeos_update_engine::Subprocess::Init();
google::ParseCommandLineFlags(&argc, &argv, true);
CommandLine::Init(argc, argv);
diff --git a/terminator.cc b/terminator.cc
new file mode 100644
index 0000000..1d33e32
--- /dev/null
+++ b/terminator.cc
@@ -0,0 +1,36 @@
+// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "update_engine/terminator.h"
+
+#include <cstdlib>
+
+namespace chromeos_update_engine {
+
+volatile sig_atomic_t Terminator::exit_blocked_ = 0;
+volatile sig_atomic_t Terminator::exit_requested_ = 0;
+
+void Terminator::Init() {
+ signal(SIGTERM, HandleSignal);
+}
+
+void Terminator::Exit() {
+ exit(0);
+}
+
+void Terminator::HandleSignal(int signum) {
+ if (exit_blocked_ == 0) {
+ Exit();
+ }
+ exit_requested_ = 1;
+}
+
+ScopedTerminatorExitUnblocker::~ScopedTerminatorExitUnblocker() {
+ Terminator::set_exit_blocked(false);
+ if (Terminator::exit_requested()) {
+ Terminator::Exit();
+ }
+}
+
+} // namespace chromeos_update_engine
diff --git a/terminator.h b/terminator.h
new file mode 100644
index 0000000..1c014ea
--- /dev/null
+++ b/terminator.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_TERMINATOR_H__
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_TERMINATOR_H__
+
+#include <signal.h>
+
+namespace chromeos_update_engine {
+
+// A class allowing graceful delayed exit.
+class Terminator {
+ public:
+ // Initializes the terminator and sets up signal handlers.
+ static void Init();
+
+ // Terminates the current process.
+ static void Exit();
+
+ // Set to true if the terminator should block termination requests in an
+ // attempt to block exiting.
+ static void set_exit_blocked(bool block) { exit_blocked_ = block ? 1 : 0; }
+
+ // Returns true if the system is trying to terminate the process, false
+ // otherwise. Returns true only if exit was blocked when the termination
+ // request arrived.
+ static bool exit_requested() { return exit_requested_ != 0; }
+
+ private:
+ // The signal handler.
+ static void HandleSignal(int signum);
+
+ static volatile sig_atomic_t exit_blocked_;
+ static volatile sig_atomic_t exit_requested_;
+};
+
+class ScopedTerminatorExitUnblocker {
+ public:
+ ~ScopedTerminatorExitUnblocker();
+};
+
+} // namespace chromeos_update_engine
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_TERMINATOR_H__
diff --git a/testrunner.cc b/testrunner.cc
index 3e40e1f..748dd5f 100644
--- a/testrunner.cc
+++ b/testrunner.cc
@@ -4,21 +4,24 @@
// based on pam_google_testrunner.cc
+#include <base/at_exit.h>
+#include <base/command_line.h>
#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-bindings.h>
#include <dbus/dbus-glib-lowlevel.h>
#include <glib.h>
#include <glib-object.h>
#include <gtest/gtest.h>
-#include "base/at_exit.h"
-#include "base/command_line.h"
+
#include "update_engine/subprocess.h"
+#include "update_engine/terminator.h"
int main(int argc, char **argv) {
::g_type_init();
g_thread_init(NULL);
dbus_g_thread_init();
base::AtExitManager exit_manager;
+ chromeos_update_engine::Terminator::Init();
chromeos_update_engine::Subprocess::Init();
CommandLine::Init(argc, argv);
::testing::InitGoogleTest(&argc, argv);