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.
Related
This question follows from my attempts to implement
http://www.microhowto.info/howto/capture_the_output_of_a_child_process_in_c.html
and
https://linux.die.net/man/2/pipe
I'm writing a shell program; the intention is that, eventually, it can execute commands and pipe them to another program. As such, I require the stdout of a child process directly, rather than outputting to terminal. I attempted to use the above guides, but I have a problem: The pipe is always empty. It just doesn't work. I have absolutely no clue why. Here's my code:
int pipefd[2];
pid_t pid;
pid = fork();
char buf;
const char* arg = "/bin/ls";
char *args[] = {"/bin/ls", (char *) 0};
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
if(pid<0) {
std::cout << "Fork() failed!." << std::endl;
exit(EXIT_FAILURE);
} else if (pid == 0) { //According to everything I could find on the internet, pipe should work.
dup2(pipefd[1], STDOUT_FILENO); // It does not. I don't know why.
close(pipefd[1]);
close(pipefd[0]);
execv(arg, args);
std::cout << "Child Error! " << errno << std::endl;
perror("execv");
exit(EXIT_FAILURE);
} else {
close(pipefd[1]);
wait(NULL);
while (read(pipefd[0], &buf, 1) > 0){
write(STDOUT_FILENO, &buf, 1);
}
write(STDOUT_FILENO, "\n", 1);
close(pipefd[0]);
}
I'm on a laptop with Ubuntu 15.04
Also, the pipe DOES work if I write/read inside one process.
Edit: Also, the execv does work - If I remove the dup2, it outputs directly to terminal and works.
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
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.
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.
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.