Skip to content

Commit

Permalink
[TShellCommand] Add option to inherit stdout/stderr handles in child …
Browse files Browse the repository at this point in the history
…process

This is needed to launch interactive commands from a TTY

([arc::pullid] 94605624-9e298880-caff20ce-256e469a)

ref:ad082051b4b8e7aca990097589be102c1b732551
  • Loading branch information
mityada committed Mar 20, 2019
1 parent f4de8a0 commit 892cc60
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 20 deletions.
76 changes: 56 additions & 20 deletions util/system/shellcommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,8 @@ class TShellCommand::TImpl
bool DetachSession;
bool CloseStreams;
TAtomic ShouldCloseInput;
bool InheritOutput;
bool InheritError;
TShellCommandOptions::TUserOptions User;
THashMap<TString, TString> Environment;
int Nice;
Expand All @@ -213,12 +215,20 @@ class TShellCommand::TImpl
TRealPipeHandle InputPipeFd[2];
// pipes are closed by automatic dtor
void PrepareParents() {
OutputPipeFd[1].Close();
ErrorPipeFd[1].Close();
if (OutputPipeFd[1].IsOpen()) {
OutputPipeFd[1].Close();
}
if (ErrorPipeFd[1].IsOpen()) {
ErrorPipeFd[1].Close();
}
#if defined(_unix_)
// not really needed, io is done via poll
SetNonBlock(OutputPipeFd[0]);
SetNonBlock(ErrorPipeFd[0]);
if (OutputPipeFd[0].IsOpen()) {
SetNonBlock(OutputPipeFd[0]);
}
if (ErrorPipeFd[0].IsOpen()) {
SetNonBlock(ErrorPipeFd[0]);
}
if (InputPipeFd[1].IsOpen())
SetNonBlock(InputPipeFd[1]);
#endif
Expand Down Expand Up @@ -269,6 +279,8 @@ class TShellCommand::TImpl
, DetachSession(options.DetachSession)
, CloseStreams(options.CloseStreams)
, ShouldCloseInput(options.ShouldCloseInput)
, InheritOutput(options.InheritOutput)
, InheritError(options.InheritError)
, User(options.User)
, Environment(options.Environment)
, Nice(options.Nice)
Expand Down Expand Up @@ -459,15 +471,31 @@ void TShellCommand::TImpl::StartProcess(TShellCommand::TImpl::TPipes& pipes) {
startup_info.cb = sizeof(startup_info);
startup_info.dwFlags = STARTF_USESTDHANDLES;

if (!SetHandleInformation(pipes.OutputPipeFd[1], HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT) || !SetHandleInformation(pipes.ErrorPipeFd[1], HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT))
ythrow TSystemError() << "cannot set handle info";
if (!InheritOutput) {
if (!SetHandleInformation(pipes.OutputPipeFd[1], HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)) {
ythrow TSystemError() << "cannot set handle info";
}
}
if (!InheritError) {
if (!SetHandleInformation(pipes.ErrorPipeFd[1], HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)) {
ythrow TSystemError() << "cannot set handle info";
}
}
if (InputStream)
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
startup_info.hStdOutput = pipes.OutputPipeFd[1];
startup_info.hStdError = pipes.ErrorPipeFd[1];
if (!InheritOutput) {
startup_info.hStdOutput = pipes.OutputPipeFd[1];
} else {
startup_info.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
}
if (!InheritError) {
startup_info.hStdError = pipes.ErrorPipeFd[1];
} else {
startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
}
if (InputStream)
startup_info.hStdInput = pipes.InputPipeFd[0];
else
Expand Down Expand Up @@ -607,8 +635,6 @@ void TShellCommand::TImpl::OnFork(TPipes& pipes, sigset_t oldmask, char* const*
ythrow TSystemError() << "Cannot " << (ClearSignalMask ? "clear" : "restore") << " signal mask in child";
}

pipes.OutputPipeFd[0].Close();
pipes.ErrorPipeFd[0].Close();
TFileHandle sIn(0);
TFileHandle sOut(1);
TFileHandle sErr(2);
Expand All @@ -622,14 +648,20 @@ void TShellCommand::TImpl::OnFork(TPipes& pipes, sigset_t oldmask, char* const*
// do not close fd 0 - next open will return it and confuse all readers
/// @todo in case of real need - reopen /dev/null
}
TFileHandle sOutNew(pipes.OutputPipeFd[1]);
sOut.LinkTo(sOutNew);
sOut.Release();
sOutNew.Release();
TFileHandle sErrNew(pipes.ErrorPipeFd[1]);
sErr.LinkTo(sErrNew);
sErr.Release();
sErrNew.Release();
if (!InheritOutput) {
pipes.OutputPipeFd[0].Close();
TFileHandle sOutNew(pipes.OutputPipeFd[1]);
sOut.LinkTo(sOutNew);
sOut.Release();
sOutNew.Release();
}
if (!InheritError) {
pipes.ErrorPipeFd[0].Close();
TFileHandle sErrNew(pipes.ErrorPipeFd[1]);
sErr.LinkTo(sErrNew);
sErr.Release();
sErrNew.Release();
}

if (WorkDir.size())
NFs::SetCurrentWorkingDirectory(WorkDir);
Expand Down Expand Up @@ -672,8 +704,12 @@ void TShellCommand::TImpl::Run() {
CollectedError.clear();
TPipes pipes;

TRealPipeHandle::Pipe(pipes.OutputPipeFd[0], pipes.OutputPipeFd[1]);
TRealPipeHandle::Pipe(pipes.ErrorPipeFd[0], pipes.ErrorPipeFd[1]);
if (!InheritOutput) {
TRealPipeHandle::Pipe(pipes.OutputPipeFd[0], pipes.OutputPipeFd[1]);
}
if (!InheritError) {
TRealPipeHandle::Pipe(pipes.ErrorPipeFd[0], pipes.ErrorPipeFd[1]);
}
if (InputStream) {
TRealPipeHandle::Pipe(pipes.InputPipeFd[0], pipes.InputPipeFd[1]);
}
Expand Down
28 changes: 28 additions & 0 deletions util/system/shellcommand.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ class TShellCommandOptions {
, DetachSession(true)
, CloseStreams(false)
, ShouldCloseInput(true)
, InheritOutput(false)
, InheritError(false)
, InputStream(nullptr)
, OutputStream(nullptr)
, ErrorStream(nullptr)
Expand Down Expand Up @@ -212,6 +214,30 @@ class TShellCommandOptions {
return *this;
}

/**
* @brief set if child should inherit output handle
*
* @param inherit if child should inherit output handle
*
* @return self
*/
inline TShellCommandOptions& SetInheritOutput(bool inherit) {
InheritOutput = inherit;
return *this;
}

/**
* @brief set if child should inherit stderr handle
*
* @param inherit if child should inherit error output handle
*
* @return self
*/
inline TShellCommandOptions& SetInheritError(bool inherit) {
InheritError = inherit;
return *this;
}

public:
bool ClearSignalMask;
bool CloseAllFdsOnExec;
Expand All @@ -222,6 +248,8 @@ class TShellCommandOptions {
bool DetachSession;
bool CloseStreams;
bool ShouldCloseInput;
bool InheritOutput;
bool InheritError;
/// @todo more options
// bool SearchPath // search exe name in $PATH
// bool UnicodeConsole
Expand Down

0 comments on commit 892cc60

Please sign in to comment.