From 4bd49e679569343cb115210680f46a9912478e25 Mon Sep 17 00:00:00 2001 From: mheinr Date: Mon, 30 Mar 2026 22:59:56 +0200 Subject: [PATCH 1/5] Update hyperion_client.c Treat socket timeouts (EAGAIN/EWOULDBLOCK) as a non-error in hyperion_read(), returning 0 instead of -1. This prevents unnecessary disconnects when no data is available yet. This fix was done by Claude 4.6 --- src/hyperion_client.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/hyperion_client.c b/src/hyperion_client.c index f732a0b..c713590 100644 --- a/src/hyperion_client.c +++ b/src/hyperion_client.c @@ -49,10 +49,16 @@ int hyperion_read() return -1; uint8_t headbuff[4]; int n = read(sockfd, headbuff, 4); + if (n < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) + return 0; // timeout is not an error, just no data yet + if (n < 0) + return -1; uint32_t messageSize = ((headbuff[0] << 24) & 0xFF000000) | ((headbuff[1] << 16) & 0x00FF0000) | ((headbuff[2] << 8) & 0x0000FF00) | ((headbuff[3]) & 0x000000FF); - if (n < 0 || messageSize >= sizeof(recvBuff)) + if (messageSize >= sizeof(recvBuff)) return -1; n = read(sockfd, recvBuff, messageSize); + if (n < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) + return 0; // timeout is not an error, just no data yet if (n < 0) return -1; _parse_reply(hyperionnet_Reply_as_root(recvBuff)); From c6dc52069f5944e41091436047637be09912cb40 Mon Sep 17 00:00:00 2001 From: mheinr Date: Tue, 31 Mar 2026 19:09:55 +0200 Subject: [PATCH 2/5] Update hyperion_client.c see: https://github.com/webosbrew/hyperion-webos/pull/141 --- src/hyperion_client.c | 93 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 76 insertions(+), 17 deletions(-) diff --git a/src/hyperion_client.c b/src/hyperion_client.c index c713590..1163013 100644 --- a/src/hyperion_client.c +++ b/src/hyperion_client.c @@ -26,7 +26,47 @@ static bool _registered = false; static int _priority = 0; static const char* _origin = NULL; static bool _connected = false; -unsigned char recvBuff[1024]; +static unsigned char recvBuff[1024]; + +enum rx_phase { RX_HEADER, RX_BODY }; + +static struct { + enum rx_phase phase; + uint8_t header[4]; + uint32_t body_len; + uint32_t received; +} rx; + +static void _rx_reset(void) +{ + memset(&rx, 0, sizeof(rx)); +} + +/** + * Try to read more bytes into buf (at offset *received, total needed: len). + * Returns: >0 bytes read this call + * 0 EAGAIN/EWOULDBLOCK (non-fatal, try again later) + * -1 fatal (EOF or real error) + * EINTR is retried internally. + */ +static int _read_exact(int fd, void* buf, size_t len, uint32_t* received) +{ + for (;;) { + ssize_t n = read(fd, (uint8_t*)buf + *received, len - *received); + if (n > 0) { + *received += (uint32_t)n; + return (int)n; + } + if (n == 0) + return 0; /* timeout or EOF — treat as transient */ + /* n < 0 */ + if (errno == EINTR) + continue; + if (errno == EAGAIN || errno == EWOULDBLOCK) + return 0; + return -1; + } +} int hyperion_client(const char* origin, const char* hostname, int port, bool unix_socket, int priority) { @@ -36,32 +76,50 @@ int hyperion_client(const char* origin, const char* hostname, int port, bool uni _registered = false; sockfd = 0; + int ret; if (unix_socket) { - return _connect_unix_socket(hostname); + ret = _connect_unix_socket(hostname); } else { - return _connect_inet_socket(hostname, port); + ret = _connect_inet_socket(hostname, port); } + _rx_reset(); + return ret; } int hyperion_read() { if (!sockfd) return -1; - uint8_t headbuff[4]; - int n = read(sockfd, headbuff, 4); - if (n < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) - return 0; // timeout is not an error, just no data yet - if (n < 0) - return -1; - uint32_t messageSize = ((headbuff[0] << 24) & 0xFF000000) | ((headbuff[1] << 16) & 0x00FF0000) | ((headbuff[2] << 8) & 0x0000FF00) | ((headbuff[3]) & 0x000000FF); - if (messageSize >= sizeof(recvBuff)) - return -1; - n = read(sockfd, recvBuff, messageSize); - if (n < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) - return 0; // timeout is not an error, just no data yet - if (n < 0) - return -1; + + int ret; + + /* Phase 1: accumulate 4-byte header */ + if (rx.phase == RX_HEADER) { + ret = _read_exact(sockfd, rx.header, 4, &rx.received); + if (ret < 0) return -1; + if (rx.received < 4) return 0; /* partial or EAGAIN */ + + rx.body_len = ((uint32_t)rx.header[0] << 24) + | ((uint32_t)rx.header[1] << 16) + | ((uint32_t)rx.header[2] << 8) + | (uint32_t)rx.header[3]; + + if (rx.body_len == 0 || rx.body_len >= sizeof(recvBuff)) { + _rx_reset(); + return -1; + } + + rx.phase = RX_BODY; + rx.received = 0; + } + + /* Phase 2: accumulate body */ + ret = _read_exact(sockfd, recvBuff, rx.body_len, &rx.received); + if (ret < 0) return -1; + if (rx.received < rx.body_len) return 0; /* partial or EAGAIN */ + _parse_reply(hyperionnet_Reply_as_root(recvBuff)); + _rx_reset(); return 0; } @@ -71,6 +129,7 @@ int hyperion_destroy() return 0; close(sockfd); sockfd = 0; + _rx_reset(); return 0; } From 8eb8ff93ce385bd03316372c296e4d9fdd68983d Mon Sep 17 00:00:00 2001 From: mheinr Date: Tue, 31 Mar 2026 21:20:20 +0200 Subject: [PATCH 3/5] Update hyperion_client.c 1. Add `#include ` to system includes 2. Add `#define RX_STALE_SECS 120` after `recvBuff` 3. Add `static time_t rx_last_data;` after `rx` struct 4. Add `rx_last_data = time(NULL);` to `_rx_reset()` 5. Add `_check_stale()` helper after `_read_exact()`, before `hyperion_client()` 6. Modify `hyperion_read()`: - After `_read_exact()` returns > 0: `rx_last_data = time(NULL);` - Replace `return 0` at idle exits with `return _check_stale();` --- src/hyperion_client.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/hyperion_client.c b/src/hyperion_client.c index 1163013..1ed0832 100644 --- a/src/hyperion_client.c +++ b/src/hyperion_client.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include "hyperion_reply_reader.h" @@ -28,6 +29,8 @@ static const char* _origin = NULL; static bool _connected = false; static unsigned char recvBuff[1024]; +#define RX_STALE_SECS 120 + enum rx_phase { RX_HEADER, RX_BODY }; static struct { @@ -37,9 +40,12 @@ static struct { uint32_t received; } rx; +static time_t rx_last_data; + static void _rx_reset(void) { memset(&rx, 0, sizeof(rx)); + rx_last_data = time(NULL); } /** @@ -68,6 +74,16 @@ static int _read_exact(int fd, void* buf, size_t len, uint32_t* received) } } +static int _check_stale(void) +{ + if (difftime(time(NULL), rx_last_data) > RX_STALE_SECS) { + WARN("No data received for %d seconds, assuming stale connection", RX_STALE_SECS); + _rx_reset(); + return -1; + } + return 0; +} + int hyperion_client(const char* origin, const char* hostname, int port, bool unix_socket, int priority) { _origin = origin; @@ -97,7 +113,8 @@ int hyperion_read() if (rx.phase == RX_HEADER) { ret = _read_exact(sockfd, rx.header, 4, &rx.received); if (ret < 0) return -1; - if (rx.received < 4) return 0; /* partial or EAGAIN */ + if (ret > 0) rx_last_data = time(NULL); + if (rx.received < 4) return _check_stale(); rx.body_len = ((uint32_t)rx.header[0] << 24) | ((uint32_t)rx.header[1] << 16) @@ -116,7 +133,8 @@ int hyperion_read() /* Phase 2: accumulate body */ ret = _read_exact(sockfd, recvBuff, rx.body_len, &rx.received); if (ret < 0) return -1; - if (rx.received < rx.body_len) return 0; /* partial or EAGAIN */ + if (ret > 0) rx_last_data = time(NULL); + if (rx.received < rx.body_len) return _check_stale(); _parse_reply(hyperionnet_Reply_as_root(recvBuff)); _rx_reset(); From 23ba1a2d72cc0d4946805611774f3564ccc43096 Mon Sep 17 00:00:00 2001 From: mheinr Date: Tue, 31 Mar 2026 21:51:39 +0200 Subject: [PATCH 4/5] Update hyperion_client.c fix linting --- src/hyperion_client.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/hyperion_client.c b/src/hyperion_client.c index 1ed0832..112df7a 100644 --- a/src/hyperion_client.c +++ b/src/hyperion_client.c @@ -112,29 +112,35 @@ int hyperion_read() /* Phase 1: accumulate 4-byte header */ if (rx.phase == RX_HEADER) { ret = _read_exact(sockfd, rx.header, 4, &rx.received); - if (ret < 0) return -1; - if (ret > 0) rx_last_data = time(NULL); - if (rx.received < 4) return _check_stale(); + if (ret < 0) + return -1; + if (ret > 0) + rx_last_data = time(NULL); + if (rx.received < 4) + return _check_stale(); rx.body_len = ((uint32_t)rx.header[0] << 24) - | ((uint32_t)rx.header[1] << 16) - | ((uint32_t)rx.header[2] << 8) - | (uint32_t)rx.header[3]; + | ((uint32_t)rx.header[1] << 16) + | ((uint32_t)rx.header[2] << 8) + | (uint32_t)rx.header[3]; if (rx.body_len == 0 || rx.body_len >= sizeof(recvBuff)) { _rx_reset(); return -1; } - rx.phase = RX_BODY; + rx.phase = RX_BODY; rx.received = 0; } /* Phase 2: accumulate body */ ret = _read_exact(sockfd, recvBuff, rx.body_len, &rx.received); - if (ret < 0) return -1; - if (ret > 0) rx_last_data = time(NULL); - if (rx.received < rx.body_len) return _check_stale(); + if (ret < 0) + return -1; + if (ret > 0) + rx_last_data = time(NULL); + if (rx.received < rx.body_len) + return _check_stale(); _parse_reply(hyperionnet_Reply_as_root(recvBuff)); _rx_reset(); From 9e92d0f6d4737675d838e4ad741209786b20701b Mon Sep 17 00:00:00 2001 From: mheinr Date: Thu, 2 Apr 2026 21:16:06 +0200 Subject: [PATCH 5/5] Update hyperion_client.c run clang-format --- src/hyperion_client.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/hyperion_client.c b/src/hyperion_client.c index 112df7a..6ab17c4 100644 --- a/src/hyperion_client.c +++ b/src/hyperion_client.c @@ -31,13 +31,14 @@ static unsigned char recvBuff[1024]; #define RX_STALE_SECS 120 -enum rx_phase { RX_HEADER, RX_BODY }; +enum rx_phase { RX_HEADER, + RX_BODY }; static struct { enum rx_phase phase; - uint8_t header[4]; - uint32_t body_len; - uint32_t received; + uint8_t header[4]; + uint32_t body_len; + uint32_t received; } rx; static time_t rx_last_data;