Followup to previous question about client/server echo program - c

Earlier I got some great advice about Client/server sockets in c. I have modified the code slightly and it works fine when I have the server running in one terminal and the clients running in other windows. Now I would like to make a TUI for the program, but I am running into a problem. Since the server is constantly listening for a client it is in an infinite loop; once the server is established nothing else can go on in the terminal. What I thought I should do is use fork() so that the server can be running in the background, leaving the TUI free to create clients. Here is my code so far (again, I cannot take much credit for the client/server code):
Server code
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
struct sockaddr myname;
char buf[80];
ssize_t readLine(int sockd, char *vptr, size_t maxlen) {
ssize_t n, rc;
char c, *buffer;
buffer = vptr;
for(n = 1; n < maxlen; n++) {
if((rc = read(sockd, &c, 1)) == 1) {
*buffer++ = c;
if(c== '\n')
break;
}
else if(rc == 0){
if(n == 1)
return 0;
else
break;
}
else {
if(errno == EINTR)
continue;
return -1;
}
}
*buffer = 0;
return n;
}
ssize_t modifyBuf(int sockd, char *vptr, size_t n) {
int i, j;
char temp;
for(i = 0, j = n-1; i < j; i++, j--) {
temp = vptr[i];
vptr[i] = vptr[j];
vptr[j] = temp;
}
for(i = 0; i < n; i++)
vptr[i] = toupper(vptr[i]);
return writeLine(sockd,vptr,n);
}
ssize_t writeLine(int sockd, const void *vptr, size_t n) {
size_t nleft;
size_t nwritten;
const char *buffer;
buffer = vptr;
nleft = n;
while(nleft > 0) {
if((nwritten = write(sockd,buffer,nleft)) <= 0){
if(errno == EINTR)
nwritten = 0;
else
return -1;
}
nleft -= nwritten;
buffer += nwritten;
}
return n;
}
makeServer() {
int sock, new_sd, adrlen, cnt;
sock = socket(AF_UNIX, SOCK_STREAM, 0);
if(sock < 0) {
printf("server socket failure %d\n", errno);
perror("server: ");
exit(1);
}
myname.sa_family = AF_UNIX;
strcpy(myname.sa_data, "/tmp/billb");
adrlen = strlen(myname.sa_data) + sizeof(myname.sa_family);
unlink("/tmp/billb"); /*defensive programming */
if(bind(sock, &myname, adrlen) < 0) {
printf("server bind failure%d\n", errno);
perror("server: ");
exit(1);
}
if(listen(sock, 5) < 0) {
printf("server listen failure %d\n", errno);
perror("server: ");
exit(1);
}
while(1) {
if((new_sd = accept(sock, &myname, &adrlen)) < 0) {
printf("server accept failure %d\n", errno);
perror("server: ");
exit(1);
}
printf("Socket address in server %d\n", getpid());
if(fork() == 0) {
close(sock);
readLine(new_sd,buf,999);
modifyBuf(new_sd,buf,strlen(buf));
exit(0);
}
close(new_sd);
}
}
Client code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
char buf[80];
struct sockaddr myname;
void replyBack(FILE *fp, int sockfd) {
char sendline[1000], recvline[1000];
printf("Enter your echo: \n");
while(fgets(sendline,1000,stdin) != NULL) {
write(sockfd,sendline,sizeof(sendline));
if(read(sockfd,recvline,1000) == 0) {
printf("str_cli: server terminated prematurely");
exit(-1);
}
fputs(recvline, stdout);
}
}
makeClient() {
int sock, adrlen, cnt;
sock = socket(AF_UNIX, SOCK_STREAM, 0);
if(sock < 0) {
printf("client socket failure%d\n", errno);
printf("client: ");
exit(1);
}
myname.sa_family = AF_UNIX;
strcpy(myname.sa_data, "/tmp/billb");
adrlen = strlen(myname.sa_data) + sizeof(myname.sa_family);
if((connect(sock, &myname, adrlen)) < 0) {
printf("client connect failure %d\n", errno);
perror("client: ");
exit(1);
}
replyBack(stdin,sock);
exit(0);
}
TUI
#include <stdio.h>
#include </server.c>
#include </client.c>
void displayWelcomeMessage(){
....
}
void showChoices(){
printf("\nTo make a new client, enter 1.\n");
printf("To close server, enter 2.\n");
printf(">>> ");
int choice;
scanf("%d",&choice);
if(choice == 1){
makeClient();
showChoices();
} else if(choice == 2){
printf("Goodbye.");
exit(0);
} else
printf("Invalid choice.\n");
showChoices();
}
void main() {
displayWelcomeMessage();
printf("Server is now listening for clients.\n");
if(fork() == 0)
makeServer();
if(fork() > 0)
showChoices();
}
The TUI should create the server when it is executed and then show a short menu. The user can chose to create a new client or exit the program. If the user creates a client then the user should be prompted for a message to send to the server; once the message is sent back the menu should be shown again. If the user elects to exit the program then the program will terminate and the socket should close. If the user makes an invalid choice, then the menu should display again.
What is happening is the server is created and I can see the menu, but if I select to create a new client the program terminates or terminates after I input any text + enter (so the message is not getting sent to the server). Like I said, the client/server works without the TUI so I believe the problem is in my usage of fork(), but I'm not sure what other way there is to solve this problem.
Phew, that was long. Thank you for your time!

