How to get output from other command line interface programs? - c++

Ok I did some research and I couldn't turn up anything useful. I am trying to write a program that will receive input from iwconfig (on a linux machine). It will then sort through the input, do some calculations and output to a database. Sorting through the input and outputting isn't an issue (or so I really hope it not to be) but what I am struggling with is reading input from another command line program. What I have right now as a base Hello World program is:
#include <iostream>
#include <cstdlib>
using namespace std;
int main() {
int numbr = 0;
cout << "Hello world!" << endl;
cin >> numbr;
cout << "number is " << numbr;
cout << system("iwconfig");
return 0;
}
However upon running the program, all it does is output hello world, ask for my random input and output it again. It does not output iwconfig (I also ran the line as just system("iwconfig"); without the output statement). Would someone be kind enough to explain how I could run a program like iwconfig and capture it's output?

"Would someone be kind enough to explain how I could run a program like iwconfig and capture it's output?"
Check the int system( const char *command ); documentation. It certainly doesn't provide to return the value, you want to output with your cout statement.
You probably want to have pipes established between your main and the iwconfig program, as described here, to control the input and output streams used by the child process.
To replicate the mentioned answer adapted:
int main() {
int fd_p2c[2], fd_c2p[2], bytes_read;
pid_t childpid;
char readbuffer[80];
string program_name = "iwconfig";
string receive_output = "";
if (pipe(fd_p2c) != 0 || pipe(fd_c2p) != 0) {
cerr << "Failed to pipe\n";
exit(1);
}
childpid = fork();
if (childpid < 0) {
cout << "Fork failed" << endl;
exit(-1);
}
else if (childpid == 0) {
if (dup2(fd_p2c[0], 0) != 0 ||
close(fd_p2c[0]) != 0 ||
close(fd_p2c[1]) != 0) {
cerr << "Child: failed to set up standard input\n";
exit(1);
}
if (dup2(fd_c2p[1], 1) != 1 ||
close(fd_c2p[1]) != 0 ||
close(fd_c2p[0]) != 0) {
cerr << "Child: failed to set up standard output\n";
exit(1);
}
execl(program_name.c_str(), program_name.c_str(), (char *) 0);
cerr << "Failed to execute " << program_name << endl;
exit(1);
}
else {
close(fd_p2c[0]);
close(fd_c2p[1]);
cout << "Writing to child: <<" << gulp_command << ">>" << endl;
int nbytes = gulp_command.length();
if (write(fd_p2c[1], gulp_command.c_str(), nbytes) != nbytes) {
cerr << "Parent: short write to child\n";
exit(1);
}
close(fd_p2c[1]);
while (1) {
bytes_read = read(fd_c2p[0], readbuffer, sizeof(readbuffer)-1);
if (bytes_read <= 0) break;
readbuffer[bytes_read] = '\0';
receive_output += readbuffer;
}
close(fd_c2p[0]);
cout << "From child: <<" << receive_output << ">>" << endl;
}
return 0;
}

Related

Using execvp with parameters

I want to know how can I run a program with parameters using fork() and execvp().
I want to run the program test and passed the parameter flag.
I am using the variable args to capture the string start /test flag. This code snippet below is contained inside a conditional statement that checks for the parsed string start /test -flag.
arguments[0] = "/test"
arguments[1] = "-flag"
char * arguments[3];
arguments[0] = (char*)args[1].c_str();
arguments[1] = (char*)args[2].c_str();
arguments[2] = NULL;
cout << "Arguments[1] = " << arguments[1] << endl;
pid_t pid = fork();
// ERROR
if (pid == -1)
perror("ERROR: Failed to fork");
// Child
if (pid == 0)
{
cout << "child: " << pid << endl;
if (execvp (arguments[0], arguments) == -1)
{
perror("exec");
}
}
// Parent
if (pid > 0)
{
wait(0);
cout << "parent: " << pid << endl;
}
Does this mean that my program test is getting passed the parameter flag as an argument? In other words, ARGV[0]: flag?
int main (int argc, char **argv)
{
cout << "YOU ENTERED: " << argc << "ARGUMENTS" << endl;
cout << "ARGV[0]: " << argv[0] << endl;
return 0;
}

pipe() and fork() example: basic_string::M_construct null not valid

