/* SciGraphica - Scientific graphics and data manipulation
* Copyright (C) 2001 Adrian E. Feiguin <feiguin@ifir.edu.ar>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* If you need to do signal handling elsewhere in the code, PLEASE use sigaction(2)
* instead of signal(3) for doing so. The effect of signal() is platform dependent
* and can break the careful signalhandling as initialized here. */
#include <stdio.h>
#include <signal.h>
#include <glib.h>
#include "sg_project_rescue.h"
#include "sg.h"
#include "sg_dialogs.h"
#include "python/python_main.h"
/* defined will include additional debugging code */
#undef DEBUG_RESCUE
#define SG_WWW "http://scigraphica.sourceforge.net"
#define SG_EMAIL "scigraphica-devel@lists.sourceforge.net"
/* set dummy values for undefined flags we use */
#ifndef SA_NODEFER
#define SA_NODEFER 0
#endif
#ifndef SA_RESTART
#define SA_RESTART 0
#endif
#ifndef SA_RESETHAND
#define SA_RESETHAND 0
#endif
static void bailout(int signum);
static gint bailout_idle(gpointer data);
static void rescue(int signum);
static int connect_sigaction(int signum, void (*function)(int),
unsigned int flags, sigset_t mask);
#ifdef DEBUG_RESCUE
static void sigaction_info(void);
#endif
/* connect signals to new handlers, see sigaction(2) and
* http://www.gnu.org/manual -> glibc -> Signal Handling */
gint
sg_project_rescue_init( void )
{
int i;
int return_value = 0;
sigset_t bailout_signals;
sigset_t rescue_signals;
sigset_t default_mask;
const unsigned int default_flags = SA_RESTART;
const unsigned int rescue_flags = SA_RESTART | SA_NODEFER;
/* clear all signal masks */
sigemptyset(&default_mask);
sigemptyset(&bailout_signals);
sigemptyset(&rescue_signals);
#define CONNECT_SIGNAL_TO_RESCUE(SIGNAL) \
return_value += connect_sigaction(SIGNAL,rescue,rescue_flags,default_mask); \
sigaddset(&rescue_signals, SIGNAL);
#define CONNECT_SIGNAL_TO_BAILOUT(SIGNAL) \
return_value += connect_sigaction(SIGNAL,bailout,default_flags,default_mask); \
sigaddset(&bailout_signals, SIGNAL);
#define CONNECT_SIGNAL_TO_DFL(SIGNAL) \
return_value += connect_sigaction(SIGNAL,SIG_DFL,default_flags,default_mask);
#define CONNECT_SIGNAL_TO_IGN(SIGNAL) \
return_value += connect_sigaction(SIGNAL,SIG_IGN,default_flags,default_mask);
#ifdef DEBUG_RESCUE
printf("Before sg_project_rescue_init:\n");
sigaction_info();
#endif
/* Program Error Signals
* These signals are generated when a serious program error is detected
* by the operating system or the computer itself. In general, all of
* these signals are indications that the program is seriously broken
* in some way, and there's usually no way to continue the computation
* which encountered the error.
* The default action for all of these signals is to cause the process
* to terminate.
*/
#ifdef SIGFPE
CONNECT_SIGNAL_TO_RESCUE( SIGFPE )
#endif
#ifdef SIGILL
CONNECT_SIGNAL_TO_RESCUE( SIGILL )
#endif
#ifdef SIGSEGV
CONNECT_SIGNAL_TO_RESCUE( SIGSEGV )
#endif
#ifdef SIGBUS
CONNECT_SIGNAL_TO_RESCUE( SIGBUS )
#endif
#ifdef SIGABRT
CONNECT_SIGNAL_TO_RESCUE( SIGABRT )
#endif
#ifdef SIGIOT
CONNECT_SIGNAL_TO_RESCUE( SIGIOT )
#endif
#ifdef SIGTRAP
CONNECT_SIGNAL_TO_RESCUE( SIGTRAP )
#endif
#ifdef SIGEMT
CONNECT_SIGNAL_TO_RESCUE( SIGEMT )
#endif
#ifdef SIGSYS
CONNECT_SIGNAL_TO_RESCUE( SIGSYS )
#endif
/* Termination Signals
* These signals are all used to tell a process to terminate, in one way
* or another.
* The (obvious) default action for all of these signals is to cause the
* process to terminate.
*/
#ifdef SIGTERM
CONNECT_SIGNAL_TO_BAILOUT( SIGTERM )
#endif
#ifdef SIGINT
CONNECT_SIGNAL_TO_BAILOUT( SIGINT )
#endif
#ifdef SIGQUIT
CONNECT_SIGNAL_TO_BAILOUT( SIGQUIT )
#endif
#ifdef SIGKILL
/* SIGKILL can not be caught or ignored */
#endif
#ifdef SIGHUP
CONNECT_SIGNAL_TO_BAILOUT( SIGHUP )
#endif
/* Alarm Signals
* These signals are used to indicate the expiration of timers.
* The default behavior for these signals is to cause program termination.
*/
#ifdef SIGALRM
CONNECT_SIGNAL_TO_DFL( SIGALRM )
#endif
#ifdef SIGVTALRM
CONNECT_SIGNAL_TO_DFL( SIGVTALRM )
#endif
#ifdef SIGPROF
CONNECT_SIGNAL_TO_DFL( SIGPROF )
#endif
/* Asynchronous I/O Signals
* These signals are used in conjunction with asynchronous I/O facilities.
* The default action for these signals is to ignore them.
*/
#ifdef SIGIO
CONNECT_SIGNAL_TO_DFL( SIGIO )
#endif
#ifdef SIGURG
CONNECT_SIGNAL_TO_DFL( SIGURG )
#endif
#ifdef SIGPOLL
CONNECT_SIGNAL_TO_DFL( SIGPOLL )
#endif
/* Job Control Signals
* These signals are used to support job control. If your system
* doesn't support job control, then these macros are defined but
* the signals themselves can't be raised or handled.
*/
#ifdef SIGCHLD
CONNECT_SIGNAL_TO_DFL( SIGCHLD )
#endif
#ifdef SIGCLD
CONNECT_SIGNAL_TO_DFL( SIGCLD )
#endif
#ifdef SIGCONT
CONNECT_SIGNAL_TO_DFL( SIGCONT )
#endif
#ifdef SIGSTOP
/* SIGSTOP can not be caught or ignored */
#endif
#ifdef SIGTSTP
CONNECT_SIGNAL_TO_DFL( SIGTSTP )
#endif
#ifdef SIGTTIN
CONNECT_SIGNAL_TO_DFL( SIGTTIN )
#endif
#ifdef SIGTTOU
CONNECT_SIGNAL_TO_DFL( SIGTTOU )
#endif
/* Operation Error Signals
* These signals are used to report various errors generated by an
* operation done by the program. They do not necessarily indicate a
* programming error in the program, but an error that prevents an
* operating system call from completing.
* The default action for all of them is to cause the process to terminate.
*/
#ifdef SIGPIPE
CONNECT_SIGNAL_TO_BAILOUT( SIGPIPE )
#endif
#ifdef SIGLOST
CONNECT_SIGNAL_TO_BAILOUT( SIGLOST )
#endif
#ifdef SIGXCPU
CONNECT_SIGNAL_TO_BAILOUT( SIGXCPU )
#endif
#ifdef SIGXFSZ
CONNECT_SIGNAL_TO_BAILOUT( SIGXFSZ )
#endif
/* Miscellaneous Signals These signals are used for various other purposes.
* In general, they will not affect your program unless it explicitly uses
* them for something.
*/
#ifdef SIGUSR1
CONNECT_SIGNAL_TO_DFL( SIGUSR1 )
#endif
#ifdef SIGUSR2
CONNECT_SIGNAL_TO_DFL( SIGUSR2 )
#endif
#ifdef SIGWINCH
CONNECT_SIGNAL_TO_DFL( SIGWINCH )
#endif
#ifdef SIGINFO
CONNECT_SIGNAL_TO_DFL( SIGINFO )
#endif
/* Other Signals
* Stack fault on coprocessor / Power fail
* No information available.
*/
#ifdef SIGSTKFLT
CONNECT_SIGNAL_TO_DFL( SIGSTKFLT )
#endif
#ifdef SIGPWR
CONNECT_SIGNAL_TO_DFL( SIGPWR )
#endif
#undef CONNECT_SIGNAL_TO_RESCUE
#undef CONNECT_SIGNAL_TO_BAILOUT
#undef CONNECT_SIGNAL_TO_DFL
#undef CONNECT_SIGNAL_TO_IGN
/* add all bailout signals to the mask of each rescue signal
* this blocks bailout signals during the rescue */
for (i = 1; i < NSIG; i++)
if (sigismember(&rescue_signals, i))
{int n;
struct sigaction rescue_action;
sigaction(i, NULL, &rescue_action);
for (n = 1; n < NSIG; n++)
if (sigismember(&bailout_signals, n))
sigaddset(&rescue_action.sa_mask, n);
return_value += sigaction(i, &rescue_action, NULL);
}
#ifdef DEBUG_RESCUE
printf("After sg_project_rescue_init:\n");
sigaction_info();
#endif
if (return_value) return -1;
else return 0;
}
static int
connect_sigaction(int signum, void (*function)(int), unsigned int flags, sigset_t mask)
{
struct sigaction action;
action.sa_handler = function;
action.sa_flags = flags;
action.sa_mask = mask;
return sigaction(signum, &action, NULL);
}
/* bailout does the real work as an idle process, when gtk is "unbusy".
* it only catches signals that are non-fatal, so ask whether
* to lose the unsaved changes or to keep the application running */
static gint bailout_idle_id = 0;
static void
bailout( int signum )
{
static int static_signum;
#ifdef DEBUG_RESCUE
printf("Inside bailout: (%d) %s\n", signum, strsignal(signum));
sigaction_info();
#endif
/* ignore consecutive bailout signals */
if (bailout_idle_id) return;
if (project_changed == FALSE) bailout_idle(NULL);
static_signum = signum;
bailout_idle_id = gtk_idle_add(bailout_idle, &static_signum);
}
static gint
bailout_idle( gpointer data )
{
if (data)
{char message[80];
gtk_idle_remove(bailout_idle_id);
snprintf(message, 80, "%s\nExit losing unsaved changes?", strsignal( *((int*)data)) );
if (sg_accept_dialog(message, 1) != YES_CLICKED) return (bailout_idle_id = 0);
}
/* remove autosave file */
sg_project_autosave_set(0);
/* kill python terminal */
child_died(0);
gtk_exit(0);
}
/* rescue tries to save the current project and exits
* memory is possibly corrupted, so rely on static storage only */
static void
rescue(int signum)
{
static char message[250] = "";
static int signal_count = 0;
/* do what SA_RESETHAND and SA_NODEFER flags supposed to do here automatically
* why are these signalflags ignored? Due to Python, Imlib, Gtk? -Rob- */
{static struct sigaction action;
sigaction(signum, NULL, &action);
if (SA_RESETHAND) action.sa_handler = (action.sa_flags & SA_RESETHAND ? SIG_DFL : rescue);
sigaction(signum, &action, NULL);
sigemptyset(&action.sa_mask);
sigaddset(&action.sa_mask, signum);
if (SA_NODEFER) sigprocmask(action.sa_flags & SA_NODEFER ? SIG_UNBLOCK : SIG_BLOCK, &action.sa_mask, NULL);
}
#ifdef DEBUG_RESCUE
printf("Inside rescue: (%d) %s [signal_count = %d]\n", signum, strsignal(signum), signal_count);
sigaction_info();
#endif
if (signal_count++ == 0)
{static char rescue_file[250];
static int rescue_OK = FALSE;
/* prepare for the worst and keep fingers crossed */
snprintf(message, 250, "%s\n\n%s\n%s\n%s\n%s", strsignal(signum),
"\nSorry, failed to rescue the project",
"\nPlease submit a bug report to",
SG_EMAIL, "or " SG_WWW);
/* save project to its original location */
snprintf(rescue_file, 250, "%s/" RESCUE_PREFIX "%s", last_project_path, last_project_filename);
rescue_OK = sg_project_file_export_xml(rescue_file);
if (rescue_OK == FALSE)
{/* failed! try saving project to the current working directory */
snprintf(rescue_file, 250, RESCUE_PREFIX "%s", last_project_filename);
rescue_OK = sg_project_file_export_xml(rescue_file);
}
if (rescue_OK == FALSE && g_get_home_dir())
{/* failed again; as a last resort save to home directory */
snprintf(rescue_file, 250, "%s/" RESCUE_PREFIX "%s", g_get_home_dir(), last_project_filename);
rescue_OK = sg_project_file_export_xml(rescue_file);
}
if (rescue_OK) /* were we lucky? */
snprintf(message, 250, "%s\n\n%s\n%c%s%c\n%s\n%s\n%s", strsignal(signum),
"Project successfully saved to",
'"', rescue_file, '"',
"\nPlease submit a bug report to ",
SG_EMAIL, "or " SG_WWW);
}
else if ( signal_count < 10 )
return; /* ignore up to 10 consecutive rescue signals */
/* when a signal is generated externally by "kill -[SIG] [SG-pid]",
* the following dialog-call will cause a stream of GLib warnings:
* g_main_iterate(): main loop already active in another thread
* don't know what to do about that -Rob- */
sg_message_dialog(message, 0);
/* kill python terminal */
child_died(0);
/* continue with the default handler and exit accordingly */
signal(signum, SIG_DFL);
raise(signum);
}
/* ----------------------------------------------------- */
/* ------- Facilities for signal-debugging only ------- */
/* ----------------------------------------------------- */
#ifdef DEBUG_RESCUE
#ifndef SA_SIGINFO
#define SA_SIGINFO 0
#endif
static const struct {
unsigned int value;
char name[10];
} flag[]={
#ifdef SA_ONSTACK
{SA_ONSTACK, " ONSTACK"},
#endif
#ifdef SA_RESTART
{SA_RESTART, " RESTART"},
#endif
#ifdef SA_NODEFER
{SA_NODEFER, " NODEFER"},
#endif
#ifdef SA_RESETHAND
{SA_RESETHAND, "RESETHAND"},
#endif
#ifdef SA_NOCLDSTOP
{SA_NOCLDSTOP, "NOCLDSTOP"},
#endif
#ifdef SA_NOCLDWAIT
{SA_NOCLDWAIT, "NOCLDWAIT"},
#endif
#ifdef SA_WAITSIG
{SA_WAITSIG, " WAITSIG"},
#endif
#ifdef SA_SIGINFO
{SA_SIGINFO, " SIGINFO"},
#endif
};
static const int number_of_flags = sizeof(flag)/sizeof(flag[0]);
static char*
strmask(sigset_t mask, char *result)
{
int i;
for (result[0]= '\0', i = 1; i < NSIG; i++)
{if ((i-1)%10==0 && i!=1) strcat(result, ".");
strcat(result, (sigismember(&mask, i) ? "1" : "0"));
}
return result;
}
/* sigaction_info prints to standard out the complete sigaction
* information table: flags - mask - handler - signal number/string */
static void
sigaction_info(void)
{
int n, m;
char mask[NSIG + NSIG/10];
struct sigaction action;
/* print the flag descriptions vertically */
for (printf("\n"), n = 0; n < strlen(flag[0].name); n++, printf("\n"))
for (m = 0; m < number_of_flags; m++)
printf(" %c", flag[m].name[n]);
/* print the sigaction information for signal number 1 to NSIG */
for (n = 1; n < NSIG; n++)
{char flags[1 + 2*number_of_flags],
handler[10];
/* print a separator line every 10 signal lines */
if ((n-1)%10 == 0)
{char c = (n == 1 ? '-' : '+');
for (m = 0; m < 2*number_of_flags; m++) printf("-");
for (printf("-%c-", c), m = 0; m < NSIG + NSIG/10; m++) printf("-");
printf("%c-----------%c------\n", c, c);
}
sigaction(n, NULL, &action);
/* convert the flags information into a 0/1 string set */
for (flags[0] = '\0', m = 0; m < number_of_flags; m++)
if (flag[m].value) strcat(flags, (action.sa_flags & flag[m].value ? " 1" : " 0") );
else strcat(flags, " X");
/* convert the handler information into readable form */
if (action.sa_flags & SA_SIGINFO)
{if (action.sa_sigaction == NULL) sprintf(handler, "NULL ");
else sprintf(handler, "%p", action.sa_sigaction);
}
else
{if (action.sa_handler == SIG_DFL) sprintf(handler, "SIG_DFL ");
else if (action.sa_handler == SIG_IGN) sprintf(handler, "SIG_IGN ");
else if (action.sa_handler == bailout) sprintf(handler, "bailout ");
else if (action.sa_handler == rescue) sprintf(handler, "rescue ");
else if (action.sa_handler == NULL) sprintf(handler, "NULL ");
else sprintf(handler, "%p", action.sa_handler);
}
/* print the sigaction information as a single line */
printf("%s | %s | %s | (%2d) %s\n", flags, strmask(action.sa_mask, mask),
handler, n, strsignal(n));
}
sigprocmask(0, NULL, &action.sa_mask);
for (n = 0; n < 2*number_of_flags - strlen("sigprocmask"); n++) printf(" ");
printf("sigprocmask = %s\n\n\n", strmask(action.sa_mask, mask));
}
#endif /* DEBUG_RESCUE */
syntax highlighted by Code2HTML, v. 0.9.1