FIFO implementing system of processes - c

I am trying to learn processes in C and I thiiink I understood the logic of pipe, but can't understand fifo, even if I read a lot about it. I recently made a program using pipe that takes a string from standard input, writes it in pipe1, checks if it's alphanumeric and if so, pipe3 reads it and shows it. If the string only contains digits, pipe2 reads it and replaces digits with _, then pipe4 reads the new string and shows it.
I'm putting it here, because I want to make something similar using fifo:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<string.h>
#include<sys/wait.h>
#include<ctype.h>
int main()
{
int p1[2];
int p2[2];
int p3[2];
int p4[2];
char input_str[100];
pid_t fork1;
pid_t fork2;
if (pipe(p1)==-1)
{
fprintf(stderr, "Pipe Failed" );
return 1;
}
if (pipe(p2)==-1)
{
fprintf(stderr, "Pipe Failed" );
return 1;
}
if (pipe(p3)==-1)
{
fprintf(stderr, "Pipe Failed" );
return 1;
}
if (pipe(p4)==-1)
{
fprintf(stderr, "Pipe Failed" );
return 1;
}
scanf("%s", input_str);
int isAlpha = 0;
int onlyDigits = 0;
for (int i=0; input_str[i]!= '\0'; i++)
{
if (isalpha(input_str[i]) != 0) {
isAlpha = 1;
onlyDigits = 0;
}
else if (isdigit(input_str[i]) != 0) {
isAlpha = 1;
onlyDigits = 1;
}
else {
isAlpha = 0;
onlyDigits = 0;
}
}
fork1 = fork();
if (fork1 < 0)
{
fprintf(stderr, "fork Failed" );
return 1;
}
else if (fork1 > 0)
{
close(p1[0]);
write(p1[1], input_str, strlen(input_str)+1);
}
else
{
close(p1[1]);
char string_from_p1[100];
read(p1[0], string_from_p1, 100);
close(p1[0]);
fork2 = fork();
if (onlyDigits) {
for (int i=0; string_from_p1[i]!= '\0'; i++) {
if (isdigit(string_from_p1[i]) != 0)
string_from_p1[i] = '_';
}
write(p2[1], string_from_p1, strlen(string_from_p1)+1);
}
else if (isAlpha) {
write(p3[1], string_from_p1, strlen(string_from_p1)+1);
}
if (fork2 < 0) {
fprintf(stderr, "fork Failed" );
return 1;
}
else if (fork2 > 0) {
char string_from_p2[100];
char string_from_p3[100];
char string_from_p4[100];
if (onlyDigits) {
close(p2[1]);
read(p2[0], string_from_p2, 100);
close(p2[0]);
write(p4[1], string_from_p2, strlen(string_from_p2)+1);
close(p4[1]);
read(p4[0], string_from_p4, 100);
printf("String from pipe4: %s\n", string_from_p4);
}
else if (isAlpha) {
close(p3[1]);
read(p3[0], string_from_p3, 100);
printf("String from pipe3: %s\n", string_from_p3);
}
}
exit(0);
}
}
Not sure how correct that is, but the FIFO program will only have 3 processes, it first reads from standard input lines of max 30 characters, writes in first exit (process2) the digits and in second exit (process3) the letters. then in process2 only shows the result (digits found), and in process3 turns small letters into capital letters and shows the result.
Can someone please help me?

As a starting point you could try something like this (most of the functions needs still to be implemented, see comments):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <ctype.h>
void read_and_write(const char *digits_fifo, const char *chars_fifo);
pid_t spawn_digits_child(const char *digits_fifo);
pid_t spawn_chars_child(const char *chars_fifo);
void digits_child(const char *digits_fifo);
void chars_child(const char *chars_fifo);
void wait_until_children_finish(pid_t pid1, pid_t pid2);
#define MAX_INPUT 30
int main() {
char *digits_fifo = "/tmp/digits_fifo";
char *chars_fifo = "/tmp/chars_fifo";
mkfifo(digits_fifo, 0666);
mkfifo(chars_fifo, 0666);
//fork digits process
pid_t pid_digits = spawn_digits_child(digits_fifo);
//fork chars process
pid_t pid_chars = spawn_chars_child(chars_fifo);
//parent
read_and_write(digits_fifo, chars_fifo);
wait_until_children_finish(pid_digits, pid_chars);
exit(0);
}
pid_t spawn_digits_child(const char *digits_fifo) {
pid_t pid1;
if ((pid1 = fork()) < 0) {
fprintf(stderr, "fork error digits process\n");
exit(-1);
} else if (pid1 == 0) {
digits_child(digits_fifo);
exit(0);
}
return pid1;
}
pid_t spawn_chars_child(const char *chars_fifo) {
//do sth similar then in spawn_digits_child but for chars child process
}
void wait_until_children_finish(pid_t pid1, pid_t pid2) {
//use waitpid to wait for child process termination
}
void read_and_write(const char *digits_fifo, const char *chars_fifo) {
//read input string
//open the two named pipes with O_WRONLY
//check with isdigit respective isalpha and send to the corresponding named pipe
//don't forget to close file handles
}
void chars_child(const char *chars_fifo) {
//open named piped with O_RDONLY
//e.g. int chars_fd = open(chars_fifo, O_RDONLY);
//read from pipe
//do uppercase string
//output it with printf
}
void digits_child(const char *digits_fifo) {
//open named piped with O_RDONLY
//e.g. int chars_fd = open(digits_fifo, O_RDONLY);
//read from pipe
//output it with printf
}