Another try with getting parallel processes to work. Please excuse the amount of code but every attempt to shorten it makes the error vanish.
What I tested so far:
sending int from parent to child, from child to parent, and from parent to child and then back: works
processing a list of int: send from parent to child, modify and back to parent: works
more data: int + string, from parent to child, modify and back to parent: works
a list of data the same way: works
But when I run the same function that works a second time it always fail.
This is the function that creates the child process:
//parent sends binary data from list to child which sends back modified data
bool processParallel6(std::vector<std::pair<int, std::string>> & data)
{
//define pipe
int parent2Child[2];
int child2Parent[2];
//create pipe
pipe(parent2Child);
pipe(child2Parent);
//fork
pid_t child = fork();
if(child == 0) //child process
{
//close not needed end of pipe
close(parent2Child[1]);
close(child2Parent[0]);
for(;;)
{
struct pollfd pfd;
pfd.fd = parent2Child[0];
pfd.events = POLLIN;
//wait until data is available at the pipe
cout << "c: poll ..." << endl;
if(poll(&pfd, 1, -1) < 0)
{
cout << "c: poll: " << strerror(errno) << endl;
exit(-1);
}
cout << "c: poll says there are data" << endl;
if((pfd.revents&POLLIN) == POLLIN)
{
int data;
std::string text;
if(!readData3(parent2Child[0], data, text))
exit(-2);
cout << "c: data received: " << data << " " << text << endl;
if(data == -1)
break;
if(!writeData3(child2Parent[1], data * 2, text + text))
exit(-3);
cout << "c: sent data to parent: " << 2 * data << " " << text + text << endl;
}
}
close(parent2Child[0]);
close(child2Parent[1]);
exit(0);
}
else //parent process
{
//close not needed end of pipe
close(parent2Child[0]);
close(child2Parent[1]);
//send data to child
if(!writeData3(parent2Child[1], data.back().first, data.back().second))
return false;
cout << "p: wrote data: " << data.back().first << " " << data.back().second << endl;
data.pop_back();
//read result from child
for(;;)
{
struct pollfd pfd;
pfd.fd = child2Parent[0];
pfd.events = POLLIN;
//wait until data is available at the pipe
cout << "p: poll ..." << endl;
if(poll(&pfd, 1, -1) < 0)
{
cout << "p poll: " << strerror(errno) << endl;
return false;
}
cout << "p: poll says there are data" << endl;
if((pfd.revents&POLLIN) == POLLIN)
{
int data;
std::string text;
if(!readData3(child2Parent[0], data, text))
return false;
cout << "p: data received: " << data << " " << text << endl;
}
if(data.empty())
break;
if(!writeData3(parent2Child[1], data.back().first, data.back().second))
return false;
cout << "p: wrote data: " << data.back().first << " " << data.back().second << endl;
data.pop_back();
}
//send stop data
if(!writeData3(parent2Child[1], -1, "notext"))
return false;
cout << "p: sent stop data " << endl;
//wait for child to end
wait(nullptr);
//close all pipes
close(parent2Child[1]);
close(child2Parent[0]);
}
return true;
}
For reading and writing data I use this two functions:
bool readData3(int fd, int & number, std::string & text)
{
char numberBuf[sizeof(int)];
int bytesRead = read(fd, numberBuf, sizeof(int));
if(bytesRead > 0)
{
number = *(int *)numberBuf;
}
else if(bytesRead < 0)
{
cout << "readData3: " << strerror(errno) << endl;
return false;
}
char sizeBuf[sizeof(int)];
int size = -1;
bytesRead = read(fd, sizeBuf, sizeof(int));
if(bytesRead > 0)
{
size = *(int *)sizeBuf;
}
else if(bytesRead < 0)
{
cout << "readData3: " << strerror(errno) << endl;
return false;
}
char textBuf[size];
bytesRead = read(fd, textBuf, size);
if(bytesRead > 0)
{
text = std::string(textBuf);
}
else if(bytesRead < 0)
{
cout << "readData3: " << strerror(errno) << endl;
return false;
}
return true;
}
bool writeData3(int fd, const int number, const std::string text)
{
int bytesWritten = write(fd, &number, sizeof(int));
if(bytesWritten < 0)
{
cout << "writeData3: " << strerror(errno) << endl;
return false;
}
int size = text.size() + 1;
bytesWritten = write(fd, &size, sizeof(int));
if(bytesWritten < 0)
{
cout << "writeData3: " << strerror(errno) << endl;
return false;
}
bytesWritten = write(fd, text.c_str(), size);
if(bytesWritten < 0)
{
cout << "writeData3: " << strerror(errno) << endl;
return false;
}
return true;
}
Finally I run it like this:
#include <iostream>
#include <vector>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <bitset>
#include <memory>
#include <poll.h>
#include <cstring>
using namespace std;
int main(int /*argc*/, char* /*argv*/[])
{
std::vector<std::pair<int, std::string>> data;
data.push_back(std::make_pair(1, "one"));
data.push_back(std::make_pair(2, "two"));
cout << "6a ########################################################" << endl << flush;
processParallel6(data);
cout << "6b ########################################################" << endl << flush;
processParallel6(data);
return 0;
}
This is the output:
6a ###############################################
p: wrote data: 2 two
p: poll ...
c: poll ...
c: poll says there are data
c: data received: 2 two
p: poll says there are data
p: data received: 4 twotwo
p: wrote data: 1 one
p: poll ...
c: sent data to parent: 4 twotwo
c: poll ...
c: poll says there are data
c: data received: 1 one
p: poll says there are data
p: data received: 2 oneone
p: sent stop data
c: sent data to parent: 2 oneone
c: poll ...
c: poll says there are data
c: data received: -1 notext
6b ###################################################
c: poll ...
terminate called after throwing an instance of 'std::logic_error'
what(): basic_string::_M_construct null not valid
c: poll says there are data
c: poll ...
c: poll says there are data
c: poll ...
The last 4 lines are repeated a thousands of times. This output comes most of the times, but sometimes I have seen a std::bad_alloc error. When I try strace it crashes too, but when it runs I have seen directly after the second run of processParallel6() a line with mmap, ENOEM and 'Cannot allocate memory'
What happens here? Why is it working the first time, but not the second time?
You attempting to copy an invalid std::string reference.
std::terminate is getting called in the constructor of std::string. The constructor is implicitly called in processParallel6 when calling writeData3:
bool writeData3(int fd, const int number, const std::string text)
...
//send data to child
if(!writeData3(parent2Child[1], data.back().first, data.back().second))
return false;
You are expecting that data.back().second is a valid string reference, but nothing in the code ensures that is the case.
You construct data and place two entries in it:
data.push_back(std::make_pair(1, "one"));
data.push_back(std::make_pair(2, "two"));
In the first call to processParallel6 you run the following block of code twice:
if(!writeData3(parent2Child[1], data.back().first, data.back().second))
return false;
cout << "p: wrote data: " << data.back().first << " " << data.back().second << endl;
data.pop_back();
At this point data is empty. You cannot make another call to processParallel6 because it expects that data contains at least one element.

