diff --git a/CodeLite/AsyncProcess/asyncprocess.h b/CodeLite/AsyncProcess/asyncprocess.h index 01e2c41d4a..4fdc2388f8 100644 --- a/CodeLite/AsyncProcess/asyncprocess.h +++ b/CodeLite/AsyncProcess/asyncprocess.h @@ -56,6 +56,7 @@ enum IProcessCreateFlags { IProcessInteractiveSSH = (1 << 9), IProcessWrapInShell = (1 << 10), // wrap the command in the OS shell (CMD, BASH) IProcessPseudoConsole = (1 << 11), // MSW only: use CreatePseudoConsole API for creating the process + IProcessNoPty = (1 << 12), // Unix only: do not use forkpty, use normal fork() }; class WXDLLIMPEXP_CL IProcess; diff --git a/CodeLite/AsyncProcess/unixprocess_impl.cpp b/CodeLite/AsyncProcess/unixprocess_impl.cpp index 65a1474cc3..ef1d9aca26 100644 --- a/CodeLite/AsyncProcess/unixprocess_impl.cpp +++ b/CodeLite/AsyncProcess/unixprocess_impl.cpp @@ -107,8 +107,8 @@ extern char* strdup(); /* Duplicate a string */ static void freeargv(char** vector) { char** scan; - if(vector != NULL) { - for(scan = vector; *scan != NULL; scan++) { + if (vector != NULL) { + for (scan = vector; *scan != NULL; scan++) { free(*scan); } delete[] vector; @@ -120,7 +120,7 @@ static void freeargv(char** vector) static char** make_argv(const wxArrayString& args, int& argc) { char** argv = new char*[args.size() + 1]; - for(size_t i = 0; i < args.size(); ++i) { + for (size_t i = 0; i < args.size(); ++i) { argv[i] = strdup(args[i].mb_str(wxConvUTF8).data()); } argv[args.size()] = 0; @@ -141,17 +141,18 @@ void UnixProcessImpl::Cleanup() { close(GetReadHandle()); close(GetWriteHandle()); - if(GetStderrHandle() != wxNOT_FOUND) { + if (GetStderrHandle() != wxNOT_FOUND) { close(GetStderrHandle()); } - if(m_thr) { + + if (m_thr) { // Stop the reader thread m_thr->Stop(); delete m_thr; } m_thr = NULL; - if(GetPid() != wxNOT_FOUND) { + if (GetPid() != wxNOT_FOUND) { wxKill(GetPid(), GetHardKill() ? wxSIGKILL : wxSIGTERM, NULL, wxKILL_CHILDREN); // The Zombie cleanup is done in app.cpp in ::ChildTerminatedSingalHandler() signal handler int status(0); @@ -163,28 +164,28 @@ bool UnixProcessImpl::IsAlive() { return kill(m_pid, 0) == 0; } bool UnixProcessImpl::ReadFromFd(int fd, fd_set& rset, wxString& output, std::string& raw_output) { - if(fd == wxNOT_FOUND) { + if (fd == wxNOT_FOUND) { return false; } - if(FD_ISSET(fd, &rset)) { + if (FD_ISSET(fd, &rset)) { // there is something to read char buffer[BUFF_SIZE + 1]; // our read buffer int bytesRead = read(fd, buffer, sizeof(buffer)); - if(bytesRead > 0) { + if (bytesRead > 0) { buffer[bytesRead] = 0; // always place a terminator raw_output = std::string(buffer, bytesRead); // Remove coloring chars from the incomnig buffer // colors are marked with ESC and terminates with lower case 'm' - if(!(this->m_flags & IProcessRawOutput)) { + if (!(this->m_flags & IProcessRawOutput)) { std::string stripped_buffer; StringUtils::StripTerminalColouring(raw_output, stripped_buffer); raw_output.swap(stripped_buffer); } wxString convBuff = wxString(raw_output.c_str(), wxConvUTF8, raw_output.length()); - if(convBuff.empty()) { + if (convBuff.empty()) { convBuff = wxString::From8BitData(raw_output.c_str(), raw_output.length()); } @@ -202,7 +203,7 @@ bool UnixProcessImpl::Read(wxString& buff, wxString& buffErr, std::string& raw_b memset(&rs, 0, sizeof(rs)); FD_SET(GetReadHandle(), &rs); - if(m_stderrHandle != wxNOT_FOUND) { + if (m_stderrHandle != wxNOT_FOUND) { FD_SET(m_stderrHandle, &rs); } @@ -216,11 +217,11 @@ bool UnixProcessImpl::Read(wxString& buff, wxString& buffErr, std::string& raw_b int maxFd = wxMax(GetStderrHandle(), GetReadHandle()); int rc = select(maxFd + 1, &rs, NULL, NULL, &timeout); errCode = errno; - if(rc == 0) { + if (rc == 0) { // timeout return true; - } else if(rc > 0) { + } else if (rc > 0) { // We differentiate between stdout and stderr? bool stderrRead = ReadFromFd(GetStderrHandle(), rs, buffErr, raw_buffErr); bool stdoutRead = ReadFromFd(GetReadHandle(), rs, buff, raw_buff); @@ -228,7 +229,7 @@ bool UnixProcessImpl::Read(wxString& buff, wxString& buffErr, std::string& raw_b } else { - if(errCode == EINTR || errCode == EAGAIN) { + if (errCode == EINTR || errCode == EAGAIN) { return true; } @@ -253,10 +254,10 @@ bool do_write(int fd, const wxMemoryBuffer& buffer) clDEBUG1() << "do_write() buffer:" << str << endl; clDEBUG1() << "do_write() length:" << str.length() << endl; } - while(bytes_left) { + while (bytes_left) { int bytes_sent = ::write(fd, (const char*)pdata, bytes_left); LOG_IF_TRACE { clDEBUG1() << "::do_write() completed. number of bytes sent:" << bytes_sent << endl; } - if(bytes_sent <= 0) { + if (bytes_sent <= 0) { return false; } pdata += bytes_sent; @@ -279,12 +280,28 @@ bool UnixProcessImpl::WriteRaw(const std::string& buff) return do_write(GetWriteHandle(), mb); } +namespace +{ +bool create_pipe(int& read_end, int& write_end) +{ + int fds[2] = { 0, 0 }; + errno = 0; + if (::pipe(fds) < 0) { + clERROR() << "Failed to create pipe()." << strerror(errno); + return false; + } + read_end = fds[0]; + write_end = fds[1]; + return true; +} +} // namespace + IProcess* UnixProcessImpl::Execute(wxEvtHandler* parent, const wxArrayString& args, size_t flags, const wxString& workingDirectory, IProcessCallback* cb) { int argc = 0; char** argv = make_argv(args, argc); - if(argc == 0 || argv == nullptr) { + if (argc == 0 || argv == nullptr) { return nullptr; } @@ -292,7 +309,7 @@ IProcess* UnixProcessImpl::Execute(wxEvtHandler* parent, const wxArrayString& ar wxString curdir = wxGetCwd(); // Prentend that we are a terminal... - int master; + int master = wxNOT_FOUND; char pts_name[1024]; memset(pts_name, 0x0, sizeof(pts_name)); @@ -300,23 +317,33 @@ IProcess* UnixProcessImpl::Execute(wxEvtHandler* parent, const wxArrayString& ar // If successful, two file descriptors are stored in stderrPipes; // bytes written on stderrPipes[1] can be read from stderrPipes[0]. // Returns 0 if successful, -1 if not. - int stderrPipes[2] = { 0, 0 }; - if(flags & IProcessStderrEvent) { - errno = 0; - if(pipe(stderrPipes) < 0) { - clERROR() << "Failed to create pipe for stderr redirecting." << strerror(errno); - flags &= ~IProcessStderrEvent; + int stderr_read = wxNOT_FOUND, stderr_write = wxNOT_FOUND; + int stdout_read = wxNOT_FOUND, stdout_write = wxNOT_FOUND; + int stdin_read = wxNOT_FOUND, stdin_write = wxNOT_FOUND; + if (flags & (IProcessStderrEvent | IProcessNoPty)) { + if (!create_pipe(stderr_read, stderr_write)) { + return nullptr; + } + } + + int rc = 0; + if (flags & IProcessNoPty) { + if (!create_pipe(stdout_read, stdout_write) || !create_pipe(stdin_read, stdin_write)) { + return nullptr; } + rc = fork(); + } else { + rc = forkpty(&master, pts_name, nullptr, nullptr); } - int rc = forkpty(&master, pts_name, nullptr, nullptr); - if(rc == 0) { + if (rc == 0) { //===------------------------------------------------------- // Child process //===------------------------------------------------------- - for(int i = 0; i < FD_SETSIZE; ++i) { - if(i != STDERR_FILENO && i != STDOUT_FILENO && i != STDIN_FILENO) { - if((i == stderrPipes[1]) && (flags & IProcessStderrEvent)) { + for (int i = 0; i < FD_SETSIZE; ++i) { + if (i != STDERR_FILENO && i != STDOUT_FILENO && i != STDIN_FILENO) { + // Do not close the interesting pipes + if (i == stderr_write || i == stdout_write || i == stdin_read) { // skip it } else { ::close(i); @@ -324,28 +351,38 @@ IProcess* UnixProcessImpl::Execute(wxEvtHandler* parent, const wxArrayString& ar } } - // Incase the user wants to get separate events for STDERR, dup2 STDERR to the PIPE write end - // we opened earlier - if(flags & IProcessStderrEvent) { - // Dup stderrPipes[1] into stderr - dup2(stderrPipes[1], STDERR_FILENO); - close(stderrPipes[1]); + if (stderr_write != wxNOT_FOUND) { + dup2(stderr_write, STDERR_FILENO); + close(stderr_write); + } + + // Make this process as its own group + setpgid(0, 0); + + if (stdout_write != wxNOT_FOUND) { + dup2(stdout_write, STDOUT_FILENO); + close(stdout_write); + } + + if (stdin_read != wxNOT_FOUND) { + dup2(stdin_read, STDIN_FILENO); + close(stdin_read); } // at this point, slave is used as stdin/stdout/stderr // Child process - if(workingDirectory.IsEmpty() == false) { - wxSetWorkingDirectory(workingDirectory); + if (!workingDirectory.empty()) { + ::wxSetWorkingDirectory(workingDirectory); } // execute the process errno = 0; - if(execvp(argv[0], argv) < 0) { + if (execvp(argv[0], argv) < 0) { wxString errmsg = strerror(errno); clERROR() << "execvp" << args << "error:" << errmsg << endl; // print the environment as well clERROR() << "Dumping environment:" << endl; - for(char** scan = environ; *scan != nullptr; scan++) { + for (char** scan = environ; *scan != nullptr; scan++) { clERROR() << *scan << endl; } } @@ -353,7 +390,7 @@ IProcess* UnixProcessImpl::Execute(wxEvtHandler* parent, const wxArrayString& ar // if we got here, we failed... exit(1); - } else if(rc < 0) { + } else if (rc < 0) { // Error // restore the working directory @@ -365,43 +402,59 @@ IProcess* UnixProcessImpl::Execute(wxEvtHandler* parent, const wxArrayString& ar //===------------------------------------------------------- // Parent //===------------------------------------------------------- - struct termios tios; - tcgetattr(master, &tios); - tios.c_lflag &= ~(ECHO | ECHONL); - tcsetattr(master, TCSAFLUSH, &tios); + if (master != wxNOT_FOUND) { + struct termios tios; + tcgetattr(master, &tios); + tios.c_lflag &= ~(ECHO | ECHONL); + tcsetattr(master, TCSAFLUSH, &tios); + } freeargv(argv); argc = 0; // disable ECHO - struct termios termio; - tcgetattr(master, &termio); - cfmakeraw(&termio); - termio.c_lflag = ICANON | ISIG; // support signals - termio.c_lflag &= ~ECHO; - termio.c_oflag = ONOCR | ONLRET; - tcsetattr(master, TCSANOW, &termio); + if (master != wxNOT_FOUND) { + struct termios termio; + tcgetattr(master, &termio); + cfmakeraw(&termio); + termio.c_lflag = ICANON | ISIG; // support signals + termio.c_lflag &= ~ECHO; + termio.c_oflag = ONOCR | ONLRET; + tcsetattr(master, TCSANOW, &termio); + } // restore the working directory - wxSetWorkingDirectory(curdir); + ::wxSetWorkingDirectory(curdir); UnixProcessImpl* proc = new UnixProcessImpl(parent); proc->m_callback = cb; - if(flags & IProcessStderrEvent) { - close(stderrPipes[1]); // close the write end + // Set fds. If "master" is != wxNOT_FOUND, then "stdout_write" and "stdin_read" are wxNOT_FOUND + // See above code + if (stderr_read != wxNOT_FOUND) { + close(stderr_write); // set the stderr handle - proc->SetStderrHandle(stderrPipes[0]); + proc->SetStderrHandle(stderr_read); + } + + if (master != wxNOT_FOUND) { + proc->SetReadHandle(master); + proc->SetWriteHandler(master); + + } else { + close(stdout_write); + proc->SetReadHandle(stdout_read); + + close(stdin_read); + proc->SetWriteHandler(stdin_write); } - proc->SetReadHandle(master); - proc->SetWriteHandler(master); proc->SetPid(rc); proc->m_flags = flags; // Keep the creation flags // Keep the terminal name, we will need it proc->SetTty(pts_name); - if(!(proc->m_flags & IProcessCreateSync)) { + if (!(proc->m_flags & IProcessCreateSync)) { proc->StartReaderThread(); } return proc; @@ -448,7 +501,7 @@ bool UnixProcessImpl::WriteToConsole(const wxString& buff) void UnixProcessImpl::Detach() { - if(m_thr) { + if (m_thr) { // Stop the reader thread m_thr->Stop(); delete m_thr; diff --git a/CodeLite/AsyncProcess/unixprocess_impl.h b/CodeLite/AsyncProcess/unixprocess_impl.h index a8d910e35c..70bb119270 100644 --- a/CodeLite/AsyncProcess/unixprocess_impl.h +++ b/CodeLite/AsyncProcess/unixprocess_impl.h @@ -23,8 +23,7 @@ ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// -#ifndef __unixprocessimpl__ -#define __unixprocessimpl__ +#pragma once #if defined(__WXMAC__) || defined(__WXGTK__) #include "asyncprocess.h" @@ -79,4 +78,3 @@ class WXDLLIMPEXP_CL UnixProcessImpl : public IProcess void Signal(wxSignal sig) override; }; #endif // #if defined(__WXMAC )||defined(__WXGTK__) -#endif // __unixprocessimpl__ diff --git a/DebugAdapterClient/DebugAdapterClient.cpp b/DebugAdapterClient/DebugAdapterClient.cpp index 1b6fe0756e..65084b0f42 100644 --- a/DebugAdapterClient/DebugAdapterClient.cpp +++ b/DebugAdapterClient/DebugAdapterClient.cpp @@ -1155,16 +1155,16 @@ bool DebugAdapterClient::StartSocketDap() if (m_session.debug_over_ssh) { // launch ssh process auto env_list = StringUtils::BuildEnvFromString(dap_server.GetEnvironment()); - m_dap_server.reset(new DapProcess( - ::CreateAsyncProcess(this, command, IProcessCreateDefault | IProcessCreateSSH | IProcessWrapInShell, - wxEmptyString, &env_list, m_session.ssh_acount.GetAccountName()))); + m_dap_server.reset(new DapProcess(::CreateAsyncProcess( + this, command, IProcessCreateDefault | IProcessCreateSSH | IProcessWrapInShell | IProcessNoPty, + wxEmptyString, &env_list, m_session.ssh_acount.GetAccountName()))); } else { // launch local process EnvSetter env; // apply CodeLite env variables auto env_list = StringUtils::ResolveEnvList(dap_server.GetEnvironment()); m_dap_server.reset(new DapProcess(::CreateAsyncProcess( - this, command, IProcessNoRedirect | IProcessWrapInShell | IProcessCreateWithHiddenConsole, wxEmptyString, - &env_list))); + this, command, IProcessNoRedirect | IProcessWrapInShell | IProcessCreateWithHiddenConsole | IProcessNoPty, + wxEmptyString, &env_list))); } return m_dap_server->IsOk(); } @@ -1182,16 +1182,16 @@ dap::Transport* DebugAdapterClient::StartStdioDap() if (m_session.debug_over_ssh) { // launch ssh process auto env_list = StringUtils::BuildEnvFromString(dap_server.GetEnvironment()); - m_dap_server.reset(new DapProcess( - ::CreateAsyncProcess(this, command, IProcessCreateDefault | IProcessCreateSSH | IProcessWrapInShell, - wxEmptyString, &env_list, m_session.ssh_acount.GetAccountName()))); + m_dap_server.reset(new DapProcess(::CreateAsyncProcess( + this, command, IProcessCreateDefault | IProcessCreateSSH | IProcessWrapInShell | IProcessNoPty, + wxEmptyString, &env_list, m_session.ssh_acount.GetAccountName()))); } else { // launch local process EnvSetter env; // apply CodeLite env variables auto env_list = StringUtils::ResolveEnvList(dap_server.GetEnvironment()); m_dap_server.reset(new DapProcess(::CreateAsyncProcess( - this, command, IProcessWrapInShell | IProcessStderrEvent | IProcessCreateWithHiddenConsole, wxEmptyString, - &env_list))); + this, command, IProcessWrapInShell | IProcessStderrEvent | IProcessCreateWithHiddenConsole | IProcessNoPty, + wxEmptyString, &env_list))); } transport->SetProcess(m_dap_server);