Two C Programs to interact with each other and play guessing game - c

So, for my class here I'm supposed to first create a guessing game that takes input from the user and based and their name, tell them if they're close to the magic number. Here is my program for that.
#include <stdio.h>
#include <string.h>
typedef struct { //user info. stores name and number from file input.
char list_name[20];
int list_num;
}list;
char user_name[20]; // info scanned in from user playing the game.
int user_magic_num; // "
int main(){
FILE *fp;
int i; // for the for loop.
list answers;
int matched = 0;
fp = fopen("answers.txt", "rb");
if(fp == NULL)
{
perror("Error opening file");
return -1;
}
inquiry();
for (i=0; i<3; i++)
{
fscanf (fp, "%s %d", answers.list_name, &answers.list_num);
if(feof(fp))
{
puts("End of file.");
break;
}
if (strcmp(answers.list_name,user_name)==0)//User is recognized.
{
matched = 1;
if(user_magic_num==answers.list_num)
{
printf("\n Yes, %s. You guess correctly! %d is the magic
number!\n",user_name, answers.list_num);
break;
}
else if(user_magic_num>answers.list_num)
{
printf("\n No, that's too high.\n");
}
else
{
printf("\n No, that's too low.\n");
}
}
}
if (!matched)
{
answers.list_num = 12345;
printf ("\n User not recognized. Default magic number set.\n\n");
if(user_magic_num==answers.list_num)
{
printf("\n You guess correctly! %d is the magic number!\n",
answers.list_num);
}
else if(user_magic_num>answers.list_num)
{
printf("\n No, that's too high.\n");
}
else
{
printf("\n No, that's too low.\n");
}
}
return 0;
}
void inquiry()
{
printf("\n Hi, what's your name? \n \n");
scanf("%s",user_name);
printf("\n Hello, %s! \n \n What's the magic number? \n \n",user_name);
scanf("%d", &user_magic_num);
printf("\n You guessed %d.\n\n", user_magic_num);
}
It works, but now I have to write another program that responds to it.
My task is to write a simple C program that ”plays” the guessing
game run by my program questioner.c.
It must respond to the two prompts described above with appropriate answers,
that is, a name and a number. It’s OK if the program provides the same name
and number each time.
For example: (Output from my program in bold, its input in italics)
*What is your name?*
**Bob**
*What is the magic number, Bob?*
**78901**
*TOO HIGH*
If your program receives an input string other than the prompts described in Problem
2 above, it should reply with three question marks (???) and then exit. For example:
*What is your name?**
**Bob**
*Do you like apples?*
**???
The instructions also say to turn this in as a single .c file, but I'm not entirely sure how I would do that without using headers? Any help or hints are greatly appreciated.

Here you need inter process communication between two processes. There are lots of options for inter process communication. But for your requirement easiest method will of Queue. Create two queues for example question_q and answer_q (check how to create queue in unix and how to read a queue and how to drop message to a queue ). Now your above programme will drop the question in the question_q which will read by other process which responds to every question of above process and drop the answer as Bob to answer_q which will again read by your another process. I think this will help you.

As Jonathan Leffler commented, you need two pipes: One for reading the program stdout and another to write to its stdin.
Here is a small example:
program.c:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv){
char name[20];
int number;
//Ask
printf("Hi, what is your name?\n");
fflush(stdout);
//Get user input
scanf("%19s", name);
//Ask
printf("What is the magic number?\n");
fflush(stdout);
//Get user input
scanf("%d", &number);
printf("Thanks!\n");
fflush(stdout);
printf("\nYour name is %s and the magic number is %d!\n", name, number);
fflush(stdout);
return 0;
}
Note that on the program I'm constantly flushing the stdout, because it's fully buffered when it points to a non-interactive device, which means it would wait for all the output to be accumulated before actually flushing it. If I didn't flush every printf in the program, the interacting program would hang waiting for the question while the program itself would wait for the answer before flushing the question to the stdout pipe.
interacting_program.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
int main(int argc, char **argv){
//A pipe to read the program output
int output_pipe[2];
//A pipe to send input to the program
int input_pipe[2];
//For the fork/exec
pid_t pid;
//To read the program output
char process_line[4096];
//Create pipes
if(pipe(output_pipe) == -1){
printf("Failed on pipe();\n");
exit(1);
} else if(pipe(input_pipe) == -1){
printf("Failed on pipe();\n");
exit(1);
}
//Fork the process
pid = fork();
if(pid == -1){
printf("Failed on fork();\n");
exit(1);
} else if(pid == 0){
//CHILD PROCESS
//We duplicate the writing end of the output pipe to have the same file descriptor number as the process stdout, so everything it writes to stdout will be sent to the reading side of the pipe (output_pipe[0])
dup2(output_pipe[1], STDOUT_FILENO);
//Now that the output is redirected we can close the pipes
close(output_pipe[1]);
close(output_pipe[0]);
//We duplicate the reading end of the input pipe to have the same file descriptor number as the process stdin, so everything that is written to the other end of the pipe (input_pipe[1]) will be sent to the process stdin.
dup2(input_pipe[0], STDIN_FILENO);
//Now that the input is redirected we can close the pipes
close(input_pipe[1]);
close(input_pipe[0]);
//Exec
execl("./program", "./program", NULL);
//In case exec fails
printf("Failed to execute program!\n");
exit(1);
} else {
//PARENT PROCESS
int read_bytes;
//Read from the pipe that is now the child process stdout
while((read_bytes = read(output_pipe[0], process_line, sizeof(process_line))) > 0){
//Check what was read and respond accordingly
printf("[PROGRAM OUTPUT]: %.*s", read_bytes, process_line);
if(strcmp(process_line, "Hi, what is your name?\n") == 0){
printf("[ANSWER]: Mike\n");
write(input_pipe[1], "Mike\n", 5);
} else if (strcmp(process_line, "What is the magic number?\n") == 0){
printf("[ANSWER]: 41\n");
write(input_pipe[1], "41\n", 3);
} else {
if(strcmp(process_line, "Thanks!\n") == 0){
printf("You're welcome!\n");
} else if(strcmp(process_line, "\nYour name is Mike and the magic number is 41!\n") == 0){
printf("Done!\n");
break;
} else {
printf("???\n");
}
}
//Clears the process line
memset(process_line, 0, sizeof(process_line));
}
//Close the pipes
close(input_pipe[1]);
close(input_pipe[0]);
close(output_pipe[1]);
close(output_pipe[0]);
printf("Waiting child process!\n");
wait(NULL);
}
return 0;
}
It's a bit simplistic and maybe you'll have to change the code to fit your needs, but it's an example of how to use pipes to read a programs stdout and write to its stdin.
First you create both pipes (they are unidirectional, so you will need one for the stdout and another one for the stdin), use the fork to create the child process, duplicate the pipes to have the file descriptor number of the stdin and stdout (remembering to have the write side of the pipe on the stdout and the read side of the pipe on the stdin) and execute the program itself, while on the parent process you'll just use the other end of those pipes to read the programs output and write to its input, using the read and write system calls.
If you're not familiar with fork, exec, pipe and dup2 they might be a little hard to understand at first, but hopefully the comments will help guide you through the code (and don't forget to also read the man pages).

