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.
Related
I am trying to write to a file and display the output of the thing i wrote with another process. The code i come up with:
void readLine (int fd, char *str) {
int n;
do {
n = read (fd, str, 1);
} while (*str++ != '\0');
}
int main(int argc,char ** argv){
int fd=open("sharedFile",O_CREAT|O_RDWR|O_TRUNC,0600);
if(fork()==0){
char buf[1000];
while(1) {
readLine(fd,buf);
printf("%s\n",buf);
}
}else{
while(1){
sleep(1);
write(fd,"abcd",strlen("abcd")+1);
}
}
}
the output i want (each result spaced from the other with a period of one second):
abcd
abcd
abcd
....
Unfortunately this code doesn't work, it seems that the child process (the reader of the file "sharedFile") reads junk from the file because somehow it reads values even when the file is empty.
When trying to debug the code, readLine function never reads the written file correctly,it always reads 0 bytes.
Can someone help?
First of all, when a file descriptor becomes shared after forking, both the parent and child are pointing to the same open file description, which means in particular that they share the same file position. This is explained in the fork() man page.
So whenever the parent writes, the position is updated to the end of the file, and thus the child is always attempting to read at the end of the file, where there's no data. That's why read() returns 0, just as normal when you hit the end of a file.
(When this happens, you should not attempt to do anything with the data in the buffer. It's not that you're "reading junk", it's that you're not reading anything but are then pretending that whatever junk was in the buffer is what you just read. In particular your code utterly disregards the return value from read(), which is how you're supposed to tell what you actually read.)
If you want the child to have an independent file position, then the child needs to open() the file separately for itself and get a new fd pointing to a new file description.
But still, when the child has read all the data that's currently in the file, read() will again return 0; it won't wait around for the parent to write some more. The fact that some other process has a file open for writing don't affect the semantics of read() on a regular file.
So what you'll need to do instead is that when read() returns 0, you manually sleep for a while and then try again. When there's more data in the file, read() will return a positive number, and you can then process the data you read. Or, there are more elegant but more complicated approaches using system-specific APIs like Linux's inotify, which can sleep until a file's contents change. You may be familiar with tail -f, which uses some combination of these approaches on different systems.
Another dangerous bug is that if someone else writes text to the file that doesn't contain a null byte where expected, your child will read more data than the buffer can fit, thus overrunning it. This can be an exploitable security vulnerability.
Here is a version of the code that fixes these bugs and works for me:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void readLine (int fd, char *str, size_t max) {
size_t pos = 0;
while (pos < max) {
ssize_t n = read(fd, str + pos, 1);
if (n == 0) {
sleep(1);
} else if (n == 1) {
if (str[pos] == '\0') {
return;
}
pos++;
} else {
perror("read() failure");
exit(2);
}
}
fprintf(stderr, "Didn't receive null terminator in time\n");
exit(2);
}
int main(int argc, char ** argv){
int fd=open("sharedFile", O_CREAT|O_RDWR|O_TRUNC, 0600);
if (fd < 0) {
perror("parent opening sharedFile");
exit(2);
}
pid_t pid = fork();
if (pid == 0){
int newfd = open("sharedFile", O_RDONLY);
if (newfd < 0) {
perror("child opening sharedFile");
exit(2);
}
char buf[1000];
while (1) {
readLine(newfd, buf, 1000);
printf("%s\n",buf);
}
} else if (pid > 0) {
while (1){
sleep(1);
write(fd,"abcd",strlen("abcd")+1);
}
} else {
perror("fork");
exit(2);
}
return 0;
}
I need to create a program that has a child process and a parent process. The child process has to convert lines sent by the parent proccess in to upper case, and the parent proccess has to send lines to the child proccess to convert, and show this converted lines by stdin. I already have this, but at the time when i execute on my terminal, the upper case lines doesn't show.
Any sugestion
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
#include <ctype.h>
int main(void) {
int p1[2];
int p2[2];
pid_t pid;
char buffer[1024];
FILE *fp1;
FILE *fp2;
pipe(p1);
pipe(p2);
pid = fork();
if (pid < 0) {
fprintf(stderr, "Error fork\n");
exit(1);
}
if (pid == 0) {
// Close pipes entrances that aren't used
close(p1[1]);
close(p2[0]);
// Open file descriptors in used entrances
fp1 = fdopen(p1[0], "r");
fp2 = fdopen(p2[1], "w");
// Read from the corresponding file descriptor (pipe extreme)
while (fgets(buffer, 1024, fp1) != NULL) {
for (int i = 0; i < strlen(buffer); i++) {
buffer[i] = toupper(buffer[i]);
}
fputs(buffer, fp2);
}
// Once finished, close the reaming pipes entrances
fclose(fp1);
fclose(fp2);
exit(1);
}
// Close unused pipes entrances
close(p1[0]);
close(p2[1]);
// Open dile descriptors
fp1 = fdopen(p1[1], "w");
fp2 = fdopen(p2[0], "r");
while (fgets(buffer, 1024, stdin) != NULL) {
fputs(buffer, fp1); // Send buffer to write line pipe
fgets(buffer, 1024, fp2); // Get buffer readed from read line pipe
printf("%s", buffer); // Print in stdout the buffer
}
// Once finished, close the reaming pipes entrances
fclose(fp1);
fclose(fp2);
// Wait fork
wait(NULL);
return 0;
}
When using a FILE * stream and the C library stream API, it's important to keep in mind that I/O operations can be "buffered". In most cases, by default, when performing writes via fputs(...) the bytes will not actually be sent to the underlying file object (a pipe end in this case) until the buffer is flushed. In your code above, you could add calls to fflush(fpN) (where N matches the numbers in your code) after both calls to fputs(...). This should help fix your problem.
Note that alternatively there are ways of manually changing the buffering mode of a given file stream. This information can be found in man setbuf.
I am developing a simple shell program, a command line interpreter and I wanted to read input from the file line by line, so I used getline() function. At the first time, the program works correctly, however, when it reaches the end of the file, instead of terminating, it starts to read a file from the start and it runs infinitely.
Here are some codes in main function that are related to getline():
int main(int argc,char *argv[]){
int const IN_SIZE = 255;
char *input = NULL;
size_t len = IN_SIZE;
// get file address
fileAdr = argv[2];
// open file
srcFile = fopen(fileAdr, "r");
if (srcFile == NULL) {
printf("No such file!\n");
exit(-1);
}
while (getline( &input, &len, srcFile) != -1) {
strtok(input, "\n");
printf("%s\n", input);
// some code that parses input, firstArgs == input
execSimpleCmd(firstArgs);
}
fclose(srcFile);
}
I am using fork() in my program and most probably it causes this problem.
void execSimpleCmd(char **cmdAndArgs) {
pid_t pid = fork();
if (pid < 0) {
// error
fprintf(stderr, "Fork Failed");
exit(-1);
} else if (pid == 0) {
// child process
if (execvp(cmdAndArgs[0], cmdAndArgs) < 0) {
printf("There is no such command!\n");
}
exit(0);
} else {
// parent process
wait(NULL);
return;
}
}
In addition, sometimes the program reads and prints a combinations of multiple lines. For example, if an input file as below:
ping
ww
ls
ls -l
pwd
it prints something like pwdg, pwdww, etc. How to fix it?
It appears that closing a FILE in some cases seeks the underlying file descriptor back to the position where the application actually read to, effectively undoing the effect of the read buffering. This matters, since the OS level file descriptors of the parent and the child point to the same file description, and the same file offset in particular.
The POSIX description of fclose() has this phrase:
[CX] [Option Start] If the file is not already at EOF, and the file is one capable of seeking, the file offset of the underlying open file description shall be set to the file position of the stream if the stream is the active handle to the underlying file description.
(Where CX means an extension to the ISO C standard, and exit() of course runs fclose() on all streams.)
I can reproduce the odd behavior with this program (on Debian 9.8):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char *argv[]){
FILE *f;
if ((f = fopen("testfile", "r")) == NULL) {
perror("fopen");
exit(1);
}
int right = 0;
if (argc > 1)
right = 1;
char *line = NULL;
size_t len = 0;
// first line
getline(&line, &len, f);
printf("%s", line);
pid_t p = fork();
if (p == -1) {
perror("fork");
} else if (p == 0) {
if (right)
_exit(0); // exit the child
else
exit(0); // wrong way to exit
} else {
wait(NULL); // parent
}
// rest of the lines
while (getline(&line, &len, f) > 0) {
printf("%s", line);
}
fclose(f);
}
Then:
$ printf 'a\nb\nc\n' > testfile
$ gcc -Wall -o getline getline.c
$ ./get
getline getline2
$ ./getline
a
b
c
b
c
Running it with strace -f ./getline clearly shows the child seeking the file descriptor back:
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f63794e0710) = 25117
strace: Process 25117 attached
[pid 25116] wait4(-1, <unfinished ...>
[pid 25117] lseek(3, -4, SEEK_CUR) = 2
[pid 25117] exit_group(1) = ?
(I didn't see the seek back with a code that didn't involve forking, but I don't know why.)
So, what happens is that the C library on the main program reads a block of data from the file, and the application prints the first line. After the fork, the child exits, and seeks the fd back to where the application level file pointer is. Then the parent continues, processes the rest of the read buffer, and when it's finished, it continues reading from the file. Because the file descriptor was seeked back, the lines starting from the second are again available.
In your case, the repeated fork() on every iteration seems to result in an infinite loop.
Using _exit() instead of exit() in the child fixes the problem in this case, since _exit() only exits the process, it doesn't do any housekeeping with the stdio buffers.
With _exit(), any output buffers are also not flushed, so you'll need to call fflush() manually on stdout and any other files you're writing to.
However, if you did this the other way around, with the child reading and buffering more than it processes, then it would be useful for the child to seek back the fd so that the parent could continue from where the child actually left.
Another solution would be not to mix stdio with fork().
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).
I need to write program, which work on Linux OS:
when the main program starts, a child program separates from main program and child can be executed when it gets an interruption from parent program;
parent program waits for a text line, enetered from keyboard (text line should end by pressing );
after the text line is entered, parent program sends an interruption to child program, which reads the text line trough "Pipe" channel and creates a text file with it.
if an empty line is entered, then both parts of the program end their work.
The problem is, I know that it is bad to use gets(), so how can I use fgets() instead in my case?
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
FILE *f;
int main(void)
{
int pfds[2];
char buf[1000];
pipe(pfds);
f = fopen("input.txt", "w");
fclose(f);
do
{
if (!fork())
{
printf("PARENT: enter input text from keyboard\n");
gets(buf);
printf("PARENT: writing to the pipe\n");
write(pfds[1], buf, 1000);
printf("PARENT: exiting\n");
exit(0);
}
else
{
f = fopen("input.txt", "a+");
printf("CHILD: waiting from PARENT\n");
read(pfds[0], buf, 1000);
printf("CHILD: read \"%s\"\n", buf);
fprintf(f,"%s\n", buf);
fclose(f);
printf("CHILD: input.txt file created\n", buf);
wait(NULL);
}
}
while (buf[0] != '\0');
printf("PROGRAM: done\n");
return 0;
}
You have opened file in "w" mode.
f = fopen("program.txt", "w");
w mode creates an empty file for writing. If a file with the same name already exists, its content is erased and the file is considered as a new empty file.
Open the file in "w+" or "a+" mode.