popen() writes output of command executed to cout - c++

I am writing an application that needs to open another process and get its output. Everywhere I've read online says I have to use popen and read from the file.
But I can't read from it. The output of the command gets outputted into the console window of the calling application. Below is the code I am using. I added some prints to debug.
#include <string>
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <array>
int main()
{
// some command that fails to execute properly.
std::string command("ls afskfksakfafkas");
std::array<char, 128> buffer;
std::string result;
std::cout << "Opening reading pipe" << std::endl;
FILE* pipe = popen(command.c_str(), "r");
if (!pipe)
{
std::cerr << "Couldn't start command." << std::endl;
return 0;
}
while (fgets(buffer.data(), 128, pipe) != NULL) {
std::cout << "Reading..." << std::endl;
result += buffer.data();
}
auto returnCode = pclose(pipe);
std::cout << result << std::endl;
std::cout << returnCode << std::endl;
return 0;
}
Reading is never actually printed to my cout and result is an empty string. I clearly see the output of the command in my terminal. If the command exits gracefully the behaviour is as expected. But I only capture the output for error cases.

Popen doesn't capture stderr only stdout. Redirecting stderr to stdout fixes the issue.
#include <string>
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <array>
int main()
{
std::string command("ls afskfksakfafkas 2>&1");
std::array<char, 128> buffer;
std::string result;
std::cout << "Opening reading pipe" << std::endl;
FILE* pipe = popen(command.c_str(), "r");
if (!pipe)
{
std::cerr << "Couldn't start command." << std::endl;
return 0;
}
while (fgets(buffer.data(), 128, pipe) != NULL) {
std::cout << "Reading..." << std::endl;
result += buffer.data();
}
auto returnCode = pclose(pipe);
std::cout << result << std::endl;
std::cout << returnCode << std::endl;
return 0;
}

You have to add "2>&1" at the end of command string
command.append(" 2>&1");
there is a full example https://www.jeremymorgan.com/tutorials/c-programming/how-to-capture-the-output-of-a-linux-command-in-c/

Related

Switch user at run time when program runs as root

