Serial port polling not working on Beaglebone Black - c++

I simply cannot get the poll() function to respond to data being sent over UART1 / /dev/ttyO1. Code as follows, maybe someone can spot my silly mistake?
#include <stdio.h>
#include <termios.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/poll.h>
#include <time.h>
#include <errno.h>
#include <unistd.h>
int openPort()
{
struct termios oldtio, newtio;
int fd = open("/dev/ttyO1",O_RDWR|O_NOCTTY|O_NONBLOCK|O_NDELAY);
if (fd == -1)
{
printf( "could not open tty" );
return -1;
}
if ( tcgetattr( fd, &oldtio ) == -1 )
{
printf( "error getting tcattr\n" );
close( fd );
return -1;
}
cfmakeraw( &newtio );
cfsetispeed( &newtio, B9600 );
cfsetospeed( &newtio, B9600 );
newtio.c_cflag = (newtio.c_cflag & ~CSIZE) | CS8 | B9600;
newtio.c_cflag |= (CLOCAL | CREAD);
newtio.c_cflag &= ~(PARENB | PARODD);
newtio.c_cflag &= ~CRTSCTS;
newtio.c_cflag &= ~CSTOPB;
newtio.c_iflag = 0;//IGNPAR;
//newtio.c_iflag &= ~(IXON | IXOFF | IXANY);
newtio.c_lflag = 0;
newtio.c_oflag = 0;
newtio.c_cc[VTIME] = 0;
newtio.c_cc[VMIN] = 1;
tcflush( fd, TCIOFLUSH );
if ( tcsetattr( fd, TCSANOW, &newtio ) == -1 )
{
close( fd );
printf( "error setting attrs\n" );
return -1;
}
return fd;
}
void selectLoop( int fd )
{
struct pollfd fds[1];
fds[0].fd = fd;
fds[0].events = POLLIN;
int rc = poll( fds, 1, 100000 );
if (rc < 0)
{
perror( "poll" );
}
else if (rc > 0)
{
char buffer[32] = {0};
int r = read( fd, buffer, sizeof(buffer) );
if (r == -1)
{
printf( "error: %s\n", strerror( errno ) );
}
else
{
printf( buffer );
}
}
else
{
printf( "No data\n" );
}
//close( fd );
sleep( 1 );
}
int main( int c, char ** v )
{
int fd = openPort();
if (fd >= 0)
{
while(1)
{
selectLoop(fd);
}
}
return 0;
}
I get the same result using select(). However if I attach a signal to "data received" then the signal fires, though this is a scenario I am trying to avoid as it plays havoc with Netbeans' debug environment.
FYI: board revision is B6, UART1 is connected by TX/RX only. The "screen" program successfully receives and sends characters from the UART, so I know that it fundamentally works.

Replace this with something sensible:
printf( buffer );
Perhaps:
for (int i = 0; i < r; ++i)
putchar(buffer[i]);
fflush(stdout);
This flushes and doesn't risk printing more characters than read if the buffer is full.

Related

Difficulty communicating over serial ports in Linux

