Unable to send message to parent process after multiple forks - c++

I have a program that forks off four processes and calls execlp() to run different code for the child. I pass the child a number as an id. So far, all the child does is try to pass the id back to the parent process. The pipes work, if i put a string though the stream it prints out in the parent process. However, when i try to put the id as an int thought the stream, it does not work. I dont even get to the line of code after the fprintf() and fflush() command in the child.
I made some changes for how i created the file descriptors and added more code for an example. Now, in the child, i am unable to create the FILE* out. However, if i create out on file descriptor 1, it does print to the screen. I tried creating out on file descriptor 3 and the program just sits there and waits for input from the child that never comes.
Here is my parent:
Mom::Mom():childCount(0)
{
pipeCount = fileCount = 0;
int fd[2];
srand(time(NULL));
for(int c=0; c<NUMJOBS; ++c) jobs[c] = newJob();
//createFileDescriptors(fd);
ret = pipe(fd);
if(ret < 0) fatal("Error creating pipes");
//cout << fd[0] << "\t" << fd[1] << endl;
pipes[fileCount++] = fdopen(fd[0], "r");
fcntl( 3, F_SETFD, 0 );
//close(fd[1]);
//for(int c=3; c<FILEDESCRIPTORS; c+=2) pipes[pipeCount++] = fdopen(c, "w");
createChildren();
for(int c=0; c<4; c++)
{
int tmp = -1;
//cout << "About to read from children, tmp = " << tmp << endl;
ret = fscanf(pipes[0], "%d", &tmp);
//char* buffer = (char*) malloc(80*sizeof(char));
//char buffer[80];
//read(3, buffer, 80);
cout << ret << "\t" << tmp << endl;
//cout << ret << " " << tmp << endl;
//free(buffer);
}
//sleep(5);
}
/*------------------------------------------------------------------------------
Create all the children by using fork() and execlp()
----------------------------------------------------------------------------*/
void Mom::createChildren()
{
int fd[2];
fcntl( fd[IN], F_SETFD, 0 );
for(int c=0; c<NUMCHILDREN; c++)
{
ret = pipe(fd);
if(ret < 0) fatal("Error creating pipes");
int pid = fork();
//cout << pid << endl;
if(pid == 0)
{
setupChild(c, fd);
}
else
{
//close(fd[1]);
}
}
}
/*------------------------------------------------------------------------------
set up the child and call exec to run ChildMain
----------------------------------------------------------------------------*/
void Mom::setupChild(int count, int fd[])
{
//cout << "Creating child with id: " << count << endl;
char cnt = '0' + count;
string id_str (&cnt + '\0');
fcntl( fd[0], F_SETFD, 0 );
pipes[fileCount++] = fdopen(fd[1], "w");
//execlp("ChildMain", "ChildMain", id_str.c_str(), NULL);
execlp("ChildMain", id_str.c_str(), NULL);
}
And here is the child code:
int main(int argc, char* argv[])
{
//cout << argv[argc-1] << endl;
if(argc < 1) fatal("Not enough arguments provided to ChildMain");
int id = atoi(argv[argc-1]);
//cout << *argv[1] << " " << id << endl;
//redirect STDIN and STDOUT
/*int c_in = dup(0);
close(0);
dup((2*id) + 5);
int c_out = dup(1);
close(1);
dup(4);*/
/////////////////////////////
//Child kid((int) *argv[1]);
FILE* out = fdopen(4, "w");
if(out == NULL)
cout << "Error opening stream to parent in child: " << id << endl;
//char childID = '0' + id;
//char buf[80];
//strcpy(buf, "Child ");
//strcat(buf, &childID);
string buf ("Child");
//cout << tmp << " " << childID << endl;
//write(4, buf.c_str(), buf.length()+1);
//cout << id << endl;
int ret = fprintf(out, "%d", id);
fflush(out);
//fclose(out);
//cout << id << " " << ret << endl;
//ch.push_back((char) id);
//put STDIN and STDOUT back to correct file descriptors
/*close(1);
dup(c_out);
close(0);
dup(c_in);*/
////////////////////////////////////////////////////////
return 0;
}
I am very confused why this works for the first child, with id 0, but no the others. Does anyone know what is wrong with my code?

execlp(3) is expecting null terminated strings as it's args. &cnt won't be null terminated.
Simple fix:
void Mom::setupChild(int count, int fd[])
{
char cnt[2];
cnt[0] = '0' + count;
cnt[1] = '\0';
fcntl( fd[(2*count)+3], F_SETFD, 0 );
execlp("ChildMain", "ChildMain", &cnt, NULL);
}
This doesn't scale to 10 processes though, so I'd probably use a buffer and just sprintf() into it.

