4
0
mirror of git://sourceware.org/git/newlib-cygwin.git synced 2025-01-16 03:19:54 +08:00
Christopher Faylor 8cb359d947 * child_info.h (child_info_spawn::hexec_proc): Eliminate.
* dcrt0.cc (dll_crt0_0): Remove hexec_proc stuff.
* fork.cc (fork_child): Remove call to pinfo_fixup_after_fork.
* pinfo.cc (set_myself): Close and zero pid_handle if set.
(pinfo_fixup_after_fork): Delete.
(proc_waiter): Don't close vchild.hProcess here.  Do that when we are remove
the vchild from procs.  Save hProcess as pid_handle only on first reparent
operation.
(pinfo::wait): Don't set pid_handle here.
(pinfo::alert_parent): Always try to send signal.  If unsuccessful then close
and zero wr_proc_pipe.
* pinfo.h (pinfo::pinfo): Make sure that appropriate parts of the class are
zeroed on construction.
(pinfo::alert_parent): Take char argument.
(pinfo_fixup_after_fork): Delete declaration.
(hexec_proc): Ditto.
* sigproc.cc (remove_proc): Close pid_handle and hProcess if appropriate.
* spawn.cc (spawn_guts): Set cygheap->pid_handle on first exec.
* cygheap.h (init_cygheap::pid_handle): New element.
* pinfo.cc (set_myself): Clear previously existing cygheap->pid_handle when a
new process has been started.
(pinfo::wait): Make sure that a handle to the newly forked/spawned process is
kept around so that the pid will not be reused.
* pinfo.h (_pinfo::pid_handle): Move.
(pinfo::pid_handle): to here.
* spawn.cc (spawn_guts): Create a pid_handle in cygheap prior to spawning to
ensure that the pid does not get reused during the lifetime of the "cygwin
pid".
* pinfo.h (pinfo::alert_parent): New function.
* exceptions.cc (sig_handle_tty_stop): Use alert_parent to send "signals" to
parent.
* fork.cc (fork_parent): Don't close pi.hProcess.  Let the waiter thread do
that.
* pinfo.cc (proc_waiter): Detect case where process exits without setting the
exit code and use value from GetExitCodeProcess.  Reluctantly implement
__SIGREPARENT.
(pinfo::alert_parent): Define.
* sigproc.h (__SIGREPARENT): New enum.
* spawn.cc (spawn_guts): Send reparent signal to parent on exec.  Always create
process in suspended state to avoid races.  Remove cygthread.h in favor of
cygtls.h throughout since cygtls now includes cygthread.h.  Eliminate
ppid_handle usage throughout.
* child_info.h: Regenerate magic number
(child_info): Remove pppid_handle.
* cygthread.h (cygthread::release): New method.  Frees thread without waiting.
* cygthread.cc (cygthread::stub): Set _ctinfo in _mytls to point to information
for executing thread.  Don't call SetEvent if thread is no longer in use.
(cygthread::simplestub): Ditto.
* cygtls.h (_cygtls::_ctinfo): New element contains pointer to information
about executing cygthread, if any.
* dcrt0.cc: Remove last vestiges of per_thread stuff.
(dll_crt0_0): Ditto.  Remove accommodation for ppid_handle.
(do_exit): Remove obsolete reparenting test.
(_exit): Exit with a more SUSv3-like exit value.
* dtable.cc (dtable::stdio_init): Check for myself->cygstarted rather than
myself->ppid_handle to see if we were started by a cygwin process.
* exceptions.cc (open_stackdumpfile): Ditto.
(handle_exceptions): Ditto.
(ctrl_c_handler): Ditto.
(sig_handle_tty_stop): Ditto.  Let parent send signal to itself on STOP.
(sigpacket::process): Comment out vfork test.
(signal_exit): Use more SUSv3-like exit value on signal.
* external.cc (fillout_pinfo): Don't set hProcess.
* fork.cc: Remove VFORK cruft.
(per_thread::set): Delete.
(fork_child): Remove perthread stuff.
(fork_parent): Remove obsolete subproc_init.  Accommodate new method for
tracking subprocesses.
* pinfo.cc (set_myself): Accommodate new pinfo/_pinfo layout.  Set some things
here that used to be set in wait_sig.
(_pinfo::exit): Set exitcode here.  Close process pipe.
(_pinfo::commune_send): Accommodeate new pinfo/_pinfo layout.
(proc_waiter): New function.  Waits, in a thread for subprocess to go away.
(pinfo::wait): New function.  Initialization for proc_waiter.
* pinfo.h (_pinfo::exitcode): New element.
(_pinfo::cygstarted): Ditto.
(_pinfo::wr_proc_pipe): Ditto.
(_pinfo::ppid_handle): Delete.
(_pinfo::hProcess): Delete.
(_pinfo::lock): Delete.
(pinfo::hProcess): New element.
(pinfo::lock): Ditto.
(pinfo::wait): Declare new function.
(pinfo::preserve): Define new function.
* sigproc.cc: Remove old stuff from wait_subproc thread based method.
(zombies): Remove.
(procs): New.
(my_parent_is_alive): Just check that the parent pid exists.
(mychild): Just use pinfo methods to determine if child is mine.
(proc_subproc): Revamp PROC_ADDCHILD to use pinfo::wait.  Remove
PROC_CHILDTERMINATED logic.  Use different method to remove processes from list
when SIGCHLD == SIG_IGN.
(proc_terminate): Gut.
(subproc_init): Delete.
(init_child_info): Remove setting of pppid_handle.
(checkstate): Revamp to only scan procs array.
(remove_proc): Rename from remove_zombie.  Don't close hProcess or pid_handle.
Don't release memory if it's myself.
(stopped_or_terminated): Change logic to handle new consolidated proc/zombie
array.
(wait_subproc): Delete.
* sigproc.h: Remove obsolete EXIT_* defines.
(subproc_init): Remove declaration.
* spawn.cc (spawn_guts): Remove reparenting stuff.  Use standard wait logic to
wait for child if started from a non-cygwin process.
* tlsoffsets.h: Regenerate.
* tty.cc (tty_init): Check for myself->cygstarted rather than
myself->ppid_handle to see if we were started by a cygwin process.
* include/sys/signal.h (external_pinfo::exitcode): Replace hProcess.
* include/sys/wait.h (WCOREDUMP): Define.
* fhandler_tty.cc (fhandler_tty_slave::read): Add debugging output for timeout
case.
* signal.cc (abort): Flag that we are exiting with the ABORT signal.
2004-11-26 04:15:10 +00:00

