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, a开发者_开发知识库s 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.
精彩评论