Complex getchar() operation in omxplayer - c++

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.

Related

How to open and close files properly in C++?

I want to open some files, redirect the output of them and then go back to previous situation, so I wrote:
int fd = open("test.txt", O_WRONLY | O_CREAT, 0666);
dup(1);
dup2(3, 1);
close(3);
//call my func() to print in std::cout which will write to test.txt
dup2(4, 1);//Close file
I know that the default input channel is in index 0, output channel (screen) in index 1 and stderr in index 2 so fd will get 3, dup 1 will create another pointer to output channel in index 4, then I replace screen with my file and close that isn't needed.
My code is correct, but did I forget anything else or can it be made shorter/ more clear?
Most importantly, in case one line fails I want to return false but this will be a lot of checks and in every advance step we need to close more. How can I solve this problem?
What if I wanted in case of failure to make everything go back to default situation?
Please Note, I only know and want to use open,close,dup,dup2
Most importantly it is the duty of a programmer to write reliable, faultless code, and this includes thorough error checking. Error checking often makes for large parts of the code, especially when user interaction is part of the program. But, there is no way around it, if you want to be/become a good programmer.
That said, it is easy (in a first step) to change your code so that it does not depend on specific file descriptor numbers (not tested).
int fileFd;
int redirectFd1;
int redirectFd2;
fileFd = open( "test.txt", O_WRONLY | O_CREAT, 0666 );
redirectFd1 = dup( 1 );
redirectFd2 = dup2( fileFd, 1 );
close( fileFd );
//call my func() to print in std::cout which will write to test.txt
dup2( redirectFd1, 1 );//Close file
Next step is to add error checking.
int fileFd;
int redirectFd1;
int redirectFd2;
if ( ( fileFd = open( "test.txt", O_WRONLY | O_CREAT, 0666 ) ) == -1 ) {
// open() failed. Do appropriate error handling here...
exit( 1 );
}
if ( ( redirectFd1 = dup( 1 ) ) == -1 ) {
// dup() failed. Do appropriate error handling here...
// Since we arrvied here, fileFd *is* open. So we need to close it.
// But redirectFd1 is *not* open
close( fileFd );
exit(1);
}
if ( ( redirectFd2 = dup2( fileFD, 1 ) ) == -1 ) {
// dup() failed. Do appropriate error handling here...
// Since we arrvied here, fileFd *and* redirectFd1 *are* open.
// So we need to close them.
// But redirectFd2 is *not* open
close( fileFd );
close( redirectFd1 );
exit(1);
}
close( fileFd );
//call my func() to print in std::cout which will write to test.txt
if ( dup2( redirectFD1, 1 ) == -1 ) {
// dup2() failed. Do appropriate error handling here...
close( redirectFd1 );
}
close( redirectFd1 );
One can argue whether checking for errors on the close( fileFd ) statement is needed or nor. Fact is that open() succeeded, so it would be very unsusual for close() to fail here. One can also argue whether checking for errors on the last dup2() is needed or not.
In general, I keep track of open files, and take care of closing in case of errros in a clean-up routine. The code might look like this:
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
int fileFd;
bool fileFdIsOpen = false;
int redirectFd1;
bool redirectFd1IsOpen = false;
int redirectFd2;
bool redirectFd2IsOpen = false;
void cleanup();
int main( int argc, char* argv[] ) {
int stdoutFd = fileno( stdout );
if ( ( fileFd = open( "test.txt", O_WRONLY | O_CREAT, 0666 ) ) == -1 ) {
// open() failed. Do appropriate error handling here...
exit( 1 );
}
fileFdIsOpen = true;
if ( ( redirectFd1 = dup( stdoutFd ) ) == -1 ) {
// dup() failed. Do appropriate error handling here...
cleanup();
exit(1);
}
redirectFd1IsOpen = true;
if ( ( redirectFd2 = dup2( fileFd, stdoutFd ) ) == -1 ) {
// dup() failed. Do appropriate error handling here...
cleanup();
exit(1);
}
redirectFd2IsOpen = true;
close( fileFd );
fileFdIsOpen = false;
//call my func() to print in std::cout which will write to test.txt
if ( dup2( redirectFd1, stdoutFd ) == -1 ) {
// dup2() failed. Do appropriate error handling here...
cleanup();
}
cleanup();
exit (0);
}
void cleanup() {
if ( fileFdIsOpen ) {
close( fileFd );
fileFdIsOpen = false;
}
if ( redirectFd1IsOpen ) {
close( redirectFd1 );
redirectFd1IsOpen = false;
}
if ( redirectFd2IsOpen ) {
close( redirectFd2 );
redirectFd2IsOpen = false;
}
}
I know that default input channel is in index 0, output channel
(screen) in index 1 and stderr in index 2 so fd will get 3
That's undetermined, process can be run in away those ids would be altered and starting descriptor also might be different from stderr+1. Theremore, on Linux there are situation where process can be created with none open, but that's different.
In C header <stdio.h> defines those variables that are supposed to be initialized by runtime library.
extern FILE *stdin;
extern FILE *stdout;
extern FILE *stderr;
Code you had shown is pretty much C code, not C++, but you can use <cstdio> and fileno function to obtain descriptor
int fileno(FILE *stream);
You have to check returned results of fileno, dup and open to be sure that you program didn't run into some limitations imposed on it, and errno variable would tell you a reason. On some other platforms, like Windows, more appropriate error reporting functions do exist.

Is inotify a one shot only solution

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

Single Producer / Consumer Ring Buffer in Shared Memory

