how to use pipe and execvp in c via linux - c

I'm trying to use pipe command and I can't understand how to.
I've a lot of versions but I can't make it work.
first of all the hierarchy:
main prog - nicecmp - that will execute the child prog and print the result
child prog - loopcmp - that will execute his child prog and get the returned value and send it back to the parent in nicecmp.
loopcmp's childs - lencmp/lexcmp - both prog will be executed in loopcmp and return value between -1 to 2. (100% works)
shortly, I need to create a pipe and a new process that will run new program (loopcmp - added in the end of the code) using execvp, and I need to print the res of the loopcmp in the parent.
I can send it directly from the prog that I executed and I can use WEXITSTATUS in the child after the end of the loopcmp.
what's the right way to do so (from the progrem execution or after that I've returned from the loopcmp)
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define LINELEN (80)
#define READFROM ("./loopcmp")
typedef enum { eLexcmp, eLencmp, eNumOfCmp } eCmpstr;
const char* cmpstr[eNumOfCmp] = { "./lexcmp", "./lencmp" };
int lencmp(const char *str1, const char *str2);
int lexcmp(const char *str1, const char *str2);
char *mygets(char *buf, int len);
int mygeti();
int main(int argc, char *argv[])
{
char str1[LINELEN + 1];
char str2[LINELEN + 1];
int index, rc, status, res;
int pfd[2];/* Pipe file descriptors */
if (pipe(pfd) == -1) /* Create pipe */
exit(-2); // pipe failed !
char* myargs[4];
myargs[0]=strdup(READFROM);
while (1)
{
printf("Please enter first string:\n");
if (mygets(str1, LINELEN) == NULL)
break;
printf("Please enter second string:\n");
if (mygets(str2, LINELEN) == NULL)
break;
myargs[2] = strdup(str1);
myargs[3] = strdup(str2);
do {
printf("Please choose:\n");
for (int i = 0; i < eNumOfCmp; i++)
printf("%d - %s\n", i, cmpstr[i]);
index = mygeti();
} while ((index < 0) || (index >= eNumOfCmp));
myargs[1] = strdup(cmpstr[index]);
rc = fork();
if (rc < 0) // fork failed !
{
printf("fork failed\n");
return -2;
}
else if (rc == 0) { // child !
if (close(pfd[1]) == -1) /* Write end is unused */
exit(-2);
/* Duplicate stdin on read end of pipe; close duplicated descriptor */
if (pfd[0] != STDIN_FILENO) { /* Defensive check */
if (dup2(pfd[0], STDIN_FILENO) == -1)
exit(-2);
if (close(pfd[0]) == -1)
exit(-2);
}
execvp(myargs[0],myargs);
}
else { // parent
if (close(pfd[1]) == -1) /* Write end is unused */
exit(-2);
/* Duplicate stdin on read end of pipe; close duplicated descriptor */
if (pfd[0] != STDIN_FILENO) { /* Defensive check */
if (dup2(pfd[0], STDIN_FILENO) == -1)
exit(-2);
if (close(pfd[0]) == -1)
exit(-2);
}
read(pfd[0], &res, sizeof(int));
printf("%d\n", res);
if (close(pfd[0]) == -1)
exit(-2);
}
}
return 0;
}
loopcmp ->
int main(int argc, char *argv[])
{
int status,rc,res = 0;
if (argc != 4)
{
return -1;
}
char* myargs[3];
for(int i=0;i<3;i++){
myargs[i]=argv[i+1];
}
rc = fork();
if (rc < 0) //fork failed
{
return -2;
}
else if (rc == 0) //I'm the child
{
if(execvp(myargs[1], myargs)==-1)
return -2;
}
else // parent
{
wait(&status);
res = WEXITSTATUS(status);
if(res ==254) // invalid file path ! (254== -2)
return -2 ;
}
write(fileno(stdout),&res,sizeof(int));
return res;
}

Related

How to make a history command

