diff --git a/winsup/cygwin/dcrt0.cc b/winsup/cygwin/dcrt0.cc index 463df3128..5cdf01c6f 100644 --- a/winsup/cygwin/dcrt0.cc +++ b/winsup/cygwin/dcrt0.cc @@ -687,28 +687,11 @@ child_info_spawn::handle_spawn () if (child_proc_info->parent) { - if (type == _CH_EXEC) - { - /* Keep pointer to parent open if we've execed so that pid will not be - reused. Try to Urther reduce permissions. */ - HANDLE new_parent; - - if (DuplicateHandle (GetCurrentProcess (), child_proc_info->parent, - GetCurrentProcess (), &new_parent, - SYNCHRONIZE, FALSE, 0)) - { - CloseHandle (child_proc_info->parent); - child_proc_info->parent = new_parent; - } - } - else - { - /* Otherwise, we no longer need this handle so close it. Need to do - this after debug_fixup_after_fork_exec or DEBUGGING handling of - handles might get confused. */ - CloseHandle (child_proc_info->parent); - child_proc_info->parent = NULL; - } + /* We no longer need this handle so close it. Need to do + this after debug_fixup_after_fork_exec or DEBUGGING handling of + handles might get confused. */ + CloseHandle (child_proc_info->parent); + child_proc_info->parent = NULL; } signal_fixup_after_exec (); diff --git a/winsup/cygwin/fork.cc b/winsup/cygwin/fork.cc index d8c4ac459..c083f7a02 100644 --- a/winsup/cygwin/fork.cc +++ b/winsup/cygwin/fork.cc @@ -209,35 +209,6 @@ frok::child (volatile char * volatile here) return 0; } -#define NO_SLOW_PID_REUSE -#ifndef NO_SLOW_PID_REUSE -static void -slow_pid_reuse (HANDLE h) -{ - static NO_COPY HANDLE last_fork_procs[NPIDS_HELD]; - static NO_COPY unsigned nfork_procs; - - if (nfork_procs >= (sizeof (last_fork_procs) / sizeof (last_fork_procs [0]))) - nfork_procs = 0; - /* Keep a list of handles to child processes sitting around to prevent - Windows from reusing the same pid n times in a row. Having the same pids - close in succesion confuses bash. Keeping a handle open will stop - windows from reusing the same pid. */ - if (last_fork_procs[nfork_procs]) - ForceCloseHandle1 (last_fork_procs[nfork_procs], fork_stupidity); - if (DuplicateHandle (GetCurrentProcess (), h, - GetCurrentProcess (), &last_fork_procs[nfork_procs], - 0, FALSE, DUPLICATE_SAME_ACCESS)) - ProtectHandle1 (last_fork_procs[nfork_procs], fork_stupidity); - else - { - last_fork_procs[nfork_procs] = NULL; - system_printf ("couldn't create last_fork_proc, %E"); - } - nfork_procs++; -} -#endif - int __stdcall frok::parent (volatile char * volatile stack_here) { @@ -437,10 +408,6 @@ frok::parent (volatile char * volatile stack_here) goto cleanup; } -#ifndef NO_SLOW_PID_REUSE - slow_pid_reuse (hchild); -#endif - /* CHILD IS STOPPED */ debug_printf ("child is alive (but stopped)"); diff --git a/winsup/cygwin/include/cygwin/version.h b/winsup/cygwin/include/cygwin/version.h index b5ba93f5a..8926d49ae 100644 --- a/winsup/cygwin/include/cygwin/version.h +++ b/winsup/cygwin/include/cygwin/version.h @@ -506,12 +506,13 @@ details. */ 333: Add timerfd_create, timerfd_gettime, timerfd_settime. 334: Remove matherr. 335: Change size of utsname, change uname output. + 336: New Cygwin PID algorithm (yeah, not really an API change) Note that we forgot to bump the api for ualarm, strtoll, strtoull, sigaltstack, sethostname. */ #define CYGWIN_VERSION_API_MAJOR 0 -#define CYGWIN_VERSION_API_MINOR 335 +#define CYGWIN_VERSION_API_MINOR 336 /* There is also a compatibity version number associated with the shared memory regions. It is incremented when incompatible changes are made to the shared diff --git a/winsup/cygwin/include/sys/cygwin.h b/winsup/cygwin/include/sys/cygwin.h index c5da87c65..480d8ea06 100644 --- a/winsup/cygwin/include/sys/cygwin.h +++ b/winsup/cygwin/include/sys/cygwin.h @@ -269,8 +269,7 @@ enum PID_INITIALIZING = 0x00800, /* Set until ready to receive signals. */ PID_NEW = 0x01000, /* Available. */ PID_ALLPIDS = 0x02000, /* used by pinfo scanner */ - PID_EXECED = 0x04000, /* redirect to original pid info block */ - PID_NOREDIR = 0x08000, /* don't redirect if execed */ + PID_PROCINFO = 0x08000, /* caller just asks for process info */ PID_EXITED = 0x40000000, /* Free entry. */ PID_REAPED = 0x80000000 /* Reaped */ }; 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); diff --git a/winsup/cygwin/pinfo.h b/winsup/cygwin/pinfo.h index c4881c7f8..81e10d3d5 100644 --- a/winsup/cygwin/pinfo.h +++ b/winsup/cygwin/pinfo.h @@ -53,8 +53,6 @@ public: DWORD exitcode; /* set when process exits */ -#define PINFO_REDIR_SIZE ((char *) &myself.procinfo->exitcode - (char *) myself.procinfo) - /* > 0 if started by a cygwin process */ DWORD cygstarted; @@ -147,22 +145,25 @@ public: class pinfo: public pinfo_minimal { bool destroy; + HANDLE winpid_hdl; _pinfo *procinfo; public: bool waiter_ready; class cygthread *wait_thread; void __reg3 init (pid_t, DWORD, HANDLE); - pinfo (_pinfo *x = NULL): pinfo_minimal (), destroy (false), procinfo (x), - waiter_ready (false), wait_thread (NULL) {} - pinfo (pid_t n, DWORD flag = 0): pinfo_minimal (), destroy (false), - procinfo (NULL), waiter_ready (false), - wait_thread (NULL) + pinfo (_pinfo *x = NULL) + : pinfo_minimal (), destroy (false), winpid_hdl (NULL), procinfo (x), + waiter_ready (false), wait_thread (NULL) {} + pinfo (pid_t n, DWORD flag = 0) + : pinfo_minimal (), destroy (false), winpid_hdl (NULL), procinfo (NULL), + waiter_ready (false), wait_thread (NULL) { init (n, flag, NULL); } pinfo (HANDLE, pinfo_minimal&, pid_t); void __reg2 thisproc (HANDLE); + void create_winpid_symlink (pid_t, DWORD); inline void _pinfo_release (); void release (); bool __reg1 wait (); @@ -239,11 +240,8 @@ public: void release (); }; -extern __inline pid_t -cygwin_pid (pid_t pid) -{ - return pid; -} +pid_t create_cygwin_pid (); +pid_t cygwin_pid (DWORD); void __stdcall pinfo_init (char **, int); extern pinfo myself; diff --git a/winsup/cygwin/release/3.0 b/winsup/cygwin/release/3.0 index f4433c320..907405a05 100644 --- a/winsup/cygwin/release/3.0 +++ b/winsup/cygwin/release/3.0 @@ -61,6 +61,9 @@ What changed: - Kerberos/MSV1_0 S4U authentication replaces two old methods: Creating a token from scratch and Cygwin LSA authentication package. +- Cygwin PIDs have been decoupled from Windows PID. Cygwin PIDs are + now incrementally dealt in the range from 2 up to 65535, POSIX-like. + Bug Fixes --------- diff --git a/winsup/cygwin/shared.cc b/winsup/cygwin/shared.cc index dd16f1466..e87f2f960 100644 --- a/winsup/cygwin/shared.cc +++ b/winsup/cygwin/shared.cc @@ -323,12 +323,17 @@ shared_info::initialize () spinlock sversion (version, CURR_SHARED_MAGIC); if (!sversion) { + LUID luid; + cb = sizeof (*this); get_session_parent_dir (); /* Create session dir if first process. */ init_obcaseinsensitive (); /* Initialize obcaseinsensitive */ tty.init (); /* Initialize tty table */ mt.initialize (); /* Initialize shared tape information */ loadavg.initialize (); /* Initialize loadavg information */ + NtAllocateLocallyUniqueId (&luid);/* Initialize pid_src to a low */ + InterlockedExchange (&pid_src, /* random value to make start pid */ + luid.LowPart % 2048);/* less predictably */ /* Defer debug output printing the installation root and installation key up to this point. Debug output except for system_printf requires the global shared memory to exist. */ diff --git a/winsup/cygwin/shared_info.h b/winsup/cygwin/shared_info.h index f331a3ab3..1a5648b24 100644 --- a/winsup/cygwin/shared_info.h +++ b/winsup/cygwin/shared_info.h @@ -33,7 +33,7 @@ public: /* Data accessible to all tasks */ -#define CURR_SHARED_MAGIC 0x9b1c0f25U +#define CURR_SHARED_MAGIC 0x6758de88U #define USER_VERSION 1 @@ -50,6 +50,7 @@ class shared_info DWORD obcaseinsensitive; mtinfo mt; loadavginfo loadavg; + LONG pid_src; void initialize (); void init_obcaseinsensitive (); diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc index 37db52608..58e2696a1 100644 --- a/winsup/cygwin/spawn.cc +++ b/winsup/cygwin/spawn.cc @@ -709,7 +709,7 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv, ::cygheap->fdtab.fixup_before_exec (pi.dwProcessId); if (mode != _P_OVERLAY) - cygpid = cygwin_pid (pi.dwProcessId); + cygpid = create_cygwin_pid (); else cygpid = myself->pid; diff --git a/winsup/doc/new-features.xml b/winsup/doc/new-features.xml index d620d1c2c..4bbf5fbd0 100644 --- a/winsup/doc/new-features.xml +++ b/winsup/doc/new-features.xml @@ -100,6 +100,11 @@ Kerberos/MSV1_0 S4U authentication replaces two old methods: Creating a token from scratch and Cygwin LSA authentication package. + +Cygwin PIDs have been decoupled from Windows PID. Cygwin PIDs are now +incrementally dealt in the range from 2 up to 65535, POSIX-like. + +