In the provided code below, the redirection methods work, however i've been having trouble with getting the pipe function to work.
This is how I'm viewing the flow of the code (at least for the pipe) and correct me if I'm wrong: When the array containing the users command (argv[]) is being traversed and checked for pipe declarations ("|"), for however many ("|") it detects, it will do that number of processes (processes++). It will then clear out the user command for future use, and go through the checks to continue the program.
I will also say I only have processes declared and iterated once, and I wasn't sure if I return it or implement it somewhere else.
Here is what I have so far:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>
int main()
{
char *path, //shell system path indicator
*argv[20], //user command
buf[80], //will pass user command to argv
n, //user input
*p; //points to buffer
int m, // represents size
status, // checks if ant conditions violate the while loop
inword,
check,
continu;
while(1) //an infinite loop to keep the shell running
{
inword = 0; //set as false
p = buf; // p represents buffer
m = 0; //size of argv array
continu = 0; //set as false
printf("\nshhh> "); //display of shell prompt
while ((n = getchar()) != '\n' || continu) //while user input is not end of line OR
{
if (n == ' ') // if input is a space
{
if (inword) // if 0
{
inword = 0; // remains 0
*p++ = 0; //increment to next position in buffer[80]
}
}
else if (n == '\n') //if user input == end of line
continu = 0; // continue is 0 which breaks the while loop
else if (n == '\\' && !inword) // if user input == '\\' and inword is not 0
continu = 1; // continu is changed to 1 and you stay in while loop
else
{
if (!inword)
{
inword = 1;
argv[m++] = p; //next iteration of user command contains buffer
*p++ = n; //next slot in buffer contains user input
}
else
*p++ = n; //next slot in buffer contains user input
}
}
*p++ = 0; // next slot in buffer is 0
argv[m] = 0; //clear user command
if (strcmp(argv[0],"exit") == 0) //if user command is exit
exit (0); //end program
int processes = 0,
gate1,
gate2,
pipe_end[2],
pipe_id;
if (fork() == 0)// passing command to array
{
for(int iteratr = 0; argv[iteratr] != 0; iteratr++)
{
if(strcmp("|", argv[iteratr]) == 0)// pipe configuration
{
processes++;
argv[iteratr] = 0; //empty array for next user command
pipe(pipe_end);
if (pipe(pipe_end)==-1) // Check for pipe functionality
{
perror("Pipe Failed");
return 1;
}
pipe_id = fork();
if (pipe_id < 0) //Check for fork functionality
{
printf("Fork failed");
return 1;
}
else if(pipe_id == 0)//Child
{
close(1); //close stdout
dup(pipe_end[1]); //copy stdout
close(pipe_end[0]); //close read in
close(pipe_end[1]); //close write out
execvp(argv[0], argv); //execute "pre" read in
}
else //Parent
{
close(0); //close stdin
dup(pipe_end[0]); //copy stdin
close(pipe_end[0]); //close read in
close(pipe_end[1]); //close write out
execvp(argv[0], argv); //execute "sort" read in
}
}
if(strcmp("<", argv[iteratr]) == 0)// Input redirect
{
gate1 = open("input.txt", O_RDONLY);
close(0);
dup(gate1);
close(gate1);
argv[iteratr] = 0;
execvp(argv[0], argv);
}
if(strcmp(">", argv[iteratr]) == 0) // Output redirect
{
gate2 = creat("output.txt", 0750);
close(1);
dup(gate2);
close(gate2);
argv[iteratr] = 0;
execvp(argv[0], argv);
}
}
execvp(argv[0], argv);
printf(" didn't exec \n ");
check = wait(&status);
}
wait(&status); //check
}
return 0;
}
Related
Unable to process the pipe function where a give pipes in which one process sends a string message to a second process, and the second process reverses the case of each character in the message and sends it back to the first process.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include <stdbool.h>
// Parent: reads from P1_READ, writes on P1_WRITE
// Child: reads from P2_READ, writes on P2_WRITE
#define P1_READ 0
#define P2_WRITE 1
#define P2_READ 2
#define P1_WRITE 3
// the total number of pipe *pairs* we need
#define NUM_PIPES 2
/*
toggleString accepts an a pointer to char array, allocates size for the
string to be toggled,
copys the argument into a string, loops through the string and for every
uppercase character
we set it to its lower case counterpart and vice versa, returning the
toggled string
*/
char *toggleString(char *argv){
int i; /* Declare counter */
char *str = malloc(sizeof(argv[1])); /* Declare array sizeof input */
strcpy(str, argv); /* Copy String to char array */
for(i=0;str[i]!='\0';i++) { //Loop through length of string
if(str[i]>='A'&&str[i]<='Z'){ //if the array at i is uppercase
str[i]+=32; //Make it lower case
} else if (str[i]>='a'&&str[i]<='z') {// if the array at i is lowercase
str[i]-=32; //Make it uppercase
}
}
return str;
}
/*
int inputValidation accept and integer (number of arugments) and a
pointer to the cmd line input array
We check to see if the command line input contains the minimal number of
arugments and check to see
whether or not the user input contains at least one reversible haracter,
if all goes well we return 0
*/
int inputValidation(int argc, char *argv[]){
int i; //Declare counter variable
bool c = false; //Declare boolean flag using imported <stdbool.h>
char str[strlen(argv[1])]; //Declare str
strcpy(str, argv[1]); //copy argument into str
if (argc != 2) { // check to see if we have enough arguments to
continue
// Prompt user of correct usage
fprintf(stderr, "\nUsage: %s <string> or <'string 1, string 2', ...,
string n'> for multiple strings\n", argv[0]);
exit(EXIT_FAILURE); //Exit on improper input
} else {
//loop through our string
for(i=0;i<strlen(str);i++) {
//if any any char is a reversible character
if(isalpha((int) str[i])){
c = true; //set the flag to true
}
}
if(c == false){ //If flag is false input does not contain any
reversible charachters
printf("\nSorry, The string you entered did NOT contain any
Alphabetical Characters\nRun me again, with at least 1 Alphabetical
character\n\n");
exit(EXIT_FAILURE); //Exit on improper input
}
return (0);
}
}
/*
Main takes input from command line, calls input validation to make sure of
proper input,
then creates the pipes we will need and the forks the child process, Parent
and Child
execute they're respective code
*/
int main(int argc, char *argv[]) {
assert(argc>1);
int fd[2*NUM_PIPES]; //Declare int[] of file descriptors
int len, i; //Declare length and integer for count
pid_t pid; //Declare process id
char parent[strlen(argv[1])]; //Declare Parent array
char child[strlen(argv[1])]; //Declare Child array
if(inputValidation(argc, argv) == 0) /* Check for proper input */
strcpy(parent, argv[1]);
// create all the descriptor pairs we need
for (i=0; i<NUM_PIPES; ++i)
{
if (pipe(fd+(i*2)) < 0)
{
perror("Failed to allocate pipes");
exit(EXIT_FAILURE);
}
}
// fork() returns 0 for child process, child-pid for parent process.
if ((pid = fork()) < 0)
{
perror("Failed to fork process");
return EXIT_FAILURE;
}
//////////////////////////////Childs Code
BEGINS//////////////////////////////////
// if the pid is zero, this is the child process
if (pid == 0)
{
// Child. Start by closing descriptors we
// don't need in this process
close(fd[P1_READ]);
close(fd[P1_WRITE]);
// used for output
pid = getpid();
// wait for parent to send us a value
len = read(fd[P2_READ], &child, len);
if (len < 0)
{
perror("Child: Failed to read data from pipe");
exit(EXIT_FAILURE);
}
else if (len == 0)
{
// not an error, but certainly unexpected
fprintf(stderr, "Child: Read EOF from pipe");
}
else
{
// report pid to console
printf("Child(%d): Recieved Message\n\nChild(%d): Toggling Case and
Sending to Parent\n",pid, pid);
// send the message to toggleString and write it to pipe//
if (write(fd[P2_WRITE], toggleString(child), strlen(child)) < 0)
{
perror("Child: Failed to write response value");
exit(EXIT_FAILURE);
}
}
// finished. close remaining descriptors.
close(fd[P2_READ]);
close(fd[P2_WRITE]);
return EXIT_SUCCESS;
}
//child code ends///
//////////////////////////////Parent Code
BEGINS//////////////////////////////////
// Parent. close unneeded descriptors
close(fd[P2_READ]);
close(fd[P2_WRITE]);
// used for output
pid = getpid();
// send a value to the child
printf("\nParent(%d): Sending %s to Child\n\n", pid, argv[1]);
if (write(fd[P1_WRITE], argv[1], strlen(argv[1])) != strlen(argv[1]))
{
perror("Parent: Failed to send value to child ");
exit(EXIT_FAILURE);
}
// now wait for a response
len = read(fd[P1_READ], &parent, strlen(parent));
if (len < 0)
{
perror("Parent: failed to read value from pipe");
exit(EXIT_FAILURE);
}
else if (len == 0)
{
// not an error, but certainly unexpected
fprintf(stderr, "Parent(%d): Read EOF from pipe", pid);
}
else
{
// report what we received
printf("\nParent(%d): Received %s from Child\n\n", pid, parent);
}
// close down remaining descriptors
close(fd[P1_READ]);
close(fd[P1_WRITE]);
// wait for child termination
wait(NULL);
return EXIT_SUCCESS;
}
//////////////////////////////Parent Code
ENDS//////////////////////////////////
This works:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include <stdbool.h>
#define P1_READ 0
#define P2_WRITE 1
#define P2_READ 2
#define P1_WRITE 3
#define NUM_PIPES 2
static
char *toggleString(char *argv)
{
int i;
char *str = malloc(strlen(argv) + 1); /* Key Fix */
strcpy(str, argv);
for (i = 0; str[i] != '\0'; i++)
{
if (str[i] >= 'A' && str[i] <= 'Z')
{
str[i] += 32;
}
else if (str[i] >= 'a' && str[i] <= 'z')
{
str[i] -= 32;
}
}
return str;
}
static
int inputValidation(int argc, char *argv[])
{
bool c = false;
char str[strlen(argv[1])];
strcpy(str, argv[1]);
if (argc != 2)
{
fprintf(stderr, "\nUsage: %s <string> or <'string 1, string 2', ..., string n'> for multiple strings\n", argv[0]);
exit(EXIT_FAILURE);
}
else
{
for (size_t i = 0; i < strlen(str); i++)
{
if (isalpha((int)str[i]))
{
c = true;
}
}
if (c == false)
{
printf("\nSorry, The string you entered did NOT contain any"
" Alphabetical Characters\nRun me again, with at least 1 Alphabetical"
" character\n\n");
exit(EXIT_FAILURE);
}
return(0);
}
}
int main(int argc, char *argv[])
{
assert(argc > 1);
int fd[2 * NUM_PIPES];
int len, i;
pid_t pid;
char parent[strlen(argv[1])];
char child[strlen(argv[1])];
if (inputValidation(argc, argv) == 0)
strcpy(parent, argv[1]);
for (i = 0; i < NUM_PIPES; ++i)
{
if (pipe(fd + (i * 2)) < 0)
{
perror("Failed to allocate pipes");
exit(EXIT_FAILURE);
}
}
if ((pid = fork()) < 0)
{
perror("Failed to fork process");
exit(EXIT_FAILURE);
}
if (pid == 0)
{
close(fd[P1_READ]);
close(fd[P1_WRITE]);
pid = getpid();
len = read(fd[P2_READ], child, sizeof(child));
if (len < 0)
{
perror("Child: Failed to read data from pipe");
exit(EXIT_FAILURE);
}
else if (len == 0)
{
fprintf(stderr, "Child: Read EOF from pipe\n");
}
else
{
child[len] = '\0';
printf("Child(%d): Received Message [%s]\nChild(%d): Toggling Case and Sending to Parent\n", pid, child, pid);
char *toggled = toggleString(child);
printf("Child(%d): Sending [%s]\n", pid, toggled);
if (write(fd[P2_WRITE], toggled, len) < 0)
{
perror("Child: Failed to write response value");
exit(EXIT_FAILURE);
}
free(toggled);
}
close(fd[P2_READ]);
close(fd[P2_WRITE]);
return EXIT_SUCCESS;
}
close(fd[P2_READ]);
close(fd[P2_WRITE]);
pid = getpid();
printf("\nParent(%d): Sending [%s] to Child\n\n", pid, argv[1]);
len = strlen(argv[1]);
if (write(fd[P1_WRITE], argv[1], len) != len)
{
perror("Parent: Failed to send value to child");
exit(EXIT_FAILURE);
}
len = read(fd[P1_READ], parent, sizeof(parent));
if (len < 0)
{
perror("Parent: failed to read value from pipe");
exit(EXIT_FAILURE);
}
else if (len == 0)
{
fprintf(stderr, "Parent(%d): Read EOF from pipe\n", pid);
}
else
{
parent[len] = '\0';
printf("\nParent(%d): Received [%s] from Child\n\n", pid, parent);
}
close(fd[P1_READ]);
close(fd[P1_WRITE]);
wait(NULL);
return EXIT_SUCCESS;
}
It was painful extracting your code from your comments, and the split over multiple line strings, and so on. The toggleString() function was broken — allocating 1 byte and then copying a string over that. The other code was not careful about null-terminating strings and handling them. These are basically the problems diagnosed in the comments.
Sample run:
$ pp53 'AbSoLuTeLy GlOrIoUs'
Parent(5209): Sending [AbSoLuTeLy GlOrIoUs] to Child
Child(5210): Received Message [AbSoLuTeLy GlOrIoUs]
Child(5210): Toggling Case and Sending to Parent
Child(5210): Sending [aBsOlUtElY gLoRiOuS]
Parent(5209): Received [aBsOlUtElY gLoRiOuS] from Child
$
Basically I have a parent process that forks a child and feeds it it's stdin through a pipe. The child process can terminate in one of two cases:
the write end of the pipe is closed by the parent, meaning it reached the end of stdin thus receiving an EOF,
or it receives a certain input through the pipe(-1 in this case) and exits
My parent code looks roughly like this:
close(pi[0]); // close input end
signal(SIGPIPE, SIG_IGN); // do not handle SIGPIPE
char buffer;
int ok = 1;
while(ok && read(STDIN_FILENO, &buffer, 1) > 0) {
int b_written = write(pi[1], &buffer, 1);
if(b_written == -1) {
if(errno == EPIPE) ok = 0;
else perror("pipe write"); // some other error
}
}
As you can see, I check whether the read end of a pipe is closed by checking for errno == EPIPE. However this means that the read loop does one extra iteration before closing. How could I possibly poll to see if the pipe is closed without necessarily writing something to it?
This snippet will check if the other end of a writable pipe is closed using poll(2). This works on Linux -- I'm not sure about other OSes or what POSIX says.
#include <poll.h>
#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>
bool is_pipe_closed(int fd) {
struct pollfd pfd = {
.fd = fd,
.events = POLLOUT,
};
if (poll(&pfd, 1, 1) < 0) {
return false;
}
return pfd.revents & POLLERR;
}
The child could send a signal, such as SIGUSR1 when it detects it has finished. Parent could set a flag to when it receives SIGUSR1 signal, and check this flag before trying to read input. But I am not absolutely sure SIGUSR1 could not be received after checking the flag ans before reading input from stdin). So I prefer to use a control pipe, each time child know it will be able to read one more data it write a 1 in this control pipe. The result could be something like that:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#define STOP_VALUE 100
#define SIZE_STDIN_BUFFER 1024
static char can_read_more = 1;
static int handle_child(int *p_child_input_stream, int *p_control_stream)
{
int pipefd[2][2];
pid_t fk;
if (pipe(pipefd[0]) < 0) // Pipe to read input from
{
perror("pipe");
return -1;
}
if (pipe(pipefd[1]) < 0) // Pipe to notifiate parent input can be processed
{
perror("pipe");
close(pipefd[0][0]);
close(pipefd[0][1]);
return -1;
}
if ((fk = fork()) < 0)
{
perror("fork");
close(pipefd[0][0]);
close(pipefd[0][1]);
close(pipefd[1][0]);
close(pipefd[1][1]);
return -1;
}
if (fk == 0)
{
close(pipefd[0][1]);
close(pipefd[1][0]);
write(pipefd[1][1], &can_read_more, sizeof(char)); // sizeof(char) == 1
ssize_t nb_read = 0;
char buffer;
while (nb_read >= 0)
{
nb_read = read(pipefd[0][0], &buffer, sizeof(char));
if (nb_read > 0)
{
printf("0x%02x\n", (unsigned int) buffer);
if (buffer == STOP_VALUE)
{
nb_read = -1;
}
else
{
write(pipefd[1][1], &can_read_more, sizeof(char));
}
}
}
close(pipefd[0][0]);
close(pipefd[1][1]);
exit(0);
}
close(pipefd[0][0]);
close(pipefd[1][1]);
*p_child_input_stream = pipefd[0][1];
*p_control_stream = pipefd[1][0];
return 0;
}
int main()
{
int child_input_stream;
int control_stream;
if (handle_child(&child_input_stream, &control_stream) < 0)
{
return 1;
}
char stdin_buffer[SIZE_STDIN_BUFFER];
char buffer;
int ok = 1;
int child_available_input = 0;
while(ok)
{
while (child_available_input <= 0 && ok)
{
ssize_t nb_control = read(control_stream, &buffer, sizeof(char));
if (nb_control > 0)
{
child_available_input += buffer;
}
else
{
fprintf(stderr, "End of child reading its input detected.\n");
ok = 0;
}
}
if (ok)
{
if (fgets(stdin_buffer, SIZE_STDIN_BUFFER, stdin) == NULL)
{
ok = 0;
}
else
{
if (stdin_buffer[strlen(stdin_buffer) - 1] == '\n')
{
stdin_buffer[strlen(stdin_buffer) - 1] = '\0';
}
char dummy;
int input;
if (sscanf(stdin_buffer, "%d%c", &input, &dummy) == 1)
{
buffer = (char) input;
write(child_input_stream, &buffer, sizeof(char));
child_available_input--;
}
}
}
}
return 0;
}
I'm working on an assignment in C aimed at using a pipe to pass variables between two processes. Both processes must be forked from the parent, and they must run concurrently to pass one character at a time (sort of demonstrated below).
The issue I'm having is that the fork()ed processes are not running concurrently. The sender seems to go first, and after running for ~26 seconds the receiver begins. Here is the code I have written:
#include <stdio.h>
int ret;
int pipearray[2];
char buffer[26];
void mysender();
void myreceiver();
int main()
{
int pid = 0;
int i = 0;
ret = pipe(pipearray);
while (i < 2) {
pid = fork();
if ( pid == 0 && i == 0 ) /* child process execution (receiver) */
{
myreceiver();
printf("Your receiver is done\n");
exit(0);
}
else if ( pid == 0 && i == 1 ) /* now executes sender */
{
mysender();
printf("Your sender is done\n");
exit(0);
}
++i;
}
close(pipearray[0]);
close(pipearray[1]);
sleep(30);
printf("Parent function has finished.\n");
return 0;
}
void mysender()
{
char c;
int index = 90;
close(pipearray[0]);
while (index > 64) /* loop for all values of A-Z in ASCII */
{
c = (char) index;
open(pipearray[1]);
write(pipearray[1], c, sizeof(c)); /* Sends letter to pipe */
--index;
sleep(1);
}
close(pipearray[1]);
}
void myreceiver()
{
int index = 0;
close(pipearray[1]);
while(buffer != 'A') /*loop runs until 'A' is handled */
{
sleep(1);
open(pipearray[0]);
read(pipearray[0], buffer, 1);
printf("%s", &buffer);
index++;
if ( index == 26 ) { break; }
}
close(pipearray[0]);
}
Expected Result:
ZYXWVUTSRQPONMLKJIHGFEDCBA
Your sender is done
Your receiver is done
The parent function has finished.
My result:
Your sender is done
The parent function has finished.
Your receiver is done
I'm very new to C programming but I've been banging away at this for a while. Any tips to why these might not be running simultaneously would be very appreciated.
There is many errors in your code. Don't try to open the pipe after fork, it is already open and has no name. Write should use the address of c. Read must read into the right place. A flush must be done after write to stdout. Your conditional must be slightly modified to be guaranteed correct. The parent process must wait its children. Here is the modified code :
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
int ret;
int pipearray[2];
char buffer[26];
void mysender();
void myreceiver();
int main()
{
int pid = 0;
int i = 0;
ret = pipe(pipearray);
while (i < 2) {
pid = fork();
if ( pid == 0 && i == 0 ) /* child process execution (receiver) */
{
myreceiver();
printf("Your receiver is done\n");
exit(0);
}
else if ( pid == 0 && i == 1 ) /* now executes sender */
{
mysender();
printf("Your sender is done\n");
exit(0);
}
++i;
}
close(pipearray[0]);
close(pipearray[1]);
// Don't sleep, but wait until the end of the two children
wait(NULL);
wait(NULL);
// sleep(30);
printf("Parent function has finished.\n");
return 0;
}
void mysender()
{
char c;
int index = 90;
close(pipearray[0]);
while (index > 64) /* loop for all values of A-Z in ASCII */
{
c = (char) index;
// try to open a anonymous pipe is a non-sense
// open(pipearray[1]);
// Send a buffer by its address
write(pipearray[1], &c, sizeof(c)); /* Sends letter to pipe */
--index;
sleep(1);
}
close(pipearray[1]);
}
void myreceiver()
{
int index = 0;
close(pipearray[1]);
// Ensure condition is entered first
buffer[index] = 0;
// This is not the best condition ever, but ok.
while(buffer[index] != 'A') /*loop runs until 'A' is handled */
{
sleep(1);
// Don't open an anonymous pipe
// open(pipearray[0]);
// Read at the right position
read(pipearray[0], buffer+index, 1);
// print and flush, could also be printf("%s"...); flush(stdout);
printf("%s\n", buffer);
index++;
if ( index == 26 ) { break; }
}
close(pipearray[0]);
}
Now, consider to remove the sleeps in the reader, as it will be synchronized with the writes such that no read is possible if no write has been done. Alos consider to read more that one byte, because there is no concept of message, so that you can read as much byte you consider necessary to read, and as usual it is better to try reading a bunch of bytes when you can.
I am writing my own simple shell for Linux in C. I am unable to implement piping without quitting the entire shell. I believe that I need to implement one more fork() to achieve this, but I am not sure where to do this.
My program has a main():
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(void){
char input[256];
char* temp = NULL;
char* args[9];
char* cmd1[5];
char* cmd2[5];
int i, j, pipe_pos;
int fd[2];
while(1){
// set all args to NULL
for (i = 0; i < 9; i++) {
args[i] = NULL;
}
for (i = 0; i < 5; i++) {
cmd1[i] = NULL;
cmd2[i] = NULL;
}
// print message to terminal
printf("My Shell (v2.0):> ");
// get input from user
fgets(input,255,stdin);
// test if input = 'quit'
if (!strncmp(input,"quit",4)) {
// exit program if input = quit
exit(0);
}
else { // continue running program
// initialise counter
i = 0;
// tokenize first word
temp = strtok(input, " \n");
// receive arguments from user input
while ((temp != NULL) && (i < 9)) {
args[i] = temp;
temp = strtok(NULL, " \n");
i++;
}
// find position of "|" (pipe)
j = i - 1;
pipe_pos = 8;
for (i = 0; i < j; i++) {
if (strcmp(args[i],"|") == 0) {
pipe_pos = i;
break;
}
}
// copy 1st command with arguments to new array
for (i = 0; i < pipe_pos; i++) {
cmd1[i] = args[i];
}
// copy 2nd command with arguments to new array
j = 0;
if (pipe_pos != 8) {
for (i = pipe_pos+1; i < 9; i++) {
// copy if array is not full
if (j < 5){
cmd2[j] = args[i];
j++;
} // end if (j < 5)
} // end for
} // end if (pipe_pos != 8) {
// run if program needs piping
if (pipe_pos != 8) {
pipeProcesses(cmd1, cmd2);
}
else {
runProcess(cmd1);
}
} // end else
}// end while
return 0;
}// end main()
A pipingProcesses() function:
void pipeProcesses(char ** cmd1, char ** cmd2){
int fd[2];
pipe(fd);
if (!fork()) {
// close STD_OUT
close(1);
// make STD_OUT same as fd[1]
dup(fd[1]);
// we don't need this
close(fd[0]);
runProcess(cmd1);
exit(0);
} else {
// close STD_IN
close(0);
// make STD_IN same as fd[0]
dup(fd[0]);
// we don't need this
close(fd[1]);
execvp(cmd2[0], cmd2);
}
}
And runProcess() function:
void runProcess(char ** cmd){
int error;
// initialise error flag
error = 0;
if (fork() == 0) {
// child process
error = execvp(cmd[0], cmd);
// print error: unknown command
if (error == -1) {
printf("ERROR: UNKNOWN COMMAND (%s)\n", cmd[0]);
exit(0);
}
}
else {
// parent process
waitpid(0,NULL,0);
}
}
Everything works as should, except that I want the shell to continue running after I have piped two commands. How can I fix this?
You call execvp(cmd2[0], cmd2); in the flow of the main program which replaces your program with cmd2.
You should fork() for that command as well.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
void tokenizer(char* input, char** output) { //My tokenizer
char* input_dup = strdup(input);
output[0] = strtok(input_dup, " ");
int i = 1;
while ((output[i] = strtok(NULL, " ")) != NULL) {
i++;
}
}
void run_command(char** args, int* fd) { //no pipe
pid_t pid = fork();
if (pid < 0) {
printf("Forking failed...\n");
}
else if (pid == 0) {
close(fd[0]);
if (fd[1] != 1)
dup2(fd[1], 1);
execvp(args[0], args);
printf("Command failed...\n");
exit(1);
}
else {
close(fd[1]);
wait(pid);
char buff[1];
while (read(fd[0], buff, 1) > 0) {
if (buff[0] == EOF || buff[0] == '\0') {
printf("Caught something, returning out...");
return;
}
else {
printf("%c", buff[0]);
}
}
}
}
//pipeline function
void run_pipe(char** args, int* fd) {
pid_t pid = fork();
if (pid < 0) {
printf("Forking failed...\n");
}
else if (pid == 0) {
if (fd[1] != 1) {
dup2(fd[1], 1);
}
execvp(args[0], args);
printf("Command failed...\n");
exit(1);
}
else {
close(fd[1]);
if (fd[0] != 0) {
dup2(fd[0], 0);
}
wait(pid);
}
}
int main(int argc, char** argv) {
printf ("Starting myshell (mysh) \n..\n..\n");
while (1) {
char cwd[1024];
printf ("mysh :: %s -> ", getcwd(cwd, sizeof(cwd)));
char ch[1024];
memset(ch, 0, 1023); //for cleanup
char c = 0;
int i = 0;
while (c != '\n') {
c = getchar();
if (c == EOF) {
printf ("EOF Received, exiting...\n");
return 0;
}
if (c != '\n')
ch[i] = c;
i++;
}
if (ch[0] != '\0') {
char* tokens[128];
tokenizer(ch, tokens);
//first check for keywords
if (strcmp(tokens[0], "cd") == 0) {
if (chdir(tokens[1]) < 0) {
printf("ERROR: Directory %s does not exist\n", tokens[1]);
}
}
else if (strcmp(tokens[0], "exit") == 0) {
printf("Leaving shell...\n");
return 0;
}
else {
char* commands[50];
memset(commands, 0, sizeof(commands));
int j = 0;
int k = 0;
int fd[2];
//try something different...
while (tokens[j] != NULL) {
if (strcmp(tokens[j], "|") == 0) {
commands[k] = NULL;
pipe(fd);
run_pipe(commands, fd);
j++;
k = 0;
}
//more cases here
else { //nothing special
commands[k] = tokens[j];
j++;
k++;
}
}
commands[k] = NULL;
pipe(fd);
run_command(commands, fd);
}
}
}
}
The above code is meant to simulate a shell. It handles single commands and it handles the pipelining properly (i.e. ps | sort | wc is returning the correct output) however when the pipelining is done it returns an EOF which is caught by the condition in the loop with getchar(). If I try to ignore this EOF it segfaults. Am I leaving a pipe open somewhere and stdin is getting flooded? Any help is appreciated.
Compilation fixes
You need to add #include <sys/wait.h> and then fix the calls to wait(). I used (twice):
int status;
int corpse = wait(&status);
printf("PID %d status 0x%.4X\n", corpse, status);
Arguably, that should be a loop looking for a specific PID, or you should use waitpid() instead. While debugging a shell, you want to know about every PID that exits and its status.
I ran `ps | wc' and got:
Starting myshell (mysh)
..
..
mysh :: /usr/local/google/home/jleffler/soq -> ps | wc
PID 25960 status 0x0000
PID 25961 status 0x0000
4 16 117
mysh :: /usr/local/google/home/jleffler/soq -> EOF Received, exiting...
If you mean "the code should have continued instead of getting EOF", then there's some more work to do.
Tangential issues
I note the line:
if (buff[0] == EOF || buff[0] == '\0')
The character in buff[0] is from a read() call. It will never be EOF meaningfully; EOF is distinct from every character (hence getchar() returns an int). This becomes significant later:
char c = 0;
while (c != '\n')
{
c = getchar();
if (c == EOF)
Since c is a char, you cannot reliably compare it with EOF. You must store the result of getchar() in an int.
I'm not yet convinced these are the cause of the trouble, but you must be careful.
Probable cause
I think the trouble is in run_pipe() in the parent code (as amended):
else
{
close(fd[1]);
if (fd[0] != 0)
{
dup2(fd[0], 0);
}
int status;
int corpse = wait(&status);
printf("PID %d status 0x%.4X\n", corpse, status);
}
The fd[0] != 0 condition will always be true (very improbable that it will be false), so you then change your shell's input to read from fd[0]. You should review that; it means that you're reading standard input from the read end of the pipe to the child. That's bad; you've lost your original input!
Your code also seems to have the parent waiting for the child to die, and then reads the pipe and echoes to standard output. This is not a good idea; it is better to make the child (last child in the pipeline) write to the standard output directly. There are two reasons for this:
The child might write more data than fits in a pipe, so it will block waiting for something to read its output, but the reader will be blocked waiting for the child to die, so you'll have a deadlock.
It slows things up, and output from the child may well be buffered instead of appearing timely on the terminal.
I'm a little sceptical about how a three-part pipeline would be handled. You need two pipes created before you run the middle process of the three; I don't see that in your code.
I was able to fix this issue. It's probably not the right way to do it, but I saved a copy of stdin and used dup2 to reset it when the pipelining was finished.
int in_bak = dup(0);
//stuff
dup2(in_bak, 0);
close(in_bak);