Non blocking pseudo terminal, recovery after POLLHUP - c++

I create a new pseudo-terminal by opening /dev/ptmx with open() function and O_RDWR | O_NOCTTY | O_NONBLOCK flags. Then I use poll() function to wait for incoming data from the remote end:
struct pollfd pollFileDescriptors[numberOfTerminals];
for (unsigned terminalIndex = 0; terminalIndex < numberOfTerminals; terminalIndex++) {
pollFileDescriptors[terminalIndex].fd = terminals[terminalIndex].getFileDescriptor();
pollFileDescriptors[terminalIndex].events = POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND;
}
int ready = poll(pollFileDescriptors, terminals.getNumberOfTerminals(), timeoutMSec);
Everything works like a dream until the remote end closes connection. In such a case the poll() function returns all the time with POLLHUP revents flag. This is by design, however what could I do to make it operating as before i.e. waiting for another process to open and use the pseudo-terminal. I mean it waits, but returns immediately with POLLHUP set. On the other hand, if I close the file descriptor I have no guarantee of receiving the same pseudo-terminal-id that after reopening the /dev/ptmx. Is there any way to remove the POLLHUP revents flag?
I found a similar question: Poll() on Named Pipe returns with POLLHUP constantly and immediately , however I am using already O_RDWR as described there but it doesn't help as in case of named pipes.

The issue can be easily solved by reopening the pseudo-terminal right after it is created. The POLLHUP won't appear as long as at least one writer exists so we can do this by ourselves with open() and ptsname():
// Create a new pseudo terminal
int fileDescriptor = open("/dev/ptmx", O_RDWR | O_NOCTTY | O_NONBLOCK);
grantpt(fileDescriptor);
unlockpt(fileDescriptor);
// Reopen it for write
const char *targetPath = ptsname(fileDescriptor);
int dummyWriterFileDescriptor = open(fileName.c_str(), O_WRONLY | O_NOCTTY | O_NONBLOCK);

Related

How to get poll()'s POLLHUP equivalent with select()?

I'm currently porting some code from Linux to Windows (with MinGW).
From what I understand, MinGW doesn't support poll(), which was used in the original, so I'm rewriting everything for select().
And now I stumbled upon if (pfd[i].revents & (POLLERR|POLLHUP))...
How can I get the equivalent of this condition with select() - or alternatively, with whatever the winsock api or MinGW provides? The POLLERR part is simple enough; if(FD_ISSET (i, &error_fd_set)) but I'm at loss about the POLLHUP part.
You can't. You have to use the "normal" way to find out whether the connection has been closed, that is reading from it.
In terms of code it'd be:
int rc = select(max_fd + 1, read_set, ..., ..., ...);
// check rc
for (int i = 0; i <= max_fd; ++i) {
if (FD_ISSET(i, &read_set)) { // data incoming on i
int rc = read(i, ..., ...); // or recv, if you use some flag
if (rc == 0) {
// i hung up
}
}
}
Otherwise, you can use WSAPoll which offers an API like the one you'd expect in UNIX-like systems.
Further information on WSAPoll.
According to my copy of The Linux Programming Interface, kernel poll events are mapped to select() events like so:
// Ready for reading
#define POLLIN_SET (POLLRDNORM | POLLRDBAND | POLLIN | POLLHUP | POLLERR)
// Ready for writing
#define POLLOUT_SET (POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR)
// Exceptional condition
#define POLLEX_SET (POLLPRI)
So this suggests that you need to check the 'ready' event. To actually distinguish between POLLHUP, POLLIN, and POLLIN | POLLHUP, you can use the following chart from the book:
| ----- Condition or event ---- |
Data in pipe? | Write end open? | select() | poll()
no no r | POLLHUP
yes yes r | POLLIN
yes no r | POLLIN | POLLHUP

mq_receive returning EAGAIN when data is present

I have an mqueue which has 2 messages in it but the call to mq_receive will not pull data. I don't have any previous experience with this message queue so forgive my ignorance. I believe the send side is working fine as echoing the "file" does show information.
QSIZE:48 NOTIFY:1 SIGNO:0 NOTIFY_PID:5741
This is 2 messages without a message signal being generated. The PID is the process that will be calling mq_receive.
For reference, here is the open code.
msgq_attr.mq_maxmsg = MAX_NUM_MESSAGES; // 20
msgq_attr.mq_msgsize = MAX_MSG_SIZE; // 256
mqrcv_id = mq_open(queue_name, O_RDONLY | O_NONBLOCK,
S_IRWXU | S_IRWXO, msgq_attr);
notify.sigev_notify = SIGEV_NONE;
notify.sigev_notify_attributes = NULL;
mq_notify(mqrcv_id, &notify);
And here is the receive.
int msgSize = mq_receive(mqrcv_id, buffer,
MAX_MSG_SIZE, &msgprio);
msgSize always returns -1 with errno EAGAIN. From the documentation, this should mean that the queue has no messages in it. Note that it is NONBLOCKING.
The issue is a code bug. The call to open should be
mqrcv_id = mq_open(queue_name, O_RDONLY | O_NONBLOCK,
S_IRWXU | S_IRWXO, &msgq_attr);

