features.h: support POSIX.1-2024
[newlib-cygwin.git] / winsup / cygwin / pinfo.cc
blobe31a67d8f412b050dd2dad738e09f6f5e459dd38
1 /* pinfo.cc: process table support
3 This file is part of Cygwin.
5 This software is a copyrighted work licensed under the terms of the
6 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
7 details. */
9 #include "winsup.h"
10 #include "miscfuncs.h"
11 #include <stdlib.h>
12 #include "cygerrno.h"
13 #include "security.h"
14 #include "path.h"
15 #include "fhandler.h"
16 #include "dtable.h"
17 #include "sigproc.h"
18 #include "pinfo.h"
19 #include "perprocess.h"
20 #include "environ.h"
21 #include "ntdll.h"
22 #include "shared_info.h"
23 #include "cygheap.h"
24 #include "cygmalloc.h"
25 #include "cygtls.h"
26 #include "tls_pbuf.h"
27 #include "child_info.h"
28 #include "dll_init.h"
30 class pinfo_basic: public _pinfo
32 public:
33 pinfo_basic();
36 pinfo_basic::pinfo_basic ()
38 dwProcessId = GetCurrentProcessId ();
39 PWCHAR pend = wcpncpy (progname, global_progname,
40 sizeof (progname) / sizeof (WCHAR) - 1);
41 *pend = L'\0';
42 /* Default uid/gid are needed very early to initialize shared user info. */
43 uid = ILLEGAL_UID;
44 gid = ILLEGAL_GID;
47 pinfo_basic myself_initial NO_COPY;
49 pinfo NO_COPY myself (static_cast<_pinfo *> (&myself_initial)); // Avoid myself != NULL checks
51 /* Setup the pinfo structure for this process. There may already be a
52 _pinfo for this "pid" if h != NULL. */
54 void
55 pinfo::thisproc (HANDLE h)
57 procinfo = NULL;
58 bool execed = !!h;
60 DWORD flags = PID_IN_USE | PID_ACTIVE;
61 /* Forked process or process started from non-Cygwin parent needs a pid. */
62 if (!execed)
64 cygheap->pid = create_cygwin_pid ();
65 flags |= PID_NEW;
66 h = INVALID_HANDLE_VALUE;
68 /* spawnve'd process got pid in parent, cygheap->pid has been set in
69 child_info_spawn::handle_spawn. */
71 init (cygheap->pid, flags, h);
72 procinfo->process_state |= PID_IN_USE;
73 procinfo->dwProcessId = myself_initial.dwProcessId;
74 procinfo->sendsig = myself_initial.sendsig;
75 wcscpy (procinfo->progname, myself_initial.progname);
76 if (!execed)
77 create_winpid_symlink ();
78 procinfo->exec_sendsig = NULL;
79 procinfo->exec_dwProcessId = 0;
80 debug_printf ("myself dwProcessId %u", procinfo->dwProcessId);
83 /* Initialize the process table entry for the current task.
84 This is not called for forked tasks, only execed ones. */
85 void
86 pinfo_init (char **envp, int envc)
88 if (envp)
90 environ_init (envp, envc);
91 /* spawn has already set up a pid structure for us so we'll use that */
92 myself->process_state |= PID_CYGPARENT;
94 else
96 /* Invent our own pid. */
98 myself.thisproc (NULL);
99 myself->pgid = myself->sid = myself->pid;
100 myself->ctty = CTTY_UNINITIALIZED;
101 myself->uid = ILLEGAL_UID;
102 myself->gid = ILLEGAL_GID;
103 environ_init (NULL, 0); /* call after myself has been set up */
104 myself->nice = winprio_to_nice (GetPriorityClass (GetCurrentProcess ()));
105 myself->ppid = 1; /* always set last */
106 debug_printf ("Set nice to %d", myself->nice);
109 myself->process_state |= PID_ACTIVE;
110 myself->process_state &= ~(PID_INITIALIZING | PID_EXITED | PID_REAPED);
111 if (being_debugged ())
112 myself->process_state |= PID_DEBUGGED;
113 myself.preserve ();
114 debug_printf ("pid %d, pgid %d, process_state %y",
115 myself->pid, myself->pgid, myself->process_state);
118 DWORD
119 pinfo::status_exit (DWORD x)
121 switch ((NTSTATUS) x)
123 case STATUS_DLL_NOT_FOUND:
125 path_conv pc;
126 if (!procinfo)
127 pc.check ("/dev/null", PC_POSIX);
128 else
130 UNICODE_STRING uc;
131 RtlInitUnicodeString(&uc, procinfo->progname);
132 pc.check (&uc, PC_POSIX);
134 small_printf ("%s: error while loading shared libraries: %s: cannot "
135 "open shared object file: No such file or directory\n",
136 pc.get_posix (), find_first_notloaded_dll (pc));
137 x = 127 << 8;
139 break;
140 case STATUS_ILLEGAL_DLL_PSEUDO_RELOCATION: /* custom error value */
141 /* We've already printed the error message in pseudo-reloc.c */
142 x = 127 << 8;
143 break;
144 case STATUS_ACCESS_VIOLATION:
145 x = SIGSEGV;
146 break;
147 case STATUS_ILLEGAL_INSTRUCTION:
148 x = SIGILL;
149 break;
150 case STATUS_NO_MEMORY:
151 /* If the PATH environment variable is longer than about 30K and the full
152 Windows environment is > 32K, startup of an exec'ed process fails with
153 STATUS_NO_MEMORY. This happens with all Cygwin executables, as well
154 as, for instance, notepad, but it does not happen with CMD for some
155 reason (but note, the environment *in* CMD is broken and shortened).
156 This occurs at a point where there's no return to the exec'ing parent
157 process, so we have to find some way to inform the user what happened.
159 FIXME: For now, just return with SIGBUS set. Maybe it's better to add
160 a lengthy small_printf instead. */
161 x = SIGBUS;
162 break;
163 case STATUS_CONTROL_C_EXIT:
164 x = SIGINT;
165 break;
166 default:
167 debug_printf ("*** STATUS_%y\n", x);
168 x = 127 << 8;
170 return EXITCODE_SET | x;
173 # define self (*this)
174 void
175 pinfo::set_exit_code (DWORD x)
177 if (x >= 0xc0000000UL)
178 self->exitcode = status_exit (x);
179 else
180 self->exitcode = EXITCODE_SET | (sigExeced ?: (x & 0xff) << 8);
183 void
184 pinfo::maybe_set_exit_code_from_windows ()
186 DWORD x = 0xdeadbeef;
187 DWORD oexitcode = self->exitcode;
189 if (hProcess && !(self->exitcode & EXITCODE_SET))
191 WaitForSingleObject (hProcess, INFINITE); /* just to be safe, in case
192 process hasn't quite exited
193 after closing pipe */
194 GetExitCodeProcess (hProcess, &x);
195 set_exit_code (x);
197 sigproc_printf ("pid %d, exit value - old %y, windows %y, cygwin %y",
198 self->pid, oexitcode, x, self->exitcode);
201 void
202 pinfo::exit (DWORD n)
204 debug_only_printf ("winpid %d, exit %d", GetCurrentProcessId (), n);
205 proc_terminate ();
206 lock_process until_exit (true);
207 cygthread::terminate ();
209 if (n != EXITCODE_NOSET)
210 self->exitcode = EXITCODE_SET | n;/* We're really exiting. Record the UNIX exit code. */
211 else
212 maybe_set_exit_code_from_windows (); /* may block */
213 exit_state = ES_FINAL;
215 if (CTTY_IS_VALID (myself->ctty) && !iscons_dev (myself->ctty))
217 lock_ttys here;
218 tty *t = cygwin_shared->tty[device::minor(myself->ctty)];
219 if (!t->slave_alive ())
220 t->setpgid (0);
223 /* FIXME: There is a potential race between an execed process and its
224 parent here. I hated to add a mutex just for that, though. */
225 struct rusage r;
226 fill_rusage (&r, GetCurrentProcess ());
227 add_rusage (&self->rusage_self, &r);
228 int exitcode = self->exitcode & 0xffff;
229 if (!self->cygstarted)
230 exitcode = ((exitcode & 0xff) << 8) | ((exitcode >> 8) & 0xff);
231 sigproc_printf ("Calling dlls.cleanup_forkables n %y, exitcode %y", n, exitcode);
232 dlls.cleanup_forkables ();
233 sigproc_printf ("Calling ExitProcess n %y, exitcode %y", n, exitcode);
234 if (!TerminateProcess (GetCurrentProcess (), exitcode))
235 system_printf ("TerminateProcess failed, %E");
236 ExitProcess (exitcode);
238 # undef self
240 /* Return next free Cygwin PID between 2 and 65535, round-robin. Each new
241 PID is checked that it doesn't collide with an existing PID. For that,
242 just check if the "cygpid.PID" section exists. */
243 pid_t
244 create_cygwin_pid ()
246 pid_t pid = 0;
247 WCHAR sym_name[24];
248 UNICODE_STRING sym_str;
249 OBJECT_ATTRIBUTES attr;
250 HANDLE sym_hdl;
251 NTSTATUS status;
257 pid = ((uint32_t) InterlockedIncrement (&cygwin_shared->pid_src))
258 % MAX_PID;
260 while (pid < 2);
261 __small_swprintf (sym_name, L"cygpid.%u", pid);
262 RtlInitUnicodeString (&sym_str, sym_name);
263 InitializeObjectAttributes (&attr, &sym_str, OBJ_CASE_INSENSITIVE,
264 get_shared_parent_dir (), NULL);
265 /* We just want to know if the section (and thus the process) still
266 exists. Instead of actually opening the section, try to open
267 it as symlink. NtOpenSymbolicLinkObject will always returns an
268 error:
269 - STATUS_OBJECT_NAME_NOT_FOUND if the section doesn't exist,
270 so the slot is free and we can use this pid.
271 - STATUS_OBJECT_TYPE_MISMATCH if the section exists, so we have
272 to skip this pid and loop to try the next one.
273 As side-effect we never have to close the section handle and thus
274 we don't influence the lifetime of the section. */
275 status = NtOpenSymbolicLinkObject (&sym_hdl, SYMBOLIC_LINK_QUERY, &attr);
277 while (status == STATUS_OBJECT_TYPE_MISMATCH);
278 return pid;
281 /* Convert Windows WINPID into Cygwin PID. Utilize the "winpid.WINPID"
282 symlinks created for each process. The symlink contains the Cygwin
283 PID as target. Return 0 if no "winpid.WINPID" symlink exists for
284 this WINPID. */
285 pid_t
286 cygwin_pid (DWORD dwProcessId)
288 WCHAR sym_name[24];
289 WCHAR pid_name[12];
290 UNICODE_STRING sym_str;
291 UNICODE_STRING pid_str;
292 OBJECT_ATTRIBUTES attr;
293 HANDLE sym_hdl;
294 NTSTATUS status;
296 __small_swprintf (sym_name, L"winpid.%u", dwProcessId);
297 RtlInitUnicodeString (&sym_str, sym_name);
298 InitializeObjectAttributes (&attr, &sym_str, OBJ_CASE_INSENSITIVE,
299 get_shared_parent_dir (), NULL);
300 status = NtOpenSymbolicLinkObject (&sym_hdl, SYMBOLIC_LINK_QUERY, &attr);
301 if (!NT_SUCCESS (status))
302 return 0;
303 RtlInitEmptyUnicodeString (&pid_str, pid_name,
304 sizeof pid_name - sizeof (WCHAR));
305 status = NtQuerySymbolicLinkObject (sym_hdl, &pid_str, NULL);
306 NtClose (sym_hdl);
307 if (!NT_SUCCESS (status))
309 system_printf ("NtOpenSymbolicLinkObject: %y, PID %u, ret 0",
310 status, dwProcessId);
311 return 0;
313 pid_str.Buffer[pid_str.Length / sizeof (WCHAR)] = L'\0';
314 pid_t ret = (pid_t) wcstoul (pid_str.Buffer, NULL, 10);
315 return ret;
318 /* Create "winpid.WINPID" symlinks with the Cygwin PID of that process as
319 target. This is used to find the Cygwin PID for a given Windows WINPID. */
320 void
321 pinfo::create_winpid_symlink ()
323 WCHAR sym_name[24];
324 WCHAR pid_name[24];
325 UNICODE_STRING sym_str;
326 UNICODE_STRING pid_str;
327 OBJECT_ATTRIBUTES attr;
329 __small_swprintf (sym_name, L"winpid.%u", procinfo->dwProcessId);
330 RtlInitUnicodeString (&sym_str, sym_name);
331 __small_swprintf (pid_name, L"%u", procinfo->pid);
332 RtlInitUnicodeString (&pid_str, pid_name);
333 InitializeObjectAttributes (&attr, &sym_str, OBJ_CASE_INSENSITIVE,
334 get_shared_parent_dir (),
335 everyone_sd (SYMBOLIC_LINK_QUERY));
336 NtCreateSymbolicLinkObject (&winpid_hdl, SYMBOLIC_LINK_ALL_ACCESS,
337 &attr, &pid_str);
340 inline void
341 pinfo::_pinfo_release ()
343 if (procinfo)
345 void *unmap_procinfo = procinfo;
346 procinfo = NULL;
347 UnmapViewOfFile (unmap_procinfo);
349 HANDLE close_h;
350 if (h)
352 close_h = h;
353 h = NULL;
354 ForceCloseHandle1 (close_h, pinfo_shared_handle);
358 void
359 pinfo::init (pid_t n, DWORD flag, HANDLE h0)
361 shared_locations shloc;
362 h = NULL;
363 if (myself && n == myself->pid)
365 procinfo = myself;
366 destroy = 0;
367 return;
370 int createit = (flag & PID_IN_USE);
371 DWORD access = FILE_MAP_READ
372 | (flag & (PID_IN_USE | PID_MAP_RW) ? FILE_MAP_WRITE : 0);
373 if (!h0 || myself.h)
374 shloc = (flag & PID_IN_USE) ? SH_JUSTCREATE : SH_JUSTOPEN;
375 else
377 shloc = SH_MYSELF;
378 if (h0 == INVALID_HANDLE_VALUE)
379 h0 = NULL;
382 procinfo = NULL;
383 PSECURITY_ATTRIBUTES sa_buf = (PSECURITY_ATTRIBUTES) alloca (1024);
384 PSECURITY_ATTRIBUTES sec_attribs = sec_user_nih (sa_buf, cygheap->user.sid(),
385 well_known_world_sid,
386 FILE_MAP_READ);
388 for (int i = 0; i < 20; i++)
390 bool created;
392 procinfo = (_pinfo *) open_shared (L"cygpid", n, h0, sizeof (_pinfo),
393 shloc, created, sec_attribs, access);
394 if (!h0)
396 if (createit)
397 __seterrno ();
398 return;
401 if (!procinfo)
403 if (exit_state)
404 return;
406 if (GetLastError () == ERROR_INVALID_HANDLE)
407 api_fatal ("MapViewOfFileEx h0 %p, i %d failed, %E", h0, i);
409 debug_printf ("MapViewOfFileEx h0 %p, i %d failed, %E", h0, i);
410 yield ();
411 continue;
414 /* Just fetching info for ps or /proc, don't do anything rash. */
415 if (!created && !(flag & PID_NEW) && !procinfo->ppid
416 && (flag & PID_PROCINFO))
417 break;
419 if (!created && createit && (procinfo->process_state & PID_REAPED))
421 memset (procinfo, 0, sizeof (*procinfo));
422 created = true; /* Lie that we created this - just reuse old
423 shared memory */
426 if (procinfo->process_state & PID_REAPED)
428 set_errno (ESRCH);
429 break;
432 /* In certain pathological cases, it is possible for the shared memory
433 region to exist for a while after a process has exited. This should
434 only be a brief occurrence, so rather than introduce some kind of
435 locking mechanism, just loop. */
436 if (!created && createit
437 && (procinfo->process_state & (PID_EXITED | PID_REAPED)))
439 debug_printf ("looping because pid %d, procinfo->pid %d, "
440 "procinfo->dwProcessid %u has PID_EXITED|PID_REAPED set",
441 n, procinfo->pid, procinfo->dwProcessId);
442 goto loop;
445 if (flag & PID_NEW)
446 procinfo->start_time = time (NULL);
447 if (created)
448 procinfo->pid = n;
450 h = h0; /* Success! */
451 break;
453 loop:
454 _pinfo_release ();
455 if (h0)
456 yield ();
459 if (h)
461 destroy = 1;
462 ProtectHandle1 (h, pinfo_shared_handle);
464 else
466 h = h0;
467 _pinfo_release ();
469 if (shloc == SH_MYSELF)
470 cygheap->shared_regions.myself_shared_addr = procinfo;
473 void
474 pinfo::set_acl()
476 PACL acl_buf = (PACL) alloca (1024);
477 SECURITY_DESCRIPTOR sd;
478 NTSTATUS status;
480 sec_acl (acl_buf, true, true, cygheap->user.sid (),
481 well_known_world_sid, FILE_MAP_READ);
482 RtlCreateSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION);
483 status = RtlSetDaclSecurityDescriptor (&sd, TRUE, acl_buf, FALSE);
484 if (!NT_SUCCESS (status))
485 debug_printf ("RtlSetDaclSecurityDescriptor %y", status);
486 else if ((status = NtSetSecurityObject (h, DACL_SECURITY_INFORMATION, &sd)))
487 debug_printf ("NtSetSecurityObject %y", status);
490 void
491 pinfo_minimal::set_inheritance (bool inherit)
493 DWORD i_flag = inherit ? HANDLE_FLAG_INHERIT : 0;
495 SetHandleInformation (rd_proc_pipe, HANDLE_FLAG_INHERIT, i_flag);
496 SetHandleInformation (hProcess, HANDLE_FLAG_INHERIT, i_flag);
497 SetHandleInformation (h, HANDLE_FLAG_INHERIT, i_flag);
500 pinfo::pinfo (HANDLE parent, pinfo_minimal& from, pid_t pid):
501 pinfo_minimal (), destroy (false), procinfo (NULL), waiter_ready (false),
502 wait_thread (NULL)
504 /* cygheap_exec_info::record_children set the inheritance of the required
505 child handles so just copy them over... */
506 rd_proc_pipe = from.rd_proc_pipe;
507 hProcess = from.hProcess;
508 h = from.h;
509 /* ...and reset their inheritance. */
510 set_inheritance (false);
511 init (pid, PID_MAP_RW, h);
514 const char *
515 _pinfo::_ctty (char *buf)
517 if (!CTTY_IS_VALID (ctty))
518 stpcpy (buf, "no ctty");
519 else
521 device d;
522 d.parse (ctty);
523 __small_sprintf (buf, "ctty %s", d.name ());
525 return buf;
528 bool
529 _pinfo::set_ctty (fhandler_termios *fh, int flags)
531 tty_min& tc = *fh->tc ();
532 debug_printf ("old %s, ctty device number %y, tc.ntty device number %y flags & O_NOCTTY %y", __ctty (), ctty, tc.ntty, flags & O_NOCTTY);
533 if (fh && (!CTTY_IS_VALID (ctty) || ctty == tc.ntty) && !(flags & O_NOCTTY))
535 if (tc.getsid () && tc.getsid () != sid && ctty == CTTY_RELEASED)
536 ; /* Do nothing if another session is associated with the TTY. */
537 else
539 ctty = tc.ntty;
540 if (cygheap->ctty != fh->archetype)
542 debug_printf ("cygheap->ctty %p, archetype %p",
543 cygheap->ctty, fh->archetype);
544 if (!cygheap->ctty)
545 syscall_printf ("ctty was NULL");
546 else
548 syscall_printf ("ctty %p, usecount %d", cygheap->ctty,
549 cygheap->ctty->archetype_usecount (0));
550 cygheap->ctty->close ();
552 cygheap->ctty = (fhandler_termios *) fh->archetype;
553 if (cygheap->ctty)
555 fh->archetype_usecount (1);
556 /* guard ctty fh */
557 report_tty_counts (cygheap->ctty, "ctty", "");
562 lock_ttys here;
563 syscall_printf ("attaching %s sid %d, pid %d, pgid %d, tty->pgid %d, tty->sid %d",
564 __ctty (), sid, pid, pgid, tc.getpgid (), tc.getsid ());
565 if (!cygwin_finished_initializing && !myself->cygstarted
566 && pgid == pid && tc.getpgid () && tc.getsid ()
567 /* Even GDB starts app via CreateProcess which changes cygstarted.
568 This results in setting the wrong pgid here, so just skip this
569 under debugger. */
570 && !being_debugged ())
571 pgid = tc.getpgid ();
573 /* May actually need to do this:
575 if (sid == pid && !tc.getsid () || !procinfo (tc.getsid ())->exists)
577 but testing for process existence is expensive so we avoid it until
578 an obvious bug surfaces. */
579 if (sid == pid && !tc.getsid ())
580 tc.setsid (sid);
581 if (CTTY_IS_VALID (ctty))
582 sid = tc.getsid ();
583 /* See above */
584 if ((!tc.getpgid () || being_debugged ()) && pgid == pid)
585 tc.setpgid (pgid);
587 debug_printf ("cygheap->ctty now %p, archetype %p", cygheap->ctty, fh ? fh->archetype : NULL);
588 return CTTY_IS_VALID (ctty);
591 /* Test to determine if a process really exists and is processing signals.
593 bool
594 _pinfo::exists ()
596 return process_state && !(process_state & (PID_EXITED | PID_REAPED));
599 bool
600 _pinfo::alive ()
602 HANDLE h = OpenProcess (PROCESS_QUERY_LIMITED_INFORMATION, false,
603 dwProcessId);
604 if (h)
605 CloseHandle (h);
606 return !!h;
609 static commune_result
610 commune_process_siginfo ()
612 commune_result res = { 0 };
614 res.pnd = sig_send (myself, __SIGPENDINGALL, NULL);
615 res.blk = cygheap->compute_sigblkmask ();
616 for (int sig = 1; sig < NSIG; ++sig)
617 if (global_sigs[sig].sa_handler == SIG_IGN)
618 res.ign |= SIGTOMASK (sig);
619 return res;
622 DWORD
623 commune_process (void *arg)
625 siginfo_t& si = *((siginfo_t *) arg);
626 tmp_pathbuf tp;
627 char *path = tp.c_get ();
628 DWORD nr;
629 HANDLE& tothem = si._si_commune._si_write_handle;
630 HANDLE process_sync =
631 OpenSemaphore (SYNCHRONIZE, false, shared_name (path, "commune", si.si_pid));
632 if (process_sync) // FIXME: this test shouldn't be necessary
633 ProtectHandle (process_sync);
635 lock_process now;
636 if (si._si_commune._si_code & PICOM_EXTRASTR)
637 si._si_commune._si_str = (char *) (&si + 1);
639 switch (si._si_commune._si_code)
641 case PICOM_CMDLINE:
643 sigproc_printf ("processing PICOM_CMDLINE");
644 unsigned n = 0;
645 int argc = __argv0_orig ? 1 : __argc_safe;
646 const char *argv[argc + 1];
648 for (int i = 0; i < argc; i++)
650 argv[i] = __argv[i] ?: "";
651 n += strlen (argv[i]) + 1;
653 argv[argc] = NULL;
654 if (!WritePipeOverlapped (tothem, &n, sizeof n, &nr, 1000L))
655 sigproc_printf ("WritePipeOverlapped sizeof argv failed, %E");
656 else for (int i = 0; i < argc; i++)
657 if (!WritePipeOverlapped (tothem, __argv[i],
658 strlen (__argv[i]) + 1, &nr, 1000L))
660 sigproc_printf ("WritePipeOverlapped arg %d failed, %E", i);
661 break;
663 break;
665 case PICOM_CWD:
667 sigproc_printf ("processing PICOM_CWD");
668 unsigned int n = strlen (cygheap->cwd.get (path, 1, 1, NT_MAX_PATH)) + 1;
669 if (!WritePipeOverlapped (tothem, &n, sizeof n, &nr, 1000L))
670 sigproc_printf ("WritePipeOverlapped sizeof cwd failed, %E");
671 else if (!WritePipeOverlapped (tothem, path, n, &nr, 1000L))
672 sigproc_printf ("WritePipeOverlapped cwd failed, %E");
673 break;
675 case PICOM_ROOT:
677 sigproc_printf ("processing PICOM_ROOT");
678 unsigned n;
679 if (cygheap->root.exists ())
680 n = strlen (strcpy (path, cygheap->root.posix_path ())) + 1;
681 else
682 n = strlen (strcpy (path, "/")) + 1;
683 if (!WritePipeOverlapped (tothem, &n, sizeof n, &nr, 1000L))
684 sigproc_printf ("WritePipeOverlapped sizeof root failed, %E");
685 else if (!WritePipeOverlapped (tothem, path, n, &nr, 1000L))
686 sigproc_printf ("WritePipeOverlapped root failed, %E");
687 break;
689 case PICOM_SIGINFO:
691 sigproc_printf ("processing PICOM_SIGINFO");
692 commune_result cr = commune_process_siginfo ();
693 if (!WritePipeOverlapped (tothem, &cr, sizeof cr, &nr, 1000L))
694 sigproc_printf ("WritePipeOverlapped siginfo failed, %E");
695 break;
697 case PICOM_FDS:
699 sigproc_printf ("processing PICOM_FDS");
700 unsigned int n = 0;
701 int fd;
702 cygheap_fdenum cfd;
703 while ((fd = cfd.next ()) >= 0)
704 n += sizeof (int);
705 cfd.rewind ();
706 if (!WritePipeOverlapped (tothem, &n, sizeof n, &nr, 1000L))
707 sigproc_printf ("WritePipeOverlapped sizeof fds failed, %E");
708 else
709 while ((fd = cfd.next ()) >= 0)
710 if (!WritePipeOverlapped (tothem, &fd, sizeof fd, &nr, 1000L))
712 sigproc_printf ("WritePipeOverlapped fd %d failed, %E", fd);
713 break;
715 break;
717 case PICOM_PIPE_FHANDLER:
719 sigproc_printf ("processing PICOM_PIPE_FHANDLER");
720 int64_t unique_id = si._si_commune._si_pipe_unique_id;
721 unsigned int n = 0;
722 cygheap_fdenum cfd;
723 while (cfd.next () >= 0)
724 if (cfd->get_unique_id () == unique_id)
726 fhandler_pipe *fh = cfd;
727 n = sizeof *fh;
728 if (!WritePipeOverlapped (tothem, &n, sizeof n, &nr, 1000L))
729 sigproc_printf ("WritePipeOverlapped sizeof hdl failed, %E");
730 else if (!WritePipeOverlapped (tothem, fh, n, &nr, 1000L))
731 sigproc_printf ("WritePipeOverlapped hdl failed, %E");
732 break;
734 if (!n && !WritePipeOverlapped (tothem, &n, sizeof n, &nr, 1000L))
735 sigproc_printf ("WritePipeOverlapped sizeof hdl failed, %E");
736 break;
738 case PICOM_FILE_PATHCONV:
740 sigproc_printf ("processing PICOM_FILE_PATHCONV");
741 int fd = si._si_commune._si_fd;
742 uint32_t flags = si._si_commune._si_flags;
743 unsigned int n = 0;
744 cygheap_fdget cfd (fd);
745 if (cfd >= 0
746 && (!(flags & FFH_LINKAT)
747 || (cfd->get_flags () & (O_TMPFILE | O_EXCL))
748 != (O_TMPFILE | O_EXCL)))
750 fhandler_base *fh = cfd;
751 void *ser_buf = fh->pc.serialize (fh->get_handle (), n);
752 if (!WritePipeOverlapped (tothem, &n, sizeof n, &nr, 1000L))
753 sigproc_printf ("WritePipeOverlapped sizeof hdl failed, %E");
754 else if (!WritePipeOverlapped (tothem, ser_buf, n, &nr, 1000L))
755 sigproc_printf ("WritePipeOverlapped hdl failed, %E");
756 cfree (ser_buf);
758 else if (!WritePipeOverlapped (tothem, &n, sizeof n, &nr, 1000L))
759 sigproc_printf ("WritePipeOverlapped sizeof hdl failed, %E");
760 break;
762 case PICOM_FD:
764 sigproc_printf ("processing PICOM_FD");
765 int fd = si._si_commune._si_fd;
766 unsigned int n = 0;
767 cygheap_fdget cfd (fd);
768 if (cfd < 0)
769 n = strlen (strcpy (path, "")) + 1;
770 else
771 n = strlen (cfd->get_proc_fd_name (path)) + 1;
772 if (!WritePipeOverlapped (tothem, &n, sizeof n, &nr, 1000L))
773 sigproc_printf ("WritePipeOverlapped sizeof fd failed, %E");
774 else if (!WritePipeOverlapped (tothem, path, n, &nr, 1000L))
775 sigproc_printf ("WritePipeOverlapped fd failed, %E");
776 break;
778 case PICOM_ENVIRON:
780 sigproc_printf ("processing PICOM_ENVIRON");
781 unsigned n = 0;
782 char **env = environ;
783 if (env)
784 for (char **e = env; *e; e++)
785 n += strlen (*e) + 1;
786 if (!WritePipeOverlapped (tothem, &n, sizeof n, &nr, 1000L))
787 sigproc_printf ("WritePipeOverlapped sizeof argv failed, %E");
788 else if (env)
789 for (char **e = env; *e; e++)
790 if (!WritePipeOverlapped (tothem, *e, strlen (*e) + 1, &nr, 1000L))
792 sigproc_printf ("WritePipeOverlapped arg %d failed, %E",
793 e - env);
794 break;
796 break;
799 if (process_sync)
801 DWORD res = WaitForSingleObject (process_sync, 5000);
802 if (res != WAIT_OBJECT_0)
803 sigproc_printf ("WFSO failed - %u, %E", res);
804 else
805 sigproc_printf ("synchronized with pid %d", si.si_pid);
806 ForceCloseHandle (process_sync);
808 CloseHandle (tothem);
809 _my_tls._ctinfo->auto_release ();
810 return 0;
813 commune_result
814 _pinfo::commune_request (__uint32_t code, ...)
816 DWORD nr;
817 commune_result res = { 0 };
818 va_list args;
819 siginfo_t si = {0};
820 HANDLE& hp = si._si_commune._si_process_handle;
821 HANDLE& fromthem = si._si_commune._si_read_handle;
822 HANDLE request_sync = NULL;
824 if (!pid)
826 set_errno (ESRCH);
827 goto err;
829 if (ISSTATE (this, PID_NOTCYGWIN))
831 set_errno (ENOTSUP);
832 goto err;
835 va_start (args, code);
836 si._si_commune._si_code = code;
837 switch (code)
839 case PICOM_PIPE_FHANDLER:
840 si._si_commune._si_pipe_unique_id = va_arg (args, int64_t);
841 break;
843 case PICOM_FD:
844 case PICOM_FILE_PATHCONV:
845 si._si_commune._si_fd = va_arg (args, int);
846 si._si_commune._si_flags = va_arg (args, uint32_t);
847 break;
849 break;
851 va_end (args);
853 char name_buf[MAX_PATH];
854 request_sync = CreateSemaphore (&sec_none_nih, 0, INT32_MAX,
855 shared_name (name_buf, "commune", myself->pid));
856 if (!request_sync)
857 goto err;
858 ProtectHandle (request_sync);
860 si.si_signo = __SIGCOMMUNE;
861 if (sig_send (this, si))
863 ForceCloseHandle (request_sync); /* don't signal semaphore since there was apparently no receiving process */
864 request_sync = NULL;
865 goto err;
868 DWORD n;
869 switch (code)
871 case PICOM_CMDLINE:
872 case PICOM_CWD:
873 case PICOM_ENVIRON:
874 case PICOM_ROOT:
875 case PICOM_FDS:
876 case PICOM_FD:
877 case PICOM_PIPE_FHANDLER:
878 case PICOM_FILE_PATHCONV:
879 if (!ReadPipeOverlapped (fromthem, &n, sizeof n, &nr, 1000L)
880 || nr != sizeof n)
882 __seterrno ();
883 goto err;
885 if (!n)
886 res.s = NULL;
887 else
889 res.s = (char *) cmalloc_abort (HEAP_COMMUNE, n);
890 char *p;
891 for (p = res.s;
892 n && ReadPipeOverlapped (fromthem, p, n, &nr, 1000L);
893 p += nr, n -= nr)
894 continue;
895 if (n)
897 __seterrno ();
898 goto err;
900 res.n = p - res.s;
902 break;
903 case PICOM_SIGINFO:
904 if (!ReadPipeOverlapped (fromthem, &res, sizeof res, &nr, 1000L)
905 || nr != sizeof res)
907 __seterrno ();
908 goto err;
910 break;
912 goto out;
914 err:
915 memset (&res, 0, sizeof (res));
917 out:
918 if (request_sync)
920 LONG res;
921 ReleaseSemaphore (request_sync, 1, &res);
922 ForceCloseHandle (request_sync);
924 if (hp)
925 CloseHandle (hp);
926 if (fromthem)
927 CloseHandle (fromthem);
928 return res;
931 fhandler_pipe *
932 _pinfo::pipe_fhandler (int64_t unique_id, size_t &n)
934 if (!pid)
935 return NULL;
936 if (pid == myself->pid)
937 return NULL;
938 commune_result cr = commune_request (PICOM_PIPE_FHANDLER, unique_id);
939 n = cr.n;
940 return (fhandler_pipe *) cr.s;
943 void *
944 _pinfo::file_pathconv (int fd, uint32_t flags, size_t &n)
946 if (!pid)
947 return NULL;
948 if (pid == myself->pid)
949 return NULL;
950 commune_result cr = commune_request (PICOM_FILE_PATHCONV, fd, flags);
951 n = cr.n;
952 return (void *) cr.s;
955 char *
956 _pinfo::fd (int fd, size_t &n)
958 char *s;
959 if (!pid)
960 return NULL;
961 if (pid != myself->pid)
963 commune_result cr = commune_request (PICOM_FD, fd);
964 s = cr.s;
965 n = cr.n;
967 else
969 cygheap_fdget cfd (fd);
970 if (cfd < 0)
971 s = cstrdup ("");
972 else
973 s = cfd->get_proc_fd_name ((char *) cmalloc_abort (HEAP_COMMUNE, NT_MAX_PATH));
974 n = strlen (s) + 1;
976 return s;
979 char *
980 _pinfo::fds (size_t &n)
982 char *s;
983 if (!pid)
984 return NULL;
985 if (pid != myself->pid)
987 commune_result cr = commune_request (PICOM_FDS);
988 s = cr.s;
989 n = cr.n;
991 else
993 n = 0;
994 int fd;
995 cygheap_fdenum cfd (true);
996 while ((fd = cfd.next ()) >= 0)
997 n += sizeof (int);
998 cfd.rewind ();
999 s = (char *) cmalloc_abort (HEAP_COMMUNE, n);
1000 int *p = (int *) s;
1001 while ((fd = cfd.next ()) >= 0 && (char *) p - s < (int) n)
1002 *p++ = fd;
1004 return s;
1007 char *
1008 _pinfo::root (size_t& n)
1010 char *s;
1011 if (!pid)
1012 return NULL;
1013 if (pid != myself->pid && !ISSTATE (this, PID_NOTCYGWIN))
1015 commune_result cr = commune_request (PICOM_ROOT);
1016 s = cr.s;
1017 n = cr.n;
1019 else
1021 if (cygheap->root.exists ())
1022 s = cstrdup (cygheap->root.posix_path ());
1023 else
1024 s = cstrdup ("/");
1025 n = strlen (s) + 1;
1027 return s;
1031 _pinfo::siginfo (sigset_t &pnd, sigset_t &blk, sigset_t &ign)
1033 commune_result cr;
1035 if (!pid)
1036 return -1;
1037 if (pid != myself->pid && !ISSTATE (this, PID_NOTCYGWIN))
1038 cr = commune_request (PICOM_SIGINFO);
1039 else
1040 cr = commune_process_siginfo ();
1041 pnd = cr.pnd;
1042 blk = cr.blk;
1043 ign = cr.ign;
1044 return -1;
1047 static HANDLE
1048 open_commune_proc_parms (DWORD pid, PRTL_USER_PROCESS_PARAMETERS prupp)
1050 HANDLE proc;
1051 NTSTATUS status;
1052 PROCESS_BASIC_INFORMATION pbi;
1053 PEB lpeb;
1055 proc = OpenProcess (PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ,
1056 FALSE, pid);
1057 if (!proc)
1058 return NULL;
1059 status = NtQueryInformationProcess (proc, ProcessBasicInformation,
1060 &pbi, sizeof pbi, NULL);
1061 if (NT_SUCCESS (status)
1062 && ReadProcessMemory (proc, pbi.PebBaseAddress, &lpeb, sizeof lpeb, NULL)
1063 && ReadProcessMemory (proc, lpeb.ProcessParameters, prupp, sizeof *prupp,
1064 NULL))
1065 return proc;
1066 NtClose (proc);
1067 return NULL;
1070 char *
1071 _pinfo::cwd (size_t& n)
1073 char *s = NULL;
1074 if (!pid)
1075 return NULL;
1076 if (ISSTATE (this, PID_NOTCYGWIN))
1078 RTL_USER_PROCESS_PARAMETERS rupp;
1079 HANDLE proc = open_commune_proc_parms (dwProcessId, &rupp);
1081 n = 0;
1082 if (!proc)
1083 return NULL;
1085 tmp_pathbuf tp;
1086 PWCHAR cwd = tp.w_get ();
1088 if (ReadProcessMemory (proc, rupp.CurrentDirectoryName.Buffer,
1089 cwd, rupp.CurrentDirectoryName.Length,
1090 NULL))
1092 /* Drop trailing backslash, add trailing \0 in passing. */
1093 cwd[rupp.CurrentDirectoryName.Length / sizeof (WCHAR) - 1]
1094 = L'\0';
1095 s = (char *) cmalloc_abort (HEAP_COMMUNE, NT_MAX_PATH);
1096 mount_table->conv_to_posix_path (cwd, s, 0);
1097 n = strlen (s) + 1;
1099 NtClose (proc);
1101 else if (pid != myself->pid)
1103 commune_result cr = commune_request (PICOM_CWD);
1104 s = cr.s;
1105 n = cr.n;
1107 else
1109 s = (char *) cmalloc_abort (HEAP_COMMUNE, NT_MAX_PATH);
1110 cygheap->cwd.get (s, 1, 1, NT_MAX_PATH);
1111 n = strlen (s) + 1;
1113 return s;
1116 char *
1117 _pinfo::cmdline (size_t& n)
1119 char *s = NULL;
1120 if (!pid)
1121 return NULL;
1122 if (ISSTATE (this, PID_NOTCYGWIN))
1124 RTL_USER_PROCESS_PARAMETERS rupp;
1125 HANDLE proc = open_commune_proc_parms (dwProcessId, &rupp);
1127 n = 0;
1128 if (!proc)
1129 return NULL;
1131 tmp_pathbuf tp;
1132 PWCHAR cmdline = tp.w_get ();
1134 if (ReadProcessMemory (proc, rupp.CommandLine.Buffer, cmdline,
1135 rupp.CommandLine.Length, NULL))
1137 /* Add trailing \0. */
1138 cmdline[rupp.CommandLine.Length / sizeof (WCHAR)]
1139 = L'\0';
1140 n = sys_wcstombs_alloc (&s, HEAP_COMMUNE, cmdline,
1141 rupp.CommandLine.Length
1142 / sizeof (WCHAR));
1143 /* Quotes & Spaces post-processing. */
1144 bool in_quote = false;
1145 for (char *c = s; *c; ++c)
1146 if (*c == '"')
1147 in_quote = !in_quote;
1148 else if (*c == ' ' && !in_quote)
1149 *c = '\0';
1151 NtClose (proc);
1153 else if (pid != myself->pid)
1155 commune_result cr = commune_request (PICOM_CMDLINE);
1156 s = cr.s;
1157 n = cr.n;
1159 else
1161 n = 0;
1162 int argc = __argv0_orig ? 1 : __argc_safe;
1164 for (int i = 0; i < argc; ++i)
1165 n += strlen (__argv[i]) + 1;
1166 char *p;
1167 p = s = (char *) cmalloc_abort (HEAP_COMMUNE, n);
1168 for (int i = 0; i < argc; ++i)
1169 p = stpcpy (p, __argv[i]) + 1;
1171 return s;
1175 char *
1176 _pinfo::environ (size_t& n)
1178 char **env = NULL;
1179 if (!pid)
1180 return NULL;
1181 if (ISSTATE (this, PID_NOTCYGWIN))
1183 RTL_USER_PROCESS_PARAMETERS rupp;
1184 HANDLE proc = open_commune_proc_parms (dwProcessId, &rupp);
1186 if (!proc)
1187 return NULL;
1189 MEMORY_BASIC_INFORMATION mbi;
1190 SIZE_T envsize;
1191 PWCHAR envblock;
1192 if (!VirtualQueryEx (proc, rupp.Environment, &mbi, sizeof(mbi)))
1194 NtClose (proc);
1195 return NULL;
1198 SIZE_T read;
1199 envsize = (ptrdiff_t) mbi.RegionSize
1200 - ((ptrdiff_t) rupp.Environment - (ptrdiff_t) mbi.BaseAddress);
1201 envblock = (PWCHAR) cmalloc_abort (HEAP_COMMUNE, envsize);
1203 if (ReadProcessMemory (proc, rupp.Environment, envblock, envsize, &read))
1204 env = win32env_to_cygenv (envblock, false);
1206 NtClose (proc);
1208 else if (pid != myself->pid)
1210 commune_result cr = commune_request (PICOM_ENVIRON);
1211 n = cr.n;
1212 return cr.s;
1214 else
1215 env = ::environ;
1217 if (env == NULL)
1218 return NULL;
1220 n = 0;
1221 for (char **e = env; *e; e++)
1222 n += strlen (*e) + 1;
1224 char *p, *s;
1225 p = s = (char *) cmalloc_abort (HEAP_COMMUNE, n);
1226 for (char **e = env; *e; e++)
1227 p = stpcpy (p, *e) + 1;
1228 return s;
1231 /* This is the workhorse which waits for the write end of the pipe
1232 created during new process creation. If the pipe is closed or a zero
1233 is received on the pipe, it is assumed that the cygwin pid has exited.
1234 Otherwise, various "signals" can be sent to the parent to inform the
1235 parent to perform a certain action. */
1236 static DWORD
1237 proc_waiter (void *arg)
1239 pinfo vchild = *(pinfo *) arg;
1240 ((pinfo *) arg)->waiter_ready = true;
1242 siginfo_t si = {0};
1243 si.si_signo = SIGCHLD;
1244 si.si_code = CLD_EXITED;
1245 si.si_pid = vchild->pid;
1246 #if 0 // FIXME: This is tricky to get right
1247 si.si_utime = pchildren[rc]->rusage_self.ru_utime;
1248 si.si_stime = pchildren[rc].rusage_self.ru_stime;
1249 #endif
1250 pid_t pid = vchild->pid;
1251 bool its_me = vchild == myself;
1253 for (;;)
1255 DWORD nb;
1256 char buf = '\0';
1258 if (!ReadFile (vchild.rd_proc_pipe, &buf, 1, &nb, NULL)
1259 && GetLastError () != ERROR_BROKEN_PIPE)
1261 system_printf ("error on read of child wait pipe %p, %E", vchild.rd_proc_pipe);
1262 break;
1265 if (!its_me && have_execed_cygwin)
1266 break;
1268 si.si_uid = vchild->uid;
1270 switch (buf)
1272 case __ALERT_ALIVE:
1273 continue;
1274 case 0:
1275 /* Child exited. Do some cleanup and signal myself. */
1276 vchild.maybe_set_exit_code_from_windows ();
1277 if (WIFEXITED (vchild->exitcode))
1278 si.si_code = CLD_EXITED;
1279 else if (WCOREDUMP (vchild->exitcode))
1280 si.si_code = CLD_DUMPED;
1281 else
1282 si.si_code = CLD_KILLED;
1283 si.si_status = vchild->exitcode;
1284 vchild->process_state = PID_EXITED;
1285 /* This should always be last. Do not use vchild-> beyond this point */
1286 break;
1287 case SIGTTIN:
1288 case SIGTTOU:
1289 case SIGTSTP:
1290 case SIGSTOP:
1291 if (ISSTATE (myself, PID_NOCLDSTOP)) // FIXME: No need for this flag to be in _pinfo any longer
1292 continue;
1293 /* Child stopped. Signal myself. */
1294 si.si_code = CLD_STOPPED;
1295 break;
1296 case SIGCONT:
1297 continue;
1298 default:
1299 system_printf ("unknown value %d on proc pipe", buf);
1300 continue;
1303 if (its_me && ch_spawn.signal_myself_exited ())
1304 break;
1306 /* Send a SIGCHLD to myself. We do this here, rather than in proc_subproc
1307 to avoid the proc_subproc lock since the signal thread will eventually
1308 be calling proc_subproc and could unnecessarily block. */
1309 sig_send (myself_nowait, si);
1311 /* If we're just stopped or got a continue signal, keep looping.
1312 Otherwise, return this thread to the pool. */
1313 if (buf != '\0')
1314 sigproc_printf ("looping");
1315 else
1316 break;
1319 sigproc_printf ("exiting wait thread for pid %d", pid);
1320 vchild.wait_thread = NULL;
1321 _my_tls._ctinfo->auto_release (); /* automatically return the cygthread to the cygthread pool */
1322 return 0;
1325 /* function to set up the process pipe and kick off proc_waiter */
1326 bool
1327 pinfo::wait ()
1329 preserve (); /* Preserve the shared memory associated with the pinfo */
1331 waiter_ready = false;
1332 /* Fire up a new thread to track the subprocess */
1333 cygthread *h = new cygthread (proc_waiter, this, "waitproc");
1334 if (!h)
1335 sigproc_printf ("tracking thread creation failed for pid %d", (*this)->pid);
1336 else
1338 wait_thread = h;
1339 sigproc_printf ("created tracking thread for pid %d, winpid %y, rd_proc_pipe %p",
1340 (*this)->pid, (*this)->dwProcessId, rd_proc_pipe);
1343 return true;
1346 /* function to send a "signal" to the parent when something interesting happens
1347 in the child. */
1348 bool
1349 _pinfo::alert_parent (char sig)
1351 DWORD nb = 0;
1353 /* Send something to our parent. If the parent has gone away, close the pipe.
1354 Don't send if this is an exec stub.
1356 FIXME: Is there a race here if we run this while another thread is attempting
1357 to exec()? */
1358 if (my_wr_proc_pipe)
1360 if (WriteFile (my_wr_proc_pipe, &sig, 1, &nb, NULL))
1361 /* all is well */;
1362 else if (GetLastError () != ERROR_BROKEN_PIPE)
1363 debug_printf ("sending %d notification to parent failed, %E", sig);
1364 else
1366 ppid = 1;
1367 HANDLE closeit = my_wr_proc_pipe;
1368 my_wr_proc_pipe = NULL;
1369 ForceCloseHandle1 (closeit, wr_proc_pipe);
1372 return (bool) nb;
1375 void
1376 pinfo::release ()
1378 _pinfo_release ();
1379 if (winpid_hdl)
1380 NtClose (winpid_hdl);
1381 HANDLE close_h;
1382 if (rd_proc_pipe)
1384 close_h = rd_proc_pipe;
1385 rd_proc_pipe = NULL;
1386 ForceCloseHandle1 (close_h, rd_proc_pipe);
1388 if (hProcess)
1390 close_h = hProcess;
1391 hProcess = NULL;
1392 ForceCloseHandle1 (close_h, childhProc);
1396 /* DOCTOOL-START
1398 <sect1 id="func-cygwin-winpid-to-pid">
1399 <title>cygwin_winpid_to_pid</title>
1401 <funcsynopsis><funcprototype>
1402 <funcdef>extern "C" pid_t
1403 <function>cygwin_winpid_to_pid</function>
1404 </funcdef>
1405 <paramdef>int <parameter>winpid</parameter></paramdef>
1406 </funcprototype></funcsynopsis>
1408 <para>Given a windows pid, converts to the corresponding Cygwin
1409 pid, if any. Returns -1 if windows pid does not correspond to
1410 a cygwin pid.</para>
1411 <example>
1412 <title>Example use of cygwin_winpid_to_pid</title>
1413 <programlisting>
1414 extern "C" cygwin_winpid_to_pid (int winpid);
1415 pid_t mypid;
1416 mypid = cygwin_winpid_to_pid (windows_pid);
1417 </programlisting>
1418 </example>
1419 </sect1>
1421 DOCTOOL-END */
1423 extern "C" pid_t
1424 cygwin_winpid_to_pid (int winpid)
1426 pinfo p (cygwin_pid (winpid));
1427 if (p)
1428 return p->pid;
1430 set_errno (ESRCH);
1431 return (pid_t) -1;
1435 #define slop_pidlist 200
1436 #define size_pidlist(i) (sizeof (pidlist[0]) * ((i) + 1))
1437 #define size_pinfolist(i) (sizeof (pinfolist[0]) * ((i) + 1))
1438 class _onreturn
1440 HANDLE h;
1441 public:
1442 ~_onreturn ()
1444 if (h)
1446 CloseHandle (h);
1449 void no_close_handle (pinfo& p)
1451 p.hProcess = h;
1452 h = NULL;
1454 _onreturn (): h (NULL) {}
1455 void operator = (HANDLE h0) {h = h0;}
1456 operator HANDLE () const {return h;}
1459 inline void
1460 winpids::add (DWORD& nelem, bool winpid, DWORD pid)
1462 pid_t cygpid = cygwin_pid (pid);
1464 if (nelem >= npidlist)
1466 npidlist += slop_pidlist;
1467 pidlist = (DWORD *) realloc (pidlist, size_pidlist (npidlist + 1));
1468 pinfolist = (pinfo *) realloc ((void *) pinfolist, size_pinfolist (npidlist + 1));
1471 _onreturn onreturn;
1472 pinfo& p = pinfolist[nelem];
1473 memset ((void *) &p, 0, sizeof (p));
1475 bool perform_copy;
1476 if (cygpid == myself->pid)
1478 p = myself;
1479 perform_copy = false;
1481 else
1483 /* Open a process to prevent a subsequent exit from invalidating the
1484 shared memory region. */
1485 onreturn = OpenProcess (PROCESS_QUERY_LIMITED_INFORMATION, false, pid);
1487 /* If we couldn't open the process then we don't have rights to it
1488 and should make a copy of the shared memory area when it exists
1489 (it may not). */
1490 perform_copy = onreturn ? make_copy : true;
1492 p.init (cygpid, PID_PROCINFO | pinfo_access, NULL);
1494 /* Did we catch the process during exec? Try to fix. */
1495 if (p && p->dwProcessId != pid)
1496 pid = p->dwProcessId;
1498 /* If we're just looking for winpids then don't do any special cygwin "stuff* */
1499 if (winpid)
1501 perform_copy = true;
1502 goto out;
1505 /* !p means that we couldn't find shared memory for this pid. Probably means
1506 that it isn't a cygwin process. */
1507 if (!p)
1509 if (!pinfo_access || !cygpid)
1510 return;
1511 p.init (cygpid, PID_PROCINFO, NULL);
1512 if (!p)
1513 return;
1516 out:
1517 /* Scan list of previously recorded pids to make sure that this pid hasn't
1518 shown up before. This can happen when a process execs. */
1519 for (unsigned i = 0; i < nelem; i++)
1520 if (pidlist[i] == pid)
1522 if (p && (_pinfo *) p != (_pinfo *) myself)
1523 p.release ();
1524 return;
1526 /* If p is "false" then, eventually any opened process handle will be closed
1527 and the function will exit without adding anything to the pid list.
1529 If p is "true" then we've discovered a cygwin process.
1531 Handle "myself" differently. Don't copy it and close/zero the handle we
1532 just opened to it. If not performing a copy, then keep the process handle
1533 open for the duration of the life of the procinfo region to potential
1534 races when a new process uses this pid. Otherwise, malloc some memory
1535 for a copy of the shared memory.
1537 If malloc failed, then "oh well". Just keep the shared memory around
1538 and eventually close the handle when the winpids goes out of scope.
1540 If malloc succeeds, copy the procinfo we just grabbed into the new region,
1541 release the shared memory and allow the handle to be closed when this
1542 function returns. */
1543 if (p)
1545 if (p == (_pinfo *) myself)
1546 /* handle specially. Close the handle but (eventually) don't
1547 deallocate procinfo in release call */;
1548 else if (!perform_copy)
1549 onreturn.no_close_handle (p); /* Don't close the handle until release */
1550 else
1552 _pinfo *pnew = (_pinfo *) malloc (sizeof (*p.procinfo));
1553 if (!pnew)
1554 onreturn.no_close_handle (p);
1555 else
1557 *pnew = *p.procinfo;
1558 p.release ();
1559 p.procinfo = pnew;
1560 p.destroy = false;
1561 if (winpid)
1562 p->dwProcessId = pid;
1566 /* Add pid to the list and bump the number of elements. */
1567 if (p || winpid)
1568 pidlist[nelem++] = pid;
1571 DWORD
1572 winpids::enum_processes (bool winpid)
1574 DWORD nelem = 0;
1576 if (!winpid)
1578 tmp_pathbuf tp;
1579 NTSTATUS status;
1580 HANDLE dir = get_shared_parent_dir ();
1581 BOOLEAN restart = TRUE;
1582 bool last_run = false;
1583 ULONG context = 0;
1584 PDIRECTORY_BASIC_INFORMATION dbi_buf = (PDIRECTORY_BASIC_INFORMATION)
1585 tp.w_get ();
1586 while (!last_run)
1588 status = NtQueryDirectoryObject (dir, dbi_buf, 65536, FALSE, restart,
1589 &context, NULL);
1590 if (!NT_SUCCESS (status))
1592 debug_printf ("NtQueryDirectoryObject, status %y", status);
1593 break;
1595 if (status != STATUS_MORE_ENTRIES)
1596 last_run = true;
1597 restart = FALSE;
1598 for (PDIRECTORY_BASIC_INFORMATION dbi = dbi_buf;
1599 dbi->ObjectName.Length > 0;
1600 dbi++)
1602 if (wcsncmp (dbi->ObjectName.Buffer, L"winpid.", 7) == 0)
1604 DWORD pid = wcstoul (dbi->ObjectName.Buffer + 7, NULL, 10);
1605 add (nelem, false, pid);
1610 else
1612 static DWORD szprocs;
1613 static PSYSTEM_PROCESS_INFORMATION procs;
1615 while (1)
1617 PSYSTEM_PROCESS_INFORMATION new_p = (PSYSTEM_PROCESS_INFORMATION)
1618 realloc (procs, szprocs += 200 * sizeof (*procs));
1619 if (!new_p)
1621 system_printf ("out of memory reading system process "
1622 "information");
1623 return 0;
1625 procs = new_p;
1626 NTSTATUS status = NtQuerySystemInformation (SystemProcessInformation,
1627 procs, szprocs, NULL);
1628 if (NT_SUCCESS (status))
1629 break;
1631 if (status != STATUS_INFO_LENGTH_MISMATCH)
1633 system_printf ("error %y reading system process information",
1634 status);
1635 return 0;
1639 PSYSTEM_PROCESS_INFORMATION px = procs;
1640 while (1)
1642 if (px->UniqueProcessId)
1643 add (nelem, true, (DWORD) (uintptr_t) px->UniqueProcessId);
1644 if (!px->NextEntryOffset)
1645 break;
1646 px = (PSYSTEM_PROCESS_INFORMATION) ((char *) px + px->NextEntryOffset);
1649 return nelem;
1652 void
1653 winpids::set (bool winpid)
1655 npids = enum_processes (winpid);
1656 if (pidlist)
1657 pidlist[npids] = 0;
1660 DWORD
1661 winpids::enum_init (bool winpid)
1663 return enum_processes (winpid);
1666 void
1667 winpids::release ()
1669 _pinfo *p;
1670 for (unsigned i = 0; i < npids; i++)
1671 if (pinfolist[i] == (_pinfo *) myself)
1672 continue;
1673 else if (pinfolist[i].hProcess)
1674 pinfolist[i].release ();
1675 else if ((p = pinfolist[i]))
1677 pinfolist[i].procinfo = NULL;
1678 free (p);
1682 winpids::~winpids ()
1684 if (npidlist)
1686 release ();
1687 free (pidlist);
1688 free (pinfolist);