home *** CD-ROM | disk | FTP | other *** search
- /****************************************************************************
- *
- * $Source: /unixb/home/unixlib/source/unixlib37/src/signal/c/RCS/post,v $
- * $Date: 1996/11/06 22:01:42 $
- * $Revision: 1.2 $
- * $State: Rel $
- * $Author: unixlib $
- *
- * $Log: post,v $
- * Revision 1.2 1996/11/06 22:01:42 unixlib
- * Yet more changes by NB, PB and SC.
- *
- * Revision 1.1 1996/10/30 22:04:51 unixlib
- * Initial revision
- *
- ***************************************************************************/
-
- static const char rcs_id[] = "$Id: post,v 1.2 1996/11/06 22:01:42 unixlib Rel $";
-
- /* signal.c.post: Written by Nick Burrett, 27 August 1996. */
-
- #include <stdlib.h>
- #include <signal.h>
- #include <stdio.h>
- #include <errno.h>
- #include <sys/wait.h>
-
- #include <sys/os.h>
- #include <sys/unix.h>
- #include <unixlib/sigstate.h>
-
- /* #define DEBUG */
-
- void
- __unixlib_default_sigaction (struct unixlib_sigstate *ss)
- {
- int signo;
-
- sigemptyset (&ss->actions[0].sa_mask);
- ss->actions[0].sa_flags = SA_RESTART;
- ss->actions[0].sa_handler = SIG_DFL;
-
- /* Set all signals to their defaults. */
- for (signo = 1; signo < NSIG; ++signo)
- ss->actions[signo] = ss->actions[0];
- }
-
-
- void __unixlib_internal_post_signal (struct unixlib_sigstate *ss,
- int signo, int sigcode, int sigerror)
- {
- enum { stop, ignore, core, term, handle } act;
- __sighandler_t handler;
- struct unixlib_signal_preempt *pe;
- sighandler_t (*preempt) (int, int, int) = NULL;
- sigset_t pending;
- int ss_suspended;
-
- post_signal:
- #ifdef DEBUG
- printf ("__unixlib_internal_post_signal: sigstate = %x, signo = %d\n", (int)ss, signo);
- #endif
-
- /* Increment the number of signals this process has received. */
- __u->usage.ru_nsignals++;
-
- /* Support for pre-emptive handlers. */
- for (pe = __u->sigpreempt[signo]; pe != NULL; pe = pe->next)
- if (sigcode >= pe->first && sigcode <= pe->last)
- {
- preempt = pe->handler;
- break;
- }
-
- handler = SIG_DFL;
- if (preempt)
- {
- /* Let the preempting handle examine the thread.
- If it returns SIG_DFL, we run the normal handler;
- otherwise we use the handler it returns. */
- #ifdef DEBUG
- os_print ("\r\n__unixlib_internal_post_signal: running a signal pre-empter\r\n");
- #endif
- handler = (*preempt) (0, signo, sigcode);
- }
-
- ss_suspended = 0;
-
- #ifdef DEBUG
- printf ("__unixlib_internal_post_signal: Do the handling\n");
- #endif
-
- if (handler != SIG_DFL)
- /* Run the preemption-provided handler. */
- act = handle;
- else
- {
- /* Do normal handling. */
-
- handler = ss->actions[signo].sa_handler;
- #ifdef DEBUG
- printf ("__unixlib_internal_post_signal: handler = %x\n", (int)handler);
- #endif
-
- if (handler == SIG_DFL)
- /* Figure out the default action for this signal. */
- switch (signo)
- {
- case 0:
- /* Special signo used for posting any pending signals. */
- act = ignore;
- break;
-
- case SIGTTIN:
- case SIGTTOU:
- case SIGSTOP:
- case SIGTSTP:
- /* Signals that will stop a process. */
- act = stop;
- break;
-
- case SIGCONT:
- case SIGIO:
- case SIGURG:
- case SIGCHLD:
- case SIGWINCH:
- /* Signals that can otherwise be ignored. */
- act = ignore;
- break;
-
- case SIGQUIT:
- case SIGILL:
- case SIGTRAP:
- case SIGIOT:
- case SIGEMT:
- case SIGFPE:
- case SIGBUS:
- case SIGSEGV:
- case SIGSYS:
- /* Fatal signals that will cause a core dump. */
- act = core;
- break;
-
- case SIGINFO:
- if (__u->pgrp == __u->pid)
- {
- /* We provide a default handler for SIGINFO since
- there is no user-specified handler. */
- act = handle;
- handler = __unixlib_siginfo_handler;
- }
- else
- act = ignore;
- break;
-
- default:
- /* All other signals will cause a process termination. */
- act = term;
- break;
- }
- else if (handler == SIG_IGN)
- act = ignore;
- else
- act = handle;
-
- if (sigmask (signo) & (sigmask (SIGTTIN) | sigmask (SIGTTOU)
- | sigmask (SIGSTOP) | sigmask (SIGTSTP)))
- /* Stop signals clear a pending SIGCONT even if they
- are handled or ignored (but not if preempted). */
- sigdelset (&ss->pending, SIGCONT);
- else if (signo == SIGCONT)
- {
- /* Even if handled or ignored (but not preempted),
- SIGCONT clears stop signals and resumes the process. */
- sigdelset (&ss->pending, SIGTTIN);
- sigdelset (&ss->pending, SIGTTOU);
- sigdelset (&ss->pending, SIGSTOP);
- sigdelset (&ss->pending, SIGTSTP);
- /* Resume all our children. */
- ss_suspended = 1;
- __u->status.stopped = 0;
- }
- }
-
- #ifdef DEBUG
- printf ("__unixlib_internal_post_signal: act = %d\n", act);
- #endif
-
- if (__u->orphaned && act == stop &&
- (signo & (sigmask (SIGTTIN) | sigmask (SIGTTOU) | sigmask (SIGTSTP))))
- {
- /* If we would ordinarily stop for a job control signal, but we are
- orphaned so noone would ever notice and continue us again, we just
- quietly die, alone and in the dark. */
- sigcode = signo;
- signo = SIGKILL;
- act = term;
- }
-
- /* Handle a blocked signal. */
- if ((sigismember (&ss->blocked, signo) && act != ignore)
- || (signo != SIGKILL && __u->stopped))
- {
- sigaddset (&ss->pending, signo);
- /* Save the code to be given to the handler when SIGNO is unblocked. */
- ss->pending_data[signo].code = sigcode;
- ss->pending_data[signo].error = sigerror;
- act = ignore;
- }
-
- #ifdef DEBUG
- printf ("__unixlib_internal_post_signal: Do the chosen action\n");
- #endif
- /* Perform the chosen action for the signal. */
- switch (act)
- {
- case stop:
- #ifdef DEBUG
- printf ("__unixlib_internal_post_signal: stop\n");
- #endif
- if (! __u->status.stopped)
- {
- /* Stop all our threads, and mark ourselves stopped. */
- __u->status.stopped = 1;
- }
- /* Wake up sigsuspend. */
- sigwakeup ();
- break;
-
- case ignore:
- #ifdef DEBUG
- printf ("__unixlib_internal_post_signal: ignore\n");
- #endif
- /* Nobody cares about this signal. */
- break;
-
- case term: /* Time to die. */
- case core: /* and leave a rotting corpse. */
- death:
- /* Stop all other threads in our task. */
- /* No more user instructions wil be executed. */
- {
- int status = W_EXITCODE (0, signo);
- /* Do a core dump if desired. Only set the wait status bit saying
- we in fact dumped core if the operation was actually successful. */
- #ifdef DEBUG
- printf ("__unixlib_internal_post_signal: term/core\n");
- #endif
- if (act == term)
- __write_termination (signo, sigcode, sigerror);
- else if (act == core && __write_corefile (signo, sigcode, sigerror))
- status |= WCOREFLAG;
-
- /* Die, returning information about how we died. */
- _exit (status);
- /* Never reached. */
- }
- break;
-
- case handle:
- /* Call a handler for this signal. */
- {
- sigset_t blocked;
- int flags;
- #ifdef DEBUG
- printf ("__unixlib_internal_post_signal: handle\n");
- #endif
- /* We're going to handle this signal now, so remove it from
- the pending list. */
- sigdelset (&ss->pending, signo);
-
- /* Block SIGNO and requested signals while running the handler. */
- blocked = ss->blocked;
- ss->blocked |= sigmask (signo) | ss->actions[signo].sa_mask;
- flags = ss->actions[signo].sa_flags;
- /* Re-instate the default signal handler. We do this before executing
- the signal handler because a new handler might be setup whilst
- executing the signal handler. */
- ss->actions[signo].sa_handler = SIG_DFL;
- ss->actions[signo].sa_flags = SA_RESTART;
- sigemptyset (&ss->actions[signo].sa_mask);
-
- /* Call the function to set the thread up to run the signal
- handler, and preserve its old context. */
- if (__unixlib_setup_sighandler (ss, handler, signo, sigcode, flags))
- {
- /* We got a fault setting up the stack frame for the handler.
- Nothing to do but die. */
- sigcode = signo;
- signo = SIGILL;
- act = core;
- goto death;
- }
- /* If we reach here, we have successfully executed the signal handler.
- All that is left is to restore the defaults. */
-
- /* Re-instate the original sigset. */
- ss->blocked = blocked;
- break;
- }
- }
-
- #ifdef DEBUG
- printf ("__unixlib_internal_post_signal: Deliver pending signals\n");
- #endif
- /* Deliver pending signals. */
-
- if (!__u->stopped && (pending = ss->pending & ~ss->blocked))
- {
- for (signo = 1; signo < NSIG; ++signo)
- if (sigismember (&pending, signo))
- {
- sigdelset (&ss->pending, signo);
- sigcode = ss->pending_data[signo].code;
- sigerror = ss->pending_data[signo].error;
- goto post_signal;
- }
- }
-
- ss->currently_handling = 0;
- /* No more signals pending. */
- sigwakeup ();
- }
-
- void __unixlib_raise_signal (struct unixlib_sigstate *ss,
- int signo, int sigcode, int sigerror)
- {
- if (ss == NULL)
- ss = &__u->sigstate;
-
- /* Mark signo as pending to be delivered. */
- sigaddset (&ss->pending, signo);
- ss->pending_data[signo].code = sigcode;
- ss->pending_data[signo].error = sigerror;
-
- if (!ss->currently_handling)
- {
- /* Post the signal if we are not already handling a signal. */
- ss->currently_handling = 1;
- __unixlib_internal_post_signal (ss, signo, sigcode, sigerror);
- }
- #ifdef DEBUG
- else
- {
- os_print ("\n\r__unixlib_raise_signal: signal ");
- os_print (sys_siglist[signo]);
- os_print (" is pending for delivery\r\n");
- }
- #endif
- }
-