Related

named pipe won't open in C program

I have user read/write permissions on a pipe. Group has read. Other has read. But program gets "stuck" when I run it. Program 1 is the "parent". Program 2 is the "child".
Program 1:
int main(int argc, char * argv[])
{
FILE *fptr; //for opening and closing input file
int fdw;// write to pipe;
int fdr; //read to pipe;
pid_t pid;
int inputarray[500];
int arraylength = 0; int j =0;
char *mypipe = "mypipe";
if (argc < 2)
{
printf("Need to provide the file's name. \n");
return EXIT_FAILURE;
}
//open input file
fptr = fopen(argv[1], "r");
if (fptr==NULL)
{
printf("fopen fail.\n");
return EXIT_FAILURE;
}
//read input file and fill array with integers
while (!feof(fptr))
{
fscanf(fptr,"%d",&inputarray[arraylength]);
arraylength = arraylength + 1;
}
fclose(fptr); //close input file
pid = fork();
mkfifo(mypipe, 0666);
fdw = open("mypipe",O_WRONLY);
if (fdw < 0)
{
perror("File can't open to write.");
return;
}
int b;
b=3;
write(fdw,&b,sizeof(b));
close(fdw);
if ( pid ==-1)
{
perror("fork");
exit(1);
}
int status; //exit status of child
if(pid==0)//if child process
{
execl("program2", (char*) NULL);
}
else //if parent process
{
wait(&status);}
if((WIFEXITED(status)))
{
printf("Child's exit code %d", WEXITSTATUS(status));
}
else{
printf("Child did not terminate with exit");}
}
Program 2:
int fdl;
int data;
fdl = open("mypipe",O_RDONLY);
if ( fdl < 0)
{
perror("File can't open to read.");
return;
}
read(fdl,&data,sizeof(data));
close(fdl);
The program will block on writing to the fifo until what it's writing is being read. The reading in the child process won't happen since the execl() doesn't happen until after the writing.
Also, it looks like both processes will actually attempt to write to the fifo since you fork() and then immediately start writing.
You should fork(), then test on the returned PID. The parent should then write to the fifo while the child should call execl(). The fifo should be created by the parent before the fork() call.
You should also consider using indent or clang-format to properly format your code, which eases reading it and may expose bugs (forgotten curly braces etc.).
A simple complete example program. The parent writes a string to the child and the child reads it character by character and outputs it to standard output:
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
void parent(void);
void child(void);
int main(void) {
pid_t pid;
mkfifo("myfifo", 0666); /* fails if exists, but we don't care here */
if ((pid = fork()) < 0)
abort();
if (pid == 0)
child(); /* will not return */
else
parent();
return EXIT_SUCCESS;
}
void parent(void) {
int fd;
int len;
int ret;
int stat;
char *ptr;
char *msg = "Hello World!";
if ((fd = open("myfifo", O_WRONLY)) < 0)
abort();
len = strlen(msg) + 1;
ptr = msg;
puts("Parent: About to write to child");
while ((ret = write(fd, ptr, len)) != 0) {
if (ret > 0) {
len -= ret;
ptr += ret;
} else
abort();
}
close(fd);
puts("Parent: Waiting for child to exit");
wait(&stat);
printf("Parent: Child exited with status %d\n", stat);
}
void child(void) {
int fd;
int ret;
char ch;
if ((fd = open("myfifo", O_RDONLY)) < 0)
abort();
puts("Child: About to read from parent");
while ((ret = read(fd, &ch, 1)) != 0) {
if (ret > 0)
putchar(ch);
else
abort();
}
putchar('\n');
close(fd);
puts("Child: I'm done here");
exit(EXIT_SUCCESS);
}
In this case, since both child and parent processes are in the same context, I could have used an anonymous pipe pair created with pipe(), but this illustrates the flow, including the creation of the named pipe.

Unable to process the pipe function

