diff options
Diffstat (limited to 'winsup/cygwin/pinfo.cc')
-rw-r--r-- | winsup/cygwin/pinfo.cc | 413 |
1 files changed, 413 insertions, 0 deletions
diff --git a/winsup/cygwin/pinfo.cc b/winsup/cygwin/pinfo.cc new file mode 100644 index 000000000..789a47920 --- /dev/null +++ b/winsup/cygwin/pinfo.cc @@ -0,0 +1,413 @@ +/* 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 %x", _STRACE_INTERFACE_ACTIVATE_ADDR, + &strace_active); + OutputDebugString (buf); + 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"); + + (void) GetModuleFileName (NULL, myself->progname, + sizeof(myself->progname)); + 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; +} |