Related

waiting for 2 child processes

I'm trying to write a C program in which the main process creates two children: Ping and Pong. Ping prints “ping” followed by a number, and Pong prints “pong” followed by a number, the output must be as the sample run in Figure 1:"
Here is what I tried to do:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
void main(){
//initializing pipes
int td[2];
int td2[2];
pipe(td);
pipe(td2);
int pid=fork();
if(pid){ //in parent process
int pid2=fork();
if(pid2){// still in parent process
//1st time
int number1;
printf("Enter a number: ");
scanf("%d",&number1);
write(td[1],&number1,sizeof(number1));
printf("<>");
write(td2[1],&number1,sizeof(number1));
printf("--");
//2nd time
int number2;
printf("Enter a number: ");
scanf("%d",&number2);
write(td[1],&number2,sizeof(number2));
printf("<>");
write(td2[1],&number2,sizeof(number2));
printf("--");
}
else{// in pong process
int number;
read(td2[0],&number,sizeof(number));
printf("pong%d \n",number);
}
}
else{ //in ping process
int number;
read(td[0],&number,sizeof(number));
printf("ping%d \n",number);
}
}//main end
explanation: the problem that I faced here, is that the pong gets printed before ping and the parent process doesn't wait for its children to end ( and some output gets printed after root/desktop etc..)
Another problem that I solved, I had read method in the parent process, it fixed my problem because I know that read forces the program to wait until something is written to the pipe, but in this case, we have "write" in the parent process, so the parent is not waiting
I also tried to implementing wait(NULL) but it doesn't seem to work.
any suggestion would be much appreciated
It appears that you (your instructor) are over-complicating.
So, you want your main() to do
int main(void) {
int n;
//set up pipes and forks
printf("Enter a number"); scanf("%d", &n);
// make child1 output " ping<n>\n"
puts(" <>");
// make child2 output " pong<n>\n"
printf(" --\nEnter a second number"); scanf("%d", &n);
// make child1 output " ping<n>\n"
puts(" <>");
// make child2 output " pong<n>\n"
puts(" -- THE END --");
// close pipes
return 0;
}
So, except for "ping" vs "pong" (and using different pipes) the children are absolutely identical. Maybe we can send the string with the number and keep the number of functions down? No ... what about setting the string when creating the process? This sounds better
// set up pipes and forks
int pipes1[2], pipes2[2];
pipe(pipes1);
if (fork() == 0) /* child #1 */ child("ping", pipes1);
close(pipes1[0]); // the read end of the pipe belongs to the child
pipe(pipes2);
if (fork() == 0) /* child #2 */ child("pong", pipes2);
close(pipes2[0]); // we are not using the read end of the pipe
Now... how do we make a child (children are waiting at their read() call) work? Well, easy! We write on our end of the pipe
scanf("%d", &n); // get value from user
write(pipes1[1], &n, sizeof n); // automatically unblock child1
write(pipes2[1], &n, sizeof n); // automatically unblock child2
Repeat these statements for the 2nd user input.
Don't forget to close the write ends of the pipes
close(pipes1[1]);
close(pipes2[1]);
And that's the main() function. What about the function for the children? The child(char *sign, int pipes[2])?
void child(char *sign, int pipes[2]) {
close(pipes[1]); // close write end
int i;
for (;;) {
if (read(pipes[0], &i, sizeof i)) {
printf(" %s%d\n", sign, i); // print and go back to waiting at the read()
} else {
break; // exit the loop when read fails
}
}
close(pipes[0]); // no more reading
exit(EXIT_SUCCESS); // not going back to main()
}