CreateFile CREATE_NEW equivalent on linux

I wrote a method which tries to create a file. However I set the flag CREATE_NEW so it can only create it when it doesnt exist. It looks like this:
for (;;)
{
handle_ = CreateFileA(filePath.c_str(), 0, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_HIDDEN | FILE_FLAG_DELETE_ON_CLOSE, NULL);
if (handle_ != INVALID_HANDLE_VALUE)
break;
boost::this_thread::sleep(boost::posix_time::millisec(10));
}
This works as it should. Now I want to port it to linux and and of course the CreateFile function are only for windows. So I am looking for something equivalent to this but on linux. I already looked at open() but I cant seem to find a flag that works like CREATE_NEW. Does anyone know a solution for this?
Take a look at the open() manpage, the combination of O_CREAT and O_EXCL is what you are looking for.
Example:
mode_t perms = S_IRWXU; // Pick appropriate permissions for the new file.
int fd = open("file", O_CREAT|O_EXCL, perms);
if (fd >= 0) {
// File successfully created.
} else {
// Error occurred. Examine errno to find the reason.
}
fd = open("path/to/file", O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC);
O_CREAT: Creates file if it does not exist. If the file exists, this flag has no effect.
O_EXCL: If O_CREAT and O_EXCL are set, open() will fail if the file exists.
O_RDWR: Open for reading and writing.
Also, creat() is equivalent to open() with flags equal to O_CREAT|O_WRONLY|O_TRUNC.
Check this: http://linux.die.net/man/2/open
This is the correct and working answer:
#include <fcntl2.h> // open
#include <unistd.h> // pwrite
//O_CREAT: Creates file if it does not exist.If the file exists, this flag has no effect.
//O_EXCL : If O_CREAT and O_EXCL are set, open() will fail if the file exists.
//O_RDWR : Open for reading and writing.
int file = open("myfile.txt", O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC);
if (file >= 0) {
// File successfully created.
ssize_t rc = pwrite(file, "your data", sizeof("myfile.txt"), 0);
} else {
// Error occurred. Examine errno to find the reason.
}
I posted this code for another person, inside a comment because his question is closed... but this code is tested by me on Ubuntu and it's working exactly as CreateFileA and WriteFile.
It will create a new file as you are seeking.

Serial port is stuck at wait(4,

I have this implementation in C++ for linux for initializing a serial port:
void setupSerialPort()
{
fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
memset(&tio, 0, sizeof(tio));
tio.c_iflag = 0;
tio.c_oflag = 0;
tio.c_cflag = CS8|CREAD|CLOCAL;
tio.c_lflag = 0;
tio.c_cc[VMIN] = 0;
tio.c_cc[VTIME] = 0;
fd = open(SERIALPORT, O_RDWR | O_NONBLOCK) ;
fcntl(fd , F_SETFL, 0); // read data in the buffer per chunk
cfsetospeed(&tio, B4800); // 4800 baud
cfsetispeed(&tio, B4800); // 4800 baud
tcsetattr(fd , TCSANOW, &tio);
}
Sometimes, the serial port reading is stuck and I use 'strace' to see what is going on. the problem is:
strace -p 9454
Process 9454 attached - interrupt to quit
wait4(-1, ^C <unfinished ...>
Process 9454 detached
How can I avoid the problem (it does not happen all the time)?
The problem occurring randomly indicates that the call to open is failing. So check the return values from open call and other remaining calls.
if (fd < 0) return;
Also ensure clean close of the serial port.
Syscalls involving IO could get stuck specially with network IO. Since you are using NON_BLOCKING mode that shouldnt be the case , The complete code would have been helpful here. Personally i had this situation before and I used timer to generate SIG_USR which used to end the blocking call with an error. Pseudo code for that algo is as follows:
initiateTimer ( timeout)
myfunctionCall(){
someSysCall();
}
if(!signalled){
disableTimer();
}else{
checkState();
if(fatal)
die;
}
My suggestions... Open the device with:
int fd = TEMP_FAILURE_RETRY(open(ttyName, O_RDWR | O_NOCTTY | O_NONBLOCK));
The macro TEMP_FAILURE_RETRY is needed to protect open, close, read and select from signal interruptions. Read about the macro if you use libc. Include O_NOCTTY flag to avoid the device becoming the controlling port of the process. Check return value for valid fd.
C.

TIOCEXCL in Solaris

I have a little problem. I work with serial port, for example /dev/term/0 and I need to lock multiple access to this device. For that I use this code:
int hComm;
hComm = open(portName, O_RDWR | O_NOCTTY | O_NDELAY);
if(hComm != -1){
ioctl(hComm, TIOCEXCL, NULL);
int flags = fcntl(hComm, F_GETFL, 0);
flags &= ~O_NDELAY;
fcntl(hComm, F_SETFL, flags);
}
All works fine. Then I run another application and try to open this port, I have error EBUSY - and it's OK, but in this moment my first application stop working. I can't read/write and close this port, I always have only one error ENXIO (No such device or address).
I have tested this code in Linux and Mac OS X and all works without any problems, but in Solaris...
I don't know what to do.