Checking if String is an int [duplicate]

This question already has answers here:
How to determine if a string is a number with C++?
(36 answers)
Closed 4 years ago.
I'm trying to enter the format of
"prog1 1 all file1"
the first input has to be a argv[1] should be an int.
So i need a way to determine if argv[1] is entered as a string "xxx" ( "prog1 xxx" ) it should return "NO PHRASE LENGTH" but its returning "INVALID PHRASE LENGTH".
I see there is a isdigit() function but im not sure how i would use that.
int main(int argc, char *argv[]){
try{
if(argc == 1){
cout << "NO PHRASE LENGTH" << endl; exit(1);
}
else if((stoi(argv[1])) <= 0 ){
cout << "INVALID PHRASE LENGTH" << endl; exit(1);
}
else if(argc == 2){
cout << "NO MODE" << endl; exit(1);
}
else if(!(std::string(argv[2]) == "all") && !(std::string(argv[2]) == "top")){
cout << "INVALID MODE" << endl;
}
else if(argc == 3){
cout << "NO FILES GIVEN" << endl;
}
else if(argc >= 4){
ifstream f;
for(int i = 4; i < argc; i--){
f.open( argv[i] );
if( ! f.good() ) {
cout << "BAD FILE " << argv[i] << endl; exit(1);
}
//cout << "OK" << endl;
//f.close();
}
}
else
return 0;
}
catch(exception e){
}}
Your code is quite confusing. I am not sure what you were doing with the for loop to open the file. Anyway, I have given an example of what it could be.
int main(int argc, char *argv[])
{
if(argc != 4)
{
cout << "Program requires 3 parameters!" << endl;
return -1;
}
if(std::string(argv[2]) != "all" && std::string(argv[2]) != "top")
{
cout << "INVALID MODE" << endl;
return -1;
}
try
{
if(stoi(argv[1]) < 1)
{
cout << "ZERO OR NEGATIVE PHRASE LENGTH" << endl;
return -1;
}
ifstream f(argv[3]);
if(!f)
{
cout << "BAD FILE " << argv[3] << endl;
return -1;
}
// Now do whatever you want with the opened file
}
catch(out_of_range e)
{
cout << "NON-INT PHRASE LENGTH" << endl;
return -1;
}
return 0;
}
Don't use std::endl when all you want to say is '\n' (or "...\n"). std::endl not only inserts a newline into the stream but also flushes it. If you *really* want to flush a stream be explicit and use std::flush.
Don't use exit() to end a program if there are other ways to do so. If you use exit() no stack unwinding will occur.
#include <cstdlib>
#include <string>
#include <iostream>
#include <exception>
#include <fstream>
int main(int argc, char **argv)
{
if (argc == 1) {
std::cerr << "NO PHRASE LENGTH\n";
return EXIT_FAILURE;
}
if (argc == 2) {
std::cerr << "NO MODE\n";
return EXIT_FAILURE;
}
if (argc == 3) {
std::cerr << "NO FILES GIVEN\n";
return EXIT_FAILURE;
}
int phrase_length;
try {
phrase_length = std::stoi(argv[1]);
if (phrase_length < 0)
throw std::out_of_range{ nullptr };
}
catch (std::invalid_argument) {
std::cerr << "NO PHRASE LENGTH";
return EXIT_FAILURE;
}
catch (std::out_of_range) {
std::cerr << "PHRASE LENGTH OUT OF RANGE\n";
return EXIT_FAILURE;
}
std::string mode{ argv[2] };
if ( mode != "all" && mode != "top") {
std::cerr << "INVALID MODE\n";
return EXIT_FAILURE;
}
for (int i = 3; i < argc; ++i) {
std::fstream input_file{ argv[i] };
if (!input_file.is_open()) {
std::cerr << "BAD FILE \"" << argv[i] << "\"\n";
return EXIT_FAILURE;
}
}
}

