So I am trying to parallel process, and use execv. The problem occurs is that when I parallel process, execv only runs in one child not both. I know that both child process's runs because of the printf statements about the pid.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
int main(int argc, char * argv[]) {
char input[100];
char * path[15];
path[0] = "/bin/";
char str[200];
FILE * fp;
if (argc == 2) {
if ((fp = fopen(argv[1], "r")) == NULL) {
printf("Error! opening file");
// Program exits if file pointer returns NULL.
exit(1);
}
}
do {
if (argc != 2) {
printf("dash> ");
fgets(input, 100, stdin);
} else if (argc == 2) {
if (fgets(input, 100, fp) == NULL)
exit(0);
printf(input);
}
input[strcspn(input, "\n")] = '\0';
char * token = strtok(input, " ");
int c = 0;
if ((strcmp(token, "exit") != 0) && (strcmp(token, "cd") != 0) && (strcmp(token, "path") != 0)) {
do {
if (path[c] == NULL) {
printf("p error");
break;
}
strcpy(str, path[c]);
strcat(str, token);
c++;
} while (access(str, X_OK) == -1);
}
if (strcmp(token, "exit") == 0) {
exit(0);
} else if (strcmp(token, "cd") == 0) {
token = strtok(NULL, " ");
if (chdir(token) == -1)
printf("errorcd");
} else if (strcmp(token, "path") == 0) {
int counter = 0;
while (path[counter] != NULL) {
path[counter] = strtok(NULL, " ");
counter++;
}
} else {
char * myargs[15];
char * par[15];
myargs[0] = token;
int counter = 0;
int track = 0;
int and = 1;
while (myargs[counter] != NULL) // &&(strcmp(myargs[counter],">"))!=0){
{
counter++;
myargs[counter] = strtok(NULL, " ");
}
int a = 0;
int i = 0;
int j = 0;
int t = 0;
// printf(myargs[i]);
for (i = 0; i <= counter - 1; i++) {
if (strcmp(myargs[i], "&") == 0)
and++;
}
int temp = and + 1;
// printf("No. %d",and);
int rc[15];
for (i = 0; i < and; i++) {
a = 0;
for (j = t; j <= counter - 1; j++) {
par[a] = myargs[j];
a++;
if (strcmp(myargs[j], "&") == 0) {
t = j + 1;
break;
}
}
if (and == 0)
par[j + 1] = NULL;
else
par[j] = NULL;
rc[i] = fork();
if (rc[i] < 0) // fork failed
{
printf("Error");
return 0;
} else if (rc[i] == 0) // child process
{
printf("\nhello, I am child (pid:%d)\n", (int) getpid());
if (j > 1) {
if (strcmp(par[j - 2], ">") == 0) {
int fd = open(par[j - 1], O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
dup2(fd, 1); // make stdout go to file
dup2(fd, 2); // make stderr go to file - you may choose to not do this
// or perhaps send stderr to another file
// }
par[counter - 2] = NULL;
}
}
execv(str, par);
printf("failed");
}
} // parent process
int status;
pid_t pid;
while (temp > 0) {
// waitpid(pid[i], 0, 0);
pid = wait( & status);
printf("Child with PID %ld exited with status 0x%x.\n", (long) pid, status);
--temp; // TODO(pts): Remove pid from the pids array.
}
}
} while (strcmp(input, "exit") != 0);
return 0;
}
I know this code is pretty bad, I have not coded in a while and am a little rusty. Anyway here is an example input and output.
dash> pwd & ls
hello, I am child (pid:40037)
hello, I am child (pid:40036)
/home/012/a/ax/axh161330/project1
Child with PID 40036 exited with status 0x0.
Child with PID 40037 exited with status 0x8b.
Child with PID -1 exited with status 0x8b.
As you can see, only one execv command executed (pwd
), while the other never happened (ls
). The first command will always work, while the 2nd will not happen.
This adaptation of your code works, at least for pwd & ls
as the command line. One of the problems was that the loop to find the command path name was in the wrong place. Another problem was that the wait()
loop was in the wrong place.
Another problem was that the command arguments were not properly separated.
The code shows the printing (quite a lot of it) that I added to make sure I understood what was going on — and which highlighted some of the problems.
Source code (exec29.c
):
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
char input[100];
char *path[15];
path[0] = "/bin/";
FILE *fp;
if (argc == 2)
{
if ((fp = fopen(argv[1], "r")) == NULL)
{
fprintf(stderr, "Error! opening file %s\n", argv[1]);
exit(1);
}
}
do
{
if (argc != 2)
{
printf("dash> ");
if (fgets(input, 100, stdin) == NULL)
exit(0);
}
else if (argc == 2)
{
if (fgets(input, 100, fp) == NULL)
exit(0);
}
input[strcspn(input, "\n")] = '\0';
printf("%s\n", input);
char *token = strtok(input, " ");
int temp = 0;
if (strcmp(token, "exit") == 0)
{
exit(0);
}
else if (strcmp(token, "cd") == 0)
{
token = strtok(NULL, " ");
if (chdir(token) == -1)
fprintf(stderr, "error: cd %s\n", token);
}
else if (strcmp(token, "path") == 0)
{
int counter = 0;
while (path[counter] != NULL)
{
path[counter] = strtok(NULL, " ");
if (path[counter] != NULL)
printf("PATH: %s\n", path[counter]);
counter++;
}
}
else
{
char *myargs[15];
char *par[15];
myargs[0] = token;
int counter = 0;
int n_cmds = 1;
while (myargs[counter] != NULL)
{
counter++;
myargs[counter] = strtok(NULL, " ");
}
for (int i = 0; i < counter; i++)
printf("%d: [[%s]]\n", i, myargs[i]);
for (int i = 0; i < counter; i++)
{
if (strcmp(myargs[i], "&") == 0)
n_cmds++;
}
temp = n_cmds;
printf("Num commands: %d\n", n_cmds);
int t = 0;
for (int i = 0; i < n_cmds; i++)
{
int a = 0;
for (int j = t; j < counter; j++)
{
if (strcmp(myargs[j], "&") == 0)
{
t = j + 1;
break;
}
par[a++] = myargs[j];
}
par[a] = NULL;
int rc = fork();
if (rc < 0)
{
fprintf(stderr, "Error: fork failed\n");
return 1;
}
else if (rc == 0)
{
char str[200];
printf("hello, I am child (pid:%d)\n", (int)getpid());
int c = 0;
do
{
if (path[c] == NULL)
{
fprintf(stderr, "path error - no PATH left\n");
break;
}
strcpy(str, path[c++]);
strcat(str, par[0]);
} while (access(str, X_OK) == -1);
printf("Cmd: [[%s]]\n", str);
char **args = par;
while (*args != NULL)
printf("[[%s]]\n", *args++);
fflush(stdout);
execv(str, par);
fprintf(stderr, "%s: failed\n", str);
exit(1);
}
}
}
while (temp > 0)
{
int status;
pid_t pid = wait(&status);
printf("%d: Child with PID %ld exited with status 0x%.4x.\n",
(int)getpid(), (long)pid, status);
--temp;
}
} while (strcmp(input, "exit") != 0);
return 0;
}
Given a file test-exec
containing:
pwd & ls
pwd -P & ls -l
I got the example output (exec29 test-exec
):
pwd & ls
0: [[pwd]]
1: [[&]]
2: [[ls]]
Num commands: 2
hello, I am child (pid:4247)
Cmd: [[/bin/pwd]]
[[pwd]]
hello, I am child (pid:4248)
Cmd: [[/bin/ls]]
[[ls]]
/Users/jleffler/soq/so-5225-1783
4246: Child with PID 4247 exited with status 0x0000.
exec23.c exec29 exec29.c exec29.dSYM exec97.c makefile test-exec test-exec-2
4246: Child with PID 4248 exited with status 0x0000.
pwd -P & ls -l
0: [[pwd]]
1: [[-P]]
2: [[&]]
3: [[ls]]
4: [[-l]]
Num commands: 2
hello, I am child (pid:4249)
Cmd: [[/bin/pwd]]
[[pwd]]
[[-P]]
hello, I am child (pid:4250)
Cmd: [[/bin/ls]]
[[ls]]
[[-l]]
/Users/jleffler/soq/so-5225-1783
4246: Child with PID 4249 exited with status 0x0000.
total 80
-rw-r--r-- 1 jleffler staff 3722 Sep 10 20:43 exec23.c
-rwxr-xr-x 1 jleffler staff 9644 Sep 10 21:41 exec29
-rw-r--r-- 1 jleffler staff 4054 Sep 10 21:41 exec29.c
drwxr-xr-x 3 jleffler staff 96 Sep 10 21:05 exec29.dSYM
-rw-r--r-- 1 jleffler staff 4549 Sep 10 21:45 exec97.c
-rw-r--r-- 1 jleffler staff 163 Sep 10 21:41 makefile
-rw-r--r-- 1 jleffler staff 24 Sep 10 21:47 test-exec
-rw-r--r-- 1 jleffler staff 34 Sep 10 21:38 test-exec-2
4246: Child with PID 4250 exited with status 0x0000.
I did remove the code that seemed to be related to I/O redirection; I didn't remove the code related to cd
, exit
, or path
, but neither did I test them.
One minor, obscure point: the standard header <iso646.h>
defines a macro and
that expands to &&
. It's best not to use any of the names from that header as variable names, though it is rare indeed that people use the header in C (but the names are reserved as 'alternative representations' in C++).