How to loop a FIFO reader program in C - c

I'm writing a c program in ubuntu that writes and reads in fifo pipe. I already have the program writing working perfectly, I now have a problem reading in a loop.
My reading program :
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#define MAX_CTRLS 80
void main()
{
printf("test1");
char ch2[MAX_CTRLS];
int pipe1;
pipe1 = open("/tmp/ctrl", O_RDONLY);
printf("test2");
read(pipe1, ch2, MAX_CTRLS);
printf("%s",ch2);
close(pipe1);
}
This program is supposed to read in the fifo, store it in ch2 then print in ch2. But when running it normally it works perfectly, while running it in a loop, it doesn't do anything.
Here is writing program 1 :
void send(char ch2[MAX_CTRLS])
{
int create;
create = mkfifo("/tmp/ctrl", 0666);
int pipe1;
pipe1 = open("/tmp/ctrl", O_WRONLY);
write(pipe1, ch2, MAX_CTRLS);
close(pipe1);
}
This one works using another program I have, that has a line like this :
ch2[0] = 'a';
printf("%s",ch2);
send(ch2);
The printf helps me confirm that ch2 is set properly. I need the reading program to loop the reading function, aka print everytime what is set in ch2.
I just ant it to print a in this case, everytime my program launches send(ch2).
Thaks in advance

I figure you want to have a loop that open the pipe and reads if anything is there, with no delay, then goes to sleep for a while before next reading.
Suggest something like this, u might also swap for(;;) to While(boolValue == true)
int fd = -1, naptime = 200; // 200msec
for (;;){
fd = open(pipe, mode|nodelay);
if (fd != -1){
int st = read (fd, buf, sizeof(buf)); /* open OK */
// do your printing of data if any exist - st holds read chars
if(st >0) printf(buf);
}
sleep(naptime); // sleep til next reading
}

I need the reading program to loop the reading function, aka print everytime what is set in ch2.
Simply adding a while(1) to run the reading program on a loop won't do good, because read() from a FIFO that has been closed (which is the case after the first run of the writing program) doesn't block, but rather returns 0, and we surely don't want a busy waiting read() loop. So, we need a means of waiting for something readable from the FIFO. If we have the fortune to run Linux, we can use inotify to get notified of writings to the FIFO. The reading code could be:
#include <sys/inotify.h>
int fd = inotify_init();
inotify_add_watch(fd, "/tmp/ctrl", IN_MODIFY);
pipe1 = open("/tmp/ctrl", O_RDONLY);
struct inotify_event inev;
while (read(fd, &inev, sizeof inev))
if (read(pipe1, ch2, MAX_CTRLS) > 0)
printf("%s", ch2), fflush(stdout);

Related

Pipe file bigger than 64Kb and get the size and send it to char *

I am trying to create a simple program using pipes, even though that are easier options for the same task (fopen(), lseek(), ftell(), etc).
First I use execve() to perform a terminal cat, and send the information through the pipe so I may be able to print the size of the file descriptor and read it to a malloc'd char pointer. My solution is this one:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <fcntl.h>
int main (int argc, char *argv[]) {
int fd[2], active;
int pipe_sz = 0;
char *name;
pipe(fd);
//change /bin/cat according to your system
char *cmd[] = {"/bin/cat", "example.txt", NULL};
if (fork() == 0) {
//CHILD
dup2(fd[1], STDOUT_FILENO);
close(fd[1]);
active = execve(cmd[0], cmd, NULL);
} else {
//PARENT
wait(NULL);
int ioctl_sz = ioctl(fd[0], FIONREAD, &pipe_sz);
name = malloc(pipe_sz + 1);
int result = read(fd[0], name, pipe_sz);
name[pipe_sz] = '\0';
printf("LEN NAME: %ld\n", strlen(name));
close(fd[0]);
close(fd[1]);
}
return 0;
}
Everything works fine as long as I keep myself inside the pipe's limits (my LINUX system is able to hold 65536 bytes). I decided to push the limits, and see what could happen. As expected, the program stays stuck when my file was above 65536 bytes.
I do not want to manually change my pipe's limits through fcntl(pipefd, F_SETPIPE_SZ, size) (since I "don't know" my file's size). Therefore, I researched, and came across the pipe2(pipefd, O_NONBLOCK) in order to avoid my pipe from stopping the reading process. Unfortunately, I received only this message:
/bin/cat: write error: Resource temporarily unavailable
I even tried a while loop read() and realloc() to see if my code could at least give the char pointer result, but I was not successful, and part of file was lost.
Is it possible to produce a code that may provide the same results as mine above with files bigger than 65536 bytes?
Is there a way to interrupt the pipe if it takes too long to finish the process? Or even make a while loop to guess and resize the pipe through
fcntl()?
Thanks everyone for the help!

