summaryrefslogtreecommitdiff
path: root/winsup/cygwin/pinfo.cc
diff options
context:
space:
mode:
Diffstat (limited to 'winsup/cygwin/pinfo.cc')
-rw-r--r--winsup/cygwin/pinfo.cc803
1 files changed, 803 insertions, 0 deletions
diff --git a/winsup/cygwin/pinfo.cc b/winsup/cygwin/pinfo.cc
new file mode 100644
index 00000000000..4a01193ae8b
--- /dev/null
+++ b/winsup/cygwin/pinfo.cc
@@ -0,0 +1,803 @@
+/* pinfo.cc: process table support
+
+ Copyright 1996, 1997, 1998, 2000, 2001, 2002, 2003 Red Hat, Inc.
+
+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 <time.h>
+#include <limits.h>
+#include <stdarg.h>
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygerrno.h"
+#include "sigproc.h"
+#include "pinfo.h"
+#include "cygwin_version.h"
+#include "perprocess.h"
+#include "environ.h"
+#include <assert.h>
+#include <ntdef.h>
+#include "ntdll.h"
+#include "cygthread.h"
+#include "shared_info.h"
+#include "cygheap.h"
+#include "fhandler.h"
+
+static char NO_COPY pinfo_dummy[sizeof (_pinfo)] = {0};
+
+pinfo NO_COPY myself ((_pinfo *)&pinfo_dummy); // Avoid myself != NULL checks
+
+HANDLE hexec_proc;
+
+void __stdcall
+pinfo_fixup_after_fork ()
+{
+ if (hexec_proc)
+ CloseHandle (hexec_proc);
+ /* Keeps the cygpid from being reused. No rights required */
+ if (!DuplicateHandle (hMainProc, hMainProc, hMainProc, &hexec_proc, 0,
+ TRUE, 0))
+ {
+ system_printf ("couldn't save current process handle %p, %E", hMainProc);
+ hexec_proc = NULL;
+ }
+ VerifyHandle (hexec_proc);
+}
+
+/* Initialize the process table.
+ This is done once when the dll is first loaded. */
+
+void __stdcall
+set_myself (pid_t pid, HANDLE h)
+{
+ DWORD winpid = GetCurrentProcessId ();
+ if (pid == 1)
+ pid = cygwin_pid (winpid);
+ myself.init (pid, PID_IN_USE | PID_MYSELF, h);
+ myself->dwProcessId = winpid;
+ myself->process_state |= PID_IN_USE;
+ myself->start_time = time (NULL); /* Register our starting time. */
+
+ (void) GetModuleFileName (NULL, myself->progname, sizeof (myself->progname));
+ if (!strace.active)
+ strace.hello ();
+ InitializeCriticalSection (&myself->lock);
+ return;
+}
+
+/* Initialize the process table entry for the current task.
+ This is not called for forked tasks, only execed ones. */
+void __stdcall
+pinfo_init (char **envp, int envc)
+{
+ if (envp)
+ {
+ environ_init (envp, envc);
+ /* spawn has already set up a pid structure for us so we'll use that */
+ myself->process_state |= PID_CYGPARENT;
+ }
+ else
+ {
+ /* Invent our own pid. */
+
+ set_myself (1);
+ myself->ppid = 1;
+ myself->pgid = myself->sid = myself->pid;
+ myself->ctty = -1;
+ myself->uid = ILLEGAL_UID;
+ myself->gid = UNKNOWN_GID;
+ environ_init (NULL, 0); /* call after myself has been set up */
+ }
+
+ debug_printf ("pid %d, pgid %d", myself->pid, myself->pgid);
+}
+
+void
+_pinfo::exit (UINT n, bool norecord)
+{
+ if (this)
+ {
+ if (!norecord)
+ process_state = PID_EXITED;
+
+ /* FIXME: There is a potential race between an execed process and its
+ parent here. I hated to add a mutex just for this, though. */
+ struct rusage r;
+ fill_rusage (&r, hMainProc);
+ add_rusage (&rusage_self, &r);
+ }
+
+ cygthread::terminate ();
+ sigproc_printf ("Calling ExitProcess %d", n);
+ ExitProcess (n);
+}
+
+void
+pinfo::init (pid_t n, DWORD flag, HANDLE in_h)
+{
+ if (myself && n == myself->pid)
+ {
+ procinfo = myself;
+ destroy = 0;
+ h = NULL;
+ return;
+ }
+
+ void *mapaddr;
+ if (!(flag & PID_MYSELF))
+ mapaddr = NULL;
+ else
+ {
+ flag &= ~PID_MYSELF;
+ HANDLE hdummy;
+ mapaddr = open_shared (NULL, 0, hdummy, 0, SH_MYSELF);
+ }
+
+ int createit = flag & (PID_IN_USE | PID_EXECED);
+ DWORD access = FILE_MAP_READ
+ | (flag & (PID_IN_USE | PID_EXECED | PID_MAP_RW) ? FILE_MAP_WRITE : 0);
+ for (int i = 0; i < 10; i++)
+ {
+ int created;
+ char mapname[CYG_MAX_PATH]; /* XXX Not a path */
+ shared_name (mapname, "cygpid", n);
+
+ int mapsize;
+ if (flag & PID_EXECED)
+ mapsize = PINFO_REDIR_SIZE;
+ else
+ mapsize = sizeof (_pinfo);
+
+ if (in_h)
+ {
+ h = in_h;
+ created = 0;
+ }
+ else if (!createit)
+ {
+ h = OpenFileMappingA (access, FALSE, mapname);
+ created = 0;
+ }
+ else
+ {
+ char sa_buf[1024];
+ PSECURITY_ATTRIBUTES sec_attribs =
+ sec_user_nih (sa_buf, cygheap->user.sid(), well_known_world_sid,
+ FILE_MAP_READ);
+ h = CreateFileMapping (INVALID_HANDLE_VALUE, sec_attribs,
+ PAGE_READWRITE, 0, mapsize, mapname);
+ created = h && GetLastError () != ERROR_ALREADY_EXISTS;
+ }
+
+ if (!h)
+ {
+ if (createit)
+ __seterrno ();
+ procinfo = NULL;
+ return;
+ }
+
+ procinfo = (_pinfo *) MapViewOfFileEx (h, access, 0, 0, 0, mapaddr);
+ ProtectHandle1 (h, pinfo_shared_handle);
+
+ if ((procinfo->process_state & PID_INITIALIZING) && (flag & PID_NOREDIR)
+ && cygwin_pid (procinfo->dwProcessId) != procinfo->pid)
+ {
+ release ();
+ set_errno (ENOENT);
+ return;
+ }
+
+ if (procinfo->process_state & PID_EXECED)
+ {
+ assert (!i);
+ pid_t realpid = procinfo->pid;
+ debug_printf ("execed process windows pid %d, cygwin pid %d", n, realpid);
+ if (realpid == n)
+ api_fatal ("retrieval of execed process info for pid %d failed due to recursion.", n);
+ n = realpid;
+ release ();
+ if (flag & PID_ALLPIDS)
+ {
+ set_errno (ENOENT);
+ break;
+ }
+ continue;
+ }
+
+ /* In certain rare, pathological cases, it is possible for the shared
+ memory region to exist for a while after a process has exited. This
+ should only be a brief occurrence, so rather than introduce some kind
+ of locking mechanism, just loop. FIXME: I'm sure I'll regret doing it
+ this way at some point. */
+ if (i < 9 && !created && createit && (procinfo->process_state & PID_EXITED))
+ {
+ low_priority_sleep (5);
+ release ();
+ continue;
+ }
+
+ if (!created)
+ /* nothing */;
+ else if (!(flag & PID_EXECED))
+ procinfo->pid = n;
+ else
+ {
+ procinfo->process_state |= PID_IN_USE | PID_EXECED;
+ procinfo->pid = myself->pid;
+ }
+ break;
+ }
+ destroy = 1;
+}
+
+void
+pinfo::set_acl()
+{
+ char sa_buf[1024];
+ SECURITY_DESCRIPTOR sd;
+
+ sec_acl ((PACL) sa_buf, true, true, cygheap->user.sid (),
+ well_known_world_sid, FILE_MAP_READ);
+ if (!InitializeSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION))
+ debug_printf ("InitializeSecurityDescriptor %E");
+ else if (!SetSecurityDescriptorDacl (&sd, TRUE, (PACL) sa_buf, FALSE))
+ debug_printf ("SetSecurityDescriptorDacl %E");
+ else if (!SetKernelObjectSecurity (h, DACL_SECURITY_INFORMATION, &sd))
+ debug_printf ("SetKernelObjectSecurity %E");
+}
+
+void
+_pinfo::set_ctty (tty_min *tc, int flags, fhandler_tty_slave *arch)
+{
+ debug_printf ("checking if /dev/tty%d differs from input", ctty);
+ if ((ctty < 0 || ctty == tc->ntty) && !(flags & O_NOCTTY))
+ {
+ ctty = tc->ntty;
+ syscall_printf ("attached tty%d sid %d, pid %d, tty->pgid %d, tty->sid %d",
+ tc->ntty, sid, pid, pgid, tc->getsid ());
+
+ pinfo p (tc->getsid ());
+ if (sid == pid && (!p || p->pid == pid || !proc_exists (p)))
+ {
+ paranoid_printf ("resetting tty%d sid. Was %d, now %d. pgid was %d, now %d.",
+ tc->ntty, tc->getsid (), sid, tc->getpgid (), pgid);
+ /* We are the session leader */
+ tc->setsid (sid);
+ tc->setpgid (pgid);
+ }
+ else
+ sid = tc->getsid ();
+ if (tc->getpgid () == 0)
+ tc->setpgid (pgid);
+ if (cygheap->ctty != arch)
+ {
+ debug_printf ("cygheap->ctty %p, arch %p", cygheap->ctty, arch);
+ if (!cygheap->ctty)
+ syscall_printf ("ctty NULL");
+ else
+ {
+ syscall_printf ("ctty %p, usecount %d", cygheap->ctty,
+ cygheap->ctty->usecount);
+ cygheap->ctty->close ();
+ }
+ cygheap->ctty = arch;
+ if (arch)
+ {
+ arch->usecount++;
+ cygheap->open_fhs++;
+ report_tty_counts (cygheap->ctty, "ctty", "incremented ", "");
+ }
+ }
+ }
+}
+
+bool
+_pinfo::alive ()
+{
+ HANDLE h = OpenProcess (PROCESS_QUERY_INFORMATION, false, dwProcessId);
+ if (h)
+ CloseHandle (h);
+ return !!h;
+}
+
+extern char **__argv;
+
+void
+_pinfo::commune_recv ()
+{
+ DWORD nr;
+ DWORD code;
+ HANDLE hp;
+ HANDLE __fromthem = NULL;
+ HANDLE __tothem = NULL;
+
+ hp = OpenProcess (PROCESS_DUP_HANDLE, false, dwProcessId);
+ if (!hp)
+ {
+ sigproc_printf ("couldn't open handle for pid %d(%u)", pid, dwProcessId);
+ hello_pid = -1;
+ return;
+ }
+ if (!DuplicateHandle (hp, fromthem, hMainProc, &__fromthem, 0, false, DUPLICATE_SAME_ACCESS))
+ {
+ sigproc_printf ("couldn't duplicate fromthem, %E");
+ CloseHandle (hp);
+ hello_pid = -1;
+ return;
+ }
+
+ if (!DuplicateHandle (hp, tothem, hMainProc, &__tothem, 0, false, DUPLICATE_SAME_ACCESS))
+ {
+ sigproc_printf ("couldn't duplicate tothem, %E");
+ CloseHandle (__fromthem);
+ CloseHandle (hp);
+ hello_pid = -1;
+ return;
+ }
+
+ CloseHandle (hp);
+ hello_pid = 0;
+
+ if (!ReadFile (__fromthem, &code, sizeof code, &nr, NULL) || nr != sizeof code)
+ {
+ /* __seterrno ();*/ // this is run from the signal thread, so don't set errno
+ goto out;
+ }
+
+ switch (code)
+ {
+ case PICOM_CMDLINE:
+ {
+ unsigned n = 1;
+ CloseHandle (__fromthem); __fromthem = NULL;
+ extern int __argc_safe;
+ const char *argv[__argc_safe + 1];
+ for (int i = 0; i < __argc_safe; i++)
+ {
+ if (IsBadStringPtr (__argv[i], INT32_MAX))
+ argv[i] = "";
+ else
+ argv[i] = __argv[i];
+ n += strlen (argv[i]) + 1;
+ }
+ argv[__argc_safe] = NULL;
+ if (!WriteFile (__tothem, &n, sizeof n, &nr, NULL))
+ {
+ /*__seterrno ();*/ // this is run from the signal thread, so don't set errno
+ sigproc_printf ("WriteFile sizeof argv failed, %E");
+ }
+ else
+ for (const char **a = argv; *a; a++)
+ if (!WriteFile (__tothem, *a, strlen (*a) + 1, &nr, NULL))
+ {
+ sigproc_printf ("WriteFile arg %d failed, %E", a - argv);
+ break;
+ }
+ if (!WriteFile (__tothem, "", 1, &nr, NULL))
+ {
+ sigproc_printf ("WriteFile null failed, %E");
+ break;
+ }
+ break;
+ }
+ case PICOM_FIFO:
+ {
+ int formic;
+ if (!ReadFile (__fromthem, &formic, sizeof formic, &nr, NULL)
+ || nr != sizeof formic)
+ {
+ /* __seterrno ();*/ // this is run from the signal thread, so don't set errno
+ goto out;
+ }
+
+ fhandler_fifo *fh = cygheap->fdtab.find_fifo ((ATOM) formic);
+ HANDLE it[] = {(fh->get_handle ()), (fh->get_output_handle ())};
+
+ if (!WriteFile (__tothem, it, sizeof (it), &nr, NULL))
+ {
+ /*__seterrno ();*/ // this is run from the signal thread, so don't set errno
+ sigproc_printf ("WriteFile read handle failed, %E");
+ }
+
+ (void) ReadFile (__fromthem, &nr, sizeof (nr), &nr, NULL);
+ break;
+ }
+ }
+
+out:
+ if (__fromthem)
+ CloseHandle (__fromthem);
+ if (__tothem)
+ CloseHandle (__tothem);
+}
+
+#define PIPEBUFSIZE (16 * sizeof (DWORD))
+
+commune_result
+_pinfo::commune_send (DWORD code, ...)
+{
+ HANDLE fromthem = NULL, tome = NULL;
+ HANDLE fromme = NULL, tothem = NULL;
+ DWORD nr;
+ commune_result res;
+ va_list args;
+
+ va_start (args, code);
+
+ res.s = NULL;
+ res.n = 0;
+
+ if (!this || !pid)
+ {
+ set_errno (ESRCH);
+ goto err;
+ }
+ if (!CreatePipe (&fromthem, &tome, &sec_all_nih, PIPEBUFSIZE))
+ {
+ sigproc_printf ("first CreatePipe failed, %E");
+ __seterrno ();
+ goto err;
+ }
+ if (!CreatePipe (&fromme, &tothem, &sec_all_nih, PIPEBUFSIZE))
+ {
+ sigproc_printf ("first CreatePipe failed, %E");
+ __seterrno ();
+ goto err;
+ }
+ EnterCriticalSection (&myself->lock);
+ myself->tothem = tome;
+ myself->fromthem = fromme;
+ myself->hello_pid = pid;
+ if (!WriteFile (tothem, &code, sizeof code, &nr, NULL) || nr != sizeof code)
+ {
+ __seterrno ();
+ goto err;
+ }
+
+ switch (code)
+ {
+ case PICOM_FIFO:
+ {
+ int formic = va_arg (args, int);
+ if (!WriteFile (tothem, &formic, sizeof formic, &nr, NULL) || nr != sizeof formic)
+ {
+ __seterrno ();
+ goto err;
+ }
+ break;
+ }
+ }
+
+ if (sig_send (this, __SIGCOMMUNE))
+ goto err;
+
+ /* FIXME: Need something better than an busy loop here */
+ bool isalive;
+ for (int i = 0; (isalive = alive ()) && (i < 10000); i++)
+ if (myself->hello_pid <= 0)
+ break;
+ else
+ low_priority_sleep (0);
+
+ CloseHandle (tome);
+ tome = NULL;
+ CloseHandle (fromme);
+ fromme = NULL;
+
+ if (!isalive)
+ {
+ set_errno (ESRCH);
+ goto err;
+ }
+
+ if (myself->hello_pid < 0)
+ {
+ set_errno (ENOSYS);
+ goto err;
+ }
+
+ size_t n;
+ switch (code)
+ {
+ case PICOM_CMDLINE:
+ if (!ReadFile (fromthem, &n, sizeof n, &nr, NULL) || nr != sizeof n)
+ {
+ __seterrno ();
+ goto err;
+ }
+ res.s = (char *) malloc (n);
+ char *p;
+ for (p = res.s; ReadFile (fromthem, p, n, &nr, NULL); p += nr)
+ continue;
+ if ((unsigned) (p - res.s) != n)
+ {
+ __seterrno ();
+ goto err;
+ }
+ res.n = n;
+ break;
+ case PICOM_FIFO:
+ {
+ DWORD x = ReadFile (fromthem, res.handles, sizeof (res.handles), &nr, NULL);
+ WriteFile (tothem, &x, sizeof (x), &x, NULL);
+ if (!x)
+ goto err;
+ if (nr != sizeof (res.handles))
+ {
+ set_errno (EPIPE);
+ goto err;
+ }
+ break;
+ }
+ }
+ CloseHandle (tothem);
+ CloseHandle (fromthem);
+ goto out;
+
+err:
+ if (tome)
+ CloseHandle (tome);
+ if (fromthem)
+ CloseHandle (fromthem);
+ if (tothem)
+ CloseHandle (tothem);
+ if (fromme)
+ CloseHandle (fromme);
+ memset (&res, 0, sizeof (res));
+
+out:
+ myself->hello_pid = 0;
+ LeaveCriticalSection (&myself->lock);
+ return res;
+}
+
+char *
+_pinfo::cmdline (size_t& n)
+{
+ char *s;
+ if (!this || !pid)
+ return NULL;
+ if (pid != myself->pid)
+ {
+ commune_result cr = commune_send (PICOM_CMDLINE);
+ s = cr.s;
+ n = cr.n;
+ }
+ else
+ {
+ n = 1;
+ for (char **a = __argv; *a; a++)
+ n += strlen (*a) + 1;
+ char *p;
+ p = s = (char *) malloc (n);
+ for (char **a = __argv; *a; a++)
+ {
+ strcpy (p, *a);
+ p = strchr (p, '\0') + 1;
+ }
+ *p = '\0';
+ }
+ return s;
+}
+
+void
+pinfo::release ()
+{
+ if (h)
+ {
+#ifdef DEBUGGING
+ if (((DWORD) procinfo & 0x77000000) == 0x61000000)
+ try_to_debug ();
+#endif
+ UnmapViewOfFile (procinfo);
+ procinfo = NULL;
+ ForceCloseHandle1 (h, pinfo_shared_handle);
+ h = NULL;
+ }
+}
+
+/* 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)
+{
+ pinfo p (cygwin_pid (winpid));
+ if (p)
+ return p->pid;
+
+ set_errno (ESRCH);
+ return (pid_t) -1;
+}
+
+#include <tlhelp32.h>
+
+#define slop_pidlist 200
+#define size_pidlist(i) (sizeof (pidlist[0]) * ((i) + 1))
+#define size_pinfolist(i) (sizeof (pinfolist[0]) * ((i) + 1))
+
+inline void
+winpids::add (DWORD& nelem, bool winpid, DWORD pid)
+{
+ pid_t cygpid = cygwin_pid (pid);
+ if (nelem >= npidlist)
+ {
+ npidlist += slop_pidlist;
+ pidlist = (DWORD *) realloc (pidlist, size_pidlist (npidlist + 1));
+ pinfolist = (pinfo *) realloc (pinfolist, size_pinfolist (npidlist + 1));
+ }
+
+ pinfolist[nelem].init (cygpid, PID_NOREDIR | (winpid ? PID_ALLPIDS : 0)
+ | pinfo_access);
+ if (winpid)
+ goto out;
+
+ if (!pinfolist[nelem])
+ {
+ if (!pinfo_access)
+ return;
+ pinfolist[nelem].init (cygpid, PID_NOREDIR | (winpid ? PID_ALLPIDS : 0));
+ if (!pinfolist[nelem])
+ return;
+ }
+
+ /* Scan list of previously recorded pids to make sure that this pid hasn't
+ shown up before. This can happen when a process execs. */
+ for (unsigned i = 0; i < nelem; i++)
+ if (pinfolist[i]->pid == pinfolist[nelem]->pid)
+ {
+ if ((_pinfo *) pinfolist[nelem] != (_pinfo *) myself)
+ pinfolist[nelem].release ();
+ return;
+ }
+
+out:
+ pidlist[nelem++] = pid;
+}
+
+DWORD
+winpids::enumNT (bool winpid)
+{
+ static DWORD szprocs;
+ static SYSTEM_PROCESSES *procs;
+
+ DWORD nelem = 0;
+ if (!szprocs)
+ procs = (SYSTEM_PROCESSES *) malloc (sizeof (*procs) + (szprocs = 200 * sizeof (*procs)));
+
+ NTSTATUS res;
+ for (;;)
+ {
+ res = NtQuerySystemInformation (SystemProcessesAndThreadsInformation,
+ procs, szprocs, NULL);
+ if (res == 0)
+ break;
+
+ if (res == STATUS_INFO_LENGTH_MISMATCH)
+ procs = (SYSTEM_PROCESSES *) realloc (procs, szprocs += 200 * sizeof (*procs));
+ else
+ {
+ system_printf ("error %p reading system process information", res);
+ return 0;
+ }
+ }
+
+ SYSTEM_PROCESSES *px = procs;
+ for (;;)
+ {
+ if (px->ProcessId)
+ add (nelem, winpid, px->ProcessId);
+ if (!px->NextEntryDelta)
+ break;
+ px = (SYSTEM_PROCESSES *) ((char *) px + px->NextEntryDelta);
+ }
+
+ return nelem;
+}
+
+DWORD
+winpids::enum9x (bool winpid)
+{
+ DWORD nelem = 0;
+
+ HANDLE h = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0);
+ if (!h)
+ {
+ system_printf ("Couldn't create process snapshot, %E");
+ return 0;
+ }
+
+ PROCESSENTRY32 proc;
+ proc.dwSize = sizeof (proc);
+
+ if (Process32First (h, &proc))
+ do
+ {
+ if (proc.th32ProcessID)
+ add (nelem, winpid, proc.th32ProcessID);
+ }
+ while (Process32Next (h, &proc));
+
+ CloseHandle (h);
+ return nelem;
+}
+
+NO_COPY CRITICAL_SECTION winpids::cs;
+
+void
+winpids::set (bool winpid)
+{
+ EnterCriticalSection (&cs);
+ npids = (this->*enum_processes) (winpid);
+ if (pidlist)
+ pidlist[npids] = 0;
+ LeaveCriticalSection (&cs);
+}
+
+void
+winpids::init ()
+{
+ InitializeCriticalSection (&cs);
+}
+
+DWORD
+winpids::enum_init (bool winpid)
+{
+ if (wincap.is_winnt ())
+ enum_processes = &winpids::enumNT;
+ else
+ enum_processes = &winpids::enum9x;
+
+ return (this->*enum_processes) (winpid);
+}
+
+void
+winpids::release ()
+{
+ for (unsigned i = 0; i < npids; i++)
+ if (pinfolist[i] && (_pinfo *) pinfolist[i] != (_pinfo *) myself)
+ pinfolist[i].release ();
+}
+
+winpids::~winpids ()
+{
+ if (npidlist)
+ {
+ release ();
+ free (pidlist);
+ free (pinfolist);
+ }
+}