How to properly close a pipe - c

I have a piece of code I am trying to alter. The code is simply 2 pipes that communicate between two processes (parent and child process). I am trying to create a "clean termination" of the program so that whenever a 0 is sent from parent to the child the respective process shuts down their end of all pipes.
The code is something like:
int main
{
int pipe1[2], pipe2[2], value, request;
if(pipe(pipe1) < 0 || pipe(pipe2) < 0)
printf("Failed to create pipes.\n");
switch(fork())
{
case -1:
perror("Cannot create fork.\n");
break;
case 0:
close (pipe1[WRITE]);
close (pipe2[READ]);
close (0);
close (1);
for(;;) {
read(pipe1[READ], &value, sizeof value);
//If user enters 0.
if(value == 0)
{
printf("Terminating child process!\n");
close(pipe1[READ]);
//write a 0 back to the parent so that it can close its end.
write(pipe2[WRITE], 0, sizeof (int));
close(pipe2[WRITE]);
exit(0);
}
else
{
/*Enter function to do something on the number here*/
write(pipe2[WRITE], &result, sizeof result);
}
}
break;
default: /* PARENT PROCESS - PRODUCER */
close(pipe1[READ]);
close(pipe2[WRITE]);
for(;;) {
printf("Ange ett tal: ");
scanf("%d", &value);
if(write(pipe1[WRITE], &value, sizeof value) != sizeof value)
{
perror("Cannot write thru pipe.\n");
return 1;
}
//Clean shutdown if value == 0
if(value == 0)
{
close(pipe2[WRITE]);
read(pipe2[READ],&result,sizeof(result));
if(result == 0)
{
close(pipe2[READ]);
printf("Pipe2[Read] closed!\n");
wait(0);
}
}
else
{
read(pipe2[READ],&result,sizeof(result));
printf("Result: %d.\n",result);
}
}
break;
}
}
I have been at this for hours now and I do not understand why my code is acting all crazy. When I enter 1 it says "Pipe closed and if i enter 0 it tries to accept another input before giving an error Cannot create pipes!.
I think I am missing something very fundamental here. Any help on the subject is very much appreciated.

Note the key change!
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
enum { READ = 0, WRITE = 1 };
int main(void)
{
int pipe1[2], pipe2[2], value, result;
if (pipe(pipe1) < 0 || pipe(pipe2) < 0)
printf("Failed to create pipes.\n");
switch (fork())
{
case -1:
perror("Cannot create fork.\n");
break;
case 0:
close(pipe1[WRITE]);
close(pipe2[READ]);
while (read(pipe1[READ], &value, sizeof(value)) > 0)
{
// If user enters 0.
if (value == 0)
{
printf("Terminating child process!\n");
close(pipe1[READ]);
// write a 0 back to the parent so that it can close its end.
write(pipe2[WRITE], 0, sizeof(int));
close(pipe2[WRITE]);
exit(0);
}
else
{
result = value + 1;
write(pipe2[WRITE], &result, sizeof result);
}
}
break;
default: /* PARENT PROCESS - PRODUCER */
close(pipe1[READ]);
close(pipe2[WRITE]);
for ( ; ; )
{
printf("Ange ett tal: ");
if (scanf("%d", &value) != 1)
break;
if (write(pipe1[WRITE], &value, sizeof(value)) != sizeof(value))
{
perror("Cannot write thru pipe.\n");
return 1;
}
// Clean shutdown if value == 0
if (value == 0)
{
close(pipe1[WRITE]); // Key change!
if (read(pipe2[READ], &result, sizeof(result)) <= 0 || result == 0)
{
close(pipe2[READ]);
printf("Pipe2[Read] closed!\n");
wait(0);
break;
}
}
else
{
if (read(pipe2[READ], &result, sizeof(result)) <= 0)
break;
printf("Result: %d.\n", result);
if (result == 0)
{
printf("Read zero - exit\n");
close(pipe1[WRITE]);
close(pipe2[READ]);
break;
}
}
}
break;
}
return 0;
}

Related

C pipe: Bad file descriptor