Here is a small example on how to implement the suggestion in my comment:
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main()
{
/* Need two sets of pipes: one for child stdin, one for child stdout */
int pipefds1[2];
int pipefds2[2];
pipe(pipefds1);
pipe(pipefds2);
int rc = fork();
if (rc == -1)
perror("fork");
else if (rc == 0)
{
/* In child */
/* Close the old stdin and stdout */
close(STDIN_FILENO);
close(STDOUT_FILENO);
/* Create new stdin/stroud from the pipes */
dup2(pipefds1[0], STDIN_FILENO);
dup2(pipefds2[1], STDOUT_FILENO);
/* Close the unneeded pipe handles */
close(pipefds1[1]);
close(pipefds2[0]);
/* Now pass control to the new program */
execl("/bin/ls", "ls", "-l", "/", NULL);
}
else
{
/* In parent */
/* Close the uneeded pipe handles */
close(pipefds1[0]);
close(pipefds2[1]);
/* We want to use stdio functions */
FILE *fp = fdopen(pipefds2[0], "r");
/* Read all from the child */
char buffer[128];
while (fgets(buffer, sizeof(buffer), fp))
{
printf("Input from child: %s\n", buffer);
}
fclose(fp);
/* Wait for child to exit */
wait(NULL);
}
return 0;
}
Hopefully this will be enough for you to build on.
The error handling is non-existant, but it is tested.

Related

Pipe method keeps failing on the parent process

char *c = strtok(a, "|"); // array for the different tokens in the command
char *d = strtok(NULL,"|");
pid_t cpid;
pipe(pfd);
cpid = fork();
if(cpid == 0){
/* child */
cout << "you are in child";
dup2(pfd[0], 0);
close(pfd[0]);
close(pfd[1]); /* the child does not need this end of the pipe */
cout << c;
execString(c);
//perror(cmd2[0]);
cout << "error";
} else{
cpid = fork();
if(cpid == 0){
/* parent */
cout <<"you are in parent";
cout << d;
dup2(pfd[1], 1);
close(pfd[0]);
close(pfd[1]); /* the parent does not need this end of the pipe */
execString(d);
//perror(cmd1[0]);
cout << "error";
} else{
int status;
close(pfd[0]);
close(pfd[1]);
waitpid(cpid, &status, 0);
perror("fork");
exit(1);
}
}
I test this code by inputting ls -l | wc -l , the execstring method breaks up my char arrays into tokens and executes them. I know it's a mess, i haven't done any C/C++ in a long time. It's strange because the child process can successfully execute the command, i can still see the output of ls -l. The error i'm getting is from wc -l, the console prints back "wc: invalid option --''. Try wc -- help for more information. Any ideas?
void execString(char *b){
char *tokens = strtok(b, " ");
pid_t child_pid;
int child_status;
child_pid = fork();
vector<char *> commandVector;
while(tokens != NULL){
commandVector.push_back(tokens);
tokens = strtok(NULL," ");
}
cout << tokens << endl;
//cout<< "execString"<<endl;
commandVector.push_back(NULL);
char **args = &commandVector[0];
int status = execvp(args[0], args);
}
I fixed it, the pipe indexes were reversed. I changed the exec command to a constant string to test if it works. It works fine. Gotta tune my execString.

CreateProcess and redirecting output