Unable to process the pipe function where a give pipes in which one process sends a string message to a second process, and the second process reverses the case of each character in the message and sends it back to the first process.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include <stdbool.h>
// Parent: reads from P1_READ, writes on P1_WRITE
// Child: reads from P2_READ, writes on P2_WRITE
#define P1_READ 0
#define P2_WRITE 1
#define P2_READ 2
#define P1_WRITE 3
// the total number of pipe *pairs* we need
#define NUM_PIPES 2
/*
toggleString accepts an a pointer to char array, allocates size for the
string to be toggled,
copys the argument into a string, loops through the string and for every
uppercase character
we set it to its lower case counterpart and vice versa, returning the
toggled string
*/
char *toggleString(char *argv){
int i; /* Declare counter */
char *str = malloc(sizeof(argv[1])); /* Declare array sizeof input */
strcpy(str, argv); /* Copy String to char array */
for(i=0;str[i]!='\0';i++) { //Loop through length of string
if(str[i]>='A'&&str[i]<='Z'){ //if the array at i is uppercase
str[i]+=32; //Make it lower case
} else if (str[i]>='a'&&str[i]<='z') {// if the array at i is lowercase
str[i]-=32; //Make it uppercase
}
}
return str;
}
/*
int inputValidation accept and integer (number of arugments) and a
pointer to the cmd line input array
We check to see if the command line input contains the minimal number of
arugments and check to see
whether or not the user input contains at least one reversible haracter,
if all goes well we return 0
*/
int inputValidation(int argc, char *argv[]){
int i; //Declare counter variable
bool c = false; //Declare boolean flag using imported <stdbool.h>
char str[strlen(argv[1])]; //Declare str
strcpy(str, argv[1]); //copy argument into str
if (argc != 2) { // check to see if we have enough arguments to
continue
// Prompt user of correct usage
fprintf(stderr, "\nUsage: %s <string> or <'string 1, string 2', ...,
string n'> for multiple strings\n", argv[0]);
exit(EXIT_FAILURE); //Exit on improper input
} else {
//loop through our string
for(i=0;i<strlen(str);i++) {
//if any any char is a reversible character
if(isalpha((int) str[i])){
c = true; //set the flag to true
}
}
if(c == false){ //If flag is false input does not contain any
reversible charachters
printf("\nSorry, The string you entered did NOT contain any
Alphabetical Characters\nRun me again, with at least 1 Alphabetical
character\n\n");
exit(EXIT_FAILURE); //Exit on improper input
}
return (0);
}
}
/*
Main takes input from command line, calls input validation to make sure of
proper input,
then creates the pipes we will need and the forks the child process, Parent
and Child
execute they're respective code
*/
int main(int argc, char *argv[]) {
assert(argc>1);
int fd[2*NUM_PIPES]; //Declare int[] of file descriptors
int len, i; //Declare length and integer for count
pid_t pid; //Declare process id
char parent[strlen(argv[1])]; //Declare Parent array
char child[strlen(argv[1])]; //Declare Child array
if(inputValidation(argc, argv) == 0) /* Check for proper input */
strcpy(parent, argv[1]);
// create all the descriptor pairs we need
for (i=0; i<NUM_PIPES; ++i)
{
if (pipe(fd+(i*2)) < 0)
{
perror("Failed to allocate pipes");
exit(EXIT_FAILURE);
}
}
// fork() returns 0 for child process, child-pid for parent process.
if ((pid = fork()) < 0)
{
perror("Failed to fork process");
return EXIT_FAILURE;
}
//////////////////////////////Childs Code
BEGINS//////////////////////////////////
// if the pid is zero, this is the child process
if (pid == 0)
{
// Child. Start by closing descriptors we
// don't need in this process
close(fd[P1_READ]);
close(fd[P1_WRITE]);
// used for output
pid = getpid();
// wait for parent to send us a value
len = read(fd[P2_READ], &child, len);
if (len < 0)
{
perror("Child: Failed to read data from pipe");
exit(EXIT_FAILURE);
}
else if (len == 0)
{
// not an error, but certainly unexpected
fprintf(stderr, "Child: Read EOF from pipe");
}
else
{
// report pid to console
printf("Child(%d): Recieved Message\n\nChild(%d): Toggling Case and
Sending to Parent\n",pid, pid);
// send the message to toggleString and write it to pipe//
if (write(fd[P2_WRITE], toggleString(child), strlen(child)) < 0)
{
perror("Child: Failed to write response value");
exit(EXIT_FAILURE);
}
}
// finished. close remaining descriptors.
close(fd[P2_READ]);
close(fd[P2_WRITE]);
return EXIT_SUCCESS;
}
//child code ends///
//////////////////////////////Parent Code
BEGINS//////////////////////////////////
// Parent. close unneeded descriptors
close(fd[P2_READ]);
close(fd[P2_WRITE]);
// used for output
pid = getpid();
// send a value to the child
printf("\nParent(%d): Sending %s to Child\n\n", pid, argv[1]);
if (write(fd[P1_WRITE], argv[1], strlen(argv[1])) != strlen(argv[1]))
{
perror("Parent: Failed to send value to child ");
exit(EXIT_FAILURE);
}
// now wait for a response
len = read(fd[P1_READ], &parent, strlen(parent));
if (len < 0)
{
perror("Parent: failed to read value from pipe");
exit(EXIT_FAILURE);
}
else if (len == 0)
{
// not an error, but certainly unexpected
fprintf(stderr, "Parent(%d): Read EOF from pipe", pid);
}
else
{
// report what we received
printf("\nParent(%d): Received %s from Child\n\n", pid, parent);
}
// close down remaining descriptors
close(fd[P1_READ]);
close(fd[P1_WRITE]);
// wait for child termination
wait(NULL);
return EXIT_SUCCESS;
}
//////////////////////////////Parent Code
ENDS//////////////////////////////////
This works:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include <stdbool.h>
#define P1_READ 0
#define P2_WRITE 1
#define P2_READ 2
#define P1_WRITE 3
#define NUM_PIPES 2
static
char *toggleString(char *argv)
{
int i;
char *str = malloc(strlen(argv) + 1); /* Key Fix */
strcpy(str, argv);
for (i = 0; str[i] != '\0'; i++)
{
if (str[i] >= 'A' && str[i] <= 'Z')
{
str[i] += 32;
}
else if (str[i] >= 'a' && str[i] <= 'z')
{
str[i] -= 32;
}
}
return str;
}
static
int inputValidation(int argc, char *argv[])
{
bool c = false;
char str[strlen(argv[1])];
strcpy(str, argv[1]);
if (argc != 2)
{
fprintf(stderr, "\nUsage: %s <string> or <'string 1, string 2', ..., string n'> for multiple strings\n", argv[0]);
exit(EXIT_FAILURE);
}
else
{
for (size_t i = 0; i < strlen(str); i++)
{
if (isalpha((int)str[i]))
{
c = true;
}
}
if (c == false)
{
printf("\nSorry, The string you entered did NOT contain any"
" Alphabetical Characters\nRun me again, with at least 1 Alphabetical"
" character\n\n");
exit(EXIT_FAILURE);
}
return(0);
}
}
int main(int argc, char *argv[])
{
assert(argc > 1);
int fd[2 * NUM_PIPES];
int len, i;
pid_t pid;
char parent[strlen(argv[1])];
char child[strlen(argv[1])];
if (inputValidation(argc, argv) == 0)
strcpy(parent, argv[1]);
for (i = 0; i < NUM_PIPES; ++i)
{
if (pipe(fd + (i * 2)) < 0)
{
perror("Failed to allocate pipes");
exit(EXIT_FAILURE);
}
}
if ((pid = fork()) < 0)
{
perror("Failed to fork process");
exit(EXIT_FAILURE);
}
if (pid == 0)
{
close(fd[P1_READ]);
close(fd[P1_WRITE]);
pid = getpid();
len = read(fd[P2_READ], child, sizeof(child));
if (len < 0)
{
perror("Child: Failed to read data from pipe");
exit(EXIT_FAILURE);
}
else if (len == 0)
{
fprintf(stderr, "Child: Read EOF from pipe\n");
}
else
{
child[len] = '\0';
printf("Child(%d): Received Message [%s]\nChild(%d): Toggling Case and Sending to Parent\n", pid, child, pid);
char *toggled = toggleString(child);
printf("Child(%d): Sending [%s]\n", pid, toggled);
if (write(fd[P2_WRITE], toggled, len) < 0)
{
perror("Child: Failed to write response value");
exit(EXIT_FAILURE);
}
free(toggled);
}
close(fd[P2_READ]);
close(fd[P2_WRITE]);
return EXIT_SUCCESS;
}
close(fd[P2_READ]);
close(fd[P2_WRITE]);
pid = getpid();
printf("\nParent(%d): Sending [%s] to Child\n\n", pid, argv[1]);
len = strlen(argv[1]);
if (write(fd[P1_WRITE], argv[1], len) != len)
{
perror("Parent: Failed to send value to child");
exit(EXIT_FAILURE);
}
len = read(fd[P1_READ], parent, sizeof(parent));
if (len < 0)
{
perror("Parent: failed to read value from pipe");
exit(EXIT_FAILURE);
}
else if (len == 0)
{
fprintf(stderr, "Parent(%d): Read EOF from pipe\n", pid);
}
else
{
parent[len] = '\0';
printf("\nParent(%d): Received [%s] from Child\n\n", pid, parent);
}
close(fd[P1_READ]);
close(fd[P1_WRITE]);
wait(NULL);
return EXIT_SUCCESS;
}
It was painful extracting your code from your comments, and the split over multiple line strings, and so on. The toggleString() function was broken — allocating 1 byte and then copying a string over that. The other code was not careful about null-terminating strings and handling them. These are basically the problems diagnosed in the comments.
Sample run:
$ pp53 'AbSoLuTeLy GlOrIoUs'
Parent(5209): Sending [AbSoLuTeLy GlOrIoUs] to Child
Child(5210): Received Message [AbSoLuTeLy GlOrIoUs]
Child(5210): Toggling Case and Sending to Parent
Child(5210): Sending [aBsOlUtElY gLoRiOuS]
Parent(5209): Received [aBsOlUtElY gLoRiOuS] from Child
$

linux - exec'd program not terminating

I am writing program in C on Linux which has to fork 2 children.
First child will send two random numbers over pipe to the second child. It will listen for SIGUSR1 signal and will then terminate.
The second child will duplicate(dup2) pipe input as STDIN and file fp as STDOUT. It will then execl program which will print out some data according to its input and end.
My problem is, that the execl'd program will never terminate and I don't know why. Any help or tips will be appreciated.
main.c (parent):
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>
const int BUFFER_SIZE = 30;
int pipefd[2] = {0,0};
int parent_pid = 0;
int first_pid = 0;
int second_pid = 0;
int sleep_time = 5;
int debug = 0;
FILE *fp;
void parent_func() {
int wstatus = 0;
sleep(sleep_time);
kill(first_pid, SIGUSR1);
wait(&wstatus);
waitpid(second_pid, &wstatus, 0);
}
static void sigusr1_handler(int sig) {
if (sig == SIGUSR1) {
fputs("TERMINATED", stderr);
close(pipefd[1]);
exit(0);
}
}
void first_func() {
struct sigaction act;
char buffer[BUFFER_SIZE];
close(pipefd[0]);
memset(&act, '\0', sizeof(act)); // clear the sigaction struct
act.sa_handler = &sigusr1_handler; // sets function to run on signal
if (sigaction(SIGUSR1, &act, NULL) < 0) { // assign sigaction
fputs("cannot assign sigaction - exiting...", stderr);
exit(1);
}
while (1) {
sprintf(buffer, "%d %d\n", rand(), rand());
write(pipefd[1], buffer, strlen(buffer));
puts(buffer);
sleep(1);
}
}
void second_func() {
close(pipefd[1]);
fp = fopen("out.txt", "w");
char buf[30];
dup2(pipefd[0], STDIN_FILENO);
close(pipefd[0]);
//dup2(fileno(fp), STDOUT_FILENO);
execl("./test", "", NULL);
perror("Error");
}
int main(int argc, char *argv[]) {
int fork_val = 0;
parent_pid = getpid();
if (pipe(pipefd)) {
fputs("cannot create pipe - exiting...", stderr);
return 1;
}
if (debug) {
sleep_time *= 10;
}
if ((fork_val = fork()) == -1) {
fputs("cannot fork process - exiting...", stderr);
return 1;
} else if (fork_val == 0) {
first_func();
} else {
first_pid = fork_val;
if ((fork_val = fork()) == -1) {
fputs("cannot fork process - exiting...", stderr);
return 1;
} else if (fork_val == 0) {
second_func();
} else {
second_pid = fork_val;
parent_func();
}
}
fclose(fp);
exit(0);
}
test.c (the execl'd file):
#include "nd.h"
#include "nsd.h"
#include <stdio.h>
#include <stdlib.h>
int main() {
int num1 = 0;
int num2 = 0;
char buffer[100];
while (fgets(buffer, 100, stdin) != NULL) {
if (sscanf(buffer, "%d %d", &num1, &num2) == 2) {
(num1 < 0) ? num1 = (num1 * -1) : num1;
(num2 < 0) ? num2 = (num2 * -1) : num2;
if (num1 == 1 || num2 == 1) {
puts("1");
} else if (num1 == num2) {
if (nd(num1) == 1) {
puts("prime");
} else {
printf("%d\n", num1);
}
} else if (nd(num1) == 1 && nd(num2) == 1) {
puts("prime");
} else {
printf("%d\n", nsd(num1, num2));
}
} else {
fputs("error\n", stderr);
}
}
fputs("DONE", stderr);
exit(0);
}
To be able to detect an end of file from a pipe you need to read from a empty pipe with no writer (no process with an open for writing descriptor).
As your writer (first_func()) never closes its descriptor and always writes something in a never ending loop the reader will either wait for some data or read some data.
Be also careful about closing non useful descriptors, if not you may encounter some problems with pipes, such has a single process that is a reader and a writer, so being unable to detect the end of file...

Parent process killing child process in infinite loop

To solve my problem, I set
prctl(PR_SET_PDEATHSIG, SIGHUP); as in stackoverflow answer before i called exec*, and took out the part where we pipe the PID. It works!!!!! Wow....
HOWEVER, stackoverflow won't let me say I've answered my own question yet...
So I tried to write a program, which I want to run a program, and kill that program after a cpl seconds if it doesn't finish. DADDY forks off a CHILD, which forks off another BABY, CHILD pipes the PID of the BABY to DADDY, which then waits a second and kills them both if they haven't wrapped up their business (it's a macabre scene). But it doesn't work, DADDY stays in S+ State, and the infinite loop that is Baby goes on forever until I ctr+c. On the bright side, this code is an amalgamation of everything I've learnt on stack-overflow. Here we go.
#include <math.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
static int read_from_pipe(int file)
{
int c;
FILE *stream = fdopen(file, "r");
if (fscanf(stream, "%d", &c) != 1)
{
fprintf(stderr, "Failed to read integer from pipe\n");
exit(1);
}
fclose(stream);
return c;
}
static void write_to_pipe(int file, int pidRacket)
{
FILE *stream = fdopen(file, "w");
fprintf(stream, "%d", pidRacket);
fclose(stream);
}
static int spawnpipe(char *fileName, int *fd)
{
int pid;
int pipe_fds[2];
char *command[] = {"racket", fileName, NULL};
if (pipe(pipe_fds) < 0)
{
fprintf(stderr, "FE: pipe\n");
exit(1);
}
switch ((pid = fork()))
{
case -1:
printf("syserr");
exit(1);
case 0:
close(1);
close(2);
dup(pipe_fds[1]);
close(pipe_fds[0]);
close(pipe_fds[1]);
execvp(*command, command);
perror("execv");
exit(EXIT_FAILURE);
default:
*fd = pipe_fds[0];
close(pipe_fds[1]);
return pid;
}
}
static int spawnfp(char *fileName, FILE **fpp)
{
int fd, pid;
pid = spawnpipe(fileName, &fd);
*fpp = fdopen(fd, "r");
return pid;
}
int main(int argc, char *argv[])
{
pid_t pid;
int mypipe[2];
if (pipe(mypipe))
{
fprintf(stderr, "Pipe failed.\n");
return EXIT_FAILURE;
}
pid = fork();
if (pid < (pid_t) 0)
{
fprintf(stderr, "Fork failed.\n");
return EXIT_FAILURE;
}
else if (pid != (pid_t) 0)
{
double diff = 0;
clock_t launch = clock();
close(mypipe[1]);
int pidRacket = read_from_pipe(mypipe[0]);
while (diff < 1.3)
{
clock_t done = clock();
diff = ((double)done - (double)launch) / (double)CLOCKS_PER_SEC;
}
kill(pidRacket, SIGKILL);
kill(pid, SIGKILL);
return EXIT_SUCCESS;
}
else if (pid == (pid_t) 0)
{
close(mypipe[0]);
char buf[100];
FILE *fp;
char *fileName = argv[1];
int pidRacket = spawnfp(fileName, &fp);
write_to_pipe(mypipe[1], pidRacket);
if (argc == 1)
{
printf("Not enough arguments!");
_exit(EXIT_FAILURE);
}
else if (argc == 2)
{
}
sleep(1);
while (fgets(buf, sizeof buf, fp))
{
printf("%s\n", buf);
}
fclose(fp);
kill(pid, SIGKILL);
return 0;
}
}
Credit to quinsley and vijay!
Various comments as I look at the code:
End messages with newlines; you're on Linux now, not Windows. Windows systems seem to encourage people to leave messages without newlines, but it won't work well on Unix in general and Linux in particular.
Don't use _exit() if you want your error messages to appear, especially ones that don't end in a newline.
Don't report error messages on standard output; report them on standard error (that's what it is for!).
Writing else if (argc == 2) { } (with nothing in the braces) is a little odd if there is an else clause after it, but it is pointless when there is no else clause. You should arguably test for argc != 2 since that is the correct number of arguments (or, perhaps more accurately, any arguments beyond argc == 2 are ignored).
If you want to sleep for a time involving sub-second timing (e.g. 1.3 seconds), use one of the appropriate sub-second sleep commands. In this case, nanosleep() is probably the function to use.
Don't use SIGKILL except in dire emergency. The process signalled with SIGKILL has no chance to clean up or anything; it is killed immediately (assuming your process is allowed to send a signal to the other at all, of course).
case -1: printf("syserr"); with no break; after it means that on error, the flow of control goes into the following case 0: code, which is not what's required. Either break; or exit(1); is probably appropriate. (Bullet 3 applies too.)
Don't close standard error. The code:
close(1);
close(2);
dup(pipe_fds[1]);
close(pipe_fds[0]);
close(pipe_fds[1]);
execvp(*command, command);
perror("execv");
_exit(EXIT_FAILURE);
is never going to report an error; you closed standard error. Remember that programs are entitled to have a standard error channel. The C standard guarantees it, but you have to cooperate and make sure you've not closed standard error.
Some of the casts in:
diff = ((double)((uintmax_t)(clock_t)done) - (double)((uintmax_t)(clock_t)launch)) / (double)CLOCKS_PER_SEC;
are unnecessary. Since both done and launch are of the type clock_t, the casts to clock_t are unnecessary. The intermediate cast to uintmax_t also isn't really necessary. You could simply write:
diff = ((double)done - (double)launch) / (double)CLOCKS_PER_SEC;
and even then, two of the three casts are theoretically redundant (any two of the three could be removed).
The code in read_from_pipe() is curious and error prone. Since you've got a file stream, simply read an integer from it using fscanf(), rather than the curious construct using double arithmetic and fractional values that are then multiplied at the end. This is especially appropriate since the write_to_pipe() code uses printf("%d", ...); to write the data. Since c is already an int, the cast in return (int)c; is superfluous.
Theoretically, it would be a good idea to check the streams returned by fdopen() to ensure that the operation did not fail.
If the pipe() function fails, you report the error on standard output and then continue as nothing had gone wrong.
It is not clear what the racket command actually does. It doesn't exist on my machine.
argv in spawnfp() is unused.
pid = fork(); if (pidDos < (pid_t) 0) generates a warning (accurately) that pidDos might be used uninitialized. The condition should presumably be using pid, not pidDos. You then send a SIGKILL signal to the PID identified at random by pidDos, which is unlikely to lead to happiness.
When I copy cat to racket and invoke the following code (as a program mk built from mk.c) as mk /etc/passwd, I get to see the password file double-spaced (and the message from the shell about Killed: 9.
#include <math.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
static int read_from_pipe(int file)
{
int c;
FILE *stream = fdopen(file, "r");
if (fscanf(stream, "%d", &c) != 1)
{
fprintf(stderr, "Failed to read integer from pipe\n");
exit(1);
}
fclose(stream);
return c;
}
static void write_to_pipe(int file, int pidRacket)
{
FILE *stream = fdopen(file, "w");
fprintf(stream, "%d", pidRacket);
fclose(stream);
}
static int spawnpipe(char *fileName, int *fd)
{
int pid;
int pipe_fds[2];
char *command[] = {"racket", fileName, NULL};
if (pipe(pipe_fds) < 0)
{
fprintf(stderr, "FE: pipe\n");
exit(1);
}
switch ((pid = fork()))
{
case -1:
printf("syserr");
exit(1);
case 0:
close(1);
close(2);
dup(pipe_fds[1]);
close(pipe_fds[0]);
close(pipe_fds[1]);
execvp(*command, command);
perror("execv");
exit(EXIT_FAILURE);
default:
*fd = pipe_fds[0];
close(pipe_fds[1]);
return pid;
}
}
static int spawnfp(char *fileName, FILE **fpp)
{
int fd, pid;
pid = spawnpipe(fileName, &fd);
*fpp = fdopen(fd, "r");
return pid;
}
int main(int argc, char *argv[])
{
pid_t pid;
int mypipe[2];
if (pipe(mypipe))
{
fprintf(stderr, "Pipe failed.\n");
return EXIT_FAILURE;
}
pid = fork();
if (pid < (pid_t) 0)
{
fprintf(stderr, "Fork failed.\n");
return EXIT_FAILURE;
}
else if (pid != (pid_t) 0)
{
double diff = 0;
clock_t launch = clock();
close(mypipe[1]);
int pidRacket = read_from_pipe(mypipe[0]);
while (diff < 1.3)
{
clock_t done = clock();
diff = ((double)done - (double)launch) / (double)CLOCKS_PER_SEC;
}
kill(pidRacket, SIGKILL);
kill(pid, SIGKILL);
return EXIT_SUCCESS;
}
else if (pid == (pid_t) 0)
{
close(mypipe[0]);
char buf[100];
FILE *fp;
char *fileName = argv[1];
int pidRacket = spawnfp(fileName, &fp);
write_to_pipe(mypipe[1], pidRacket);
if (argc == 1)
{
printf("Not enough arguments!");
_exit(EXIT_FAILURE);
}
else if (argc == 2)
{
}
sleep(1);
while (fgets(buf, sizeof buf, fp))
{
printf("%s\n", buf);
}
fclose(fp);
kill(pid, SIGKILL);
return 0;
}
}
I fixed some, but by no means all, of the issues identified in this revision of the code.
Oh, and item 16: the read end of the pipe isn't closed until the third process terminates. You need to pass mypipe[1] to spawnfp(), which needs to relay it to spawnpipe(), and the child created there needs to close the pipe descriptor before executing 'racket'. This is compounded by fscanf() looking for either EOF or a non-digit at the end of the PID it reads from the pipe. You could provide a newline or something at the end and that would also free up the parent process to spin in its timing loop. Since you say racket doesn't terminate, that's why you don't see anything much.
It's easier to paste the whole program again than present the diffs:
#include <assert.h>
#include <math.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
static int read_from_pipe(int file)
{
int c;
FILE *stream = fdopen(file, "r");
assert(stream != 0);
if (fscanf(stream, "%d", &c) != 1)
{
fprintf(stderr, "Failed to read integer from pipe\n");
exit(1);
}
fclose(stream);
return c;
}
static void write_to_pipe(int file, int pidRacket)
{
FILE *stream = fdopen(file, "w");
assert(stream != 0);
fprintf(stderr, "%d: pidRacket = %d\n", (int)getpid(), pidRacket);
fprintf(stream, "%d", pidRacket);
fclose(stream);
}
static int spawnpipe(char *fileName, int *fd, int pfd)
{
int pid;
int pipe_fds[2];
char *command[] = {"racket", fileName, NULL};
if (pipe(pipe_fds) < 0)
{
fprintf(stderr, "FE: pipe\n");
exit(1);
}
switch ((pid = fork()))
{
case -1:
printf("syserr");
exit(1);
case 0:
close(pfd);
close(1);
//close(2);
dup(pipe_fds[1]);
close(pipe_fds[0]);
close(pipe_fds[1]);
execvp(*command, command);
perror("execv");
exit(EXIT_FAILURE);
default:
fprintf(stderr, "%d: pid = %d\n", (int)getpid(), pid);
*fd = pipe_fds[0];
close(pipe_fds[1]);
return pid;
}
}
static int spawnfp(char *fileName, FILE **fpp, int pfd)
{
int fd, pid;
pid = spawnpipe(fileName, &fd, pfd);
*fpp = fdopen(fd, "r");
assert(*fpp != 0);
return pid;
}
int main(int argc, char *argv[])
{
pid_t pid;
int mypipe[2];
if (pipe(mypipe))
{
fprintf(stderr, "Pipe failed.\n");
return EXIT_FAILURE;
}
pid = fork();
if (pid < (pid_t) 0)
{
fprintf(stderr, "Fork failed.\n");
return EXIT_FAILURE;
}
else if (pid != (pid_t) 0)
{
double diff = 0.0;
clock_t launch = clock();
close(mypipe[1]);
fprintf(stderr, "%d: Reading from pipe:\n", (int)getpid());
int pidRacket = read_from_pipe(mypipe[0]);
fprintf(stderr, "%d: Read PID %d from pipe\n", (int)getpid(), pidRacket);
while (diff < 1.3)
{
clock_t done = clock();
diff = ((double)done - (double)launch) / (double)CLOCKS_PER_SEC;
printf("%f\n", diff);
}
kill(pidRacket, SIGKILL);
kill(pid, SIGKILL);
return EXIT_SUCCESS;
}
else if (pid == (pid_t) 0)
{
close(mypipe[0]);
char buf[100];
FILE *fp;
char *fileName = argv[1];
int pidRacket = spawnfp(fileName, &fp, mypipe[1]);
fprintf(stderr, "%d: Writing PID %d to pipe\n", (int)getpid(), pidRacket);
write_to_pipe(mypipe[1], pidRacket);
fprintf(stderr, "%d: Written PID to pipe\n", (int)getpid());
if (argc == 1)
{
printf("Not enough arguments!");
_exit(EXIT_FAILURE);
}
else if (argc == 2)
{
}
sleep(1);
while (fgets(buf, sizeof buf, fp))
{
printf("%s\n", buf);
}
fclose(fp);
fprintf(stderr, "%d: Finished reading from pipe\n", (int)getpid());
kill(pid, SIGKILL);
return 0;
}
}
I made this a while back for stupid fun, it uses up a big chunk of your cpu to run but I'm sure you can modify it to break at a certain point or to fit your needs maybe.
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
int main(int argc, char*argv[])
{
int childpid;
int pids[100];
int count1 = 0, count2 = 0;
int count3 = 0;
L1:
childpid = fork();
if(childpid == 0)
{
}
else
{
if(childpid != 0 && childpid != -1)
{
if(count3 < 100)
{
pids[count3] = childpid;
printf("Pid:%d\n",pids[count3]);
count3++;
goto L1;
}
else
{
count3--;
goto L2;
}
}
L2:
while(count3 > 0)
{
if(pids[count3] != -1 || pids[count3] != 1)
{
printf("Killing pid:%d\n",pids[count3]);
kill(pids[count3],SIGKILL);
}
count3--;
}
if(count3 == 0)
{
goto L1;
}
}
return 0;
}

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