Recently I've been playing about with using shared memory for IPC. One thing I've been trying to implement is a simple ring buffer with 1 process producing and 1 process consuming. Each process has its own sequence number to track its position. These sequence numbers are updated using atomic ops to ensure the correct values are visible to the other process. The producer will block once the ring buffer is full. The code is lock free in that no semaphores or mutexes are used.
Performance wise I'm getting roughly 20 million messages per second on my rather modest VM - Pretty happy with that :)
What I'm curious about how 'correct' my code is. Can anyone spot any inherent issues / race conditions? Here's my code. Thanks in advance for any comments.
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include <string.h>
#define SHM_ID "/mmap-test"
#define BUFFER_SIZE 4096
#define SLEEP_NANOS 1000 // 1 micro
struct Message
{
long _id;
char _data[128];
};
struct RingBuffer
{
size_t _rseq;
char _pad1[64];
size_t _wseq;
char _pad2[64];
Message _buffer[BUFFER_SIZE];
};
void
producerLoop()
{
int size = sizeof( RingBuffer );
int fd = shm_open( SHM_ID, O_RDWR | O_CREAT, 0600 );
ftruncate( fd, size+1 );
// create shared memory area
RingBuffer* rb = (RingBuffer*)mmap( 0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0 );
close( fd );
// initialize our sequence numbers in the ring buffer
rb->_wseq = rb->_rseq = 0;
int i = 0;
timespec tss;
tss.tv_sec = 0;
tss.tv_nsec = SLEEP_NANOS;
while( 1 )
{
// as long as the consumer isn't running behind keep producing
while( (rb->_wseq+1)%BUFFER_SIZE != rb->_rseq%BUFFER_SIZE )
{
// write the next entry and atomically update the write sequence number
Message* msg = &rb->_buffer[rb->_wseq%BUFFER_SIZE];
msg->_id = i++;
__sync_fetch_and_add( &rb->_wseq, 1 );
}
// give consumer some time to catch up
nanosleep( &tss, 0 );
}
}
void
consumerLoop()
{
int size = sizeof( RingBuffer );
int fd = shm_open( SHM_ID, O_RDWR, 0600 );
if( fd == -1 ) {
perror( "argh!!!" ); return;
}
// lookup producers shared memory area
RingBuffer* rb = (RingBuffer*)mmap( 0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0 );
// initialize our sequence numbers in the ring buffer
size_t seq = 0;
size_t pid = -1;
timespec tss;
tss.tv_sec = 0;
tss.tv_nsec = SLEEP_NANOS;
while( 1 )
{
// while there is data to consume
while( seq%BUFFER_SIZE != rb->_wseq%BUFFER_SIZE )
{
// get the next message and validate the id
// id should only ever increase by 1
// quit immediately if not
Message msg = rb->_buffer[seq%BUFFER_SIZE];
if( msg._id != pid+1 ) {
printf( "error: %d %d\n", msg._id, pid ); return;
}
pid = msg._id;
++seq;
}
// atomically update the read sequence in the ring buffer
// making it visible to the producer
__sync_lock_test_and_set( &rb->_rseq, seq );
// wait for more data
nanosleep( &tss, 0 );
}
}
int
main( int argc, char** argv )
{
if( argc != 2 ) {
printf( "please supply args (producer/consumer)\n" ); return -1;
} else if( strcmp( argv[1], "consumer" ) == 0 ) {
consumerLoop();
} else if( strcmp( argv[1], "producer" ) == 0 ) {
producerLoop();
} else {
printf( "invalid arg: %s\n", argv[1] ); return -1;
}
}
Seems correct to me at a first glance. I realize that you are happy with the performance but a fun experiment might be to use something more light weight than a __sync_fetch_and_add. AFAIK it is a full memory barrier, which is expensive. Since there is a single producer and a single consumer, a release and a corresponding acquire operation should give you better performance. Facebook's Folly library has a single producer single consumer queue that uses the new C++11 atomics here: https://github.com/facebook/folly/blob/master/folly/ProducerConsumerQueue.h

how to capture result from system() in C/C++ [duplicate]

This question already has answers here:
Closed 13 years ago.
Possible Duplicate:
How can I run an external program from C and parse its output?
Hi,
Could someone please tell us how to capture a result when executing system() function ?
Actually I wrote a c++ program that displays the machine's IP address, called "ipdisp" and I want when a sever program executes this ipdisp program, the server captes the display IP address. So, is this possible? if yes, how?
thanks for your replies
Yes, you can do this but you can't use system(), you'll have to use popen() instead. Something like:
FILE *f = popen("ipdisp", "r");
while (!feof(f)) {
// ... read lines from f using regular stdio functions
}
pclose(f);
Greg is not entirely correct. You can use system, but it's a really bad idea. You can use system by writing the output of the command to a temporary file and then reading the file...but popen() is a much better approach. For example:
#include <stdlib.h>
#include <stdio.h>
void
die( char *msg ) {
perror( msg );
exit( EXIT_FAILURE );
}
int
main( void )
{
size_t len;
FILE *f;
int c;
char *buf;
char *cmd = "echo foo";
char *path = "/tmp/output"; /* Should really use mkstemp() */
len = (size_t) snprintf( buf, 0, "%s > %s", cmd, path ) + 1;
buf = malloc( len );
if( buf == NULL ) die( "malloc");
snprintf( buf, len, "%s > %s", cmd, path );
if( system( buf )) die( buf );
f = fopen( path, "r" );
if( f == NULL ) die( path );
printf( "output of command: %s\n", buf );
while(( c = getc( f )) != EOF )
fputc( c, stdout );
return EXIT_SUCCESS;
}
There are lots of problems with this approach...(portability of the syntax for redirection, leaving the file on the filesystem, security issues with other processes reading the temporary file, etc, etc.)

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.