Continuous writing and reading using pipes with multiple processes

My code consists of two processes. The parent process continuously reads a single char from stdin and write to the pipe (without the need to press ENTER). The child process reads from the pipe and writes to stdout. My parent process successfully writes to the pipe, but child process isn't printing the output.
The reason the child process isn't printing out the output is because it's stuck in the while loop of the parent process and never enters the child process's while loop.
When I force quit the parent process using the Activity Monitor on my mac, what I typed in actually gets printed out. Followed by "Killed:9"
Is there a way to fix my code so each time the Parent(Input)receives a character, the Child(Output) prints each char out without getting stick in the while loop of the parent process?
char input() {
char input = getchar();
return input;
}
int main(void) {
int inputOutputFd[2];
pid_t childpid = 0;
system("/bin/stty raw igncr -echo");
if(pipe(inputOutputFd) < 0) {
perror("Failed to create pipe");
return 1;
}
if((childpid = fork()) == -1) {
perror("Failed to fork input child");
return 1;
}
//parent's code -INPUT
if (childpid > 0) {
close(inputOutputFd[0]);
printf("Please enter a word or phrase");
while(1) {
char inputChar = input();
write(inputOutputFd[1], &inputChar, sizeof(inputChar));
}
close(inputOutputFd[1]);
wait(NULL);
} else {
//child -OUTPUT
char outputChar;
close(inputOutputFd[1]);
while (read(inputOutputFd[0], &outputChar, sizeof(outputChar)) > 0)
{
printf("%c", outputChar);
fflush(stdin);
}
} //END OF IF-ELSE LOOP
}//END MAIN
Everything works fine, there is nothing stuck or anything, until you're expecting output in your console. The bug is in those two lines:
printf("%c", outputChar);
fflush(stdin);
stdin is standard input. You are writing to standard output.
printf("%c", outputChar);
fflush(stdout);
works for me.

writing and reading within a fork - C

