aboutsummaryrefslogtreecommitdiffstats
path: root/util/system
diff options
context:
space:
mode:
authorkerzum <kerzum@yandex-team.ru>2022-02-10 16:49:33 +0300
committerDaniil Cherednik <dcherednik@yandex-team.ru>2022-02-10 16:49:33 +0300
commit9a7232babfd763ccfe827bc70e82e0f50cfd8276 (patch)
treea39808b7482c4711a80f799a7281adb36d76a13a /util/system
parent0e68ae909d3b76a5a001a07880eb0010dec6b2ea (diff)
downloadydb-9a7232babfd763ccfe827bc70e82e0f50cfd8276.tar.gz
Restoring authorship annotation for <kerzum@yandex-team.ru>. Commit 1 of 2.
Diffstat (limited to 'util/system')
-rw-r--r--util/system/shellcommand.cpp552
-rw-r--r--util/system/shellcommand.h138
-rw-r--r--util/system/shellcommand_ut.cpp224
3 files changed, 457 insertions, 457 deletions
diff --git a/util/system/shellcommand.cpp b/util/system/shellcommand.cpp
index b1989b5c8c..3cc23600cf 100644
--- a/util/system/shellcommand.cpp
+++ b/util/system/shellcommand.cpp
@@ -4,7 +4,7 @@
#include "sigset.h"
#include "atomic.h"
-#include <util/folder/dirut.h>
+#include <util/folder/dirut.h>
#include <util/generic/algorithm.h>
#include <util/generic/buffer.h>
#include <util/generic/vector.h>
@@ -18,12 +18,12 @@
#include <errno.h>
-#if defined(_unix_)
+#if defined(_unix_)
#include <unistd.h>
#include <fcntl.h>
#include <grp.h>
#include <sys/wait.h>
-
+
using TPid = pid_t;
using TWaitResult = pid_t;
using TExitStatus = int;
@@ -34,25 +34,25 @@ using TGetGroupListGid = int;
#else
using TGetGroupListGid = gid_t;
#endif
-#elif defined(_win_)
+#elif defined(_win_)
#include <string>
#include "winint.h"
-
+
using TPid = HANDLE;
using TWaitResult = DWORD;
using TExitStatus = DWORD;
#define WAIT_PROCEED WAIT_TIMEOUT
#pragma warning(disable : 4296) // 'wait_result >= WAIT_OBJECT_0' : expression is always tru
-#else
+#else
#error("unknown os, shell command is not implemented")
-#endif
-
+#endif
+
#define DBG(stmt) \
{}
-// #define DBG(stmt) stmt
-
+// #define DBG(stmt) stmt
+
namespace {
constexpr static size_t DATA_BUFFER_SIZE = 128 * 1024;
@@ -107,89 +107,89 @@ namespace {
#endif
}
-// temporary measure to avoid rewriting all poll calls on win TPipeHandle
-#if defined(_win_)
+// temporary measure to avoid rewriting all poll calls on win TPipeHandle
+#if defined(_win_)
using REALPIPEHANDLE = HANDLE;
#define INVALID_REALPIPEHANDLE INVALID_HANDLE_VALUE
-
-class TRealPipeHandle
+
+class TRealPipeHandle
: public TNonCopyable {
-public:
+public:
inline TRealPipeHandle() noexcept
- : Fd_(INVALID_REALPIPEHANDLE)
- {
- }
-
+ : Fd_(INVALID_REALPIPEHANDLE)
+ {
+ }
+
inline TRealPipeHandle(REALPIPEHANDLE fd) noexcept
- : Fd_(fd)
- {
- }
-
+ : Fd_(fd)
+ {
+ }
+
inline ~TRealPipeHandle() {
- Close();
- }
-
+ Close();
+ }
+
bool Close() noexcept {
- bool ok = true;
- if (Fd_ != INVALID_REALPIPEHANDLE)
- ok = CloseHandle(Fd_);
- Fd_ = INVALID_REALPIPEHANDLE;
- return ok;
- }
-
+ bool ok = true;
+ if (Fd_ != INVALID_REALPIPEHANDLE)
+ ok = CloseHandle(Fd_);
+ Fd_ = INVALID_REALPIPEHANDLE;
+ return ok;
+ }
+
inline REALPIPEHANDLE Release() noexcept {
- REALPIPEHANDLE ret = Fd_;
- Fd_ = INVALID_REALPIPEHANDLE;
- return ret;
- }
-
+ REALPIPEHANDLE ret = Fd_;
+ Fd_ = INVALID_REALPIPEHANDLE;
+ return ret;
+ }
+
inline void Swap(TRealPipeHandle& r) noexcept {
- DoSwap(Fd_, r.Fd_);
- }
-
+ DoSwap(Fd_, r.Fd_);
+ }
+
inline operator REALPIPEHANDLE() const noexcept {
- return Fd_;
- }
-
+ return Fd_;
+ }
+
inline bool IsOpen() const noexcept {
- return Fd_ != INVALID_REALPIPEHANDLE;
- }
-
+ return Fd_ != INVALID_REALPIPEHANDLE;
+ }
+
ssize_t Read(void* buffer, size_t byteCount) const noexcept {
- DWORD doneBytes;
+ DWORD doneBytes;
if (!ReadFile(Fd_, buffer, byteCount, &doneBytes, nullptr))
- return -1;
- return doneBytes;
- }
+ return -1;
+ return doneBytes;
+ }
ssize_t Write(const void* buffer, size_t byteCount) const noexcept {
- DWORD doneBytes;
+ DWORD doneBytes;
if (!WriteFile(Fd_, buffer, byteCount, &doneBytes, nullptr))
- return -1;
- return doneBytes;
- }
-
+ return -1;
+ return doneBytes;
+ }
+
static void Pipe(TRealPipeHandle& reader, TRealPipeHandle& writer, EOpenMode mode) {
(void)mode;
- REALPIPEHANDLE fds[2];
+ REALPIPEHANDLE fds[2];
if (!CreatePipe(&fds[0], &fds[1], nullptr /* handles are not inherited */, 0))
- ythrow TFileError() << "failed to create a pipe";
- TRealPipeHandle(fds[0]).Swap(reader);
- TRealPipeHandle(fds[1]).Swap(writer);
- }
-
+ ythrow TFileError() << "failed to create a pipe";
+ TRealPipeHandle(fds[0]).Swap(reader);
+ TRealPipeHandle(fds[1]).Swap(writer);
+ }
+
private:
- REALPIPEHANDLE Fd_;
-};
-
-#else
+ REALPIPEHANDLE Fd_;
+};
+
+#else
using TRealPipeHandle = TPipeHandle;
using REALPIPEHANDLE = PIPEHANDLE;
#define INVALID_REALPIPEHANDLE INVALID_PIPEHANDLE
-#endif
-
-class TShellCommand::TImpl
+#endif
+
+class TShellCommand::TImpl
: public TAtomicRefCount<TShellCommand::TImpl> {
-private:
+private:
TPid Pid;
TString Command;
TList<TString> Arguments;
@@ -208,7 +208,7 @@ private:
TFileHandle OutputHandle;
TFileHandle ErrorHandle;
- /// @todo: store const TShellCommandOptions, no need for so many vars
+ /// @todo: store const TShellCommandOptions, no need for so many vars
bool TerminateFlag = false;
bool ClearSignalMask = false;
bool CloseAllFdsOnExec = false;
@@ -230,9 +230,9 @@ private:
struct TProcessInfo {
TImpl* Parent;
- TRealPipeHandle InputFd;
- TRealPipeHandle OutputFd;
- TRealPipeHandle ErrorFd;
+ TRealPipeHandle InputFd;
+ TRealPipeHandle OutputFd;
+ TRealPipeHandle ErrorFd;
TProcessInfo(TImpl* parent, REALPIPEHANDLE inputFd, REALPIPEHANDLE outputFd, REALPIPEHANDLE errorFd)
: Parent(parent)
, InputFd(inputFd)
@@ -242,12 +242,12 @@ private:
}
};
- struct TPipes {
- TRealPipeHandle OutputPipeFd[2];
- TRealPipeHandle ErrorPipeFd[2];
- TRealPipeHandle InputPipeFd[2];
- // pipes are closed by automatic dtor
- void PrepareParents() {
+ struct TPipes {
+ TRealPipeHandle OutputPipeFd[2];
+ TRealPipeHandle ErrorPipeFd[2];
+ TRealPipeHandle InputPipeFd[2];
+ // pipes are closed by automatic dtor
+ void PrepareParents() {
if (OutputPipeFd[1].IsOpen()) {
OutputPipeFd[1].Close();
}
@@ -255,16 +255,16 @@ private:
ErrorPipeFd[1].Close();
}
if (InputPipeFd[1].IsOpen()) {
- InputPipeFd[0].Close();
+ InputPipeFd[0].Close();
}
- }
- void ReleaseParents() {
- InputPipeFd[1].Release();
- OutputPipeFd[0].Release();
- ErrorPipeFd[0].Release();
- }
- };
-
+ }
+ void ReleaseParents() {
+ InputPipeFd[1].Release();
+ OutputPipeFd[0].Release();
+ ErrorPipeFd[0].Release();
+ }
+ };
+
struct TPipePump {
TRealPipeHandle* Pipe;
IOutputStream* OutputStream;
@@ -273,12 +273,12 @@ private:
TString InternalError;
};
-#if defined(_unix_)
+#if defined(_unix_)
void OnFork(TPipes& pipes, sigset_t oldmask, char* const* argv, char* const* envp, const std::function<void()>& afterFork) const;
-#else
- void StartProcess(TPipes& pipes);
-#endif
-
+#else
+ void StartProcess(TPipes& pipes);
+#endif
+
public:
inline TImpl(const TStringBuf cmd, const TList<TString>& args, const TShellCommandOptions& options, const TString& workdir)
: Pid(0)
@@ -298,7 +298,7 @@ public:
, UseShell(options.UseShell)
, QuoteArguments(options.QuoteArguments)
, DetachSession(options.DetachSession)
- , CloseStreams(options.CloseStreams)
+ , CloseStreams(options.CloseStreams)
, ShouldCloseInput(options.ShouldCloseInput)
, InputMode(options.InputMode)
, OutputMode(options.OutputMode)
@@ -386,24 +386,24 @@ public:
return ErrorHandle;
}
- // start child process
- void Run();
+ // start child process
+ void Run();
inline void Terminate() {
if (!!Pid && (AtomicGet(ExecutionStatus) == SHELL_RUNNING)) {
- bool ok =
-#if defined(_unix_)
+ bool ok =
+#if defined(_unix_)
kill(DetachSession ? -1 * Pid : Pid, SIGTERM) == 0;
if (!ok && (errno == ESRCH) && DetachSession) {
// this could fail when called before child proc completes setsid().
ok = kill(Pid, SIGTERM) == 0;
kill(-Pid, SIGTERM); // between a failed kill(-Pid) and a successful kill(Pid) a grandchild could have been spawned
}
-#else
+#else
TerminateProcess(Pid, 1 /* exit code */);
-#endif
+#endif
if (!ok) {
- ythrow TSystemError() << "cannot terminate " << Pid;
+ ythrow TSystemError() << "cannot terminate " << Pid;
}
}
}
@@ -426,28 +426,28 @@ public:
pi->InputFd.Close();
pi->ErrorFd.Close();
pi->OutputFd.Close();
-
- if (pi->Parent->CloseStreams) {
+
+ if (pi->Parent->CloseStreams) {
if (pi->Parent->ErrorStream) {
- pi->Parent->ErrorStream->Finish();
+ pi->Parent->ErrorStream->Finish();
}
if (pi->Parent->OutputStream) {
- pi->Parent->OutputStream->Finish();
+ pi->Parent->OutputStream->Finish();
}
- }
-
+ }
+
delete pi;
return true;
}
- // interchange io while process is alive
+ // interchange io while process is alive
inline static void Communicate(TProcessInfo* pi);
inline static void* WatchProcess(void* data) {
TProcessInfo* pi = reinterpret_cast<TProcessInfo*>(data);
- Communicate(pi);
+ Communicate(pi);
return nullptr;
- }
+ }
inline static void* ReadStream(void* data) noexcept {
TPipePump* pump = reinterpret_cast<TPipePump*>(data);
@@ -510,16 +510,16 @@ public:
}
TString GetQuotedCommand() const;
-};
+};
-#if defined(_win_)
-void TShellCommand::TImpl::StartProcess(TShellCommand::TImpl::TPipes& pipes) {
- // Setup STARTUPINFO to redirect handles.
+#if defined(_win_)
+void TShellCommand::TImpl::StartProcess(TShellCommand::TImpl::TPipes& pipes) {
+ // Setup STARTUPINFO to redirect handles.
STARTUPINFOW startup_info;
- ZeroMemory(&startup_info, sizeof(startup_info));
- startup_info.cb = sizeof(startup_info);
- startup_info.dwFlags = STARTF_USESTDHANDLES;
-
+ ZeroMemory(&startup_info, sizeof(startup_info));
+ startup_info.cb = sizeof(startup_info);
+ startup_info.dwFlags = STARTF_USESTDHANDLES;
+
if (OutputMode != TShellCommandOptions::HANDLE_INHERIT) {
if (!SetHandleInformation(pipes.OutputPipeFd[1], HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)) {
ythrow TSystemError() << "cannot set handle info";
@@ -531,11 +531,11 @@ void TShellCommand::TImpl::StartProcess(TShellCommand::TImpl::TPipes& pipes) {
}
}
if (InputMode != TShellCommandOptions::HANDLE_INHERIT) {
- if (!SetHandleInformation(pipes.InputPipeFd[0], HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT))
- ythrow TSystemError() << "cannot set handle info";
+ if (!SetHandleInformation(pipes.InputPipeFd[0], HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT))
+ ythrow TSystemError() << "cannot set handle info";
}
-
- // A sockets do not work as std streams for some reason
+
+ // A sockets do not work as std streams for some reason
if (OutputMode != TShellCommandOptions::HANDLE_INHERIT) {
startup_info.hStdOutput = pipes.OutputPipeFd[1];
} else {
@@ -547,20 +547,20 @@ void TShellCommand::TImpl::StartProcess(TShellCommand::TImpl::TPipes& pipes) {
startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
}
if (InputMode != TShellCommandOptions::HANDLE_INHERIT) {
- startup_info.hStdInput = pipes.InputPipeFd[0];
+ startup_info.hStdInput = pipes.InputPipeFd[0];
} else {
// Don't leave hStdInput unfilled, otherwise any attempt to retrieve the operating-system file handle
// that is associated with the specified file descriptor will led to errors.
startup_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
}
-
- PROCESS_INFORMATION process_info;
+
+ PROCESS_INFORMATION process_info;
// TString cmd = "cmd /U" + TUtf16String can be used to read unicode messages from cmd
- // /A - ansi charset /Q - echo off, /C - command, /Q - special quotes
+ // /A - ansi charset /Q - echo off, /C - command, /Q - special quotes
TString qcmd = GetQuotedCommand();
TString cmd = UseShell ? "cmd /A /Q /S /C \"" + qcmd + "\"" : qcmd;
- // winapi can modify command text, copy it
-
+ // winapi can modify command text, copy it
+
Y_ENSURE_EX(cmd.size() < MAX_COMMAND_LINE, yexception() << "Command is too long (length=" << cmd.size() << ")");
TTempArray<wchar_t> cmdcopy(MAX_COMMAND_LINE);
Copy(cmd.data(), cmd.data() + cmd.size(), cmdcopy.Data());
@@ -617,16 +617,16 @@ void TShellCommand::TImpl::StartProcess(TShellCommand::TImpl::TPipes& pipes) {
if (!res) {
AtomicSet(ExecutionStatus, SHELL_ERROR);
- /// @todo: write to error stream if set
+ /// @todo: write to error stream if set
TStringOutput out(CollectedError);
out << "Process was not created: " << LastSystemErrorText() << " command text was: '" << GetAString(cmdcopy.Data()) << "'";
- }
+ }
Pid = process_info.hProcess;
CloseHandle(process_info.hThread);
DBG(Cerr << "created process id " << Pid << " in dir: " << cwd << ", cmd: " << cmdcopy.Data() << Endl);
-}
-#endif
-
+}
+#endif
+
void ShellQuoteArg(TString& dst, TStringBuf argument) {
dst.append("\"");
TStringBuf l, r;
@@ -657,14 +657,14 @@ TString TShellCommand::TImpl::GetQuotedCommand() const {
// Don't add unnecessary quotes. It's especially important for the windows with a 32k command line length limit.
if (QuoteArguments && ArgNeedsQuotes(argument)) {
::ShellQuoteArgSp(quoted, argument);
- } else {
+ } else {
quoted.append(" ").append(argument);
- }
- }
- return quoted;
-}
-
-#if defined(_unix_)
+ }
+ }
+ return quoted;
+}
+
+#if defined(_unix_)
void TShellCommand::TImpl::OnFork(TPipes& pipes, sigset_t oldmask, char* const* argv, char* const* envp, const std::function<void()>& afterFork) const {
try {
if (DetachSession) {
@@ -715,7 +715,7 @@ void TShellCommand::TImpl::OnFork(TPipes& pipes, sigset_t oldmask, char* const*
sErr.Release();
sErrNew.Release();
}
-
+
if (WorkDir.size()) {
NFs::SetCurrentWorkingDirectory(WorkDir);
}
@@ -724,8 +724,8 @@ void TShellCommand::TImpl::OnFork(TPipes& pipes, sigset_t oldmask, char* const*
for (int fd = NSystemInfo::MaxOpenFiles(); fd > STDERR_FILENO; --fd) {
fcntl(fd, F_SETFD, FD_CLOEXEC);
}
- }
-
+ }
+
if (!User.Name.empty()) {
ImpersonateUser(User);
}
@@ -750,18 +750,18 @@ void TShellCommand::TImpl::OnFork(TPipes& pipes, sigset_t oldmask, char* const*
Cerr << "Process was not created: "
<< "unknown error" << Endl;
}
-
+
_exit(-1);
-}
-#endif
-
-void TShellCommand::TImpl::Run() {
+}
+#endif
+
+void TShellCommand::TImpl::Run() {
Y_ENSURE(AtomicGet(ExecutionStatus) != SHELL_RUNNING, TStringBuf("Process is already running"));
- // Prepare I/O streams
- CollectedOutput.clear();
- CollectedError.clear();
- TPipes pipes;
-
+ // Prepare I/O streams
+ CollectedOutput.clear();
+ CollectedError.clear();
+ TPipes pipes;
+
if (OutputMode != TShellCommandOptions::HANDLE_INHERIT) {
TRealPipeHandle::Pipe(pipes.OutputPipeFd[0], pipes.OutputPipeFd[1], CloseOnExec);
}
@@ -770,11 +770,11 @@ void TShellCommand::TImpl::Run() {
}
if (InputMode != TShellCommandOptions::HANDLE_INHERIT) {
TRealPipeHandle::Pipe(pipes.InputPipeFd[0], pipes.InputPipeFd[1], CloseOnExec);
- }
-
+ }
+
AtomicSet(ExecutionStatus, SHELL_RUNNING);
-
-#if defined(_unix_)
+
+#if defined(_unix_)
// block all signals to avoid signal handler race after fork()
sigset_t oldmask, newmask;
SigFillSet(&newmask);
@@ -817,12 +817,12 @@ void TShellCommand::TImpl::Run() {
envp.push_back(nullptr);
}
- pid_t pid = fork();
- if (pid == -1) {
+ pid_t pid = fork();
+ if (pid == -1) {
AtomicSet(ExecutionStatus, SHELL_ERROR);
- /// @todo check if pipes are still open
+ /// @todo check if pipes are still open
ythrow TSystemError() << "Cannot fork";
- } else if (pid == 0) { // child
+ } else if (pid == 0) { // child
if (envp.size() != 0) {
OnFork(pipes, oldmask, qargv.data(), envp.data(), FuncAfterFork);
} else {
@@ -833,17 +833,17 @@ void TShellCommand::TImpl::Run() {
if (SigProcMask(SIG_SETMASK, &oldmask, nullptr) != 0) {
ythrow TSystemError() << "Cannot restore signal mask in parent";
}
- }
- Pid = pid;
-#else
- StartProcess(pipes);
-#endif
- pipes.PrepareParents();
-
+ }
+ Pid = pid;
+#else
+ StartProcess(pipes);
+#endif
+ pipes.PrepareParents();
+
if (AtomicGet(ExecutionStatus) != SHELL_RUNNING) {
- return;
+ return;
}
-
+
if (InputMode == TShellCommandOptions::HANDLE_PIPE) {
TFileHandle inputHandle(pipes.InputPipeFd[1].Release());
InputHandle.Swap(inputHandle);
@@ -861,32 +861,32 @@ void TShellCommand::TImpl::Run() {
TProcessInfo* processInfo = new TProcessInfo(this,
pipes.InputPipeFd[1].Release(), pipes.OutputPipeFd[0].Release(), pipes.ErrorPipeFd[0].Release());
- if (AsyncMode) {
+ if (AsyncMode) {
WatchThread = new TThread(&TImpl::WatchProcess, processInfo);
- WatchThread->Start();
- /// @todo wait for child to start its process session (if options.Detach)
+ WatchThread->Start();
+ /// @todo wait for child to start its process session (if options.Detach)
} else {
- Communicate(processInfo);
- }
-
- pipes.ReleaseParents(); // not needed
-}
+ Communicate(processInfo);
+ }
-void TShellCommand::TImpl::Communicate(TProcessInfo* pi) {
+ pipes.ReleaseParents(); // not needed
+}
+
+void TShellCommand::TImpl::Communicate(TProcessInfo* pi) {
THolder<IOutputStream> outputHolder;
IOutputStream* output = pi->Parent->OutputStream;
if (!output) {
- outputHolder.Reset(output = new TStringOutput(pi->Parent->CollectedOutput));
+ outputHolder.Reset(output = new TStringOutput(pi->Parent->CollectedOutput));
}
-
+
THolder<IOutputStream> errorHolder;
IOutputStream* error = pi->Parent->ErrorStream;
if (!error) {
- errorHolder.Reset(error = new TStringOutput(pi->Parent->CollectedError));
+ errorHolder.Reset(error = new TStringOutput(pi->Parent->CollectedError));
}
-
+
IInputStream*& input = pi->Parent->InputStream;
-
+
#if defined(_unix_)
// not really needed, io is done via poll
if (pi->OutputFd.IsOpen()) {
@@ -900,7 +900,7 @@ void TShellCommand::TImpl::Communicate(TProcessInfo* pi) {
}
#endif
- try {
+ try {
#if defined(_win_)
TPipePump pumps[3] = {0};
pumps[0] = {&pi->ErrorFd, error};
@@ -920,172 +920,172 @@ void TShellCommand::TImpl::Communicate(TProcessInfo* pi) {
#else
TBuffer buffer(DATA_BUFFER_SIZE);
TBuffer inputBuffer(DATA_BUFFER_SIZE);
- int bytes;
- int bytesToWrite = 0;
+ int bytes;
+ int bytesToWrite = 0;
char* bufPos = nullptr;
#endif
- TWaitResult waitPidResult;
+ TWaitResult waitPidResult;
TExitStatus status = 0;
-
- while (true) {
- {
+
+ while (true) {
+ {
with_lock (pi->Parent->TerminateMutex) {
if (TerminateIsRequired(pi)) {
return;
}
}
- waitPidResult =
-#if defined(_unix_)
+ waitPidResult =
+#if defined(_unix_)
waitpid(pi->Parent->Pid, &status, WNOHANG);
-#else
+#else
WaitForSingleObject(pi->Parent->Pid /* process_info.hProcess */, pi->Parent->PollDelayMs /* ms */);
Y_UNUSED(status);
-#endif
- // DBG(Cerr << "wait result: " << waitPidResult << Endl);
+#endif
+ // DBG(Cerr << "wait result: " << waitPidResult << Endl);
if (waitPidResult != WAIT_PROCEED) {
- break;
+ break;
}
- }
+ }
/// @todo factor out (poll + wfmo)
#if defined(_unix_)
bool haveIn = false;
bool haveOut = false;
bool haveErr = false;
- if (!input && pi->InputFd.IsOpen()) {
- DBG(Cerr << "closing input stream..." << Endl);
- pi->InputFd.Close();
- }
- if (!output && pi->OutputFd.IsOpen()) {
- DBG(Cerr << "closing output stream..." << Endl);
- pi->OutputFd.Close();
- }
- if (!error && pi->ErrorFd.IsOpen()) {
- DBG(Cerr << "closing error stream..." << Endl);
- pi->ErrorFd.Close();
- }
-
+ if (!input && pi->InputFd.IsOpen()) {
+ DBG(Cerr << "closing input stream..." << Endl);
+ pi->InputFd.Close();
+ }
+ if (!output && pi->OutputFd.IsOpen()) {
+ DBG(Cerr << "closing output stream..." << Endl);
+ pi->OutputFd.Close();
+ }
+ if (!error && pi->ErrorFd.IsOpen()) {
+ DBG(Cerr << "closing error stream..." << Endl);
+ pi->ErrorFd.Close();
+ }
+
if (!input && !output && !error) {
- continue;
+ continue;
}
-
+
struct pollfd fds[] = {
{REALPIPEHANDLE(pi->InputFd), POLLOUT, 0},
{REALPIPEHANDLE(pi->OutputFd), POLLIN, 0},
{REALPIPEHANDLE(pi->ErrorFd), POLLIN, 0}};
- int res;
-
+ int res;
+
if (!input) {
- fds[0].events = 0;
+ fds[0].events = 0;
}
if (!output) {
- fds[1].events = 0;
+ fds[1].events = 0;
}
if (!error) {
- fds[2].events = 0;
+ fds[2].events = 0;
}
-
- res = PollD(fds, 3, TInstant::Now() + TDuration::MilliSeconds(pi->Parent->PollDelayMs));
- // DBG(Cerr << "poll result: " << res << Endl);
+
+ res = PollD(fds, 3, TInstant::Now() + TDuration::MilliSeconds(pi->Parent->PollDelayMs));
+ // DBG(Cerr << "poll result: " << res << Endl);
if (-res == ETIMEDOUT || res == 0) {
- // DBG(Cerr << "poll again..." << Endl);
- continue;
- }
+ // DBG(Cerr << "poll again..." << Endl);
+ continue;
+ }
if (res < 0) {
- ythrow yexception() << "poll failed: " << LastSystemErrorText();
+ ythrow yexception() << "poll failed: " << LastSystemErrorText();
}
-
+
if ((fds[1].revents & POLLIN) == POLLIN) {
- haveOut = true;
+ haveOut = true;
} else if (fds[1].revents & (POLLERR | POLLHUP)) {
output = nullptr;
}
-
+
if ((fds[2].revents & POLLIN) == POLLIN) {
- haveErr = true;
+ haveErr = true;
} else if (fds[2].revents & (POLLERR | POLLHUP)) {
error = nullptr;
}
-
+
if (input && ((fds[0].revents & POLLOUT) == POLLOUT)) {
- haveIn = true;
+ haveIn = true;
}
- if (haveOut) {
- bytes = pi->OutputFd.Read(buffer.Data(), buffer.Capacity());
- DBG(Cerr << "transferred " << bytes << " bytes of output" << Endl);
+ if (haveOut) {
+ bytes = pi->OutputFd.Read(buffer.Data(), buffer.Capacity());
+ DBG(Cerr << "transferred " << bytes << " bytes of output" << Endl);
if (bytes > 0) {
output->Write(buffer.Data(), bytes);
} else {
output = nullptr;
}
- }
- if (haveErr) {
- bytes = pi->ErrorFd.Read(buffer.Data(), buffer.Capacity());
- DBG(Cerr << "transferred " << bytes << " bytes of error" << Endl);
+ }
+ if (haveErr) {
+ bytes = pi->ErrorFd.Read(buffer.Data(), buffer.Capacity());
+ DBG(Cerr << "transferred " << bytes << " bytes of error" << Endl);
if (bytes > 0) {
error->Write(buffer.Data(), bytes);
} else {
error = nullptr;
}
- }
-
- if (haveIn) {
- if (!bytesToWrite) {
- bytesToWrite = input->Read(inputBuffer.Data(), inputBuffer.Capacity());
- if (bytesToWrite == 0) {
+ }
+
+ if (haveIn) {
+ if (!bytesToWrite) {
+ bytesToWrite = input->Read(inputBuffer.Data(), inputBuffer.Capacity());
+ if (bytesToWrite == 0) {
if (AtomicGet(pi->Parent->ShouldCloseInput)) {
input = nullptr;
}
- continue;
+ continue;
}
- bufPos = inputBuffer.Data();
+ bufPos = inputBuffer.Data();
}
-
+
bytes = pi->InputFd.Write(bufPos, bytesToWrite);
- if (bytes > 0) {
- bytesToWrite -= bytes;
- bufPos += bytes;
+ if (bytes > 0) {
+ bytesToWrite -= bytes;
+ bufPos += bytes;
} else {
input = nullptr;
}
-
- DBG(Cerr << "transferred " << bytes << " bytes of input" << Endl);
+
+ DBG(Cerr << "transferred " << bytes << " bytes of input" << Endl);
}
#endif
- }
- DBG(Cerr << "process finished" << Endl);
-
+ }
+ DBG(Cerr << "process finished" << Endl);
+
// What's the reason of process exit.
// We need to set exit code before waiting for input thread
// Otherwise there is no way for input stream provider to discover
// that process has exited and stream shouldn't wait for new data.
- bool cleanExit = false;
+ bool cleanExit = false;
TMaybe<int> processExitCode;
-#if defined(_unix_)
+#if defined(_unix_)
processExitCode = WEXITSTATUS(status);
if (WIFEXITED(status) && processExitCode == 0) {
- cleanExit = true;
+ cleanExit = true;
} else if (WIFSIGNALED(status)) {
processExitCode = -WTERMSIG(status);
}
-#else
- if (waitPidResult == WAIT_OBJECT_0) {
- DWORD exitCode = STILL_ACTIVE;
+#else
+ if (waitPidResult == WAIT_OBJECT_0) {
+ DWORD exitCode = STILL_ACTIVE;
if (!GetExitCodeProcess(pi->Parent->Pid, &exitCode)) {
- ythrow yexception() << "GetExitCodeProcess: " << LastSystemErrorText();
+ ythrow yexception() << "GetExitCodeProcess: " << LastSystemErrorText();
}
- if (exitCode == 0)
- cleanExit = true;
+ if (exitCode == 0)
+ cleanExit = true;
processExitCode = static_cast<int>(exitCode);
- DBG(Cerr << "exit code: " << exitCode << Endl);
+ DBG(Cerr << "exit code: " << exitCode << Endl);
}
-#endif
+#endif
pi->Parent->ExitCode = processExitCode;
- if (cleanExit) {
+ if (cleanExit) {
AtomicSet(pi->Parent->ExecutionStatus, SHELL_FINISHED);
- } else {
+ } else {
AtomicSet(pi->Parent->ExecutionStatus, SHELL_ERROR);
}
@@ -1108,18 +1108,18 @@ void TShellCommand::TImpl::Communicate(TProcessInfo* pi) {
}
#endif
} catch (const yexception& e) {
- // Some error in watch occured, set result to error
+ // Some error in watch occured, set result to error
AtomicSet(pi->Parent->ExecutionStatus, SHELL_INTERNAL_ERROR);
- pi->Parent->InternalError = e.what();
+ pi->Parent->InternalError = e.what();
if (input) {
- pi->InputFd.Close();
+ pi->InputFd.Close();
}
- Cdbg << "shell command internal error: " << pi->Parent->InternalError << Endl;
+ Cdbg << "shell command internal error: " << pi->Parent->InternalError << Endl;
}
- // Now we can safely delete process info struct and other data
- pi->Parent->TerminateFlag = true;
+ // Now we can safely delete process info struct and other data
+ pi->Parent->TerminateFlag = true;
TerminateIsRequired(pi);
-}
+}
TShellCommand::TShellCommand(const TStringBuf cmd, const TList<TString>& args, const TShellCommandOptions& options,
const TString& workdir)
diff --git a/util/system/shellcommand.h b/util/system/shellcommand.h
index 8730627fe5..4304202c68 100644
--- a/util/system/shellcommand.h
+++ b/util/system/shellcommand.h
@@ -88,7 +88,7 @@ public:
* @note in default close-on-exec mode is off.
* @return self
*/
- inline TShellCommandOptions& SetCloseAllFdsOnExec(bool closeAllFdsOnExec) {
+ inline TShellCommandOptions& SetCloseAllFdsOnExec(bool closeAllFdsOnExec) {
CloseAllFdsOnExec = closeAllFdsOnExec;
return *this;
}
@@ -98,30 +98,30 @@ public:
* in separate thread, and control will be returned immediately
*
* @param async true if asynchonous mode is needed
- * @note in default async mode launcher will need 100% cpu for rapid process termination
+ * @note in default async mode launcher will need 100% cpu for rapid process termination
* @return self
*/
- inline TShellCommandOptions& SetAsync(bool async) {
+ inline TShellCommandOptions& SetAsync(bool async) {
AsyncMode = async;
- if (AsyncMode)
- PollDelayMs = 0;
- return *this;
- }
-
- /**
- * @brief specify delay for process controlling loop
- * @param ms number of milliseconds to poll for
- * @note for synchronous process default of 1s should generally fit
- * for async process default is no latency and that consumes 100% one cpu
- * SetAsync(true) will reset this delay to 0, so call this method after
- * @return self
- */
- inline TShellCommandOptions& SetLatency(size_t ms) {
- PollDelayMs = ms;
+ if (AsyncMode)
+ PollDelayMs = 0;
return *this;
}
/**
+ * @brief specify delay for process controlling loop
+ * @param ms number of milliseconds to poll for
+ * @note for synchronous process default of 1s should generally fit
+ * for async process default is no latency and that consumes 100% one cpu
+ * SetAsync(true) will reset this delay to 0, so call this method after
+ * @return self
+ */
+ inline TShellCommandOptions& SetLatency(size_t ms) {
+ PollDelayMs = ms;
+ return *this;
+ }
+
+ /**
* @brief set the stream, which is input fetched from
*
* @param stream Pointer to stream.
@@ -168,17 +168,17 @@ public:
}
/**
- * @brief set if Finish() should be called on user-supplied streams
- * if process is run in async mode Finish will be called in process' thread
- * @param val if Finish() should be called
- * @return self
- */
- inline TShellCommandOptions& SetCloseStreams(bool val) {
- CloseStreams = val;
- return *this;
- }
-
- /**
+ * @brief set if Finish() should be called on user-supplied streams
+ * if process is run in async mode Finish will be called in process' thread
+ * @param val if Finish() should be called
+ * @return self
+ */
+ inline TShellCommandOptions& SetCloseStreams(bool val) {
+ CloseStreams = val;
+ return *this;
+ }
+
+ /**
* @brief set if input stream should be closed after all data is read
* call SetCloseInput(false) for interactive process
* @param val if input stream should be closed
@@ -190,21 +190,21 @@ public:
}
/**
- * @brief set if command should be interpreted by OS shell (/bin/sh or cmd.exe)
- * shell is enabled by default
- * call SetUseShell(false) for command to be sent to OS verbatim
- * @note shell operators > < | && || will not work if this option is off
- * @param useShell if command should be run in shell
- * @return self
- */
- inline TShellCommandOptions& SetUseShell(bool useShell) {
- UseShell = useShell;
- if (!useShell)
- QuoteArguments = false;
- return *this;
- }
-
- /**
+ * @brief set if command should be interpreted by OS shell (/bin/sh or cmd.exe)
+ * shell is enabled by default
+ * call SetUseShell(false) for command to be sent to OS verbatim
+ * @note shell operators > < | && || will not work if this option is off
+ * @param useShell if command should be run in shell
+ * @return self
+ */
+ inline TShellCommandOptions& SetUseShell(bool useShell) {
+ UseShell = useShell;
+ if (!useShell)
+ QuoteArguments = false;
+ return *this;
+ }
+
+ /**
* @brief set if the arguments should be wrapped in quotes.
* Please, note that this option makes no difference between
* real arguments and shell syntax, so if you execute something
@@ -214,28 +214,28 @@ public:
* which will never end successfully.
* By default, this option is turned on.
*
- * @note arguments will only be quoted if shell is used
+ * @note arguments will only be quoted if shell is used
* @param quote if the arguments should be quoted
*
* @return self
*/
- inline TShellCommandOptions& SetQuoteArguments(bool quote) {
+ inline TShellCommandOptions& SetQuoteArguments(bool quote) {
QuoteArguments = quote;
return *this;
}
- /**
- * @brief set to run command in new session
- * @note set this option to off to deliver parent's signals to command as well
- * @note currently ignored on windows
- * @param detach if command should be run in new session
- * @return self
- */
- inline TShellCommandOptions& SetDetachSession(bool detach) {
- DetachSession = detach;
- return *this;
- }
-
+ /**
+ * @brief set to run command in new session
+ * @note set this option to off to deliver parent's signals to command as well
+ * @note currently ignored on windows
+ * @param detach if command should be run in new session
+ * @return self
+ */
+ inline TShellCommandOptions& SetDetachSession(bool detach) {
+ DetachSession = detach;
+ return *this;
+ }
+
/**
* @brief specifies pure function to be called in the child process after fork, before calling execve
* @note currently ignored on windows
@@ -295,7 +295,7 @@ public:
return *this;
}
-public:
+public:
bool ClearSignalMask = false;
bool CloseAllFdsOnExec = false;
bool AsyncMode = false;
@@ -309,10 +309,10 @@ public:
EHandleMode OutputMode = HANDLE_STREAM;
EHandleMode ErrorMode = HANDLE_STREAM;
- /// @todo more options
- // bool SearchPath // search exe name in $PATH
- // bool UnicodeConsole
- // bool EmulateConsole // provide isatty == true
+ /// @todo more options
+ // bool SearchPath // search exe name in $PATH
+ // bool UnicodeConsole
+ // bool EmulateConsole // provide isatty == true
/// @todo command's stdin should be exposet as IOutputStream to support dialogue
IInputStream* InputStream;
IOutputStream* OutputStream;
@@ -320,8 +320,8 @@ public:
TUserOptions User;
THashMap<TString, TString> Environment;
int Nice = 0;
-
- static const size_t DefaultSyncPollDelay = 1000; // ms
+
+ static const size_t DefaultSyncPollDelay = 1000; // ms
std::function<void()> FuncAfterFork = {};
};
@@ -349,7 +349,7 @@ public:
* @param cmd binary name
* @param args arguments list
* @param options execution options
- * @todo store entire options structure
+ * @todo store entire options structure
*/
TShellCommand(const TStringBuf cmd, const TList<TString>& args, const TShellCommandOptions& options = TShellCommandOptions(),
const TString& workdir = TString());
@@ -440,7 +440,7 @@ public:
*
* @return self
*/
- TShellCommand& Run();
+ TShellCommand& Run();
/**
* @brief terminate the execution
@@ -448,14 +448,14 @@ public:
*
* @return self
*/
- TShellCommand& Terminate();
+ TShellCommand& Terminate();
/**
* @brief wait until the execution is finished
*
* @return self
*/
- TShellCommand& Wait();
+ TShellCommand& Wait();
/**
* @brief close process' stdin
diff --git a/util/system/shellcommand_ut.cpp b/util/system/shellcommand_ut.cpp
index 9d849279d2..5daa23e29f 100644
--- a/util/system/shellcommand_ut.cpp
+++ b/util/system/shellcommand_ut.cpp
@@ -17,16 +17,16 @@
#include <util/string/strip.h>
#include <util/folder/tempdir.h>
-#if defined(_win_)
+#if defined(_win_)
#define NL "\r\n"
const char catCommand[] = "sort"; // not really cat but ok
const size_t textSize = 1;
-#else
+#else
#define NL "\n"
const char catCommand[] = "/bin/cat";
const size_t textSize = 20000;
-#endif
-
+#endif
+
class TGuardedStringStream: public IInputStream, public IOutputStream {
public:
TGuardedStringStream() {
@@ -72,18 +72,18 @@ Y_UNIT_TEST_SUITE(TShellQuoteTest) {
Y_UNIT_TEST_SUITE(TShellCommandTest) {
Y_UNIT_TEST(TestNoQuotes) {
- TShellCommandOptions options;
- options.SetQuoteArguments(false);
- TShellCommand cmd("echo hello");
- cmd.Run();
- UNIT_ASSERT_VALUES_EQUAL(cmd.GetError(), "");
- UNIT_ASSERT_VALUES_EQUAL(cmd.GetOutput(), "hello" NL);
- UNIT_ASSERT(TShellCommand::SHELL_FINISHED == cmd.GetStatus());
+ TShellCommandOptions options;
+ options.SetQuoteArguments(false);
+ TShellCommand cmd("echo hello");
+ cmd.Run();
+ UNIT_ASSERT_VALUES_EQUAL(cmd.GetError(), "");
+ UNIT_ASSERT_VALUES_EQUAL(cmd.GetOutput(), "hello" NL);
+ UNIT_ASSERT(TShellCommand::SHELL_FINISHED == cmd.GetStatus());
UNIT_ASSERT(cmd.GetExitCode().Defined() && 0 == cmd.GetExitCode());
UNIT_ASSERT_VALUES_EQUAL(cmd.GetQuotedCommand(), "echo hello");
- }
-
+ }
+
Y_UNIT_TEST(TestOnlyNecessaryQuotes) {
TShellCommandOptions options;
options.SetQuoteArguments(true);
@@ -98,75 +98,75 @@ Y_UNIT_TEST_SUITE(TShellCommandTest) {
}
Y_UNIT_TEST(TestRun) {
- TShellCommand cmd("echo");
- cmd << "hello";
- cmd.Run();
- UNIT_ASSERT_VALUES_EQUAL(cmd.GetError(), "");
-#if defined(_win_)
- UNIT_ASSERT_VALUES_EQUAL(cmd.GetOutput(), "\"hello\"\r\n");
-#else
- UNIT_ASSERT_VALUES_EQUAL(cmd.GetOutput(), "hello\n");
-#endif
- UNIT_ASSERT(TShellCommand::SHELL_FINISHED == cmd.GetStatus());
+ TShellCommand cmd("echo");
+ cmd << "hello";
+ cmd.Run();
+ UNIT_ASSERT_VALUES_EQUAL(cmd.GetError(), "");
+#if defined(_win_)
+ UNIT_ASSERT_VALUES_EQUAL(cmd.GetOutput(), "\"hello\"\r\n");
+#else
+ UNIT_ASSERT_VALUES_EQUAL(cmd.GetOutput(), "hello\n");
+#endif
+ UNIT_ASSERT(TShellCommand::SHELL_FINISHED == cmd.GetStatus());
UNIT_ASSERT(cmd.GetExitCode().Defined() && 0 == cmd.GetExitCode());
- }
- // running with no shell is not implemented for win
- // there should be no problem with it as long as SearchPath is on
+ }
+ // running with no shell is not implemented for win
+ // there should be no problem with it as long as SearchPath is on
Y_UNIT_TEST(TestNoShell) {
-#if defined(_win_)
- const char dir[] = "dir";
-#else
- const char dir[] = "ls";
-#endif
-
+#if defined(_win_)
+ const char dir[] = "dir";
+#else
+ const char dir[] = "ls";
+#endif
+
TShellCommandOptions options;
- options.SetQuoteArguments(false);
-
- {
- options.SetUseShell(false);
- TShellCommand cmd(dir, options);
+ options.SetQuoteArguments(false);
+
+ {
+ options.SetUseShell(false);
+ TShellCommand cmd(dir, options);
cmd << "|"
<< "sort";
-
- cmd.Run();
- UNIT_ASSERT(TShellCommand::SHELL_ERROR == cmd.GetStatus());
+
+ cmd.Run();
+ UNIT_ASSERT(TShellCommand::SHELL_ERROR == cmd.GetStatus());
UNIT_ASSERT(cmd.GetExitCode().Defined() && 0 != cmd.GetExitCode());
- }
- {
- options.SetUseShell(true);
- TShellCommand cmd(dir, options);
+ }
+ {
+ options.SetUseShell(true);
+ TShellCommand cmd(dir, options);
cmd << "|"
<< "sort";
- cmd.Run();
- UNIT_ASSERT(TShellCommand::SHELL_FINISHED == cmd.GetStatus());
+ cmd.Run();
+ UNIT_ASSERT(TShellCommand::SHELL_FINISHED == cmd.GetStatus());
UNIT_ASSERT_VALUES_EQUAL(cmd.GetError().size(), 0u);
UNIT_ASSERT(cmd.GetExitCode().Defined() && 0 == cmd.GetExitCode());
- }
- }
+ }
+ }
Y_UNIT_TEST(TestAsyncRun) {
- TShellCommandOptions options;
+ TShellCommandOptions options;
options.SetAsync(true);
-#if defined(_win_)
- // fails with weird error "Input redirection is not supported"
- // TShellCommand cmd("sleep", options);
- // cmd << "3";
- TShellCommand cmd("ping 1.1.1.1 -n 1 -w 2000", options);
-#else
- TShellCommand cmd("sleep", options);
- cmd << "2";
-#endif
+#if defined(_win_)
+ // fails with weird error "Input redirection is not supported"
+ // TShellCommand cmd("sleep", options);
+ // cmd << "3";
+ TShellCommand cmd("ping 1.1.1.1 -n 1 -w 2000", options);
+#else
+ TShellCommand cmd("sleep", options);
+ cmd << "2";
+#endif
UNIT_ASSERT(TShellCommand::SHELL_NONE == cmd.GetStatus());
cmd.Run();
sleep(1);
UNIT_ASSERT(TShellCommand::SHELL_RUNNING == cmd.GetStatus());
cmd.Wait();
- UNIT_ASSERT(TShellCommand::SHELL_RUNNING != cmd.GetStatus());
- UNIT_ASSERT_VALUES_EQUAL(cmd.GetError(), "");
-#if !defined(_win_)
+ UNIT_ASSERT(TShellCommand::SHELL_RUNNING != cmd.GetStatus());
+ UNIT_ASSERT_VALUES_EQUAL(cmd.GetError(), "");
+#if !defined(_win_)
UNIT_ASSERT(TShellCommand::SHELL_FINISHED == cmd.GetStatus());
UNIT_ASSERT_VALUES_EQUAL(cmd.GetOutput().size(), 0u);
UNIT_ASSERT(cmd.GetExitCode().Defined() && 0 == cmd.GetExitCode());
-#endif
+#endif
}
Y_UNIT_TEST(TestQuotes) {
TShellCommandOptions options;
@@ -174,15 +174,15 @@ Y_UNIT_TEST_SUITE(TShellCommandTest) {
TString output;
TStringOutput outputStream(output);
options.SetOutputStream(&outputStream);
- TShellCommand cmd("echo", options);
+ TShellCommand cmd("echo", options);
cmd << input;
cmd.Run().Wait();
output = StripString(output);
-#if defined(_win_)
- UNIT_ASSERT_VALUES_EQUAL("\"a\\\"a a\"", output);
-#else
+#if defined(_win_)
+ UNIT_ASSERT_VALUES_EQUAL("\"a\\\"a a\"", output);
+#else
UNIT_ASSERT_VALUES_EQUAL(input, output);
-#endif
+#endif
UNIT_ASSERT_VALUES_EQUAL(cmd.GetError().size(), 0u);
}
Y_UNIT_TEST(TestRunNonexistent) {
@@ -199,13 +199,13 @@ Y_UNIT_TEST_SUITE(TShellCommandTest) {
UNIT_ASSERT_VALUES_UNEQUAL(cmd.GetError().size(), 0u);
UNIT_ASSERT(cmd.GetExitCode().Defined() && 2 == cmd.GetExitCode());
}
- // 'type con' and 'copy con con' want real console, not stdin, use sort
+ // 'type con' and 'copy con con' want real console, not stdin, use sort
Y_UNIT_TEST(TestInput) {
TShellCommandOptions options;
TString input = (TString("a") * 2000).append(NL) * textSize;
TStringInput inputStream(input);
options.SetInputStream(&inputStream);
- TShellCommand cmd(catCommand, options);
+ TShellCommand cmd(catCommand, options);
cmd.Run().Wait();
UNIT_ASSERT_VALUES_EQUAL(input, cmd.GetOutput());
UNIT_ASSERT_VALUES_EQUAL(cmd.GetError().size(), 0u);
@@ -218,28 +218,28 @@ Y_UNIT_TEST_SUITE(TShellCommandTest) {
TString output;
TStringOutput outputStream(output);
options.SetOutputStream(&outputStream);
- TShellCommand cmd(catCommand, options);
+ TShellCommand cmd(catCommand, options);
cmd.Run().Wait();
UNIT_ASSERT_VALUES_EQUAL(input, output);
UNIT_ASSERT_VALUES_EQUAL(cmd.GetError().size(), 0u);
}
Y_UNIT_TEST(TestIO) {
- // descriptive test: use all options
- TShellCommandOptions options;
+ // descriptive test: use all options
+ TShellCommandOptions options;
options.SetAsync(true);
- options.SetQuoteArguments(false);
- options.SetLatency(10);
+ options.SetQuoteArguments(false);
+ options.SetLatency(10);
options.SetClearSignalMask(true);
- options.SetCloseAllFdsOnExec(true);
+ options.SetCloseAllFdsOnExec(true);
options.SetCloseInput(false);
TGuardedStringStream write;
- options.SetInputStream(&write);
+ options.SetInputStream(&write);
TGuardedStringStream read;
- options.SetOutputStream(&read);
- options.SetUseShell(true);
-
+ options.SetOutputStream(&read);
+ options.SetUseShell(true);
+
TShellCommand cmd("cat", options);
- cmd.Run();
+ cmd.Run();
write << "alpha" << NL;
while (read.Str() != "alpha" NL) {
@@ -252,50 +252,50 @@ Y_UNIT_TEST_SUITE(TShellCommandTest) {
}
write << "zeta" << NL;
- cmd.CloseInput();
- cmd.Wait();
-
- UNIT_ASSERT_VALUES_EQUAL(cmd.GetError(), "");
- UNIT_ASSERT(TShellCommand::SHELL_FINISHED == cmd.GetStatus());
- UNIT_ASSERT_VALUES_EQUAL(read.Str(), "alpha" NL "omega" NL "zeta" NL);
+ cmd.CloseInput();
+ cmd.Wait();
+
+ UNIT_ASSERT_VALUES_EQUAL(cmd.GetError(), "");
+ UNIT_ASSERT(TShellCommand::SHELL_FINISHED == cmd.GetStatus());
+ UNIT_ASSERT_VALUES_EQUAL(read.Str(), "alpha" NL "omega" NL "zeta" NL);
UNIT_ASSERT(cmd.GetExitCode().Defined() && 0 == cmd.GetExitCode());
- }
+ }
Y_UNIT_TEST(TestStreamClose) {
struct TStream: public IOutputStream {
- size_t NumCloses = 0;
+ size_t NumCloses = 0;
void DoWrite(const void* buf, size_t len) override {
Y_UNUSED(buf);
Y_UNUSED(len);
}
void DoFinish() override {
- ++NumCloses;
- }
- } stream;
-
- auto options1 = TShellCommandOptions().SetCloseStreams(false).SetOutputStream(&stream).SetErrorStream(&stream);
- TShellCommand("echo hello", options1).Run().Wait();
- UNIT_ASSERT_VALUES_EQUAL(stream.NumCloses, 0);
-
- auto options = TShellCommandOptions().SetCloseStreams(true).SetOutputStream(&stream).SetErrorStream(&stream);
- TShellCommand("echo hello", options).Run().Wait();
+ ++NumCloses;
+ }
+ } stream;
+
+ auto options1 = TShellCommandOptions().SetCloseStreams(false).SetOutputStream(&stream).SetErrorStream(&stream);
+ TShellCommand("echo hello", options1).Run().Wait();
+ UNIT_ASSERT_VALUES_EQUAL(stream.NumCloses, 0);
+
+ auto options = TShellCommandOptions().SetCloseStreams(true).SetOutputStream(&stream).SetErrorStream(&stream);
+ TShellCommand("echo hello", options).Run().Wait();
UNIT_ASSERT_VALUES_EQUAL(stream.NumCloses, 2);
- }
+ }
Y_UNIT_TEST(TestInterruptSimple) {
- TShellCommandOptions options;
- options.SetAsync(true);
+ TShellCommandOptions options;
+ options.SetAsync(true);
options.SetCloseInput(false);
TGuardedStringStream write;
options.SetInputStream(&write); // set input stream that will be waited by cat
- TShellCommand cmd(catCommand, options);
- cmd.Run();
+ TShellCommand cmd(catCommand, options);
+ cmd.Run();
sleep(1);
- UNIT_ASSERT(TShellCommand::SHELL_RUNNING == cmd.GetStatus());
- cmd.Terminate();
- cmd.Wait();
- UNIT_ASSERT(TShellCommand::SHELL_RUNNING != cmd.GetStatus());
- }
-#if !defined(_win_)
- // this ut is unix-only, port to win using %TEMP%
+ UNIT_ASSERT(TShellCommand::SHELL_RUNNING == cmd.GetStatus());
+ cmd.Terminate();
+ cmd.Wait();
+ UNIT_ASSERT(TShellCommand::SHELL_RUNNING != cmd.GetStatus());
+ }
+#if !defined(_win_)
+ // this ut is unix-only, port to win using %TEMP%
Y_UNIT_TEST(TestInterrupt) {
TString tmpfile = TString("shellcommand_ut.interrupt.") + ToString(RandomNumber<ui32>());
@@ -379,7 +379,7 @@ Y_UNIT_TEST_SUITE(TShellCommandTest) {
UNIT_ASSERT(TShellCommand::SHELL_FINISHED == cmd.GetStatus());
UNIT_ASSERT(cmd.GetExitCode().Defined() && 0 == cmd.GetExitCode());
}
-#endif
+#endif
Y_UNIT_TEST(TestInternalError) {
TString input = (TString("a") * 2000).append("\n");
TStringInput inputStream(input);
@@ -387,7 +387,7 @@ Y_UNIT_TEST_SUITE(TShellCommandTest) {
TShellCommandOptions options;
options.SetInputStream(&inputStream);
options.SetOutputStream(&outputStream);
- TShellCommand cmd(catCommand, options);
+ TShellCommand cmd(catCommand, options);
cmd.Run().Wait();
UNIT_ASSERT(TShellCommand::SHELL_INTERNAL_ERROR == cmd.GetStatus());
UNIT_ASSERT_VALUES_UNEQUAL(cmd.GetInternalError().size(), 0u);