I have success writing code in Windows and performing serial port communication. But not in Linux so far.
Here is the code I wrote:
I have used Picocom to verify that I can communicate over the serial ports via my (USB to NULL Modem to USB) cable.
Setting the code to None blocking yields "errno: 11 Resource temporarily unavailable" on both client and server."
When I set to Blocking, it hangs in code at the Read File method. If I comment that line out it runs... but not getting data.
Server sends packets continuous.
Client receives packets continuous.
Problem seems to be bytes aren't received at the client. They are sent, however at the server.
CLIENT:
RECV(1611912): NumChars: 0 String:
RECV(1611913): NumChars: 0 String:
RECV(1611918): NumChars: 0 String:
RECV(1611919): NumChars: 0 String:
RECV(1611920): NumChars: 0 String:
SERVER:
SENT(106775): NumChars: 10 String: 0123456789
SENT(106776): NumChars: 10 String: 0123456789
SENT(106777): NumChars: 10 String: 0123456789
SENT(106778): NumChars: 10 String: 0123456789
SENT(106779): NumChars: 10 String: 0123456789
to run:
g++ -o sp serialport.cpp
client: ./sp /dev/ttyS0 c
server: ./sp /dev/ttyS4 s
#include <sys/ioctl.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
class C_SerialPort
{
private:
int giFD;
char gpcDevice[100] = {0};
public:
//------------------------------------------------
//
//------------------------------------------------
int GetError(char* pcMessage = (char*)"")
{
char pcError[100] = {0};
sprintf(pcError, " (%s): errno: %d %s\n", pcMessage, errno, strerror(errno));
if(errno > 0)
{
printf("%s", pcError);
}
return errno;
}
//------------------------------------------------
//
//------------------------------------------------
int Connect()
{
return Connect((char*)"/dev/ttyS0");
//return Connect((char*)"/dev/ttyUSB0");
}
//------------------------------------------------
//
//------------------------------------------------
int Connect(char *pcDevice)
{
strcpy(gpcDevice, pcDevice);
struct termios s_TA;
// Open the serial port
giFD = open(gpcDevice, O_RDWR | O_NOCTTY | O_NDELAY | O_FSYNC );
if(giFD < 0)
{
printf("open_port: Unable to open %s\n%s", gpcDevice, strerror(errno));
printf("EXITING...\n");
return 1;
}
else
{
printf("Connect Device: %s\n", gpcDevice);
}
// get attributes
if(tcgetattr(giFD, &s_TA) != 0)
{
GetError((char*)"tcgetattr");
printf("EXITING...\n");
return 1;
}
// clear terminalAttributes data
//memset(&s_TA, 0, sizeof(struct termios));
if(0)
{
// 57600 bauds; 8 bits per word; Ignore modem control lines; Enable receiver.
s_TA.c_cflag = B57600 | CS8 | CLOCAL | CREAD;
// Ignore framing errors and parity errors.
s_TA.c_iflag = IGNPAR | ONLCR;
//Enable implementation-defined output processing.
s_TA.c_oflag = OPOST;
// min time; min bytes to read
s_TA.c_cc[VTIME] = 1;
s_TA.c_cc[VMIN] = 1;// none zero blocks
}
else
{
cfsetospeed(&s_TA, B9600);
cfsetispeed(&s_TA, B9600);
s_TA.c_cflag |= (CLOCAL | CREAD); /* ignore modem controls */
s_TA.c_cflag &= ~CSIZE;
s_TA.c_cflag |= CS8; /* 8-bit characters */
s_TA.c_cflag &= ~PARENB; /* no parity bit */
s_TA.c_cflag &= ~CSTOPB; /* only need 1 stop bit */
s_TA.c_cflag &= ~CRTSCTS; /* no hardware flowcontrol */
/* setup for non-canonical mode */
s_TA.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
s_TA.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
s_TA.c_oflag &= ~OPOST;
/* fetch bytes as they become available */
s_TA.c_cc[VMIN] = 1; // 0 1
s_TA.c_cc[VTIME] = 1; // 0 1
}
//int iVal = fcntl(giFD, F_SETFL, 0); // set blocking?
//printf("file status = 0x%x\n", iVal);
//GetError((char*)"fcntl");
// Set the port to our state
if (tcsetattr(giFD, TCSANOW, &s_TA) != 0)
{
GetError((char*)"tcsetattr");
printf("EXITING...\n");
return 1;
}
// flushes data written but not transmitted.
// flushes data received but not read.
tcflush(giFD, TCOFLUSH);
tcflush(giFD, TCIFLUSH);
printf("CONNECTION OK\n");
//return giFD;
return 0;
}
//------------------------------------------------
//
//------------------------------------------------
void Disconnect(void)
{
close(giFD);
printf("nPort 1 has been CLOSED and %d is the file descriptionn", giFD);
GetError((char*)"Disconnect");
}
//------------------------------------------------
//
//------------------------------------------------
int SendArray(unsigned char *buffer, int len)
{
int n = write(giFD, buffer, len);
// error catch
if(n < 0)
GetError((char*)"write");
return n;
}
//------------------------------------------------
//
//------------------------------------------------
int GetArray (unsigned char *buffer, int len)
{
int n = 0;
int len2 = BytesToRead();
n = read(giFD, buffer, len2); // this line is an issue? with settings?
// error catch
//if(n < 0)
// GetError((char*)"read");
return n;
}
//------------------------------------------------
//
//------------------------------------------------
void Clear()
{
tcflush(giFD, TCIFLUSH);
tcflush(giFD, TCOFLUSH);
}
//------------------------------------------------
//
//------------------------------------------------
int BytesToRead()
{
int iBytes = 0;
ioctl(giFD, FIONREAD, &iBytes);
//printf("Byte2Read: %d\n", iBytes);
// error catch
GetError((char*)"BytesToRead");
return iBytes;
}
};
//------------------------------------------------
//
//------------------------------------------------
int main(int argc, char const *argv[])
{
// device
char *pcDevice = (char*)argv[1];
printf("init:device:%s\n", pcDevice);
// connection type
char cConnType = argv[2][0];
printf("ConnectionType:%c\n", cConnType);
// instantiate SerialPort
C_SerialPort c_SP;
// connect
int iReturn = c_SP.Connect(pcDevice);
if(iReturn != 0)
{
printf("EXITING...\n");
return 1;
}
// clear buffer
c_SP.Clear();
printf("clear\n");
printf("prior...\n");
// main loop
while(1)
{
int iSleep_ms = 200;
usleep(iSleep_ms);
char pcArray[100] = {0};
int iNumChars = 0;
if(cConnType == 's')
{
static long lCount = 0;
// Send
strcpy(pcArray, "0123456789");
iNumChars = c_SP.SendArray((unsigned char*)pcArray, 10);
if(iNumChars > 0)
{
printf("SENT(%ld): NumChars: %d String: %s\n", lCount, iNumChars, pcArray);
}
lCount++;
}
if(cConnType == 'c')
{
static long lCount = 0;
// Receive
iNumChars = c_SP.GetArray((unsigned char*)pcArray, sizeof(pcArray));
if(iNumChars > 0)
{
printf("RECV(%ld): NumChars: %d String: %s\n", lCount, iNumChars, pcArray);
}
else
{
//printf("RECV: NumChars: %d String: %s\n", iNumChars, pcArray);
}
lCount++;
}
}
c_SP.Disconnect();
return 0;
}
You have four major bugs:
Your GetArray function ignores the return value of BytesToRead. It should read the lesser of the buffer size or the number of bytes available.
Your code has no ability to sanely handle the case where BytesToRead returns zero.
You ignore the return value of GetArray. When you print pcArray, you do not tell printf how many characters to output. So how is it supposed to know what to print?
You overwrite the returned values from tcgetattr by calling memset.
The issue is the serial port to communicate through.
The /dev/ttyS0 and /dev/ttyS4 are the serial ports after the USB in the NULL modem cable hook up. But to communicate I had to use /dev/ttyUSB0 and /dev/ttyUSB1