The exercise is simple. The father process lets me write my name and surname in a file. The son process waits 5 seconds, then reads and displays it. I can't use the #wait() function.
#include <cstdlib>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char** argv) {
char name[20];
char surname[20];
char outp[50];
// The file pointer
FILE *file;
// Open the file in write mode
file = fopen("dati.txt", "w+");
pid_t pid;
pid=fork();
if(pid>0){
printf("Father");
printf("Insert name: ");
scanf("%s",name);
for (int i=0; i<strlen(name); i++) {
if (i==0) {
if (name[i]>='a' && name[i]<='z')
name[i]-=32;
} else {
if (name[i]>='A' && name[i]<='Z')
name[i]+=32;
}
}
printf("Insert surname: ");
scanf("%s", surname);
for (int i=0; i<strlen(name); i++){
if (i==0){
if (surname[i]>='a' && surname[i]<='z')
surname[i]-=32;
} else {
if (surname[i]>='A' && surname[i]<='Z')
surname[i]+=32;
}
}
fputs(name, file);
fputs(" ",file);
fputs(surname, file);
printf("Father exits");
fclose(file);
exit(0);
}else{
sleep(5);
// position to the start of the file
rewind(file);
fgets(outp, 50, file);
printf("Read string: %s", outp);
fclose(file);
exit(0);
}
return 0;
}
If I insert name and surname before the 5th second, the program writes them, but doesn't display them. Otherwise, if I write the name and "accidentally" wait for the 5th second, it displays the file content (which are basically random characters).
You are closing the file in the main process, then you want to reuse the same pointer in the child process. This is not possible. You need to reopen the file in the child process.
So, you need to write this after the sleep() call:
file = fopen("dati.txt", "r");
You also won't need the #rewind() function call, as you will be back at the start of the file.
UPDATE: The problem was that w+ means
write/update: Create an empty file and open it for update
So, you the file was erased. With r mode, it works just fine.
PS: By the way, you shouldn't use cstdio in a C program, use only the standard stdio.h header file. There was also a compilation error, because you are redeclaring the loop variable i.
You need to fopen file after fork() both for parent and child separately:
if (pid > 0) {
/* Parent process */
file = fopen("dati.txt", "w+");
/* write to file */
...
} else {
/* child process */
sleep(5);
file = fopen("dati.txt", "r");
/* read from file */
...
}
With this change it will work. Mind that, only the parent process is actively connected to your console - the output of child will show after 5 seconds and might appear after parent process exits (due to no call to wait). So you can see "Father exists" message and get console prompt before child will show any output.

Sending multiple strings using pipes to child process

I have a task in Linux and I can't get it work.
I have a program that receives a text file as parameter. It then creates a child process using fork() and sends to the child process, line by line the content of the text file received as parameter. The child process needs to count the lines and return to the parent process the number of lines received.
This is what I have until now, but somewhat the child process does not receive all the lines. For my test I used a text file with 9 lines. The parent sent 9 lines as strings but the child process received only 2 or 3 of them.
What am I doing wrong?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
char string[80];
char readbuffer[80];
int pid, p[2];
FILE *fp;
int i=0;
if(argc != 2)
{
printf("Syntax: %s [file_name]\n", argv[0]);
return 0;
}
fp = fopen(argv[1], "r");
if(!fp)
{
printf("Error: File '%s' does not exist.\n", argv[1]);
return 0;
}
if(pipe(p) == -1)
{
printf("Error: Creating pipe failed.\n");
exit(0);
}
// creates the child process
if((pid=fork()) == -1)
{
printf("Error: Child process could not be created.\n");
exit(0);
}
/* Main process */
if (pid)
{
// close the read
close(p[0]);
while(fgets(string,sizeof(string),fp) != NULL)
{
write(p[1], string, (strlen(string)+1));
printf("%s\n",string);
}
// close the write
close(p[1]);
wait(0);
}
// child process
else
{
// close the write
close(p[1]);
while(read(p[0],readbuffer, sizeof(readbuffer)) != 0)
{
printf("Received string: %s\n", readbuffer);
}
// close the read
close(p[0]);
}
fclose(fp);
}
A pipe is a unidirectional interprocess communication channel. You have to create 2 pipes, one to speak to the child process, the other to read data back.
Remember to close the unused side of the pipe on both processes.
You are sending the null terminator to the other process:
write(p[1], string, (strlen(string)+1));
That makes the result confusing because when you print what you've received, you only see up to the null terminator.
If you do this instead:
write(p[1], string, strlen(string));
you should get what you expect.
You're not counting the number of lines, you're counting the number of times read(2) returns.
When using pipes, read(2) will pull as much data as possible from the pipe: min(pipe_available, space_available). It doesn't care for newlines, 0 bytes etc. Simple tricks to make it work:
Use a loop to walk readbuffer and look for \n
Use fdopen + fgets (I have a feeling this is probably flawed)
look into manpage of pipe ( man 2 pipe ), the program you're trying to write is as an example there, compare it with yours :)