I have the simple producer consumer program in C try to solve it with fork
I get error exactly when producer tries to write on pipe:
I have wrote another program with the same logic but this one does not give me any clue to know why?
Producer failed to write item on pipe: Bad file descriptor
Anyone have idea why I get this error?
Thanks
#define READ 0
#define WRITE 1
int mutex = 1, full = 0, empty = BUFFER_SIZE, x = 0;
void consumer();
void producer();
int wait_(int);
int signal_(int);
int pipefd[2];
int main() {
printf("Starting producer-consumer problem!\n");
//We intend to run the producer in parent process and the consumer in the child process
if (pipe(pipefd) == -1) { /* An error has occurred. */
fprintf(stderr, "%s", "The call to pipe() has failed.\n");
exit(EXIT_FAILURE);
}
for (int j = 0; j < sizeof(pipefd); j++) {
if (pipe(&pipefd[j]) < 0) { //Initialize each pipe appropriately
perror("Error in making pipe...");
}
}
pid_t pid = fork();
if (pid < 0) {
perror("**********Error in creating fork()!**************\n");
exit(STDERR_FILENO);
} else if (pid == 0) {
consumer();//We intend to run the consumer in child
} else {
producer();//We intend to run the producer in parent
}
return 0;
}
int wait_(int s) {
return (--s);
}
int signal_(int s) {
return (++s);
}
void producer() {
printf("Starting Producer\n");
//while (1) {
//sleep(1);
if (close(pipefd[READ]) != 0) {
perror("Error in closing reading pipe");
}
if (write(pipefd[WRITE], &full, 1) < 0) {
perror("Producer failed to write item on pipe");
}
if ((mutex == 1) && (empty != 0)) {
mutex = wait_(mutex);
full = signal_(full);
empty = wait_(empty);
x++;
printf("Producer produces the item %d\n", x);
mutex = signal_(mutex);
}
if (close(pipefd[WRITE]) != 0) {
perror("Error in closing writing pipe");
}
//}
}
void consumer() {
printf("Starting Consumer\n");
//while (1) {
//sleep(1);
int status = 0;
wait(&status); /* wait for all children to return back the result */
if (close(pipefd[WRITE]) != 0) {
perror("Error in closing reading pipe");
}
if (read(pipefd[READ], &full, 1) > 0) {
printf("Consumer\t%d\n", full);
}
if ((mutex == 1) && (full != 0)) {
mutex = wait_(mutex);
full = wait_(full);
empty = signal_(empty);
printf("Consumer consumes item %d\n", x);
x--;
mutex = signal_(mutex);
}
if (close(pipefd[READ]) != 0) {
perror("Error in closing reading pipe");
}
//}
}
The sizeof operator returns the size in bytes. So on a typical system where an int is four bytes, sizeof(pipefd) would result in the value 8. Which is not the correct number of elements for your loop.
Also, pipe(&pipefd[j]) is not correct either. The two pipes in pipefd are already initialized "appropriately". There's no need for any more initialization. Especially since in both this and the previous case you will have undefined behavior.

Pass parameter to another function using C

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.

Transfer switch cases value to another process with named pipe

In my program, I want to send my switch case value to another process using named pipe "pipeselect". I write the number in the pipe and read the number in another program. But When I run the problem, it cannot show anything when I enter a case value. How can I do this?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
char pipeselect[] = "/tmp/pipeselect";
char bufs[2];
int fds;
int select1;
/* Pipe Creation */
if (access(pipeselect, F_OK) == -1)
{
fds = mkfifo(pipeselect, 0700);
if (fds != 0)
{
printf("Pipe creation error\n");
exit(1);
}
}
printf("1. Option 1\n");
printf("2. Option 2\n");
printf("Please select an option: ");
scanf("%d", &select1);
int i = select1;
switch (i)
{
case 1:
if ((fds = open(pipeselect, O_WRONLY)) < 0)
{
printf("Pipe open error\n");
exit(1);
}
write(fds, bufs, i);
close(fds);
printf("Option 1 is selected\n");
break;
case 2:
if ((fds = open(pipeselect, O_WRONLY)) < 0)
{
printf("Pipe open error\n");
exit(1);
}
write(fds, bufs, i);
close(fds);
printf("Option 2 is selected\n");
break;
default:
printf("Wrong Input!\n");
break;
unlink(pipeselect);
exit(0);
}
}
You probably need to use write like this:
bufs[0] = i; // put value entered by user into buffer
write(fds, bufs, 1); // write 1 byte from the buffer
BTW you could narrow down your code like this:
...
scanf("%d", &select1);
if (select1 == 1 || select1 == 2)
{
if ((fds = open(pipeselect, O_WRONLY)) < 0) {
printf("Pipe open error\n");
exit(1);
}
bufs[0] = select1; // put value entered by user into buffer
write(fds, bufs, 1); // write 1 byte from the buffer
close(fds);
printf("Option %d is selected\n", select1);
}
else {
printf("Wrong Input!\n");
}
unlink(pipeselect);
exit(0);

Loop fails to continue looping