552 lines
12 KiB
C++

/* signal.cc
Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Red Hat, Inc.
Written by Steve Chamberlain of Cygnus Support, sac@cygnus.com
Significant changes by Sergey Okhapkin <sos@prospect.com.ru>
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#include "winsup.h"
#include <stdlib.h>
#include "cygerrno.h"
#include <sys/cygwin.h>
#include "pinfo.h"
#include "sigproc.h"
#include "hires.h"
#include "security.h"
#include "cygtls.h"
int sigcatchers; /* FIXME: Not thread safe. */
#define sigtrapped(func) ((func) != SIG_IGN && (func) != SIG_DFL)
static inline void
set_sigcatchers (void (*oldsig) (int), void (*cursig) (int))
{
#ifdef DEBUGGING
int last_sigcatchers = sigcatchers;
#endif
if (!sigtrapped (oldsig) && sigtrapped (cursig))
sigcatchers++;
else if (sigtrapped (oldsig) && !sigtrapped (cursig))
sigcatchers--;
#ifdef DEBUGGING
if (last_sigcatchers != sigcatchers)
sigproc_printf ("last %d, old %d, cur %p, cur %p", last_sigcatchers,
sigcatchers, oldsig, cursig);
#endif
}
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 || sig == SIGKILL || sig == SIGSTOP)
{
set_errno (EINVAL);
syscall_printf ("SIG_ERR = signal (%d, %p)", sig, func);
return (_sig_func_ptr) SIG_ERR;
}
prev = global_sigs[sig].sa_handler;
global_sigs[sig].sa_handler = func;
global_sigs[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. */
global_sigs[sig].sa_flags |= SA_RESTART;
set_sigcatchers (prev, func);
syscall_printf ("%p = signal (%d, %p)", prev, sig, func);
return prev;
}
extern "C" int
nanosleep (const struct timespec *rqtp, struct timespec *rmtp)
{
int res = 0;
sig_dispatch_pending ();
pthread_testcancel ();
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) _my_tls.call_signal_handler ();
set_errno (EINTR);
res = -1;
}
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)
{
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 ("signal %d out of range", sig);
return -1;
}
if (oldset)
{
if (check_null_invalid_struct_errno (oldset))
return -1;
*oldset = opmask;
}
if (set)
{
if (check_invalid_read_struct_errno (set))
return -1;
sigset_t newmask = opmask;
switch (sig)
{
case SIG_BLOCK:
/* add set to current mask */
newmask |= *set;
break;
case SIG_UNBLOCK:
/* remove set from current mask */
newmask &= ~*set;
break;
case SIG_SETMASK:
/* just set it */
newmask = *set;
break;
default:
set_errno (EINVAL);
return -1;
}
(void) set_signal_mask (newmask, opmask);
}
return 0;
}
static int
kill_worker (pid_t pid, siginfo_t& si)
{
sig_dispatch_pending ();
int res = 0;
pinfo dest (pid);
bool sendSIGCONT;
if (!dest)
{
set_errno (ESRCH);
return -1;
}
if ((sendSIGCONT = (si.si_signo < 0)))
si.si_signo = -si.si_signo;
DWORD process_state = dest->process_state;
if (si.si_signo == 0)
{
res = proc_exists (dest) ? 0 : -1;
if (res < 0)
set_errno (ESRCH);
}
else if ((res = sig_send (dest, si)))
{
sigproc_printf ("%d = sig_send, %E ", res);
res = -1;
}
else if (sendSIGCONT)
{
siginfo_t si2;
si2.si_signo = SIGCONT;
si2.si_code = SI_KERNEL;
si2.si_pid = si2.si_uid = si2.si_errno = 0;
(void) sig_send (dest, si2);
}
syscall_printf ("%d = kill_worker (%d, %d), process_state %p", res, pid,
si.si_signo, process_state);
return res;
}
int
raise (int sig)
{
return kill (myself->pid, sig);
}
static int
kill0 (pid_t pid, siginfo_t& si)
{
syscall_printf ("kill (%d, %d)", pid, si.si_signo);
/* check that sig is in right range */
if (si.si_signo < 0 || si.si_signo >= NSIG)
{
set_errno (EINVAL);
syscall_printf ("signal %d out of range", si.si_signo);
return -1;
}
/* Silently ignore stop signals from a member of orphaned process group.
FIXME: Why??? */
if (ISSTATE (myself, PID_ORPHANED) &&
(si.si_signo == SIGTSTP || si.si_signo == SIGTTIN || si.si_signo == SIGTTOU))
si.si_signo = 0;
return (pid > 0) ? kill_worker (pid, si) : kill_pgrp (-pid, si);
}
int
killsys (pid_t pid, int sig)
{
siginfo_t si;
si.si_signo = sig;
si.si_code = SI_KERNEL;
si.si_pid = si.si_uid = si.si_errno = 0;
return kill0 (pid, si);
}
int
kill (pid_t pid, int sig)
{
siginfo_t si;
si.si_signo = sig;
si.si_code = SI_USER;
si.si_pid = si.si_uid = si.si_errno = 0;
return kill0 (pid, si);
}
int
kill_pgrp (pid_t pid, siginfo_t& si)
{
int res = 0;
int found = 0;
int killself = 0;
sigproc_printf ("pid %d, signal %d", pid, si.si_signo);
winpids pids ((DWORD) PID_MAP_RW);
for (unsigned i = 0; i < pids.npids; i++)
{
_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)) ||
(pid > 1 && p->pgid != pid) ||
(si.si_signo < 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);
if (p == myself)
killself++;
else if (kill_worker (p->pid, si))
res = -1;
found++;
}
if (killself && !exit_state && kill_worker (myself->pid, si))
res = -1;
if (!found)
{
set_errno (ESRCH);
res = -1;
}
syscall_printf ("%d = kill (%d, %d)", res, pid, si.si_signo);
return res;
}
extern "C" int
killpg (pid_t pgrp, int sig)
{
return kill (-pgrp, sig);
}
extern "C" void
abort (void)
{
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) _my_tls.call_signal_handler (); /* Call any signal handler */
do_exit (SIGABRT); /* signal handler didn't exit. Goodbye. */
}
extern "C" int
sigaction (int sig, const struct sigaction *newact, struct sigaction *oldact)
{
sig_dispatch_pending ();
/* check that sig is in right range */
if (sig < 0 || sig >= NSIG)
{
set_errno (EINVAL);
sigproc_printf ("signal %d, newact %p, oldact %p", sig, newact, oldact);
syscall_printf ("SIG_ERR = sigaction signal %d out of range", sig);
return -1;
}
struct sigaction oa = global_sigs[sig];
if (newact)
sigproc_printf ("signal %d, newact %p (handler %p), oa %p", sig, newact, newact->sa_handler, oa, oa.sa_handler);
else
sigproc_printf ("signal %d, newact %p, oa %p", sig, newact, oa, oa.sa_handler);
if (newact)
{
if (sig == SIGKILL || sig == SIGSTOP)
{
set_errno (EINVAL);
return -1;
}
global_sigs[sig] = *newact;
if (newact->sa_handler == SIG_IGN)
sig_clear (sig);
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)
*oldact = oa;
return 0;
}
extern "C" int
sigaddset (sigset_t *set, const int sig)
{
/* check that sig is in right range */
if (sig <= 0 || sig >= NSIG)
{
set_errno (EINVAL);
syscall_printf ("SIG_ERR = sigaddset signal %d out of range", sig);
return -1;
}
*set |= SIGTOMASK (sig);
return 0;
}
extern "C" int
sigdelset (sigset_t *set, const int sig)
{
/* check that sig is in right range */
if (sig <= 0 || sig >= NSIG)
{
set_errno (EINVAL);
syscall_printf ("SIG_ERR = sigdelset signal %d out of range", sig);
return -1;
}
*set &= ~SIGTOMASK (sig);
return 0;
}
extern "C" int
sigismember (const sigset_t *set, int sig)
{
/* check that sig is in right range */
if (sig <= 0 || sig >= NSIG)
{
set_errno (EINVAL);
syscall_printf ("SIG_ERR = sigdelset signal %d out of range", sig);
return -1;
}
if (*set & SIGTOMASK (sig))
return 1;
else
return 0;
}
extern "C" int
sigemptyset (sigset_t *set)
{
*set = (sigset_t) 0;
return 0;
}
extern "C" int
sigfillset (sigset_t *set)
{
*set = ~((sigset_t) 0);
return 0;
}
extern "C" int
sigsuspend (const sigset_t *set)
{
return handle_sigsuspend (*set);
}
extern "C" int
sigpause (int signal_mask)
{
return handle_sigsuspend ((sigset_t) signal_mask);
}
extern "C" int
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_ptr)
{
int sig = sigwaitinfo (set, NULL);
if (sig > 0)
*sig_ptr = sig;
return sig > 0 ? 0 : -1;
}
extern "C" int
sigwaitinfo (const sigset_t *set, siginfo_t *info)
{
pthread_testcancel ();
HANDLE h;
h = _my_tls.event = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL);
if (!h)
{
__seterrno ();
return -1;
}
_my_tls.sigwait_mask = *set;
sig_dispatch_pending (true);
int res;
switch (WaitForSingleObject (h, INFINITE))
{
case WAIT_OBJECT_0:
if (!sigismember (set, _my_tls.infodata.si_signo))
{
set_errno (EINTR);
res = -1;
}
else
{
if (info)
*info = _my_tls.infodata;
res = _my_tls.infodata.si_signo;
InterlockedExchange ((LONG *) &_my_tls.sig, (LONG) 0);
}
break;
default:
__seterrno ();
res = -1;
}
CloseHandle (h);
sigproc_printf ("returning sig %d", res);
return res;
}
extern "C" int
sigqueue (pid_t pid, int sig, const union sigval value)
{
siginfo_t si;
pinfo dest (pid);
if (!dest)
{
set_errno (ESRCH);
return -1;
}
si.si_signo = sig;
si.si_code = SI_USER;
si.si_pid = si.si_uid = si.si_errno = 0;
si.si_value = value;
return sig_send (dest, si);
}