diff options
Diffstat (limited to 'winsup/cygwin/pinfo.cc')
-rw-r--r-- | winsup/cygwin/pinfo.cc | 212 |
1 files changed, 128 insertions, 84 deletions
diff --git a/winsup/cygwin/pinfo.cc b/winsup/cygwin/pinfo.cc index 90dfd2b7c..98168c76a 100644 --- a/winsup/cygwin/pinfo.cc +++ b/winsup/cygwin/pinfo.cc @@ -58,8 +58,7 @@ pinfo::thisproc (HANDLE h) DWORD flags = PID_IN_USE | PID_ACTIVE; if (!h) { - h = INVALID_HANDLE_VALUE; - cygheap->pid = cygwin_pid (myself_initial.pid); + cygheap->pid = create_cygwin_pid (); flags |= PID_NEW; } @@ -68,16 +67,10 @@ pinfo::thisproc (HANDLE h) procinfo->dwProcessId = myself_initial.pid; procinfo->sendsig = myself_initial.sendsig; wcscpy (procinfo->progname, myself_initial.progname); + create_winpid_symlink (procinfo->pid, procinfo->dwProcessId); + procinfo->exec_sendsig = NULL; + procinfo->exec_dwProcessId = 0; debug_printf ("myself dwProcessId %u", procinfo->dwProcessId); - if (h != INVALID_HANDLE_VALUE) - { - /* here if execed */ - static pinfo NO_COPY myself_identity; - myself_identity.init (cygwin_pid (procinfo->dwProcessId), PID_EXECED, NULL); - procinfo->exec_sendsig = NULL; - procinfo->exec_dwProcessId = 0; - myself_identity->ppid = procinfo->pid; - } } /* Initialize the process table entry for the current task. @@ -109,7 +102,8 @@ pinfo_init (char **envp, int envc) myself->process_state |= PID_ACTIVE; myself->process_state &= ~(PID_INITIALIZING | PID_EXITED | PID_REAPED); myself.preserve (); - debug_printf ("pid %d, pgid %d, process_state %y", myself->pid, myself->pgid, myself->process_state); + debug_printf ("pid %d, pgid %d, process_state %y", + myself->pid, myself->pgid, myself->process_state); } DWORD @@ -152,7 +146,7 @@ pinfo::status_exit (DWORD x) reason (but note, the environment *in* CMD is broken and shortened). This occurs at a point where there's no return to the exec'ing parent process, so we have to find some way to inform the user what happened. - + FIXME: For now, just return with SIGBUS set. Maybe it's better to add a lengthy small_printf instead. */ x = SIGBUS; @@ -229,6 +223,106 @@ pinfo::exit (DWORD n) } # undef self +/* Return next free Cygwin PID between 2 and 65535, round-robin. Each new + PID is checked that it doesn't collide with an existing PID. For that, + just check if the "cygpid.PID" section exists. */ +pid_t +create_cygwin_pid () +{ + pid_t pid = 0; + WCHAR sym_name[24]; + UNICODE_STRING sym_str; + OBJECT_ATTRIBUTES attr; + HANDLE sym_hdl; + NTSTATUS status; + + do + { + do + { + pid = ((uint32_t) InterlockedIncrement (&cygwin_shared->pid_src)) + % 65536; + } + while (pid < 2); + __small_swprintf (sym_name, L"cygpid.%u", pid); + RtlInitUnicodeString (&sym_str, sym_name); + InitializeObjectAttributes (&attr, &sym_str, OBJ_CASE_INSENSITIVE, + get_shared_parent_dir (), NULL); + /* We just want to know if the section (and thus the process) still + exists. Instead of actually opening the section, try to open + it as symlink. NtOpenSymbolicLinkObject will always returns an + error: + - STATUS_OBJECT_NAME_NOT_FOUND if the section doesn't exist, + so the slot is free and we can use this pid. + - STATUS_OBJECT_TYPE_MISMATCH if the section exists, so we have + to skip this pid and loop to try the next one. + As side-effect we never have to close the section handle and thus + we don't influence the lifetime of the section. */ + status = NtOpenSymbolicLinkObject (&sym_hdl, SYMBOLIC_LINK_QUERY, &attr); + } + while (status == STATUS_OBJECT_TYPE_MISMATCH); + return pid; +} + +/* Convert Windows WINPID into Cygwin PID. Utilize the "winpid.WINPID" + symlinks created for each process. The symlink contains the Cygwin + PID as target. Return 0 if no "winpid.WINPID" symlink exists for + this WINPID. */ +pid_t +cygwin_pid (DWORD dwProcessId) +{ + WCHAR sym_name[24]; + WCHAR pid_name[12]; + UNICODE_STRING sym_str; + UNICODE_STRING pid_str; + OBJECT_ATTRIBUTES attr; + HANDLE sym_hdl; + NTSTATUS status; + + __small_swprintf (sym_name, L"winpid.%u", dwProcessId); + RtlInitUnicodeString (&sym_str, sym_name); + InitializeObjectAttributes (&attr, &sym_str, OBJ_CASE_INSENSITIVE, + get_shared_parent_dir (), NULL); + status = NtOpenSymbolicLinkObject (&sym_hdl, SYMBOLIC_LINK_QUERY, &attr); + if (!NT_SUCCESS (status)) + return 0; + RtlInitEmptyUnicodeString (&pid_str, pid_name, + sizeof pid_name - sizeof (WCHAR)); + status = NtQuerySymbolicLinkObject (sym_hdl, &pid_str, NULL); + NtClose (sym_hdl); + if (!NT_SUCCESS (status)) + { + system_printf ("NtOpenSymbolicLinkObject: %y, PID %u, ret 0", + status, dwProcessId); + return 0; + } + pid_str.Buffer[pid_str.Length / sizeof (WCHAR)] = L'\0'; + pid_t ret = (pid_t) wcstoul (pid_str.Buffer, NULL, 10); + return ret; +} + +/* Create "winpid.WINPID" symlinks with the Cygwin PID of that process as + target. This is used to find the Cygwin PID for a given Windows WINPID. */ +inline void +pinfo::create_winpid_symlink (pid_t cygpid, DWORD winpid) +{ + WCHAR sym_name[24]; + WCHAR pid_name[24]; + UNICODE_STRING sym_str; + UNICODE_STRING pid_str; + OBJECT_ATTRIBUTES attr; + + __small_swprintf (sym_name, L"winpid.%u", + procinfo->dwProcessId ?: myself_initial.pid); + RtlInitUnicodeString (&sym_str, sym_name); + __small_swprintf (pid_name, L"%u", procinfo->pid); + RtlInitUnicodeString (&pid_str, pid_name); + InitializeObjectAttributes (&attr, &sym_str, OBJ_CASE_INSENSITIVE, + get_shared_parent_dir (), NULL); + NtCreateSymbolicLinkObject (&winpid_hdl, SYMBOLIC_LINK_ALL_ACCESS, + &attr, &pid_str); +} + inline void pinfo::_pinfo_release () { @@ -252,20 +346,18 @@ pinfo::init (pid_t n, DWORD flag, HANDLE h0) { shared_locations shloc; h = NULL; - if (myself && !(flag & PID_EXECED) - && (n == myself->pid || (DWORD) n == myself->dwProcessId)) + if (myself && n == myself->pid) { procinfo = myself; destroy = 0; return; } - int createit = flag & (PID_IN_USE | PID_EXECED); + int createit = (flag & PID_IN_USE); DWORD access = FILE_MAP_READ - | (flag & (PID_IN_USE | PID_EXECED | PID_MAP_RW) - ? FILE_MAP_WRITE : 0); + | (flag & (PID_IN_USE | PID_MAP_RW) ? FILE_MAP_WRITE : 0); if (!h0 || myself.h) - shloc = (flag & (PID_IN_USE | PID_EXECED)) ? SH_JUSTCREATE : SH_JUSTOPEN; + shloc = (flag & PID_IN_USE) ? SH_JUSTCREATE : SH_JUSTOPEN; else { shloc = SH_MYSELF; @@ -281,14 +373,8 @@ pinfo::init (pid_t n, DWORD flag, HANDLE h0) for (int i = 0; i < 20; i++) { - DWORD mapsize; - if (flag & PID_EXECED) - mapsize = PINFO_REDIR_SIZE; - else - mapsize = sizeof (_pinfo); - - procinfo = (_pinfo *) open_shared (L"cygpid", n, h0, mapsize, &shloc, - sec_attribs, access); + procinfo = (_pinfo *) open_shared (L"cygpid", n, h0, sizeof (_pinfo), + &shloc, sec_attribs, access); if (!h0) { if (createit) @@ -311,33 +397,10 @@ pinfo::init (pid_t n, DWORD flag, HANDLE h0) bool created = shloc != SH_JUSTOPEN; - /* Detect situation where a transitional memory block is being retrieved. - If the block has been allocated with PINFO_REDIR_SIZE but not yet - updated with a PID_EXECED state then we'll retry. */ - if (!created && !(flag & PID_NEW) && !procinfo->ppid) - { - /* Fetching process info for /proc or ps? just ignore this one. */ - if (flag & PID_NOREDIR) - break; - /* FIXME: Do we ever hit this case? And if so, in what situation? */ - system_printf ("This shouldn't happen:\n" - " me: (%d, %d, %d, %W)\n" - " pid %d\n" - " process_state %y\n" - " cygstarted %d\n" - " dwProcessId %d\n" - " name %W", - myself->pid, myself->dwProcessId, myself->cygstarted, - myself->progname, - procinfo->pid, procinfo->process_state, - procinfo->cygstarted, procinfo->dwProcessId, - procinfo->progname); - /* If not populated, wait 2 seconds for procinfo to become populated. - Would like to wait with finer granularity but that is not easily - doable. */ - for (int i = 0; i < 200 && !procinfo->ppid; i++) - Sleep (10); - } + /* Just fetching info for ps or /proc, don't do anything rash. */ + if (!created && !(flag & PID_NEW) && !procinfo->ppid + && (flag & PID_PROCINFO)) + break; if (!created && createit && (procinfo->process_state & PID_REAPED)) { @@ -346,32 +409,18 @@ pinfo::init (pid_t n, DWORD flag, HANDLE h0) shared memory */ } - if ((procinfo->process_state & PID_REAPED) - || ((procinfo->process_state & PID_INITIALIZING) && (flag & PID_NOREDIR) - && cygwin_pid (procinfo->dwProcessId) != procinfo->pid)) + if (procinfo->process_state & PID_REAPED) { set_errno (ESRCH); break; } - if (procinfo->process_state & PID_EXECED) - { - pid_t realpid = procinfo->pid; - debug_printf ("execed process windows pid %u, 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; - CloseHandle (h0); - h0 = NULL; - goto loop; - } - /* In certain 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. */ - if (!created && createit && (procinfo->process_state & (PID_EXITED | PID_REAPED))) + if (!created && createit + && (procinfo->process_state & (PID_EXITED | PID_REAPED))) { debug_printf ("looping because pid %d, procinfo->pid %d, " "procinfo->dwProcessid %u has PID_EXITED|PID_REAPED set", @@ -381,15 +430,8 @@ pinfo::init (pid_t n, DWORD flag, HANDLE h0) if (flag & PID_NEW) procinfo->start_time = time (NULL); - if (!created) - /* nothing */; - else if (!(flag & PID_EXECED)) + if (created) procinfo->pid = n; - else - { - procinfo->process_state |= PID_IN_USE | PID_EXECED; - procinfo->pid = myself->pid; - } h = h0; /* Success! */ break; @@ -529,7 +571,7 @@ _pinfo::set_ctty (fhandler_termios *fh, int flags) bool __reg1 _pinfo::exists () { - return process_state && !(process_state & (PID_EXITED | PID_REAPED | PID_EXECED)); + return process_state && !(process_state & (PID_EXITED | PID_REAPED)); } bool @@ -1279,6 +1321,8 @@ void pinfo::release () { _pinfo_release (); + if (winpid_hdl) + NtClose (winpid_hdl); HANDLE close_h; if (rd_proc_pipe) { @@ -1389,7 +1433,7 @@ winpids::add (DWORD& nelem, bool winpid, DWORD pid) make a copy of the shared memory area when it exists (it may not). */ perform_copy = onreturn ? make_copy : true; - p.init (cygpid, PID_NOREDIR | pinfo_access, NULL); + p.init (cygpid, PID_PROCINFO | pinfo_access, NULL); } /* If we're just looking for winpids then don't do any special cygwin "stuff* */ @@ -1403,9 +1447,9 @@ winpids::add (DWORD& nelem, bool winpid, DWORD pid) that it isn't a cygwin process. */ if (!p) { - if (!pinfo_access) + if (!pinfo_access || !cygpid) return; - p.init (cygpid, PID_NOREDIR, NULL); + p.init (cygpid, PID_PROCINFO, NULL); if (!p) return; } @@ -1491,7 +1535,7 @@ winpids::enum_processes (bool winpid) { restart = FALSE; f.dbi.ObjectName.Buffer[f.dbi.ObjectName.Length / sizeof (WCHAR)] = L'\0'; - if (wcsncmp (f.dbi.ObjectName.Buffer, L"cygpid.", 7) == 0) + if (wcsncmp (f.dbi.ObjectName.Buffer, L"winpid.", 7) == 0) { DWORD pid = wcstoul (f.dbi.ObjectName.Buffer + 7, NULL, 10); add (nelem, false, pid); |