Improve the robustness of the IrDA I/O code

The select system call modifies the file descriptor set, and depending
on the underlying implementation also the timeout. Therefore these
parameters should be re-initialized before every call.

The existing code also didn't handle EINTR and EAGAIN correct.
This commit is contained in:
Jef Driesen 2017-02-14 21:41:44 +01:00
parent 24cbff9a9f
commit f83d156fdb

View File

@ -36,6 +36,7 @@
#include <linux/irda.h> // irda
#include <sys/select.h> // select
#include <sys/ioctl.h> // ioctl
#include <sys/time.h>
#endif
#include "irda.h"
@ -47,6 +48,7 @@
typedef int s_ssize_t;
typedef DWORD s_errcode_t;
#define S_ERRNO WSAGetLastError ()
#define S_EINTR WSAEINTR
#define S_EAGAIN WSAEWOULDBLOCK
#define S_ENOMEM WSA_NOT_ENOUGH_MEMORY
#define S_EINVAL WSAEINVAL
@ -59,6 +61,7 @@ typedef DWORD s_errcode_t;
typedef ssize_t s_ssize_t;
typedef int s_errcode_t;
#define S_ERRNO errno
#define S_EINTR EINTR
#define S_EAGAIN EAGAIN
#define S_ENOMEM ENOMEM
#define S_EINVAL EINVAL
@ -408,20 +411,24 @@ dc_irda_read (dc_irda_t *device, void *data, size_t size, size_t *actual)
goto out_invalidargs;
}
struct timeval tv;
if (device->timeout >= 0) {
tv.tv_sec = (device->timeout / 1000);
tv.tv_usec = (device->timeout % 1000) * 1000;
}
fd_set fds;
FD_ZERO (&fds);
FD_SET (device->fd, &fds);
while (nbytes < size) {
int rc = select (device->fd + 1, &fds, NULL, NULL, (device->timeout >= 0 ? &tv : NULL));
fd_set fds;
FD_ZERO (&fds);
FD_SET (device->fd, &fds);
struct timeval tvt;
if (device->timeout > 0) {
tvt.tv_sec = (device->timeout / 1000);
tvt.tv_usec = (device->timeout % 1000) * 1000;
} else if (device->timeout == 0) {
timerclear (&tvt);
}
int rc = select (device->fd + 1, &fds, NULL, NULL, device->timeout >= 0 ? &tvt : NULL);
if (rc < 0) {
s_errcode_t errcode = S_ERRNO;
if (errcode == S_EINTR)
continue; // Retry.
SYSERROR (device->context, errcode);
status = syserror(errcode);
goto out;
@ -432,6 +439,8 @@ dc_irda_read (dc_irda_t *device, void *data, size_t size, size_t *actual)
s_ssize_t n = recv (device->fd, (char*) data + nbytes, size - nbytes, 0);
if (n < 0) {
s_errcode_t errcode = S_ERRNO;
if (errcode == S_EINTR || errcode == S_EAGAIN)
continue; // Retry.
SYSERROR (device->context, errcode);
status = syserror(errcode);
goto out;
@ -468,12 +477,32 @@ dc_irda_write (dc_irda_t *device, const void *data, size_t size, size_t *actual)
}
while (nbytes < size) {
s_ssize_t n = send (device->fd, (char*) data + nbytes, size - nbytes, 0);
if (n < 0) {
fd_set fds;
FD_ZERO (&fds);
FD_SET (device->fd, &fds);
int rc = select (device->fd + 1, NULL, &fds, NULL, NULL);
if (rc < 0) {
s_errcode_t errcode = S_ERRNO;
if (errcode == S_EINTR)
continue; // Retry.
SYSERROR (device->context, errcode);
status = syserror(errcode);
goto out;
} else if (rc == 0) {
break; // Timeout.
}
s_ssize_t n = send (device->fd, (char*) data + nbytes, size - nbytes, 0);
if (n < 0) {
s_errcode_t errcode = S_ERRNO;
if (errcode == S_EINTR || errcode == S_EAGAIN)
continue; // Retry.
SYSERROR (device->context, errcode);
status = syserror(errcode);
goto out;
} else if (n == 0) {
break; // EOF.
}
nbytes += n;