You seem to be calling fork() twice - surely you only want to call it once? I think your code should be:
if(fork() == 0)
makeServer();
else
showChoices();
Also, you may want to investigate the use of wait(), which will allow you to wait for a child process to complete.

Related

C: Reading from OpenSSL Socket

I'm using the OpenSSL library to secure network messages, but for some reason it doesn't always seems to work. Actually most of the time it doesn't work. When I run the compiled code and connect to the socket, most of the time it just runs the code of the main process in the child as well, but sometimes it runs the child instructions. Obiously, this isn't the way it should work, the child should exit instead, after it handled the client (handle_client(newfd)) all the time. One interesting part is, if I remove the handle_client(newfd) line from the child instructions and put something small there, like printf("test"), then the child works every time as it should, it prints test and exits right after that. Is this some kind of limitation in fork(), or I just shouldn't run this much code in a child? Or something else? Any help would be really appriciated!
main.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <netdb.h>
#include <sys/wait.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <fcntl.h>
#include "json.h"
#include "create_socket.h"
#include "get_addr.h"
#include "handle_income.h"
#include "handle_client.h"
int main(void) {
int newfd;
struct sockaddr_storage their_addr;
char s[INET6_ADDRSTRLEN];
pid_t pid;
unsigned int cpc = 0;
int listenfd = create_socket("8069");
if (listenfd < 0)
exit(1);
while(1) {
socklen_t sin_size = sizeof their_addr;
if ((newfd = accept(listenfd, (struct sockaddr *)&their_addr, &sin_size)) == -1)
continue;
inet_ntop(their_addr.ss_family,
get_addr((struct sockaddr *)&their_addr), s, sizeof s);
printf("conn %s siz %d\n", s, (int) sin_size); //REMOVE
if ((pid = fork()) < 0) {
exit(1);
} else if (pid == 0) {
close(listenfd);
handle_client(newfd);
exit(0);
}
printf("child %d\n", (int) pid); //REMOVE
cpc++;
while(cpc) {
pid = waitpid((pid_t) -1, NULL, WNOHANG);
if (pid < 0)
exit(1);
else if (pid == 0)
break;
else
cpc--;
}
}
EVP_cleanup();
exit(0);
}
handle_client.h:
#define READ_SIZE 32
void handle_client(int newfd) {
char *buffer = NULL;
char *tmp_buffer = malloc(READ_SIZE);
unsigned long buffer_size = 0;
unsigned long received = 0;
int status = 0;
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
SSL_CTX *sslctx = SSL_CTX_new(SSLv23_server_method());
if (sslctx) {
SSL_CTX_set_ecdh_auto(sslctx, 1);
if ((SSL_CTX_use_certificate_file(sslctx, "/ssl-cert.pem", SSL_FILETYPE_PEM)) > 0) {
if ((SSL_CTX_use_PrivateKey_file(sslctx, "/ssl-key.pem", SSL_FILETYPE_PEM)) > 0) {
SSL *ssl = SSL_new(sslctx);
SSL_set_fd(ssl, newfd);
if (SSL_accept(ssl) > 0) {
fcntl(newfd, F_SETFL, fcntl(newfd, F_GETFL, 0) | O_NONBLOCK);
do {
if (received >= buffer_size) {
char *tmp;
buffer_size += READ_SIZE;
if ((tmp = realloc(buffer, buffer_size)) == NULL) {
break;
} else {
buffer = tmp;
}
}
status = SSL_read(ssl, tmp_buffer, READ_SIZE);
if (status > 0) {
received += status;
strncat(buffer, tmp_buffer, status);
} else {
ERR_print_errors_fp(stderr);
}
} while (status > 0);
free(tmp_buffer);
buffer[received] = 0;
if (received < buffer_size) {
buffer = realloc(buffer, received);
}
printf("%s\n", buffer); //REMOVE
char *response = handle_income(buffer);
SSL_write(ssl, response, strlen(response));
printf("%s\n", response); //REMOVE
}
SSL_free(ssl);
}
}
}
SSL_CTX_free(sslctx);
close(newfd);
}
There might be other problems too, but you should fix the following buffer overflow error, and check if it fix visible problem too:
SSL_read may return less than READ_SIZE. So the next piece of code is broken.
Problem occurs for example, when the 1st SSL_read() returns for example 16, and the next call return 32 (=READ_SIZE). Allocated buffer size during 2nd strncat call is 32, so buffer overflow may occurs during strncat().
if (received >= buffer_size) {
char *tmp;
buffer_size += READ_SIZE;
if ((tmp = realloc(buffer, buffer_size)) == NULL) {
break;
} else {
buffer = tmp;
}
}
status = SSL_read(ssl, tmp_buffer, READ_SIZE);
if (status > 0) {
received += status;
strncat(buffer, tmp_buffer, status);
}
...
There might be other problem with strncat call. It needs one extra byte for null terminator of string. From man page:
the size of dest must be at least strlen(dest)+n+1

