Files
magic/utils/signals.c
T
Patrick Kurth ea03883829 Fixed unused-parameters/unused-variables warnings
Added a ARG_UNUSED macro in utils/magic.h, which
produces a simple cast to void to silence the warnings.
The reasons this is used as opposed to an attribute is
that most compilers should support this.
Besides this, the macro can be easily searched for in
the future to potentially remove (where possible) the
unused parameters.
2025-10-10 22:12:19 +02:00

701 lines
16 KiB
C

/*
* signals.c --
*
* Handles signals, such as stop, start, interrupt.
*
* *********************************************************************
* * Copyright (C) 1985, 1990 Regents of the University of California. *
* * Permission to use, copy, modify, and distribute this *
* * software and its documentation for any purpose and without *
* * fee is hereby granted, provided that the above copyright *
* * notice appear in all copies. The University of California *
* * makes no representations about the suitability of this *
* * software for any purpose. It is provided "as is" without *
* * express or implied warranty. Export of this software outside *
* * of the United States of America may require an export license. *
* *********************************************************************
*/
#ifndef lint
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/utils/signals.c,v 1.2 2010/03/08 13:33:34 tim Exp $";
#endif /* not lint */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h> /* for getpid() */
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#include <fcntl.h>
#include "tcltk/tclmagic.h"
#include "utils/main.h"
#include "utils/magic.h"
#include "utils/magsgtty.h"
#include "textio/textio.h"
#include "utils/geometry.h"
#include "utils/signals.h"
#include "windows/windows.h"
#include "graphics/graphics.h"
#ifndef SIGEMT
#define SIGEMT 7 /* EMT instruction (SIGUNUSED) */
#endif
#ifndef SIGBUS
#define SIGBUS 10 /* bus error (SIGUSR1) */
#endif
#ifndef SIGSYS
#define SIGSYS 12 /* bad argument to system call (SIGUSR2) */
#endif
#if !defined(SIGIOT) && defined(SIGABRT)
#define SIGIOT SIGABRT /* io-trap signal redefined */
#endif
#if defined(linux) || defined(EMSCRIPTEN)
#if SIGBUS == SIGUNUSED
#undef SIGBUS
#define SIGBUS SIGUSR1
#endif
#if SIGSYS == SIGUNUSED
#undef SIGSYS
#define SIGSYS SIGUSR2
#endif
#endif
#ifndef FASYNC
# define FASYNC 00100 /* kludge for SUN2s */
#endif
/* specially imported */
extern bool DBWriteBackup();
/* macs support BSD4.2 signals, so turn off the SYSV flag for this module */
#ifdef __APPLE__
#undef SYSV
#endif
void sigSetAction(int, sigRetVal (*)(int));
/* becomes true when we get an interrupt */
global bool SigInterruptPending = FALSE;
/* Becomes true when IO is possible on one of the files passed to SigWatchFile.
* Spurious signals are sometimes generated -- use select() to make
* sure that what you want is really there.
*/
global bool SigIOReady = FALSE;
/* If set to 1, we will set SigInterruptPending whenever we set SigIOReady. */
/* If set to -1, then SigInterruptPending is never set */
global char SigInterruptOnSigIO;
/*
* Set to true when we recieve a SIGWINCH/SIGWINDOW signal
* (indicating that a window has changed size or otherwise needs attention).
*/
global bool SigGotSigWinch = FALSE;
/*
* Local data structures
*/
static bool sigInterruptReceived = FALSE;
static int sigNumDisables = 0;
/*---------------------------------------------------------
* While we can conveniently run Ctrl-C interrupts from
* the terminal at any time, we can't do this from the
* window in Tcl/Tk because the GUI event handler is not
* a separate process. So we run a timer process during
* WindUpdate() which periodically processes Tk events
* in the window.
*
* This timer can be set for "secs" second intervals. If
* "secs" is zero, it defaults to a 1/4 second interval.
*---------------------------------------------------------
*/
void
SigSetTimer(int secs)
{
struct itimerval subsecond; /* one-quarter second interval */
/*
if (GrDisplayStatus == DISPLAY_IDLE)
fprintf(stderr, "Timer start\n");
else
fprintf(stderr, "Timer repeat\n");
fflush(stderr);
*/
subsecond.it_interval.tv_sec = 0;
subsecond.it_interval.tv_usec = 0;
subsecond.it_value.tv_sec = secs;
subsecond.it_value.tv_usec = (secs == 0) ? 250000 : 0;
setitimer(ITIMER_REAL, &subsecond, NULL);
}
/*---------------------------*/
/* Remove any existing timer */
/*---------------------------*/
void
SigRemoveTimer()
{
struct itimerval zero; /* zero time to stop the timer */
/* fprintf(stderr, "Timer stop\n"); fflush(stderr); */
zero.it_value.tv_sec = 0;
zero.it_value.tv_usec = 0;
zero.it_interval.tv_sec = 0;
zero.it_interval.tv_usec = 0;
setitimer(ITIMER_REAL, &zero, NULL);
}
sigRetVal
sigOnAlarm(int signo)
{
ARG_UNUSED(signo);
if (GrDisplayStatus == DISPLAY_IN_PROGRESS)
GrDisplayStatus = DISPLAY_BREAK_PENDING;
sigReturn;
}
/*-----------------------------------------*/
/* Set timer to act as a display interrupt */
/*-----------------------------------------*/
void
SigTimerDisplay()
{
sigSetAction(SIGALRM, sigOnAlarm);
}
/*------------------------------------------*/
/* Set timer to act like a Ctrl-C interrupt */
/*------------------------------------------*/
void
SigTimerInterrupts()
{
sigSetAction(SIGALRM, sigOnInterrupt);
}
/*---------------------------------------------------------
* sigOnStop:
* This procedure handles stop signals.
*
* Results:
* sigReturn result (see signals.h)
*
* Side Effects:
* The text display is reset, and we stop
*---------------------------------------------------------
*/
sigRetVal
sigOnStop(int signo)
{
ARG_UNUSED(signo);
/* fix things up */
TxResetTerminal(TRUE);
GrStop();
/* restore the default action and resend the signal */
sigSetAction(SIGTSTP, SIG_DFL);
kill(getpid(),
#ifdef linux
SIGSTOP
#else
SIGTSTP
#endif
);
/* -- we stop here -- */
/* NOTE: The following code really belongs in a routine that is
* called in response to a SIGCONT signal, but it doesn't seem to
* work that way. Maybe there is a Unix bug with this???
*/
GrResume();
TxSetTerminal();
TxReprint();
/* catch future stops now that we have finished resuming */
sigSetAction(SIGTSTP, sigOnStop);
sigReturn;
}
/*
*---------------------------------------------------------
* SigCheckProcess ---
*
* Check if a process exists by sending it a signal.
* Perhaps there are better ways to do this?
*
* Results:
* TRUE if the process exists, FALSE if not.
*
* Side effects:
* Whatever happens when the process gets SIGCONT;
* hopefully nothing.
*---------------------------------------------------------
*/
bool
SigCheckProcess(pid)
int pid;
{
int result;
result = kill((pid_t)pid, SIGCONT);
if (result == 0) return TRUE;
else return FALSE;
}
/*---------------------------------------------------------
* sigEnableInterrupts:
* This procedure reenables our handling of interrupts.
*
* Results: None.
*
* Side Effects:
* None.
*---------------------------------------------------------
*/
void
SigEnableInterrupts()
{
/*sigNumDisables--;*/
if (sigNumDisables == 1)
{
SigInterruptPending = sigInterruptReceived;
sigInterruptReceived = FALSE;
}
sigNumDisables--;
}
/*---------------------------------------------------------
* sigDisableInterrupts:
* This procedure disables our handling of interrupts.
*
* Results: None.
*
* Side Effects:
* None.
*---------------------------------------------------------
*/
void
SigDisableInterrupts()
{
sigNumDisables++;
if (sigNumDisables == 1)
{
sigInterruptReceived = SigInterruptPending;
SigInterruptPending = FALSE;
}
/*sigNumDisables++;*/
}
/*
* ----------------------------------------------------------------------------
* SigWatchFile --
*
* Take interrupts on a given IO stream.
*
* Results:
* None.
*
* Side effects:
* SigIOReady will be set when the IO stream becomes ready. It is
* the responsibility of the client to clear that flag when needed.
* ----------------------------------------------------------------------------
*/
void
SigWatchFile(filenum, filename)
int filenum; /* A file descriptor number */
char *filename; /* Used to recognize special files that
* don't support a full range of fcntl
* calls (such as windows: /dev/winXX).
*/
{
int flags;
bool iswindow;
iswindow = (filename && (strncmp(filename, "/dev/win", 8) == 0));
flags = fcntl(filenum, F_GETFL, 0);
if (flags == -1)
{
perror("(Magic) SigWatchFile1");
return;
}
if (!mainDebug)
{
/* turn on FASYNC */
#ifndef SYSV
#ifdef F_SETOWN
if (!iswindow)
{
if (fcntl(filenum, F_SETOWN, -getpid()) == -1)
perror("(Magic) SigWatchFile2");
}
#endif
#endif /* SYSV */
#ifdef FASYNC
if (fcntl(filenum, F_SETFL, flags | FASYNC) == -1)
perror("(Magic) SigWatchFile3");
#else
# ifdef FIOASYNC
flags = 1;
if (ioctl(filenum, FIOASYNC, &flags) == -1)
perror("(Magic) SigWatchFile3a");
# endif
#endif
}
else
{
#ifdef FASYNC
/* turn off FASYNC */
if (fcntl(filenum, F_SETFL, flags & (~FASYNC)) == -1)
perror("(Magic) SigWatchFile4");
#else
# ifdef FIOASYNC
flags = 0;
if (ioctl(filenum, FIOASYNC, &flags) == -1)
perror("(Magic) SigWatchFile3b");
# endif
#endif
}
}
/*
* ----------------------------------------------------------------------------
* SigUnWatchFile --
*
* Do not take interrupts on a given IO stream.
*
* Results:
* None.
*
* Side effects:
* SigIOReady will be not set when the IO stream becomes ready.
* ----------------------------------------------------------------------------
*/
/*ARGSUSED*/
void
SigUnWatchFile(filenum, filename)
int filenum; /* A file descriptor number */
char *filename; /* Used to recognize special files that
* don't support a full range of fcntl
* calls (such as windows: /dev/winXX).
*/
{
ARG_UNUSED(filename);
int flags;
flags = fcntl(filenum, F_GETFL, 0);
if (flags == -1)
{
perror("(Magic) SigUnWatchFile1");
return;
}
#ifdef FASYNC
/* turn off FASYNC */
if (fcntl(filenum, F_SETFL, flags & (~FASYNC)) == -1)
perror("(Magic) SigUnWatchFile4");
#else
# ifdef FIOASYNC
flags = 0;
if (ioctl(filenum, FIOASYNC, &flags) == -1)
perror("(Magic) SigWatchFile3");
# endif
#endif
}
/*---------------------------------------------------------
* sigOnInterrupt:
* This procedure handles interupt signals.
*
* Results:
* sigReturn result (see signals.h)
*
* Side Effects:
* A global flag is set
*---------------------------------------------------------
*/
sigRetVal
sigOnInterrupt(int signo)
{
ARG_UNUSED(signo);
if (sigNumDisables != 0)
sigInterruptReceived = TRUE;
else
SigInterruptPending = TRUE;
sigReturn;
}
/*
* ----------------------------------------------------------------------------
* sigOnTerm:
*
* Catch the terminate (SIGTERM) signal.
* Force all modified cells to be written to disk (in new files,
* of course).
*
* Results:
* Function does not return.
*
* Side effects:
* Writes cells out to disk (by calling DBWriteBackup()).
* Exits.
* ----------------------------------------------------------------------------
*/
sigRetVal
sigOnTerm(int signo)
{
ARG_UNUSED(signo);
DBWriteBackup(NULL);
exit (1);
}
/*
* ----------------------------------------------------------------------------
* sigOnWinch --
*
* A window has changed size or otherwise needs attention.
*
* Results:
* sigReturn result (see signals.h)
*
* Side effects:
* Sets a global flag.
* ----------------------------------------------------------------------------
*/
sigRetVal
sigOnWinch(int signo)
{
ARG_UNUSED(signo);
SigGotSigWinch = TRUE;
sigReturn;
}
/*
* ----------------------------------------------------------------------------
* sigIO --
*
* Some IO device is ready (probably the keyboard or the mouse).
*
* Results:
* sigReturn result (see signals.h)
*
* Side effects:
* Sets a global flag.
* ----------------------------------------------------------------------------
*/
sigRetVal
sigIO(int signo)
{
ARG_UNUSED(signo);
SigIOReady = TRUE;
if (SigInterruptOnSigIO == 1) sigOnInterrupt(0);
sigReturn;
}
/*
* ----------------------------------------------------------------------------
*
* sigCrash --
*
* Something when wrong, reset the terminal and die.
*
* Results:
* Function never returns.
*
* Side Effects:
* We die.
*
* ----------------------------------------------------------------------------
*/
sigRetVal
sigCrash(signum)
int signum;
{
static int magicNumber = 1239987;
char *msg;
extern bool AbortFatal;
#if !defined(linux) && !defined(EMSCRIPTEN)
if (magicNumber == 1239987) {
/* Things aren't screwed up that badly, try to reset the terminal */
magicNumber = 0;
switch (signum) {
case SIGILL: {msg = "Illegal Instruction"; break;};
case SIGTRAP: {msg = "Instruction Trap"; break;};
case SIGIOT: {msg = "IO Trap"; break;};
case SIGEMT: {msg = "EMT Trap"; break;};
case SIGFPE: {msg = "Floating Point Exception"; break;};
case SIGBUS: {msg = "Bus Error"; break;};
case SIGSEGV: {msg = "Segmentation Violation"; break;};
case SIGSYS: {msg = "Bad System Call"; break;};
default: {msg = "Unknown signal"; break;};
};
strcpy(AbortMessage, msg);
AbortFatal = TRUE;
niceabort();
TxResetTerminal(TRUE);
}
#else
if (magicNumber == 1239987) {
magicNumber = 0;
/* Don't use switch statement. These values aren't mutually exclusive
* under Linux.
*/
if (signum == SIGILL) msg = "Illegal Instruction";
else if (signum == SIGTRAP) msg = "Instruction Trap";
else if (signum == SIGIOT) msg = "IO Trap";
else if (signum == SIGEMT) msg = "EMT Trap";
else if (signum == SIGFPE) msg = "Floating Point Exception";
else if (signum == SIGBUS) msg = "Bus Error";
else if (signum == SIGSEGV) msg = "Segmentation Violation";
else if (signum == SIGSYS) msg = "Bad System Call";
else msg = "Unknown signal";
strcpy(AbortMessage, msg);
AbortFatal = TRUE;
niceabort();
TxResetTerminal(TRUE);
}
#endif
/* Crash & burn */
magicNumber = 0;
exit(12);
}
/*
* ----------------------------------------------------------------------------
* SigInit:
*
* Set up signal handling for all signals.
*
* Results:
* None.
*
* Side effects:
* Signal handling is set up.
* ----------------------------------------------------------------------------
*/
void
SigInit(batchmode)
int batchmode;
{
/* fprintf(stderr, "Establishing signal handlers.\n"); fflush(stderr); */
if (batchmode)
{
SigInterruptOnSigIO = -1;
}
else
{
SigInterruptOnSigIO = 0;
sigSetAction(SIGINT, sigOnInterrupt);
sigSetAction(SIGTERM, sigOnTerm);
}
/* Under Tcl, sigOnStop just causes Tcl to hang forever. So don't */
/* set any new actions. */
#ifndef MAGIC_WRAPPER
#ifdef SIGTSTP
sigSetAction(SIGTSTP, sigOnStop);
#endif
#ifdef SIGWINCH
sigSetAction(SIGWINCH, sigOnWinch);
#endif
#ifdef SIGWINDOW
sigSetAction(SIGWINDOW, sigOnWinch);
#endif
#endif /* MAGIC_WRAPPER */
if (!mainDebug )
{
sigSetAction(SIGIO, sigIO);
#ifdef MAGIC_WRAPPER
if (batchmode == 0)
SigTimerDisplay();
else
sigSetAction(SIGALRM, SIG_IGN);
#else
sigSetAction(SIGALRM, SIG_IGN);
#endif
sigSetAction(SIGPIPE, SIG_IGN);
#ifdef SIGPOLL
if (SIGIO != SIGPOLL)
{
sigSetAction(SIGPOLL, SIG_IGN);
}
#endif
}
#if !defined(SYSV) && !defined(CYGWIN) && !defined(EMSCRIPTEN)
sigsetmask(0);
#endif
}
void
sigSetAction(int signo, sigRetVal (*handler)(int))
{
#if defined(SYSV) || defined(CYGWIN) || defined(__NetBSD__) || defined(EMSCRIPTEN)
struct sigaction sa;
sa.sa_handler = handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(signo, &sa, (struct sigaction *)NULL);
#else
struct sigvec sv;
sv.sv_handler = handler;
sv.sv_mask = 0;
sv.sv_flags = 0;
sigvec(signo, &sv, (struct sigvec *)NULL);
#endif
}