diff options
Diffstat (limited to 'libbutl/process.cxx')
-rw-r--r-- | libbutl/process.cxx | 144 |
1 files changed, 84 insertions, 60 deletions
diff --git a/libbutl/process.cxx b/libbutl/process.cxx index 570c857..e416807 100644 --- a/libbutl/process.cxx +++ b/libbutl/process.cxx @@ -1,9 +1,7 @@ // file : libbutl/process.cxx -*- C++ -*- // license : MIT; see accompanying LICENSE file -#ifndef __cpp_modules_ts -#include <libbutl/process.mxx> -#endif +#include <libbutl/process.hxx> #include <errno.h> @@ -49,6 +47,14 @@ # elif defined(__NetBSD__) && __NetBSD__ >= 6 # define LIBBUTL_POSIX_SPAWN // +// On OpenBSD posix_spawn() appeared in 5.2 (see the man page for details). +// +# elif defined(__OpenBSD__) +# include <sys/param.h> // OpenBSD (yyyymm) +# if OpenBSD >= 201211 // 5.2 released on 1 Nov 2012. +# define LIBBUTL_POSIX_SPAWN +# endif +// // posix_spawn() appeared in Version 3 of the Single UNIX Specification that // was implemented in MacOS 10.5 (see the man page for details). // @@ -87,29 +93,20 @@ # endif // _MSC_VER #endif -#include <cassert> - -#ifndef __cpp_lib_modules_ts -#include <string> -#include <vector> -#include <chrono> -#include <cstdint> -#include <cstddef> -#include <system_error> - #include <ios> // ios_base::failure -#include <cstring> // strlen(), strchr(), strncmp() +#include <memory> // unique_ptr +#include <cstring> // strlen(), strchr(), strpbrk(), strncmp() #include <utility> // move() #include <ostream> +#include <cassert> #ifndef _WIN32 -#include <thread> // this_thread::sleep_for() +# include <thread> // this_thread::sleep_for() #else -#include <map> -#include <ratio> // milli -#include <cstdlib> // __argv[] -#include <algorithm> // find() -#endif +# include <map> +# include <ratio> // milli +# include <cstdlib> // __argv[] +# include <algorithm> // find() #endif #include <libbutl/process-details.hxx> @@ -119,32 +116,8 @@ namespace butl shared_mutex process_spawn_mutex; // Out of module purview. } -#ifdef __cpp_modules_ts -module butl.process; - -// Only imports additional to interface. -#ifdef __clang__ -#ifdef __cpp_lib_modules_ts -import std.core; -import std.io; -import std.threading; // Clang wants it in purview (see process-details.hxx). -#endif -import butl.path; -import butl.fdstream; -import butl.vector_view; -import butl.small_vector; -#endif - -#ifndef _WIN32 -import std.threading; -#endif - -import butl.utility; // icasecmp() -import butl.fdstream; // fdopen_null() -#else -#include <libbutl/utility.mxx> -#include <libbutl/fdstream.mxx> -#endif +#include <libbutl/utility.hxx> // icasecmp() +#include <libbutl/fdstream.hxx> // fdopen_null() using namespace std; @@ -217,7 +190,7 @@ namespace butl } void process:: - print (ostream& o, const char* const args[], size_t n) + print (ostream& o, const char* const* args, size_t n) { size_t m (0); const char* const* p (args); @@ -410,7 +383,7 @@ namespace butl } process:: - process (const process_path& pp, const char* args[], + process (const process_path& pp, const char* const* args, pipe pin, pipe pout, pipe perr, const char* cwd, const char* const* evars) @@ -670,6 +643,10 @@ namespace butl { // Child. // + // NOTE: make sure not to call anything that may acquire a mutex that + // could be already acquired in another thread, most notably + // malloc(). @@ What about exceptions (all the fail() calls)? + // Duplicate the user-supplied (fd > -1) or the created pipe descriptor // to the standard stream descriptor (read end for STDIN_FILENO, write // end otherwise). Close the pipe afterwards. @@ -729,6 +706,9 @@ namespace butl try { + // @@ TODO: redo without allocation (PATH_MAX?) Maybe + // also using C API to avoid exceptions. + // if (e != nullptr) setenv (string (v, e - v), e + 1); else @@ -736,6 +716,8 @@ namespace butl } catch (const system_error& e) { + // @@ Should we assume this cannot throw? + // throw process_child_error (e.code ().value ()); } } @@ -776,6 +758,13 @@ namespace butl { if (handle != 0) { + // First close any open pipe ends for good measure but ignore any + // errors. + // + out_fd.reset (); + in_ofd.reset (); + in_efd.reset (); + int es; int r (waitpid (handle, &es, 0)); handle = 0; // We have tried. @@ -857,6 +846,12 @@ namespace butl return getpid (); } + process::handle_type process:: + current_handle () + { + return getpid (); + } + // process_exit // process_exit:: @@ -1309,13 +1304,30 @@ namespace butl }; const char* process:: - quote_argument (const char* a, string& s) + quote_argument (const char* a, string& s, bool bat) { - // On Windows we need to protect values with spaces using quotes. - // Since there could be actual quotes in the value, we need to - // escape them. + // On Windows we need to protect values with spaces using quotes. Since + // there could be actual quotes in the value, we need to escape them. // - bool q (*a == '\0' || strchr (a, ' ') != nullptr); + // For batch files we also protect equal (`=`), comma (`,`) and semicolon + // (`;`) since otherwise an argument containing any of these will be split + // into several as if they were spaces (that is, the parts will appear in + // %1 %2, etc., instead of all in %1). This of course could break some + // batch files that rely on this semantics (for example, to automatically + // handle --foo=bar as --foo bar) but overall seeing a single argument + // (albeit quoted) is closer to the behavior of real executables. So we do + // this by default and if it becomes a problem we can invent a flag + // (probably in process_env) to disable this quoting (and while at it we + // may add a flag to disable all quoting since the user may need to quote + // some arguments but not others). + // + // While `()` and `[]` are not special characters, some "subsystems" + // (e.g., Cygwin/MSYS2) try to interpret them in certain contexts (e.g., + // relative paths). So we quote them as well (over-quoting seems to be + // harmless according to the "Parsing C Command-Line Arguments" MSDN + // article). + // + bool q (*a == '\0' || strpbrk (a, bat ? " =,;" : " ()[]") != nullptr); if (!q && strchr (a, '"') == nullptr) return a; @@ -1326,8 +1338,8 @@ namespace butl s += '"'; // Note that backslashes don't need escaping, unless they immediately - // precede the double quote (see `Parsing C Command-Line Arguments` MSDN - // article for more details). For example: + // precede the double quote (see "Parsing C Command-Line Arguments" MSDN + // article for details). For example: // // -DPATH="C:\\foo\\" -> -DPATH=\"C:\\foo\\\\\" // -DPATH=C:\foo bar\ -> "-DPATH=C:\foo bar\\" @@ -1366,7 +1378,7 @@ namespace butl static map<string, bool> detect_msys_cache_; process:: - process (const process_path& pp, const char* args[], + process (const process_path& pp, const char* const* args, pipe pin, pipe pout, pipe perr, const char* cwd, const char* const* evars) @@ -1548,12 +1560,12 @@ namespace butl // string cmd_line; { - auto append = [&cmd_line, buf = string ()] (const char* a) mutable + auto append = [&batch, &cmd_line, buf = string ()] (const char* a) mutable { if (!cmd_line.empty ()) cmd_line += ' '; - cmd_line += quote_argument (a, buf); + cmd_line += quote_argument (a, buf, batch.has_value ()); }; if (batch) @@ -1795,7 +1807,6 @@ namespace butl using namespace chrono; - // Retry for about 1 hour. // system_clock::duration timeout (1h); @@ -1881,7 +1892,7 @@ namespace butl return PeekNamedPipe (h, &c, 1, &n, nullptr, nullptr) && n == 1; }; - // Hidden by butl::duration that is introduced via fdstream.mxx. + // Hidden by butl::duration that is introduced via fdstream.hxx. // using milli_duration = chrono::duration<DWORD, milli>; @@ -1962,6 +1973,10 @@ namespace butl { if (handle != 0) { + out_fd.reset (); + in_ofd.reset (); + in_efd.reset (); + DWORD es; DWORD e (NO_ERROR); if (WaitForSingleObject (handle, INFINITE) != WAIT_OBJECT_0 || @@ -2069,6 +2084,15 @@ namespace butl return GetCurrentProcessId (); } + process::handle_type process:: + current_handle () + { + // Note that the returned handle is a pseudo handle (-1) that does not + // need to be closed. + // + return GetCurrentProcess (); + } + // process_exit // process_exit:: |