Serial data sniffer not working as expected

I'm trying to get serial data from one port /dev/ttyUSB0 and pass it to the second one /dev/ttyUSB1 and vice versa. By my, apparently, flaunted logic the code below should work as expected. But the data seems not to reach the second port correctly, as I am not getting a correct response. I need my own program because I need to control the flow of data between the two serial devices.
#include <iostream>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
using namespace std;
int set_interface_attribs(int fd, int speed)
{
struct termios tty;
if (tcgetattr(fd, &tty) < 0) {
printf("Error from tcgetattr: %s\n", strerror(errno));
return -1;
}
cfsetospeed(&tty, (speed_t)speed);
cfsetispeed(&tty, (speed_t)speed);
tty.c_cflag |= (CLOCAL | CREAD); /* ignore modem controls */
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8; /* 8-bit characters */
tty.c_cflag &= ~PARENB; /* no parity bit */
tty.c_cflag &= ~CSTOPB; /* only need 1 stop bit */
tty.c_cflag &= ~CRTSCTS; /* no hardware flowcontrol */
/* setup for non-canonical mode */
tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
tty.c_oflag &= ~OPOST;
/* fetch bytes as they become available */
tty.c_cc[VMIN] = 1;
tty.c_cc[VTIME] = 1;
if (tcsetattr(fd, TCSANOW, &tty) != 0) {
printf("Error from tcsetattr: %s\n", strerror(errno));
return -1;
}
return 0;
}
void set_mincount(int fd, int mcount)
{
struct termios tty;
if (tcgetattr(fd, &tty) < 0) {
printf("Error tcgetattr: %s\n", strerror(errno));
return;
}
tty.c_cc[VMIN] = mcount ? 1 : 0;
tty.c_cc[VTIME] = 5; /* half second timer */
if (tcsetattr(fd, TCSANOW, &tty) < 0)
printf("Error tcsetattr: %s\n", strerror(errno));
}
void read_send(int port1, int port2)
{
unsigned char buf[1024];
int rdlen;
int wlen;
printf("Reading port 1\n");
rdlen = read(port1, buf, sizeof(buf) - 1);
if (rdlen > 0) {
unsigned char *p;
printf("Writing %d:\n", rdlen);
wlen = write (port2, buf, rdlen);
tcdrain(port2);
if (wlen != rdlen) {
printf("Error from write: %d, %s\n", wlen, strerror(errno));
}
} else if (rdlen < 0) {
printf("Error from read: %d: %s\n", rdlen, strerror(errno));
}
}
void connect_data(int port1, int port2)
{
//read 1 write on 2; read 2 write on 1
read_send(port1, port2);
read_send(port2, port1);
}
int main()
{
char *portname_cp = "/dev/ttyUSB0";
char *portname_rfid = "/dev/ttyUSB1";
int fd_cp;
int fd_rfid;
int wlen;
fd_rfid = open(portname_rfid, O_RDWR | O_NOCTTY | O_SYNC);
fd_cp = open(portname_cp, O_RDWR | O_NOCTTY | O_SYNC);
if (fd_rfid < 0) {
printf("Error opening %s: %s\n", portname_rfid, strerror(errno));
return -1;
}
if (fd_cp < 0) {
printf("Error opening %s: %s\n", portname_cp, strerror(errno));
return -1;
}
printf("Ports opened\n");
/*baudrate 115200, 8 bits, no parity, 1 stop bit */
set_interface_attribs(fd_rfid, B115200);
//set_mincount(fd_rfid, 0); /* set to pure timed read */
set_interface_attribs(fd_cp, B115200);
//set_mincount(fd_cp, 0); /* set to pure timed read */
do {
connect_data(fd_cp, fd_rfid);
} while(true);
return 0;
}

