OSDN Git Service

Eliminate use of sigframe and sigthread throughout.
[pf3gnuchains/pf3gnuchains3x.git] / winsup / cygwin / signal.cc
index 11d0921..9bf9eb0 100644 (file)
@@ -1,6 +1,6 @@
 /* signal.cc
 
-   Copyright 1996, 1997, 1998, 1999, 2000 Cygnus Solutions.
+   Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003 Red Hat, Inc.
 
    Written by Steve Chamberlain of Cygnus Support, sac@cygnus.com
    Significant changes by Sergey Okhapkin <sos@prospect.com.ru>
@@ -12,12 +12,14 @@ Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
 details. */
 
 #include "winsup.h"
-#include <errno.h>
+#include <stdlib.h>
 #include "cygerrno.h"
 #include <sys/cygwin.h>
-#include "sync.h"
-#include "sigproc.h"
 #include "pinfo.h"
+#include "sigproc.h"
+#include "hires.h"
+#include "security.h"
+#include "cygtls.h"
 
 int sigcatchers;       /* FIXME: Not thread safe. */
 
@@ -43,10 +45,11 @@ set_sigcatchers (void (*oldsig) (int), void (*cursig) (int))
 extern "C" _sig_func_ptr
 signal (int sig, _sig_func_ptr func)
 {
+  sig_dispatch_pending ();
   _sig_func_ptr prev;
 
   /* check that sig is in right range */
-  if (sig < 0 || sig >= NSIG)
+  if (sig < 0 || sig >= NSIG || sig == SIGKILL || sig == SIGSTOP)
     {
       set_errno (EINVAL);
       syscall_printf ("SIG_ERR = signal (%d, %p)", sig, func);
@@ -56,58 +59,105 @@ signal (int sig, _sig_func_ptr func)
   prev = myself->getsig (sig).sa_handler;
   myself->getsig (sig).sa_handler = func;
   myself->getsig (sig).sa_mask = 0;
+  /* SA_RESTART is set to maintain BSD compatible signal behaviour by default.
+     This is also compatible with the behaviour of signal(2) in Linux. */
+  myself->getsig (sig).sa_flags |= SA_RESTART;
   set_sigcatchers (prev, func);
-  
+
   syscall_printf ("%p = signal (%d, %p)", prev, sig, func);
   return prev;
 }
 
-extern "C" unsigned int
-sleep (unsigned int seconds)
+extern "C" int
+nanosleep (const struct timespec *rqtp, struct timespec *rmtp)
 {
-  int rc;
-  unsigned start_time;
-  unsigned int res;
-
-  start_time = GetTickCount ();
+  int res = 0;
+  sig_dispatch_pending ();
+  pthread_testcancel ();
 
-  syscall_printf ("sleep (%d)", seconds);
-  rc = WaitForSingleObject (signal_arrived, seconds * 1000);
-  if (rc == WAIT_TIMEOUT)
-    res = 0;
-  else
-    res = seconds - (GetTickCount () - start_time)/1000;
+  if ((unsigned int) rqtp->tv_sec > (HIRES_DELAY_MAX / 1000 - 1)
+      || (unsigned int) rqtp->tv_nsec > 999999999)
+    {
+      set_errno (EINVAL);
+      return -1;
+    }
+  DWORD resolution = gtod.resolution ();
+  DWORD req = ((rqtp->tv_sec * 1000 + (rqtp->tv_nsec + 999999) / 1000000
+               + resolution - 1) / resolution) * resolution;
+  DWORD end_time = gtod.dmsecs () + req;
+  syscall_printf ("nanosleep (%ld)", req);
+
+  int rc = pthread::cancelable_wait (signal_arrived, req);
+  DWORD rem;
+  if ((rem = end_time - gtod.dmsecs ()) > HIRES_DELAY_MAX)
+    rem = 0;
+  if (rc == WAIT_OBJECT_0)
+    {
+      (void) call_signal_handler_now ();
+      set_errno (EINTR);
+      res = -1;
+    }
 
-  syscall_printf ("%d = sleep (%d)", res, seconds);
+  if (rmtp)
+    {
+      rmtp->tv_sec = rem / 1000;
+      rmtp->tv_nsec = (rem % 1000) * 1000000;
+    }
 
+  syscall_printf ("%d = nanosleep (%ld, %ld)", res, req, rem);
   return res;
 }
 
 extern "C" unsigned int
+sleep (unsigned int seconds)
+{
+  struct timespec req, rem;
+  req.tv_sec = seconds;
+  req.tv_nsec = 0;
+  nanosleep (&req, &rem);
+  return rem.tv_sec + (rem.tv_nsec > 0);
+}
+
+extern "C" unsigned int
 usleep (unsigned int useconds)
 {
-  syscall_printf ("usleep (%d)", useconds);
-  WaitForSingleObject (signal_arrived, (useconds + 500) / 1000);
-  syscall_printf ("0 = usleep (%d)", useconds);
-  return 0;
+  struct timespec req;
+  req.tv_sec = useconds / 1000000;
+  req.tv_nsec = (useconds % 1000000) * 1000;
+  int res = nanosleep (&req, 0);
+  return res;
 }
 
 extern "C" int
 sigprocmask (int sig, const sigset_t *set, sigset_t *oldset)
 {
+  return handle_sigprocmask (sig, set, oldset, myself->getsigmask ());
+}
+
+int __stdcall
+handle_sigprocmask (int sig, const sigset_t *set, sigset_t *oldset, sigset_t& opmask)
+{
+  sig_dispatch_pending ();
   /* check that sig is in right range */
   if (sig < 0 || sig >= NSIG)
     {
-      set_errno (EINVAL);
-      syscall_printf ("SIG_ERR = sigprocmask sig %d out of range", sig);
+      set_errno (ESRCH);
+      syscall_printf ("SIG_ERR = sigprocmask signal %d out of range", sig);
       return -1;
     }
 
   if (oldset)
-    *oldset = myself->getsigmask ();
+    {
+      if (check_null_invalid_struct_errno (oldset))
+       return -1;
+      *oldset = opmask;
+    }
+
   if (set)
     {
-      sigset_t newmask = myself->getsigmask ();
+      if (check_invalid_read_struct_errno (set))
+       return -1;
+      sigset_t newmask = opmask;
       switch (sig)
        {
        case SIG_BLOCK:
@@ -126,7 +176,7 @@ sigprocmask (int sig, const sigset_t *set, sigset_t *oldset)
          set_errno (EINVAL);
          return -1;
        }
-      (void) set_process_mask (newmask);
+      (void) set_signal_mask (newmask, opmask);
     }
   return 0;
 }
@@ -134,6 +184,8 @@ sigprocmask (int sig, const sigset_t *set, sigset_t *oldset)
 static int
 kill_worker (pid_t pid, int sig)
 {
+  sig_dispatch_pending ();
+
   int res = 0;
   pinfo dest (pid);
   BOOL sendSIGCONT;
@@ -144,17 +196,16 @@ kill_worker (pid_t pid, int sig)
       return -1;
     }
 
-  dest->setthread2signal (NULL);
-
   if ((sendSIGCONT = (sig < 0)))
     sig = -sig;
 
-#if 0
-  if (dest == myself && !sendSIGCONT)
-    dest = myself_nowait_nonmain;
-#endif
+  DWORD process_state = dest->process_state;
   if (sig == 0)
-    res = proc_exists (dest) ? 0 : -1;
+    {
+      res = proc_exists (dest) ? 0 : -1;
+      if (res < 0)
+       set_errno (ESRCH);
+    }
   else if ((res = sig_send (dest, sig)))
     {
       sigproc_printf ("%d = sig_send, %E ", res);
@@ -163,32 +214,31 @@ kill_worker (pid_t pid, int sig)
   else if (sendSIGCONT)
     (void) sig_send (dest, SIGCONT);
 
-  syscall_printf ("%d = kill_worker (%d, %d)", res, pid, sig);
+  syscall_printf ("%d = kill_worker (%d, %d), process_state %p", res, pid, sig, process_state);
   return res;
 }
 
 int
-_raise (int sig)
+raise (int sig)
 {
-  return _kill (myself->pid, sig);
+  return kill (myself->pid, sig);
 }
 
-/* This is called _kill because the real kill is in newlib.  */
 int
-_kill (pid_t pid, int sig)
+kill (pid_t pid, int sig)
 {
   syscall_printf ("kill (%d, %d)", pid, sig);
   /* check that sig is in right range */
   if (sig < 0 || sig >= NSIG)
     {
       set_errno (EINVAL);
-      syscall_printf ("sig %d out of range", sig);
+      syscall_printf ("signal %d out of range", sig);
       return -1;
     }
 
   /* Silently ignore stop signals from a member of orphaned process group.
      FIXME: Why??? */
-  if (ISSTATE(myself, PID_ORPHANED) &&
+  if (ISSTATE (myself, PID_ORPHANED) &&
       (sig == SIGTSTP || sig == SIGTTIN || sig == SIGTTOU))
     sig = 0;
 
@@ -202,22 +252,20 @@ kill_pgrp (pid_t pid, int sig)
   int found = 0;
   int killself = 0;
 
-  sigproc_printf ("pid %d, sig %d", pid, sig);
+  sigproc_printf ("pid %d, signal %d", pid, sig);
 
-  winpids pids;
+  winpids pids ((DWORD) PID_MAP_RW);
   for (unsigned i = 0; i < pids.npids; i++)
     {
-      pinfo p (pids[i], PID_NOREDIR);
+      _pinfo *p = pids[i];
 
       if (!proc_exists (p))
        continue;
 
       /* Is it a process we want to kill?  */
-      if (pid == 0 && (p->pgid != myself->pgid || p->ctty != myself->ctty))
-       continue;
-      if (pid > 1 && p->pgid != pid)
-       continue;
-      if (sig < 0 && NOTSTATE(p, PID_STOPPED))
+      if ((pid == 0 && (p->pgid != myself->pgid || p->ctty != myself->ctty)) ||
+         (pid > 1 && p->pgid != pid) ||
+         (sig < 0 && NOTSTATE (p, PID_STOPPED)))
        continue;
       sigproc_printf ("killing pid %d, pgrp %d, p->ctty %d, myself->ctty %d",
                      p->pid, p->pgid, p->ctty, myself->ctty);
@@ -241,20 +289,46 @@ kill_pgrp (pid_t pid, int sig)
 }
 
 extern "C" int
-killpg (int pgrp, int sig)
+killpg (pid_t pgrp, int sig)
+{
+  return kill (-pgrp, sig);
+}
+
+extern "C" void
+abort (void)
 {
-  return _kill (-pgrp, sig);
+  sig_dispatch_pending ();
+  /* Flush all streams as per SUSv2.
+     From my reading of this document, this isn't strictly correct.
+     The streams are supposed to be flushed prior to exit.  However,
+     if there is I/O in any signal handler that will not necessarily
+     be flushed.
+     However this is the way FreeBSD does it, and it is much easier to
+     do things this way, so... */
+  if (_REENT->__cleanup)
+    _REENT->__cleanup (_REENT);
+
+  /* Ensure that SIGABRT can be caught regardless of blockage. */
+  sigset_t sig_mask;
+  sigfillset (&sig_mask);
+  sigdelset (&sig_mask, SIGABRT);
+  set_signal_mask (sig_mask);
+
+  raise (SIGABRT);
+  (void) call_signal_handler_now (); /* Call any signal handler */
+  do_exit (1); /* signal handler didn't exit.  Goodbye. */
 }
 
 extern "C" int
 sigaction (int sig, const struct sigaction *newact, struct sigaction *oldact)
 {
-  sigproc_printf ("sig %d, newact %p, oldact %p", sig, newact, oldact);
+  sig_dispatch_pending ();
+  sigproc_printf ("signal %d, newact %p, oldact %p", sig, newact, oldact);
   /* check that sig is in right range */
   if (sig < 0 || sig >= NSIG)
     {
       set_errno (EINVAL);
-      syscall_printf ("SIG_ERR = sigaction sig %d out of range", sig);
+      syscall_printf ("SIG_ERR = sigaction signal %d out of range", sig);
       return -1;
     }
 
@@ -262,7 +336,7 @@ sigaction (int sig, const struct sigaction *newact, struct sigaction *oldact)
 
   if (newact)
     {
-      if ((sig == SIGKILL || sig == SIGSTOP) && newact->sa_handler != SIG_DFL)
+      if (sig == SIGKILL || sig == SIGSTOP)
        {
          set_errno (EINVAL);
          return -1;
@@ -273,6 +347,12 @@ sigaction (int sig, const struct sigaction *newact, struct sigaction *oldact)
       if (newact->sa_handler == SIG_DFL && sig == SIGCHLD)
        sig_clear (sig);
       set_sigcatchers (oa.sa_handler, newact->sa_handler);
+      if (sig == SIGCHLD)
+       {
+         myself->process_state &= ~PID_NOCLDSTOP;
+         if (newact->sa_flags & SA_NOCLDSTOP);
+           myself->process_state |= PID_NOCLDSTOP;
+       }
     }
 
   if (oldact)
@@ -288,7 +368,7 @@ sigaddset (sigset_t *set, const int sig)
   if (sig <= 0 || sig >= NSIG)
     {
       set_errno (EINVAL);
-      syscall_printf ("SIG_ERR = sigaddset sig %d out of range", sig);
+      syscall_printf ("SIG_ERR = sigaddset signal %d out of range", sig);
       return -1;
     }
 
@@ -303,7 +383,7 @@ sigdelset (sigset_t *set, const int sig)
   if (sig <= 0 || sig >= NSIG)
     {
       set_errno (EINVAL);
-      syscall_printf ("SIG_ERR = sigdelset sig %d out of range", sig);
+      syscall_printf ("SIG_ERR = sigdelset signal %d out of range", sig);
       return -1;
     }
 
@@ -318,7 +398,7 @@ sigismember (const sigset_t *set, int sig)
   if (sig <= 0 || sig >= NSIG)
     {
       set_errno (EINVAL);
-      syscall_printf ("SIG_ERR = sigdelset sig %d out of range", sig);
+      syscall_printf ("SIG_ERR = sigdelset signal %d out of range", sig);
       return -1;
     }
 
@@ -343,17 +423,6 @@ sigfillset (sigset_t *set)
 }
 
 extern "C" int
-sigpending (sigset_t *set)
-{
-  unsigned bit;
-  *set = 0;
-  for (int sig = 1; sig < NSIG; sig++)
-    if (*myself->getsigtodo (sig) && myself->getsigmask () & (bit = SIGTOMASK (sig)))
-      *set |= bit;
-  return 0;
-}
-
-extern "C" int
 sigsuspend (const sigset_t *set)
 {
   return handle_sigsuspend (*set);
@@ -370,3 +439,43 @@ pause (void)
 {
   return handle_sigsuspend (myself->getsigmask ());
 }
+
+extern "C" int
+siginterrupt (int sig, int flag)
+{
+  struct sigaction act;
+  (void) sigaction(sig, NULL, &act);
+  if (flag)
+    act.sa_flags &= ~SA_RESTART;
+  else
+    act.sa_flags |= SA_RESTART;
+  return sigaction (sig, &act, NULL);
+}
+
+
+extern "C" int
+sigwait (const sigset_t *set, int *sig)
+{
+  pthread_testcancel ();
+  _my_tls.event = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL);
+  if (!_my_tls.event)
+    {
+      __seterrno ();
+      return -1;
+    }
+
+  _my_tls.sigwait_mask = *set;
+
+  switch (WaitForSingleObject (_my_tls.event, INFINITE))
+    {
+    case WAIT_OBJECT_0:
+      CloseHandle (_my_tls.event);
+      _my_tls.event = NULL;
+      *sig = InterlockedExchange ((LONG *) &_my_tls.sig, (LONG) 0);
+      break;
+    default:
+      __seterrno ();
+      return -1;
+    }
+  return 0;
+}