How to use a pseudo-terminal returned from posix_openpt?

I'm trying to use posix_openpt on Mac. The issue I'm seeing is that I get a file descriptor back from posix_openpt. I use the file descriptor for reading and create a copy using dup for writing. The issue I'm running into is that when I write to the master file descriptor, I read that data back out from the master. So no data ends up at the slave. I confirmed this by using posix_spawnp to run a program with stdin/stdout/stderr set to the slave file. The program hangs indefinitely waiting for input. Here is my code (note, all error handling was removed for legibility):
int master_fd = posix_openpt(O_RDWR);
grantpt(master_fd);
unlockpt(master_fd);
char *slave_filename_orig = ptsname(master_fd);
size_t slave_filename_len = strlen(slave_filename_orig);
char slave_filename[slave_filename_len + 1];
strcpy(slave_filename, slave_filename_orig);
posix_spawn_file_actions_t fd_actions;
posix_spawn_file_actions_init(&fd_actions);
posix_spawn_file_actions_addopen(&fd_actions, STDIN_FILENO, slave_filename, O_RDONLY, 0644);
posix_spawn_file_actions_addopen(&fd_actions, STDOUT_FILENO, slave_filename, O_WRONLY, 0644);
posix_spawn_file_actions_adddup2(&fd_actions, STDOUT_FILENO, STDERR_FILENO);
pid_t pid;
posix_spawnp(&pid, "wc", &fd_actions, NULL, NULL, NULL);
int master_fd_write = dup(master_fd);
char *data = "hello world";
write(master_fd_write, data, strlen(data));
close(master_fd_write);
char buffer[1024];
read(master_fd, buffer, 1024); // <- Issue Here
// buffer now contains hello world. It should contain the output of `wc`
(Note: The above was only tested on Linux; I don't have a Mac to work on, but I have no reason to believe it's any different in the details here.)
There are several problems with your code:
At least on Linux, calling posix_spawn() with a null pointer causes a crash. You need to provide all the arguments. Even if Macs accept it the way you have it, doing this is a Good Idea.
Next, wc reading from standard input will wait until an attempt to read more data gives an End Of File condition before it prints out the statistics it gathers; your code doesn't do this. With a pty, if you write a specific byte (Typically with the value 4, but it can be different, so best to use what the terminal says instead of hardcoding it) to it, the terminal driver will recognize that as signalling EOF without having to close the master like you would when using a pipe (Making it impossible to read the output of wc).
Second, the terminal's default settings include echoing the input; that's what you're reading.
A cleaned up version that addresses these issues and more (Like yours, with most error checking omitted; real code should be checking all these functions for errors):
#define _XOPEN_SOURCE 700
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <fcntl.h>
#include <spawn.h>
#include <termios.h>
#include <unistd.h>
#include <wait.h>
int main(void) {
int master_fd = posix_openpt(O_RDWR);
grantpt(master_fd);
unlockpt(master_fd);
char *slave_filename_orig = ptsname(master_fd);
size_t slave_filename_len = strlen(slave_filename_orig);
char slave_filename[slave_filename_len + 1];
strcpy(slave_filename, slave_filename_orig);
//printf("slave pty filename: %s\n", slave_filename);
// Open the slave pty in this process
int slave_fd = open(slave_filename, O_RDWR);
// Set up slave pty to not echo input
struct termios tty_attrs;
tcgetattr(slave_fd, &tty_attrs);
tty_attrs.c_lflag &= ~ECHO;
tcsetattr(slave_fd, TCSANOW, &tty_attrs);
posix_spawn_file_actions_t fd_actions;
posix_spawn_file_actions_init(&fd_actions);
// Use adddup2 instead of addopen since we already have the pty open.
posix_spawn_file_actions_adddup2(&fd_actions, slave_fd, STDIN_FILENO);
posix_spawn_file_actions_adddup2(&fd_actions, slave_fd, STDOUT_FILENO);
// Also close the master and original slave fd in the child
posix_spawn_file_actions_addclose(&fd_actions, master_fd);
posix_spawn_file_actions_addclose(&fd_actions, slave_fd);
posix_spawnattr_t attrs;
posix_spawnattr_init(&attrs);
pid_t pid;
extern char **environ;
char *const spawn_argv[] = {"wc" , NULL};
posix_spawnp(&pid, "wc", &fd_actions, &attrs, spawn_argv, environ);
close(slave_fd); // No longer needed in the parent process
const char *data = "hello world\n";
ssize_t len = strlen(data);
if (write(master_fd, data, len) != len) {
perror("write");
}
// Send the terminal's end of file interrupt
cc_t tty_eof = tty_attrs.c_cc[VEOF];
if (write(master_fd, &tty_eof, sizeof tty_eof) != sizeof tty_eof) {
perror("write EOF");
}
// Wait for wc to exit
int status;
waitpid(pid, &status, 0);
char buffer[1024];
ssize_t bytes = read(master_fd, buffer, 1024);
if (bytes > 0) {
fwrite(buffer, 1, bytes, stdout);
}
close(master_fd);
return 0;
}
When compiled and run, outputs
1 2 12
There are two problems with this code.
First, you are seeing "hello world" on master_fd because by default terminals echo. You need to set the terminal to raw mode to suppress that.
Second, wc won't output anything until it sees an EOF, and it will not see an EOF until you close the master. Not just master_fd_write mind you, but all copies of master_fd, including master_fd itself. However, once you close the master, you cannot read from it.
Choose some other program that wc to demonstrate the functionality of posix_openpt.
Edit: It is possible to raise the end-of-file condition on the slave without closing the master by writing ^D (EOT, ascii 4).

