Is inotify a one shot only solution - c++

I have a program whiuch utilizes inotify.
What it does is it start watching the directory for the file being created. When it happens the program reads the content then deletes the file.
Then the user initiates an action which will create the same file again. However, inotify does not see that the file has been created a second time and the file is not processed.
Code is as follows:
fileCreated = false;
m_wd1 = inotify_add_watch( m_fd, "/tmp", IN_CREATE );
if( m_wd1 == -1 )
{
}
else
{
while( true )
{
poll_num = poll( &fds, nfds, -1 );
if( poll_num == -1 )
{
if( errno == EINTR )
continue;
syslog( LOG_ERR, "Fail to run poll" );
result = 1;
}
else if( poll_num > 0 && ( fds.revents & POLLIN ) )
{
syslog( LOG_DEBUG, "Polling is successful" );
for( ;; )
{
len = read( m_fd, buf, sizeof( buf ) );
if( len == -1 && errno != EAGAIN )
{
syslog( LOG_ERR, "Failure to read the inotify event" );
result = 1;
break;
}
for( ptr = buf; ptr < buf + len; ptr += sizeof( struct inotify_event ) + event->len )
{
event = (const struct inotify_event *) ptr;
if( event->mask & IN_CREATE )
{
std::string name( event->name );
if( name == "scan_results" )
{
fileCreated = true;
break;
}
}
}
if( fileCreated || result )
break;
}
}
if( fileCreated )
{
std::ifstream log( "scan_results" );
if( log.rdstate() & std::ifstream::failbit ) != 0 )
{
}
else
{
}
log.close();
if( remove( "scan_results" ) != 0 )
{
syslog( LOG_ERR, "Failed to remove the file" );
}
else
{
syslog( LOG_DEBUG, "File deleted successfully" );
}
}
fileCreated = false;
The while() loop runs only once. When the action happens second time I see a message "Polling is successful".
Should I add IN_MODIFY as a mask for inotify?
If it matters - this code is running inside std::thread.

Turns out that polling plus reading inotify is not thread-safe.
So to overcome this I had to add inotify_init() to every thread. So now every thread have its own inotify file descriptor. And now it looks like program works.
Thank you everybody for reading and trying to help.

Well, I have an answer, it's not a short one and it's directly from the man 7 inotify page in linked above in the comments. Your two pending questions from the narrowing of your questions in the comments are:
if I call remove() on the file - does this mean that the watch in the directory for file creation will be removed?; and
if it is true can you give me some pseudocode for the workaround?
The answer to (1) is Yes, but the watch may be recycled causing the next file/dir to which it is assigned to potentially read the pending requests for the file/dir that was closed/deleted:
When a watch descriptor is removed by calling inotify_rm_watch(2) (or
because a watch file is deleted or the filesystem that contains it is
unmounted), ...
(caveat to be aware of)
... any pending unread events for that watch descriptor
remain available to read. As watch descriptors are subsequently
allocated with inotify_add_watch(2), the kernel cycles through the
range of possible watch descriptors (0 to INT_MAX) incrementally.
When allocating a free watch descriptor, no check is made to see
whether that watch descriptor number has any pending unread events in
the inotify queue. Thus, it can happen that a watch descriptor is
reallocated even when pending unread events exist for a previous
incarnation of that watch descriptor number, with the result that the
application might then read those events and interpret them as
belonging to the file associated with the newly recycled watch
descriptor. In practice, the likelihood of hitting this bug may be
extremely low, since it requires that an application cycle through
INT_MAX watch descriptors, release a watch descriptor while leaving
unread events for that watch descriptor in the queue, and then
recycle that watch descriptor. For this reason, and because there
have been no reports of the bug occurring in real-world applications,
as of Linux 3.15, no kernel changes have yet been made to eliminate
this possible bug.
(I doubt you will ever have that many watches open)
In answer to the second question, the pseudo code would simply be to check the mask for:
IN_DELETE_SELF
Watched file/directory was itself deleted. (This event
also occurs if an object is moved to another filesystem,
since mv(1) in effect copies the file to the other
filesystem and then deletes it from the original filesys‐
tem.) In addition, an IN_IGNORED event will subsequently
be generated for the watch descriptor.
So you can check with either the mask IN_DELETE_SELF or IN_IGNORED if the file/directory being watched is deleted.

I can't see exactly what is wrong with your code since you haven't provided an mcve, but watching a directory should give you all of the subscribed events for the directory until you stop reading them.
Here's an example of using inotify to watch a directory...
#include <iostream>
#include <unistd.h>
#include <sys/inotify.h>
int main(int argc, char *argv[]) {
const int ifd = inotify_init();
const int iwd = inotify_add_watch(ifd, "/tmp/inotify", IN_CREATE);
char buf[4096];
while (read(ifd, buf, sizeof(buf)) > 0) {
const struct inotify_event* ie = (const struct inotify_event*) buf;
std::cout << ie->name << std::endl;
}
return 0;
}
(jason#pi) [4868] ~/tmp touch /tmp/inotify/foo
(jason#pi) [4880] ~/tmp touch /tmp/inotify/bar
(jason#pi) [4881] ~/tmp rm /tmp/inotify/foo
(jason#pi) [4882] ~/tmp touch /tmp/inotify/foo
(jason#pi) [4879] ~/tmp ./so
foo
bar
foo

Related

Using timer with zmq

I am working on a project where I have to use zmq_poll. But I did not completely understand what it does.
So I also tried to implement it:
zmq_pollitem_t timer_open(void){
zmq_pollitem_t items[1];
if( items[0].socket == nullptr ){
printf("error socket %s: %s\n", zmq_strerror(zmq_errno()));
return;
}
else{
items[0].socket = gsock;
}
items[0].fd = -1;
items[0].events = ZMQ_POLLIN;
// get a timer
items[0].fd = timerfd_create( CLOCK_REALTIME, 0 );
if( items[0].fd == -1 )
{
printf("timerfd_create() failed: errno=%d\n", errno);
items[0].socket = nullptr;
return;
}
int rc = zmq_poll(items,1,-1);
if(rc == -1){
printf("error poll %s: %s\n", zmq_strerror(zmq_errno()));
return;
}
else
return items[0];
}
I am very new to this topic and I have to modify an old existing project and replace the functions with the one of zmq. On other websites I saw examples where they used two items and the zmq_poll function in an endless loop. I have read the documentation but still could not properly understand how this works. And these are the other two functions I have implemented. I do not know if it is the correct way to implement it like this:
void timer_set(zmq_pollitem_t items[] , long msec, ipc_timer_mode_t mode ) {
struct itimerspec t;
...
timerfd_settime( items[0].fd , 0, &t, NULL );
}
void timer_close(zmq_pollitem_t items[]){
if( items[0].fd != -1 )
close(items[0].fd);
items[0].socket = nullptr;
}
I am not sure if I need the zmq_poll function because I am using a timer.
EDIT:
void some_function_timer_example() {
// We want to wait on two timers
zmq_pollitem_t items[2] ;
// Setup first timer
ipc_timer_open_(&items[0]);
ipc_timer_set_(&items[0], 1000, IPC_TIMER_ONE_SHOT);
// Setup second timer
ipc_timer_open_(&items[1]);
ipc_timer_set_(&items[1], 1000, IPC_TIMER_ONE_SHOT);
// Now wait for the timers in a loop
while (1) {
//ipc_timer_set_(&items[0], 1000, IPC_TIMER_REPEAT);
//ipc_timer_set_(&items[1], 5000, IPC_TIMER_REPEAT);
int rc = zmq_poll (items, 2, -1);
assert (rc >= 0); /* Returned events will be stored in items[].revents */
if (items [0].revents & ZMQ_POLLIN) {
// Process task
std::cout << "revents: 1" << std::endl;
}
if (items [1].revents & ZMQ_POLLIN) {
// Process weather update
std::cout << "revents: 2" << std::endl;
}
}
}
Now it still prins very fast and is not waiting. It is still waiting only in the beginning. And when the timer_set is inside the loop it waits properly, only if the waiting time is the same like: ipc_timer_set(&items[1], 1000,...) and ipctimer_set(&items[0], 1000,...)
So how do I have to change this? Or is this the correct behavior?
zmq_poll works like select, but it allows some additional stuff. For instance you can select between regular synchronous file descriptors, and also special async sockets.
In your case you can use the timer fd as you have tried to do, but you need to make a few small changes.
First you have to consider how you will invoke these timers. I think the use case is if you want to create multiple timers and wait for them. This would be typically the function in yuor current code that might be using a loop for the timer (either using select() or whatever else they might be doing).
It would be something like this:
void some_function() {
// We want to wait on two timers
zmq_pollitem items[2];
// Setup first timer
ipc_timer_open(&item[0]);
ipc_timer_set(&item[0], 1000, IPC_TIMER_ONE_REPEAT);
// Setup second timer
ipc_timer_open(&item[1]);
ipc_timer_set(&item[1], 5000, IPC_TIMER_ONE_SHOT);
// Now wait for the timers in a loop
while (1) {
int rc = zmq_poll (items, 2, -1);
assert (rc >= 0); /* Returned events will be stored in items[].revents */
}
}
Now, you need to fix the ipc_timer_open. It will be very simple - just create the timer fd.
// Takes a pointer to pre-allocated zmq_pollitem_t and returns 0 for success, -1 for error
int ipc_timer_open(zmq_pollitem_t *items){
items[0].socket = NULL;
items[0].events = ZMQ_POLLIN;
// get a timer
items[0].fd = timerfd_create( CLOCK_REALTIME, 0 );
if( items[0].fd == -1 )
{
printf("timerfd_create() failed: errno=%d\n", errno);
return -1; // error
}
return 0;
}
Edit: Added as reply to comment, since this is long:
From the documentation:
If both socket and fd are set in a single zmq_pollitem_t, the ØMQ socket referenced by socket shall take precedence and the value of fd shall be ignored.
So if you are passing the fd, you have to set socket to NULL. I am not even clear where gsock is coming from. Is this in the documentation? I couldn't find it.
And when will it break out of the while(1) loop?
This is application logic, and you have to code according to what you require. zmq_poll just keeps returning everytime one of the timer hits. In this example, every second the zmq_poll returns because the first timer (which is a repeat) keeps triggering. But at 5 seconds, it will also return because of the second timer (which is a one shot). Its up to you to decide when you exit the loop. Do you want this to go infinitely? Do you need to check for a different condition to exit the loop? Do you want to do this for say 100 times and then return? You can code whatever logic you want on top of this code.
And what kind of events are returned back
ZMQ_POLLIN since timer fds behave like readable file descriptors.

Complex getchar() operation in omxplayer

Recently I have been trying to redirect the output of a named pipe into a running instance of omxplayer (see here for a minimum working example that doesn't control omxplayer even though it should). I suspected that it had something to do with reading and writing to pipes --perhaps a new line got appended-- so I got some help and wrote a C program which writes to a pipe and reads from it (see here) but turns out that it is not a read/write error either. So I went and traced omxplayer's code thinking that no matter how complicated it was, eventually there has to be a place which has standard C++ code which reads user input, and thank god I found it. Here is the method which, to the best of my understanding, is responsible for getting user input and preparing it for the Dbus to do all its heavenly goodness:
void Keyboard::Process()
{
while(!m_bStop && conn && dbus_connection_read_write_dispatch(conn, 0))
{
int ch[8];
int chnum = 0;
while ((ch[chnum] = getchar()) != EOF) chnum++;
if (chnum > 1) ch[0] = ch[chnum - 1] | (ch[chnum - 2] << 8);
if (m_keymap[ch[0]] != 0)
send_action(m_keymap[ch[0]]);
else
Sleep(20);
}
}
As far as I can glean, the while(!m_bStop... is a conditional just to make sure things are still working, m_keymap is a cypher which matches integers such as 'p' or 'q' to enum values such as ACTION_PAUSE and ACTION_QUIT, and I presume send_action() just gets the ball rolling.
Questions:
Here is what I do not understand:
How is EOF detected when I am not even pressing Enter --> while ((ch[chnum] = getchar()) != EOF) (in case you are confused by this, when a movie is playing I press p to pause the film, not p and then Enter or Ctrl+D). I have attached a small script below labeled getchar.c which illustrates how it is looping forever.
Why are we looping for potentially more than 8 iterations in the while loop while ((ch[chnum] = getchar()) != EOF) chnum++ when the array ch is only of length 8?
This might be implementation specific, but why an array of size 8 when all inputs are guaranteed to be 1 character long (I can both see it here in the map, and by the fact that keys are processed instantly). Is this in any way related to the arrow keys and the escape key?
Assuming it is possible to have more than 1 character, somehow, what is this line supposed to do if (chnum > 1) ch[0] = ch[chnum - 1] | (ch[chnum - 2] << 8)?
Knowing, finally, how omxplayer reads user generated input, can anyone tell me why my simple script, labelled omxplayer_test.c, does not succeed in controlling the player?
getchar.c:
#include <stdio.h>
int main( int argc, char *argv[] ) {
int ch [ 8 ];
int chnum = 0;
while ( ( ch [ chnum ] = getchar() ) != EOF ) chnum++;
printf ( "You will never make it here!\n" );
return 0;
}
omxplayer_test.c:
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdbool.h>
#define PIPE_PATH "testpipe"
#define VIDEO_PATH "Matrix.mkv"
#define P_MESSAGE "p"
#define Q_MESSAGE "q"
#define I_MESSAGE "."
#define VIDEO_PLAYER "omxplayer"
#define SLEEP_PERIOD 2L
int main()
{
int fd;
pid_t pid;
pid_t wpid;
int status;
char shellCmd [ 1000 ];
struct timespec time1, time2; //used for sleeping
bool parent;
char c;
parent = false;
//Make pipe BEFORE forking
mkfifo ( PIPE_PATH, 0666 );
if ( ( pid = fork () ) < 0 )
{
perror ( "Fork Failed\n" );
return -1;
}
else if ( pid == 0 )
{ //first child launches the movie
sprintf ( shellCmd, "%s %s < %s >/dev/null 2>&1", VIDEO_PLAYER, VIDEO_PATH, PIPE_PATH );
if ( system ( shellCmd ) == -1 )
{
printf ( "Error: %s\n", shellCmd );
fflush(stdout);
}
printf ("First child finished\n");
fflush (stdout);
}
else
{
if ( ( pid = fork () ) < 0 )
{
perror ( "Fork Failed\n" );
return -1;
}
else if ( pid == 0 )
{ //second child waits x seconds then pauses/unpauses/quits movie
time1.tv_sec = SLEEP_PERIOD; //sleep for x seconds
time1.tv_nsec = 0L; //Dont worry about milli seconds
nanosleep ( &time1, &time2 );
//Suprisingly, this signal which gets ball rolling works
fd = open ( PIPE_PATH, O_WRONLY );
write ( fd, I_MESSAGE, sizeof ( I_MESSAGE ) );
nanosleep ( &time1, &time2 );
printf ( "Sleep over, pausing movie\n");
fflush(stdout);
write ( fd, I_MESSAGE, sizeof ( P_MESSAGE ) );
nanosleep ( &time1, &time2 );
printf ( "Sleep over, unpausing movie\n");
fflush(stdout);
write ( fd, P_MESSAGE, sizeof ( P_MESSAGE ) );
nanosleep ( &time1, &time2 );
printf ( "Sleep over, quiting movie\n");
fflush(stdout);
write ( fd, Q_MESSAGE, sizeof ( Q_MESSAGE ) );
close ( fd );
printf ("Second child finished\n");
fflush (stdout);
}
else
{
parent = true;
}
}
while ( ( wpid = wait ( &status ) ) > 0 )
{
printf ( "Exit status of %d was %d (%s)\n", ( int ) wpid, status, ( status == 0 ) ? "accept" : "reject" );
fflush(stdout);
}
if ( parent == true )
{
printf ("deleting pipe\n");
fflush(stdout);
unlink ( PIPE_PATH );
}
return 0;
}
#puk I stumbled upon your 'old´ question but in case you didn't have it answered by yourself.
Look at something similar on https://github.com/popcornmix/omxplayer/issues/131. As omxplayer releases 0.3.x comes from this 'popcornmix' repository, I will answer these questions there because it is a better place for your omxplayer questions ;)
But I will answer the question why your omxplayer_test.c isn't working here, as it is your code that let omxplayer fail :) Strictly not true, as it is a current issue in omxplayer :(
The sending of I_MESSAGE surprises me the most, as I don't know of any keyboard input handling on a '.' character. On the other hand, 'i' commands omxplayer to go to the previous chapter. So if you didn't key-mapped anything on the '.' input key or meant the real 'i' actions, just leave that out; it isn't (and shouldn't be) needed to start omxplayer.
To pause omxplayer, your typo'd it with the I_MESSAGE in stead of P_MESSAGE.
But sending commands to omxplayer with the write() and the sizeof() of the message causes the same effect as mentioned in forementioned issue. The sizeof() of an x_MESSAGE gives 2 as result back, and not 1 (one)! The sizeof() counts in the '\0'-character of a (c-coded) string; e.g. "p" is stored as 'p''\0', so at least 2 characters. So use strlen() (which needs #include <string.h>) in stead, as it will sent only the x_MESSAGE character.

Opening a console from app

I am working on a C and C++ app that uses some graphical engine to handle gtk windows (Opencv/highgui). This app does some minor output to stdout/cout.
On Windows, starting this kind of app from the desktop automatically opens a console, showing the user what is been written on standard output, either with "printf()" or "std::cout".
On Linux, if I start it from a previously opened console, no trouble. But if I start it through the desktop (double-click), then linux doesn't open an associated console, and data written on stdout/cout is lost.
Seems that this is the normal behaviour on Linux (?).
I would like to automatically open a console from my app, when compiled on a linux platform.
This seems like a dupe of this one, the point is, it doesn't work! I have at present the following code:
#ifndef __WIN32
filebuf* console = new filebuf();
console->open( "/dev/tty", ios::out );
if( !console->is_open() )
cerr << "Can't open console" << endl;
else
cout.ios::rdbuf(console);
#endif
(cerr is redirected in a file using freopen() )
I keep getting "Can't open console". I tried replacing the console name:
console->open( "/dev/console", ios::out );
but that didn't change.
Am I in the right direction? What can I try next? Should I try to open specifically the terminal application (xterm)? But then, how could I "connect" that console with my app?
Solution 1
Very simple solution you might not like: have a script that runs your application in a terminal using gnome-terminal -x <your_program> <your_args>. Double-clicking the script will open the terminal.
Solution 2
A bit more involved solution add a '--noconsole' argument to your application. If the argument is present, just run your application. If '--noconsole' is not present:
if( fork() == 0 ) {
execlp("gnome-terminal", "gnome-terminal", "-x", argv[0], "--noconsole", NULL );
} else {
exit( 0 );
}
This creates a child process in which it runs the application in gnome-terminal using the --noconsole arugment. Makes sense? A bit hacky, but hey, it works.
Solution 3
This one is the trickiest solution, but in some ways more elegant. The idea is to redirect our stdout to a file and create a terminal running tail -f <file_name> --pid=<parent_pid>. This prints the output of the parent process and terminates when the parent dies.
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
// Create terminal and redirect output to it, returns 0 on success,
// -1 otherwise.
int make_terminal() {
char pidarg[256]; // the '--pid=' argument of tail
pid_t child; // the pid of the child proc
pid_t parent; // the pid of the parent proc
FILE* fp; // file to which output is redirected
int fn; // file no of fp
// Open file for redirection
fp = fopen("/tmp/asdf.log","w");
fn = fileno(fp);
// Get pid of current process and create string with argument for tail
parent = getpid();
sprintf( pidarg, "--pid=%d", parent );
// Create child process
child = fork();
if( child == 0 ) {
// CHILD PROCESS
// Replace child process with a gnome-terminal running:
// tail -f /tmp/asdf.log --pid=<parent_pid>
// This prints the lines outputed in asdf.log and exits when
// the parent process dies.
execlp( "gnome-terminal", "gnome-terminal", "-x", "tail","-f","/tmp/asdf.log", pidarg, NULL );
// if there's an error, print out the message and exit
perror("execlp()");
exit( -1 );
} else {
// PARENT PROCESS
close(1); // close stdout
int ok = dup2( fn, 1 ); // replace stdout with the file
if( ok != 1 ) {
perror("dup2()");
return -1;
}
// Make stdout flush on newline, doesn't happen by default
// since stdout is actually a file at this point.
setvbuf( stdout, NULL, _IONBF, BUFSIZ );
}
return 0;
}
int main( int argc, char *argv[]) {
// Attempt to create terminal.
if( make_terminal() != 0 ) {
fprintf( stderr, "Could not create terminal!\n" );
return -1;
}
// Stuff is now printed to terminal, let's print a message every
// second for 10 seconds.
int i = 0;
while( i < 10 ) {
printf( "iteration %d\n", ++ i );
sleep( 1 );
}
return 0;
}
Your examples all "open" a console - in the sense that one opens a file. This doesn't do anything to a gui. If you want to do that you will have to open a gtk window and direct the output to it.

Linux: Executing child process with piped stdin/stdout

Using Linux and C++, I would like a function that does the following:
string f(string s)
{
string r = system("foo < s");
return r;
}
Obviously the above doesn't work, but you get the idea. I have a string s that I would like to pass as the standard input of a child process execution of application "foo", and then I would like to record its standard output to string r and then return it.
What combination of Linux syscalls or POSIX functions should I use?
I'm using Linux 3.0 and do not need the solution to work with older systems.
The code provided by eerpini does not work as written. Note, for example, that the pipe ends that are closed in the parent are used afterwards. Look at
close(wpipefd[1]);
and the subsequent write to that closed descriptor. This is just transposition, but it shows this code has never been used. Below is a version that I have tested. Unfortunately, I changed the code style, so this was not accepted as an edit of eerpini's code.
The only structural change is that I only redirect the I/O in the child (note the dup2 calls are only in the child path.) This is very important, because otherwise the parent's I/O gets messed up. Thanks to eerpini for the initial answer, which I used in developing this one.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#define PIPE_READ 0
#define PIPE_WRITE 1
int createChild(const char* szCommand, char* const aArguments[], char* const aEnvironment[], const char* szMessage) {
int aStdinPipe[2];
int aStdoutPipe[2];
int nChild;
char nChar;
int nResult;
if (pipe(aStdinPipe) < 0) {
perror("allocating pipe for child input redirect");
return -1;
}
if (pipe(aStdoutPipe) < 0) {
close(aStdinPipe[PIPE_READ]);
close(aStdinPipe[PIPE_WRITE]);
perror("allocating pipe for child output redirect");
return -1;
}
nChild = fork();
if (0 == nChild) {
// child continues here
// redirect stdin
if (dup2(aStdinPipe[PIPE_READ], STDIN_FILENO) == -1) {
exit(errno);
}
// redirect stdout
if (dup2(aStdoutPipe[PIPE_WRITE], STDOUT_FILENO) == -1) {
exit(errno);
}
// redirect stderr
if (dup2(aStdoutPipe[PIPE_WRITE], STDERR_FILENO) == -1) {
exit(errno);
}
// all these are for use by parent only
close(aStdinPipe[PIPE_READ]);
close(aStdinPipe[PIPE_WRITE]);
close(aStdoutPipe[PIPE_READ]);
close(aStdoutPipe[PIPE_WRITE]);
// run child process image
// replace this with any exec* function find easier to use ("man exec")
nResult = execve(szCommand, aArguments, aEnvironment);
// if we get here at all, an error occurred, but we are in the child
// process, so just exit
exit(nResult);
} else if (nChild > 0) {
// parent continues here
// close unused file descriptors, these are for child only
close(aStdinPipe[PIPE_READ]);
close(aStdoutPipe[PIPE_WRITE]);
// Include error check here
if (NULL != szMessage) {
write(aStdinPipe[PIPE_WRITE], szMessage, strlen(szMessage));
}
// Just a char by char read here, you can change it accordingly
while (read(aStdoutPipe[PIPE_READ], &nChar, 1) == 1) {
write(STDOUT_FILENO, &nChar, 1);
}
// done with these in this example program, you would normally keep these
// open of course as long as you want to talk to the child
close(aStdinPipe[PIPE_WRITE]);
close(aStdoutPipe[PIPE_READ]);
} else {
// failed to create child
close(aStdinPipe[PIPE_READ]);
close(aStdinPipe[PIPE_WRITE]);
close(aStdoutPipe[PIPE_READ]);
close(aStdoutPipe[PIPE_WRITE]);
}
return nChild;
}
Since you want bidirectional access to the process, you would have to do what popen does behind the scenes explicitly with pipes. I am not sure if any of this will change in C++, but here is a pure C example :
void piped(char *str){
int wpipefd[2];
int rpipefd[2];
int defout, defin;
defout = dup(stdout);
defin = dup (stdin);
if(pipe(wpipefd) < 0){
perror("Pipe");
exit(EXIT_FAILURE);
}
if(pipe(rpipefd) < 0){
perror("Pipe");
exit(EXIT_FAILURE);
}
if(dup2(wpipefd[0], 0) == -1){
perror("dup2");
exit(EXIT_FAILURE);
}
if(dup2(rpipefd[1], 1) == -1){
perror("dup2");
exit(EXIT_FAILURE);
}
if(fork() == 0){
close(defout);
close(defin);
close(wpipefd[0]);
close(wpipefd[1]);
close(rpipefd[0]);
close(rpipefd[1]);
//Call exec here. Use the exec* family of functions according to your need
}
else{
if(dup2(defin, 0) == -1){
perror("dup2");
exit(EXIT_FAILURE);
}
if(dup2(defout, 1) == -1){
perror("dup2");
exit(EXIT_FAILURE);
}
close(defout);
close(defin);
close(wpipefd[1]);
close(rpipefd[0]);
//Include error check here
write(wpipefd[1], str, strlen(str));
//Just a char by char read here, you can change it accordingly
while(read(rpipefd[0], &ch, 1) != -1){
write(stdout, &ch, 1);
}
}
}
Effectively you do this :
Create pipes and redirect the stdout and stdin to the ends of the two pipes (note that in linux, pipe() creates unidirectional pipes, so you need to use two pipes for your purpose).
Exec will now start a new process which has the ends of the pipes for stdin and stdout.
Close the unused descriptors, write the string to the pipe and then start reading whatever the process might dump to the other pipe.
dup() is used to create a duplicate entry in the file descriptor table. While dup2() changes what the descriptor points to.
Note : As mentioned by Ammo# in his solution, what I provided above is more or less a template, it will not run if you just tried to execute the code since clearly there is a exec* (family of functions) missing, so the child will terminate almost immediately after the fork().
Ammo's code has some error handling bugs. The child process is returning after dup failure instead of exiting. Perhaps the child dups can be replaced with:
if (dup2(aStdinPipe[PIPE_READ], STDIN_FILENO) == -1 ||
dup2(aStdoutPipe[PIPE_WRITE], STDOUT_FILENO) == -1 ||
dup2(aStdoutPipe[PIPE_WRITE], STDERR_FILENO) == -1
)
{
exit(errno);
}
// all these are for use by parent only
close(aStdinPipe[PIPE_READ]);
close(aStdinPipe[PIPE_WRITE]);
close(aStdoutPipe[PIPE_READ]);
close(aStdoutPipe[PIPE_WRITE]);

Read() from file descriptor hangs

Hey, hopefully this should be my last PTY-related question and I can move onto more exciting issues. (c;
Here's a set of small functions I have written for creating and reading/writing to a pty: http://pastebin.com/m4fcee34d The only problem is that they don't work! After I run the initializer and writeToPty( "ls -l" ) , 'output' from readFromPty is still empty.
Ubuntu, QT C++
EDITED: Ok, I can confirm all this stuff works except for the read loop. In the debuggers' locals/watchers tab it shows that the QString 'output' actually does get the right data put in it, but after it ( the read() ) runs out of characters from the output it runs and then hangs. What is going on and how can I fix it?
Thanks! (c:
#include <iostream>
#include <unistd.h>
#include <utmp.h>
#include <pty.h>
#include <QString>
#include <QThread>
// You also need libutil in your .pro file for this to compile.
class CMkPty
{
public:
CMkPty( int *writeChannel, int *readChannel );
~CMkPty();
int runInPty( char *command );
int writeToPty( char *input );
int readFromPty( QString output );
int m_nPid;
private:
int m_nMaster, m_nSlave, m_nPosition, m_nBytes;
char *m_chName;
void safe_print( char *s );
char m_output;
};
CMkPty::CMkPty( int *masterFD, int *slaveFD )
{
openpty( &m_nMaster, &m_nSlave, (char*)0, __null, __null );
m_nPid = fork();
*masterFD = m_nMaster;
*slaveFD = m_nSlave;
if( m_nPid == 0 )
{
login_tty( m_nSlave );
execl( "/bin/bash", "-l", (char*)0 );
return;
}
else if( m_nPid > 0 )
{
return;
}
else if( m_nPid < 0 )
{
std::cout << "Failed to fork." ;
return;
}
}
CMkPty::~CMkPty()
{
close( m_nMaster );
close( m_nSlave );
}
int CMkPty::writeToPty( char *szInput )
{
int nWriteTest;
write( m_nMaster, szInput, sizeof( szInput ) );
nWriteTest = write( m_nMaster, "\n", 1 );
if( nWriteTest < 0 )
{
std::cout << "Write to PTY failed" ;
return -1;
}
return 0;
}
int CMkPty::readFromPty( QString output )
{
char buffer[ 160 ];
m_nBytes = sizeof( buffer );
while ( ( m_nPosition = read( m_nMaster, buffer, m_nBytes ) ) > 0 )
{
buffer[ m_nPosition ] = 0;
output += buffer;
}
return 0;
}
EDIT: Here's a link to the question with the code that finally worked for me.
I'm note entirely familiar with posix, but after reading this page http://pwet.fr/man/linux/fonctions_bibliotheques/posix/read I had some insight. What's more, I don't see you adjusting your M_nBytes value if you haven't read as much as you were expecting on the first pass of the loop.
edit: from that link, perhaps this will be of some help:
If some process has the pipe open for writing and O_NONBLOCK is clear, read() shall block the calling thread until some data is written or the pipe is closed by all processes that had the pipe open for writing.
When attempting to read a file (other than a pipe or FIFO) that supports non-blocking reads and has no data currently available:
*
If O_NONBLOCK is clear, read() shall block the calling thread until some data becomes available.
so essentially, if you're not in an error state, and you tell it to keep reading, it will block until it finds something to read.