Reading and Writing to a Named Pipe

I'm working with two programs. The customer.c program writes an int to a named pipe and the bank.c reads the pipe and prints the int. The customer will choose one of two named pipes, "atm1" and "atm2".
Ultimately I would like to run two customer.c programs, one for each pipe at the same time, but I have some issues with the writing and reading to the named pipe.
If I just run bank.c and one customer.c I don't get any output.
If I run bank.c and two customer.c the output doesn't always print or is out of order when it does.
I tried to use fsync() to flush but that didn't work either.
customer.c
int main(int argc, char *argv[]){
int fd, num =0;
if((fd = open(argv[1], O_WRONLY)) == -1){
...
}
while(1){
printf("Enter a integer:\n");
scanf("%d", &num);
if(num < 0){
break;
}
if(write(fd, &num, sizeof(num)) <= 0){...}
fsync(fd);
}
close(fd);
return 0;
}
bank.c
int main(){
int fd, sd, num=0, sret, fret, maxfd;
//fd_set readfds;
//struct timeval timeout;
if(mkfifo("atm1", 0666) == -1){...}
if(mkfifo("atm2", 0666) == -1){...}
if((fd = open("atm1", O_RDONLY)) == -1){...}
if((sd = open("atm2", O_RDONLY)) == -1){...}
while(1){
if((sret = read(sd, &num, sizeof(num))) > 0){
printf("%d\n", num);
}
if((fret = read(fd, &num, sizeof(num))) > 0){
printf("%d\n", num);
}
if(sret <= 0 && fret <= 0){
break;
}
}
close(fd);
close(sd);
return 0;
}
Any pointer?
you need to make multi-threads server for multiple same time recv. and require to check pipe name is already exist.
blank.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <pthread.h>
void *client(void *data)
{
char *pname = (char*) data ;
int sd =0 ;
int num ;
int sret=0 ;
if((sd = open(pname, O_RDONLY)) == -1) {
printf("open failed:%s\n", pname) ;
return NULL ;
}
printf("client[%d] start\n", sd) ;
while(1){
num=0 ;
if((sret = read(sd, &num, sizeof(num))) > 0){
printf("[%d] recv:%d\n", sd, num);
}
if(sret <= 0) {
break;
}
}
close(sd);
printf("client[%d] end\n", sd) ;
return NULL;
}
int main(){
int status ;
pthread_t t1, t2 ;
if(mkfifo("atm1", 0666) == -1) {
printf("mkfifio failed : %d\n", errno) ;
if (errno==EEXIST ) {
printf("already exist. ignore\n") ;
}
else
return -1;
}
if(mkfifo("atm2", 0666) == -1) {
printf("mkfifio failed2 : %d\n", errno) ;
if (errno==EEXIST ) {
printf("already exist. ignore\n") ;
}
else
return -1;
}
pthread_create(&t1, NULL, client, (void*)"atm1") ;
pthread_create(&t2, NULL, client, (void*)"atm2") ;
pthread_join(t1, (void**)&status) ;
pthread_join(t2, (void**)&status) ;
return 0;
}
you can access the atm1 and atm2 at the same time.
./customer atm1
./customer atm2

