gambas-source-code/main/lib/signal/csignal.c
Benoît Minisini 4afe7caf6b [INTERPRETER]
* BUG: Disable debugging messages from signal management routines.

[GB.SIGNAL]
* NEW: Call previous signal handlers automatically when a specific signal 
  is catched.


git-svn-id: svn://localhost/gambas/trunk@4722 867c0c6c-44f3-4631-809d-bfa615b0a4ec
2012-05-08 17:15:54 +00:00

358 lines
7.3 KiB
C

/***************************************************************************
csignal.c
(c) 2000-2012 Benoît Minisini <gambas@users.sourceforge.net>
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, 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., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
***************************************************************************/
#define __CSIGNAL_C
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "main.h"
#include "csignal.h"
#ifndef SIGPOLL
#define SIGPOLL SIGIO
#endif
#ifndef SIGPWR
#define SIGPWR -1
#endif
#if !defined(OS_BSD) && !defined(OS_CYGWIN)
typedef
struct siginfo siginfo_t;
#endif
// The -1 signal is used for ignored signal numbers
/*#define DEBUG*/
#define SIGNAL_MAX 31
typedef
struct SIGNAL_HANDLER {
struct SIGNAL_HANDLER *next;
struct SIGNAL_HANDLER *prev;
int num;
bool catch;
struct sigaction action;
}
SIGNAL_HANDLER;
static int _signal = -1;
static SIGNAL_HANDLER *_handlers = NULL;
static bool _init_signal = FALSE;
static int _num_signal_watch = 0;
static int _pipe_signal[2];
static GB_FUNCTION _application_signal_func;
static void callback_signal(int fd, int type, void *data)
{
char num;
fprintf(stderr, "callback_signal\n");
if (read(fd, &num, 1) != 1)
return;
GB.Push(1, GB_T_INTEGER, num);
GB.Call(&_application_signal_func, 1, TRUE);
}
static void init_signal(void)
{
_num_signal_watch++;
if (_num_signal_watch > 1)
return;
if (GB.GetFunction(&_application_signal_func, (void *)GB.Application.StartupClass(), "Application_Signal", "i", ""))
{
GB.Error("No Application_Signal event handler defined in startup class");
return;
}
if (pipe(_pipe_signal) != 0)
{
GB.Error("Cannot create signal handler pipes");
return;
}
fcntl(_pipe_signal[0], F_SETFD, FD_CLOEXEC);
fcntl(_pipe_signal[1], F_SETFD, FD_CLOEXEC);
GB.Watch(_pipe_signal[0], GB_WATCH_READ, (void *)callback_signal, 0);
_init_signal = TRUE;
}
static void exit_signal(void)
{
if (_num_signal_watch <= 0)
return;
_num_signal_watch--;
if (_num_signal_watch)
return;
GB.Watch(_pipe_signal[0], GB_WATCH_NONE, NULL, 0);
close(_pipe_signal[0]);
close(_pipe_signal[1]);
_init_signal = FALSE;
}
static SIGNAL_HANDLER *find_handler(int num)
{
SIGNAL_HANDLER *sh;
sh = _handlers;
while (sh)
{
if (sh->num == num)
return sh;
sh = sh->next;
}
return NULL;
}
static void remove_handler(SIGNAL_HANDLER *handler)
{
if (handler->prev)
handler->prev->next = handler->next;
if (handler->next)
handler->next->prev = handler->prev;
if (handler == _handlers)
_handlers = handler->next;
if (handler->catch)
exit_signal();
GB.Free(POINTER(&handler));
}
static void add_handler(int num, struct sigaction *action)
{
SIGNAL_HANDLER *sh;
sh = find_handler(num);
if (sh)
{
sigaction(num, action, NULL);
return;
}
GB.Alloc(POINTER(&sh), sizeof(SIGNAL_HANDLER));
sh->num = num;
sh->next = _handlers;
sh->prev = NULL;
_handlers = sh;
sh->catch = (action->sa_flags & SA_SIGINFO) != 0;
if (sh->catch)
init_signal();
if (sigaction(num, action, &sh->action))
{
GB.Error("Unable to modify signal handler");
exit_signal();
return;
}
}
static void handle_signal(int num, siginfo_t *info, void *context)
{
SIGNAL_HANDLER *sh;
char cnum = (char)num;
int save_errno;
save_errno = errno;
for(;;)
{
if (write(_pipe_signal[1], &cnum, 1) == 1)
break;
if (errno != EINTR)
break;
}
// Call old signal handler
sh = find_handler(num);
if (sh->action.sa_handler != SIG_DFL && sh->action.sa_handler != SIG_IGN)
{
if (sh->action.sa_flags & SA_SIGINFO)
{
//fprintf(stderr, "Calling old action %p\n", _old_SIGCHLD_action.sa_sigaction);
(*sh->action.sa_sigaction)(num, info, context);
}
else
{
//fprintf(stderr, "Calling old handler %p\n", _old_SIGCHLD_action.sa_handler);
(*sh->action.sa_handler)(num);
}
}
errno = save_errno;
}
BEGIN_METHOD_VOID(Signal_Reset)
SIGNAL_HANDLER *sh;
if (_signal < 0)
return;
sh = find_handler(_signal);
if (!sh)
return;
if (sigaction(_signal, &sh->action, NULL))
{
GB.Error("Unable to reset signal handler");
return;
}
remove_handler(sh);
END_METHOD
BEGIN_METHOD_VOID(Signal_Ignore)
struct sigaction action;
if (_signal < 0)
return;
action.sa_handler = SIG_IGN;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
add_handler(_signal, &action);
END_METHOD
BEGIN_METHOD_VOID(Signal_Catch)
struct sigaction action;
if (_signal < 0)
return;
action.sa_sigaction = handle_signal;
sigemptyset(&action.sa_mask);
action.sa_flags = SA_SIGINFO;
add_handler(_signal, &action);
END_METHOD
BEGIN_METHOD(Signal_get, GB_INTEGER num)
int num = VARG(num);
if (num < -1 || num > SIGNAL_MAX)
{
GB.Error("Bad signal number");
return;
}
_signal = num;
RETURN_SELF();
END_METHOD
BEGIN_METHOD_VOID(Signal_exit)
exit_signal();
while (_handlers)
remove_handler(_handlers);
END_METHOD
GB_DESC CSignalHandlerDesc[] =
{
GB_DECLARE(".SignalHandler", 0), GB_VIRTUAL_CLASS(),
GB_STATIC_METHOD("Reset", NULL, Signal_Reset, NULL),
GB_STATIC_METHOD("Ignore", NULL, Signal_Ignore, NULL),
GB_STATIC_METHOD("Catch", NULL, Signal_Catch, NULL),
GB_END_DECLARE
};
GB_DESC CSignalDesc[] =
{
GB_DECLARE("Signal", 0),
GB_VIRTUAL_CLASS(),
GB_CONSTANT("SIGHUP", "i", SIGHUP),
GB_CONSTANT("SIGINT", "i", SIGINT),
GB_CONSTANT("SIGQUIT", "i", SIGQUIT),
GB_CONSTANT("SIGILL", "i", SIGILL),
GB_CONSTANT("SIGTRAP", "i", SIGTRAP),
GB_CONSTANT("SIGABRT", "i", SIGABRT),
#ifndef OS_CYGWIN
// Cygwin doesn't define this SIGNAL
GB_CONSTANT("SIGIOT", "i", SIGIOT),
#endif
GB_CONSTANT("SIGBUS", "i", SIGBUS),
GB_CONSTANT("SIGFPE", "i", SIGFPE),
GB_CONSTANT("SIGKILL", "i", SIGKILL),
GB_CONSTANT("SIGUSR1", "i", SIGUSR1),
GB_CONSTANT("SIGSEGV", "i", SIGSEGV),
GB_CONSTANT("SIGUSR2", "i", SIGUSR2),
GB_CONSTANT("SIGPIPE", "i", SIGPIPE),
GB_CONSTANT("SIGALRM", "i", SIGALRM),
GB_CONSTANT("SIGTERM", "i", SIGTERM),
//GB_CONSTANT("SIGSTKFLT", "i", SIGSTKFLT),
//GB_CONSTANT("SIGCLD", "i", SIGCLD),
GB_CONSTANT("SIGCHLD", "i", SIGCHLD),
GB_CONSTANT("SIGCONT", "i", SIGCONT),
GB_CONSTANT("SIGSTOP", "i", SIGSTOP),
GB_CONSTANT("SIGTSTP", "i", SIGTSTP),
GB_CONSTANT("SIGTTIN", "i", SIGTTIN),
GB_CONSTANT("SIGTTOU", "i", SIGTTOU),
GB_CONSTANT("SIGURG", "i", SIGURG),
GB_CONSTANT("SIGXCPU", "i", SIGXCPU),
GB_CONSTANT("SIGXFSZ", "i", SIGXFSZ),
GB_CONSTANT("SIGVTALRM", "i", SIGVTALRM),
GB_CONSTANT("SIGPROF", "i", SIGPROF),
GB_CONSTANT("SIGWINCH", "i", SIGWINCH),
GB_CONSTANT("SIGPOLL", "i", SIGPOLL),
GB_CONSTANT("SIGIO", "i", SIGIO),
GB_CONSTANT("SIGPWR", "i", SIGPWR),
GB_CONSTANT("SIGSYS", "i", SIGSYS),
GB_STATIC_METHOD("_exit", NULL, Signal_exit, NULL),
GB_STATIC_METHOD("_get", ".SignalHandler", Signal_get, "(Signal)i"),
GB_END_DECLARE
};