abc/src/misc/util/utilSignal.c

521 lines
12 KiB
C
Raw Normal View History

/**CFile****************************************************************
FileName [utilSignal.c]
SystemName [ABC: Logic synthesis and verification system.]
PackageName []
Synopsis []
Author []
Affiliation [UC Berkeley]
Date []
Revision []
***********************************************************************/
#ifndef _MSC_VER
#include <main.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <hashGen.h>
#include <errno.h>
#include <pthread.h>
#include "abc_global.h"
ABC_NAMESPACE_IMPL_START
////////////////////////////////////////////////////////////////////////
/// DECLARATIONS ///
////////////////////////////////////////////////////////////////////////
static Hash_Gen_t* watched_pid_hash = NULL;
static Hash_Gen_t* watched_tmp_files_hash = NULL;
static sigset_t* old_procmask;
////////////////////////////////////////////////////////////////////////
/// FUNCTION DEFINITIONS ///
////////////////////////////////////////////////////////////////////////
/**Function*************************************************************
Synopsis []
Description [Kills all watched child processes and remove all watched termporary files.]
SideEffects []
SeeAlso []
***********************************************************************/
void Util_SignalCleanup()
{
int i;
Hash_Gen_Entry_t* pEntry;
// kill all watched child processes
Hash_GenForEachEntry(watched_pid_hash, pEntry, i)
{
pid_t pid = (pid_t)pEntry->key;
pid_t ppid = (pid_t)pEntry->data;
if (getpid() == ppid)
{
kill(pid, SIGINT);
}
}
// remove watched temporary files
Hash_GenForEachEntry(watched_tmp_files_hash, pEntry, i)
{
int fname = (const char*)pEntry->key;
pid_t ppid = (pid_t)pEntry->data;
if( getpid() == ppid )
{
remove(fname);
}
}
}
/**Function*************************************************************
Synopsis []
Description [Sets up data structures needed for cleanup in signal handler.]
SideEffects []
SeeAlso []
***********************************************************************/
void Util_SignalStartHandler()
{
watched_pid_hash = Hash_GenAlloc(100, Hash_DefaultHashFuncInt, Hash_DefaultCmpFuncInt, 0);
watched_tmp_files_hash = Hash_GenAlloc(100, Hash_DefaultHashFuncStr, strcmp, 1);
}
/**Function*************************************************************
Synopsis []
Description [Frees data structures used for clean up in signal handler.]
SideEffects []
SeeAlso []
***********************************************************************/
void Util_SignalResetHandler()
{
int i;
Hash_Gen_Entry_t* pEntry;
sigset_t procmask, old_procmask;
sigemptyset(&procmask);
sigaddset(&procmask, SIGINT);
sigprocmask(SIG_BLOCK, &procmask, &old_procmask);
Hash_GenFree(watched_pid_hash);
watched_pid_hash = Hash_GenAlloc(100, Hash_DefaultHashFuncInt, Hash_DefaultCmpFuncInt, 0);
Hash_GenFree(watched_tmp_files_hash);
watched_tmp_files_hash = Hash_GenAlloc(100, Hash_DefaultHashFuncStr, strcmp, 1);
sigprocmask(SIG_SETMASK, &old_procmask, NULL);
}
void Util_SignalStopHandler()
{
int i;
Hash_Gen_Entry_t* pEntry;
Hash_GenFree(watched_pid_hash);
watched_pid_hash = NULL;
Hash_GenFree(watched_tmp_files_hash);
watched_tmp_files_hash = NULL;
}
/**Function*************************************************************
Synopsis []
Description [Blocks SIGINT. For use when updating watched processes and temporary files to prevent race conditions with the signal handler.]
SideEffects []
SeeAlso []
***********************************************************************/
static int nblocks = 0;
void Util_SignalBlockSignals()
{
sigset_t procmask;
assert(nblocks==0);
nblocks ++ ;
sigemptyset(&procmask);
sigaddset(&procmask, SIGINT);
sigprocmask(SIG_BLOCK, &procmask, &old_procmask);
}
/**Function*************************************************************
Synopsis []
Description [Unblocks SIGINT after a call to Util_SignalBlockSignals.]
SideEffects []
SeeAlso []
***********************************************************************/
void Util_SignalUnblockSignals()
{
assert( nblocks==1);
nblocks--;
sigprocmask(SIG_SETMASK, &old_procmask, NULL);
}
static void watch_tmp_file(const char* fname)
{
if( watched_tmp_files_hash != NULL )
{
Hash_GenWriteEntry(watched_tmp_files_hash, Extra_UtilStrsav(fname), (void*)getpid() );
}
}
static void unwatch_tmp_file(const char* fname)
{
if ( watched_tmp_files_hash )
{
assert( Hash_GenExists(watched_tmp_files_hash, fname) );
Hash_GenRemove(watched_tmp_files_hash, fname);
assert( !Hash_GenExists(watched_tmp_files_hash, fname) );
}
}
/**Function*************************************************************
Synopsis []
Description [Adds a process id to the list of processes that should be killed in a signal handler.]
SideEffects []
SeeAlso []
***********************************************************************/
void Util_SignalAddChildPid(int pid)
{
if ( watched_pid_hash )
{
Hash_GenWriteEntry(watched_pid_hash, (void*)pid, (void*)getpid());
}
}
/**Function*************************************************************
Synopsis []
Description [Removes a process id from the list of processes that should be killed in a signal handler.]
SideEffects []
SeeAlso []
***********************************************************************/
void Util_SignalRemoveChildPid(int pid)
{
if ( watched_pid_hash )
{
Hash_GenRemove(watched_pid_hash, (void*)pid);
}
}
// a dummy signal hanlder to make sure that SIGCHLD and SIGINT will cause sigsuspend() to return
static int null_sig_handler(int signum)
{
return 0;
}
// enusre that sigsuspend() returns when signal signum occurs -- sigsuspend() does not return if a signal is ignored
static void replace_sighandler(int signum, struct sigaction* old_sa, int replace_dfl)
{
sigaction(signum, NULL, old_sa);
if( old_sa->sa_handler == SIG_IGN || old_sa->sa_handler==SIG_DFL && replace_dfl)
{
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = null_sig_handler;
sigaction(signum, &sa, &old_sa);
}
}
//
static int do_waitpid(pid_t pid, sigset_t* old_procmask)
{
int status;
struct sigaction sigint_sa;
struct sigaction sigchld_sa;
sigset_t waitmask;
// ensure SIGINT and SIGCHLD are not blocked during sigsuspend()
memcpy(&waitmask, old_procmask, sizeof(sigset_t));
sigdelset(&waitmask, SIGINT);
sigdelset(&waitmask, SIGCHLD);
// ensure sigsuspend() returns if SIGINT or SIGCHLD occur, and save the current settings for SIGCHLD and SIGINT
replace_sighandler(SIGINT, &sigint_sa, 0);
replace_sighandler(SIGCHLD, &sigchld_sa, 1);
for(;;)
{
int rc;
// wait for a signal -- returns if SIGINT or SIGCHLD (or any other signal that is unblocked and not ignored) occur
sigsuspend(&waitmask);
// check if pid has terminated
rc = waitpid(pid, &status, WNOHANG);
// stop if terminated or some other error occurs
if( rc > 0 || rc == -1 && errno!=EINTR )
{
break;
}
}
// process is dead, should no longer be watched
Util_SignalRemoveChildPid(pid);
// restore original behavior of SIGINT and SIGCHLD
sigaction(SIGINT, &sigint_sa, NULL);
sigaction(SIGCHLD, &sigchld_sa, NULL);
return status;
}
static int do_system(const char* cmd, sigset_t* old_procmask)
{
int pid;
pid = fork();
if (pid == -1)
{
// fork failed
return -1;
}
else if( pid == 0)
{
// child process
sigprocmask(SIG_SETMASK, old_procmask, NULL);
execl("/bin/sh", "sh", "-c", cmd, NULL);
_exit(127);
}
Util_SignalAddChildPid(pid);
return do_waitpid(pid, old_procmask);
}
/**Function*************************************************************
Synopsis []
Description [Replaces system() with a function that allows SIGINT to interrupt.]
SideEffects []
SeeAlso []
***********************************************************************/
int Util_SignalSystem(const char* cmd)
{
int status;
sigset_t procmask;
sigset_t old_procmask;
// if signal handler is not installed, run the original system()
if ( ! watched_pid_hash && ! watched_tmp_files_hash )
return system(cmd);
// block SIGINT and SIGCHLD
sigemptyset(&procmask);
sigaddset(&procmask, SIGINT);
sigaddset(&procmask, SIGCHLD);
sigprocmask(SIG_BLOCK, &procmask, &old_procmask);
// call the actual function
status = do_system(cmd, &old_procmask);
// restore signal block mask
sigprocmask(SIG_SETMASK, &old_procmask, NULL);
return status;
}
/**Function*************************************************************
Synopsis []
Description []
SideEffects []
SeeAlso []
***********************************************************************/
////////////////////////////////////////////////////////////////////////
/// END OF FILE ///
////////////////////////////////////////////////////////////////////////
#else /* #ifndef _MSC_VER */
#include "abc_global.h"
ABC_NAMESPACE_IMPL_START
void Util_SignalCleanup()
{
}
void Util_SignalStartHandler()
{
}
void Util_SignalResetHandler()
{
}
void Util_SignalStopHandler()
{
}
void Util_SignalBlockSignals()
{
}
void Util_SignalUnblockSignals()
{
}
void watch_tmp_file(const char* fname)
{
}
void unwatch_tmp_file(const char* fname)
{
}
void Util_SignalAddChildPid(int pid)
{
}
void Util_SignalRemoveChildPid(int pid)
{
}
int Util_SignalSystem(const char* cmd)
{
return system(cmd);
}
#endif /* #ifdef _MSC_VER */
int tmpFile(const char* prefix, const char* suffix, char** out_name);
/**Function*************************************************************
Synopsis []
Description [Create a temporary file and add it to the list of files to be cleaned up in the signal handler.]
SideEffects []
SeeAlso []
***********************************************************************/
int Util_SignalTmpFile(const char* prefix, const char* suffix, char** out_name)
{
int fd;
Util_SignalBlockSignals();
fd = tmpFile(prefix, suffix, out_name);
if ( fd != -1 )
{
watch_tmp_file( *out_name );
}
Util_SignalUnblockSignals();
return fd;
}
/**Function*************************************************************
Synopsis []
Description [Remove a temporary file (and remove it from the watched files list.]
SideEffects []
SeeAlso []
***********************************************************************/
void Util_SignalTmpFileRemove(const char* fname, int fLeave)
{
Util_SignalBlockSignals();
unwatch_tmp_file(fname);
if (! fLeave)
{
remove(fname);
}
Util_SignalUnblockSignals();
}
ABC_NAMESPACE_IMPL_END