How to pass values between pipes

So I create 10 processes and in each process add a bunch of random numbers together so that each process holds a partial sum. How do I pass the partial sum back to the parent process? I am having a little difficulty and any help would be much appreciated. Thank-you
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <sys/wait.h>
int MAX_PROC = 10;
pid_t pids[10];
void childProcess(void);
void parentProcess(void);
int startChildren(void);
main() {
srand(time(NULL));
clock_t begin,end;
double timeSpent;
begin = clock();
startChildren();
end = clock();
timeSpent = (double)(end-begin);
//printf("\n Time taken: %f \n", timeSpent);
return 0;
}
int startChildren(void) {
int i = 0;
for (i = 0; i < MAX_PROC; ++i) {
pids[i] = fork();
if (pids[i] == 0) {
childProcess();
return(0);
}
}
int status;
pid_t pid;
while (MAX_PROC > 0) {
pid = wait(&status);
MAX_PROC--;
}
parentProcess();
return(0);
}
void childProcess(void)
{
int i = 0;
int partial = 0;
for(i=0; i<10000; i++) {
int r = rand() % 101;
partial += r;
}
}
void parentProcess(void)
{
printf("*** Parent is done ***\n");
}
Here's an example of how to use socketpair() to pass data between parent/child processes (in UNIX-like OSes):
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#define DATA1 "In Xanadu, did Kublai Khan . . ."
#define DATA2 "A stately pleasure dome decree . . ."
/*
* This program creates a pair of connected sockets, then forks and
* communicates over them. This is very similar to communication with pipes;
* however, socketpairs are two-way communications objects. Therefore,
* this program can send messages in both directions.
*/
main()
{
int sockets[2], child;
char buf[1024];
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) < 0) {
perror("opening stream socket pair");
exit(1);
}
if ((child = fork()) == -1)
perror("fork");
else if (child) { /* This is the parent. */
close(sockets[0]);
if (read(sockets[1], buf, 1024, 0) < 0)
perror("reading stream message");
printf("-->%s\n", buf);
if (write(sockets[1], DATA2, sizeof(DATA2)) < 0)
perror("writing stream message");
close(sockets[1]);
} else { /* This is the child. */
close(sockets[1]);
if (write(sockets[0], DATA1, sizeof(DATA1)) < 0)
perror("writing stream message");
if (read(sockets[0], buf, 1024, 0) < 0)
perror("reading stream message");
printf("-->%s\n", buf);
close(sockets[0]);
}
}

How to send arrays between threads using pipes?

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.

Linux inter-processes communication

