Using execvp with parameters - c++

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;
}

Related

Why is only the first Child Process printing something out?

The program I am working on is called myfile - It should find a file in a certain searchpath. You should also be able to search for multiple files and if so, I MUST create multiple child processes with fork(). The problem is, i dont get the expected outcome printed out. If I am searching for multiple files, only the first file gets "returned". I think it has something todo with the second for in the else statement. For debugging I am printing out every PID of the child processes. Is the problem maybe that the child processes are working with the same variables at the same time?
The Code:
`
#include<iostream>
#include<dirent.h>
#include<string.h>
#include<unistd.h>
#include<sys/wait.h>
#include<stdlib.h>
using namespace std;
int main(int argc, char** argv)
{
struct dirent *d;
DIR *dr;
dr = opendir(argv[1]);
pid_t pid;
int numberOfFiles = argc - 2;
if(dr!=NULL){
if(numberOfFiles == 1){
// normal search without fork
for(d=readdir(dr); d!=NULL; d=readdir(dr)){
if(strcmp(d->d_name, argv[argc-1]) == 0){
cout << "<" << getpid() << ">: " << argv[argc-1] << ": <" << realpath(argv[argc-1], NULL) << ">" <<endl;
}
}
}else{
// search with fork
for(int i = 2; i < argc; i++){
if(fork() == 0){
cout << "Current PID: " << getpid() << " " << argv[i] <<endl;
for(d=readdir(dr); d!=NULL; d=readdir(dr)){
if(strcmp(d->d_name, argv[i]) == 0){
cout << "Current i=" << i << " "<< "<" << getpid() << ">: " << argv[i] << ": <" << realpath(argv[i], NULL) << ">" <<endl;
}
}
exit(0);
}
}
for(int i=0; i < numberOfFiles; i++){
wait(NULL);
}
}
}else
cout<<"\nError Occurred!"<<endl;
cout<<endl;
return 0;
}
`
Screenshot of output in terminal:
The problem is, i dont get the expected outcome printed out. If I am searching for multiple files, only the first file gets "returned". I think it has something todo with the second for in the else statement. For debugging I am printing out every PID of the child processes. Is the problem maybe that the child processes are working with the same variables at the same time?
Looks like a problem with opendir, readdir, and reusing variables. I cut your program down to:
int main(int argc, char **argv)
{
struct dirent *d;
DIR *dr;
dr = opendir(argv[1]);
for (int i = 2; i < argc; i++)
{
if (fork() == 0)
{
cout << "Current PID: " << getpid() << " " << argv[i] << endl;
for (d = readdir(dr); d != NULL; d = readdir(dr))
{
cout << getpid() << "\t" << d->d_name << endl;
}
exit(0);
}
}
for (int i = 0; i < argc - 2; i++)
wait(NULL);
}
And got this output (from ./testfork ../bla 1 2):
Current PID: 17197 1
Current PID: 17198 2
17198 test.txt
17198 ..
17198 .
Which shows that once one process has read to end with readdir, then the other gets nothing. However, if I move the call to opendir to after the fork:
int main(int argc, char **argv)
{
struct dirent *d;
DIR *dr;
for (int i = 2; i < argc; i++)
{
if (fork() == 0)
{
dr = opendir(argv[1]); // <- Here
cout << "Current PID: " << getpid() << " " << argv[i] << endl;
for (d = readdir(dr); d != NULL; d = readdir(dr))
{
cout << getpid() << "\t" << d->d_name << endl;
}
exit(0);
}
}
for (int i = 0; i < argc - 2; i++)
wait(NULL);
}
The the output becomes:
Current PID: 17751 1
17751 test.txt
17751 ..
17751 .
Current PID: 17752 2
17752 test.txt
17752 ..
17752 .
I don't quite understand why this happens, since the way fork works should ensure that each process get their own copy of the memory (though possibly only after writing to it). So when one process modifies dr, then that change should not be reflected in the other processes.
Perhaps it is due to dr actually being changed through system calls (by way of readdir), and not by the process directly?

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.

Need help in implementing conditional outputs using the fork() function