I am having trouble creating a child process, and I'm not sure if I have the execvp argument right. Is there a way to fix it so it'll pass correctly?
int execute(char* input) {
int i = 0;
char* shell_argv[MAX_CMD_LINE_ARGS];
memset(shell_argv, 0, MAX_CMD_LINE_ARGS * sizeof(char));
//passing pointer of input and element list 128
int shell_argc = parse(input, shell_argv);
int status = 0;
pid_t pid = fork();
if (pid < 0) {
fprintf(stderr, "Fork() failed\n"); } // send to stderr
else if (pid == 0) { // child
// fill in code for execvp(...) <- this is what I'm having trouble with
if (execvp(shell_argv[0], shell_argv) == -1 && strcmp(input, "history") != 0) {
printf("Invalid command\n");
}
} else { // parent ----- don't wait if you are creating a daemon (background) process
while (wait(&status) != pid) { }
}
return 0;
}
There are some errors in your code:
shell_argv is an array of char*, memset length shoud be MAX_CMD_LINE_ARGS * sizeof(char*); or use a simple way char *shell_argv[MAX_CMD_LINE_ARGS] = {0};
I can't find a standard function parse(), maybe you implemented it by your self. I have made a workround to run the code.
memory for the element in shell_argv should be free at the end of founction.
The input argument of execvp is correct. Here is all the code for your reference.
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <stdlib.h>
#define MAX_CMD_LINE_ARGS 255
int parse(char *input, char** shell_argv){
shell_argv[0] = strdup("date");
shell_argv[1] = strdup("+%s");
}
int execute(char *input)
{
int i = 0;
char *shell_argv[MAX_CMD_LINE_ARGS];
memset(shell_argv, 0, MAX_CMD_LINE_ARGS * sizeof(char*));
// passing pointer of input and element list 128
int shell_argc = parse(input, shell_argv);
int status = 0;
pid_t pid = fork();
if (pid < 0)
{
fprintf(stderr, "Fork() failed\n");
} // send to stderr
else if (pid == 0)
{ // child
// fill in code for execvp(...) <- this is what I'm having trouble with
if (execvp(shell_argv[0], shell_argv) == -1 && strcmp(input, "history") != 0)
{
printf("Invalid command\n");
}
}
else
{ // parent ----- don't wait if you are creating a daemon (background) process
while (wait(&status) != pid)
{
}
}
for(int i=0;i<MAX_CMD_LINE_ARGS;i++){
if(shell_argv[i] != NULL){
free(shell_argv[i]);
}
}
return 0;
}
int main(){
execute("date +%s");
return 0;
}
Test result:
gxie#ubuntu20:~/test $ gcc main.c
gxie#ubuntu20:~/test $ ./a.out
1663837441

Scanf through pipe lock