How to output to a file with information from a pipe in C?

I'm confused about what I'm doing wrong when I'm attempting to output to a file after I've execed a second program.
Say I have input file that has the following names:
Marty B. Beach 7 8
zachary b. Whitaker 12 23
Ivan sanchez 02 15
Jim Toolonganame 9 03
After my programs finish, it will convert the student's names to their usernames and output it to a file such as this:
mbb0708
zbw1223
is0215
jt0903
Currently as my program stands, it outputs nothing to the file and the terminal seems to be in an infinite loop despite self testing my converter program before and making sure it outputs names correctly to stdout.
I'm not sure what I'm doing wrong here? First time programming with pipes. I know have to make use of the read and write commands extract the data, but with the dup2 command is that necessary for the read command alone?
manager.c
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
int main(int argc, char** argv)
{
pid_t pid;
int nbytes;
/*Buffer to hold data from pipe*/
char buffer[BUFSIZ + 1];
/*Pipe Information*/
int commpipe[2];
if(pipe(commpipe))
{
fprintf(stderr, "Pipe failed.\n");
return EXIT_FAILURE;
}
if((pid = fork()) == -1)
{
fprintf(stderr,"Fork error. Exiting.\n");
exit(1);
}
else if(pid == 0)
{
/*This is the child process. Close our copy of the write end of the file descriptor.*/
close(commpipe[1]);
/* Connect the read end of the pipe to standard input*/
dup2(commpipe[0], STDIN_FILENO);
/*Program will convert the Student's name to their respective names*/
execl("converter","converter",NULL);
/*Exit if failure appears*/
exit(EXIT_FAILURE);
}
else
{
FILE *file;
file = fopen("usernames.txt","a+"); //append a file(add text to a file or create a file it does not exist)
/*Close or copy of the read end of the file descriptor */
//close(commpipe[1]);
nbytes = write(commpipe[1], buffer, BUFSIZ);
//Read from pipe here first?
//Output to usernames.txt the usernames of the user from the pipe.
fprintf(file, "%s", buffer);
/*Wait for the child process to finish*/
waitpid(pid, NULL, 0);
}
return 0;
}
One problem is that after manager has sent all the data to converter, the manager is not closing commpipe[1]. Because of that, converter will never get EOF on stdin so will not exit.
Most likely manager isn't getting any data back from converter due to buffering. Some implementations of stdio use full-buffer buffering (as opposed to line-buffering) when not writing to a terminal. Once you fix the previous error and get the process to close, that will flush stdout. You can also consider adding fflush(stdout) after your puts line.
Have a look at the OpenGroup site, there's an example that looks similar to yours. I suggest you get the sample working first with some hard coded. Once that is working, add the code to read and write the results.
I made some minor changes to get the example working:
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <assert.h>
int main(int argc, char** argv){
int fildes[2];
const int BSIZE = 100;
char buf[BSIZE];
ssize_t nbytes;
int status;
status = pipe(fildes);
if (status == -1 ) {
/* an error occurred */
printf("Error!\n");
exit(-1);
}
printf("Forking!\n");
switch (fork()) {
case -1: /* Handle error */
printf("Broken Handle :(\n");
break;
case 0: /* Child - reads from pipe */
printf("Child!\n");
close(fildes[1]); /* Write end is unused */
nbytes = read(fildes[0], buf, BSIZE); /* Get data from pipe */
/* At this point, a further read would see end of file ... */
assert(nbytes < BSIZE); /* Prevent buffer overflow */
buf[nbytes] = '\0'; /* buf won't be NUL terminated */
printf("Child received %s", buf);
close(fildes[0]); /* Finished with pipe */
fflush(stdout);
exit(EXIT_SUCCESS);
default: /* Parent - writes to pipe */
printf("Parent!\n");
close(fildes[0]); /* Read end is unused */
write(fildes[1], "Hello world\n", 12); /* Write data on pipe */
close(fildes[1]); /* Child will see EOF */
/* Note that the Parent should wait for a response from the
child here, because the child process will be terminated once
the parent exits */
exit(EXIT_SUCCESS);
}
return 0;
}
As I understand, your converter program reads lines from stdin and writes them to stdout. As a pipe is a uni-directional entity, you will need TWO of them to communicate with the manager - one to send data to the converter and one to receive output from it.
Maybe you should consider enhancing the converter to take (as optional arguments) the name of an input and output file.

Resources