diff options
-rw-r--r-- | butl/pager.cxx | 4 | ||||
-rw-r--r-- | butl/process | 32 | ||||
-rw-r--r-- | butl/process.cxx | 35 | ||||
-rw-r--r-- | butl/process.ixx | 6 | ||||
-rw-r--r-- | tests/process/driver.cxx | 20 |
5 files changed, 64 insertions, 33 deletions
diff --git a/butl/pager.cxx b/butl/pager.cxx index 3d429f5..b154a5c 100644 --- a/butl/pager.cxx +++ b/butl/pager.cxx @@ -28,6 +28,10 @@ namespace butl bool verbose, const string* pager, const vector<string>* pager_options) + // Create successfully exited process. Will "wait" for it if fallback to + // non-interactive execution path. + // + : p_ (optional<process::status_type> (0)) { // If we are using the default pager, try to get the terminal width // so that we can center the output. diff --git a/butl/process b/butl/process index e1eea29..c69c306 100644 --- a/butl/process +++ b/butl/process @@ -16,6 +16,7 @@ #include <butl/path> #include <butl/export> +#include <butl/optional> namespace butl { @@ -106,6 +107,16 @@ namespace butl class LIBBUTL_EXPORT process { public: +#ifndef _WIN32 + using handle_type = pid_t; + using id_type = pid_t; + using status_type = int; +#else + using handle_type = void*; // Win32 HANDLE + using id_type = std::uint32_t; // Win32 DWORD + using status_type = std::uint32_t; // Win32 DWORD +#endif + // Start another process using the specified command line. The default // values to the in, out and err arguments indicate that the child process // should inherit the parent process stdin, stdout, and stderr, @@ -202,10 +213,13 @@ namespace butl process (const process&) = delete; process& operator= (const process&) = delete; - // Create an empty or "already terminated" process. That is, handle is 0 - // and exit status is 0. + // Create an empty or "already terminated" process. By default the + // termination status is abnormal but you can change that. + // + // @@ Need to audit all calls (__attribute__((deprecated))). // - process (); + explicit + process (optional<status_type> status = nullopt); // Resolve process' paths based on the initial path in args0. If recall // differs from initial, adjust args0 to point to the recall path. If @@ -274,22 +288,12 @@ namespace butl print (std::ostream&, const char* const args[], size_t n = 0); public: -#ifndef _WIN32 - using handle_type = pid_t; - using id_type = pid_t; - using status_type = int; -#else - using handle_type = void*; // Win32 HANDLE - using id_type = std::uint32_t; // Win32 DWORD - using status_type = std::uint32_t; // Win32 DWORD -#endif - static id_type current_id (); public: handle_type handle; - status_type status; + optional<status_type> status; // Absence means terminated abnormally. int out_fd; // Write to this fd to send to the new process' stdin. int in_ofd; // Read from this fd to receive from the new process' stdout. diff --git a/butl/process.cxx b/butl/process.cxx index 64366ba..3af42cf 100644 --- a/butl/process.cxx +++ b/butl/process.cxx @@ -385,25 +385,23 @@ namespace butl { if (handle != 0) { - int r (waitpid (handle, &status, 0)); + status_type es; + int r (waitpid (handle, &es, 0)); handle = 0; // We have tried. if (r == -1) { + // If ignore errors then just leave status nullopt, so it has the same + // semantics as for abnormally terminated process. + // if (!ie) throw process_error (errno, false); - else - // Fold into status, so this and subsequent wait() calls return - // false. There is no portable way to update the status bits - // representing a process exit code specifically. So we set all bits - // to 1 and recon on getting non-zero exit status wherever the exact - // bits are. - // - status = ~0; } + else if (WIFEXITED (es)) + status = WEXITSTATUS (es); } - return WIFEXITED (status) && WEXITSTATUS (status) == 0; + return status && *status == 0; } bool process:: @@ -411,7 +409,8 @@ namespace butl { if (handle != 0) { - int r (waitpid (handle, &status, WNOHANG)); + status_type es; + int r (waitpid (handle, &es, WNOHANG)); if (r == 0) // Not exited yet. return false; @@ -420,9 +419,12 @@ namespace butl if (r == -1) throw process_error (errno, false); + + if (WIFEXITED (es)) + status = WEXITSTATUS (es); } - s = WIFEXITED (status) && WEXITSTATUS (status) == 0; + s = status && *status == 0; return true; } @@ -890,14 +892,15 @@ namespace butl status = s; else { + // If ignore errors then just leave status nullopt, so it has the same + // semantics as for abnormally terminated process. + // if (!ie) throw process_error (error_msg (e)); - else - status = 1; // Fold into status. } } - return status == 0; + return status && *status == 0; } bool process:: @@ -923,7 +926,7 @@ namespace butl status = s; } - s = status == 0; + s = status && *status == 0; return true; } diff --git a/butl/process.ixx b/butl/process.ixx index 83918bf..6d4dd2f 100644 --- a/butl/process.ixx +++ b/butl/process.ixx @@ -100,9 +100,9 @@ namespace butl } inline process:: - process () + process (optional<status_type> s) : handle (0), - status (0), // This is a bit of an assumption. + status (s), out_fd (-1), in_ofd (-1), in_efd (-1) @@ -155,7 +155,7 @@ namespace butl wait (); handle = p.handle; - status = p.status; + status = std::move (p.status); out_fd = p.out_fd; in_ofd = p.in_ofd; in_efd = p.in_efd; diff --git a/tests/process/driver.cxx b/tests/process/driver.cxx index 0aa9177..d59b1a7 100644 --- a/tests/process/driver.cxx +++ b/tests/process/driver.cxx @@ -255,6 +255,26 @@ main (int argc, const char* argv[]) return 0; } + // Test processes created as "already terminated". + // + { + process p; + assert (!p.wait ()); // "Terminated" abnormally. + } + + { + // Note that if to create as just process(0) then the + // process(const char* args[], int=0, int=1, int=2) ctor is being called. + // + process p (optional<process::status_type> (0)); + assert (p.wait ()); // "Exited" successfully. + } + + { + process p (optional<process::status_type> (1)); + assert (!p.wait ()); // "Exited" with an error. + } + const char* s ("ABC\nXYZ"); assert (exec (p)); |