I have an exercise where I need to interact with a C program through pipe.
I have the following source, which I can't modify.
#include <stdio.h>
#include <stdlib.h>
int main()
{
int number;
int answer;
number = rand() % 100;
printf("Print the double of the number %d\n", number);
scanf("%d", &answer);
if(number * 2 == answer)
printf("Success\n");
else
printf("Error\n");
}
I tried to interact with this program with this code
#include <unistd.h>
#include <stdio.h>
int main(int argc, char **argv, char **env)
{
int STDIN_PIPE[2];
int STDOUT_PIPE[2];
pipe(STDIN_PIPE);
pipe(STDOUT_PIPE);
pid_t pid = fork();
if(pid == 0)
{
char *path = "/path/to/binary";
char *args[2];
args[0] = path;
args[1] = NULL;
close(STDIN_PIPE[1]);
close(STDOUT_PIPE[0]);
dup2(STDIN_PIPE[0], STDIN_FILENO);
dup2(STDOUT_PIPE[1], STDOUT_FILENO);
execve(path, args, env);
}
else
{
char buf[128];
close(STDIN_PIPE[0]);
close(STDOUT_PIPE[1]);
while(read(STDOUT_PIPE[0], buf, 1))
write(1, buf, 1);
}
}
But when I run it, it falls in an infinite loop without printing nothing.
I have fixed a number of issues in your code, added a lot of error checks and completed it so that the end goal is reached.
In the child process, srand() must be called to initialize the random number generator or you always get the same value.
The in the child process, you must flush(stdout) after printing the question so that it is really written to the pipe.
And finally, scanf() return value must be checked.
In the main process, I added a lot of error checks. And I write a readLine function to - guess what - read a line from the pipe. A line is terminated by the end-of-line character \n.
There is still room for some enhancements...
I tested my code using Visual Studio Code configured for gcc and running under Ubuntu 20.04.
Here is the child process source:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
int number;
int answer;
time_t t;
srand((unsigned)time(&t));
number = rand() % 100;
printf("Print the double of the number %d\n", number);
fflush(stdout);
int n = scanf("%d", &answer);
if (n != 1) {
printf("Invalid input\n");
return 1;
}
if ((number * 2) == answer) {
printf("Success\n");
return 0;
}
printf("Error %d is not 2 * %d\n", answer, number);
return 1;
}
And here is the main process source:
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int readLine(int fd, char *buf, int bufSize);
int main(int argc, char **argv, char **env)
{
int STDIN_PIPE[2];
int STDOUT_PIPE[2];
if (pipe(STDIN_PIPE))
{
perror("pipe(STDIN_PIPE)");
return 1;
}
if (pipe(STDOUT_PIPE)) {
perror("pipe(STDOUT_PIPE)");
return 1;
}
pid_t pid = fork();
if (pid == 0)
{
char *path = "../Child/Child"; // Path to child process, adapt to your environment
char *args[2];
args[0] = path;
args[1] = NULL;
if (dup2(STDIN_PIPE[0], STDIN_FILENO) == -1) {
perror("dup2(STDIN) failed");
return 1;
}
if (dup2(STDOUT_PIPE[1], STDOUT_FILENO) == -1) {
perror("dup2(STDIN) failed");
return 1;
}
// Close all pipe ends
close(STDIN_PIPE[0]); // Close read end of STDIN_PIPE
close(STDIN_PIPE[1]); // Write end of STDIN_PIPE
close(STDOUT_PIPE[0]); // Read end of STDOUT_PIPE
close(STDOUT_PIPE[1]); // Close write end of STDOUT_PIPE
if (execve(path, args, env) == -1) {
perror("execve failed");
return 1;
}
}
else
{
char buf[128];
int bufSize = sizeof(buf) / sizeof(buf[0]);
int i;
// Read the question asked by child process
if (readLine(STDOUT_PIPE[0], buf, bufSize) < 0) {
printf("readLine failed.\n");
return 1;
}
// We receive something like "Print the double of the number 83"
printf("Child process question is \"%s\".\n", buf);
// Extract the number at end of string
i = strlen(buf) - 1;
while ((i >= 0) && isdigit(buf[i]))
i--;
int value = atoi(buf + i + 1);
// Write our answer to write end of STDIN_PIPE
char answer[128];
int answerSize = sizeof(answer) / sizeof(answer[0]);
int answerLen = snprintf(answer, answerSize, "%d\n", value * 2);
printf("Our answer is \"%d\".\n", value * 2);
if (write(STDIN_PIPE[1], answer, answerLen) != answerLen) {
printf("write failed.\n");
return 1;
}
// Read the response (success or failure) sent by child process
if (readLine(STDOUT_PIPE[0], buf, bufSize) < 0) {
printf("readLine failed.\n");
return 1;
}
if (strcasecmp(buf, "Success") == 0)
printf("Child process returned success.\n");
else
printf("Child process returned failure.\n");
// Close all pipe ends
close(STDIN_PIPE[0]); // Close read end of STDIN_PIPE
close(STDIN_PIPE[1]); // Write end of STDIN_PIPE
close(STDOUT_PIPE[0]); // Read end of STDOUT_PIPE
close(STDOUT_PIPE[1]); // Close write end of STDOUT_PIPE
}
return 0;
}
// Read a line from file descriptor
// A line is any characters until \n is received or EOF
// \n is not kept
// Return the number of characters read or <0 if error:
// -1 => Input buffer overflow
// -2 => read() failed and errno has the error
int readLine(int fd, char *buf, int bufSize)
{
int i = 0;
while (1)
{
// Check if enough room in the buffer
if (i >= bufSize) {
printf("Input buffer overflow\n");
return -1;
}
// Read one character from the pipe
ssize_t n = read(fd, buf + i, 1);
if (n == -1)
{
perror("read() failed");
return -2;
}
if (n == 0)
{
// EOF received, that's OK
return i;
}
// NUL terminate the buffer
buf[i + 1] = 0;
// Check for end of line character
if (buf[i] == '\n') {
buf[i] = 0; // Remove ending \n
return i;
}
i++;
}
}