How to track termination/running of child process that is not being "waited" for?

I have an assignment where I have to write a subshell in C++. Essentially, I am forking (if the input is not jobs or exit) and in the child process, I am calling exec to run the command. It needs to run alongside the parent process, so for example if I call sleep 100, the program should immediately be ready to run the next command since the parent is not "waiting" for it since I am using WNOHANG. However, my issue is when I need to track the actual state- if sleep is still running, I want to be able to get that the command is still running, but I am unable to do so. It always shows up as exited, when I use the various macros. I have no idea how to approach this differently and would appreciate some guidance.
(i didn't include the declarations of the variables since the editor on this website was messing it up for some reason)
do{
cout << "# ";
getline(cin, input);
if (input == "jobs")
{
cout << "Process ID | State | Command " << endl;
cout << "-----------+--------------+-----------------------------------------------" << endl;
//jobs stuff goes here, need to print pids, states, and commands
if(counter == 0)
{
cout << " [NO PROCESSES] " << endl;
}
else
{
for(i = 0; i < counter; i++)
{
cout << pidArray[i] << " ";
cout << statusArray[i] << " ";
cout << cmdArray[i] << endl;
}
}
}
else
{
cmdArray[i] = input;
i++;
counter++;
pid = fork();
if( pid == 0)
{
execl("/bin/sh", "sh", "-c", input.c_str(), (char *)0);
//break;
}
else if (pid == -1)
{
break;
}
else
{
pidArray[i-1] = pid;
//int rc = waitid(P_PID, pid, NULL, WNOHANG);
// int rc =waitpid(pid, &status, WNOHANG | WNOWAIT );
//exitChecker = waitpid(pid, &status, WNOHANG);
usleep(100000);
if (WIFEXITED(status))
{
cout << "terminated" << endl;
}
else
{
cout << "running" << endl;
}
}
}
}while(input != "exit");
return 0;
}
Thanks in advance.

Why does read of /proc/cpuinfo seem to not advance file position?

I have the following code which ends up forever reading '/proc/cpuinfo' as it keeps getting the same result every read. Why doesn't the file pointer get advanced and reach eof ever? Seems this special file has different semantics.
const int bufSize = 4096;
char buf[bufSize + 1];
const string cpuInfo = "/proc/cpuinfo";
int cpuFD = ::open(cpuInfo.c_str(), O_RDONLY);
if (cpuFD == -1) {
logOutputStream << "Failed attempt to open '" << cpuInfo << "': "
<< strerror(errno) << endl;
} else {
assert(bufSize <= SSIZE_MAX);
logOutputStream << "Contents of: '" << cpuInfo << "'.\n";
for (int nRead = ::read(cpuFD, buf, bufSize); nRead != 0;) {
if (nRead == -1) {
logOutputStream << "Failed attempt to read '" << cpuInfo << "': "
<< strerror(errno) << endl;
break;
} else {
buf[nRead] = '\0';
logOutputStream << buf;
}
}
if (::close(cpuFD) == -1) {
logOutputStream << "Failed attempt to close '" << cpuInfo << "': "
<< strerror(errno) << endl;
}
}
for (int nRead = ::read(cpuFD, buf, bufSize); nRead != 0;) {
is wrong. You're using read as an initializer, so read is only being called once, not once per loop. After that, you're just looping forever printing it out (because nothing is changing nRead).
What happens if you try dumping the content into an actual text file with something like
cat /proc/cpuinfo > cpuinfo.txt
and then reading that file ?