I was working on some code to create a new folder everytime a specific FIFO is written to:
int main (int argc, char* argv[]) {
if (argc != 2) {
printf("Usage: %s <directory>\n",argv[0]);
exit(0);
}
int ret = chdir(argv[1]);
if (ret < 0) {
printf("Could not chdir to %s\n",argv[1]);
perror("");
exit(0);
}
ret = mkfifo("create",0666);
if (ret < 0) {
perror("Could not make create fifo");
return 0;
}
int ctfd = open("create",O_RDWR);
if (ctfd < 0) {
perror("Could not open create");
return 0;
}
char create[1];
//socket counter
int count = 0;
char crdir[15];
for(;;ret = read(ctfd,create,1)) {
//printf("%s %d\n",create,count);
if (ret < 0) {
perror("Failed to read from create");
continue;
}
else if (ret < 1 || create[0] == '\n') {
continue;
}
sprintf(crdir,"%d",count);
ret = mkdir(crdir,0777);
if (ret < 0) {
printf("Failed to create dir");
perror("");
continue;
}
count++;
switch(fork()) {
case -1:
perror("Failed to fork");
break;
case 0:
printf("%d\n",count);
break;
default:
//char* argv[ ] = {"netclient",cte};
exit(0);
}
printf("test");
}
//execvp()
return 0;
}
However when run this happens:
arthur#dent:~/coding/netdir$ mkdir test
arthur#dent:~/coding/netdir$ ./netserver test &
[1] 2122
arthur#dent:~/coding/netdir$ echo 1 > test/create
[1]+ Done ./netserver test
1
What did I do wrong? Why does it not continue looping and wait for more input?
The FIFO is opened for reading and writing so that it would not be closed when the writing stopped, did that not work properly?
Taking a second look, I would say your problem is in switch( fork() ).
fork() returns 0 for the child process and the child's PID for the parent but you have exit(0); in default: so you exit the parent.
Another problem (seen first):
Your loop is:
for(;;ret = read(ctfd,create,1)) {
do_something;
}
which is equal to:
while( 1 ) {
do_something;
ret = read(ctfd,create,1);
}
do you 'do something' before reading. That leads to Undefined Beahaviour, e.g. becaus the contents of create isn't defined.

I am having trouble redirecting my output back from file to standard out C

I have looked all over the internet and die.net and can't see to make my code work. My problem is that I am able to redirect the output to a file, but have trouble bringing it back to standard out, I have tried using dup, dup2 and close, but maybe I am using them wrong. Any help would be appreciated, thank you
. My problem begins at the if(myargc >= 3) block when I am trying to redirect the output.
main()
{
int i, myargc =0, background, newfile, file, stdout2, read = 0, write = 0;
pid_t pid;
char input[512], *myargv[60];
while(1)
{
background = 1;
printf("Myshell>");
gets(input);
//scanf("%s", input);
myargc = parser(input, myargv);
if(strcmp(*myargv, "exit") == 0)
{
exit(0);
}
if(strcmp(myargv[myargc-1], "&") == 0)
{
background = 0;
myargv[myargc-1] = '\0';
myargc--;
}
if(myargc >= 3)
{
if(strcmp(myargv[myargc-2], ">") == 0)
{
write = 1;
file = creat(myargv[myargc-1], S_IWUSR);
myargv[myargc-2] = '\0';
if(file < 0)
{
printf("File could not be created.\n");
}
printf("Redirecting output to file %s.\n", myargv[myargc-1]);
fflush(stdout);
stdout2 = dup(STDOUT_FILENO);
//fclose(stdout); // fclose() for type FILE*
newfile = dup2(file, 1); // uses lowest number descriptor (1, since just
// closed stdout)
close(file); // closes old file descriptor duplicate, close() uses int
myargc = myargc-2;
}
}
if ((pid = fork()) == -1 )
{
// if fork fails, print error and exit
perror("Fork failed");
exit(-1);
}
else if (pid == 0) { // child process
if (read == 0 && write == 0)
{
printf("This is the child ready to execute: ");
fflush(stdout);
for (i =0; i < myargc; i++)
{
printf("%s ", myargv[i]);
fflush(stdout);
}
printf("\n");
}
if (execvp(*myargv,myargv) < 0);
{
printf("Execution failed.");
}/* error exit - exec returned */
close(newfile);
dup2(stdout2, STDIN_FILENO);
//close(file);
//if (close(file) == 0)
//{
//dup2(newfile, STDOUT_FILENO);
//close(newfile);
//close(stdout2);
// printf("Reopened stdout\n");
// }
perror("Exec returned");
exit(-1);
}
close(newfile);
if (background == 1) { /* this is the parent -- wait for child to terminate */
wait(pid,0,0);
printf("The parent is exiting now\n");
}else{
waitpid(pid, NULL, WNOHANG); //returns immediately, no wait
}
//test for correct parsing
printf("myargv:\n");
for (i = 0; i < myargc; i++)
{
printf("%s\n", myargv[i]);
}
printf("myargc: %d\n", myargc);
// clear out buffers
memset(&myargv[0], 0, sizeof(myargv));
memset(&input[0], 0, sizeof(input));
}
}

Resources