aboutsummaryrefslogtreecommitdiff
path: root/libbutl/process.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'libbutl/process.cxx')
-rw-r--r--libbutl/process.cxx144
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::