If some c++ program runs as root and i want to execute some of the commands with different user and read the output of that command and again switch back to root user so can someone guide me how to achieve that in linux OS & c++ ?
Below is the reference code i wrote. Can someone guide me which is correct or not ?
#include <iostream>
#include <stdio.h>
#include <string.h>
std::wstring PopenRead(const std::wstring &cmd)
{
std::wstring res;
std::string s_cmd(cmd.begin(), cmd.end());
FILE *f = popen((const char *)s_cmd.c_str(), "r");
if (f)
{
char buffer[1024];
int cnt;
int rc;
while ((cnt = fread(buffer, 1, 1024, f)) > 0)
{
buffer[cnt] = 0;
std::string s_val = std::string(buffer);
std::wstring wsTmp(s_val.begin(), s_val.end());
res += wsTmp;
}
rc = pclose(f);
std::wcout << "Output is: " << res << std::endl;
return res;
}
return L"";
}
int main()
{
std::wstring command = L"su test_user -c 'ls -ltr /home/test_user'";
std::wstring exec_res = PopenRead(command);
return 0;
}
I tried to do it using popen and sudo command as I'm using Ubuntu 18.04 LTS
Please find below code in C++
#include <string>
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <array>
int main()
{
//To switch user as user
std::string switch_user_command("echo 'userpassword' | sudo -u user 2>&1");
std::array<char, 128> buffer;
std::string result;
std::cout << "Opening reading pipe" << std::endl;
FILE* pipe;
pipe = popen(switch_user_command.c_str(), "r");
if (!pipe)
{
std::cerr << "Couldn't start command." << std::endl;
return 0;
}
while (fgets(buffer.data(), 128, pipe) != NULL) {
//std::cout << "Reading..." << std::endl;
result += buffer.data();
}
auto returnCode1 = pclose(pipe);
std::cout << result << std::endl;
std::cout << returnCode1 << std::endl;
result.clear();
//To run ls command
std::string command("ls 2>&1");
pipe = popen(command.c_str(), "r");
if (!pipe)
{
std::cerr << "Couldn't start command." << std::endl;
return 0;
}
while (fgets(buffer.data(), 128, pipe) != NULL) {
//std::cout << "Reading..." << std::endl;
result += buffer.data();
}
auto returnCode2 = pclose(pipe);
std::cout << result << std::endl;
std::cout << returnCode2 << std::endl;
//To run command as root/sudo
result.clear();
std::cout << "Trying to run ls as sudo .. " << std::endl;
std::string switch_root_command("echo 'sudopassword' | sudo -S ls 2>&1");
pipe = popen(switch_root_command.c_str(), "r");
if (!pipe)
{
std::cerr << "Couldn't start command." << std::endl;
return 0;
}
while (fgets(buffer.data(), 128, pipe) != NULL) {
//std::cout << "Reading..." << std::endl;
result += buffer.data();
}
auto returnCode3 = pclose(pipe);
std::cout << result << std::endl;
std::cout << returnCode3 << std::endl;
return 0;
}
Please use below command to compile in g++
g++ prog.cpp -o prog -std=c++11
This produces below output (as I don't have any 'user' account)
Opening reading pipe
sudo: unknown user: user
sudo: unable to initialize policy plugin
256
1.cpp
2
2.cpp
0
Trying to run ls as sudo ..
1.cpp
2
2.cpp
0
abhi
0
I hope it helps!

Cannot open text file using ifstream

ifstream fin;
fin.open("‪C:\\Users\\Zach\\Desktop\\input.txt");
if (!fin)
{
cout << "e";
}
e is printing whether I use the full pathway or just input.txt from a resource file
If the file exists, make sure that you have got the path specified correctly. Since you're running on Windows, you can verify the full path to your executable with the following code.
#include <iostream>
#include <fstream>
#include <string>
#include <windows.h>
#define BUFSIZE 4096
std::string getExePath()
{
char result[BUFSIZE];
return std::string(result, GetModuleFileName(NULL, result, BUFSIZE));
}
int main()
{
std::ifstream infile("input.txt");
if (infile.is_open())
{
std::cout << "Success!" << std::endl;
infile.close();
}
else
{
std::cout << "Failed to open input.txt!" << std::endl;
std::cout << "Executable path is ->" << getExePath() << "<-" << std::endl;
}
return 0;
}
This will allow you to verify that your path to the input file is correct, assuming that it's collocated with your executable.
You need to direct output into the ifstream object by using fin << "string"; and not directing to standard out via cout.

Calling external program using boost::process causes caller to hang (Linux)

I am using boost::process to call an external program - the external program reads input via stdin, and writes to stdout and stderr. The external program is as follows (expects a single argument - the path to a file for debugging)
#include <fstream>
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>
int main(int argc, char** argv)
{
try
{
if (argc != 2)
{
throw std::logic_error("Expected two arguments");
}
std::ofstream ofs(argv[1]);
std::vector<std::string> someTestInput;
ofs << "Starting program..." << std::endl;
// Read from cin
{
ofs << "Reading from cin..." << std::endl;
std::string input;
while (std::getline(std::cin, input))
{
ofs << "Received from cin: " << input << std::endl;
someTestInput.emplace_back(input);
}
ofs << "Finished receiving from cin..." << std::endl;
}
// Error if nothing has been input
if (someTestInput.empty())
{
throw std::logic_error("Expected some input and received nothing...");
}
ofs << "Writing to cout..." << std::endl;
// Write to cout
for (const auto& output : someTestInput)
{
std::cout << output << '\n';
}
ofs << "Finished!\n";
}
catch (std::exception& e)
{
std::cerr << "Error caught: " << e.what() << '\n';
return 1;
}
return 0;
}
The caller expects 2+ arguments, one of which is the path to the external program, and the rest are passed on as arguments to the external program.
It hangs while waiting for the process to exit, and it seems like the external program is waiting for an EOF from stdin.
#include <memory>
#include <vector>
#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/process.hpp>
int main(int argc, char** argv)
{
try
{
if (argc < 2)
{
throw std::logic_error("Expecting at least 2 arguments...");
}
std::vector<std::string> args;
for (int i = 1; i < argc; ++i)
{
args.emplace_back(argv[i]);
}
std::cout << "Creating stdout, stderr pipes...\n";
// Create pipes for stdout, stderr
boost::process::pipe pstdout = boost::process::create_pipe();
boost::process::pipe pstderr = boost::process::create_pipe();
std::cout << "Mapping pipes to sources...\n";
// Map pipe source from stdout and stderr to sources
boost::iostreams::file_descriptor_source sourcestdout(pstdout.source, boost::iostreams::close_handle);
boost::iostreams::file_descriptor_source sourcestderr(pstderr.source, boost::iostreams::close_handle);
std::cout << "Setting up streams for the sources...\n";
// And set up streams for the sources
boost::iostreams::stream<boost::iostreams::file_descriptor_source> istdout(sourcestdout);
boost::iostreams::stream<boost::iostreams::file_descriptor_source> istderr(sourcestderr);
std::unique_ptr<boost::process::child> p;
// Want to check for process result, but also need to ensure stdin handle is closed properly,
// so place everything in separate scope
{
std::cout << "Mapping pipes to sinks...\n";
// Map pipe sink from stdout and stderr to sinks
boost::iostreams::file_descriptor_sink sinkstdout(pstdout.sink, boost::iostreams::close_handle);
boost::iostreams::file_descriptor_sink sinkstderr(pstderr.sink, boost::iostreams::close_handle);
std::cout << "Creating stdin pipe, mapping to source and sink...\n";
boost::process::pipe pstdin = boost::process::create_pipe();
// For stdin, map pipe to source and sink as before - want it to close on exiting this scope
boost::iostreams::file_descriptor_sink sinkstdin(pstdin.sink, boost::iostreams::close_handle);
boost::iostreams::file_descriptor_source sourcestdin(pstdin.source, boost::iostreams::close_handle);
boost::iostreams::stream<boost::iostreams::file_descriptor_sink> ostdin(sinkstdin);
std::cout << "Calling process... \n";
// Call process
p = std::unique_ptr<boost::process::child>(new boost::process::child(boost::process::execute(
boost::process::initializers::set_args(args),
boost::process::initializers::throw_on_error(),
boost::process::initializers::bind_stdout(sinkstdout),
boost::process::initializers::bind_stderr(sinkstderr),
boost::process::initializers::bind_stdin(sourcestdin)
)));
std::cout << "Sending test data...\n";
// Send some test data to cin - comment out the below to test for error case
ostdin << "Test Input 1\n";
ostdin << "Some\n";
ostdin << "Useful\n";
ostdin << "Data\n";
std::cout << "Test data sent, exiting scope...\n";
}
std::cout << "Check if process has exited...\n";
// Check if process has exited OK - if not, report errors
if (boost::process::wait_for_exit(*p))
{
std::cout << "Has not exited OK, reporting problems...\n";
// Gather output from stderr
std::string error;
while (std::getline(istderr, error))
{
std::cout << "Error: " << error << '\n';
}
throw std::logic_error("Problem executing TestProgram...");
}
std::cout << "Exited OK, here is output from the callee...\n";
// Gather the output
std::string output;
while (std::getline(istdout, output))
{
std::cout << output << '\n';
}
}
catch (std::exception& e)
{
std::cerr << "Error: " << e.what() << '\n';
return 1;
}
}
I was under the impression that placing my stdin pipe and related sources/sinks within a scope will guarantee they're closed, and therefore send the EOF.
The same code works perfectly under Windows (VS2013, boost_1_53).
I am using boost_1_53, boost-process 0.5, gcc 4.8.2.
That does not happen, because there's still a pipe handle open in the child process; that is only closed on posix if you set it explicitly (on windows it is done automatically). So you'd need to add something like that:
#if defined (BOOST_POSIX_API)
fcntl(pstdout.sink, F_SETFD, FD_CLOEXEC);
fcntl(pstderr.sink, F_SETFD, FD_CLOEXEC);
#endif
I would however recommend to use boost.asio and wait asynchronously for the exit of the subprocess and close the pipes there.
Just FYI: I've worked on boost-process 0.6 which has a different interface but makes the asio stuff much easier. This will hopefully be in review in October/November, so it might become an official boost library soon. It's currently in beta so you might want to check that one out.

Redirecting stdout and stderr to a single file in c++

How to redirect stdout and stderr to a single file in c++?
what i tried was:
if((LogStream = freopen(strFilePath.c_str(), "w", stdout)) == NULL)
{
cout << "Failed to redirect console logs\n";
}
if((LogStream = freopen(strFilePath.c_str(), "a", stderr)) == NULL)
{
cout << "Failed to redirect console logs\n";
}
But it is not working as i expected.
Thanks in advance
Here's how you can change each of the raw file/C buffered file/C++ buffered streams:
#include <unistd.h>
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
int main() {
// unbuffered system-call level write() access:
::fclose(stderr);
if (::dup2(STDOUT_FILENO, STDERR_FILENO) == -1) {
cout << "dup2() failed!" << endl;
return -1;
}
char msg_err[] = "write(STDERR_FILENO) OK.\n";
char msg_out[] = "write(STDOUT_FILENO) still OK.\n";
write(STDERR_FILENO, msg_err, strlen(msg_err));
write(STDOUT_FILENO, msg_out, strlen(msg_out));
// buffered C-style access: (ordering may be weird w/o fflush())
stderr = ::fdopen(STDOUT_FILENO, "a");
fprintf(stderr, "fprintf(stderr) OK.\n");
fprintf(stdout, "fprintf(stdout) still OK.\n");
// buffered C++ access:
std::cerr.rdbuf(std::cout.rdbuf());
cerr << "cerr << OK.\n";
cout << "cout << still OK.\n";
}
That said, it's still better to do this from the invoking shell.

posix_spawnp and piping child output to a string

I am struggling with process creation and piping the child process' output into a string of the parent process. I got it working on Windows (using CreatePipe and CreateProcess and ReadFile), but can't seem to get the exact analog on Unix to work. This is my code:
#include <spawn.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
int exit_code;
int cout_pipe[2];
int cerr_pipe[2];
posix_spawn_file_actions_t action;
if(pipe(cout_pipe) || pipe(cerr_pipe))
cout << "pipe returned an error.\n";
posix_spawn_file_actions_init(&action);
posix_spawn_file_actions_addclose(&action, cout_pipe[0]);
posix_spawn_file_actions_addclose(&action, cerr_pipe[0]);
posix_spawn_file_actions_adddup2(&action, cout_pipe[1], 1);
posix_spawn_file_actions_adddup2(&action, cerr_pipe[1], 2);
posix_spawn_file_actions_addclose(&action, cout_pipe[1]);
posix_spawn_file_actions_addclose(&action, cerr_pipe[1]);
vector<string> argmem = {"bla"};
vector<char*> args = {&argmem[0][0], nullptr}; // I don't want to call new.
pid_t pid;
if(posix_spawnp(&pid, "echo", &action, NULL, &args[0], NULL) != 0)
cout << "posix_spawnp failed with error: " << strerror(errno) << "\n";
//close(cout_pipe[0]);
//close(cerr_pipe[0]);
close(cout_pipe[1]);
close(cerr_pipe[1]);
waitpid(pid,&exit_code,0);
cout << "exit code: " << exit_code << "\n";
// Read from pipes
const size_t buffer_size = 1024;
string buffer;
buffer.resize(buffer_size);
ssize_t bytes_read = read(cout_pipe[0], &buffer[0], buffer_size);
while ((bytes_read = read(cout_pipe[0], &buffer[0], buffer_size)) > 0)
{
cout << "read " << bytes_read << " bytes from stdout.\n";
cout << buffer.substr(0, static_cast<size_t>(bytes_read)+1) << "\n";
bytes_read = read(cout_pipe[0], &buffer[0], buffer_size);
}
if(bytes_read == -1)
cout << "Failure reading from stdout pipe.\n";
while ((bytes_read = read(cerr_pipe[0], &buffer[0], buffer_size)) > 0)
{
cout << "read " << bytes_read << " bytes from stderr.\n";
cout << buffer.substr(0, static_cast<size_t>(bytes_read)+1) << "\n";
bytes_read = read(cout_pipe[0], &buffer[0], buffer_size);
}
if(bytes_read == -1)
cout << "Failure reading from stderr pipe.\n";
posix_spawn_file_actions_destroy(&action);
}
The output is:
exit code: 0
So I suppose everything is working except the actual piping. What is wrong here? I also wonder if there is a way to read the piped bytes in a waitpid loop, but when I try that, the parent process hangs infinitely.
posix_spawn is interesting and useful, which makes this question worth necromancing -- even if it is no longer relevant to the OP.
There are some significant bugs in the code as posted. I suspect that some of these were the result of hacking in desperation, but I don't know which was the original bug:
The args array does not include the argv[0] that would represent the executable name. This results in the echo program never seeing the intended argv[1] ("bla").
The read() function is called from different places in a way that just doesn't make sense. A correct way to do this would be to only call read as part of the control expression for the while loops.
waitpid() is called before reading from the pipes. This prevents the I/O from completing (in non-trivial cases at least).
A more subtle issue with this code is that attempts to read all of the child's stdout before reading anything from stderr. In principle, this could cause the child to block while attempting to write to stderr, thus preventing the program from completing. Creating an efficient solution to this is more complicated as it requires that you can read from whichever pipe has available data. I used poll() for this. Another approach would be to use multiple threads.
Additionally, I have used sh (the command shell, i.e. bash) as the child process. This provides a great deal of additional flexibility, such as running a pipeline instead of a single executable. In particular, though, using sh provides the simple convenience of not having to manage the parsing of the command-line.
/*BINFMTCXX: -std=c++11 -Wall -Werror
*/
#include <spawn.h> // see manpages-posix-dev
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
int exit_code;
int cout_pipe[2];
int cerr_pipe[2];
posix_spawn_file_actions_t action;
if(pipe(cout_pipe) || pipe(cerr_pipe))
cout << "pipe returned an error.\n";
posix_spawn_file_actions_init(&action);
posix_spawn_file_actions_addclose(&action, cout_pipe[0]);
posix_spawn_file_actions_addclose(&action, cerr_pipe[0]);
posix_spawn_file_actions_adddup2(&action, cout_pipe[1], 1);
posix_spawn_file_actions_adddup2(&action, cerr_pipe[1], 2);
posix_spawn_file_actions_addclose(&action, cout_pipe[1]);
posix_spawn_file_actions_addclose(&action, cerr_pipe[1]);
//string command = "echo bla"; // example #1
string command = "pgmcrater -width 64 -height 9 |pgmtopbm |pnmtoplainpnm";
string argsmem[] = {"sh","-c"}; // allows non-const access to literals
char * args[] = {&argsmem[0][0],&argsmem[1][0],&command[0],nullptr};
pid_t pid;
if(posix_spawnp(&pid, args[0], &action, NULL, &args[0], NULL) != 0)
cout << "posix_spawnp failed with error: " << strerror(errno) << "\n";
close(cout_pipe[1]), close(cerr_pipe[1]); // close child-side of pipes
// Read from pipes
string buffer(1024,' ');
std::vector<pollfd> plist = { {cout_pipe[0],POLLIN}, {cerr_pipe[0],POLLIN} };
for ( int rval; (rval=poll(&plist[0],plist.size(),/*timeout*/-1))>0; ) {
if ( plist[0].revents&POLLIN) {
int bytes_read = read(cout_pipe[0], &buffer[0], buffer.length());
cout << "read " << bytes_read << " bytes from stdout.\n";
cout << buffer.substr(0, static_cast<size_t>(bytes_read)) << "\n";
}
else if ( plist[1].revents&POLLIN ) {
int bytes_read = read(cerr_pipe[0], &buffer[0], buffer.length());
cout << "read " << bytes_read << " bytes from stderr.\n";
cout << buffer.substr(0, static_cast<size_t>(bytes_read)) << "\n";
}
else break; // nothing left to read
}
waitpid(pid,&exit_code,0);
cout << "exit code: " << exit_code << "\n";
posix_spawn_file_actions_destroy(&action);
}