I have to simulate this command using pipes in c: echo "<exp>" | bc -lq.
Process A must read a string and send it to process B;
Process B executes the "bc -lq" command and returns the result to A.
The code is this, but I can't understand why it doesn't work; in particular, the "bc" command appears to be unable to read the expression from stdin.
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/wait.h>
#define N 1024
#define LEGGI(stringa) if(scanf("%s", stringa) == 0) \
{ \
perror("Impossibile leggere la stringa"); \
exit(EXIT_FAILURE); \
} \
void closePipe(int pipeFd);
void Dup2(int pipeTempFd, int fd);
void Write(int pipeTempFd, char stringa[], int n);
void Read(int pipeTempFd, char stringa[], int n);
int main()
{
char stringa[N];
LEGGI(stringa);
int pipeFd[2];
int pRicezioneFd[2];
if(pipe(pipeFd) == -1 || pipe(pRicezioneFd) == -1)
{
perror("impossibile eseguire la pipe");
exit(EXIT_FAILURE);
}
if(strncmp(stringa, "exit", N) != 0)
{
pid_t processPid;
if((processPid = fork()) == 1)
{
perror("impossibile eseguire la fork");
exit(EXIT_FAILURE);
}
if(processPid != 0)
{
closePipe(pipeFd[0]);
closePipe(pRicezioneFd[1]);
printf("operazione: %s\n", stringa);
Write(pipeFd[1], stringa, N);
closePipe(pipeFd[1]);
read(pRicezioneFd[0], stringa, N);
closePipe(pRicezioneFd[0]);
if(wait(NULL) == -1)
{
perror("Impossibile eseguire la wait");
exit(EXIT_FAILURE);
}
printf("%s\n", stringa);
}
else
{
closePipe(pipeFd[1]);
closePipe(pRicezioneFd[0]);
Dup2(pipeFd[0], 0);
Dup2(pRicezioneFd[1], 1);
Dup2(pRicezioneFd[1], 2);
// closePipe(pipeFd[0]);
// closePipe(pRicezioneFd[1]);
if(execl("/usr/bin/bc", "bc", "-lq", NULL) == -1)
{
perror("programma non reperibile");
exit(EXIT_FAILURE);
}
}
}
return 0;
}
void closePipe(int pipeFd)
{
if(close(pipeFd) == -1)
{
perror("impossibile chiudere il fd della pipe");
exit(EXIT_FAILURE);
}
}
void Dup2(int pipeTempFd, int fd)
{
if(dup2(pipeTempFd, fd) == -1)
{
perror("Impossibile eseguire la dup2");
exit(EXIT_FAILURE);
}
}
void Write(int pipeTempFd, char stringa[], int n)
{
if(write(pipeTempFd, stringa, n) == -1)
{
perror("impossibile scrivere sulla pipa");
exit(EXIT_FAILURE);
}
}
void Read(int pipeTempFd, char stringa[], int n)
{
if(read(pipeTempFd, stringa, n) == -1)
{
perror("impossibile leggere dalla pipe pipa");
exit(EXIT_FAILURE);
}
}
You are writing all 1024 bytes of the mostly uninitialized stringa to bc, which is then choking on illegal characters. bc expects newline terminated, "plain text" expressions.
#define N 1024
char stringa[N]; // stringa := "\236\0\0\241\177>\0\0\3704\241..."
scanf("%s", stringa); // stringa := "1+2\0\177>\0\0\3704\241..."
// ^^^^^^^^^^^^^^^^^^^^
....
Write(pipeFd[1], stringa, N); // ohimè!
You'll want something like this, instead:
Write(pipeFd[1], stringa, strlen(stringa));
Write(pipeFd[1], "\n", 1);
I think my code is similar to yours, one main change was the arguments to the exec call. Another change was that I only have 2 dup2 calls in the child process, one for changing stdin and the other for stdout. You only need to change these.
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/wait.h>
#define N 1024
#define READ_END 0
#define WRITE_END 1
int main()
{
char *input = NULL;
size_t len = 0;
ssize_t amt_read = 0;
puts("Please enter a string: ");
amt_read = getline(&input, &len, stdin);
if(amt_read < 0)
exit(EXIT_FAILURE);
pid_t pid;
int fd[2];
int fd_return[2];
// Create the pipes
pipe(fd);
pipe(fd_return);
pid = fork();
if(pid==0)
{ // If child process
dup2(fd[READ_END], STDIN_FILENO);
dup2(fd_return[WRITE_END], STDOUT_FILENO);
close(fd[WRITE_END]);
close(fd[READ_END]);
close(fd_return[WRITE_END]);
close(fd_return[READ_END]);
execl("/usr/bin/bc", "/usr/bin/bc", "-lq", (char *)NULL);
fprintf(stderr, "Failed to execute\n");
exit(EXIT_FAILURE);
}
else
{ // If parent process
int status;
close(fd[READ_END]);
close(fd_return[WRITE_END]);
// Write to the pipe
write(fd[WRITE_END], input, strlen(input));
close(fd[WRITE_END]);
// Wait for the child to finish
waitpid(pid, &status, 0);
char buff[N];
buff[N-1] = 0;
read(fd_return[READ_END], buff, N-1);
*(index(buff, '\n')) = '\0'; // Add null terminator on your own
close(fd_return[READ_END]);
printf("The Result is: %s\n", buff);
}
free(input);
return 0;
}
EDIT:
I edited the code and debugged it. I also changed the user input schematic so now you no longer allocated static storage for the user input, rather it is allocated dynamically and then this storage is freed at the end with free();
Note, I did leave the static storage for reading the input back in, you can change this in the future if you want.
illegal character: ^# comes from the fact that write() was writing too much, notice that now we only write strlen(buff) amount.
Note: The input to bc will not work unless the string ends like "...\n\0" luckily getline() does this by default.
Also, I left all the pipe closing the same, that was not causing the issue.
Related
I'm writing a program that has an interface like:
myprog file1 file2 c
This program creates two children and P2 with an execlp opens file2, makes a grep -c on this file for founding c and gives the results to his brother P1 (I have to do it closing the STDOUT's FD and dupping the pipe p2p1 between them). P1 receives this from p2p1 and sends this value to P0. Moreover this also makes the same thing with file1 and gives the result to P0, which will print them.
The problem is: the father P0 reads something, but it is wrong.
What I'm supposed to do? Here's the code and thanks for your attention.
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#define MAX_STRING_LENGTH 128
/**************************/
/* DICHIARAZIONE FUNZIONI */
/**************************/
void wait_child();
void processo_p2(char *inputfile, char *c);
void processo_p1(char *inputfile, char *c);
/*********************/
/* VARIABILI GLOBALI */
/*********************/
int p1p0[2], p2p1[2];
int main(int argc, char* argv[])
{
int pid[2], i, value, count=0;
char *c, buf[10];
if (argc !=4)
{
fprintf(stderr, "Numero di argomenti errato\n");
fprintf(stderr, "Usage: %s file1 file2 C\n", argv[0]);
exit(EXIT_FAILURE);
}
c=argv[3];
/* Init */
pipe(p1p0);
pipe(p2p1);
for (i=0; i<2; i++)
{
pid[i] = fork();
if (pid[i] < 0)
{
fprintf(stderr, "P0: Errore nella fork");
exit(EXIT_FAILURE);
}
else if (pid[i] == 0)
{
if (i==0) /*P1*/
{
close(p1p0[0]);
close(p2p1[1]);
sleep(1);
processo_p1(argv[1], c);
close(p2p1[0]);
close(p1p0[1]);
exit(EXIT_SUCCESS);
}
else if (i==1)
{
close(p2p1[0]);
close(p1p0[0]);
close(p1p0[1]);
processo_p2(argv[2],c);
close(p2p1[1]);
exit(EXIT_SUCCESS);
}
}
else
{
printf("P0: created child P%d with PID %d\n", i+1, pid[i]);
close(p2p1[0]);
close(p2p1[1]);
close(p1p0[1]);
}
}
i=0;
int nread;
while ( (nread = read(p2p1[0], &buf[i], sizeof(char)) ) > 0 ) {
i++;
buf[i] = '\0';
printf("%s\n",buf);
for(i=0;i<2;i++)
{
wait_child();
}
return 0;
}
void processo_p2(char *inputfile, char *c)
{
int fd, nread, i=0, found=0;
char temp, row[100];
close(1);
dup(p2p1[1]);
execlp("grep", "grep", "-c", c, inputfile, (char *)0);
perror("P2: errorr in exec");
close(1);
}
void processo_p1(char *inputfile, char *c)
{
int fd, nrw, sk, nread, p2=0, i=0;
int value=1;
char temp, row[100], buf[10];
//RECEIVING DATA FROM P2 AND SENDING TO P0
while ( (nread = read(p2p1[0], &buf[i], sizeof(char)) ) > 0 ) {
i++;
}
buf[i] = '\0';
printf("from p2: %s\n",buf); //NOTHING STAMPED
write(p1p0[1],&buf,strlen(buf)+1);
close(1);
dup(p1p0[1]);
execlp("grep", "grep", "-c", c, inputfile, (char *)0);
perror("P1: errore in exec");
close(p1p0[1]);
}
void wait_child() {
int pid_terminated,status;
pid_terminated=wait(&status);
if (pid_terminated < 0)
{
fprintf(stderr, "%d\n", getpid());
perror("P0: errore in wait");
exit(EXIT_FAILURE);
}
if(WIFEXITED(status))
{
printf("P0: terminazione volontaria del figlio %d con stato %d\n",
pid_terminated, WEXITSTATUS(status));
if (WEXITSTATUS(status) == EXIT_FAILURE)
{
fprintf(stderr, "P0: errore nella terminazione del figlio pid_terminated\n");
exit(EXIT_FAILURE);
}
}
else if(WIFSIGNALED(status))
{
fprintf(stderr, "P0: terminazione involontaria del figlio %d a causa del segnale %d\n",
pid_terminated,WTERMSIG(status));
exit(EXIT_FAILURE);
}
}
Trivia (but it stops the code compiling): The code shown originally has a close brace in the wrong place — the functions are all apparently embedded inside main(). The last brace should be moved up the file considerably.
In the loop that launches the child processs, you have the parent process execute:
printf("P0: created child P%d with PID %d\n", i+1, pid[i]);
close(p2p1[0]);
close(p2p1[1]);
close(p1p0[1]);
In the parent process, immediately after the loop, you have:
int nread;
while ( (nread = read(p2p1[0], &buf[i], sizeof(char)) ) > 0 ) {
i++;
buf[i] = '\0';
printf("%s\n",buf);
for(i=0;i<2;i++)
{
wait_child();
}
return 0;
}
'Tis a pity that you closed the file descriptor you're trying to read from. The parent should be reading from p1p0[0], which you did leave open.
Fixes:
Move the set of three closes out of the loop.
Read from the correct file descriptor.
Insert a missing close brace for the while loop shown, or remove its open brace.
The question in the comments has, as usual for comments, inscrutable code. The problem though is that you have too big a loop.
This is what the tail of my fixed version of your main() function looks like:
…rest of loop to launch children…
else
{
printf("P0: created child P%d with PID %d\n", i + 1, pid[i]);
}
}
close(p2p1[0]);
close(p2p1[1]);
close(p1p0[1]);
i = 0;
int nread;
while ((nread = read(p1p0[0], &buf[i], sizeof(char)) ) > 0)
i++;
buf[i] = '\0';
printf("%s\n", buf);
for (i = 0; i < 2; i++)
{
wait_child();
}
return 0;
}
Given that I saved the source code in a file pip.c and created the program pip from it, a sample run produced:
$ ./pip pip.c pip.c c
P0: created child P1 with PID 75458
P0: created child P2 with PID 75459
from p2: 49
49
P0: terminazione volontaria del figlio 75459 con stato 0
P0: terminazione volontaria del figlio 75458 con stato 0
$
I'm not entirely happy with all the newlines, but at least the same answer is given twice, as it obviously should be.
I have three threads - the first one reads sentences until ";" is given, the second counts the characters in those sentences, and the third shows the result.
Well, I've done this for only one sentence, but sending arrays through pipes seems to generate some problems (as well as reading multiple strings from a thread).
For reading, I can put the string only once, and no more. Even mutex on the whole function doesn't work. Why is that so?
Also, after writing the string I get "write: Success" message.
What's wrong here?
This is the code:
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/stat.h>
#include <pthread.h>
#include <string.h>
int first[2];
int second[2];
void *input(void *ptr)
{
char str[100], ch = '0';
int length, i = 0;
while(1)
{
while(ch != ';')
{
printf("Enter the %d message: ", i + 1);
fflush(stdout);
length = read(STDIN_FILENO, str, sizeof(str));
if(write(first[1], str, sizeof(str)) != length)
{
perror("write");
exit(2);
}
if(length <= 0)
{
if(length == -1)
perror("read");
close(first[1]);
exit(2);
}
i++;
}
}
}
void *countChars(void *ptr)
{
char str[100];
int length, count = 0, i = 0;
while(1)
{
length = read(first[0], str, sizeof(str));
if(length <= 0)
{
if(length == -1)
perror("read");
close(first[0]);
close(second[1]);
exit(2);
}
if(write(STDOUT_FILENO, str, length) != length)
{
perror("write");
exit(2);
}
while(str[count] != '\n') count++;
write(second[1], &count, sizeof(count));
count = 0;
}
}
void *output(void *ptr)
{
int length, count = 0, i = 0;
while(1)
{
length = read(second[0], &count, sizeof(count));
if(length < sizeof(count))
{
close(second[0]);
exit(2);
}
printf("Number of characters: %d\n", count);
}
}
int main()
{
pthread_t t1, t2, t3;
if(pipe(first) == -1)
{
printf("First pipe error");
exit(1);
}
if(pipe(second) == -1)
{
printf("Second pipe error");
exit(1);
}
pthread_create(&t1, NULL, input, NULL);
pthread_create(&t2, NULL, countChars, NULL);
pthread_create(&t3, NULL, output, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
pthread_join(t3, NULL);
return 0;
}
EDIT.
I think the question would be - how to logically solve this? I see it that way:
Thread1 -> (string) -> Thread2 -> (number of chars) -> Thread3 - save elements somewhere
...
Thread1 -> (ending string) -> Thread2 -> (number of chars removed later) -> Thread3 - display all elements
BUT if so then - how to make threads run one by one like this? How to stop the application on ending string? Where to save those integer values in thread 3?
Pipes are used to communicate data between processes, not threads. Threads run in the same process and have access to the same memory so it is pointless to use pipes in that case.
An example of a pipeline with three processes. Parent sends "hello world" to child, who prepends the string length and sends that new string to the grandchild who prints it to stdout.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void parent(int fd_write) {
char *msg = "hello world";
ssize_t len = strlen(msg);
if (write(fd_write, msg, len) != len) {perror("parent write"); exit(1);}
}
void child(int fd_read, int fd_write) {
char msg_in[100], msg_out[150];
ssize_t len = read(fd_read, msg_in, sizeof msg_in);
if (len == -1) {perror("child read"); exit(1);}
msg_in[len] = '\0';
len = sprintf(msg_out, "%d: %s", (int)len, msg_in);
if (write(fd_write, msg_out, len) != len) {perror("child write"); exit(1);}
}
void grandchild(int fd_read) {
char msg[256];
ssize_t len = read(fd_read, msg, sizeof msg);
if (len == -1) {perror("grandchild read"); exit(1);}
msg[len] = '\0';
printf("Msg: %s\n", msg);
}
int main() {
enum {READ, WRITE};
pid_t pid;
int fd[2];
if (pipe(fd) == -1) {perror("first pipe"); exit(1);}
pid = fork();
if (pid == -1) {perror("first fork"); exit(1);}
if (pid == 0) {
int fd2[2];
if (pipe(fd2) == -1) {perror("second pipe"); exit(1);}
pid = fork();
if (pid == -1) {perror("second fork"); exit(1);}
if (pid == 0) {
close(fd2[WRITE]);
grandchild(fd2[READ]);
close(fd2[READ]);
exit(0);
}
close(fd[WRITE]); close(fd2[READ]);
child(fd[READ], fd2[WRITE]);
close(fd[READ]); close(fd2[WRITE]);
wait(NULL);
exit(0);
}
close(fd[READ]);
parent(fd[WRITE]);
close(fd[WRITE]);
wait(NULL);
return 0;
}
In the input thread, after the read call length = read(STDIN_FILENO, str, sizeof(str));, you are writing sizeof(str) and not of size length.
It should be
if(write(first[1], str, length) != length)
Another issue is your code is not matching your specification.
You say that input thread is reading until ';', but ch is never modified in the loop. Fix your code.
To solve my problem, I set
prctl(PR_SET_PDEATHSIG, SIGHUP); as in stackoverflow answer before i called exec*, and took out the part where we pipe the PID. It works!!!!! Wow....
HOWEVER, stackoverflow won't let me say I've answered my own question yet...
So I tried to write a program, which I want to run a program, and kill that program after a cpl seconds if it doesn't finish. DADDY forks off a CHILD, which forks off another BABY, CHILD pipes the PID of the BABY to DADDY, which then waits a second and kills them both if they haven't wrapped up their business (it's a macabre scene). But it doesn't work, DADDY stays in S+ State, and the infinite loop that is Baby goes on forever until I ctr+c. On the bright side, this code is an amalgamation of everything I've learnt on stack-overflow. Here we go.
#include <math.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
static int read_from_pipe(int file)
{
int c;
FILE *stream = fdopen(file, "r");
if (fscanf(stream, "%d", &c) != 1)
{
fprintf(stderr, "Failed to read integer from pipe\n");
exit(1);
}
fclose(stream);
return c;
}
static void write_to_pipe(int file, int pidRacket)
{
FILE *stream = fdopen(file, "w");
fprintf(stream, "%d", pidRacket);
fclose(stream);
}
static int spawnpipe(char *fileName, int *fd)
{
int pid;
int pipe_fds[2];
char *command[] = {"racket", fileName, NULL};
if (pipe(pipe_fds) < 0)
{
fprintf(stderr, "FE: pipe\n");
exit(1);
}
switch ((pid = fork()))
{
case -1:
printf("syserr");
exit(1);
case 0:
close(1);
close(2);
dup(pipe_fds[1]);
close(pipe_fds[0]);
close(pipe_fds[1]);
execvp(*command, command);
perror("execv");
exit(EXIT_FAILURE);
default:
*fd = pipe_fds[0];
close(pipe_fds[1]);
return pid;
}
}
static int spawnfp(char *fileName, FILE **fpp)
{
int fd, pid;
pid = spawnpipe(fileName, &fd);
*fpp = fdopen(fd, "r");
return pid;
}
int main(int argc, char *argv[])
{
pid_t pid;
int mypipe[2];
if (pipe(mypipe))
{
fprintf(stderr, "Pipe failed.\n");
return EXIT_FAILURE;
}
pid = fork();
if (pid < (pid_t) 0)
{
fprintf(stderr, "Fork failed.\n");
return EXIT_FAILURE;
}
else if (pid != (pid_t) 0)
{
double diff = 0;
clock_t launch = clock();
close(mypipe[1]);
int pidRacket = read_from_pipe(mypipe[0]);
while (diff < 1.3)
{
clock_t done = clock();
diff = ((double)done - (double)launch) / (double)CLOCKS_PER_SEC;
}
kill(pidRacket, SIGKILL);
kill(pid, SIGKILL);
return EXIT_SUCCESS;
}
else if (pid == (pid_t) 0)
{
close(mypipe[0]);
char buf[100];
FILE *fp;
char *fileName = argv[1];
int pidRacket = spawnfp(fileName, &fp);
write_to_pipe(mypipe[1], pidRacket);
if (argc == 1)
{
printf("Not enough arguments!");
_exit(EXIT_FAILURE);
}
else if (argc == 2)
{
}
sleep(1);
while (fgets(buf, sizeof buf, fp))
{
printf("%s\n", buf);
}
fclose(fp);
kill(pid, SIGKILL);
return 0;
}
}
Credit to quinsley and vijay!
Various comments as I look at the code:
End messages with newlines; you're on Linux now, not Windows. Windows systems seem to encourage people to leave messages without newlines, but it won't work well on Unix in general and Linux in particular.
Don't use _exit() if you want your error messages to appear, especially ones that don't end in a newline.
Don't report error messages on standard output; report them on standard error (that's what it is for!).
Writing else if (argc == 2) { } (with nothing in the braces) is a little odd if there is an else clause after it, but it is pointless when there is no else clause. You should arguably test for argc != 2 since that is the correct number of arguments (or, perhaps more accurately, any arguments beyond argc == 2 are ignored).
If you want to sleep for a time involving sub-second timing (e.g. 1.3 seconds), use one of the appropriate sub-second sleep commands. In this case, nanosleep() is probably the function to use.
Don't use SIGKILL except in dire emergency. The process signalled with SIGKILL has no chance to clean up or anything; it is killed immediately (assuming your process is allowed to send a signal to the other at all, of course).
case -1: printf("syserr"); with no break; after it means that on error, the flow of control goes into the following case 0: code, which is not what's required. Either break; or exit(1); is probably appropriate. (Bullet 3 applies too.)
Don't close standard error. The code:
close(1);
close(2);
dup(pipe_fds[1]);
close(pipe_fds[0]);
close(pipe_fds[1]);
execvp(*command, command);
perror("execv");
_exit(EXIT_FAILURE);
is never going to report an error; you closed standard error. Remember that programs are entitled to have a standard error channel. The C standard guarantees it, but you have to cooperate and make sure you've not closed standard error.
Some of the casts in:
diff = ((double)((uintmax_t)(clock_t)done) - (double)((uintmax_t)(clock_t)launch)) / (double)CLOCKS_PER_SEC;
are unnecessary. Since both done and launch are of the type clock_t, the casts to clock_t are unnecessary. The intermediate cast to uintmax_t also isn't really necessary. You could simply write:
diff = ((double)done - (double)launch) / (double)CLOCKS_PER_SEC;
and even then, two of the three casts are theoretically redundant (any two of the three could be removed).
The code in read_from_pipe() is curious and error prone. Since you've got a file stream, simply read an integer from it using fscanf(), rather than the curious construct using double arithmetic and fractional values that are then multiplied at the end. This is especially appropriate since the write_to_pipe() code uses printf("%d", ...); to write the data. Since c is already an int, the cast in return (int)c; is superfluous.
Theoretically, it would be a good idea to check the streams returned by fdopen() to ensure that the operation did not fail.
If the pipe() function fails, you report the error on standard output and then continue as nothing had gone wrong.
It is not clear what the racket command actually does. It doesn't exist on my machine.
argv in spawnfp() is unused.
pid = fork(); if (pidDos < (pid_t) 0) generates a warning (accurately) that pidDos might be used uninitialized. The condition should presumably be using pid, not pidDos. You then send a SIGKILL signal to the PID identified at random by pidDos, which is unlikely to lead to happiness.
When I copy cat to racket and invoke the following code (as a program mk built from mk.c) as mk /etc/passwd, I get to see the password file double-spaced (and the message from the shell about Killed: 9.
#include <math.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
static int read_from_pipe(int file)
{
int c;
FILE *stream = fdopen(file, "r");
if (fscanf(stream, "%d", &c) != 1)
{
fprintf(stderr, "Failed to read integer from pipe\n");
exit(1);
}
fclose(stream);
return c;
}
static void write_to_pipe(int file, int pidRacket)
{
FILE *stream = fdopen(file, "w");
fprintf(stream, "%d", pidRacket);
fclose(stream);
}
static int spawnpipe(char *fileName, int *fd)
{
int pid;
int pipe_fds[2];
char *command[] = {"racket", fileName, NULL};
if (pipe(pipe_fds) < 0)
{
fprintf(stderr, "FE: pipe\n");
exit(1);
}
switch ((pid = fork()))
{
case -1:
printf("syserr");
exit(1);
case 0:
close(1);
close(2);
dup(pipe_fds[1]);
close(pipe_fds[0]);
close(pipe_fds[1]);
execvp(*command, command);
perror("execv");
exit(EXIT_FAILURE);
default:
*fd = pipe_fds[0];
close(pipe_fds[1]);
return pid;
}
}
static int spawnfp(char *fileName, FILE **fpp)
{
int fd, pid;
pid = spawnpipe(fileName, &fd);
*fpp = fdopen(fd, "r");
return pid;
}
int main(int argc, char *argv[])
{
pid_t pid;
int mypipe[2];
if (pipe(mypipe))
{
fprintf(stderr, "Pipe failed.\n");
return EXIT_FAILURE;
}
pid = fork();
if (pid < (pid_t) 0)
{
fprintf(stderr, "Fork failed.\n");
return EXIT_FAILURE;
}
else if (pid != (pid_t) 0)
{
double diff = 0;
clock_t launch = clock();
close(mypipe[1]);
int pidRacket = read_from_pipe(mypipe[0]);
while (diff < 1.3)
{
clock_t done = clock();
diff = ((double)done - (double)launch) / (double)CLOCKS_PER_SEC;
}
kill(pidRacket, SIGKILL);
kill(pid, SIGKILL);
return EXIT_SUCCESS;
}
else if (pid == (pid_t) 0)
{
close(mypipe[0]);
char buf[100];
FILE *fp;
char *fileName = argv[1];
int pidRacket = spawnfp(fileName, &fp);
write_to_pipe(mypipe[1], pidRacket);
if (argc == 1)
{
printf("Not enough arguments!");
_exit(EXIT_FAILURE);
}
else if (argc == 2)
{
}
sleep(1);
while (fgets(buf, sizeof buf, fp))
{
printf("%s\n", buf);
}
fclose(fp);
kill(pid, SIGKILL);
return 0;
}
}
I fixed some, but by no means all, of the issues identified in this revision of the code.
Oh, and item 16: the read end of the pipe isn't closed until the third process terminates. You need to pass mypipe[1] to spawnfp(), which needs to relay it to spawnpipe(), and the child created there needs to close the pipe descriptor before executing 'racket'. This is compounded by fscanf() looking for either EOF or a non-digit at the end of the PID it reads from the pipe. You could provide a newline or something at the end and that would also free up the parent process to spin in its timing loop. Since you say racket doesn't terminate, that's why you don't see anything much.
It's easier to paste the whole program again than present the diffs:
#include <assert.h>
#include <math.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
static int read_from_pipe(int file)
{
int c;
FILE *stream = fdopen(file, "r");
assert(stream != 0);
if (fscanf(stream, "%d", &c) != 1)
{
fprintf(stderr, "Failed to read integer from pipe\n");
exit(1);
}
fclose(stream);
return c;
}
static void write_to_pipe(int file, int pidRacket)
{
FILE *stream = fdopen(file, "w");
assert(stream != 0);
fprintf(stderr, "%d: pidRacket = %d\n", (int)getpid(), pidRacket);
fprintf(stream, "%d", pidRacket);
fclose(stream);
}
static int spawnpipe(char *fileName, int *fd, int pfd)
{
int pid;
int pipe_fds[2];
char *command[] = {"racket", fileName, NULL};
if (pipe(pipe_fds) < 0)
{
fprintf(stderr, "FE: pipe\n");
exit(1);
}
switch ((pid = fork()))
{
case -1:
printf("syserr");
exit(1);
case 0:
close(pfd);
close(1);
//close(2);
dup(pipe_fds[1]);
close(pipe_fds[0]);
close(pipe_fds[1]);
execvp(*command, command);
perror("execv");
exit(EXIT_FAILURE);
default:
fprintf(stderr, "%d: pid = %d\n", (int)getpid(), pid);
*fd = pipe_fds[0];
close(pipe_fds[1]);
return pid;
}
}
static int spawnfp(char *fileName, FILE **fpp, int pfd)
{
int fd, pid;
pid = spawnpipe(fileName, &fd, pfd);
*fpp = fdopen(fd, "r");
assert(*fpp != 0);
return pid;
}
int main(int argc, char *argv[])
{
pid_t pid;
int mypipe[2];
if (pipe(mypipe))
{
fprintf(stderr, "Pipe failed.\n");
return EXIT_FAILURE;
}
pid = fork();
if (pid < (pid_t) 0)
{
fprintf(stderr, "Fork failed.\n");
return EXIT_FAILURE;
}
else if (pid != (pid_t) 0)
{
double diff = 0.0;
clock_t launch = clock();
close(mypipe[1]);
fprintf(stderr, "%d: Reading from pipe:\n", (int)getpid());
int pidRacket = read_from_pipe(mypipe[0]);
fprintf(stderr, "%d: Read PID %d from pipe\n", (int)getpid(), pidRacket);
while (diff < 1.3)
{
clock_t done = clock();
diff = ((double)done - (double)launch) / (double)CLOCKS_PER_SEC;
printf("%f\n", diff);
}
kill(pidRacket, SIGKILL);
kill(pid, SIGKILL);
return EXIT_SUCCESS;
}
else if (pid == (pid_t) 0)
{
close(mypipe[0]);
char buf[100];
FILE *fp;
char *fileName = argv[1];
int pidRacket = spawnfp(fileName, &fp, mypipe[1]);
fprintf(stderr, "%d: Writing PID %d to pipe\n", (int)getpid(), pidRacket);
write_to_pipe(mypipe[1], pidRacket);
fprintf(stderr, "%d: Written PID to pipe\n", (int)getpid());
if (argc == 1)
{
printf("Not enough arguments!");
_exit(EXIT_FAILURE);
}
else if (argc == 2)
{
}
sleep(1);
while (fgets(buf, sizeof buf, fp))
{
printf("%s\n", buf);
}
fclose(fp);
fprintf(stderr, "%d: Finished reading from pipe\n", (int)getpid());
kill(pid, SIGKILL);
return 0;
}
}
I made this a while back for stupid fun, it uses up a big chunk of your cpu to run but I'm sure you can modify it to break at a certain point or to fit your needs maybe.
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
int main(int argc, char*argv[])
{
int childpid;
int pids[100];
int count1 = 0, count2 = 0;
int count3 = 0;
L1:
childpid = fork();
if(childpid == 0)
{
}
else
{
if(childpid != 0 && childpid != -1)
{
if(count3 < 100)
{
pids[count3] = childpid;
printf("Pid:%d\n",pids[count3]);
count3++;
goto L1;
}
else
{
count3--;
goto L2;
}
}
L2:
while(count3 > 0)
{
if(pids[count3] != -1 || pids[count3] != 1)
{
printf("Killing pid:%d\n",pids[count3]);
kill(pids[count3],SIGKILL);
}
count3--;
}
if(count3 == 0)
{
goto L1;
}
}
return 0;
}
Hi i'm trying to build a shell on linux and i'm stuck with the pipelining part.First i take the inputs from the user like "ls | sort" then when i try to run the program it lookls like the commands ls and sort doesnt work
It looks like i've done everything right but it still cant seem to work. can you help please. thanks in advance
include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <fcntl.h>
#include <sys/stat.h>
#define CREATE_FLAGS (O_WRONLY | O_CREAT | O_APPEND)
#define CREATE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
int setup();
int main(int argc, const char * argv[])
{
while(1)
{
printf("333sh: ");
if(setup())
break;
}
return 0;
}
int setup(){
char input [128];
char *arg[32];
int i = 1;
while(fgets(input,128,stdin)!=NULL)
{
arg[0] = strtok(input," \n");
while((arg[i]=strtok(NULL," \n")) != NULL){
i++;
}
if (arg[1]!=NULL && strcmp(arg[1],"|")==0 && arg[2]!=NULL ){
pid_t pid;
int fd[3];
pipe(fd);
pid=fork();
if(pid<0){
printf("fork");
}
else if(pid==0){
pid_t cpid;
cpid=fork();
if(cpid==0){
dup2(fd[2], 1); // Replace stdin with the read end of the pipe
close(fd[0]); // Don't need another copy of the pipe read end hanging about
close(fd[2]);
execvp(arg[0],arg);
}
else if(pid>0){
dup2(fd[0], 0); // Replace stdout with the write end of the pipe
close(fd[0]); //close read from pipe, in parent
close(fd[2]); // Don't need another copy of the pipe write end hanging about
execvp(arg[2], arg);
}
}
else if(pid>0){
waitpid(pid, NULL,0);
}
}
}
}
Your biggest problem is that your argument lists for your commands are malformed (after you've resolved the index 2 vs index 1 issue with the pipe file descriptors diagnosed by Ben Jackson in his answer).
I added a function:
static void dump_args(int pid, char **argv)
{
int i = 0;
fprintf(stderr, "args for %d:\n", pid);
while (*argv != 0)
fprintf(stderr, "%d: [%s]\n", i++, *argv++);
}
and called it just before the calls to execvp(), and the output I got was:
$ ./ns
333sh: ls | sort
args for 29780:
0: [ls]
1: [|]
2: [sort]
ls: sort: No such file or directory
ls: |: No such file or directory
^C
$
The control-C was me interrupting the program. The arguments for each command must be 'the command name' (conventionally, the name of the executable), followed by the remaining arguments and a null pointer.
Your tokenization code is not providing two correct commands.
You also have a problem with which PID you're looking at:
cpid = fork();
if (cpid == 0)
{
dup2(fd[1], 1);
close(fd[0]);
close(fd[1]);
dump_args(getpid(), arg);
execvp(arg[0], arg);
fprintf(stderr, "Failed to exec %s\n", arg[0]);
exit(1);
}
else if (pid > 0) // should be cpid!
{
dup2(fd[0], 0);
close(fd[0]);
close(fd[1]);
dump_args(pid, arg);
execvp(arg[1], arg);
fprintf(stderr, "Failed to exec %s\n", arg[1]);
exit(1);
}
You also need to close the pipe file descriptors in the parent process before waiting.
This code compiles and 'works' for simple x | y command sequences such as ls | sort or ls | sort -r. However, it is far from being a general solution; you'll need to fix your argument parsing code quite a lot before you reach a general solution.
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int setup(void);
int main(void)
{
while (1)
{
printf("333sh: ");
if (setup())
break;
}
return 0;
}
static void dump_args(int pid, char **argv)
{
int i = 0;
fprintf(stderr, "args for %d:\n", pid);
while (*argv != 0)
fprintf(stderr, "%d: [%s]\n", i++, *argv++);
}
int setup(void)
{
char input[128];
char *arg[32];
int i = 1;
while (fgets(input, sizeof(input), stdin) != NULL)
{
arg[0] = strtok(input, " \n");
while ((arg[i] = strtok(NULL, " \n")) != NULL)
{
i++;
}
if (arg[1] != NULL && strcmp(arg[1], "|") == 0 && arg[2] != NULL)
{
pid_t pid;
int fd[2];
arg[1] = NULL;
pipe(fd);
pid = fork();
if (pid < 0)
{
fprintf(stderr, "fork failed\n");
return 1;
}
else if (pid == 0)
{
pid_t cpid = fork();
if (cpid < 0)
{
fprintf(stderr, "fork failed\n");
return 1;
}
else if (cpid == 0)
{
printf("Writer: [%s]\n", arg[0]);
dup2(fd[1], 1);
close(fd[0]);
close(fd[1]);
dump_args(getpid(), arg);
execvp(arg[0], arg);
fprintf(stderr, "Failed to exec %s\n", arg[0]);
exit(1);
}
else
{
printf("Reader: [%s]\n", arg[2]);
assert(cpid > 0);
dup2(fd[0], 0);
close(fd[0]);
close(fd[1]);
dump_args(getpid(), &arg[2]);
execvp(arg[2], &arg[2]);
fprintf(stderr, "Failed to exec %s\n", arg[2]);
exit(1);
}
}
else
{
close(fd[0]);
close(fd[1]);
assert(pid > 0);
while (waitpid(pid, NULL, 0) != -1)
;
}
}
}
return 1;
}
You're using fd[0] and fd[2] but pipe(fd) only sets fd[0] and fd[1].
Couple of immediate problems:
setup() has no return value, but you expect an int
The definition of fgets is:
char * fgets ( char * str, int num, FILE * stream );
Get string from stream
Reads characters from stream and stores them as a C string into str until (num-1) characters have been read or either a newline or the end-of-file is reached, whichever happens first.
A newline character makes fgets stop reading, but it is considered a valid character by the function and included in the string copied to str.
fgets() returns NULL on an error; otherwise it returns a pointer to str. So this seems like a very unsound test condition in your while loop.
I am trying to learn pipes and I am trying out this program:
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<sys/types.h>
#include<fcntl.h>
#define MAXLINE 100
void main(){
int pipe1[2],pipe2[2];
pid_t childpid;
if(pipe(pipe1)<0){
perror("Unable to create the pipe for pipe1");
exit(-1);
}
if(pipe(pipe2)<0){
perror("Unable to create the pipe for pipe1");
exit(-1);
}
childpid=fork();
printf("The child PID is:%d\n",childpid);
if(childpid==0){
printf("In the child process");
close(pipe1[1]);
close(pipe2[0]);
server(pipe1[0],pipe2[1]);
exit(0);
}
close(pipe1[0]);
close(pipe2[1]);
client(pipe2[0],pipe1[1]);
waitpid(childpid,NULL,0);
exit(0);
}
void client(int readfd,int writefd){
int n,len;
char buff[MAXLINE];
printf("Please enter the name of the file to be read:");
fgets(buff,MAXLINE,stdin);
len=strlen(buff);
if(buff[len-1]=='\n')
len--;
write(writefd,buff,len);
printf("File name written into the pipe\n");
printf("The num of bytes written are:\n",read(readfd,buff,MAXLINE));
while((n-read(readfd,buff,MAXLINE))>0){
printf("Trying to read the content\n");
write(STDOUT_FILENO,buff,n);
}
}
void server(int readfd,int writefd){
int fd,n;
char buff[MAXLINE + 1];
write(writefd,"Yello in the server process",strlen("Yello in the server process"));
if((n=read(readfd,buff,MAXLINE))==0)
perror("End of file while reading");
buff[n]='\0';
if((fd=fopen(buff,O_RDONLY))<0){
snprintf(buff+n,sizeof(buff)-n,"Can't open, %s",strerror(errno));
n=strlen(buff);
write(writefd,buff,n);
}
while( (n=read(fd,buff,MAXLINE))>0){
write(writefd,buff,n);
close(fd);
}
}
The problem is I enter the file name and the program just exits. I tried to gdb the child process by setting set "follow-fork-mode child", and still nothing happens. Any ideas as to where I could be going wrong?
Ok, some more additional debugging info is: I set the follow-fork-mode to child.and it is causing a segmentation fault at the opening of the file.
Program received signal SIGSEGV, Segmentation fault.
[Switching to process 28025]
0x00197f08 in _IO_file_fopen () from /lib/libc.so.6
This code in client() looks suspicious:
while((n-read(readfd,buff,MAXLINE))>0){
Surely, that should be:
while ((n = read(readfd, buff, MAXLINE)) > 0)
{
The change from - to = is the important one, of course; the rest are cosmetic (and one is even controversial).
You should also pay attention to compiler warnings. Given:
int fd,n;
...
if((fd=fopen(buff,O_RDONLY))<0){
There's no way this should be compiling without major warnings; fopen() returns a FILE *, not a file descriptor (int).
You also seem to have some odd communications. The server sends a message to the client before reading the file name from the client. The client, OTOH, does not necessarily read that message separately; it gets a glob of information and reports how many bytes it got. That may have included some of the file as well as the introductory message.
You shouldn't close a file in the loop that is reading from it:
while( (n=read(fd,buff,MAXLINE))>0){
write(writefd,buff,n);
close(fd);
}
The close should be outside the loop.
This code more or less works; it is messier than I'd like, but it does more or less work.
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define MAXLINE 100
static void server(int readfd, int writefd);
static void client(int readfd, int writefd);
int main(void)
{
int pipe1[2], pipe2[2];
pid_t childpid;
if (pipe(pipe1)<0)
{
perror("Unable to create the pipe for pipe1");
exit(-1);
}
if (pipe(pipe2)<0)
{
perror("Unable to create the pipe for pipe2");
exit(-1);
}
childpid = fork();
printf("The child PID is:%d\n", childpid);
if (childpid == 0)
{
printf("In the child process\n");
close(pipe1[1]);
close(pipe2[0]);
server(pipe1[0], pipe2[1]);
exit(0);
}
close(pipe1[0]);
close(pipe2[1]);
client(pipe2[0], pipe1[1]);
waitpid(childpid, NULL, 0);
return 0;
}
static void client(int readfd, int writefd)
{
int n, len;
char buff[MAXLINE];
printf("Please enter the name of the file to be read:");
fgets(buff, MAXLINE, stdin);
len = strlen(buff);
if (buff[len-1]=='\n')
len--;
write(writefd, buff, len);
printf("File name (%.*s) written into the pipe\n", len, buff);
printf("The num of bytes written are: %d\n", (int)read(readfd, buff, MAXLINE));
while ((n = read(readfd, buff, MAXLINE)) > 0)
{
printf("Trying to read the content\n");
write(STDOUT_FILENO, buff, n);
}
}
static void server(int readfd, int writefd)
{
int fd, n;
char buff[MAXLINE + 1];
fprintf(stderr, "Server: %d\n", (int)getpid());
write(writefd, "Yello in the server process", strlen("Yello in the server process"));
if ((n = read(readfd, buff, MAXLINE))==0)
perror("End of file while reading");
buff[n] = '\0';
if ((fd = open(buff, O_RDONLY)) < 0)
{
snprintf(buff+n, sizeof(buff)-n, "Can't open, %s", strerror(errno));
n = strlen(buff);
write(writefd, buff, n);
}
else
{
while ((n = read(fd, buff, MAXLINE)) > 0)
{
if (write(writefd, buff, n) != n)
{
fprintf(stderr, "Write failed in server\n");
break;
}
}
close(fd);
}
}
Note that the code does not try using a file descriptor that it fails to open. It does not crash; the n-read(...) problem is one major part of the trouble, comments notwithstanding. The misuse of fopen() for open() was another major part of the trouble.