Unable to process the pipe function

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
$

linux - exec'd program not terminating

I am writing program in C on Linux which has to fork 2 children.
First child will send two random numbers over pipe to the second child. It will listen for SIGUSR1 signal and will then terminate.
The second child will duplicate(dup2) pipe input as STDIN and file fp as STDOUT. It will then execl program which will print out some data according to its input and end.
My problem is, that the execl'd program will never terminate and I don't know why. Any help or tips will be appreciated.
main.c (parent):
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>
const int BUFFER_SIZE = 30;
int pipefd[2] = {0,0};
int parent_pid = 0;
int first_pid = 0;
int second_pid = 0;
int sleep_time = 5;
int debug = 0;
FILE *fp;
void parent_func() {
int wstatus = 0;
sleep(sleep_time);
kill(first_pid, SIGUSR1);
wait(&wstatus);
waitpid(second_pid, &wstatus, 0);
}
static void sigusr1_handler(int sig) {
if (sig == SIGUSR1) {
fputs("TERMINATED", stderr);
close(pipefd[1]);
exit(0);
}
}
void first_func() {
struct sigaction act;
char buffer[BUFFER_SIZE];
close(pipefd[0]);
memset(&act, '\0', sizeof(act)); // clear the sigaction struct
act.sa_handler = &sigusr1_handler; // sets function to run on signal
if (sigaction(SIGUSR1, &act, NULL) < 0) { // assign sigaction
fputs("cannot assign sigaction - exiting...", stderr);
exit(1);
}
while (1) {
sprintf(buffer, "%d %d\n", rand(), rand());
write(pipefd[1], buffer, strlen(buffer));
puts(buffer);
sleep(1);
}
}
void second_func() {
close(pipefd[1]);
fp = fopen("out.txt", "w");
char buf[30];
dup2(pipefd[0], STDIN_FILENO);
close(pipefd[0]);
//dup2(fileno(fp), STDOUT_FILENO);
execl("./test", "", NULL);
perror("Error");
}
int main(int argc, char *argv[]) {
int fork_val = 0;
parent_pid = getpid();
if (pipe(pipefd)) {
fputs("cannot create pipe - exiting...", stderr);
return 1;
}
if (debug) {
sleep_time *= 10;
}
if ((fork_val = fork()) == -1) {
fputs("cannot fork process - exiting...", stderr);
return 1;
} else if (fork_val == 0) {
first_func();
} else {
first_pid = fork_val;
if ((fork_val = fork()) == -1) {
fputs("cannot fork process - exiting...", stderr);
return 1;
} else if (fork_val == 0) {
second_func();
} else {
second_pid = fork_val;
parent_func();
}
}
fclose(fp);
exit(0);
}
test.c (the execl'd file):
#include "nd.h"
#include "nsd.h"
#include <stdio.h>
#include <stdlib.h>
int main() {
int num1 = 0;
int num2 = 0;
char buffer[100];
while (fgets(buffer, 100, stdin) != NULL) {
if (sscanf(buffer, "%d %d", &num1, &num2) == 2) {
(num1 < 0) ? num1 = (num1 * -1) : num1;
(num2 < 0) ? num2 = (num2 * -1) : num2;
if (num1 == 1 || num2 == 1) {
puts("1");
} else if (num1 == num2) {
if (nd(num1) == 1) {
puts("prime");
} else {
printf("%d\n", num1);
}
} else if (nd(num1) == 1 && nd(num2) == 1) {
puts("prime");
} else {
printf("%d\n", nsd(num1, num2));
}
} else {
fputs("error\n", stderr);
}
}
fputs("DONE", stderr);
exit(0);
}
To be able to detect an end of file from a pipe you need to read from a empty pipe with no writer (no process with an open for writing descriptor).
As your writer (first_func()) never closes its descriptor and always writes something in a never ending loop the reader will either wait for some data or read some data.
Be also careful about closing non useful descriptors, if not you may encounter some problems with pipes, such has a single process that is a reader and a writer, so being unable to detect the end of file...

Shell program with pipes in C

I have a problem with pipes. My program is a Shell program in C. I want to execute for example ls | wc, but what I get after running is:
ls: cannot access |: no such file or directory ls: cannot access wc: no such file or directory.
What am I doing wrong?
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#define MAX_CMD_LENGTH 100
#define MAX_NUM_PARAMS 10
int parsecmd(char* cmd, char** params) { //split cmd into array of params
int i,n=-1;
for(i=0; i<MAX_NUM_PARAMS; i++) {
params[i] = strsep(&cmd, " ");
n++;
if(params[i] == NULL) break;
}
return(n);
};
int executecmd(char** params) {
pid_t pid = fork(); //fork process
if (pid == -1) { //error
char *error = strerror(errno);
printf("error fork!!\n");
return 1;
} else if (pid == 0) { // child process
execvp(params[0], params); //exec cmd
char *error = strerror(errno);
printf("unknown command\n");
return 0;
} else { // parent process
int childstatus;
waitpid(pid, &childstatus, 0);
return 1;
}
};
int execpipe (char ** argv1, char ** argv2) {
int fds[2];
pipe(fds);
int i;
pid_t pid = fork();
for (i=0; i<2; i++) {
if (pid == -1) { //error
char *error = strerror(errno);
printf("error fork!!\n");
return 1;
} else
if (pid == 0) {
if(i ==0){
close(fds[1]);
dup2(fds[0], 0);
close(fds[0]);
execvp(argv1[0], argv1);
char *error = strerror(errno);
printf("unknown command\n");
return 0;
} else if(i == 1) {
close(fds[0]);
dup2(fds[1], 1);
close(fds[1]);
execvp(argv2[0], argv2);
char *error = strerror(errno);
printf("unknown command\n");
return 0;
}
} else { // parent process
int childstatus;
waitpid(pid, &childstatus, 0);
return 1;
}
} // end for
};
int main() {
char cmd[MAX_CMD_LENGTH+1];
char * params[MAX_NUM_PARAMS+1];
char * argv1[MAX_NUM_PARAMS+1];
char * argv2[MAX_NUM_PARAMS+1];
int k, y, x;
int f = 1;
while(1) {
printf("$"); //prompt
if(fgets(cmd, sizeof(cmd), stdin) == NULL) break; //read command, ctrl+D exit
if(cmd[strlen(cmd)-1] == '\n') { //remove newline char
cmd[strlen(cmd)-1] = '\0';
}
int j=parsecmd(cmd, params); //split cmd into array of params
if (strcmp(params[0], "exit") == 0) break; //exit
for (k=0; k <j; k++) { //elegxos gia uparksi pipes
if (strcmp(params[k], "|") == 0) {
f = 0; y = k;
printf("pipe found\n");
}
}
if (f==0) {
for (x=0; x<k; x++) {
argv1[x]=params[x];
}
int z = 0;
for (x=k+1; x< j; x++) {
argv2[z]=params[x];
z++;
}
if (execpipe(argv1, argv2) == 0) break;
} else if (f==1) {
if (executecmd(params) == 0) break;
}
} // end while
return 0;
}
Updated your code with following corrections.
Removed for() loop that iterated two times after fork() call.
Removed incorrect close of pipe FDs after dup2 calls for both parent and child processes.
Aligned the command that needed to be run as per the file descriptors that were duplicated in dup2() calls for parent and child. Basically I needed to swap execvp(argv2[0], argv2) and execvp(argv1[0], argv1) calls.
Added a break; statement in the for loop that searched for pipe character.
The updated code is as below.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#define MAX_CMD_LENGTH 100
#define MAX_NUM_PARAMS 10
int parsecmd(char* cmd, char** params) { //split cmd into array of params
int i,n=-1;
for(i=0; i<MAX_NUM_PARAMS; i++) {
params[i] = strsep(&cmd, " ");
n++;
if(params[i] == NULL) break;
}
return(n);
};
int executecmd(char** params) {
pid_t pid = fork(); //fork process
if (pid == -1) { //error
char *error = strerror(errno);
printf("error fork!!\n");
return 1;
} else if (pid == 0) { // child process
execvp(params[0], params); //exec cmd
char *error = strerror(errno);
printf("unknown command\n");
return 0;
} else { // parent process
int childstatus;
waitpid(pid, &childstatus, 0);
return 1;
}
};
int execpipe (char ** argv1, char ** argv2) {
int fds[2];
pipe(fds);
int i;
pid_t pid = fork();
if (pid == -1) { //error
char *error = strerror(errno);
printf("error fork!!\n");
return 1;
}
if (pid == 0) { // child process
close(fds[1]);
dup2(fds[0], 0);
//close(fds[0]);
execvp(argv2[0], argv2); // run command AFTER pipe character in userinput
char *error = strerror(errno);
printf("unknown command\n");
return 0;
} else { // parent process
close(fds[0]);
dup2(fds[1], 1);
//close(fds[1]);
execvp(argv1[0], argv1); // run command BEFORE pipe character in userinput
char *error = strerror(errno);
printf("unknown command\n");
return 0;
}
};
int main() {
char cmd[MAX_CMD_LENGTH+1];
char * params[MAX_NUM_PARAMS+1];
char * argv1[MAX_NUM_PARAMS+1] = {0};
char * argv2[MAX_NUM_PARAMS+1] = {0};
int k, y, x;
int f = 1;
while(1) {
printf("$"); //prompt
if(fgets(cmd, sizeof(cmd), stdin) == NULL) break; //read command, ctrl+D exit
if(cmd[strlen(cmd)-1] == '\n') { //remove newline char
cmd[strlen(cmd)-1] = '\0';
}
int j=parsecmd(cmd, params); //split cmd into array of params
if (strcmp(params[0], "exit") == 0) break; //exit
for (k=0; k <j; k++) { //elegxos gia uparksi pipes
if (strcmp(params[k], "|") == 0) {
f = 0; y = k;
printf("pipe found\n");
break;
}
}
if (f==0) {
for (x=0; x<k; x++) {
argv1[x]=params[x];
}
int z = 0;
for (x=k+1; x< j; x++) {
argv2[z]=params[x];
z++;
}
if (execpipe(argv1, argv2) == 0) break;
} else if (f==1) {
if (executecmd(params) == 0) break;
}
} // end while
return 0;
}
If you are interested only in changes I made, here is the diff between your code and the above updated code:
--- original.c
+++ updated.c
## -4,6 +4,7 ##
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
+#include <sys/wait.h>
#define MAX_CMD_LENGTH 100
## -43,44 +44,36 ##
pipe(fds);
int i;
pid_t pid = fork();
- for (i=0; i<2; i++) {
if (pid == -1) { //error
char *error = strerror(errno);
printf("error fork!!\n");
return 1;
- } else
- if (pid == 0) {
- if(i ==0){
+ }
+ if (pid == 0) { // child process
close(fds[1]);
dup2(fds[0], 0);
- close(fds[0]);
- execvp(argv1[0], argv1);
+ //close(fds[0]);
+ execvp(argv2[0], argv2); // run command AFTER pipe character in userinput
char *error = strerror(errno);
printf("unknown command\n");
return 0;
- } else if(i == 1) {
+ } else { // parent process
close(fds[0]);
dup2(fds[1], 1);
- close(fds[1]);
- execvp(argv2[0], argv2);
+ //close(fds[1]);
+ execvp(argv1[0], argv1); // run command BEFORE pipe character in userinput
char *error = strerror(errno);
printf("unknown command\n");
return 0;
}
- } else { // parent process
- int childstatus;
- waitpid(pid, &childstatus, 0);
- return 1;
- }
- } // end for
};
int main() {
char cmd[MAX_CMD_LENGTH+1];
char * params[MAX_NUM_PARAMS+1];
- char * argv1[MAX_NUM_PARAMS+1];
- char * argv2[MAX_NUM_PARAMS+1];
+ char * argv1[MAX_NUM_PARAMS+1] = {0};
+ char * argv2[MAX_NUM_PARAMS+1] = {0};
int k, y, x;
int f = 1;
while(1) {
## -95,6 +88,7 ##
if (strcmp(params[k], "|") == 0) {
f = 0; y = k;
printf("pipe found\n");
+ break;
}
}
if (f==0) {
execv* procedure doesn't interpret shell script string. It merely starts an executable file and passes an array of arguments to it. Thus, it cannot organize a pipeline.
If you need "normal" shell command execution, you may want to use system(char*) procedure instead of execvp.
Otherwise, if you need to do the pipes yourself, you may want to parse the string with '|' special characters and use pipe(), fork() and I/O redirection. Like here How to run a command using pipe?

Resources