Linux C: "Interactive session" with separate read and write named pipes? - c

I am trying to work with "Introduction to Interprocess Communication Using Named Pipes - Full-Duplex Communication Using Named Pipes", link ; in particular fd_server.c (included below for reference)
Here is my info and compile line:
:~$ cat /etc/issue
Ubuntu 10.04 LTS \n \l
:~$ gcc --version
gcc (Ubuntu 4.4.3-4ubuntu5) 4.4.3
:~$ gcc fd_server.c -o fd_server
fd_server.c creates two named pipes, one for reading and one for writing. What one can do, is: in one terminal, run the server and read (through cat) its write pipe:
:~$ ./fd_server & 2>/dev/null
[1] 11354
:~$ cat /tmp/np2
and in another, write (using echo) to server's read pipe:
:~$ echo "heeellloooo" > /tmp/np1
going back to first terminal, one can see:
:~$ cat /tmp/np2
HEEELLLOOOO
0[1]+ Exit 13 ./fd_server 2> /dev/null
What I would like to do, is make sort of a "interactive" (or "shell"-like) session; that is, the server is run as usual, but instead of running cat and echo, I'd like to use something akin to screen. What I mean by that, is that screen can be called like screen /dev/ttyS0 38400, and then it makes a sort of a interactive session, where what is typed in terminal is passed to /dev/ttyS0, and its response is written to terminal. Now, of course, I cannot use screen, because in my case the program has two separate nodes, and as far as I can tell, screen can refer to only one.
How would one go about to achieve this sort of "interactive" session in this context (with two separate read/write pipes)?
Code below:
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
//#include <fullduplex.h> /* For name of the named-pipe */
#define NP1 "/tmp/np1"
#define NP2 "/tmp/np2"
#define MAX_BUF_SIZE 255
#include <stdlib.h> //exit
#include <string.h> //strlen
int main(int argc, char *argv[])
{
int rdfd, wrfd, ret_val, count, numread;
char buf[MAX_BUF_SIZE];
/* Create the first named - pipe */
ret_val = mkfifo(NP1, 0666);
if ((ret_val == -1) && (errno != EEXIST)) {
perror("Error creating the named pipe");
exit (1);
}
ret_val = mkfifo(NP2, 0666);
if ((ret_val == -1) && (errno != EEXIST)) {
perror("Error creating the named pipe");
exit (1);
}
/* Open the first named pipe for reading */
rdfd = open(NP1, O_RDONLY);
/* Open the second named pipe for writing */
wrfd = open(NP2, O_WRONLY);
/* Read from the first pipe */
numread = read(rdfd, buf, MAX_BUF_SIZE);
buf[numread] = '0';
fprintf(stderr, "Full Duplex Server : Read From the pipe : %sn", buf);
/* Convert to the string to upper case */
count = 0;
while (count < numread) {
buf[count] = toupper(buf[count]);
count++;
}
/*
* Write the converted string back to the second
* pipe
*/
write(wrfd, buf, strlen(buf));
}
Edit:
Right, just to clarify - it seems I found a document discussing something very similar, it is - a modification of the script there ("For example, the following script configures the device and starts a background process for copying all received data from the serial device to standard output...") for the above program is below:
# stty raw #
( ./fd_server 2>/dev/null; )&
bgPidS=$!
( cat < /tmp/np2 ; )&
bgPid=$!
# Read commands from user, send them to device
echo $(kill -0 $bgPidS 2>/dev/null ; echo $?)
while [ "$(kill -0 $bgPidS 2>/dev/null ; echo $?)" -eq "0" ] && read cmd; do
# redirect debug msgs to stderr, as here we're redirected to /tmp/np1
echo "$? - $bgPidS - $bgPid" >&2
echo "$cmd"
echo -e "\nproc: $(kill -0 $bgPidS 2>/dev/null ; echo $?)" >&2
done >/tmp/np1
echo OUT
# Terminate background read process - if they still exist
if [ "$(kill -0 $bgPid 2>/dev/null ; echo $?)" -eq "0" ] ;
then
kill $bgPid
fi
if [ "$(kill -0 $bgPidS 2>/dev/null ; echo $?)" -eq "0" ] ;
then
kill $bgPidS
fi
# stty cooked
So, saving the script as say starter.sh and calling it, results with the following session:
$ ./starter.sh
0
i'm typing here and pressing [enter] at end
0 - 13496 - 13497
I'M TYPING HERE AND PRESSING [ENTER] AT END
0~�.N=�(�~� �����}����#������~� [garble]
proc: 0
OUT
which is what I'd call for "interactive session" (ignoring the debug statements) - server waits for me to enter a command; it gives its output after it receives a command (and as in this case it exits after first command, so does the starter script as well). Except that, I'd like to not have buffered input, but sent character by character (meaning the above session should exit after first key press, and print out a single letter only - which is what I expected stty raw would help with, but it doesn't: it just kills reaction to both Enter and Ctrl-C :) )
I was just wandering if there already is an existing command (akin to screen in respect to serial devices, I guess) that would accept two such named pipes as arguments, and establish a "terminal" or "shell" like session through them; or would I have to use scripts as above and/or program own 'client' that will behave as a terminal..

If you just want to be able to receive multiple lines, rather than exiting after one, this is simple. You just need to place a loop around your read/write code, like so (quick and dirty):
while( 1 ) {
numread = read(rdfd, buf, MAX_BUF_SIZE);
fprintf(stderr, "Full Duplex Server : Read From the pipe : %sn", buf);
/* Convert to the string to upper case */
count = 0;
while (count < numread) {
buf[count] = toupper(buf[count]);
count++;
}
/*
* Write the converted string back to the second
* pipe
*/
write(wrfd, buf, strlen(buf));
}
Of course, now you have an application which will never exit, and will start doing nothing as soon as it gets an EOF, etc. So, you can reorganize it to check for errors:
numread = read(rdfd, buf, MAX_BUF_SIZE);
while( numread > 0) {
/* ... etc ... */
numread = read(rdfd,buf, MAX_BUF_SIZE);
}
if( numread == 0 ) {
/* ... handle eof ... */
}
if( numread < 0 ) {
/* ... handle io error ... */
}
From the man page, read returns 0 for EOF and -1 for an error (you have read the man page, right? http://linux.die.net/man/2/read ). So what this does is keeps on grabbing bytes from the read pipe until it reaches EOF or some error, in which case you (probably) print a message and exit. That said, you might just do a reopen when you get an EOF so you can get more input.
Once you've modified your program to read continuously, entering multiple lines interactively is simple. Just execute:
cat - > /tmp/np1
The '-' explicitly tells cat to read from stdin (this is the default, so you don't actually need the dash). So cat will pass everything you enter on to your pipe program. You can insert an EOF using Ctrl+D, which will cause cat to stop reading stdin. What happens to your pipe program depends on how you handle the EOF in your read loop.
Now, if you want another program that does all the io, without cat, (so you end up with a stdio echo program), the pseudocode is going to look sort of like this:
const int stdin_fd = 0; // known unix constant!
int readpipe_fd = open the read pipe, as before
int writepipe_fd = open the write pipe, as before
read stdin into buffer
while( stdin is reading correctly ) {
write data from stdin to read pipe
check write is successful
read write pipe into buffer
check read is successful
write buffer to stdout (fprintf is fine)
read stdin into buffer.
}
You can use the read system call to read stdin if you feel like it, but you can also just use stdio. Reading, writing, and opening your pipes should all be identical to your server program, except read/write is all reversed.

Related

Prevent read() systemcall returing with 0 when run as background process

I have a piece of software that is able to read commands from stdin for debug purposes in a separate thread. When my software runs as foreground process read behaves as expected, its blocking and waits for input by the user, i.e the thread sleeps.
When the software is run as a background process, read constantly returns 0 (possible EOF detected?).
The problem here is, that this specific read is in a while(true) loop. It runs as fast as it can and steals precious CPU load on my embedded device.
I tried redirecting /dev/null to the process but the behavior was the same. I am running my custom Linux on an ARM Cortex A5 board.
The problematic piece of code follows and is run inside its own thread:
char bufferUserInput[256];
const int sizeOfBuffer = SIZE_OF_ARRAY(bufferUserInput);
while (1)
{
int n = read(0, bufferUserInput, sizeOfBuffer); //filedes = 0 equals to reading from stdin
printf("n is: %d\n", n);
printf("Errno: %s",strerror(errno));
if (n == 1)
{
continue;
}
if ((1 < n)
&& (n < sizeOfBuffer)
&& ('\n' == bufferUserInput[n - 1]))
{
printf("\r\n");
bufferUserInput[n - 1] = '\0';
ProcessUserInput(&bufferUserInput[0]);
} else
{
n = 0;
}
}
I am looking for a way to prevent read from constantly returning when running in the background and wait for user input (which of course will never come).
If you start your program in the "background" (as ./program &) from a shell script, it's stdin will be redirected from /dev/null (with some exceptions).
Trying to read from /dev/null will always return 0 (EOF).
Example (on linux):
sh -c 'ls -l /proc/self/fd/0 & wait'
... -> /dev/null
sh -c 'dd & wait'
... -> 0 bytes copied, etc
The fix from the link above should also work for you:
#! /bin/sh
...
exec 3<&0
./your_program <&3 &
...
When stdin is not a terminal, read is returning with 0 because you are at the end of the file. read only blocks after reading all available input when there could be more input in the future, which is considered to be possible for terminals, pipes, sockets, etc. but not for regular files nor for /dev/null. (Yes, another process could make a regular file bigger, but that possibility isn't considered in the specification for read.)
Ignoring the various problems with your read loop that other people have pointed out (which you should fix anyway, as this will make reading debug commands from the user more reliable) the simplest change to your code that will fix the problem you're having right now is: check on startup whether stdin is a terminal, and don't launch the debug thread if it isn't. You do that with the isatty function, declared in unistd.h.
#include <stdio.h>
#include <unistd.h>
// ...
int main(void)
{
if (isatty(fileno(stdin)))
start_debug_thread();
// ...
}
(Depending on your usage context, it might also make sense to run the debug thread when stdin is a pipe or a socket, but I would personally not bother, I would rely on ssh to provide a remote (pseudo-)terminal when necessary.)
read() doesn't return 0 when reading from the terminal in a backgrounded process.
It either continues to block while causing a SIGTTIN to be sent to the process (which may break the blocking and cause retval=-1,errno=EINTR to be returned or it causes retval=-1, errno EIO if SIGTTIN is ignore.
The snippet below demonstrates this:
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
int main()
{
char c[256];
ssize_t nr;
signal(SIGTTIN,SIG_IGN);
nr = read(0,&c,sizeof(c));
printf("%zd\n", nr);
if(0>nr) perror(0);
fflush(stdout);
}
The code snippet you've shown can't possibly test reveal 0-returns since you never test for zero-ness in the return value.

Piping the output of a command as an input of another in C

So, I have this command line I want to execute in C:
ps -eo user,pid,ppid 2> log.txt | grep user 2>>log.txt | sort -nk2 > out.txt
And I need to figure out how I'd make the code... fd
What I understand is that I have the father ps... Which needs to redirect the output as an input to the grep, and it also needs to error output to log.txt...
The same with the grep... the output must be redirected to the sort, and the error must be saved in log.txt.
and I'd just output the sort to the file...
Something like this:
FATHER(ps) SON(grep) SON-SON? (sort)
0->must be closed ----> 0 ----> 0
/ /
1 ---------------/ 1 --------/ 1 -->out.txt
2 ---> log.txt 2 ---> log.txt 2 -->nowhere?
But I don't know how this would be coded...
I'd appreciate your help.
You can execute shell commands in C programs directly using sh -c
(How do I execute a Shell built-in command with a C function?)
Pipes can also be used in C programs directly using popen()
The following program example shows how to pipe the output of the ps -A command to the grep init command: ps -A | grep init
#include <stdio.h>
#include <stdlib.h>
int
main ()
{
FILE *ps_pipe;
FILE *grep_pipe;
int bytes_read;
int nbytes = 100;
char *my_string;
/* Open our two pipes */
ps_pipe = popen ("ps -A", "r");
grep_pipe = popen ("grep init", "w");
/* Check that pipes are non-null, therefore open */
if ((!ps_pipe) || (!grep_pipe))
{
fprintf (stderr,
"One or both pipes failed.\n");
return EXIT_FAILURE;
}
/* Read from ps_pipe until two newlines */
my_string = (char *) malloc (nbytes + 1);
bytes_read = getdelim (&my_string, &nbytes, "\n\n", ps_pipe);
/* Close ps_pipe, checking for errors */
if (pclose (ps_pipe) != 0)
{
fprintf (stderr,
"Could not run 'ps', or other error.\n");
}
/* Send output of 'ps -A' to 'grep init', with two newlines */
fprintf (grep_pipe, "%s\n\n", my_string);
/* Close grep_pipe, checking for errors */
if (pclose (grep_pipe) != 0)
{
fprintf (stderr,
"Could not run 'grep', or other error.\n");
}
/* Exit! */
return 0;
}
source: http://crasseux.com/books/ctutorial/Programming-with-pipes.html
else use named pipes (https://en.wikipedia.org/wiki/Named_pipe)
http://www.cs.fredonia.edu/zubairi/s2k2/csit431/more_pipes.html
See also this C program to perform a pipe on three commands (uses fork())

Cannot mimic the action of `echo` command in C code

I have written a couple of bash script files that communicate via two serial ports. One script can be thought of as a receiver and the other as a transmitter. The receiving script reads and displays data line by line until it reads the character sequence <break>. It then stops listening, and sends a character sequence of either one or two back to the transmitter. The script is thus:
#!/bin/bash
# Read and reply bash script.
exec 3<> /dev/ttyS4
while true; do
#cat -v /dev/ttyS4 | while read -r input; do
while read -r -u 3 input; do
if [ "$input" = "<break>" ]
then
echo "break command received."
break
else
echo -e "${input}"
fi
done
echo "Sending selection"
if [ "$selection" = "one" ]
then
selection="two"
else
selection="one"
fi
echo "$selection" >&3
done
The transmitter script transmits some character data and then waits for the reply of one or two from the receiver script above:
exec 4<> /dev/ttyS0
selection="one"
while true; do
echo "************************************" > /dev/ttyS0
echo " Selection: ${selection}" > /dev/ttyS0
echo "************************************" > /dev/ttyS0
echo "<break>" > /dev/ttyS0
read -r -t 3 -u 4 input
if [ -z "$input" ]
then
echo "Response from remote timed out."
elif [ "$input" = "one" ]
then
selection=$input
elif [ "$input" = "two" ]
then
selection=$input
colour=$RED
else
echo "Unknown selection: $input"
fi
sleep 1
done
The above two scripts work fine and the receiver script correctly identifies the <break> character sequence.
I wish to replace the 'transmitter' script with a small C program however I find that when I send the character sequence <break> the receiver script this time does NOT identify is as the 'end-of-transmission', is simple echos <break> to stdout, and NOT break command received. I have tried several things such as adding escape characters but there is obviously something different about the way Bash echo works and how I send the data in my C code:
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#define TRUE 1
#define FALSE 0
int main(int argc, char *argv[]) {
int selected_tool = DEFAULT_TOOL_SELECTION;
FILE *ser2_fd_write, *ser2_fdrw;
struct termios tios, tios_w; // ser 2 termios structure
int ser2_fd, ser2_fdw;
char read_buffer[BUFFER_SIZE];
struct timespec tdelay;
bzero(&tdelay, sizeof(tdelay));
tdelay.tv_sec = 1;
tdelay.tv_nsec = 5000;
if ((ser2_fd = open("/dev/ttyS0", O_RDWR)) == -1){
printf("Unable to open ttyS0 as read-write only.\n");
return EXIT_FAILURE;
}
bzero(&tios, sizeof(tios));
cfsetispeed(&tios, B38400);
cfsetospeed(&tios, B38400);
tios.c_cflag = B38400 | CS8 | CLOCAL | CREAD | CRTSCTS;
tios.c_iflag = IGNPAR;
tios.c_oflag = 0;
tios.c_lflag = ICANON; //0
tios.c_cc[VTIME] = 0;
tios.c_cc[VMIN] = 10;
tcflush(ser2_fd, TCIFLUSH);
if (tcsetattr(ser2_fd, TCSANOW, &tios) == -1){
printf("Could not set ser 2 attributes.\n");
return -1;
}
if ((ser2_fdrw = fdopen(ser2_fd, "awr")) == NULL){
printf("Unable to open file descriptor.\n");
return EXIT_FAILURE;
}
while(1){
push_to_ser2(ser2_fdrw, selected_tool);
/*
* This is where sending <break> differs from echo
*/
//fputs("break", ser2_fdrw);
fprintf(ser2_fdrw, "break\r\n");
//fprintf(ser2_fdrw, "\r\n");
//write(ser2_fd,"break\r\n", 9);
fflush(ser2_fdrw);
int c = 0;
nanosleep(&tdelay, NULL);
tcflush(ser2_fd, TCIOFLUSH);
tcdrain(ser2_fd);
fcntl(ser2_fd, F_SETFL, 0);
if ( (c = read(ser2_fd, read_buffer, BUFFER_SIZE)) > 0){
read_buffer[c] = 0;
if (strcmp(read_buffer, "one\r\n") == 0){
selected_tool = 1;
} else if (strcmp(read_buffer, "two\r\n") == 0){
selected_tool = 2;
}
}else{
}
}
return EXIT_SUCCESS;
}
/*
* Convenience function to push data to ser 2
* *c_data pointer to the card data
* *t_data pointer to the tool data
*/
void push_to_ser2(FILE * fd, int tool){
fprintf(fd, "**********************************************************\n");
fprintf(fd, "* *\n");
fprintf(fd, "* Tool %d Data *\n", tool);
fprintf(fd, "* *\n");
fprintf(fd, "**********************************************************\n");
fprintf(fd,"\r\n");
fflush(fd);
}
I've tried various alterations to the termios struct too but it makes no difference. Any ideas?
There are several issues with your code that should be corrected:
Instead of bzero(&tios, sizeof(tios)) the code should be calling tcgetattr() to properly initialize the structure. This can be a serious issue for canonical input, as the existing code will have zeroed out all of the control code specifications instead of having proper definitions.
Instead of direct assignments, the code should be performing bit-wise operations (in order to preserve existing settings). See Setting Terminal Modes Properly.
You're specifying non-canonical output with canonical input, which is an atypical combination. Seems like you want canonical mode for both input and output.
VMIN and VTIME are only meaningful for non-canonical input, and should not be specified for canonical input (as it clobbers the VEOF and VEOL character specifications).
The read(ser2_fd,...) is silently ignoring errors.
Instead of using fdopen() and fprintf(), just use write(ser2_fd, ...) to simplify the code and overhead. Downsides are that you'll have to specify the byte counts and use sprintf() to perform integer to string conversion.
See Serial Programming Guide for POSIX Operating Systems.
It looks as if the bash script is sending the literal string <break>, angle brackets included. Meanwhile, the C program is only sending break. Add the angle brackets to the literal in the C program. (Those brackets don't have any special meaning to either echo or fprintf, so they just get sent as-is.)

C: How to redirect named pipe to stdin/out of child process

Basically I want to do in C (and without buffering) the same as this bash-script:
#!/bin/sh
cat ./fifo_in | myprogram > ./fifo_out
In other words I want to exec "myprogram" and redirect its stdin and stdout to two pipes which have been created previously.
Another program is feeding data into fifo_in and reading out of fifo_out.
Of course it would be easy to just read from ./fifo_in, buffer it in the parent and write to myprogram's stdin (and reverse for stdout and ./fifo_out) but I think there is probably a way to let "myprogram" read/write directly from/to the fifos without buffering in the parent process.
Edit:
Eugen's answer seems to be the correct one, but I cannot get it to work.
I use this function on the C-side, which seems correct to me:
pid_t execpipes(const char *wd, const char *command, const char *pipename)
{
char pipename_in[FALK_NAMESIZE];
char pipename_out[FALK_NAMESIZE];
strcpy(pipename_in, FALKPATH);
strcat(pipename_in, "/");
strcat(pipename_in, FALK_FIFO_PATH);
strcat(pipename_in, "/");
strncat(pipename_in, pipename, FALK_NAMESIZE-2);
strcpy(pipename_out, pipename_in);
strcat(pipename_out, "R");
pid_t pid;
pid = fork();
if (pid < 0)
{ //Error occured
perror("fork");
exit(1);
}
if (pid == 0)
{
chdir(wd);
d("execpipes: pipename_in=\"%s\"\n", pipename_in);
d(" pipename_out=\"%s\"\n", pipename_out);
freopen(pipename_in,"r",stdin);
freopen(pipename_out,"w",stdout);
d("execpipes: command=\"%s\"\n", command);
execl("/bin/sh", "sh", "-c", command, (char *)NULL); // using execv is probably faster
// Should never get here
perror("execl");
exit(1);
}
return pid;
}
I read and write the pipes from a PHP-script (only relevant part posted):
$pipe_in = fopen($fp.$pipename, "w");
$DEBUG .= "Write to pipe_in\n";
$ret = fwrite($pipe_in, $in);
$pipe_out = fopen($fp.$pipename.'R', "r");
$DEBUG .= "Read from pipe_out\n";
$atext = fread($pipe_out, 200000); // Program hangs here
The program is started correctly, receives the input via $pipe_in correctly, processes the data correctly and (because it ran fine for many months) I assume it puts out the data correctly to stdout, but when I try to read from $pipe_out, it hangs. I know that the pipes themselves are set up correctly because if I don't open $pipe_out, the program does not get any input - which makes sense because there is no reader for $pipe_out and therefore the pipeline is not complete. So I can open $pipe_out, but I cannot read anything from it, which is quite strange.
Edit2:
Program works now, thanks guys - For some reason the first pipe has to be closed before you can read from the second pipe:
$pipe_in = fopen($fp.$pipename, "w");
$pipe_out = fopen($fp.$pipename.'R', "r");
$DEBUG .= "Write to pipe_in\n";
$ret = fwrite($pipe_in, $in);
fclose($pipe_in);
$DEBUG .= "Read from pipe_out\n";
$atext = fread($pipe_out, 200000);
fclose($pipe_out);
unlink($fp.$pipename);
unlink($fp.$pipename.'R');
I'd write a small wrapper for myprogram, that does
freopen("./fifo_in","r",stdin)
freopen("./fifo_out","w",stdout)
(Ofcourse not with constant paths!), then execve myprogram
Korn shell supports coprocesses, which I think effectively does what you ask: read from a pipe and write to a pipe (which can be stdout and stdin of a C process)
http://www.dartmouth.edu/~rc/classes/ksh/coprocesses.html
How about
myprogram < ./fifo_in > ./fifo_out
?
As for getting rid of the buffering: Since your program directly reads/writes the pipes, the buffering shouldn't hurt you.
An important point is that the process which writes fifo_in should flush properly so you don't have to wait. The same goes for your output: As soon as a "work unit" is complete, flush your stdout which will make the data available to whoever reads the output pipe.
But you can't do anything in myprogram to make the writer of fifo_in flush its buffers.
[EDIT] To do this from C (without the help of a shell), use code like this:
- Put the names of the two pipes into local variables on the stack
- Call `fork()`. If that returns '0', then open the two fifos with `freopen()` [like Eugen suggested][1]
- Call `execve` to launch the real exec.
That's (in a nutshell) what the shell is doing when it runs commands. Make sure the parent process (the one where fork() returns a PID != 0) handles the signal SIGCHLD
Perhaps you are looking of a named pipe? For example:
mkfifo fifo_in
As a test stub for my_program.c, to read fifo_in via the buffered stdin:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void) {
char buf[80];
if (!freopen("./fifo_in", "r", stdin)) {
perror("freopen");
exit(EXIT_FAILURE);
}
while (!ferror(stdin)) {
while (fgets(buf, sizeof buf, stdin))
fputs(buf, stdout);
sleep(1);
}
return 0;
}
Then as a test for the writer, using the bash shell:
for x in {1..10}; do
echo $x
echo $x >> fifo_in
sleep 1
done
Notes:
I'd prefer to use unbuffered I/O.
The writer, at least on my machine, blocks until there is a reader.
The reader, in this sample, cannot tell when the writer is finished.

read not blocking on named pipe

i have the following bit of C code which reads from a pipe and then should block but it never blocks
int pipe_fd;
int res;
int open_mode = O_RDONLY;
char buf[100];
int bytes_read = 0;
memset (buf, '\0', sizeof(buf));
pipe_fd = open(FIFO_NAME, open_mode);
if (access(FIFO_NAME, F_OK) == -1)
{
res = mkfifo(FIFO_NAME, 0777);
if (res != 0)
{
fprintf (stderr, "Could not create fifo %s\n", FIFO_NAME);
exit (EXIT_FAILURE);
}
}
for(;;)
{
do
{
res = read(pipe_fd, buf, sizeof(buf));
bytes_read += res;
}while (res > 0);
// process data then go back and block
............
}
It is sent a simple buffer by some code in a bash script like this './test 1'
#!/bin/bash
pipe=/tmp/pipe
if [[ ! -p $pipe ]]; then
echo "Reader not running"
exit 1
fi
if [[ "$1" ]]; then
echo "some string" >$pipe
else
echo "q" >$pipe
fi
I run the C code program in gdb and initially it does block on the read but as soon as i call the bash script the C code no longer blocks , it does successfully read the data from
the buffer and then each time it reads there are 0 bytes read so not sure why its no longer blocking. The 'some string' data is correctly received at the other side.
I just need it to sit there waiting for data process it and then go back and wait for more
I run the C code program in gdb and initially it does block on the read but as soon as i call the bash script the C code no longer blocks , it does successfully read the data from the buffer and then each time it reads there are 0 bytes read so not sure why its no longer blocking. The 'some string' data is correctly received at the other side.
0 means EOF. FIFO can be read or written only when there are processes connected to it for both reading and writing. When there are no more writers (your shell scripts terminated) readers are notified about that through read() returning the EOF.
FIFOs behave that way to be compatible with shell pipe logic e.g.:
$ mkfifo ./tmp1
$ cat < input > ./tmp1 &
$ cat < ./tmp1 > /dev/null
If read() will not return EOF, the second cat would block forever.
I just need it to sit there waiting for data process it and then go back and wait for more
In your C program you have to reopen() the FIFO after read() returned the EOF first time.
P.S. Found quite nice FIFO summary for your. Check the table on the second page.
your bash script closes the pipe so the C is getting an "eof" condition
I think write side shell script close the pipe every time when echo something.
So, the write script need to open the pipe and repeatedly use the opended descriptor to write something and close the opended descripter finally.

Resources