Goto sanos source index

//
// signal.c
//
// Signal and exception handling
//
// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 
// 1. Redistributions of source code must retain the above copyright 
//    notice, this list of conditions and the following disclaimer.  
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.  
// 3. Neither the name of the project nor the names of its contributors
//    may be used to endorse or promote products derived from this software
//    without specific prior written permission. 
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
// SUCH DAMAGE.
// 

#include <os.h>
#include <string.h>
#include <os/syscall.h>

void dump_stack(struct context *ctxt);

struct sigentry {
  char *label;
  char *name;
  int flags;
  int defaction;
};

#define SIGACT_TERM      0
#define SIGACT_ABORT     1
#define SIGACT_IGN       2
#define SIGACT_STOP      3
#define SIGACT_CONT      4

struct sigentry sigtab[_NSIG] = {
  {"SIGNUL",    "Null", 0, SIGACT_IGN},
  {"SIGHUP",    "Hangup", 0, SIGACT_TERM},
  {"SIGINT",    "Interrupt", 0, SIGACT_TERM},
  {"SIGQUIT",   "Quit", 0, SIGACT_ABORT},
  {"SIGILL",    "Illegal instruction", 0, SIGACT_ABORT},
  {"SIGTRAP",   "Trace trap", 0, SIGACT_ABORT},
  {"SIGABRT",   "Abort", 0, SIGACT_ABORT},
  {"SIGBUS",    "BUS error", 0, SIGACT_TERM},
  {"SIGFPE",    "Floating-point exception", 0, SIGACT_ABORT},
  {"SIGKILL",   "Kill", 0, SIGACT_TERM},
  {"SIGUSR1",   "User-defined signal 1", 0, SIGACT_TERM},
  {"SIGSEGV",   "Segmentation violation", 0, SIGACT_ABORT},
  {"SIGUSR2",   "User-defined signal 2", 0, SIGACT_TERM},
  {"SIGPIPE",   "Broken pipe", 0, SIGACT_TERM},
  {"SIGALRM",   "Alarm clock", 0, SIGACT_TERM},
  {"SIGTERM",   "Termination", 0, SIGACT_TERM},
  {"SIGSTKFLT", "Stack fault", 0, SIGACT_ABORT},
  {"SIGCHLD",   "Child status has changed", 0, SIGACT_IGN},
  {"SIGCONT",   "Continue", 0, SIGACT_CONT},
  {"SIGSTOP",   "Stop", 0, SIGACT_STOP},
  {"SIGTSTP",   "Keyboard stop", 0, SIGACT_STOP},
  {"SIGTTIN",   "Background read from tty", 0, SIGACT_STOP},
  {"SIGTTOU",   "Background write to tty", 0, SIGACT_STOP},
  {"SIGURG",    "Urgent condition on socket", 0, SIGACT_IGN},
  {"SIGXCPU",   "CPU limit exceeded", 0, SIGACT_ABORT},
  {"SIGXFSZ",   "File size limit exceeded", 0, SIGACT_ABORT},
  {"SIGVTALRM", "Virtual alarm clock", 0, SIGACT_TERM},
  {"SIGPROF",   "Profiling alarm clock", 0, SIGACT_TERM},
  {"SIGWINCH",  "Window size change", 0, SIGACT_IGN},
  {"SIGIO",     "I/O now possible", 0, SIGACT_IGN},
  {"SIGPWR",    "Power failure restart", 0, SIGACT_IGN},
  {"SIGSYS",    "Bad system call", 0, SIGACT_ABORT}
};

char *strsignal(int signum) {
  if (signum >= 0 && signum < _NSIG) {
    return sigtab[signum].name;
  } else {
    return "Unknown";
  }
}

void sigexit(struct siginfo *info, int action) {
  __asm {
    mov eax, [action]
    mov ebx, [info]
    int 49
  }
}

int sigemptyset(sigset_t *set) {
  if (!set) {
    errno = EFAULT;
    return -1;
  }

  *set = 0;
  return 0;
}

int sigfillset(sigset_t *set) {
  if (!set) {
    errno = EFAULT;
    return -1;
  }

  *set = 0xFFFFFFFF;
  return 0;
}

