Related
The code I've written finds out the number of words in multiple text files by creating multiple processes with each process being responsible for one file to count its words.
What I want to do is using pipes to find out total number of words in all files.
So the parent should:
creates a pipe between the each child and itself so it can get the number of words from each child
reports the total number of words in all the files by adding the numbers received through pipes
checks the exit status of each child and prints out how that child
exited
also let each child:
sends the number of the words to the parent via the pipe
send 0 as word count through the pipe to the parent if the file does
not exist or any other error happens
returns/exits with 0 if it is successfull in opening the file and
counting the words in that file, returns/exits with 1 if there is an
error (e.g., file does not exist etc.)
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#define MAX_CHAR 100
pid_t getpid(void);
pid_t getppid(void);
char* itoa(int i, char b[]){
char const digit[] = "0123456789";
char* p = b;
if(i<0){
*p++ = '-';
i *= -1;
}
int shifter = i;
do{ //Move to where representation ends
++p;
shifter = shifter/10;
}while(shifter);
*p = '\0';
do{ //Move back, inserting digits as u go
*--p = digit[i%10];
i = i/10;
}while(i);
return b;
}
int countWords(char * fp, int pid) {
FILE * file;
int words = 0;
char word[MAX_CHAR];
//execute this function only if child process of parent, no gradchild is allowed to execute this function!
if (pid == getppid()) {
file = fopen(fp, "r");
if (file == NULL) {
return -1;
}
//find string in the file and count the words.
while (fscanf(file, "%s", word) != EOF) {
words++;
}
return words;
} else {
return -1;
}
return 0;
}
int main(int argc, char * arvg[]) {
//if invalid arguments
if (argc < 2) {
fprintf(stderr, "ERROR: INVALID ARGUMENTS");
exit(-1);
}
int count = 0, pid, ppid, status, totalwords;
int result = -1;
int fd[2];
char string[100];
char readbuffer[80];
int *write_fd = &fd[1];
int *read_fd = &fd[0];
result = pipe(fd);
if(-1 == result){
perror("pipe");
return -1;
}
//creates (argc - 1) child processes using fork()
pid = (int) malloc((argc - 1) * sizeof(int));
//parent pid
ppid = getpid();
//each child process to count the number of words in each file
for (int i = 1; i < argc; i++) {
//child process
pid = fork();
if( pid == -1){
perror("failed to fork");
return -1;
}else if (pid == 0) {
// call a function to count the number of words in file arvg[i]
int words = countWords(arvg[i], ppid);
close(*read_fd);
if (words >= 0) {
printf("Child process pid_%d for %s :number of words is %d\n", i, arvg[i], words);
//I don't know how to write int into the pipe,so below might be wrong
write(*write_fd, words, 1);
return 0;
} else if (words == -1) {
printf("Child process pid_%d for %s :does not exists\n", i, arvg[I]);
//I don't know how to write int into the pipe,so below might be wrong
write(STDOUT_FILENO, words, 1);
exit(1);
}
} else {
close(*write_fd);
//and I have no idea how to read int from pipes
read(*read_fd, &readbuffer, 1);
totalwords += ???
close(*read_fd);
//Wait until all child processes exit/return
if (ppid == getpid()) {
wait( & status);
}
//inspect their exit codes, WEXITSTATUS = return code when child exits
if (WEXITSTATUS(status) == 1) {
count++;
}
}
}
printf("Main process created %d child processes to count words in %d files\n", argc - 1, argc - 1);
printf("Total words is %d", totalwords);
printf("%d files have been counted sucessfully!\n", argc - 1 - count);
printf("%d files did not exist.\n", count);
return 0;
}```
Can someone help me to figure out this? I don't really know how to achieve my goal with pipe.
found some issues with the code. I fixed them for you (however, I would have done the same thing slight differently)
reading and writing int from a pipe is pretty straight forward, just typecast correctly while reading or writing to an int.
malloc to a pid was not necessary. Also malloc returns a pointer and should have been typecasted with (int*)
always add the right includes while using calls. Manual page or reading about the calls while trying to understand the parameters passed and return values is extremely useful.
Enough said, here is your working code
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#define MAX_CHAR 100
pid_t getpid(void);
pid_t getppid(void);
char* itoa(int i, char b[]){
char const digit[] = "0123456789";
char* p = b;
if(i<0){
*p++ = '-';
i *= -1;
}
int shifter = i;
do{ //Move to where representation ends
++p;
shifter = shifter/10;
}while(shifter);
*p = '\0';
do{ //Move back, inserting digits as u go
*--p = digit[i%10];
i = i/10;
}while(i);
return b;
}
int countWords(char * fp, int pid) {
FILE * file;
int words = 0;
char word[MAX_CHAR];
//execute this function only if child process of parent, no gradchild is allowed to execute this function!
if (pid == getppid()) {
file = fopen(fp, "r");
if (file == NULL) {
return -1;
}
//find string in the file and count the words.
while (fscanf(file, "%s", word) != EOF) {
words++;
}
return words;
} else {
return -1;
}
return 0;
}
int main(int argc, char * arvg[]) {
//if invalid arguments
if (argc < 2) {
fprintf(stderr, "ERROR: INVALID ARGUMENTS");
exit(-1);
}
int count = 0, pid, ppid, status, totalwords = 0;
int result = -1;
int fd[2];
char string[100];
char readbuffer[80];
int *write_fd = &fd[1];
int *read_fd = &fd[0];
int recvd = 0;
result = pipe(fd);
if(-1 == result){
perror("pipe");
return -1;
}
//creates (argc - 1) child processes using fork()
//pid = (int) malloc((argc - 1) * sizeof(int));
//parent pid
ppid = getpid();
//each child process to count the number of words in each file
for (int i = 1; i < argc; i++) {
//child process
pid = fork();
if( pid == -1){
perror("failed to fork");
return -1;
}else if (pid == 0) {
printf ("%d child running \n", i);
// call a function to count the number of words in file arvg[i]
int words = countWords(arvg[i], ppid);
close(*read_fd);
if (words >= 0) {
printf("Child process pid_%d for %s :number of words is %d\n", i, arvg[i], words);
//I don't know how to write int into the pipe,so below might be wrong
write(*write_fd, (void *)&words, 1);
return 0;
} else if (words == -1) {
printf("Child process pid_%d for %s :does not exists\n", i, arvg[i]);
//I don't know how to write int into the pipe,so below might be wrong
write(STDOUT_FILENO, (void *)&words, 1);
exit(1);
}
} else {
close(*write_fd);
//and I have no idea how to read int from pipes
read(*read_fd, (void*)&recvd, 1);
totalwords += recvd;
printf("recvd %d \n", totalwords);
close(*read_fd);
//Wait until all child processes exit/return
if (ppid == getpid()) {
wait( & status);
}
//inspect their exit codes, WEXITSTATUS = return code when child exits
if (WEXITSTATUS(status) == 1) {
count++;
}
}
}
printf("Main process created %d child processes to count words in %d files\n", argc - 1, argc - 1);
printf("Total words is %d\n", totalwords);
printf("%d files have been counted sucessfully!\n", argc - 1 - count);
printf("%d files did not exist.\n", count);
return 0;
}
Well, the first time around, I did not focus on the algo. I fixed all of it. The problem is forking in a loop and reading and writing it would lead to wrong results. Moreover, Parent needs to look for EOF to ensure all read has happened. Anyways, Here is the code that should work
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#define MAX_CHAR 100
pid_t getpid(void);
pid_t getppid(void);
char* itoa(int i, char b[]){
char const digit[] = "0123456789";
char* p = b;
if(i<0){
*p++ = '-';
i *= -1;
}
int shifter = i;
do{ //Move to where representation ends
++p;
shifter = shifter/10;
}while(shifter);
*p = '\0';
do{ //Move back, inserting digits as u go
*--p = digit[i%10];
i = i/10;
}while(i);
return b;
}
// count word from file provided
int countWords(char * fp, int pid) {
FILE * file;
int words = 0;
char word[MAX_CHAR];
//execute this function only if child process of parent, no gradchild is allowed to execute this function!
if (pid == getppid()) {
file = fopen(fp, "r");
if (file == NULL) {
return -1;
}
//find string in the file and count the words.
while (fscanf(file, "%s", word) != EOF) {
words++;
}
return words;
} else {
return -1;
}
return 0;
}
//do everything related to child here in this function
void child_process(int write_fd, char *filename, int ppid)
{
// call a function to count the number of words in file argv[i]
printf("counting words of %s\n", filename);
int words = countWords(filename, ppid);
if (words >= 0) {
printf("Child process pid for %s :number of words is %d\n", filename, words);
write(write_fd, (void *)&words, 1);
close(write_fd);
exit(0);
} else if (words == -1) {
printf("Child process pid for %s :does not exist\n", filename);
write(STDOUT_FILENO, (void *)&words, 1);
close(write_fd);
exit(1);
}
return;
}
int main(int argc, char * argv[]) {
//if invalid arguments
if (argc < 2) {
fprintf(stderr, "ERROR: INVALID ARGUMENTS");
exit(-1);
}
int pid = 0;
int ppid = 0;
int totalwords = 0;
int fd[2] = {0};
int write_fd = 0;
int read_fd = 0;
int recvd = 0;
// open a pipe
if(-1 == pipe(fd)){
perror("pipe");
return -1;
}
// assign write_fd and read_fd
write_fd = fd[1];
read_fd = fd[0];
//parent pid
ppid = getpid();
//each child process to count the number of words in each file
pid = fork();
for (int i = 0; i < argc-1; i++)
{
//child process
if (pid == 0) {
close(read_fd);
child_process(write_fd, argv[i+1], ppid);
break;
} else {
pid = fork();
}
}
// don't let child run beyond this point
if (pid == 0) {
exit(0);
}
// parent only code
if (pid > 0)
{
close(write_fd);
while (read(read_fd, (void*)&recvd, 1) > 0)
{
wait(NULL);
totalwords += recvd;
}
close(read_fd);
}
printf("Main process created %d child processes to count words in %d files\n", argc - 1, argc - 1);
printf("Total words is %d\n", totalwords);
printf("%d files have been counted sucessfully!\n", argc - 1);
}
I am trying to find a way to make this algorithm run concurrent and be simultaneous. So far it has only 1 for-loop that reads each file and then makes a process for each file.
I believe this algorithm runs sequentially which is not what I want...
I thought about creating an outer for-loop where I put the wait(null) and read command. But when I tried it did not work, didn't produce output. Currently I have the wait(null) command in the parent process.
Any suggestions?
Code:
int main(int argc, char *argv[])
{
// ATTRIBUTES //
int freq[argc-1], ipc[argc][2], nbytes, i;// freq of words, pipes, counters
ssize_t errorfi;
char readbuffer[9999];
char** k = malloc(50);
char** op = malloc(50);
if(argc == ONE) { // there are no files given, throw error and close
fprintf(stderr, "There are no files found from %s\n", argv[0]);
exit(0);
}
for(i = 1; i < argc; i++) // creates pipes for ipc
pipe(ipc[i]);
pid_t pids[argc-1]; // array of pids
for(i = 1; i < argc; i++) { // reads input after position 0(a.out)
pid_t pid = fork(); // creates process
pids[i-1] = pid;
if( pid < 0 ) { // bad fork process: error
perror("bad fork");
exit(0);
}
else if(pid > 0) { //parent process
close(ipc[i][1]);
wait(NULL);
nbytes = read(ipc[i][0], readbuffer, sizeof(readbuffer));
if(nbytes > 0)
printf("%s\n", readbuffer);
}
else if(pid == 0) { // child process
close(ipc[i][0]);
k = inputReader(argv[i]); // finds filename,w1,w2,w3,uniqueWords
char info[50] = "";
strcat(info, k[0]);
strcat(info, " ");
strcat(info, k[1]);
strcat(info, " ");
strcat(info, k[2]);
strcat(info, " ");
strcat(info, k[3]);
strcat(info, " ");
strcat(info, k[4]);
int uniqueWordint = atoi(k[4]);
freq[i-1] = uniqueWordint; // freq of all uniqueWords
errorfi = write(ipc[i][1], info, strlen(info)+1); // writes info to pipe i
if (errorfi < 0 ) {
fprintf(stderr, "error found when writing in pipe errofi: %d\n", errorfi);
exit(0);
}
exit(0); // close process
} // closes child process
} // closes for-loop for each process
for(j = 0; j < argc-1; j++) {
wait(2); // if i put read command here it won't work
}
return(0); // close main
}
This is pretty much a sequential execution indeed. The parent-process enters a loop, forks a child-process, and then it won't continue to the next loop until that child-process is done.
What you could do is create a pid_t array of size argc, to store each fork()'s return value.
Also create a new loop after "for-loop for each process", where the parent-process would wait for all of his children, using wait(2) or waitpid(2), depending on whether you need to process each child's result in the specific order or not, and continue processing them (reading or whatever needed.
I am trying to run N concurrent processes in a C program. I've built a simple example that takes commands as arguments, creates a fork for each one, and executes it.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv)
{
int i;
for(i = 1; i < argc; i++)
{
pid_t pid = fork();
if(pid < 0)
{
fprintf(stderr, "forking error\n");
exit(1);
}
else if(pid > 0)
{
int status;
waitpid(pid, &status, 0);
printf("Command %s has completed successfully by PID=%d\n", argv[i], pid);
}
else
{
char cmd[1024];
sprintf(cmd, "%s", argv[i], i);
system(cmd);
_exit(1);
}
}
printf("Finished\n");
return 0;
}
This seems to run the processes correctly, but not concurrently. Any ideas as to what am I doing wrong?
EDIT: I've edited based on suggestions, but this also does not seem to work.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv)
{
int i;
pid_t *pids = malloc( sizeof(pid_t) * (argc) );
int *statuses = malloc( sizeof(int) * (argc) );
for(i = 1; i < argc; i++)
{
pid_t pid = fork();
if(pid < 0)
{
fprintf(stderr, "forking error\n");
exit(1);
}
else if(pid > 0)
{
//int status;
//waitpid(pid, &status, 0);
//printf("Command %s has completed successfully by PID=%d\n", argv[i], pid);
pids[i] = pid;
}
else
{
char cmd[1024];
sprintf(cmd, "%s > out.%d", argv[i], i);
system(cmd);
_exit(1);
}
}
int needtowait = 0;
do
{
needtowait = 0;
for(i = 1; i < argc; i++)
{
if(pids[i] > 0)
{
if(waitpid(pids[i], &statuses[i], 0) != 0)
{
pids[i] = 0;
char *successstr = "successfully";
if(statuses[i])
{
successstr = "unsuccessfully";
}
printf("Command %s has completed %s by PID=%d\n", argv[i], successstr, pids[i]);
}
}
else
{
needtowait = 1;
}
sleep(0);
}
} while(needtowait);
printf("Finished!\n");
return 0;
}
The reason you are not running these processes concurrently is in this line:
waitpid(pid, &status, 0);
The main process that forks out the child process waits for the child process to exit before continuing with the loop, and starting the next process.
Since you want to run your processes concurrently, you can do this: allocate an array of pid_t for process IDs, and fill it in inside the loop. Once you are out of the loop, you can wait for the individual processes to complete by executing waitpid calls in a loop.
pid_t *pids = malloc(argc * sizeof(pid_t));
for (int i = 0 ; i < argc ; i++) { // Start i at 0, not at 1
pid_t pid = fork();
if (pid < 0) {
...
} else if (pid > 0) {
pids[i] = pid;
} else {
char cmd[1024];
sprintf(cmd, "%s", argv[i+1], i+1);
system(cmd);
_exit(1);
}
}
for (int i = 0 ; i < argc ; i++) {
int status;
waitpid(pids[i], &status, 0);
printf("Command %s has completed successfully by PID=%d\n", argv[i+1], pids[i]);
}
Sure. Your parent process is waiting for the child process to finish executing before forking again. You're just running cmd sequentially N times.
I'm working on a mini shell for a college assignment. We have to read in the command, find the binary to execute from the path var, and execute command, both with and without pipes. I have everything working (I think) except for the pipe.
Through web searches I've been able to build a test program that use two hard coded commands and pipes one to the other, with the expected results. Now when I copy and paste that code into my actual program, the first command outputs fine (actually outputs the command as if there were no pipe), while the second I don't think actually does anything (the output from the first is not piped through to the second).
Here is the entire code:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#define BUFFSIZE 1024
#define MAXWORDS 17
#define MAXCHAR 64
static char *path;
extern char **environ;
//split cmd "string" on pipe (|) symbol
void split(char **pipe, char **left, char **right, int n)
{
int i, x;
for(i = 0; i < n; i++)
{
if (strchr(&pipe[i][0], '|') != 0)
{
for(x = 0; x < i; x++)
strcpy(left[x], pipe[x]);
left[x++] = 0;
break;
}
}
i++;
for(x = 0; i < n; x++)
strcpy(right[x], pipe[i++]);
right[x++] = 0;
}
//Find directory where cmd can be executed from (PATH or direct access)
char *finddir(char *s)
{
char *pp;
char *pf;
int ok;
strcpy(path, getenv("PATH"));
pp = strtok(path, ":");
while (pp != NULL)
{
pf = (char *)malloc(strlen(pp) + strlen(s) + 2);
if (pf == NULL)
{
fprintf(stderr, "Out of memory in finddir\n");
return NULL;
}
strcpy(pf,pp);
strcat(pf,"/");
strcat(pf,s);
ok = !access(pf, X_OK);
free(pf);
if (ok)
return pp;
pp = strtok(NULL, ":");
}
return NULL;
}
int cmdcheck(char *cmd, char *p)
{
char *dir;
if (strchr(p, '/') != NULL)
sprintf(cmd, "%s\0", p);
else
{
dir = finddir(p);
if (dir == NULL)
return 1;
else
sprintf(cmd, "%s/%s\0", dir, p);
}
return 0;
}
void runpipe(int pfd[], char *cmd1, char *p1[], char *cmd2, char *p2[])
{
int pid;
int status;
switch (pid = fork())
{
case 0: //Child
dup(pfd[0]);
close(pfd[1]); //the child does not need this end of the pipe
execve(cmd2, p2, environ);
perror(cmd2);
default: //Parent
dup(pfd[1]);
close(pfd[0]); //the parent does not need this end of the pipe
execve(cmd1, p1, environ);
perror(cmd1);
case -1: //ERROR
perror("fork-RP");
exit(1);
}
}
int main(void)
{
int status; //read status when reading cmd in
char ch; //character currently reading
int n, i, x; //(n) count of chars read; (i) cmd args iter; (x) cmd arg iter in cmd array
char buffer[BUFFSIZE]; //read buffer
char *token; //token var when splitting buffer
int pid0, pid1, pid2; //return ID from fork call
int which; //return value from wait (child pID that just ended)
char msg[100]; //messages to print out
char *cmd1, *cmd2; //cmds when piping
char *params[MAXWORDS]; //cmd parameters to send to execve
int fd[2]; //pipe file descriptors
char *pparam1[MAXWORDS]; //cmd "string" on left side of pipe
char *pparam2[MAXWORDS]; //cmd on right side of pipe
for(;;)
{
for (i = 0; i < MAXWORDS; i++)
params[i] = malloc(MAXCHAR);
n = 0;
write(1, "# ", 2);
for(;;)
{
status = read(0, &ch, 1);
if (status == 0)
return 0; //End of file
if (status == -1)
return 1; //Error
if(n == BUFFSIZE)
{
write(1, "Line too long\n", 14);
return 1;
}
buffer[n++] = ch;
if(ch == '\n')
break;
}
buffer[n] = '\0';
x = 0;
token = strtok(buffer, " \t\n\0");
while(token != NULL)
{
strcpy(params[x++], token);
token = strtok(NULL, " \t\n\0");
}
params[x] = 0;
path = getenv("PATH");
if (path == NULL)
{
fprintf(stderr, "PATH environment variable not found.\n");
return 1;
}
n = strlen(path);
path = (char *)malloc(n+1);
if (path == NULL)
{
fprintf(stderr, "Unable to allocate space for copy of PATH.\n");
return 1;
}
cmd1 = malloc(MAXCHAR);
cmd2 = malloc(MAXCHAR);
for (i = 0; i < MAXWORDS; i++)
pparam1[i] = malloc(MAXCHAR);
for (i = 0; i < MAXWORDS; i++)
pparam2[i] = malloc(MAXCHAR);
split(params, pparam1, pparam2, x);
//Check first cmd
if(cmdcheck(cmd1, pparam1[0]))
{
sprintf(msg, "cmd '%s' is not executable\n", pparam1[0]);
write(1, msg, strlen(msg));
break;
}
//Check second cmd
if(cmdcheck(cmd2, pparam2[0]))
{
sprintf(msg, "cmd '%s' is not executable\n", pparam2[0]);
write(1, msg, strlen(msg));
break;
}
pipe(fd);
switch (pid0 = fork())
{
case 0: //Child
switch (pid1 = fork())
{
case 0: //Child
runpipe(fd, cmd1, pparam1, cmd2, pparam2);
exit(0);
default:
exit(0);
//break;
case -1: //ERROR
perror("fork-2");
exit(1);
}
default: //Parent
which = wait(&status);
if (which == -1)
{
write(1, "wait failed\n", 12);
exit(1);
}
if (status & 0xff)
sprintf(msg, "process %d terminated abnormally for reason %d\n", which, status & 0xff);
else
sprintf(msg, "process %d terminated normally with status %d\n", which, (status >> 8) & 0xff);
write(1, msg, strlen(msg));
break;
case -1: //ERROR
perror("fork-1");
exit(1);
}
free(cmd1);
free(cmd2);
for (i = 0; i < MAXWORDS; i++)
free(pparam1[i]);
for (i = 0; i < MAXWORDS; i++)
free(pparam2[i]);
free(path);
for (i = 0; i < MAXWORDS; i++)
free(params[i]);
}
return 0;
}
Typing echo one | wc -l at the prompt will only output one with the respective wait print statement following. It has been a few years since I've used C, so am I on the right track?
Thanks.
EDIT:
Here is the runpipe function as it stands now. But the only thing that is printed is the wait statement.
void runpipe(int pfd[], char *cmd1, char *p1[], char *cmd2, char *p2[])
{
const int READ = 0;
const int WRITE = 1;
int pid;
int status;
switch (pid = fork())
{
case 0: //Child
close(pfd[WRITE]);
dup2(pfd[READ], STDIN_FILENO);
close(pfd[READ]);
execve(cmd2, p2, environ);
perror(cmd2);
default: //Parent
close(pfd[READ]);
dup2(pfd[WRITE], STDOUT_FILENO);
close(pfd[WRITE]);
execve(cmd1, p1, environ);
perror(cmd1);
case -1: //ERROR
perror("fork-RP");
exit(1);
}
}
There are a couple of things going on there that are contributing to the unexpected behavior.
The first is that you're forking too much. If you unroll your runpipe() function call into the switch statement in main(), you'll see that you reach the great-grandchild level:
switch (pid0 = fork())
{
case 0: // Child
switch (pid1 = fork())
{
case 0: // GRAND-Child
// function call to runpipe()
switch (pid = fork())
{
case 0: // GREAT-GRAND-Child
close(pfd[WRITE]);
dup2(pfd[READ], STDIN_FILENO);
close(pfd[READ]);
execve(cmd2, p2, environ);
perror(cmd2);
default: // GRAND-Child
close(pfd[READ]);
dup2(pfd[WRITE], STDOUT_FILENO);
close(pfd[WRITE]);
execve(cmd1, p1, environ);
perror(cmd1);
Which is not necessary. Fork once in main() and then call your runpipe() function.
Related to this issue is where you're creating your pipe. When you fork, the newly created child process inherits all of the parent process's open files (among many other things). This includes the default descriptors 0, 1, and 2 (stdin, stdout, and stderr), as well as any other open files, including the pipe you created called fd. This means that the parent, child, grandchild, and great-grandchild are all inheriting a copy of both ends of the pipe. You correctly close the unused ends inside the runpipe() function (the grandchild's and great-grandchild's copies), but the parent and child in your main() function also have copies!
Since the only pair of processes using the pipe are those created in runpipe(), you can move the declaration of fd and the call to pipe(2) into that function.
These two modifications will resolve your issues.
A completely unrelated issue that just relates to the flow of your shell is that your main() ends up doing its wait(2) on the "parent" process of the runpipe() function. Since that parent is the one running cmd1, your shell is going to return its prompt as soon as cmd1 finishes, instead of when the last command (cmd2 in this case) in the pipeline finishes. You can see the behavioral difference by running something like echo | sleep 10 into your shell and a real shell.
The dup function duplicates a file descriptor, and returns the new duplicate. However, this will not work, as stdin in the child still exists, and the new file descriptor will not be put in place of the standard input.
You must close the standard input file descriptor first, before doing dup. Or use dup2 which will close the destination file descriptor automatically first before doing the duplication:
dup2(pfd[0], STDIN_FILENO);
I'm trying to implement pipelining in an Operating Systems shell project by creating a number of FIFO pipes using mkfifo(). Currently, the program seems to hang after opening the first FIFO for writing. Additionally, the FIFO appears when doing an ls on the working directory. Am I using freopen() wrong in this instance?
void execute_external()
{
int backgrounding = 0;
if (raw_command[strlen(raw_command)-1] == '&')
{
pmesg(2, "Backgrounding requested.\n");
raw_command[strlen(raw_command)-1] = '\0';
raw_command = realloc(raw_command, strlen(raw_command)-1);
backgrounding = 1;
}
int numCommands = 1;
char **commands;
commands = malloc(sizeof(char *));
if(strstr(raw_command, "|") != NULL)
{
numCommands = separate_pipeline_commands(commands);
}
else
{
commands[0] = malloc(strlen(raw_command) * sizeof(char));
commands[0] = raw_command;
}
int i;
char *fifo = malloc(MAXFIFOLEN);
for (i = 0; i < numCommands; i++)
{
char **parameters_array = malloc(strlen(commands[i]) * sizeof(char *));
int num_params;
num_params = str_to_str_array(commands[i], parameters_array);
pmesg(2, "Command is %s.\n", commands[i]);
if (numCommands > 1)
{
int j;
for (j = 0; j < numCommands - 1; j++)
{
sprintf(fifo, "fifo%i", j);
mkfifo(fifo, S_IWUSR | S_IRUSR );
}
}
pid_t pid = fork();
pmesg(2, "Process forked. ID = %i. \n", pid);
if (pid < 0)
{
fprintf(to_write_to, "Could not fork a process to complete the external command.\n");
exit(EXIT_FAILURE);
}
int status;
if (pid == 0) // This is the child process
{
pmesg(1, "input: [%s] output: [%s] input: %d, output: %d backgrounding is %d\n",input_file_name, output_file_name, redirect_input, redirect_output, backgrounding);
if (numCommands > 1 && i == 0) // we may be pipelining and this is the first process
{
sprintf(fifo, "%s%i", "fifo", i);
printf("Initial output: %s.\n", fifo);
freopen(fifo, "w", stdout);
//~ unlink(fifo);
}
else if (numCommands > 1 && i !=0 && (i != numCommands-1)) // we are pipelining and this is not the first or last process
{
sprintf(fifo, "%s%i", "fifo", i-1);
printf("Input for process %i: %s.\n", i, fifo);
freopen(fifo, "r", stdin);
//~ unlink(fifo);
sprintf(fifo, "%s%i", "fifo", i+1);
printf("Output for process %i: %s.\n", i, fifo);
freopen(fifo, "w", stdout);
//~ unlink(fifo_2);
}
else if (numCommands != 1 &&i == numCommands)
{
char *fifo = malloc(strlen("fifo")+2);
sprintf(fifo, "%s%i", "fifo", i);
freopen(fifo, "r", stdin);
free(fifo);
}
if(redirect_output == 1)
{
freopen(output_file_name, "w", stdout);
}
if(redirect_input == 1)
{
freopen(input_file_name, "r", stdin);
}
if (backgrounding != 0)
{
freopen("/dev/null", "w", stdout);
}
pmesg(2, "This is the child process, running execlp.\n");
pmesg(1, "Command: [%s]\n", parameters_array[0]);
if (execvp(parameters_array[0], parameters_array) < 0)
{
fprintf(to_write_to, "Could not execute the external command. errno: %i.\n", errno);
exit(EXIT_FAILURE);
}
else { pmesg(2, "Executed the child process.\n");}
}
else
{
if (backgrounding != 0)
{
enqueue(&process_queue, pid, clock(), 0, 0);
printQueue(process_queue);
}
if (backgrounding == 0) { while(wait(&status) != pid); }// Wait for the child to finish executing
}
pmesg(1, "The child has finished executing.\n");
free(parameters_array);
}
free(commands);
}
I've placed some comments in-line to your code below, but XAder's summary is worth reading first, to help understand the big picture before my detailed nitpicks.
I suggest writing out, in very long form, the exact code you would execute for a pipeline of two commands, then the code for a pipeline of three commands, and then four.. and then place a loop around the duplicated code. (The fact that you've only got one fork() suggests to me that you did the code for two commands, but not three. :)
Hope this helps.
/* if numCommands == 1 or 0, the rest is probably useless too
so this should guard the entire routine, not just the routine
that creates FIFOs */
if (numCommands > 1)
{
int j;
for (j = 0; j < numCommands - 1; j++)
{
char *fifo = malloc(strlen("fifo")+1); /* BUG #1 */
sprintf(fifo, "%s%i", "fifo", j); /* BUG #2 */
mkfifo(fifo, S_IWUSR | S_IRUSR );
free(fifo); /* messy */
}
}
/* Bug #1 The malloc(strlen()+1) is only good for "fifo", doesn't
actually allocate space for your number. Don't forget the
ascii NUL character at the end of the string. Add another +1. */
/* Bug #2 works for numCommands < 10; with 10 commands, you'll need to +2
for two characters, and so on */
/* messy -- maybe just have a 'char fifo[MAXFIFOLEN];' allocation,
and set MAXFIFOLEN to 255 or 4096 or something. No need to allocate
and free a simple little buffer a dozen times. */
pid_t pid = fork();
/* You should probably place fork() in a for loop; one new process
per command. Another choice is to write this whole routine recursively
which might be adding too many moving parts into one program */
pmesg(2, "Process forked. ID = %i. \n", pid);
/* Note that parent and child get different values of 'pid', so printing
the value of pid here may be very confusing */
int status;
if (fork < 0) /* BUG #3 fork() is a function; should be pid */
{
fprintf(to_write_to, "Could not fork a process to complete the external command.\n");
/* perror() is a wonderful routine! it helps users know _why_ something failed */
exit(EXIT_FAILURE);
}
if (pid == 0) // This is the child process
/* only ONE path below will ever execute; this is why your fork()
should be in a loop over numCommands, so each command's process
can get its own individual stdin and stdout hooked together */
{
pmesg(1, "input: [%s] output: [%s] input: %d, output: %d backgrounding is %d\n",input_file_name, output_file_name, redirect_input, redirect_output, backgrounding);
if (numCommands > 1 && i == 0) // we may be pipelining and this is the first process
{
char *fifo = malloc(strlen("fifo")+2);
sprintf(fifo, "%s%i", "fifo", i); /* could just be sprintf(fifo, "fifo%i", i); */
printf("Initial output: %s.\n", fifo);
freopen(fifo, "w", stdout);
//~ unlink(fifo);
free(fifo);
}
else if (numCommands > 1 && i !=0 && (i != numCommands-1)) // we are pipelining and this is not the first or last process
{
char *fifo = malloc(strlen("fifo")+2);
sprintf(fifo, "%s%i", "fifo", i-1);
printf("Input for process %i: %s.\n", i, fifo);
freopen(fifo, "r", stdin);
//~ unlink(fifo);
char *fifo_2 = malloc(strlen("fifo")+2);
sprintf(fifo_2, "%s%i", "fifo", i+1);
printf("Output for process %i: %s.\n", i, fifo);
freopen(fifo_2, "w", stdout);
//~ unlink(fifo_2);
free(fifo);
free(fifo_2);
}
else if (numCommands != 1 &&i == numCommands)
{
char *fifo = malloc(strlen("fifo")+2);
sprintf(fifo, "%s%i", "fifo", i);
freopen(fifo, "r", stdin);
free(fifo);
}
In general:
bad memory allocation for mkfifo filename
according to the code there's logic only for the child process and it can go only by one of 3 paths so always only one process opens file and according to the man pages it shall be blocked until two parties will open fh.
i, numofcommands and logic are very unclear - Try this approach