I have two programs: Program "Vanilla", and program "verB".
My instructions are that the main process will deal with I\O from the user, and the child will call execve() and run the "Vanilla" process. To accomplish this, I have to use dup2() to replace stdin\stdout on both pipes. (The Vanilla program should use fgets() to read from the pipe).
Inside the "Vanilla" program I read two strings from the user until ctrl+D is pressed, Vanilla calls "xorMethod()" which is doing something (not relevant what) and returns a result.
When I run the "verB" program on Linux(), I only get the "Please insert first, the second string" and then nothing happens and the program stops running.
I want that the parent will continue getting two strings until ctrl+D is pressed, and print the result that he got from his child on the screen.
Vanilla.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "Vanila.h"
#include "xor.h"
#define MAXLEN 80
int main()
{
char s1[MAXLEN + 1];
char s2[MAXLEN + 1];
while (!feof(stdin))
{
if (readString(s1) == -1)
break;
if (readString(s2) == -1)
break;
fflush(stdin);
int res = xorMethod(s1, s2);
printf("%s xor %s = %d", s1, s2, res);
}
return 1;
}
int readString(char * string)
{
if ((fgets(string, MAXLEN + 1, stdin) < 0 || feof(stdin)))
return -1;
string[strcspn(string, "\n")] = 0;
return 1;
}
verB.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "Vanila.h"
#define MAXLEN 80
int readStr(char * string);
int main()
{
int pipeToChild[2];
int pipeToParent[2];
char * argv[] = { "./Vanilla",NULL };
if (pipe(pipeToChild) == -1)
return -1;
if (pipe(pipeToParent) == -1)
return -1;
pid_t pid = fork();
if (pid == -1)
return -1;
if (pid == 0) //CHILD proccess
{
close(pipeToChild[0]);
close(pipeToParent[1]);
dup2(pipeToChild[0], fileno(stdin));
dup2(pipeToParent[1], fileno(stdout));
execve(argv[0], argv, NULL);
}
else
{
char string1[MAXLEN + 1];
char string2[MAXLEN + 1];
char result[MAXLEN + 1];
close(pipeToChild[0]);
close(pipeToParent[1]);
while (!feof(stdin))
{
printf("Please insert first string : ");
if (readStr(string1) == -1)
return -1;
printf("Please insert second string : ");
if (readStr(string2) == -1)
return -1;
write(pipeToChild[1], string1, strlen(string1));
write(pipeToChild[1], string2, strlen(string2));
read(pipeToParent[0], &result, MAXLEN);
printf("%s\n", result);
}
wait(NULL);
}
return 1;
}
int readStr(char * string)
{
if ((fgets(string, MAXLEN + 1, stdin) < 0 || feof(stdin)))
return -1;
string[strcspn(string, "\n")] = 0;
return 1;
}
You close the wrong ends of the pipes in the child process. You close the read end of pipeToChild and then dup2 the standard input stream, so you child program will have a closed standard input stream. You should close the write end of pipeToChild and the read end of pipeToParent in the child process, and the other way around in the main process:
if (pid == 0) //CHILD proccess
{
close(pipeToChild[1]);
close(pipeToParent[0]);
/* ... */
else //PARENT
{
close(pipeToChild[0]);
close(pipeToParent[1]);
Related
Why this code may generate Runtime Error in ejudge? This program counts the number of words from stdin input. Words can be separated by any amount of ' ' and '\n'.
It seems like fork() can cause a problem but I am not sure why I don't get the same error on my computer.
ejudge uses gcc - Plain C, 64 bit, using -std=c11 or -std=gnu11
The task:
On the standard input stream a text string is given which consists of
words (a sequence of non-space characters), between which there can be
any number of whitespace characters, including line feeds.
You need to calculate the number of words if you know there are not
more than 255, and output this value to the standard output stream.
Use creating new processes so that each process reads not more than
one word, e.g. using scanf("%s", ...).
You can only output the result from the process which was started
first (i.e. from the original program).
The resulting program must return with return code 0.
The size of each word does not exceed 4096 bytes.
My code:
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#define DBG(args...) fprintf(stderr, args)
//#define DBG(args...)
int main(int argc, char* argv[])
{
int status;
pid_t pid;
pid_t first_child;
for (int i = 0; i < 256; ++i) {
pid = fork();
if (pid == 0) { // child continue reading
char str[4097];
if (scanf("%s", str) != EOF)
continue;
exit(1);
} else {
if (i == 1) {
first_child = pid;
}
if (wait(&status) == first_child) {
break;
} else {
exit(WEXITSTATUS(status) + 1);
}
}
}
fprintf(stdout, "%i\n", WEXITSTATUS(status));
fflush(stdout);
fclose(stdout);
return 0;
}
Rewrote the algorithm and it worked!
In the first version, many unnecessary forks were made. For example, if 6 were intended it created 12.
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
// #define DBG(args...) fprintf(stderr, args)
#define DBG(args...)
int main(int argc, char* argv[])
{
int status;
pid_t first_pid;
pid_t pid = fork();
if (pid != 0) {
wait(&status);
printf("%i\n", WEXITSTATUS(status));
return 0;
}
for (int i = 0; i < 256; ++i) {
char str[4097];
if (scanf("%s", str) == EOF) {
DBG("PID %i\n", pid);
exit(0);
}
pid = fork();
if (pid != 0)
break;
}
DBG("PID %i waiting\n", pid);
wait(&status);
exit(WEXITSTATUS(status) + 1);
}
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++;
}
}
We were asked to prompt the user to enter phrases, and continue asking them until they get the correct phrase needed, for 30 seconds. Here's what I've come up with:
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void childprocess(void)
{
int start = 30;
do
{
start--;
sleep(1);
} while (start >= 0);
printf("Time ran out!\n");
exit(EXIT_SUCCESS);
}
int main(void)
{
pid_tiChildID;/* Holds PID of current child */
char word[100] = "cat";
char input[100];
int length;
iChildID = fork();
if (0 > iChildID)
{
perror(NULL);
return 1;
}
else if (0 == iChildID)
{
childprocess();
return 0;
}
/* Parent process */
while (1)
{
fgets(input, sizeof(input), stdin);
length = strlen(input);
if (input[length - 1] == '\n')
{
--length;
input[length] = '\0';
}
if (strcmp(word, input) == 0)
break;
printf("Try again\n");
}
kill(iChildID, SIGUSR1);/* terminate repeating message */
printf("Finally!\n");
return 0;
}
The problem: after 30 seconds, it prints "Time runs out" but won't terminate. How do I terminate the program after 30 seconds? Any help?
Here, you are using fork which creates two separate processes with two different PIDs. You are killing child process but parent is still running so program just dont quit.
You could have also used pthread instead of fork with remains in same single process but what ever you are trying to achieve is simple with alarm function. You dont have to manage any other process. Just use alarm.
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
static void ALARMhandler(int sig)
{
printf("Time ran out!\n");
exit(EXIT_SUCCESS);
}
int main(void)
{
char word[100] = "cat";
char input[100];
size_t length;
signal(SIGALRM, ALARMhandler);
alarm(30);
while(1) {
fgets(input, sizeof(input),stdin);
length = strlen(input);
if(input[length-1] == '\n') {
--length;
input[length] = '\0';
}
if (strcmp(word,input) == 0)
break;
printf("Try again\n");
}
/* terminate repeating message */
printf("Finally!\n");
return 0;
}
Hope it helps !!
Can I use standard input for interprocess communication? I wrote the following gnu c code as an experiment, but the program hangs waiting for input after printing the character defined as val. Neither a newline nor fflush in the sending process seem to alleviate the problem.
#include <unistd.h>
#include <stdio.h>
int main(void) {
char val = '!';
int proc = fork();
if (proc < 0)
return -1;
if (proc == 0) {
write(0, &val, 1);
return 0;
}
else {
char ch[2] = { 0 };
read(0, ch, 1);
printf("%s\n", ch);
return 0;
}
return -2;
}
You can use pipe for IPC. Now if you want to use STDIN_FILENO and STDOUT_FILENO it would look like this:
#include <unistd.h>
#include <stdio.h>
int main(void) {
char val = '!';
int filedes[2];
pipe(filedes);
int proc = fork();
if (proc < 0)
return -1;
if (proc == 0) {
close(1);
dup(filedes[1]);
close(filedes[0]);
close(filedes[1]);
write(1, &val, 1);
return 0;
}
else {
char ch[2] = { 0 };
close(0);
dup(filedes[0]);
close(filedes[0]);
close(filedes[1]);
read(0, ch, 1);
printf("%s\n", ch);
return 0;
}
return -2;
}
Combination close(x) and dup(filedes[x]) closes STDOUT/STDIN makes copy of filedes[x] into first available descriptor, what you just closed. As suggested by Jonathan example is now closing both filedes ends and without any doubts is using STDIN/STDOUT.
I have a problem where I must implement a key logger into a shell we have made in class. I am having trouble getting the flow of the program within a while loop to continue looping after a child process is created and it has ran execlp().
Here is a simple program I have made to work on the part I am having trouble with.. My main program, pipe.c, includes the parent/child process with a while loop that "should" continue getting an input from the user with fgets(), create a child process, use dup2(), write to stdout, then the child process invoke the receive.c executable which will get the input from stdin and display it..
/* file: pipe.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main() {
int key_logger_on = 0;
int p[2];
pid_t pid;
char str[256];
char input[1024];
int status;
char * file = "test.txt";
printf("Input :: ");
while(fgets(input, sizeof(input), stdin)) {
if (pipe(p)==-1) {
perror("Pipe create error");
exit(1);
}
if ((pid=fork())==-1) {
perror("Fork create error");
exit(1);
}
if (pid==0) {
close(p[1]); // Close write
dup2(p[0],0);
close(p[0]);
execlp("receive",file,NULL);
}
else {
close(p[0]); // Close read
fflush(stdout);
dup2(p[1],1);
close(p[1]);
write(1, input, strlen(input)+1);
waitpid(pid, NULL, 0);
}
printf("Input :: ");
}
}
Here is the simple receive.c that gets the stdin of the input and displays it. The file is just a test of passing a parameter.
/* file: receive.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
char input[256];
fgets(input, sizeof(input), stdin);
printf("FILE: %s RECEIVE: %s", argv[0],input);
return 0;
}
Right now, all this does for me is when ran the first time, it gets the input, sends it to stdout, child calls receive, prints out the input, and then the whole parent program exits, the while loop is ignored, everything just ends. I'm very new to forks and pipes so this is very frustrating to deal with! Even made me post a question on here for the first time! Thank you very much in advance.
Did it today as repetition task for me . CHeck this code . I tested it with your receive too :
#define PREAD 0
#define PWRITE 1
/*
*
*/
int main(int argc, char** argv) {
int key_logger_on = 0;
int pIn[2];
int pOut[2];
pid_t pid;
char str[256];
char input[1024] = "";
int status;
char file[] = "test.txt";
char buf;
printf("Input :: ");
while (fgets(input,sizeof(input),stdin)) {
char nChar;
int nResult;
if (pipe(pIn) < 0) {
perror("allocating pipe for child input redirect");
return -1;
}
if (pipe(pOut) < 0) {
close(pIn[PREAD]);
close(pIn[PWRITE]);
perror("allocating pipe for child output redirect");
return -1;
}
pid = fork();
if ( pid==0) {
// child continues here
// redirect stdin
if (dup2(pIn[PREAD], 0) == -1) {
perror("stdin");
return -1;
}
// redirect stdout
if (dup2(pOut[PWRITE], 1) == -1) {
perror("stdout");
return -1;
}
// redirect stderr
if (dup2(pOut[PWRITE], 2) == -1) {
perror("stderr");
return -1;
}
// all these are for use by parent only
close(pIn[PREAD]);
close(pIn[PWRITE]);
close(pOut[PREAD]);
close(pOut[PWRITE]);
// run child process image
nResult = execl("receive",file,NULL);
exit(nResult);
} else if (pid > 0) {
// parent continues here
// close unused file descriptors, these are for child only
close(pIn[PREAD]);
close(pOut[PWRITE]);
write(pIn[PWRITE], input, strlen(input));
// char by char reading
while (read(pOut[PREAD], &nChar, 1) == 1) {
write(STDOUT_FILENO, &nChar, 1);
}
// close we done
close(pIn[PWRITE]);
close(pOut[PREAD]);
}
printf("Input :: ");
}
}