Communication between processes - pipe and fifo

I need to create program with 3 processes:
The first process should repeatedly read /dev/urandom and send 15 chars each cycle to the second process via a pipe.
The second process should convert received data to hex and send the result to the third process via a fifo.
The third process should print the received data.
This is what I wrote so far. Communication using the pipe is working fine, however there is some problem with the fifo - when I change n to a larger number such as 100000 or 1000000, the program doesn't start. When it's smaller, say 500 or 1000, the program works. What could be the reason behind that?
This is how I run it:
cat /dev/urandom | ./a.out
And here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#define FIFO "my_fifo"
int main(void) {
int pdesk[2];
char buf[15];
char buffer[15];
char hex[30];
char f[30];
int len;
int n;
n = 100;
umask(0);
mkfifo(FIFO, 0666);
pipe(pdesk);
if (fork() == 0) {
for (int i = 0; i < n; i++) {
read(STDIN_FILENO, buffer, 15);
write(pdesk[1], buffer, 15);
}
close(pdesk[1]);
} else {
sleep(1);
int fp;
for(int i = 0; i < n; i++) {
read(pdesk[0], buf, 15);
for(int a = 0, b = 0; b < 30; ++a, b+= 2)
sprintf(hex + b, "%02x", buf[a] & 0xff);
fp = open(FIFO, O_WRONLY);
write(fp, hex, 30);
close(fp);
usleep(10000);
}
close(pdesk[0]);
}
if (fork() == 0) {
sleep(2);
int fp;
for (int i = 0; i < n; i++) {
fp = open(FIFO, O_RDONLY);
read(fp, f, 30);
printf("Odczytano: %s\n", f);
close(fp);
usleep(10000);
}
}
}
If I understand your code correct, it will do the following:
With the first fork you start a child that reads from stdin and writes to the pipe.
Your parent process reads from the pipe and writes to the FIFO.
When your parent process has finished its loop it calls the second fork to create another child which will read from the FIFO and print the data.
When the loop count is too large you will reach the buffer limit of the FIFO and the parent will block because no process is reading from the FIFO. When the process is blocked in writing to the FIFO it will never create the child that is expected to read from the FIFO.
I think the main problem is that you should create the second child before starting the loop that reads the data from the pipe and writes to the FIFO.
Some additional remarks:
With cat /dev/urandom | ./a.out your program does not read /dev/urandom directly. It reads from a pipe which might behave differently.
You should always check the return value of read. It will tell you how many bytes it has read which may be less than you asked it to read. If you want to have exactly 15 characters you might have to read several times if you get less than 15 characters.
The same applies to write.
Thank you very much. When the process that displays data is above other child processes, it finally works.
With cat /dev/urandom | ./a.out your program does not read /dev/urandom directly. It reads from a pipe which might behave differently.
How could I change it?
The programs also needs to read files the same way it reads from /dev/urandom, for example:
cat file.txt | ./a.out
I took your advice and started to check the value of read and now it doesn't go behind the range of file. The problem is I don't know how to check which parameter was called (and hence I can't check the length of file) - if it was file.txt, /dev/urandom, none or anything else. I tried with
int main(char argc, char* argv[])
but argv is always ./a.out, no matter what I call. Is there any way to check that?

Should a read from FIFO block after all the data was just read from that FIFO?

I'm learning about pipe programming in Linux, and am having trouble understanding pipe / FIFO management.
I wrote a small program which opens a FIFO I created (I did mkfifo newfifo in my terminal before executing the program). I then repeatedly read and dump my character buffer. I'm filling the FIFO using echo "message" > newfifo from another terminal's cmd line.
The problem is that when I write to the FIFO, I can read that data in the buffer, but then the read doesn't block anymore. My understanding was that after I read the data from the FIFO, the FIFO should be empty and the read should block. Am I thinking about this wrong, or am I incorrectly managing the FIFO?
Code is below:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#define NEWPIPE "./newfifo"
void main()
{
int great_success = 0;
int fd;
char buffer[20];
fd = open(NEWPIPE, O_RDONLY);
while (1) {
great_success = read(fd, buffer, 20);
if (great_success < 0) {
printf("pipe failed\n");
} else {
printf("buffer : %s\n", buffer);
printf("great_success = %d\n", great_success);
great_success = 0;
}
}
}
Your understanding of how fifos works is incorrect. They are much like pipes: if the write end is closed (the echo command has terminated), the read end will read end-of-file (EOF), i.e. return 0.
Note that when you open the fifo, it isn't read that is blocking. The blocking system call is the open() system call, as explained in http://linux.die.net/man/4/fifo
Because the process(echo "message" > newfifo) is a short program, it terminated quickly. Once the process terminated, there is no write end for the pipe, so the read end in another process gets an EOF.

making named pipes and using poll

I'm so confused with this, I need to create named pipes using mkfifo (i know how to do this) the program before would use fork to create child processes that would do something, but now I have to replace fork with poll() to watch multiple streams (that's the part I don't get). In more detailed, when i run my program in terminal, its suppose to make the mkfifo files and then wait till a stream comes in, hence just stay there, not closing. Then I open up a new terminal, and need to input exactly this into terminal "cat file1 > (name of the mkfifo files)" and what that should do is make the program read the data that was in file1, on any of the input pipes made from mkfifo. I've looked everywhere but can never put things together to make it work.
this is what i have so far
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <poll.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include "battleFieldReader.h" //this is where all the data computation is done
main(int argc, char *argv[])
{
if (sscanf (argv[1], "%i", &N) !=1 )
{
printf ("ERROR: Please input an integer.\n");
_exit(EXIT_SUCCESS);
}
struct pollfd pfd[2];
pid = getpid();
printf("pid=%d\n",pid);
N = atoi(argv[1]);
signal(SIGTERM, removePipes);
int i;
char fileName[32];
char fileName2[32];
snprintf (fileName, sizeof(fileName), "%d_%di", pid, 0);
mkfifo(fileName, 0666);
pfd[0].fd = open(fileName, O_RDONLY | O_NDELAY);
pfd[0].events = POLLIN;
snprintf (fileName2, sizeof(fileName2), "%d_%do", pid, 0);
mkfifo(fileName2, 0666);
pfd[1].fd = open(fileName2, O_WRONLY | O_NDELAY);
pfd[1].events = POLLIN;
while(1)
{
int n;
n = poll(pfd, 2, 3000);
if(n < 1)
{
printf("waiting...\n");
continue;
}
if(pfd[0].revents & POLLIN)
{
printf("test\n");
/*ideally this is where i would put a method to compute data but its just an infinite loop, its suppose to stop, so it can do the same thing whenever another file comes on, but I dont know how to stop it either*/
}
}
}
whats happening is I'm creating a pipe 2N times, one for input and output for whatever process id is running the program. then wait till something comes in on one of the pipes and then spit out what needs to be done with the file data. Can anyone clear things with me, if I'm going in the right direction or something.
poll tells you if you can read from or write to a file descriptor without blocking (or without getting an EAGAIN/EWOULDBLOCK result if the fd in non-blocking).
Your code has a number of obvious problems:
You say you want to create 2N fifos, but then you only create 2. Your pfd array has a fixed size of 2, too. You'll need a bigger array and loop to create more.
You open an O_WRONLY file descriptor to write to a pipe, but then you set the events field to POLLIN, which will test for input available. You want POLLOUT to test for output possible.
In your processing loop, you poll two fds, but you only check pfd[0] for availability, and then you never do anything with it. You should read pfd[0].fd after the pfd[0].revents & POLLIN check succeeds. You should also check pfd[1].revents & POLLOUT and write data to pfd[1].fd if that succeeds.

Resources