In the below program i'm trying to implement these conditions:
I'm trying to only implement the first child process to print “hi”?
and the root process to print “areyou”?
and the final child process must exit from the system without doing anything?
#include <iostream>
#include <sys/types.h>
#include <unistd.h>
using namespace std;
pid_t pid1,pid2,pid3,pid4;
int function(){
pid1=fork();
if(pid1>0)
{
cout << "hi" << getpid()<<" " << getppid()<< endl; /*first child process should print "hi"*/
}
pid2=fork();
cout << "hell" << getpid()<<" " << getppid()<< endl;
pid3=fork();
cout << "how " <<getpid() <<" "<<getppid() <<endl;
pid4=fork();
if(pid4>0){
return 0;/* final child process should exit from the system with out doing anything*/
}
else{
cout << "areyou "<<getpid()<<" "<<getppid()<<endl;
}
}
int main() {
/* and the root process should print "are you"*/
function();
}
-with if(pid1>0) i guess i tried to implement first child to output "hi" and i feel i'm lost in understanding how can i get only the root parent process to print "areyou", and how to control the last child to exit with out doing anything
You may do something like
void function()
{
pid_t pid1, pid2, pid3, pid4;
pid1 = fork();
if (pid1 == 0)
{
// first child process should print "hi"
cout << "hi " << getpid() << " " << getppid()<< endl;
}
pid2 = fork();
cout << "hell " << getpid() <<" " << getppid() << endl;
pid3 = fork();
cout << "how " << getpid() <<" "<<getppid() << endl;
pid4 = fork(); // Mostly useless as only parent print something for this one
if (pid1 == 0 && pid2 == 0 && pid3 == 0 && pid4 == 0){
return; // final child process should exit from the system with out doing anything
} else if (pid1 > 0 && pid2 > 0 && pid3 > 0 && pid4 > 0){
cout << "areyou "<< getpid() << " "<< getppid() << endl;
}
}
Demo
with if(pid1>0) i guess i tried to implement first child to output "hi"
No, it's the parent that gets a positive pid (on success), because it gets the process-id of the child it just forked, or -1 if the fork call fails. The child receives a return value of 0.
What you want to do goes like this:
if(pid1 < 0)
{
cout << "fork failed to create a child process."
}
else if (pid1 > 0) // Parent
{
cout << "areyou";
}
else // child
{
cout << "hi";
}

How to get output from other command line interface programs?

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;
}

How do I get my pipe command to accept options?

I am writing a shell for a class and I need to implement pipes. Right now my code works only when the first command does not use an argument. For example: 'ls | wc -l' works but 'ls -lt | head' does not.
void usePipe(string &command)
{
int fd_pipe[2];
int pid1;
int pid2;
int status;
int wpid;
string cmd1,cmd2;
stringstream ss(command);
vector<string> tok1,tok2;
char **cmd1c;
char **cmd2c;
vector<const char*>args1,args2;
pid1 = fork();
if(pid1 == 0)
{
getline(ss,cmd1, '|');
getline(ss,cmd2, '|');
cmd1 = reduce(cmd1);
cmd2 = reduce(cmd2);
cout << cmd1 << "|" << endl;
cout << cmd2 << "|" << endl;
tok1 = split(cmd1,' ');
tok2 = split(cmd2, ' ');
for(int i = 0; i < tok1.size(); i++)
{
if(tok1.at(i).c_str()[0] != '\0')
{
args1.push_back(tok1.at(i).c_str());
cout << "Pushed command: " << args1.back() << endl;
}
}
args1.push_back(NULL);
cmd1c =(char**) &args1[0];
for(int i = 0; i < tok2.size(); i++)
{
if(tok2.at(i).c_str()[0] != '\0')
{
args2.push_back(tok2.at(i).c_str());
cout << "Pushed command: " << args2.back() << endl;
}
}
args2.push_back(NULL);
cmd2c =(char**) &args2[0];
pipe(fd_pipe);
pid2 = fork();
if(pid2 == 0)
{
cout << "in second child" <<endl;
dup2(fd_pipe[1],1);
close(fd_pipe[0]);
close(fd_pipe[1]);
execvp(cmd1c[0], cmd1c);
perror("Exec Failed ");
exit(5);
}
cout << "in first child"<< endl;
dup2(fd_pipe[0],0);
close(fd_pipe[0]);
close(fd_pipe[1]);
execvp(cmd2c[0], cmd1c);
perror("Exec Failed ");
exit(5);
}
wpid = wait(&status);
cout << "Shell process "<< pid1 << " exited with status " << (status >> 8) << endl;
}
Found it. In the first child I was giving execvp the wrong arguments. It needed to be
execvp(cmd2c[0], cmd2c);