There are 2 apps.
AppCMD is a command line app and AppMAIN starts AppCMD with some command line args.
Unfortunately AppMAIN does not seem to handle the output off AppCMD very well and something is going wrong.
I'd like to log the calls to AppCMD and its output to see what is going on.
In order to do so I want to replace AppCMD with another binary AppWRAP that forwards the calls to a renamed AppCMD and logs it's output.
AppWRAP should act like a transparent Man-In-The-Middle.
For testing purposes I wrote a simple AppCMD that just outputs it's command line args:
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
cout << "#### Hello, I'm the test binary that wants to be wrapped." << endl;
if (argc < 2) {
cout << "#### There where no command line arguments." << endl;
}
else {
cout << "#### These are my command line arguments:";
for (int i = 1; i < argc; ++i) cout << " " << argv[i];
cout << endl;
}
cout << "#### That's pretty much everything I do ... yet ;)" << endl;
return 0;
}
I followed MSDN: Creating a Child Process with Redirected Input and Output to implement AppWrap but I got stuck since it does not return and I cant figure out why:
#include <iostream>
#include <sstream>
#include <Windows.h>
using namespace std;
const string TARGET_BINARY("TestBinary.exe");
const size_t BUFFSIZE = 4096;
HANDLE in_read = 0;
HANDLE in_write = 0;
HANDLE out_read = 0;
HANDLE out_write = 0;
int main(int argc, char *argv[])
{
stringstream call;
cout << "Hello, I'm BinTheMiddle." << endl;
//-------------------------- CREATE COMMAND LINE CALL --------------------------
call << TARGET_BINARY;
for (int i = 1; i < argc; ++i) {
call << " " << argv[i];
}
cout << "Attempting to call '" << call.str() << "'" << endl;
//------------------------------ ARRANGE IO PIPES ------------------------------
SECURITY_ATTRIBUTES security;
security.nLength = sizeof(SECURITY_ATTRIBUTES);
security.bInheritHandle = NULL;
security.bInheritHandle = TRUE;
security.lpSecurityDescriptor = NULL;
if (!CreatePipe(&out_read, &out_write, &security, 0)) {
cout << "Error: StdoutRd CreatePipe" << endl;
return -1;
}
if (!SetHandleInformation(out_read, HANDLE_FLAG_INHERIT, 0)) {
cout << "Stdout SetHandleInformation" << endl;
return -2;
}
if (!CreatePipe(&in_read, &in_write, &security, 0)) {
cout << "Stdin CreatePipe" << endl;
return -3;
}
if (!SetHandleInformation(in_write, HANDLE_FLAG_INHERIT, 0)) {
cout << "Stdin SetHandleInformation" << endl;
return -4;
}
//------------------------------ START TARGET APP ------------------------------
STARTUPINFO start;
PROCESS_INFORMATION proc;
ZeroMemory(&start, sizeof(start));
start.cb = sizeof(start);
start.hStdError = out_write;
start.hStdOutput = out_write;
start.hStdInput = in_read;
start.dwFlags |= STARTF_USESTDHANDLES;
ZeroMemory(&proc, sizeof(proc));
// Start the child process.
if (!CreateProcess(NULL, (LPSTR) call.str().c_str(), NULL, NULL, TRUE,
0, NULL, NULL, &start, &proc))
{
cout << "CreateProcess failed (" << GetLastError() << ")" << endl;
return -1;
}
// Wait until child process exits.
WaitForSingleObject(proc.hProcess, INFINITE);
// Close process and thread handles.
CloseHandle(proc.hProcess);
CloseHandle(proc.hThread);
//----------------------------------- OUTPUT -----------------------------------
DWORD dwRead;
CHAR chBuf[127];
while (ReadFile(out_read, chBuf, 127, &dwRead, NULL)) {
cout << "Wrapped: " << chBuf << endl;
}
return 0;
}
It seems like it is waiting for ReadFile to return. Can anybody spot what I'm doing wrong?
I call the binary this way:
> shell_cmd_wrapper.exe param1 param2
This is the console output but the binary does not return.
Hello, I'm BinTheMiddle.
Attempting to call 'TestBinary.exe param1 param2'
Wrapped:#### Hello, I'm the test binary that wants to be wrapped.
#### These are my command line arguments: param1 param2
#### That'sD
Wrapped: pretty much everything I do ... yet ;)
s to be wrapped.
#### These are my command line arguments: param1 param2
#### That'sD
(Please ignore that I don't clear the buffer)
Close the out_write and in_read handles after calling CreateProcess. Otherwise ReadFile on out_read will block when the pipe is empty because there's still a potential writer even after the child has exited -- the out_write handle in the current process.
Also, as noted by Harry Johnston in a comment, waiting for the process to exit before reading from the pipe can potentially cause a deadlock. The child will block on WriteFile if the pipe fills up.

create multiple processes with fork() that communicate with pipe without using exit() or wait()

wait(), exit() and signals are forbidden
only pipes allowed
A user gives an integer positive number-N and N-processes are created, father creates a child, that child becomes a father and creates another child and so on. Each one of the first processes (N-1) should wait to finish its process-child first and then itself. The initial process should print "1-My Process ID: ", the next process that's been created the number "2 My process ID: and my father's ID:" and so on.
my code. i don't have wait or exit instead i use return(-1).
but i didn't managed to print accordingly the numbers 1 my process id..., 2 my process id..., 3 my process id... and so on.
any ideas?
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
using namespace std;
/* Read characters from the pipe and echo them to stdout. */
void read_from_pipe (int file)
{
FILE *stream;
int c;
stream = fdopen (file, "r");
while ((c = fgetc (stream)) != EOF)
putchar (c);
fclose (stream);
}
/* Write some random text to the pipe. */
void write_to_pipe (int file)
{
FILE *stream;
stream = fdopen (file, "w");
fprintf (stream, "\n");
fprintf (stream, " ");
fclose (stream);
}
int main (void)
{
pid_t pid;
int mypipe[2];
int j = 1;
int i;
cout << "\nassume father is by default the first process\n" << "Please enter how child-processes you want: ";
cin >> i;
for( ; j < i; j++)
{
/* Create the pipe. */
if (pipe (mypipe))
{
fprintf (stderr, "Pipe failed.\n");
return (-1);
}
/* Create the child process. */
pid = fork ();
if (pid == (pid_t) 0)
{
/* This is the child process. Close other end first. */
pid = getpid();
close (mypipe[1]);
read_from_pipe (mypipe[0]);
printf("Child's ID: %d\n",pid);
sleep(0);
}
else if (pid > (pid_t) 0)
{
/* This is the parent process. Close other end first. */
pid = getpid();
close (mypipe[0]);
write_to_pipe (mypipe[1]);
printf("Dad's ID: %d\n",pid);
sleep(0);
}
else
{
/* The fork failed. */
fprintf (stderr, "Fork failed.\n");
return (-1);
}
}//end for
//close (mypipe[0]);
//write_to_pipe (mypipe[1]);
// printf("Dad's ID: %d\n",pid);
return (-1);
}// EOP
Recursion could be simpler than iteration because you want each child to in turn create another child. The trick to avoid wait is to have each parent to read on the read end of the pipe, and to have the child to close the write end just before returning without writing anything. Because the read will be blocked until either something has been written or the other end is closed.
You cannot be sure of the order in which the processes will actually end because you do not call wait, but you are sure that a parent cannot end before its childs has terminated its job.
Code could be:
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <iostream>
using std::cout;
using std::cin;
using std::cerr;
using std::endl;
int start_child(int i, int j) {
int my_pipe[2];
pid_t parent_pid, pid;
/* Create the pipe. */
if (pipe (my_pipe))
{
cerr << "Pipe failed." << endl;
return (-1);
}
/* Create the child process. */
parent_pid = getpid();
pid = fork ();
if (pid == (pid_t) 0) {
/* child */
pid = getpid();
close(my_pipe[0]);
cout << "I'm child " << j << "- my pid is " << pid <<
" - my parent's pid is " << parent_pid << endl;
if (i > 1) start_child(i - 1, j + 1);
if (pid == getpid()) cout << "End of child "<< j << endl;
close(my_pipe[1]);
}
else if (pid == (pid_t) -1) {
perror("forking");
close(my_pipe[0]);
close(my_pipe[1]);
return -1;
}
else {
/* parent */
close(my_pipe[1]);
char buf[2];
read(my_pipe[0], buf, 2); // wait for the child to close its pipe end
close(my_pipe[0]);
}
return 0;
}
int main (void)
{
pid_t pid = getpid();
int i;
cout << "\nassume father is by default the first process\n" << "Please enter how child-processes you want: ";
cin >> i;
cout << "I'm parent - my pid is " << pid << endl;
int cr = start_child(i, 1);
if (pid == getpid()) cout << "End of parent" << endl;
return cr;
}// EOP

Opening a pipe to a program and putting it into standard input

so I'm trying exec a program given by a user, say ls on unix system. And save all of that into a string such that I can store it in a map.
My the program I need to call is Variables[key] (which is map), which is the user submitted command, after which I want to save over it with the string created by it. This is code that I have tried to adapt to it, originally it was a redirection to a file, but having issues trying to use it with a string stream. Thanks in advance for any help <3
string key = argv[1];
int fds[2];
int count;
int fd;
char c;
pid_t pid;
pipe(fds);
if (fork() == 0)
{
dup2(fds[0], 0);
close(fds[1]);
stringstream ss;
while ((count = read(0, &c, 1)) > 0)
{
ss << &c;
}
string temp;
ss >> temp;
cout << temp << endl;
execlp("echo", "echo", NULL);
}
else if ((pid = fork()) == 0)
{
dup2(fds[1], 1);
close(fds[0]);
cout << argv[1] << argv << endl;
execvp(argv[1], argv);
perror("execvp failed");
}
else
{
waitpid(pid, NULL, 0);
close(fds[0]);
close(fds[1]);
}
Instead of this:
ss << &c;
Use this:
ss << c;
What you do is to write a char* into your stream, which is assumed to be a null terminated string. However, it's just a char not a null terminated string, so what you are doing is undefined behaviour. Instead, you should just write the character to the stream.

Why is my C++ array printing the same values?

I am working on a code where it will do Linux command piping. Basically in my code, it will parse the user input command, then run it using the execvp function.
However, to do this, I would need to know the command, as well as its parameters. I have been trying to get the parsing to work correctly, however, it seems that when I do a test case, the output from both of the arrays that store their respective programs is the same. The commands/parameters are stored in a char array called prgname1 and prgname2.
For instance, if I were to run my program with the parameter "ps aux | grep [username]", then the output of prgname1[0] and prgname2[0] are both [username]. They are supposed to be ps and grep, respectively.
Can anyone take a look at my code and see where I might be having an error which is causing this?
Thanks!
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <iostream>
#define MAX_PARA_NUM 5
#define MAX_COMMAND_LEN 1024
using namespace std;
int main(int argc, char *argv[]) {
char *prgname1[MAX_PARA_NUM], *prgname2[MAX_PARA_NUM];
char command[MAX_COMMAND_LEN];
int pfd[2];
pipe(pfd);
pid_t cid1, cid2;
char *full = argv[1];
char str[MAX_COMMAND_LEN];
int i = 0;
int j = 0;
int k = 0;
int ind = 0;
while (ind < strlen(full)) {
if (full[ind] == ' ') {
strncpy(command, str, i);
cout << command << endl;
prgname1[j] = command;
j++;
i = 0;
ind++;
}
else {
str[i] = full[ind];
i++;
ind++;
}
if(full[ind] == '|') {
i = 0;
j = 0;
ind+=2;
while (ind < strlen(full)) {
if (full[ind] == ' ') {
strncpy(command, str, i);
cout << command << endl;
prgname2[j] = command;
j++;
i = 0;
ind++;
}
else {
str[i] = full[ind];
i++;
ind++;
}
if (ind == strlen(full)) {
strncpy(command, str, i);
cout << command << endl;
prgname2[j] = command;
break;
}
}
}
}
// test output here not working correctly
cout << prgname1[0] << endl;
cout << prgname2[0] << endl;
// exits if no parameters passed
if (argc != 2) {
cout << "Usage:" << argv[0] << endl;
exit(EXIT_FAILURE);
}
// exits if there is a pipe error
if (pipe(pfd) == -1) {
cerr << "pipe" << endl;
exit(EXIT_FAILURE);
}
cid1 = fork(); // creates child process 1
// exits if there is a fork error
if (cid1 == -1 || cid2 == -1) {
cerr << "fork";
exit(EXIT_FAILURE);
}
// 1st child process executes and writes to the pipe
if (cid1 == 0) {
char **p = prgname1;
close(1); // closes stdout
dup(pfd[1]); // connects pipe output to stdout
close(pfd[0]); // closes pipe input as it is not needed
close(pfd[1]); // closes pipe output as pipe is connected
execvp(prgname1[0], p);
cerr << "execlp 1 failed" << endl;
cid2 = fork();
}
// 2nd child process reads from the pipe and executes
else if (cid2 == 0) {
char **p = prgname2;
close(0); // closes stdin
dup(pfd[0]); // connects pipe input to stdin
close(pfd[0]); // closes pipe input as pipe is connected
close(pfd[1]); // closes pipe output as it is not needed
execvp(prgname2[0], p);
cerr << "execlp 2 failed" << endl;
}
else {
sleep(1);
waitpid(cid1, NULL, 0);
waitpid(cid2, NULL, 0);
cout << "Program successfully completed" << endl;
exit(EXIT_SUCCESS);
}
return 0;
}
argv[1] gives you the first argument on the command line - not the entire command line. If you want the full list of command line arguments passed into the process, you will need to append argv[1], argv[2], ..., argv[argc - 1] together with a space between each.
Additionally, when you process it, you are setting the pointer for your prgname1[index] to command, so every time you set a given character pointer, they are all pointing to the same location (hence, they are all the same value). You need to allocate space for each element in prgname1 and copy command into it (using strncpy). Alternatively, using std::string and std::vector eliminates much of your current code.