Related
I'm making simple shell program and trying to exit it if the user enters "exit" and I've tried a few different keywords such as exit(), return 0, break;
This is my code:
void read_command(char path[], char *args[], char input[]) {
char *array[MAX], *ptr;
char *inputptr;
if ((inputptr = strchr(input, '\n')) != NULL) {
*inputptr = '\0';
}
int i = 0;
char *p = strtok(input, " ");
while (p != NULL) {
array[i++] = p;
p = strtok(NULL, " ");
}
for (int j = 0; j < i; j++) {
args[j] = array[j];
}
}
int main() {
char path[MAX];
char *args[MAX] = {NULL};
int status;
char input[MAX];
while (TRUE) {
printf(">> ");
fgets(input, sizeof(input), stdin);
if (fork() != 0) {
if (waitpid(-1, &status, 0) < 0) {
perror("waitpid error ");
}
} else {
read_command(path, args, input);
if (strcmp(input, "exit") == 0) {
exit(0);
}
strcpy(path, "/bin/");
strcat(path, args[0]);
if (execve(path, args, 0) < 0) {
perror("exec error ");
return EXIT_FAILURE;
}
}
}
return EXIT_SUCCESS;
}
When I return the strcomp() value it does give me 0 so I'm not sure why it's not working the program seems to completely ignore that exit statement and just carries on with the code, can someone explain how I could to this? Thank you.
You're calling exit in the child process that you just forked off. Instead, read the command in the parent process and then either exit or fork. By the way, you should really be checking that fork does not return -1:
read_command(path, args, input);
if (strcmp(input, "exit") == 0)
/* Don't pass zero here, that's not portable. */
exit(EXIT_SUCCESS);
pid_t child;
switch ((child = fork())) {
case -1:
perror("fork failed");
exit(EXIT_FAILURE);
case 0:
// call exec
default:
/* Don't pass -1 here if you know which child to wait for.
Also, you can just pass NULL if to status */
if (waitpid(child, NULL, 0) < 0)
perror("waitpid error ");
}
Ok so short story is:
I have a program that need to do X and it does 0.25X. I use 2 forks and 4 pipes, and I dont know how to debug this. (using eclipse c/c++ in linux env).
Long story:
I have a program that need to calculate gcd (Greatest common divisor) from a text file containing pairs of ints. This program has a father (main) and 2 childerns (forks) that need to talk to the father through pipes. (2 pipes for each childern.)
When I complie and run the program in ubuntu, I get no error but the proram doesnt complete its tasks. I have no idea where/why it breaks. How can I debug this? Im coding in eclipse c/c++ and debugger can't handle the forks.. When I debug it is reading all numbers from file (doesnt calculate gcd) but when I run in ubuntu terminal it only reads first line and breaks. here is the full code:
int main(int argc, char **argv) {
if (argc != 2 || strcmp(argv[1], "--help") == 0) {
fprintf(stderr, "usage: %s <FILE NAME>\n", argv[0]);
return EXIT_FAILURE;
}
int pfd_child1_r[2], pfd_child1_w[2], pfd_child2_r[2], pfd_child2_w[2];
if (pipe(pfd_child1_r) == -1 || pipe(pfd_child1_w) == -1
|| pipe(pfd_child2_r) == -1 || pipe(pfd_child2_w) == -1) {
perror("cannot pipe()");
return EXIT_FAILURE;
}
createChilds(pfd_child1_r, pfd_child1_w, pfd_child2_r, pfd_child2_w);
FILE *fp = fopen(argv[1], "r");
if (fp == NULL) {
perror("fopen(): ");
return EXIT_FAILURE;
}
char line[100];
char *token;
int numbers[2], num, line_count = 1, counter = 0, result = 0;
while (fgets(line, sizeof(line), fp) != NULL) {
token = strtok(line, " ");
while (token != NULL) {
num = atoi(token);
if (num < 1 || counter == 2) {
fprintf(stderr, "illegal input at line %d\n",
line_count);
return EXIT_FAILURE;
}
numbers[counter] = num;
counter++;
token = strtok(NULL, " ");
}
counter = 0;
if (line_count % 2 == 0) { // use first child
write(pfd_child1_w[1], &numbers[0], sizeof(int));
write(pfd_child1_w[1], &numbers[1], sizeof(int));
} else { // use second child
write(pfd_child2_w[1], &numbers[0], sizeof(int));
write(pfd_child2_w[1], &numbers[1], sizeof(int));
}
if (line_count > 1) { // after first run alternate to get result
if (line_count % 2 == 0) { // read from second child
read(pfd_child2_r[0], &result, sizeof(int));
printf("%d %d\t\tgcd: %d\n", numbers[0], numbers[1], result);
} else { // read from first child
read(pfd_child1_r[0], &result, sizeof(int));
printf("%d %d\t\tgcd: %d\n", numbers[0], numbers[1], result);
}
}
line_count++;
}
fclose(fp);
return EXIT_SUCCESS;
}
void createChilds(int pfd_child1_r[2], int pfd_child1_w[2], int pfd_child2_r[2],
int pfd_child2_w[2]) {
switch (fork()) {
case -1:
perror("cannot fork()");
exit(EXIT_FAILURE);
case 0: /* First child: */
if (close(pfd_child1_r[0]) == -1) { /* Read end is unused */
perror("cannot close()");
exit(EXIT_FAILURE);
}
if (close(pfd_child1_w[1]) == -1) { /* Write end is unused */
perror("cannot close()");
exit(EXIT_FAILURE);
}
/* Duplicate stdout on write end of pipe; close duplicated descriptor */
if (pfd_child1_w[1] != STDOUT_FILENO) { /* Defensive check */
if (dup2(pfd_child1_r[1], STDOUT_FILENO) == -1) {
perror("cannot dup2()");
exit(EXIT_FAILURE);
}
if (close(pfd_child1_r[1]) == -1) {
perror("cannot close()");
exit(EXIT_FAILURE);
}
}
/* Duplicate stdin on read end of pipe; close duplicated descriptor */
if (pfd_child1_w[1] != STDIN_FILENO) { /* Defensive check */
if (dup2(pfd_child1_w[0], STDIN_FILENO) == -1) {
perror("cannot dup2()");
exit(EXIT_FAILURE);
}
if (close(pfd_child1_w[0]) == -1) {
perror("cannot close()");
exit(EXIT_FAILURE);
}
}
execlp("./v1_child", "./v1_child", NULL); /* Writes to pipe */
exit(EXIT_SUCCESS);
default: /* Parent go to next child */
break;
}
switch (fork()) {
case -1:
perror("cannot fork()");
exit(EXIT_FAILURE);
case 0: /* Second child: exec 'wc' to read from pipe */
if (close(pfd_child2_r[0]) == -1) { /* Read end is unused */
perror("cannot close()");
exit(EXIT_FAILURE);
}
if (close(pfd_child2_w[1]) == -1) { /* Write end is unused */
perror("cannot close()");
exit(EXIT_FAILURE);
}
/* Duplicate stdout on write end of pipe; close duplicated descriptor */
if (pfd_child2_w[1] != STDOUT_FILENO) { /* Defensive check */
if (dup2(pfd_child2_r[1], STDOUT_FILENO) == -1) {
perror("cannot dup2()");
exit(EXIT_FAILURE);
}
if (close(pfd_child2_r[1]) == -1) {
perror("cannot close()");
exit(EXIT_FAILURE);
}
}
/* Duplicate stdin on read end of pipe; close duplicated descriptor */
if (pfd_child2_w[1] != STDIN_FILENO) { /* Defensive check */
if (dup2(pfd_child2_w[0], STDIN_FILENO) == -1) {
perror("cannot dup2()");
exit(EXIT_FAILURE);
}
if (close(pfd_child2_w[0]) == -1) {
perror("cannot close()");
exit(EXIT_FAILURE);
}
}
execlp("./v1_child", "./v1_child", NULL); /* Writes to pipe */
exit(EXIT_SUCCESS);
default: /* Parent falls through */
break;
}
/* Parent closes unused file descriptors for pipe */
if (close(pfd_child1_r[1]) == -1 || close(pfd_child1_w[0]) == -1
|| close(pfd_child2_r[1]) == -1 || close(pfd_child2_w[0]) == -1) {
perror("cannot close()");
exit(EXIT_FAILURE);
}
the second file is the gcd file, i still have not finish it and the loop the should keep geting the numbers is not present. but i just want to get the first line working properly then i will fix the rest.
int gcd(int n1, int n2) {
if (n2 == 0)
return n1;
return gcd(n2, n1 % n2);
}
int main(int argc, char **argv) {
int n1, n2, result;
if (scanf("%d %d", &n1,&n2) != 2) {
fprintf(stderr, "error reading numbers in child\n");
return -1;
}
if (n1 > n2)
result = gcd(n1, n2);
else
result = gcd(n2,n1);
printf("%d", result);
}
How to Debug
An easy way to debug is always to add fprintf(stderr, "...") statements to the child program. Then you can run the program and also see what the child processes are doing.
Transfer Values
Since you redirect stdin and stdout and use sscanf/printf in the v1_child program which calculates gcd, I assume you want to transfer the values as strings.
An easy way could be to use fprintf to write ints as formatted strings. You could use fdopen to associate a stream to an existing pipe file descriptor.
Accordingly, you must convert numbers from and to strings.
Variable Length Data and Buffered I/O
If you use strings to transfer values, each pair of values has a variable length. Typically, a newline character is used in a C program to signal a complete input record.
Another reason for reading/writing a whole line is that read/write calls can also transfer only a partial number of bytes. You must therefore know when an input record is completed. An alternative would be to have a binary format, which would automatically represent a format with fixed lengths.
By using streams you work with buffered I/O, with fflush you can ensure that all buffered data is written through the underlying write function of the stream.
Functions
One could divide the features into several functions to make the flow a little easier to understand.
Possible Improvements
This should perhaps already be a start.
Another possible improvement could be the use of strtol instead of atoi, since atoi does not perform error checking. Similar sscanf does not report conversion errors (e.g. non-numeric characters at the end of the line), at least we look at the number of assigned input items.
There are presumably still possibilities to improve the readability of the code.
With waitpid the exit status code of the child could be checked in the parent.
Program
Your code, slightly modified in the points mentioned above, could then look like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void create_pipe(int *);
void close_fd(int);
int child(const int *, const int *);
int read_input_line(FILE *fp, char *line, int max, int *numbers, int line_count);
void write_to_child(FILE *fp, const int *numbers);
int read_from_child(FILE *fp);
int main(int argc, char *argv[]) {
if (argc != 2 || strcmp(argv[1], "--help") == 0) {
fprintf(stderr, "usage: %s <FILE NAME>\n", argv[0]);
return EXIT_FAILURE;
}
int pfd_child1_r[2];
int pfd_child1_w[2];
int pfd_child2_r[2];
int pfd_child2_w[2];
create_pipe(pfd_child1_r);
create_pipe(pfd_child1_w);
create_pipe(pfd_child2_r);
create_pipe(pfd_child2_w);
pid_t pid1 = fork();
if (pid1 == 0) { //child 1
close_fd(pfd_child2_r[0]);
close_fd(pfd_child2_r[1]);
close_fd(pfd_child2_w[0]);
close_fd(pfd_child2_w[1]);
return child(pfd_child1_r, pfd_child1_w);
} else if (pid1 > 0) {
close_fd(pfd_child1_r[1]);
close_fd(pfd_child1_w[0]);
pid_t pid2 = fork();
if (pid2 == 0) { //child 2
close_fd(pfd_child1_r[0]);
close_fd(pfd_child1_w[1]);
return child(pfd_child2_r, pfd_child2_w);
} else if (pid2 > 0) {
close_fd(pfd_child2_r[1]);
close_fd(pfd_child2_w[0]);
FILE *fp_child1_w = fdopen(pfd_child1_w[1], "w");
FILE *fp_child2_w = fdopen(pfd_child2_w[1], "w");
FILE *fp_child1_r = fdopen(pfd_child1_r[0], "r");
FILE *fp_child2_r = fdopen(pfd_child2_r[0], "r");
if (!fp_child1_w || !fp_child2_w || !fp_child1_r || !fp_child2_r) {
perror("fdopen() failed");
return EXIT_FAILURE;
}
FILE *fp = fopen(argv[1], "r");
if (fp == NULL) {
perror("fopen(): ");
return EXIT_FAILURE;
}
char line[100];
int numbers[2], line_count = 0;
while (read_input_line(fp, line, sizeof(line), numbers, line_count) == 2) {
if (line_count % 2 == 0) {
write_to_child(fp_child1_w, numbers);
} else {
write_to_child(fp_child2_w, numbers);
}
if (line_count % 2 == 0) {
int result = read_from_child(fp_child1_r);
printf("%d %d\t\tgcd: %d\n", numbers[0], numbers[1], result);
} else {
int result = read_from_child(fp_child2_r);
printf("%d %d\t\tgcd: %d\n", numbers[0], numbers[1], result);
}
line_count++;
}
//fclose closes also associated file descriptor
fclose(fp_child1_w);
fclose(fp_child2_w);
fclose(fp_child1_r);
fclose(fp_child2_r);
fclose(fp);
return EXIT_SUCCESS;
} else {
perror("second fork failed");
return EXIT_FAILURE;
}
} else {
perror("first fork failed");
return EXIT_FAILURE;
}
}
int read_input_line(FILE *fp, char *line, int max, int *numbers, int line_count) {
char *token;
int num, counter = 0;
line[0] = '\0';
if (fgets(line, max, fp) != NULL) {
token = strtok(line, " ");
while (token != NULL) {
num = atoi(token);
if (num < 1 || counter == 2) {
fprintf(stderr, "illegal input at line %d\n", line_count + 1);
exit(EXIT_FAILURE);
}
numbers[counter] = num;
counter++;
token = strtok(NULL, " ");
}
}
return counter;
}
int read_from_child(FILE *fp) {
char buf[128];
int result = -1;
if (fgets(buf, sizeof(buf), fp)) {
if (sscanf(buf, "%d", &result) == 1)
return result;
}
return -1;
}
void write_to_child(FILE *fp, const int *numbers) {
fprintf(fp, "%d %d\n", numbers[0], numbers[1]);
fflush(fp);
}
int child(const int *pfd_child_r, const int *pfd_child_w) {
dup2(pfd_child_r[1], STDOUT_FILENO);
dup2(pfd_child_w[0], STDIN_FILENO);
close_fd(pfd_child_r[0]);
close_fd(pfd_child_r[1]);
close_fd(pfd_child_w[0]);
close_fd(pfd_child_w[1]);
execlp("./v1_child", "./v1_child", NULL);
fprintf(stderr, "execution of v1_child failed\n");
exit(EXIT_FAILURE);
}
void create_pipe(int *fd) {
if (pipe(fd) == -1) {
perror("cannot pipe()");
exit(EXIT_FAILURE);
}
}
void close_fd(int fd) {
if (close(fd) == -1) {
perror("cannot close()");
exit(EXIT_FAILURE);
}
}
The corresponding v1_child.c could look like:
#include <stdio.h>
#include <stdlib.h>
int gcd(int n1, int n2) {
if (n2 == 0)
return n1;
return gcd(n2, n1 % n2);
}
int main(void) {
int n1, n2, result;
char buf[128];
while(fgets(buf, sizeof(buf), stdin)) {
if (sscanf(buf, "%d %d", &n1, &n2) != 2) {
fprintf(stderr, "error reading numbers in child\n");
return -1;
}
if (n1 > n2)
result = gcd(n1, n2);
else
result = gcd(n2, n1);
printf("%d\n", result);
fflush(stdout);
}
return EXIT_SUCCESS;
}
Test
With the input of
5 25
49 14
64 462
1155 84
the output would be
5 25 gcd: 5
49 14 gcd: 7
64 462 gcd: 2
1155 84 gcd: 21
You write binary values to the pipes, but try to read text representation. So either use a variation of printf (like dprintf) in the parent, or (better) use read (and write) in the child.
I've written a basic shell in C, and I'm trying to catch a SIGTSTP signal from a child process. To do this, I've set up a handler for SIGCHLD, but the signal number being returned is 20, when it should be 24.
I have my SIGCHLD handler:
signal(SIGCHLD, trapChld);
void trapChld(int signo) {
printf("%d", signo);
}
This prints signal 20 when kill -SIGTSTP child_pid is run. Why might this be happening?
Here's my full code:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
int statusCode;
int foregroundMode = 0;
int bg = 0;
int bgPsArray[20];
int bgPsCount = 0;
int i;
char line[256];
pid_t popBgProcess() {
int size = sizeof(bgPsArray)/sizeof(bgPsArray[0]);
if (size > 0) {
return bgPsArray[size+1];
} else {
return 0;
}
}
void trapInterrupt(int _) {
int childStatus;
pid_t child;
while ((child = popBgProcess())) {
if(child != getpid()) {
kill(child, SIGKILL);
waitpid(child, &childStatus, 0);
}
}
}
void trapChld(int signo) {
printf("%d", signo);
if(signo == 24) {
if(foregroundMode == 0) {
write(1, "Entering foreground-only mode (& is now ignored)\n", 49);
write(1, ": ", 2);
fflush(stdout);
foregroundMode = 1;
} else {
write(1, "Exiting foreground-only mode\n", 29);
write(1, ": ", 2);
fflush(stdout);
foregroundMode = 0;
}
}
}
int getCommand() {
printf(": ");
fflush(stdout);
if(fgets(line, sizeof(line), stdin) != NULL) {
char *position = strchr(line, '\n');
*position = '\0'; // Replace '\n' with '\0'
if(foregroundMode == 1) { // Foreground mode on
if((position = strchr(line, '&')) != NULL) {
*position = '\0'; // Replace '&' with '\0'
}
bg = 0; // Ignore '&' so do not create background process
} else { // Foreground mode off
if((position = strchr(line, '&')) != NULL) {
*position = '\0'; // Replace '&' with '\0'
bg = 1; // Is a background process
} else {
bg = 0;
}
}
} else { // If input is null
return 0;
}
return 1;
}
void checkProcessCompletion() {
int status;
for(i=0; i<bgPsCount; i++) {
if(waitpid(bgPsArray[i], &status, WNOHANG) > 0) {
if(WIFEXITED(status)) { // If exit
printf("Background PID %d is done: exit value %d\n", bgPsArray[i], WEXITSTATUS(status));
fflush(stdout);
} else if(WIFSIGNALED(status)) { // If signal
printf("Background PID %d is done: terminated by signal %d\n", bgPsArray[i], WTERMSIG(status));
fflush(stdout);
}
}
}
}
int runCommand(int cmd) {
if(cmd == 0) { // Return if there was no command
return 0;
} else if(strcmp(line, "exit") == 0) {
exit(0);
} else if(strstr(line, "#")) { // Comment input (do nothing)
} else if(strcmp(line, "status") == 0) {
printf("exit value %d\n", statusCode);
fflush(stdout);
}
else if(strncmp("cd", line, strlen("cd")) == 0) {
if(line[2] == ' ') { // If space after 'cd' expect directory
char cwd[1024];
getcwd(cwd, sizeof(cwd));
char *path = strstr(line, " ");
if(path) {
path += 1;
char *value;
value = malloc(strlen(path));
memcpy(value, path, strlen(path));
*(value + strlen(path)) = 0;
sprintf(cwd, "%s/%s", cwd, value); // Directory to change to
free(value);
}
chdir(cwd); // cd to new directory
} else { // cd with no argument
char *home = getenv("HOME");
chdir(home); // cd to HOME directory
}
}
else { // System commands
pid_t pid, ppid;
int status;
char *command;
char *args[256];
int argCount;
command = strtok(line, " ");
// Create args array for execvp
args[0] = command;
argCount = 1;
args[argCount] = strtok(NULL, " ");
while(args[argCount] != NULL) { // Add arguments to array
argCount++;
args[argCount] = strtok(NULL, " ");
}
if((pid = fork()) < 0) { // Fork fails
perror("fork");
fflush(stdout);
exit(1);
}
if(pid == 0) { // Child process
for(i=0; i<argCount; i++) {
if(strcmp(args[i], "<") == 0) { // Redirecting input
if(access(args[i+1], R_OK) == -1) { // File is unreadable
perror("access");
fflush(stdout);
} else { // File is readable
int file = open(args[i+1], O_RDONLY, 0);
dup2(file, STDIN_FILENO);
close(file);
execvp(command, &command);
}
}
else if(strcmp(args[i], ">") == 0) { // Redirecting output
int file = creat(args[i+1], 7777);
dup2(file, STDOUT_FILENO);
close(file);
execvp(command, args);
} else { // No redirection
execvp(command, args);
}
}
perror("execvp"); // Error for execvp
exit(1);
} else { // Parent process
if (bg == 1) { // Background process
int status;
int process;
printf("Background PID: %d\n", pid);
fflush(stdout);
bgPsArray[bgPsCount] = pid; // Add process to background process array
bgPsCount++;
process = waitpid(pid, &status, WNOHANG);
} else { // Foreground process
int status;
waitpid(pid, &status, 0); // Wait on the process
if(WIFEXITED(status)) {
statusCode = WEXITSTATUS(status);
}
}
}
}
return 1;
}
int main(int argc, char *argv[], char *envp[]) {
// Creating 'junk' manually is necessary because output redirection is broken,
// and a large portion of the grading script is depedent upon it's existence.
FILE *fp = fopen("junk", "ab+");
const char *text;
fprintf(fp, "Junk in junkfile\n");
fclose(fp);
signal(SIGINT, trapInterrupt);
signal(SIGCHLD, trapChld);
while(1) {
checkProcessCompletion(); //Check the processes
int cmd = getCommand(); // Get command from user
int result = runCommand(cmd);
if (result == 0) {
break;
}
}
return 0;
}
You haven't told us what platform you're running on, so this is just a guess, but perhaps it's because that platform defines SIGTSTP as 20?
Linux does, for example:
$ grep SIGTSTP /usr/include/asm/signal.h
#define SIGTSTP 20
A better question is why do you think it should be 24? On AIX it's 18. On HP-UX it's 25. Various Cygwin headers define it as 8, 18, or 24 (because the Cygwin headers come from glib and are full of platform-specific conditional-compilation shenanigans); 18 is the actual value used at runtime.
On Solaris, now, it happens to be 24. I believe Solaris 2 inherited that from SVR4, and subsequent Solaris releases kept it. But the signal numbers are not standardized by any of the applicable specifications (SUS and its ancestors, such as POSIX and XPG3).
Don't assume the signal values are fixed. That's why you have signal.h.
Oh, and sigaction(2) is preferable to signal(2) on platforms that support it, which is most of them.
int main(void){
int n, user_length;
char userid[30];
char password[11];
if ((n = read(STDIN_FILENO, userid, 10)) == -1) {
perror("read");
exit(1);
} else if(n == 0) {
fprintf(stderr, "Error: could not read from stdin");
exit(1);
}
if (userid[n-1] == '\n')
userid[n-1] = '\0';
else
userid[n] = '\0';
if ((n = read(STDIN_FILENO, password, 10)) == -1) {
perror("read");
exit(1);
} else if (n == 0) {
fprintf(stderr, "Error: could not read from stdin");
exit(1);
}
if (password[n-1] == '\n')
password[n-1] = '\0';
else
password[n] = '\0';
strcat(userid, ":");
user_length = strlen(userid);
strcat(userid, password);
FILE *fp = fopen(PASSWORD_FILE, "r");
if (!fp) {
perror("fopen");
exit(1);
}
char line[MAXLINE];
while(fgets(line, sizeof(line) - 1, fp)) {
line[strlen(line) - 1] = '\0';
if (strcmp(userid, line) == 0)
exit(0); // found match
else if(strncmp(userid, line, user_length) == 0)
exit (2); // invalid password
}
exit(3); // no such user
}
Above is the implementation of validate.c, but how do I pass value such as userid and password to the function by using pipe(),dup2 or execl()
I used the following`
int main(void) {
char userid[10];
char password[10];
int pid;
int p[2][4];
char other[MAXSIZE];
/* Read a user id and password from stdin */
printf("User id:\n");
scanf("%s", userid);
printf("Password:\n");
scanf("%s", password);
/*Your code here*/
if (pipe(p[1]) == -1) {
perror("pipe");
}
if (pipe(p[0]) == -1) {
perror("pipe");
}
pid = fork();
if (pid != 0) {
close(p[1][0]);
close(p[0][0]);
dup2(p[1][1],STDIN_FILENO);
dup2(p[0][1],STDIN_FILENO);
close(p[1][1]);
close(p[0][1]);
int status;
if (wait(&status)!= -1) {
if (WIFEXITED(status)) {
printf("[%d] Child exited with %d\n", getpid(), WEXITSTATUS(status));
switch(WEXITSTATUS(status)){
case 0:
printf("found match\n");
break;
case 2:
printf("invalid password\n");
break;
case 3:
printf("No such user\n");
break;
default:
printf("error has occur\n");
break;
};
} else {
printf("[%d] Child exited abnormally\n", getpid());
}
}
} else if (pid == 0) {
close(p[1][1]);
close(p[0][1]);
dup2(p[1][0], fileno(stdout));
dup2(p[1][0], fileno(stdout));
execl("validate",other);
printf("what\n");
close(p[1][0]);
close(p[0][0]);
} else {
perror("fork");
exit(1);
}
return 0;
}
But the prompt always asks me for re-entering the input. What is wrong with this approach?( Note: I "execl" "validate" because it is an executable file that has been already created. The execl() I wrote simply calls the validate.c function )
As I said in the comments you probably do not need to spawn another process for this but You have an error in the way you call execl.
This:
execl("validate",other);
Should be:
execl(filename,list of arguments, NULL);
This is the documentation page. They use (char *) 0 which is the same as using NULL.
So, I thought I was on the right track with trying to imitate the bash shell, but I'm having issues piping. I am getting an error executing the second command. I was wondering if someone could explain to me how to fix this and why it's going wrong.
I'm very new to C & Linux commands so any supplemental information that could help me along the way would be also be appreciated.
Thank you so much for your time. My code is below, but there is a lot of it. My issue is occurring in the exec_pipe function. I would normally include what I have used for input and what I am getting for output, but my sample input is actually executable files my professor gave us for testing. Unfortunately, mine is not working like it does in the shell. I am just getting my error print out:
Inside Case 5
Inside Exec_Pipe
Error in Pipe EXECVP cmd2
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdbool.h>
#include <time.h>
#include <limits.h>
#include <fcntl.h>
#include <sys/wait.h>
#define BUFSIZE 1024
#define CSTRSIZE 100
#define CMDSIZE 30
#define DEBUG 1
//I referenced our blackboard source code files to create the fork functions and to deal with file descriptors
void exec_cmd(char** cmd1){
pid_t pid;
if((pid = fork()) < 0){
printf("Child Process Failed\n");
}else if(pid == 0){
if(execvp(cmd1[0], cmd1) < 0){
printf("Execution Failed\n");
exit(1);
}
}else{
wait(NULL);
}
}
void exec_cmd_in(char** cmd1, char* infile){
pid_t pid;
int fdi;
if((pid = fork()) < 0){
printf("Child Process Failed\n");
}else if(pid == 0){
fdi = open(infile, O_RDONLY);
if(fdi == -1){
printf("No Infile");
}
}
}
void exec_cmd_opt_in_append(char** cmd1, char* infile, char* outfile){
/* pid_t pid;
int fdi, fdo;
if((pid = fork()) < 0){
printf("Child Process Failed\n");
}else if(pid == 0){
fdo = open(outfile, O_RDWR | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
if(fdo == -1){
printf("No Outfile");
}
if(dup2(fdi, 0) == -1){
printf("Infile not updated");
}
if(dup2(fdo, 1) == -1){
printf("Outfile not updated");
}
close(fdi);
close(fdo);
if(execvp(cmd1[0], cmd1) < 0){
printf("Execution Failed\n");
exit(1);
}
}else{
wait(NULL);
} */
}
void exec_cmd_opt_in_write(char** cmd1, char* infile, char* outfile){
/* pid_t pid;
int fdi, fdo;
if((pid = fork()) < 0 ){
printf("Fork Error");
exit(1);
}else if(pid == 0 ){
fdo = open(outfile, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if(fdo == -1){
printf("No Outfile");
}
if(dup2(fdi, 0) == -1){
printf("Infile not updated");
}
if(dup2(fdo, 1) == -1){
printf("Outfile not updated");
}
close(fdi);
close(fdo);
if(execvp(cmd1[0], cmd1) < 0){
printf("Execution Failed\n");
exit(1);
}
}else{
wait(NULL);
}
*/
}
void exec_pipe(char** cmd1, char** cmd2){
pid_t pid;
int pipefd[2];
// pipe[1] is the write end of the pipe
// pipe[0] is the read end of the pipe
// making a pipe
printf("Inside Exec_Pipe\n");
pid = fork();
switch(pid){
case -1:
//error in fork
printf("Fork Error\n");
//Exit
exit(1);
case 0:
//child
break;
default:
//parent
wait(NULL);
}
//This will be executed by child process
if(pipe(pipefd) < 0 ) {
//error condition
printf("Pipe Error");
exit(1);
}
pid = fork();
switch(pid){
case -1:
//error in fork
printf("Fork Error\n");
//Exit
case 0:
//child
close(STDIN_FILENO);
//direct STDOUT to the pipe
dup2(pipefd[1], STDOUT_FILENO);
//Close descriptors
close(pipefd[0]);
close(pipefd[1]);
//Execute Command1
execvp(cmd1[0], cmd1);
//execvp should not return, so if it does
//there is an error!
printf("Error in EXECVP cmd1");
exit(1);
default:
//parent
close(STDIN_FILENO);
//direct input to the pipe
dup2(pipefd[0],STDIN_FILENO);
//close descriptors
close(pipefd[0]);
close(pipefd[1]);
//execute command 2
execvp(cmd2[0],cmd2);
//if execvp makes it back, error condition
printf("Error in Pipe EXECVP cmd2");
exit(1);
}
}
void exec_pipe_opt_in_append(char** cmd1, char** cmd2, char* infile, char* outfile){
}
void exec_pipe_opt_in_write(char** cmd1, char** cmd2, char* infile, char* outfile){
}
int parse_command(char* line, char** cmd1, char** cmd2, char* infile, char* outfile){
/*
(1)Create a bunch of flags to compare for the right return value
(2)Loop over the entire line and set the flags
(3)Add a bunch of if statements to compare flags
(4)If there is more than one flag for pipe, we can't handle it. Regurn 9.
(5)If there is &, we can't handle.
(6)Return the right value
*/
int pipe_found = 0;
int input_found = 0;
int redirection = 0;
int i = 0;
int spaces = 0;
int append = 0;
int special = 0;
while(line[i] != '\0'){
if(line[i] == '|'){
pipe_found++;
}
if(line[i] == '<'){
input_found = 1;
}
if((line[i] == '&') || (line[i] == '*') || (line[i] == '^') || (line[i] == '%') || (line[i] == '#') || (line[i] == '!') || (line[i] == '#') || (line[i] == '(') || (line[i] == ')')){
special = 1;
}
if(line[i] == '>'){
redirection = 1;
if(line[i+1] == '>'){
append = 1;
}
}
if(line[i] == ' '){
spaces++;
}
i++;
}
if((strlen(line) >=4) && (line[0] == 'q') && (line[1] == 'u') && (line[2] == 'i') && (line[3] == 't')){
return 0;
}
if((pipe_found == 0) && (special == 0)){
if((redirection == 0) && (input_found == 0)){
return 1;
}else if((redirection == 0) && (input_found == 1)){
return 2;
}else if(append == 1){
return 3;
}else if(redirection == 1){
return 4;
}
}else if((pipe_found == 1) && (special == 0)){
if((redirection == 0) && (input_found == 0)){
return 5;
}else if((redirection == 0) && (input_found == 1)){
return 6;
}else if(append == 1){
return 7;
}else if(redirection == 1){
return 8;
}
}
return 9;
}
//I referenced StackOverflow and some online libraries to get this tokenize function
char ** tokenize(char *str, char *delim, unsigned int *number_tokens) {
char *pch = strtok(str, delim);
unsigned int ntok = 0;
if(pch != NULL) {
ntok = 1;
}else{
return NULL;
}
char **tokens = realloc(NULL, sizeof(char *)*ntok);
tokens[ntok-1] = pch;
while(pch != NULL) {
pch = strtok(NULL, delim);
ntok++;
tokens = realloc(tokens, sizeof(char *)*ntok);
tokens[ntok-1] = pch;
}
if(number_tokens) {
*number_tokens = ntok;
}
return tokens;
}
//I referenced StackOverflow.com for this trim function
char *trim(char *str) {
char *end;
if(str == NULL){
return NULL;
}
while(isspace(*str)){
str++;
}
end = str + strlen(str) - 1;
while(end > str && isspace(*end)) {
end--;
}
*(end+1) = 0;
return str;
}
int main(int argc, char *argv[]){
int returnValue = 0;
char *infile = NULL;
char *outfile = NULL;
char **cmd = NULL;
char **cmd1_tokens = NULL;
char **cmd2_tokens = NULL;
char *input;
int current_cmd = 0;
/*
(1)If the user does not enter a command line argument, get one after typing "myshell-%"
(2)Call parse_command on the user input to get the right return value
(3)Begin parsing the user input within main
*/
if(argc == 1){
printf("myshell-%%\n");
fgets (input, 20, stdin);
returnValue = parse_command(input, cmd1_tokens, cmd2_tokens, infile, outfile);
cmd = tokenize(input, "|", NULL);
}else{
returnValue = parse_command(argv[1], cmd1_tokens, cmd2_tokens, infile, outfile);
cmd = tokenize(argv[1], "|", NULL);
}
int infileIt = 0;
while(cmd[current_cmd] != NULL) {
unsigned int number_tokens = 0;
char **infile_token = tokenize(cmd[current_cmd], "<", &number_tokens);
if(number_tokens > 1){
while(infile_token[infileIt] != NULL){
infileIt++;
}
}
if(infile_token[1] != NULL) {
number_tokens = 0;
char **infile_outfile_token = tokenize(infile_token[1], ">", &number_tokens);
if(number_tokens > 1){
infile = infile_outfile_token[0];
infile = infile_token[1];
}
}
number_tokens = 0;
char **outfile_token = tokenize(cmd[current_cmd], ">", &number_tokens);
if(number_tokens > 1){
outfile = outfile_token[1];
}
current_cmd++;
}
//Trim the in/outfiles
infile = trim(infile);
outfile = trim(outfile);
/*
Start breaking up cmd[0] and cmd[1] into smaller chunks and saving into the appropriate cmd
*/
cmd1_tokens = tokenize(cmd[0], " ", NULL);
if(cmd[1] != NULL){
cmd2_tokens = tokenize(cmd[1], " ", NULL);
}
int cmd1Args = 0;
while(cmd1_tokens[cmd1Args] != NULL){
cmd1Args++;
}
int cmd2Args= 0;
if(cmd2_tokens != NULL){
while(cmd2_tokens[cmd2Args] != NULL){
cmd2Args++;
}
}
int iterator = 0;
while((iterator < cmd1Args) && (cmd1Args != 0)){
printf("Cmd1: %s\n", cmd1_tokens[iterator]);
iterator++;
}
iterator = 0;
while((iterator < cmd2Args)&&(cmd2Args != 0)){
printf("Cmd2: %s\n", cmd2_tokens[iterator]);
iterator++;
}
if(infile != NULL){
printf("Infile: %s\n", infile);
}
if(outfile != NULL){
printf("Outfile: %s\n", outfile);
}
/*Use a switch statement to process all the return values (0 ot 9) of parse_command.
Our program should execute the “line” if the return code from parse_command
function is 0 to 8, that is the line is deemed “valid”. For return code 9,
our program simply output ”Not handled at this time!”.*/
switch(returnValue){
case 0 :
printf("Exiting Program.\n");
exit(1);
break;
case 1 :
printf("Inside Case 1\n");
exec_cmd(cmd1_tokens);
break;
case 2 :
printf("Inside Case 2\n");
exec_cmd_in(cmd1_tokens, infile);
break;
case 3 :
printf("Inside Case 3\n");
exec_cmd_opt_in_append(cmd1_tokens, infile, outfile);
break;
case 4 :
printf("Inside Case 4\n");
exec_cmd_opt_in_write(cmd1_tokens, infile, outfile);
break;
case 5 :
printf("Inside Case 5\n");
exec_pipe(cmd1_tokens, cmd2_tokens);
break;
case 6 :
printf("Inside Case 6\n");
//exec_pipe_in(cmd1_tokens, cmd2_tokens, infile);
break;
case 7 :
printf("Inside Case 7\n");
exec_pipe_opt_in_append(cmd1_tokens, cmd2_tokens, infile, outfile);
break;
case 8 :
printf("Inside Case 8\n");
exec_pipe_opt_in_write(cmd1_tokens, cmd2_tokens, infile, outfile);
break;
default :
printf("Inside Case 9\n");
printf("Not handled at this time!\n");
}
return 0;
}
Without having access to the input file that you're giving it, it's a little hard to say what's going on, but here are some tips for debugging it.
First, when something you don't understand is happening, it can be a good idea to strip it down to a minimal, working, self contained example that demonstrates the problem. Sometimes, just the process of cutting it down to that small example can help you to find the problem; but if not, it gives you a much smaller example to ask about.
Next, when putting in these print statements to debug what's going on, give yourself a little more context. Especially in the one that indicates an error; print out what the error is, and what the arguments were to the function that failed. Rather than just:
printf("Error in Pipe EXECVP cmd2");
You can use strerror to get a string representing the error number:
printf("Error %d in Pipe EXECVP cmd2: %s\n", errno, strerror(errno));
And you can also print out what the command and all of your arguments were:
for (char **arg = cmd2; *arg != NULL; ++arg) {
printf("cmd2[%ld] = %s", arg - cmd2, *arg);
}
Between printing out the actual error and printing out the command name and all of the arguments, that should help you to debug the problem.
If you could add that information to your question, and maybe cut your example down to a more minimal example as well as showing a minimal example of the input that causes a problem, we could probably help out a lot more.