linux c++ serial port echoes output

In my program which sends bytes to a serial port, I receive bytes which I send. I don't want to receive bytes which I send, and I don't know how to do this?
#include <stdio.h>
#include <fcntl.h>
#include <termios.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/time.h>
#include <sys/select.h>
#include <sys/ioctl.h>
int fd;
struct termios tio, old_tio;
time_t SubTime_mSec(timeval val1, timeval val2)
{
timeval tv;
if (val1.tv_sec > val2.tv_sec) return (0);
if (val1.tv_sec == val2.tv_sec)
if (val1.tv_usec > val2.tv_usec) return (0);
tv.tv_sec = val2.tv_sec - val1.tv_sec;
if (val1.tv_usec < val2.tv_usec) {
tv.tv_usec = val2.tv_usec - val1.tv_usec;
} else {
tv.tv_sec --;
tv.tv_usec = 1000000 + val2.tv_usec - val1.tv_usec;
}
return(tv.tv_sec*1000 + tv.tv_usec/1000);
}
void RTUOutMessage(int cnt1, int cnt2)
{
unsigned char msg[13] = {0x01, 0x10, 0x00, 0x2F, 0x00, 0x02, 0x04, 0x4B, 0x64, 0x3D, 0xD9, 0x36, 0xC6};
int Len = 13;
int status, i, j;
ioctl(fd, TIOCMGET, &status);
status = status | TIOCM_RTS | TIOCM_DTR;
ioctl(fd, TIOCMSET, &status);
write(fd, msg, Len);
for (j = 0; j < cnt1; j ++)
for (i = 0; i < cnt2; i ++);
ioctl(fd, TIOCMGET, &status);
status &= ~TIOCM_RTS;
status &= ~TIOCM_DTR;
ioctl(fd, TIOCMSET, &status);
timeval start_t, curr_t;
char Buff[80];
int l;
gettimeofday(&start_t, NULL);
curr_t = start_t;
while (SubTime_mSec(start_t, curr_t) < 1000) {
l = read(fd, Buff, 80);
if (l > 0) {
printf("BUFFER=");
for(i = 0; i < l; i++) {
printf(" %x", Buff[i]);
}
//printf("\n");
}
gettimeofday(&curr_t, NULL);
}
}
void InitPort(void)
{
int StopBits = 2;
if ((fd = open("/dev/ttyAM2", O_RDWR | O_NDELAY)) < 0) {
printf("Couldn't open //dev//ttyAM2\n");
}
tcflush(fd, TCIOFLUSH);
int n = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, n & ~O_NDELAY);
tcgetattr(fd, &old_tio);
tcgetattr(fd, &tio);
cfsetospeed(&tio, (speed_t)B9600);
cfsetispeed(&tio, (speed_t)B9600);
tio.c_cflag = (tio.c_cflag & ~CSIZE) | CS8;
tio.c_cflag |= CLOCAL | CREAD ;
tio.c_cflag &= ~OFILL;
//parity
tio.c_cflag &= ~(PARENB | PARODD);
tio.c_cflag &= ~CRTSCTS;
if (StopBits == 2) tio.c_cflag |= CSTOPB;
else tio.c_cflag &= ~CSTOPB;
tio.c_iflag=IGNBRK;
tio.c_iflag &= ~(IXON|IXOFF|IXANY);
tio.c_lflag=0;
tio.c_oflag=0;
tio.c_cc[VTIME]=0;
tio.c_cc[VMIN]=0;
if (tcsetattr(fd, TCSANOW, &tio)!=0) printf("tcsetattr() 1 failed\n");
int mcs=0;
ioctl(fd, TIOCMGET, &mcs);
mcs |= TIOCM_RTS;
ioctl(fd, TIOCMSET, &mcs);
if (tcgetattr(fd, &tio)!=0) printf("tcgetattr() 4 failed\n");
tio.c_cflag &= ~CRTSCTS;
if (tcsetattr(fd, TCSANOW, &tio)!=0) printf("tcsetattr() 2 failed\n");
}
int main(int argc, char **argv)
{
InitPort();
int cnt1, cnt2;
cnt1 = 3;
cnt2 = 20000;
cnt1 = atoi(argv[1]);
cnt2 = atoi(argv[2]);
for(;;) {
RTUOutMessage(cnt1, cnt2);
usleep(1000000);
}
tcsetattr(fd, TCSANOW, &old_tio);
close(fd);
printf("End\n");
return 0;
}
If you are receiving the chars that you sent, then the remote's serial port is apparently echoing its input.
You will either have to disable input echo at the other device, or perform "echo cancellation" in your receive logic. I.E. create an "echo FIFO". Each byte output to the serial port is also written to the "echo FIFO". Set a flag indicating that an echo is expected. As input is received, compare it to the "echo FIFO", and remove chars when it matches, and toss the receive char. If no match, then accept receive char. Clear the echo flag when the "echo FIFO" is empty.
BTW the code appears to set the serial port to raw or non-canonical mode. The preferred method would be to use
cfmakeraw(&tio);
rather than
tio.c_iflag=IGNBRK;
tio.c_iflag &= ~(IXON|IXOFF|IXANY);
tio.c_lflag=0;
tio.c_oflag=0;

