#
# add_file "tests/t_database_sig_cleanup.at"
#
# patch "ChangeLog"
# from [016732448d020c58a1245727ae00ec66f9533fdb]
# to [028ab1eab281874b48bc82bfeeca036c574ca51a]
#
# patch "database.cc"
# from [5b488d9296a1165037f850298c34720b5e4fb253]
# to [034cc97a43dccb1c7be5388339af9f33fe6597e4]
#
# patch "database.hh"
# from [dc62f8b1975f2884f63949fd701072a409ce0740]
# to [db62b6b964da9632dc1b04024eff7b749bce8fe0]
#
# patch "keys.cc"
# from [3fb812c189636b0fb652994edb0b82fde2d71f93]
# to [a2f02dffc377c4d2f0f8ba613db1b0c4230d3949]
#
# patch "main.cc"
# from [93c14a170425bd2be15e68d0f4eed476f9b77144]
# to [0981328eff2cd408bb7d9b13208b16243feec637]
#
# patch "monotone.cc"
# from [8c23f5814ae8730ac72e4c4e51c5ea7aaed31a3b]
# to [c44e323fa88aa1053a8fd0060f418b1c4870123b]
#
# patch "sanity.cc"
# from [e21f793216b8f4177d0dbcb663de917140ae3acb]
# to [d81f699c8daf70322c2fe440c7a366ac13aefda6]
#
# patch "sanity.hh"
# from [5a3b4f9635067d92301924a42621085c15a33188]
# to [0e176663e0e3f7a8e26cb0c1abb194366ea7d878]
#
# patch "tests/t_database_sig_cleanup.at"
# from []
# to [22a764be6d58c7bc266abcd21aaaed47e765cdb3]
#
# patch "testsuite.at"
# from [3dd7d23b79312c68c41089c651f9d1d7e91f150e]
# to [813048e60d51b50221c35169d8bb6ac11bdf57d8]
#
========================================================================
--- ChangeLog 016732448d020c58a1245727ae00ec66f9533fdb
+++ ChangeLog 028ab1eab281874b48bc82bfeeca036c574ca51a
@@ -1,3 +1,14 @@
+2005-10-19 Matt Johnston
+
+ * main.cc, database.{cc,hh}: SIGINT and SIGTERM handlers
+ exit gracefully, and try to ROLLBACK+close any databases to clean up
+ .db-journal files. Added new database::close() method to be used
+ rather than sqlite_close() directly
+ * monotone.{cc,hh}, sanity.{cc.hh}: move clean_shutdown flag to
+ global_sanity
+ * tests/t_database_sig_cleanup.at: test it
+ * keys.cc: don't L() private key
+
2005-10-19 Matthew A. Nicholson
* std_hooks.lua: Minor correction to vim warning during 3-way merge.
========================================================================
--- database.cc 5b488d9296a1165037f850298c34720b5e4fb253
+++ database.cc 034cc97a43dccb1c7be5388339af9f33fe6597e4
@@ -53,6 +53,12 @@
int const any_rows = -1;
int const any_cols = -1;
+namespace
+{
+ // track all open databases for close_all_databases() handler
+ set sql_contexts;
+}
+
extern "C" {
// some wrappers to ease migration
const char *sqlite3_value_text_s(sqlite3_value *v);
@@ -462,7 +468,7 @@
calculate_schema_id(__sql, id);
- sqlite3_close(__sql);
+ close();
out << F("database schema version: %s") % id << endl;
}
@@ -475,7 +481,7 @@
open();
migrate_monotone_schema(__sql, __app);
- sqlite3_close(__sql);
+ close();
}
void
@@ -537,11 +543,7 @@
// trigger destructors to finalize cached statements
statement_cache.clear();
- if (__sql)
- {
- sqlite3_close(__sql);
- __sql = 0;
- }
+ close();
}
void
@@ -2429,13 +2431,33 @@
{
int error;
+ I(!__sql);
+
error = sqlite3_open(filename.as_external().c_str(), &__sql);
+ if (__sql)
+ {
+ I(sql_contexts.find(__sql) == sql_contexts.end());
+ sql_contexts.insert(__sql);
+ }
+
N(!error, (F("could not open database '%s': %s")
% filename % string(sqlite3_errmsg(__sql))));
}
+void
+database::close()
+{
+ if (__sql)
+ {
+ sqlite3_close(__sql);
+ I(sql_contexts.find(__sql) != sql_contexts.end());
+ sql_contexts.erase(__sql);
+ __sql = 0;
+ }
+}
+
// transaction guards
transaction_guard::transaction_guard(database & d) : committed(false), db(d)
@@ -2455,3 +2477,22 @@
{
committed = true;
}
+
+// called to avoid foo.db-journal files hanging around if we exit cleanly
+// without unwinding the stack (happens with SIGINT & SIGTERM)
+void
+close_all_databases()
+{
+ L(F("attempting to rollback and close %d databases") % sql_contexts.size());
+ for (set::iterator i = sql_contexts.begin();
+ i != sql_contexts.end(); i++)
+ {
+ // the ROLLBACK is required here, even though the sqlite docs
+ // imply that transactions are rolled back on database closure
+ int exec_err = sqlite3_exec(*i, "ROLLBACK", NULL, NULL, NULL);
+ int close_err = sqlite3_close(*i);
+
+ L(F("exec_err = %d, close_err = %d") % exec_err % close_err);
+ }
+ sql_contexts.clear();
+}
========================================================================
--- database.hh dc62f8b1975f2884f63949fd701072a409ce0740
+++ database.hh db62b6b964da9632dc1b04024eff7b749bce8fe0
@@ -203,6 +203,7 @@
void check_filename();
void open();
+ void close();
public:
@@ -458,6 +459,8 @@
void commit();
};
+void
+close_all_databases();
#endif // __DATABASE_HH__
========================================================================
--- keys.cc 3fb812c189636b0fb652994edb0b82fde2d71f93
+++ keys.cc a2f02dffc377c4d2f0f8ba613db1b0c4230d3949
@@ -192,13 +192,11 @@
bool force = force_from_user;
L(F("base64-decoding %d-byte private key\n") % priv().size());
- L(F("priv '%s'\n") % priv());
decode_base64(priv, decoded_key);
for (int i = 0; i < 3; ++i)
{
get_passphrase(lua, id, phrase, false, force);
L(F("have %d-byte encrypted private key\n") % decoded_key().size());
- L(F("decoded '%s'\n") % decoded_key());
shared_ptr pkcs8_key;
try
========================================================================
--- main.cc 93c14a170425bd2be15e68d0f4eed476f9b77144
+++ main.cc 0981328eff2cd408bb7d9b13208b16243feec637
@@ -25,6 +25,10 @@
#include
#include
+#include "i18n.h"
+#include "database.hh"
+#include "sanity.hh"
+
// Microsoft + other compatible compilers such as Intel
#if defined(_MSC_VER) || (defined(__MWERKS__) && __MWERKS__ >= 0x3000)
#define MS_STRUCTURED_EXCEPTION_HANDLING
@@ -266,6 +270,8 @@
struct sigaction old_SIGSEGV_action;
struct sigaction old_SIGBUS_action;
struct sigaction old_SIGABRT_action;
+ struct sigaction old_SIGTERM_action;
+ struct sigaction old_SIGINT_action;
struct sigaction old_SIGPIPE_action;
all_signals_action.sa_flags = 0;
@@ -281,10 +287,13 @@
sigaction(SIGSEGV, &all_signals_action, &old_SIGSEGV_action);
sigaction(SIGBUS , &all_signals_action, &old_SIGBUS_action);
sigaction(SIGABRT, &all_signals_action, &old_SIGABRT_action);
+ sigaction(SIGTERM, &all_signals_action, &old_SIGTERM_action);
+ sigaction(SIGINT, &all_signals_action, &old_SIGINT_action);
sigaction(SIGPIPE, &ignore_signals_action, &old_SIGPIPE_action);
int result = 0;
bool trapped_signal = false;
+ bool clean_signal_exit = false;
char const *em = NULL;
volatile int sigtype = sigsetjmp(jump_buf, 1);
@@ -312,6 +321,14 @@
case SIGBUS:
em = "signal: memory access violation";
break;
+ case SIGINT:
+ em = _("interrupted");
+ clean_signal_exit = true;
+ break;
+ case SIGTERM:
+ em = _("terminated by signal");
+ clean_signal_exit = true;
+ break;
default:
em = "signal: unrecognized signal";
}
@@ -323,6 +340,14 @@
sigaction(SIGBUS , &old_SIGBUS_action , sigaction_ptr());
sigaction(SIGABRT, &old_SIGABRT_action, sigaction_ptr());
sigaction(SIGPIPE, &old_SIGPIPE_action, sigaction_ptr());
+
+ if(clean_signal_exit)
+ {
+ global_sanity.clean_shutdown = true;
+ ui.inform(em);
+ close_all_databases();
+ return 1;
+ }
if(trapped_signal)
throw unix_signal_exception(em);
========================================================================
--- monotone.cc 8c23f5814ae8730ac72e4c4e51c5ea7aaed31a3b
+++ monotone.cc c44e323fa88aa1053a8fd0060f418b1c4870123b
@@ -125,12 +125,10 @@
// in other words, this program should *never* unexpectedly terminate
// without dumping some diagnostics.
-static bool clean_shutdown;
-
void
dumper()
{
- if (!clean_shutdown)
+ if (!global_sanity.clean_shutdown)
global_sanity.dump_buffer();
Botan::Init::deinitialize();
@@ -237,7 +235,6 @@
int
cpp_main(int argc, char ** argv)
{
- clean_shutdown = false;
int ret = 0;
atexit(&dumper);
@@ -367,12 +364,12 @@
case OPT_VERSION:
print_version();
- clean_shutdown = true;
+ global_sanity.clean_shutdown = true;
return 0;
case OPT_FULL_VERSION:
print_full_version();
- clean_shutdown = true;
+ global_sanity.clean_shutdown = true;
return 0;
case OPT_REVISION:
@@ -578,22 +575,22 @@
poptPrintHelp(ctx(), stdout, 0);
cout << endl;
commands::explain_usage(u.which, cout);
- clean_shutdown = true;
+ global_sanity.clean_shutdown = true;
return 2;
}
}
catch (informative_failure & inf)
{
ui.inform(inf.what);
- clean_shutdown = true;
+ global_sanity.clean_shutdown = true;
return 1;
}
catch (std::ios_base::failure const & ex)
{
- clean_shutdown = true;
+ global_sanity.clean_shutdown = true;
return 1;
}
- clean_shutdown = true;
+ global_sanity.clean_shutdown = true;
return ret;
}
========================================================================
--- sanity.cc e21f793216b8f4177d0dbcb663de917140ae3acb
+++ sanity.cc d81f699c8daf70322c2fe440c7a366ac13aefda6
@@ -30,7 +30,8 @@
sanity global_sanity;
sanity::sanity() :
- debug(false), quiet(false), relaxed(false), logbuf(0xffff), already_dumping(false)
+ debug(false), quiet(false), relaxed(false), logbuf(0xffff),
+ already_dumping(false), clean_shutdown(false)
{
std::string flavour;
get_system_flavour(flavour);
========================================================================
--- sanity.hh 5a3b4f9635067d92301924a42621085c15a33188
+++ sanity.hh 0e176663e0e3f7a8e26cb0c1abb194366ea7d878
@@ -61,6 +61,7 @@
system_path filename;
std::string gasp_dump;
bool already_dumping;
+ bool clean_shutdown;
std::vector musings;
void log(boost::format const & fmt,
========================================================================
--- tests/t_database_sig_cleanup.at
+++ tests/t_database_sig_cleanup.at 22a764be6d58c7bc266abcd21aaaed47e765cdb3
@@ -0,0 +1,44 @@
+# -*- Autoconf -*-
+
+AT_SETUP([database is closed on signal exit])
+
+MONOTONE_SETUP
+
+# this test checks that .db-journal files aren't left lying about if the
+# process is killed with SIGTERM or SIGINT
+
+AT_DATA(testfile, [stuff
+])
+AT_CHECK(MONOTONE add testfile, [], [ignore], [ignore])
+
+# a hack to make the monotone process hang around with the database locked.
+
+AT_DATA(wait.lua, [
+function get_passphrase(key) sleep(1000) end
+])
+
+
+# SIGTERM first
+AT_CHECK(MONOTONE --rcfile=wait.lua --branch=testbranch commit --message=blah-blah & echo $! > monotone_commit.pid, [], [ignore], [ignore])
+AT_CHECK(sleep 2, [], [ignore], [ignore])
+AT_CHECK(test -f test.db-journal, [], [ignore], [ignore])
+AT_CHECK(kill -TERM `cat monotone_commit.pid`, [], [ignore], [ignore])
+AT_CHECK(test -f test.db-journal, [1], [ignore], [ignore])
+
+
+# and again for SIGINT
+AT_CHECK(MONOTONE --rcfile=wait.lua --branch=testbranch commit --message=blah-blah & echo $! > monotone_commit.pid, [], [ignore], [ignore])
+AT_CHECK(sleep 2, [], [ignore], [ignore])
+AT_CHECK(test -f test.db-journal, [], [ignore], [ignore])
+AT_CHECK(kill -INT `cat monotone_commit.pid`, [], [ignore], [ignore])
+AT_CHECK(test -f test.db-journal, [1], [ignore], [ignore])
+
+
+# should be cleaned up for SIGSEGV
+AT_CHECK(MONOTONE --rcfile=wait.lua --branch=testbranch commit --message=blah-blah & echo $! > monotone_commit.pid, [], [ignore], [ignore])
+AT_CHECK(sleep 2, [], [ignore], [ignore])
+AT_CHECK(test -f test.db-journal, [], [ignore], [ignore])
+AT_CHECK(kill -SEGV `cat monotone_commit.pid`, [], [ignore], [ignore])
+AT_CHECK(test -f test.db-journal, [], [ignore], [ignore])
+
+AT_CLEANUP
========================================================================
--- testsuite.at 3dd7d23b79312c68c41089c651f9d1d7e91f150e
+++ testsuite.at 813048e60d51b50221c35169d8bb6ac11bdf57d8
@@ -717,3 +717,4 @@
m4_include(tests/t_add_inside_MT.at)
m4_include(tests/t_annotate_renames.at)
m4_include(tests/t_config_confdir.at)
+m4_include(tests/t_database_sig_cleanup.at)