I have to implement a testing program(quiz), which besides displaying the question and reading the answer, it has to display the time left at each one minute past. After finishing the examination time, by finishing the questions or by running out of time,the program has to get back from the beginning, when before the start, we enter the name of the candidate. This implementation has to be done using processes. Below is the code that i have written so far. The problem is that i am not sure that i am making a good communication between the process and the subprocesses, especially because i am not using a pipe. Some opinions?
#include<stdio.h>
#include<sys/types.h>
#include<signal.h>
#include<unistd.h>
#include<sys/wait.h>
#define T 180
void firstChildAction(){
static const char filename[] = "/home/osystems01/laura/text";
char question[100];
char answer[100];
FILE *file = fopen(filename,"r");
if(file != NULL){
while(fgets(question,sizeof question,file) != NULL){
fputs(question, stdout);
scanf("%s",&answer);
}
fclose(file);
}
else{
perror(filename);
}
}
void secondChildAction(){
int i;
for(i = T; i >= 0; i-=60){
if( i/60 != 0){
printf("You have %d %s left.\n", i/60,(i/60 > 1)?"minutes":"minute");
sleep(60);
}
else{
printf("The time is over\n");
break;
}
}
}
int main() {
pid_t pidA;
pid_t pidB;
pid_t wPid;
char name[20];
while(1){
printf("Enter the candidate name or Quit to exit: \n");
scanf("%s",&name);
if(strcmp(name,"Quit") == 0 || strcmp(name,"quit") == 0){
printf("The program is terminating.....\n");
break;
}
else{
pidA = fork();
if(pidA == 0){
firstChildAction();
exit(0);
}
else{
pidB = fork();
if(pidB == 0){
secondChildAction();
exit(0);
}
}
int status;
while(wPid = wait(&status)) > 0 ){
if(WIFEXITED(status)){
int result = WEXITSTATUS(status);
printf("Exit status of %d is %d\n", wPid, result);
if(wPid == pidA){
kill(pidB,SIGTERM);
kill(pidA,SIGTERM);
}
else if(wPid == pidB){
kill(pidA,SIGTERM);
kill(pidB,SIGTERM);
}
}
}
}
}
return 0;
}
Pipes as such don't require you to provide a regular file, but they can have a unique, globally visible name, which is provided by a (unused) filename you have to specify. The contents of the file, if any, is handled by the library.
There are (simple) pipes for communication among related processes (such as a child and a parent process in the same process hierarchy) where the pipe handle can easily be passed to other processes.
The other flavor is called 'named pipes' for processes with any relation, where one can lookup the pipe handle using the global name (as explained in the answer of the question I linked). You can think of a pipe as of a directly connected speaking tube, allowing two processes to chitchat about whatever they like, using read and write functions. On Linux, a pipe is a simplex (at a time, one talks, the other one listens). One would nee two pipes for bidirectional async IO in this case (https://unix.stackexchange.com/questions/53641/how-to-make-bidirectional-pipe-between-two-programs). The immediate buffer for input and output is abstracted. Its just like with network sockets.
I'd suggest to compile this nice example in the accepted answer to play around with: https://stackoverflow.com/a/2789967/1175253
Edit
Example code with error handling. Treat pipe.h & pipe.c as a library (link NamedPipeReader and NamedPipeWriter against it).
This code would need further testing, however, the code is able to (re)open named pipes in any order.
pipe.h
#ifndef PIPE_H_
#define PIPE_H_
//C headers
#include <errno.h>
#include <assert.h>
//Linux headers
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#ifdef __cplusplus
extern "C"
{
#endif
int open_named_pipe(const char* const name, const int permissions, const int mode, int* pipe_created);
#ifdef __cplusplus
}
#endif
#endif /* PIPE_H_ */
pipe.c
#include "pipe.h"
#include <stdio.h>
int open_named_pipe(const char* const name, const int permissions, const int mode, int* pipe_created)
{
int fd;
assert(name);
assert(permissions);
assert(pipe_created);
//Create or use an existing pipe special file
if (0 == mkfifo(name, permissions))
{
*pipe_created = 1;
printf("Successfully created named pipe '%s'\n", name);
}
else
{
switch (errno)
{
case EEXIST:
//this is OK, as the other process might already has created the special file
printf("Opened existing named pipe '%s'\n", name);
break;
default:
fprintf(stderr, "Failed to create or access named pipe '%s'\n", name);
perror(" ");
return -1;
};
}
fd = open(name, mode);
if (fd < 0)
{
perror("Could not open pipe for writing");
if (*pipe_created)
{
if (0 == unlink(name))
{
*pipe_created = 0;
}
else
{
perror("Failed to unlink named pipe");
}
}
}
return fd;
}
NamedPipeReader.c
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include "pipe.h"
//Globals
const char* const pipe_name = "/tmp/myfifo";
const int pipe_permissions = 0600;
const size_t read_buffer_size = 1024; //[bytes]
const size_t read_retry_delay = 25000; //[us]
int fd = -1;
int pipe_created = 0;
char* read_buffer = NULL;
//Handles EPIPE signal
void signal_handler(int signal)
{
fprintf(stderr, "cought signal %d\n", signal);
}
//Handles cleanup on exit
void exit_handler(void)
{
if (read_buffer)
free(read_buffer);
if (fd >= 0)
close(fd);
//if this process created the FIFO, we unlink it
if (pipe_created == 0)
unlink(pipe_name);
}
int main()
{
//Locals
int run = 1;
int received = 0;
//Install the exit handler
atexit(&exit_handler);
signal(EPIPE, signal_handler);
signal(EACCES, signal_handler);
//Allocate the buffer
read_buffer = (char*) malloc(read_buffer_size);
if (!read_buffer)
{
perror("Failed to allocate buffer");
return EXIT_FAILURE;
}
restart: ;
//Close if already open
if(fd >= 0)
close(fd);
//Create or use an existing pipe special file
fd = open_named_pipe(pipe_name, pipe_permissions, O_RDONLY, &pipe_created);
if (fd < 0)
{
return EXIT_FAILURE;
}
while (run)
{
assert(fd >= 0);
assert(read_buffer_size > 1);
received = read(fd, read_buffer, read_buffer_size - 1);
if (received > 0)
{
//add a NUL char for string termination
read_buffer[received] = '0';
printf("local process %llu received: %s\n", (unsigned long long) getpid(), read_buffer);
}
else if (received == 0)
{
//EOF reached, this happens in case the writer has closed its handle.
//Perform a delayed restart and recreate the named pipe
usleep(read_retry_delay);
printf("Restarting...\n");
goto restart;
}
else
{
switch (errno)
{
case EAGAIN:
//Wait, if the pipe is empty,
//happens when opened with the O_NONBLOCK flag
usleep(read_retry_delay);
break;
case EPIPE:
case EBADF:
case EBADFD:
perror("Pipe error");
printf("Restarting...\n");
goto restart;
default:
perror("Pipe error");
return EXIT_FAILURE;
};
}
}
return EXIT_SUCCESS;
}
NamedPipeWriter.c
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include "pipe.h"
//Globals
const char* const pipe_name = "/tmp/myfifo";
const int pipe_permissions = 0600;
const size_t write_buffer_size = 1024; //[bytes]
const size_t write_retry_delay = 25000; //[us]
const size_t write_interval = 1000000;
int fd = -1;
int pipe_created = 0;
char* write_buffer = NULL;
//Handles EPIPE signal
void signal_handler(int signal)
{
fprintf(stderr, "cought signal %d\n", signal);
}
//Handles cleanup on exit
void exit_handler(void)
{
if (write_buffer)
free(write_buffer);
if (fd >= 0)
close(fd);
//if this process created the FIFO, we unlink it
if (pipe_created == 0)
unlink(pipe_name);
}
//Main Function
int main()
{
//Locals
int run = 1;
int sent = 0;
int msg_len = 0;
//Install the exit handler
atexit(&exit_handler);
signal(EPIPE, signal_handler);
signal(EACCES, signal_handler);
//Allocate the buffer
write_buffer = (char*) malloc(write_buffer_size);
if (!write_buffer)
{
perror("Failed to allocate buffer");
return EXIT_FAILURE;
}
restart: ;
//Close if already open
if(fd >= 0)
close(fd);
//Create or use an existing pipe special file
fd = open_named_pipe(pipe_name, pipe_permissions, O_WRONLY, &pipe_created);
if (fd < 0)
{
return EXIT_FAILURE;
}
while (run)
{
//Print message into the buffer
msg_len = snprintf(write_buffer, write_buffer_size, "Greetings from process %llu\n", (unsigned long long) getpid());
{
char* msg_ptr = write_buffer;
char* msg_end = write_buffer + msg_len;
while (msg_ptr != msg_end)
{
assert(fd >= 0);
assert(msg_ptr < msg_end);
sent = write(fd, msg_ptr, msg_end - msg_ptr);
if (sent > 0)
{
msg_ptr += sent;
}
else if (sent == 0)
{
//retry delay for nonblocking writes
usleep(write_retry_delay);
}
else
{
switch (errno)
{
case EAGAIN:
//Wait, if the pipe is full,
//happens when opened with the O_NONBLOCK flag
usleep(write_retry_delay);
break;
case EPIPE:
case EBADF:
case EBADFD:
perror("Pipe error");
printf("Restarting...\n");
goto restart;
default:
perror("Pipe error");
return EXIT_FAILURE;
};
}
}
printf("Written: %s\n", write_buffer);
usleep(write_interval);
}
}
return EXIT_SUCCESS;
}

Resources