Disabling stdout buffering of a forked process

I wrote a code in C/C++ which forks a child process, duplicates the stdin/stdout into a pipe ends and calls execvp.
Everything is working fine (i.e. the output from stdin/err/out is captured by the parent process)
The problem is that the child stdout is buffered.
so if the child code looks like this:
printf("Enter any key and hit ENTER:\n");
fgets(line);
printf("read: %s\n", line);
exit(0);
In the parent process I don't see the line 'Enter any key:' - it will be "flushed" only after the program calls exit (which auto flushes the stdout buffer) or an explicit call to 'flush(stdout)' is added
I did some research and tried adding a call to disable the stdout buffering by adding a call to:
setvbuf(stdout, NULL, _IONBF, 0);
just before calling execvp(...) in the parent process
so the relevant code looks now like this:
int rc = fork();
if ( rc == 0 ) {
// Child process
if(workingDirectory.IsEmpty() == false) {
wxSetWorkingDirectory( workingDirectory );
}
int stdin_file = fileno( stdin );
int stdout_file = fileno( stdout );
int stderr_file = fileno( stderr );
// Replace stdin/out with our pipe ends
dup2 ( stdin_pipe_read, stdin_file );
close( stdin_pipe_write );
dup2 ( stdout_pipe_write, stdout_file);
dup2 ( stdout_pipe_write, stderr_file);
close( stdout_pipe_read );
setvbuf(stdout, NULL, _IONBF, 0);
// execute the process
execvp(argv[0], argv);
exit(0);
}
With no luck.
Any ideas?
EDIT:
here is a sample of the parent code, the only thing needs changing is the path to the child executable:
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/select.h>
#include <errno.h>
#include <sys/wait.h>
#include <string>
#include <string.h>
#include <cstdio>
static int read_handle(-1);
static pid_t pid;
bool read_from_child(std::string& buff) {
fd_set rs;
timeval timeout;
memset(&rs, 0, sizeof(rs));
FD_SET(read_handle, &rs);
timeout.tv_sec = 1; // 1 second
timeout.tv_usec = 0;
int rc = select(read_handle+1, &rs, NULL, NULL, &timeout);
if ( rc == 0 ) {
// timeout
return true;
} else if ( rc > 0 ) {
// there is something to read
char buffer[1024*64]; // our read buffer
memset(buffer, 0, sizeof(buffer));
if(read(read_handle, buffer, sizeof(buffer)) > 0) {
buff.clear();
buff.append( buffer );
return true;
}
return false;
} else { /* == 0 */
if ( rc == EINTR || rc == EAGAIN ) {
return true;
}
// Process terminated
int status(0);
waitpid(pid, &status, 0);
return false;
}
}
void execute() {
char *argv[] = {"/home/eran/devl/TestMain/Debug/TestMain", NULL};
int argc = 1;
int filedes[2];
int filedes2[2];
// create a pipe
int d;
d = pipe(filedes);
d = pipe(filedes2);
int stdin_pipe_write = filedes[1];
int stdin_pipe_read = filedes[0];
int stdout_pipe_write = filedes2[1];
int stdout_pipe_read = filedes2[0];
int rc = fork();
if ( rc == 0 ) {
// Child process
int stdin_file = fileno( stdin );
int stdout_file = fileno( stdout );
int stderr_file = fileno( stderr );
// Replace stdin/out with our pipe ends
dup2 ( stdin_pipe_read, stdin_file );
close( stdin_pipe_write );
dup2 ( stdout_pipe_write, stdout_file);
dup2 ( stdout_pipe_write, stderr_file);
close( stdout_pipe_read );
setvbuf(stdout, NULL, _IONBF, 0);
// execute the process
execvp(argv[0], argv);
} else if ( rc < 0 ) {
perror("fork");
return;
} else {
// Parent
std::string buf;
read_handle = stdout_pipe_read;
while(read_from_child(buf)) {
if(buf.empty() == false) {
printf("Received: %s\n", buf.c_str());
}
buf.clear();
}
}
}
int main(int argc, char **argv) {
execute();
return 0;
}
Actually, after struggling with it a bit, it seems like the only solution to this problem is by making the 'parent' process pretending to be a terminal using the OS pseudo terminal API calls.
One should call 'openpty()' before the fork(), and inside the child code, he should call 'login_tty(slave)' the slave is then becoming the stdin/out and stderr.
By pretending to a terminal, the buffering of stdout is automatically set to 'line mode' (i.e. flush occurs when \n is encountered). The parent should use the 'master' descriptor for readin/writing with the child process.
The modified parent code (in case anyone will ever need this):
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/select.h>
#include <errno.h>
#include <sys/wait.h>
#include <string>
#include <string.h>
#include <cstdio>
#include <pty.h>
#include <utmp.h>
static int read_handle(-1);
static pid_t pid;
bool read_from_child(std::string& buff) {
fd_set rs;
timeval timeout;
memset(&rs, 0, sizeof(rs));
FD_SET(read_handle, &rs);
timeout.tv_sec = 1; // 1 second
timeout.tv_usec = 0;
int rc = select(read_handle+1, &rs, NULL, NULL, &timeout);
if ( rc == 0 ) {
// timeout
return true;
} else if ( rc > 0 ) {
// there is something to read
char buffer[1024*64]; // our read buffer
memset(buffer, 0, sizeof(buffer));
if(read(read_handle, buffer, sizeof(buffer)) > 0) {
buff.clear();
buff.append( buffer );
return true;
}
return false;
} else { /* == 0 */
if ( rc == EINTR || rc == EAGAIN ) {
return true;
}
// Process terminated
int status(0);
waitpid(pid, &status, 0);
return false;
}
}
void execute() {
char *argv[] = {"/home/eran/devl/TestMain/Debug/TestMain", NULL};
int argc = 1;
int master, slave;
openpty(&master, &slave, NULL, NULL, NULL);
int rc = fork();
if ( rc == 0 ) {
login_tty(slave);
close(master);
// execute the process
if(execvp(argv[0], argv) != 0)
perror("execvp");
} else if ( rc < 0 ) {
perror("fork");
return;
} else {
// Parent
std::string buf;
close(slave);
read_handle = master;
while(read_from_child(buf)) {
if(buf.empty() == false) {
printf("Received: %s", buf.c_str());
}
buf.clear();
}
}
}
int main(int argc, char **argv) {
execute();
return 0;
}
Would inserting a call to fflush(stdout) after the printf not suffice?
Otherwise setvbuf should do the trick:
setvbuf(stdout,NULL,_IOLBF,0);
http://lists.gnu.org/archive/html/bug-coreutils/2008-11/msg00164.html
you can use LD_PRELOAD to setvbuf
If you ever need to achieve more control over the buffering of the child process, I made a module that expose features of the stdbuf preload trick, see this self answered question of mine:
C Control buffering of Child process

