From 61de587a2c493d61be81701fc025b1aef9e20a3e Mon Sep 17 00:00:00 2001 From: Sven Trenkel Date: Wed, 2 Dec 2009 23:47:34 +0100 Subject: [PATCH] Some signal dodging stuff. --- src/python.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 51 insertions(+), 7 deletions(-) diff --git a/src/python.c b/src/python.c index b67a7950..86bad3da 100644 --- a/src/python.c +++ b/src/python.c @@ -1,6 +1,7 @@ #include #include +#include #if HAVE_PTHREAD_H # include #endif @@ -729,20 +730,59 @@ static int cpy_shutdown(void) { else Py_DECREF(ret); } + PyErr_Print(); Py_Finalize(); return 0; } +static void cpy_int_handler(int sig) { + return; +} + static void *cpy_interactive(void *data) { - CPY_LOCK_THREADS - if (PyImport_ImportModule("readline") == NULL) { - /* This interactive session will suck. */ - cpy_log_exception("interactive session init"); - } - PyRun_InteractiveLoop(stdin, ""); - CPY_RELEASE_THREADS + sigset_t sigset; + struct sigaction sig_int_action, old; + + /* Signal handler in a plugin? Bad stuff, but the best way to + * handle it I guess. In an interactive session people will + * press Ctrl+C at some time, which will generate a SIGINT. + * This will cause collectd to shutdown, thus killing the + * interactive interpreter, and leaving the terminal in a + * mess. Chances are, this isn't what the user wanted to do. + * + * So this is the plan: + * 1. Block SIGINT in the main thread. + * 2. Install our own signal handler that does nothing. + * 3. Unblock SIGINT in the interactive thread. + * + * This will make sure that SIGINT won't kill collectd but + * still interrupt syscalls like sleep and pause. + * It does not raise a KeyboardInterrupt exception because so + * far nobody managed to figure out how to do that. */ + memset (&sig_int_action, '\0', sizeof (sig_int_action)); + sig_int_action.sa_handler = cpy_int_handler; + sigaction (SIGINT, &sig_int_action, &old); + + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); + PyEval_AcquireThread(state); + if (PyImport_ImportModule("readline") == NULL) { + /* This interactive session will suck. */ + cpy_log_exception("interactive session init"); + } + PyRun_InteractiveLoop(stdin, ""); + PyErr_Print(); + PyEval_ReleaseThread(state); NOTICE("python: Interactive interpreter exited, stopping collectd ..."); + /* Restore the original collectd SIGINT handler and raise SIGINT. + * The main thread still has SIGINT blocked and there's nothing we + * can do about that so this thread will handle it. But that's not + * important, except that it won't interrupt the main loop and so + * it might take a few seconds before collectd really shuts down. */ + sigaction (SIGINT, &old, NULL); raise(SIGINT); + pause(); return NULL; } @@ -750,6 +790,7 @@ static int cpy_init(void) { cpy_callback_t *c; PyObject *ret; static pthread_t thread; + sigset_t sigset; PyEval_InitThreads(); /* Now it's finally OK to use python threads. */ @@ -760,6 +801,9 @@ static int cpy_init(void) { else Py_DECREF(ret); } + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + pthread_sigmask(SIG_BLOCK, &sigset, NULL); state = PyEval_SaveThread(); if (do_interactive) { if (pthread_create(&thread, NULL, cpy_interactive, NULL)) { -- 2.11.0