Why is only the first Child Process printing something out? - c++

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?

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

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.

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 enable dragging a file on the *.exe and get it as parameter?

What do I have to do to make my program use a file that has been dragged and dropped onto its icon as a parameter?
My current main method looks like this:
int main(int argc, char* argv[])
{
if (argc != 2) {
cout << "ERROR: Wrong amount of arguments!" << endl;
cout << "\n" << "Programm closed...\n\n" << endl;
exit(1);
return 0;
}
Converter a(argv[1]);
// ...
cout << "\n" << "Programm finished...\n\n" << endl;
// cin.ignore();
return 0;
}
What I'd really like to be able to do is select 10 (or so) files, drop them onto the EXE, and process them from within my application.
EDIT:
The incomming parameter is used as filename, constructed in the cunstructor.
Converter::Converter(char* file) {
// string filename is a global variable
filename = file;
myfile.open(filename.c_str(), ios_base::in);
}
The method where the textfile gets read:
string Converter::readTextFile() {
char c;
string txt = "";
if (myfile.is_open()) {
while (!myfile.eof()) {
myfile.get(c);
txt += c;
}
} else {
error("ERROR: can't open file:", filename.c_str());
}
return txt;
}
EDIT2:
deleted
Update:
I got again to this point.
Actual Main method:
// File path as argument
int main(int argc, char* argv[]) {
if (argc < 2) {
cout
<< "ERROR: Wrong amount of arguments! Give at least one argument ...\n"
<< endl;
cout << "\n" << "Programm closed...\n\n" << endl;
cin.ignore();
exit(1);
return 0;
}
vector<string> files;
for (int g = 1; g < argc; g++) {
string s = argv[g];
string filename = "";
int pos = s.find_last_of("\\", s.size());
if (pos != -1) {
filename = s.substr(pos + 1);
cout << "argv[1] " << argv[1] << endl;
cout << "\n filename: " << filename << "\n pos: " << pos << endl;
files.push_back(filename);
}
files.push_back(s);
}
for (unsigned int k = 0; k < files.size(); k++)
{
cout << "files.at( " << k << " ): " << files.at(k).c_str() << endl;
Converter a(files.at(k).c_str());
a.getATCommandsFromCSV();
}
cout << "\n" << "Programm finished...\n\n" << endl;
cin.ignore();
return 0;
}
Actually the console window apears for maybe 0.5 sec and closes again.
It doen't stop on any of my cin.ignore(); Maybe it doesn't get there?
Can anyone help?
Your program does not need to do anything special apart from handling command-line arguments. When you drag-drop a file onto an application in Explorer it does nothing more than to pass the file name as argument to the program. Likewise for multiple files.
If all you expect is a list of file names, then just iterate over all arguments, do whatever you want with them and be done. This will work for zero to almost arbitrarily many arguments.
Maybe you could write a test program like this:
int main(int argc, char* argv[])
{
// argv[0] is not interesting, since it's just your program's path.
for (int i = 1; i < argc, ++i)
cout << "argv[" << i << "] is " << argv[i] << endl;
return 0;
}
And see what happens after you throw different files at it.
EDIT: Just look at Joey's answer.
Answer to the main question
TO SEE THE ANSWER TO YOUR LAST PROBLEM SEE BOTTOM OF THIS ANSWER
All drag&dropped files are get-able as argv[orderOfTheFile] (orderOfTheFile is from 1-n),
however how does windows create that order, now that is a real mystery...
Anyway let's say I would create 26 plain text files ( *.txt ), from a.txt to z.txt on my Desktop,
now if I would drag&dropped them on my ArgsPrinter_c++.exe located directly on C:\ drive,
an output would be similar to this:
argc = 27
argv[0] = C:\ArgsPrinter_c++.exe
argv[1] = C:\Users\MyUserName\Desktop\c.txt
argv[2] = C:\Users\MyUserName\Desktop\d.txt
argv[3] = C:\Users\MyUserName\Desktop\e.txt
argv[4] = C:\Users\MyUserName\Desktop\f.txt
argv[5] = C:\Users\MyUserName\Desktop\g.txt
argv[6] = C:\Users\MyUserName\Desktop\h.txt
argv[7] = C:\Users\MyUserName\Desktop\i.txt
argv[8] = C:\Users\MyUserName\Desktop\j.txt
argv[9] = C:\Users\MyUserName\Desktop\k.txt
argv[10] = C:\Users\MyUserName\Desktop\l.txt
argv[11] = C:\Users\MyUserName\Desktop\m.txt
argv[12] = C:\Users\MyUserName\Desktop\n.txt
argv[13] = C:\Users\MyUserName\Desktop\o.txt
argv[14] = C:\Users\MyUserName\Desktop\p.txt
argv[15] = C:\Users\MyUserName\Desktop\q.txt
argv[16] = C:\Users\MyUserName\Desktop\r.txt
argv[17] = C:\Users\MyUserName\Desktop\s.txt
argv[18] = C:\Users\MyUserName\Desktop\t.txt
argv[19] = C:\Users\MyUserName\Desktop\u.txt
argv[20] = C:\Users\MyUserName\Desktop\v.txt
argv[21] = C:\Users\MyUserName\Desktop\w.txt
argv[22] = C:\Users\MyUserName\Desktop\x.txt
argv[23] = C:\Users\MyUserName\Desktop\y.txt
argv[24] = C:\Users\MyUserName\Desktop\z.txt
argv[25] = C:\Users\MyUserName\Desktop\a.txt
argv[26] = C:\Users\MyUserName\Desktop\b.txt
My ArgsPrinter_c++.exe source code:
#include <iostream>
using namespace std;
int main(int argc, char* argv[]) {
cout << "argc = " << argc << endl;
for(int i = 0; i < argc; i++)
cout << "argv[" << i << "] = " << argv[i] << endl;
std::cin.ignore();
return 0;
}
Your last problem
I have created a simple program that creates only a sceleton of your class so it can be used, and the program's main itself ran JUST FINE => if your program exits too soon, the problem will be in your class...
Tested source code:
#include <iostream>
#include <vector>
using namespace std;
class Converter{
public:
Converter(const char* f){ cout << f << endl; }
void getATCommandsFromCSV(){ cout << "called getATCommandsFromCSV" << endl; }
};
int main(int argc, char* argv[]) {
vector<string> files;
for (int g = 1; g < argc; g++) {
string s = argv[g];
string filename = "";
int pos = s.find_last_of("\\", s.size());
if (pos != -1) {
filename = s.substr(pos + 1);
cout << "argv[1] " << argv[1] << endl;
cout << "\n filename: " << filename << "\n pos: " << pos << endl;
files.push_back(filename);
}
files.push_back(s);
}
for (unsigned int k = 0; k < files.size(); k++)
{
cout << "files.at( " << k << " ): " << files.at(k).c_str() << endl;
Converter a(files.at(k).c_str());
a.getATCommandsFromCSV();
}
cout << "\n" << "Programm finished...\n\n" << endl;
cin.ignore();
return 0;
}