SSL_accept hangs after calling fork()

I'm writing an app in C++ using openssl, and I can't seem to get the ssl socket connection to work.
I have an abstract class, with multiple functions implemented using various protocols by the inheriting classes and simple TCP and UDP ( posix sockets ) work fine.
I could not get the ssl working though and after some code browsing and tweaking I found out, that it is the fork() function which causes problems.
I wrote a very simple program to check things out, and this is what I get:
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <iostream>
#include <errno.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/timeb.h>
#include <netinet/in.h>
#include <sys/select.h>
#include <netdb.h>
#include <time.h>
#include </usr/local/include/ssl/ssl.h>
#include "SSL_CA.cpp"
int main( int argc, char **argv ) {
int retval;
SSL_CTX *ctx;
SSL *con;
int port = atoi(argv[1]);
sockaddr_in client_addr;
sockaddr_in serv_addr;
socklen_t client_len;
int max_clients = 40;
int serv_sock, client_sock;
if ( ( serv_sock = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 )
return -12;
SSL_library_init();
int SSL_CA = NowySSL();
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = port;
cout << "Conf " << endl;
if ( bind( serv_sock, ( sockaddr * ) &serv_addr, sizeof( serv_addr ) ) < 0 )
return -1;
if ( listen( serv_sock, max_clients ) < 0 )
return -1;
while (1) {
cout << "Waiting" << endl;
if ( ( client_sock = accept( serv_sock, ( sockaddr * ) &client_addr, &client_len ) ) < 0 )
return -1;
pid_t pid = fork();
if ( pid = 0 ) {
con = NULL;
ctx = NULL;
ctx = (SSL_CTX *)SSL_CTX_new( SSLv23_server_method() );
con = (SSL *)SSL_new( (SSL_CTX *)ctx );
if (
!( ( ( !SSL_CA ) && ( SSL_NO_CA_RUN( con, ctx, client_sock, 0 ) ) ) ||
( ( SSL_CA ) && ( SSL_CA_RUN( con, ctx, client_sock, true ) ) ) )
)
return -1;
char buf[10];
if ( ( retval = SSL_read( con, buf, 10 ) ) <= 0 )
return -16;
cout << "Got msg " << buf << " " << retval << endl;
sprintf(buf, "12345" );
if ( ( retval = SSL_write( con, buf, strlen(buf) ) ) <= 0 )
return -1;
cout << "Sent" << endl;
}
else cout << "Parent " << endl;
}
return 0;
}
These are the SSL_CA_RUN and SSL_NO_CA_RUN functions - not written by me, but i have to use it
int SSL_NO_CA_RUN (SSL *con, SSL_CTX *ctx, int msgsock, int exit_now)
{
// printf ("SSL NO CA\n");
// SSL_library_init();
// ctx=(SSL_CTX *)SSL_CTX_new(SSLv23_server_method());
// con=(SSL *)SSL_new((SSL_CTX *)ctx);
if ((!SSL_set_fd (con,msgsock))||
(!SSL_use_RSAPrivateKey_file (con,"ktsa_u_linux.k",1))||
(!SSL_use_certificate_file (con,"ktsa_u_linux.c",1))||
(!SSL_accept (con)))
{
printf ("SSL ERROR (%s)\n",strerror(errno));
if (exit_now)
{
zamknij_polaczenie (con,ctx,msgsock);
exit(0);
}
return false;
}
// printf ("SSL NO CA OK!!!\n");
return true;
}
int SSL_CA_RUN (SSL *con, SSL_CTX *ctx, int msgsock, int exit_now)
{
int ret, ok=true;
X509 *peer;
FILE *fp;
char error[250];
// printf ("SSL CA\n");
//MARCIN (START)
//robimy linki do certyfikatow Marcin
certhash(CA_DIR,CRL_DIR,getpid());
//dostep_read (890);
// SSL_library_init();
// printf("\n");
// ctx=(SSL_CTX *)SSL_CTX_new(SSLv23_server_method());
// con=(SSL *)SSL_new((SSL_CTX *)ctx);
cb=pem_passwd_cb;
SSL_CTX_set_default_passwd_cb_userdata(ctx,haslo);
SSL_CTX_set_default_passwd_cb(ctx,cb);
//SSL_set_verify_depth(con,10); default = 9
SSL_set_verify(con,SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT,NULL);
// SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT,NULL);
//SSL_CTX_set_cipher_list(ctx,"SHA1+3DES:+RSA:HIGH");
//SSL_set_cipher_list(con,"SHA1+3DES:+RSA:HIGH");
if ((!SSL_set_fd (con,msgsock))||
(!SSL_use_RSAPrivateKey_file (con,KEY_FILE,1))||
(!SSL_use_certificate_file (con,CERT_FILE,1))||
// (!SSL_CTX_load_verify_locations(ctx,CA_FILE,NULL))||
(!SSL_CTX_load_verify_locations(ctx,NULL,linkdir))||
(!SSL_accept (con))
)
{
if (exit_now)
{
remove_symdir(getpid());
// printf("error connect\n");
zamknij_polaczenie (con,ctx,msgsock);
exit(0);
} else remove_symdir(getpid());
ok=false;
}
if (ok)
{
peer=SSL_get_peer_certificate(con);
if(peer!=NULL)
{
//ret=1 - odwolany
//ret=0 - wszystko ok
//ret = inne - jakis inny blad, np. brak wlasciwej listy crl
//ret=check_peer_crl_dir(peer,error,CRL_DIR); //sprawdzenie odwolania certyfikatu przychodzacego
ret=check_peera(peer,error,0); //sprawdzenie odwolania certyfikatu przychodzacego
// printf("peer certyfikat:%s:%i\n",error,ret);
}
if ((peer==NULL)||(ret==1))
{
//nie akceptujemy polaczenia bo nie dostalismy certyfikatu lub certyfikat jest odwolany
// printf("polaczenie odrzucone - certyfikat peera odwolany, brak certyfikatu lub wystawiony przez inne CA \n");
if (exit_now)
{
remove_symdir(getpid());
zamknij_polaczenie (con,ctx,msgsock);
exit(2);
}
ok=false;
}
}
if (ok)
{
X509_free(peer); //potrzebne?
peer=NULL;
//mozna sprawdzic tez nasz certyfikat
if (!(fp = fopen (CERT_FILE, "r"))) //"/router/ssl/cert/sslcert.cer"
printf ("Error reading my certificate file\n");
if (!(peer = PEM_read_X509 (fp, NULL, NULL, NULL)))
printf ("Error reading my certificate in file\n");
if (fp)fclose (fp);
if(peer!=NULL)
{
//ret=1 - odwolany
//ret=0 - wszystko ok
//ret = inne - jakis inny blad, np. brak wlasciwej listy crl
//ret=check_peer_crl_dir(peer,error,CRL_DIR); //sprawdzenie odwolania certyfikatu przychodzacego
ret=check_peera(peer,error,0); //sprawdzenie odwolania certyfikatu naszego
// printf("nasz certyfikat:%s:%i\n",error,ret);
}
if ((peer==NULL)||(ret==1))
{
//nie akceptujemy polaczenia bo nasz certyfikat ma problem lub jest odwolany
// printf("polaczenie odrzucone- nasz certyfikat odwolany\n");
if (exit_now)
{
remove_symdir(getpid());
zamknij_polaczenie (con,ctx,msgsock);
exit(2);
}
ok=false;
}
}
if (ok)
{
X509_free(peer); //potrzebne?
peer=NULL;
}
// printf("connected...ok\n");
remove_symdir(getpid());
return ok;
}
If I don't call fork, all works fine, the message gets through, if the fork's there it gets stuck on ssl_accept.
Any help?
Not sure if this is just a typo or your real problem but this isn't going to do what you intend:
pid_t pid = fork();
if ( pid = 0 )
{
con = NULL;
//.............
Your child code is not going to execute.