430 lines
9.9 KiB
C++
430 lines
9.9 KiB
C++
/* pinfo.cc: process table support
|
|
|
|
Copyright 1996, 1997, 1998, 2000 Cygnus Solutions.
|
|
|
|
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 <stdlib.h>
|
|
#include <time.h>
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include "winsup.h"
|
|
|
|
/* The first pid used; also the lowest value allowed. */
|
|
#define PBASE 1000
|
|
|
|
static char NO_COPY pinfo_dummy[sizeof(pinfo)] = {0};
|
|
|
|
pinfo NO_COPY *myself = (pinfo *)&pinfo_dummy; // Avoid myself != NULL checks
|
|
|
|
/* Initialize the process table.
|
|
This is done once when the dll is first loaded. */
|
|
|
|
void
|
|
pinfo_list::init (void)
|
|
{
|
|
next_pid = PBASE; /* Next pid to try to allocate. */
|
|
|
|
/* We assume the shared data area is already initialized to zeros.
|
|
Note that SIG_DFL is zero. */
|
|
}
|
|
|
|
pinfo * __stdcall
|
|
set_myself (pinfo *p)
|
|
{
|
|
myself = p;
|
|
if (!p)
|
|
return NULL;
|
|
|
|
myself->start_time = time (NULL); /* Register our starting time. */
|
|
|
|
char buf[30];
|
|
__small_sprintf (buf, "cYg%8x %x", _STRACE_INTERFACE_ACTIVATE_ADDR,
|
|
&strace.active);
|
|
OutputDebugString (buf);
|
|
|
|
(void) GetModuleFileName (NULL, myself->progname,
|
|
sizeof(myself->progname));
|
|
if (strace.active)
|
|
{
|
|
extern char osname[];
|
|
strace.prntf (1, "**********************************************");
|
|
strace.prntf (1, "Program name: %s", myself->progname);
|
|
strace.prntf (1, "App version: %d.%d, api: %d.%d",
|
|
user_data->dll_major, user_data->dll_minor,
|
|
user_data->api_major, user_data->api_minor);
|
|
strace.prntf (1, "DLL version: %d.%d, api: %d.%d",
|
|
cygwin_version.dll_major, cygwin_version.dll_minor,
|
|
cygwin_version.api_major, cygwin_version.api_minor);
|
|
strace.prntf (1, "DLL build: %s", cygwin_version.dll_build_date);
|
|
strace.prntf (1, "OS version: Windows %s", osname);
|
|
strace.prntf (1, "**********************************************");
|
|
}
|
|
|
|
return myself;
|
|
}
|
|
|
|
/* Initialize the process table entry for the current task.
|
|
This is not called for fork'd tasks, only exec'd ones. */
|
|
void __stdcall
|
|
pinfo_init (LPBYTE info)
|
|
{
|
|
if (info != NULL)
|
|
{
|
|
/* The process was execed. Reuse entry from the original
|
|
owner of this pid. */
|
|
environ_init (); /* Needs myself but affects calls below */
|
|
|
|
/* spawn has already set up a pid structure for us so we'll use that */
|
|
|
|
myself->process_state |= PID_CYGPARENT;
|
|
|
|
/* Inherit file descriptor information from parent in info.
|
|
*/
|
|
LPBYTE b = dtable.de_linearize_fd_array (info);
|
|
extern char title_buf[];
|
|
if (b && *b)
|
|
old_title = strcpy (title_buf, (char *)b);
|
|
}
|
|
else
|
|
{
|
|
/* Invent our own pid. */
|
|
|
|
if (!set_myself (cygwin_shared->p.allocate_pid ()))
|
|
api_fatal ("No more processes");
|
|
myself->ppid = myself->pgid = myself->sid = myself->pid;
|
|
myself->ctty = -1;
|
|
myself->uid = USHRT_MAX;
|
|
|
|
environ_init (); /* call after myself has been set up */
|
|
}
|
|
|
|
debug_printf ("pid %d, pgid %d", myself->pid, myself->pgid);
|
|
}
|
|
|
|
/* [] operator. This is the mechanism for table lookups. */
|
|
/* Returns the index into the pinfo_list table for pid arg */
|
|
|
|
pinfo *
|
|
pinfo_list::operator[] (pid_t pid)
|
|
{
|
|
if (pid <= 0)
|
|
return NULL;
|
|
|
|
pinfo *p = vec + (pid % size ());
|
|
|
|
if (p->pid != pid || p->process_state == PID_NOT_IN_USE)
|
|
return NULL;
|
|
else
|
|
return p;
|
|
}
|
|
|
|
struct sigaction&
|
|
pinfo::getsig(int sig)
|
|
{
|
|
#ifdef _MT_SAFE
|
|
if ( thread2signal )
|
|
return thread2signal->sigs[sig];
|
|
return sigs[sig];
|
|
#else
|
|
return sigs[sig];
|
|
#endif
|
|
};
|
|
|
|
sigset_t&
|
|
pinfo::getsigmask ()
|
|
{
|
|
#ifdef _MT_SAFE
|
|
if ( thread2signal )
|
|
return *thread2signal->sigmask;
|
|
return sig_mask;
|
|
#else
|
|
return sig_mask;
|
|
#endif
|
|
};
|
|
|
|
void
|
|
pinfo::setsigmask (sigset_t _mask)
|
|
{
|
|
#ifdef _MT_SAFE
|
|
if ( thread2signal )
|
|
*(thread2signal->sigmask) = _mask;
|
|
sig_mask=_mask;
|
|
#else
|
|
sig_mask=_mask;
|
|
#endif
|
|
}
|
|
|
|
LONG *
|
|
pinfo::getsigtodo(int sig)
|
|
{
|
|
#ifdef _MT_SAFE
|
|
if ( thread2signal )
|
|
return thread2signal->sigtodo + __SIGOFFSET + sig;
|
|
return _sigtodo + __SIGOFFSET + sig;
|
|
#else
|
|
return _sigtodo + __SIGOFFSET + sig;
|
|
#endif
|
|
}
|
|
|
|
extern HANDLE hMainThread;
|
|
|
|
HANDLE
|
|
pinfo::getthread2signal()
|
|
{
|
|
#ifdef _MT_SAFE
|
|
if ( thread2signal )
|
|
return thread2signal->win32_obj_id;
|
|
return hMainThread;
|
|
#else
|
|
return hMainThread;
|
|
#endif
|
|
}
|
|
|
|
void
|
|
pinfo::setthread2signal(void *_thr)
|
|
{
|
|
#ifdef _MT_SAFE
|
|
// assert has myself lock
|
|
thread2signal=(ThreadItem*)_thr;
|
|
#else
|
|
#endif
|
|
}
|
|
|
|
void
|
|
pinfo::copysigs(pinfo *_other)
|
|
{
|
|
sigs = _other->sigs;
|
|
}
|
|
|
|
pinfo * __stdcall
|
|
procinfo (int pid)
|
|
{
|
|
return cygwin_shared->p[pid];
|
|
}
|
|
|
|
#ifdef DEBUGGING
|
|
/*
|
|
* Code to lock/unlock the process table.
|
|
*/
|
|
|
|
int __stdcall
|
|
lpfu (const char *func, int ln, DWORD timeout)
|
|
{
|
|
int rc;
|
|
DWORD t;
|
|
|
|
debug_printf ("timeout %d, pinfo_mutex %p", timeout, pinfo_mutex);
|
|
t = (timeout == INFINITE) ? 10000 : timeout;
|
|
SetLastError(0);
|
|
while ((rc = WaitForSingleObject (pinfo_mutex, t)) != WAIT_OBJECT_0)
|
|
{
|
|
if (rc == WAIT_ABANDONED_0)
|
|
break;
|
|
system_printf ("%s:%d having problems getting lock", func, ln);
|
|
system_printf ("*** %s, rc %d, %E", cygwin_shared->p.lock_info, rc);
|
|
if (t == timeout)
|
|
break;
|
|
}
|
|
|
|
__small_sprintf (cygwin_shared->p.lock_info, "%s(%d), pid %d ", func, ln,
|
|
(user_data && myself) ? (int)myself->dwProcessId : -1);
|
|
return rc;
|
|
}
|
|
|
|
void
|
|
unlock_pinfo (void)
|
|
{
|
|
|
|
debug_printf ("handle %d", pinfo_mutex);
|
|
|
|
if (!cygwin_shared->p.lock_info[0])
|
|
system_printf ("lock_info not set?");
|
|
else
|
|
strcat (cygwin_shared->p.lock_info, " unlocked");
|
|
if (!ReleaseMutex (pinfo_mutex))
|
|
system_printf ("ReleaseMutext (pinfo_mutex<%p>) failed, %E", pinfo_mutex);
|
|
}
|
|
#else
|
|
/*
|
|
* Code to lock/unlock the process table.
|
|
*/
|
|
|
|
int __stdcall
|
|
lock_pinfo_for_update (DWORD timeout)
|
|
{
|
|
DWORD rc;
|
|
DWORD t;
|
|
|
|
debug_printf ("timeout %d, pinfo_mutex %p", timeout, pinfo_mutex);
|
|
t = (timeout == INFINITE) ? 10000 : timeout;
|
|
SetLastError(0);
|
|
while ((rc = WaitForSingleObject (pinfo_mutex, t)) != WAIT_OBJECT_0)
|
|
{
|
|
if (rc == WAIT_ABANDONED_0)
|
|
break;
|
|
system_printf ("rc %d, pinfo_mutex %p, %E", pinfo_mutex, rc);
|
|
if (t == timeout)
|
|
break;
|
|
if (rc == WAIT_FAILED)
|
|
/* sigh, must be properly fixed up later. */
|
|
return rc;
|
|
Sleep(10); /* to prevent 100% CPU in those rare cases */
|
|
}
|
|
|
|
return (int)rc;
|
|
}
|
|
|
|
void
|
|
unlock_pinfo (void)
|
|
{
|
|
|
|
debug_printf ("handle %d", pinfo_mutex);
|
|
|
|
ReleaseMutex (pinfo_mutex);
|
|
}
|
|
#endif
|
|
|
|
|
|
/* Allocate a process table entry by finding an empty slot in the
|
|
fixed-size process table. We could use a linked list, but this
|
|
would probably be too slow.
|
|
|
|
Try to allocate next_pid, incrementing next_pid and trying again
|
|
up to size() times at which point we reach the conclusion that
|
|
table is full. Eventually at this point we would grow the table
|
|
by size() and start over. If we find a pid to use,
|
|
|
|
If all else fails, sweep through the loop looking for processes that
|
|
may have died abnormally without registering themselves as "dead".
|
|
Clear out these pinfo structures. Then scan the table again.
|
|
|
|
Note that the process table is in the shared data space and thus
|
|
is susceptible to corruption. The amount of time spent scanning the
|
|
table is presumably quite small compared with the total time to
|
|
create a process.
|
|
*/
|
|
|
|
pinfo *
|
|
pinfo_list::allocate_pid (void)
|
|
{
|
|
|
|
pinfo *newp;
|
|
|
|
lock_pinfo_for_update (INFINITE);
|
|
for (int tries = 0; ; tries++)
|
|
{
|
|
for (int i = next_pid; i < (next_pid + size ()); i++)
|
|
{
|
|
/* i mod size() gives place to check */
|
|
newp = vec + (i % size());
|
|
if (newp->process_state == PID_NOT_IN_USE)
|
|
{
|
|
debug_printf ("found empty slot %d for pid %d",
|
|
(i % size ()), i);
|
|
next_pid = i;
|
|
goto gotit;
|
|
}
|
|
}
|
|
|
|
if (tries > 0)
|
|
break;
|
|
|
|
/* try once to remove bogus dead processes */
|
|
debug_printf ("clearing out deadwood");
|
|
for (newp = vec; newp < vec + size(); newp++)
|
|
proc_exists (newp);
|
|
}
|
|
|
|
/* The process table is full. */
|
|
debug_printf ("process table is full");
|
|
unlock_pinfo ();
|
|
|
|
return NULL;
|
|
|
|
gotit:
|
|
|
|
/* Set new pid based on the position of this element in the pinfo list */
|
|
newp->pid = next_pid;
|
|
|
|
/* Determine next slot to consider, wrapping if we hit the end of
|
|
* the array. Since allocation involves looping through size () pids,
|
|
* don't allow next_pid to be greater than SHRT_MAX - size ().
|
|
*/
|
|
if (next_pid < (SHRT_MAX - size ()))
|
|
next_pid++;
|
|
else
|
|
next_pid = PBASE;
|
|
|
|
newp->process_state = PID_IN_USE;
|
|
unlock_pinfo ();
|
|
|
|
memset (newp, 0, PINFO_ZERO);
|
|
debug_printf ("pid %d, state %x", newp->pid, newp->process_state);
|
|
return newp;
|
|
}
|
|
|
|
void
|
|
pinfo::record_death (int lock)
|
|
{
|
|
int unlock = lock ? 0 : lock_pinfo_for_update (999);
|
|
if (dwProcessId == GetCurrentProcessId () && !my_parent_is_alive ())
|
|
{
|
|
process_state = PID_NOT_IN_USE;
|
|
hProcess = NULL;
|
|
}
|
|
|
|
if (unlock)
|
|
unlock_pinfo ();
|
|
}
|
|
|
|
/* DOCTOOL-START
|
|
|
|
<sect1 id="func-cygwin-winpid-to-pid">
|
|
<title>cygwin_winpid_to_pid</title>
|
|
|
|
<funcsynopsis>
|
|
<funcdef>extern "C" pid_t
|
|
<function>cygwin_winpid_to_pid</function>
|
|
</funcdef>
|
|
<paramdef>int <parameter>winpid</parameter></paramdef>
|
|
</funcsynopsis>
|
|
|
|
<para>Given a windows pid, converts to the corresponding Cygwin
|
|
pid, if any. Returns -1 if windows pid does not correspond to
|
|
a cygwin pid.</para>
|
|
<example>
|
|
<title>Example use of cygwin_winpid_to_pid</title>
|
|
<programlisting>
|
|
extern "C" cygwin_winpid_to_pid (int winpid);
|
|
pid_t mypid;
|
|
mypid = cygwin_winpid_to_pid (windows_pid);
|
|
</programlisting>
|
|
</example>
|
|
</sect1>
|
|
|
|
DOCTOOL-END */
|
|
|
|
extern "C" pid_t
|
|
cygwin_winpid_to_pid (int winpid)
|
|
{
|
|
for (int i = 0; i < cygwin_shared->p.size (); i++)
|
|
{
|
|
pinfo *p = &cygwin_shared->p.vec[i];
|
|
|
|
if (p->process_state == PID_NOT_IN_USE)
|
|
continue;
|
|
|
|
/* FIXME: signed vs unsigned comparison: winpid can be < 0 !!! */
|
|
if (p->dwProcessId == (DWORD)winpid)
|
|
return p->pid;
|
|
}
|
|
|
|
set_errno (ESRCH);
|
|
return (pid_t) -1;
|
|
}
|