int sigaddset(sigset_t *set, int signum) {
  if (!set) {
    errno = EFAULT;
    return -1;
  }

  if (signum < 0 || signum >= _NSIG) {
    errno = EINVAL;
    return -1;
  }

  *set |= (1 << signum);
  return 0;
}

int sigdelset(sigset_t *set, int signum) {
  if (!set) {
    errno = EFAULT;
    return -1;
  }

  if (signum < 0 || signum >= _NSIG) {
    errno = EINVAL;
    return -1;
  }

  *set &= ~(1 << signum);
  return 0;
}

int sigismember(sigset_t *set, int signum) {
  if (!set) {
    errno = EFAULT;
    return -1;
  }

  if (signum < 0 || signum >= _NSIG) {
    errno = EINVAL;
    return -1;
  }

  return (*set & (1 << signum)) != 0;
}

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact) {
  struct process *proc = gettib()->proc;

  if (signum < 0 || signum >= _NSIG) {
    errno = EINVAL;
    return -1;
  }

  if (oldact) memcpy(oldact, proc->handlers + signum, sizeof(struct sigaction));
  memcpy(proc->handlers + signum, act, sizeof(struct sigaction));

  return 0;
}

sighandler_t signal(int signum, sighandler_t handler) {
  struct sigaction act;
  struct sigaction oldact;

  act.sa_handler = handler;
  act.sa_mask = 0;
  act.sa_flags = 0;

  if (sigaction(signum, &act, &oldact) < 0) return SIG_ERR;
  return oldact.sa_handler;
}

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset) {
  return syscall(SYSCALL_SIGPROCMASK, &how);
}

int sigpending(sigset_t *set) {
  return syscall(SYSCALL_SIGPENDING, &set);
}

int sigsuspend(const sigset_t *mask) {
  int rc;

  if (mask) {
    rc = sigprocmask(SIG_BLOCK, mask, NULL);
    if (rc < 0) return rc;
  }

  rc = waitone(self(), INFINITE);

  if (mask) sigprocmask(SIG_UNBLOCK, mask, NULL);

  return rc;
}

int sendsig(handle_t thread, int signum) {
  return syscall(SYSCALL_SENDSIG, &thread);
}

int kill(pid_t pid, int signum) {
  handle_t h;
  int rc;

  h = getprochandle(pid);
  if (h < 0) return h;

  rc = sendsig(h, signum);
  close(h);

  return rc;
}

int raise(int signum) {
  return sendsig(self(), signum);
}

unsigned alarm(unsigned seconds) {
  return syscall(SYSCALL_ALARM, &seconds);
}

void globalhandler(struct siginfo *info) {
  struct sigaction *act;
  struct sigaction oldact;
  int signum = info->si_signo;

  if (signum < 0 || signum >= _NSIG) return;

  act = gettib()->proc->handlers + signum;
  memcpy(&oldact, act, sizeof(struct sigaction));

  if (oldact.sa_handler == SIG_DFL) {
    switch (sigtab[signum].defaction) {
      case SIGACT_TERM:
        syslog(LOG_ERR, "terminating with signal %d (%s)", signum, strsignal(signum));
        dump_stack(info->si_ctxt);
        exit((signum << 8) | 0x10000);

      case SIGACT_ABORT:
        if (getpeb()->debug) sigexit(info, 1);
        syslog(LOG_ERR, "aborting with signal %d (%s)", signum, strsignal(signum));
        dump_stack(info->si_ctxt);
        exit((signum << 8) | 0x10000);

      case SIGACT_IGN:
        break;

      case SIGACT_STOP:
        syslog(LOG_INFO, "stopped");
        suspend(self());
        break;

      case SIGACT_CONT:
        break;
    }
  } else if (oldact.sa_handler != SIG_IGN) {
    if (oldact.sa_flags & SA_RESETHAND) {
      act->sa_handler = SIG_IGN;
      act->sa_flags &= ~SA_SIGINFO;
    }

    if (oldact.sa_mask) sigprocmask(SIG_BLOCK, &oldact.sa_mask, NULL);

    if (oldact.sa_flags & SA_SIGINFO) {
      oldact.sa_sigaction(signum, info, info->si_ctxt);
    } else {
      oldact.sa_handler(signum);
    }

    if (oldact.sa_mask) sigprocmask(SIG_UNBLOCK, &oldact.sa_mask, NULL);
  }

  sigexit(info, 0);
}