I am doing a simple project in C involving fork() and socketpairs. The program forks a process then creates a socketpair for the processes to talk to one another. The parent process reads lines from stdin one at a time until EOF, and then sends the lines one by one the the child. The child converts the lines to uppercase and sends them back to the parent, who sends them to stdout. Here is my code
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
int main(int argc, char *argv[]) {
/* code */
int returnCode, status, socket[2], j;
char* buffer;
size_t bufferSize = 100;
size_t line;
buffer = (char *)malloc(bufferSize * sizeof(char));
socketpair(AF_UNIX, SOCK_STREAM, 0, socket);
returnCode = fork();
if(returnCode == -1){
printf("Fork failed\n");
}
if(returnCode > 0){
while(getline(&buffer, &bufferSize, stdin) != -1){
write(socket[1], buffer, bufferSize);
read(socket[1], buffer, bufferSize);
printf("%s", buffer);
}
}
else{
read (socket[0], buffer, bufferSize);
for(j = 0; j[buffer];j++){
buffer[j] = toupper(buffer[j]);
}
write(socket[0], buffer, bufferSize);
}
return 0;
}
My program prints the first line from stdin capitalized, but then it hangs. I have to ctrl-c to exit. How do i get the sockets to loop thru until EOF printing the modified lines?
My program prints the first line from stdin capitalized, but then it
hangs. I have to ctrl-c to exit. How do i get the sockets to loop thru
until EOF printing the modified lines?
Your child process's routine (i.e. the one that does the toupper() calls) only calls read() one time, and then calls write() one time, and then exits. That is why only one line is processed. To get it to do multiple lines, you'd need to put the code into a loop:
[...]
else{
while(read(socket[0], buffer, bufferSize) > 0)
{
for(j = 0; j[buffer];j++){
buffer[j] = toupper(buffer[j]);
}
write(socket[0], buffer, bufferSize);
}
}
return 0;
}
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'm experimenting on how to communicate between a thread and the main function in C
There is a behavior that I don't understand in the following code :
#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
void* output(void* pipe1);
int main(int argc, const char *argv[])
{
pthread_t tid0;
int pipe1[2];
char buffer[200];
// Creating the pipe
pipe(pipe1);
// Creating the thread and passing the pipe as argument
pthread_create(&tid0, NULL, output, &pipe1);
// Input from user
scanf("%s", buffer);
// Writing to the pipe
write(pipe1[1], buffer, strlen(buffer));
return 0;
}
void* output(void* pipe1) {
char buffer[200];
// Reading the pipe and print the buffer
read(((int*)pipe1)[0], buffer, strlen(buffer));
printf("thread say: %s\n", buffer);
pthread_exit(NULL);
}
Why the read function doesn't block on the pipe's file descriptor ?
Maybe I should close the end of the pipe but since they share the same memory space, the error "bad file descriptor" is returned when I will call read or write.
Maybe you can guide me to other methods if pipe is really a bad solution (with an example it will be amazing ! :) )
Many thanks !
EDIT: SOLUTION
Many thank for your answer here is the code that have the expected behavior
#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
void* output(void* pipe1);
int main(int argc, const char *argv[])
{
pthread_t tid0;
int pipe1[2];
char buffer[200];
// Creating the pipe
pipe(pipe1);
// Creating the thread and passing the pipe as argument
pthread_create(&tid0, NULL, output, &pipe1);
// Input from user
scanf("%s", buffer);
// Writing to the pipe
if (write(pipe1[1], buffer, strlen(buffer)) < 0) {
perror("write");
exit(1);
}
// join so the main "wait" for the thread
pthread_join(tid0, NULL);
return 0;
}
void* output(void* pipe1) {
char buffer[200];
int nread;
// Reading the pipe and print the buffer
nread = read(((int*)pipe1)[0], buffer, sizeof buffer - 1);
if (nread < 0) {
fprintf(stderr, "ERROR\n");
perror("read");
exit(1);
}
buffer[nread] = '\0';
fprintf(stderr, "thread say: %s\n", buffer);
pthread_exit(NULL);
}
char buffer[200];
read(((int*)pipe1)[0], buffer, strlen(buffer));
You are calling strlen on an uninitialized buffer. This is allowed to crash your program. Instead, you got lucky, and all it did was tell read to read zero bytes, so read returned without doing anything.
What you actually want is
ssize_t nread = read(((int *)pipe1)[0], buffer, sizeof buffer - 1);
if (nread < 0) {
perror("read");
return 0;
}
buffer[nread] = '\0';
What read wants to be told is how much space you are giving it to read into, not the length of any string that may or may not already be in that space. That's sizeof buffer, minus one so we always have space to add a string terminator.
It's correct to use strlen when writing, because you only want to write the actual string, not any junk that might be beyond the end of the string; but then write doesn't write the string terminator to the pipe, so read doesn't read one, so you have to add it by hand. And, of course, always check for errors.
Also, keep in mind that the threads run simultaneously. Even after fixing this bug, the write may already have happened by the time the reader-thread calls read, and if it hasn't, it probably will happen very soon. If you want to observe the reader-thread actually blocking in read you need to delay before calling write.
I am trying to simulate conversation between a caller and a receiver using pipes. I am forking a process and making the parent process the receiver and the child process the caller.
Here is the code:
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <sys/types.h>
#include <unistd.h>
#define BUF_LEN 25
#define READ_END 0
#define WRITE_END 1
int main()
{
int fd[2];
if (pipe(fd) == -1) {
fprintf(stderr, "Pipe failed");
return 1;
}
pid_t pid = fork();
if (pid < 0) {
fprintf(stderr, "Fork failed");
return 1;
}
// the parent process is the receiver
if (pid > 0) {
close(fd[WRITE_END]);
char buffer[BUF_LEN + 1] = "";
do {
read(fd[READ_END], buffer, sizeof buffer);
if (strcmp(buffer, "")) {
printf("Received %s\n", buffer);
}
strcpy(buffer, "");
} while (strcmp(buffer, "Bye!"));
close(fd[READ_END]);
} else {
close(fd[READ_END]);
// const char *msg = "Hello";
char buffer[BUF_LEN + 1] = "";
bool end_call = false;
do {
printf("Caller: ");
fgets(buffer, sizeof buffer, stdin);
if (strcmp(buffer, "Bye!")) {
end_call = true;
}
// printf("Sent %s\n", buffer);
write(fd[WRITE_END], buffer, strlen(buffer) + 1);
} while (!end_call);
close(fd[WRITE_END]);
}
return 0;
}
But when I run this, I get this unexpected output:
Caller: Hi
Received Hi
HI
Hello
Bye!
^C
The receiver stops working, it is not receiving the inputs I give. Also there are extra newlines appearing in the output. Why is this occuring?
Edit:
As pointed by out Dmitri, I have changed the strcmp test in the caller and the printf statement in the receiver.
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <sys/types.h>
#include <unistd.h>
#define BUF_LEN 25
#define READ_END 0
#define WRITE_END 1
int main()
{
int fd[2];
if (pipe(fd) == -1) {
fprintf(stderr, "Pipe failed"); return 1; }
pid_t pid = fork();
if (pid < 0) {
fprintf(stderr, "Fork failed");
return 1;
}
// the parent process is the receiver
if (pid > 0) {
close(fd[WRITE_END]);
char buffer[BUF_LEN + 1] = "";
do {
read(fd[READ_END], buffer, sizeof buffer);
if (strcmp(buffer, "")) {
printf("Received %s", buffer);
}
strcpy(buffer, "");
} while (strcmp(buffer, "Bye!"));
close(fd[READ_END]);
} else {
close(fd[READ_END]);
// const char *msg = "Hello";
char buffer[BUF_LEN + 1] = "";
bool end_call = false;
do {
printf("Caller: ");
fgets(buffer, sizeof buffer, stdin);
if (!strcmp(buffer, "Bye!")) {
end_call = true;
}
// printf("Sent %s\n", buffer);
write(fd[WRITE_END], buffer, strlen(buffer) + 1);
} while (!end_call);
close(fd[WRITE_END]);
}
return 0;
}
But it is still not exiting after receiving "Bye!".
Caller: hi
Received hi
Caller: Hello
Received Hello
Caller: Bye!
Received Bye!
Caller: Bye!
Received Bye!
Caller: ^C
The strcmp() returns 0 on success. But there are also several other problems with your code:
The string will never be equal to "Bye!", there is going to be a new line attached as well as the null character indicating the end of string (total of 6 chars).
The pipes use streams not "packets" you never know how many bytes you will receive from one call to read(). It might be incomplete string or if the data is sent very fast you might get 2 strings glued to each other. You need to implement your own "protocol" to parse the data out of the stream.
You are not checking if the pipe was closed on the other side (read would return 0)
You get extra new line in the output because it is attached to the string read by fgets()
Output may be messed up because you have no control on when processes flush to stdout (sort of a racing condition but it will not crash).
Because of the newline captured by fgets you need to test the exit condition for Bye!\n which includes that newline. strcmp(buffer, "Bye!\n")
This is in addition to fixing the negation issue pointed out by #blackpen.
Your program has several issues.
First, when fgets() reads a line, the newline at the end is included (if there was sufficient space in the buffer). The extra newlines you're seeing are because the string you send contains one, then you add another when you print it in the receiver. Also, you're looking for the string, "Bye!" to decide when to quit... but the string you actually get is "Bye!\n". You need to either strip the newline off the end of the string you read from stdin in the sender, or account for the newline already in the string when you print it and in your comparisons.
Second, in the sender, you have your logic inverted when checking when to quit: You're setting end_call = true; when buffer doesn't contain "Bye!", instead of when it does. This causes the sender to exit after sending the first string rather than looping (also, the comparison needs to be fixed to account for the newline as mentioned above). It's not the receiver that stops early, it's the sender... the receiver runs forever due to the next issue.
In the receiver, you're clearing the buffer at the end of your loop, right before you check for "Bye!" in the loop test. That prevents the comparison in the loop test from ever finding a match, so the loop is infinite. Clear the buffer at the start of the loop before fgets() instead. (And once again, fix the comparison to account for the newline at the end).
Change the test in client to the following. That will let the child/parent communicate.
if (!strcmp(buffer, "Bye!")) {
The client has newline in the buffer it reads ("Bye!" following by a newline character). That fails to match exit test. You could try to remove it.
fgets(buffer, sizeof buffer, stdin);
buffer[strlen(buffer)-1]='\0';
In the server side, don't initialze the buffer (just before while-check). Because that would reinitialize the very string that you are about to check.
strcpy(buffer, "");
If you want, you could put that just before you run the read function.
You may also get into problem buffering problem (in matching for end condition) and race conditions (while closing fds) as pointed out by other answers. But, I guess it is good enough example for just "learning". Good luck.
I would like to read in a file descriptor line by line until there is nothing else to read. I expected read(2) to return zero when the end of the file descriptor is reach, but on the contrary, it seems to wait for the file descriptor to be closed. I would like to avoid closing my file descriptor as I have more to write in it.
Here is a comprehensive example I have done :
#include <stdio.h>
#include <stdlib.h>
size_t read_line(int fd, char* buffer);
int main()
{
int fd[2];
if (pipe(fd) != 0) {
fprintf(stderr,"Pipe error.\n");
exit(1);
}
if (fork() == 0) {
close(fd[0]);
int i;
for (i = 0; i < 10; ++i)
dprintf(fd[1], "FOO\n");
pause();
exit(0);
}
close(fd[1]);
char buffer[20];
while (read_line(fd[0], buffer) > 0)
printf("%s\n", buffer);
printf("BAR !!\n");
exit(0);
}
size_t read_line(int fd, char* buffer)
{
char char_buf;
size_t buffer_size = 0;
while (read(fd, &char_buf, 1) > 0 && char_buf != '\n')
buffer[buffer_size++] = char_buf;
buffer[buffer_size] = '\0';
return buffer_size;
}
My read_line function should be returning zero at the end. It works properly if I replace "pause();" with "close(fd[1]);", but obviously, it is not my goal as I would avoid to loose the pipe.
Thanks !
There is no way to do this by checking the return value of read. The reason being that while the file descriptor is empty, read will just wait. Your problem is similar to that of waiting until you have no input from a scanf, if that makes it easier for you to understand.
A solution is to have a specific value which will stop the loop. For example if you were reading non-negative integers, this value could be -1.
Another solution comes from the fact that if a pipe is empty and no process has the pipe open for write, a read returns 0. So when you are done writing, close the file destriptor p[1].(p[1] must also be closed from the sender process).
I need help with this sample application. When I run it, it gets stuck after the child process prints "Child sending!".
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#define INPUT 0
#define OUTPUT 1
int main()
{
int fd1[2];
int fd2[2];
int pid;
if (pipe(fd1) < 0)
exit(1);
if (pipe(fd2) < 0)
exit(1);
if ((pid = fork()) < 0)
{
perror("fork");
exit(1);
}
else if (pid == 0)
{
close(fd1[INPUT]);
close(fd2[OUTPUT]);
char *str = "Hello World!";
printf("Child sending!\n");
write(fd1[OUTPUT], str, strlen(str));
char *bufferc = (char *)malloc(1000);
char *readbufferc = (char *)malloc(80);
int rdc;
int gotdata = 0;
while (gotdata == 0)
while ((rdc = read(fd2[INPUT], readbufferc, sizeof(readbufferc))) > 0)
{
strncat(bufferc,readbufferc,rdc);
gotdata = 1;
}
printf("Child received: %s",bufferc);
free(readbufferc);
free(bufferc);
exit(0);
}
else
{
close(fd1[OUTPUT]);
close(fd2[INPUT]);
int rd;
char *buffer = (char *)malloc(1000);
char *readbuffer = (char *)malloc(80);
int gd = 0;
while (gd == 0)
while ((rd = read(fd1[INPUT],readbuffer, sizeof(readbuffer))) > 0)
{
strncat(buffer, readbuffer,rd);
gd = 1;
}
printf("Parent received: %s\n",buffer);
free(readbuffer);
printf("Parent sending!");
write(fd2[OUTPUT], buffer, strlen(buffer));
free(buffer);
}
return 0;
}
On a side note, is there a way to debug when I use fork because gdb automatically goes to the parent process
After the child writes to the parent, it must close the write end of the pipe so the parent knows it has reached EOF.
There are many bugs in your code. Why are you using fd2 without initializing it? Remove it.
Now its stuck at "Child sending" because pipe read is a blocking call and you are putting it in a while loop which will never return. Please refer to man page of pipe.
If you want to break that while loop, close all write ends of that pipe.
Also to debug child process, use gdb command follow-fork-mode as child before call to fork() while debugging.
Several things wrong:
fd2 is just never initialized.
The parent will never exit this:
while ((rd = read(fd1[INPUT],readbuffer, sizeof(readbuffer))) > 0)
{
strncat(buffer, readbuffer,rd);
gd = 1;
}
If there is no data to read, read will block and just not return. The only thing that would make it exit is if the connection was closed and the child doesn't close it.
You are calling read() in the expectation that if there is nothing to read, it will return with zero bytes read. However, what you are seeing is because read() is waiting for some data before returning. To address this, you need to do one of two things:
set your socket to do non-blocking reads (not recommended)
use select() or poll() to see whether there is some data to read before you read it
Also, several other points:
don't cast the returns from malloc()
check that malloc() does not return NULL
replace the whole gotdata thing with a break instruction