]> sigrok.org Git - libsigrok.git/blame - src/tcp.c
tcp: check for poll(2) and select(2), workaround for shutdown(2) API
[libsigrok.git] / src / tcp.c
CommitLineData
fcab496c
GS
1/*
2 * This file is part of the libsigrok project.
3 *
4 * Copyright (C) 2023 Gerhard Sittig <gerhard.sittig@gmx.net>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
8c542453 20#include "config.h"
fcab496c
GS
21
22/* TODO
23 * Can we sort these include directives? Or do the platform specific
24 * headers depend on a specific order? Experience from VXI maintenance
25 * suggests that some systems can be picky and it's hard to notice ...
26 * For now the include statements follow the scpi_tcp.c template.
27 */
28#if defined _WIN32
29#define _WIN32_WINNT 0x0501
30#include <winsock2.h>
31#include <ws2tcpip.h>
32#endif
33
34#include <errno.h>
35#include <glib.h>
fcab496c
GS
36#include <string.h>
37#include <unistd.h>
38
39#if !defined _WIN32
40#include <arpa/inet.h>
41#include <netdb.h>
42#include <netinet/in.h>
43#include <sys/socket.h>
44#include <sys/types.h>
45#endif
46
8c542453
GS
47#if HAVE_POLL
48#include <poll.h>
49#elif HAVE_SELECT
50#include <sys/select.h>
51#endif
52
fcab496c
GS
53#include <libsigrok/libsigrok.h>
54#include "libsigrok-internal.h"
55
8c542453
GS
56/*
57 * Workaround because Windows cannot simply use established identifiers.
58 * https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-shutdown
59 */
60#if !defined SHUT_RDWR && defined SD_BOTH
61# define SHUT_RDWR SD_BOTH
62#endif
63
fcab496c
GS
64#define LOG_PREFIX "tcp"
65
66/**
67 * Check whether a file descriptor is readable (without blocking).
68 *
69 * @param[in] fd The file descriptor to check for readability.
70 *
71 * @return TRUE when readable, FALSE when read would block or when
72 * readability could not get determined.
73 *
74 * @since 6.0
75 *
76 * TODO Move to common code, applies to non-sockets as well.
77 */
78SR_PRIV gboolean sr_fd_is_readable(int fd)
79{
8c542453 80#if HAVE_POLL
fcab496c
GS
81 struct pollfd fds[1];
82 int ret;
83
84 memset(fds, 0, sizeof(fds));
85 fds[0].fd = fd;
86 fds[0].events = POLLIN;
87 ret = poll(fds, ARRAY_SIZE(fds), -1);
88 if (ret < 0)
89 return FALSE;
90 if (!ret)
91 return FALSE;
92 if (!(fds[0].revents & POLLIN))
93 return FALSE;
94
95 return TRUE;
8c542453
GS
96#elif HAVE_SELECT
97 fd_set rfds;
98 struct timeval tv;
99 int ret;
100
101 FD_ZERO(&rfds);
102 FD_SET(fd, &rfds);
103 memset(&tv, 0, sizeof(tv));
104 ret = select(fd + 1, &rfds, NULL, NULL, &tv);
105 if (ret < 0)
106 return FALSE;
107 if (!ret)
108 return FALSE;
109 if (!FD_ISSET(fd, rfds))
110 return FALSE;
111 return TRUE;
112#else
113 (void)fd;
114 return FALSE;
115#endif
fcab496c
GS
116}
117
118/**
119 * Create a TCP communication instance.
120 *
121 * @param[in] host_addr The host name or IP address (a string).
122 * @param[in] tcp_port The TCP port number.
123 *
124 * @return A @ref sr_tcp_dev_inst structure on success. #NULL otherwise.
125 *
126 * @since 6.0
127 */
128SR_PRIV struct sr_tcp_dev_inst *sr_tcp_dev_inst_new(
129 const char *host_addr, const char *tcp_port)
130{
131 char *host, *port;
132 struct sr_tcp_dev_inst *tcp;
133
134 host = NULL;
135 if (host_addr && *host_addr)
136 host = g_strdup(host_addr);
137 port = NULL;
138 if (tcp_port && *tcp_port)
139 port = g_strdup(tcp_port);
140
141 tcp = g_malloc0(sizeof(*tcp));
142 if (!tcp)
143 return NULL;
144 tcp->host_addr = host;
145 tcp->tcp_port = port;
146 tcp->sock_fd = -1;
147 return tcp;
148}
149
150/**
151 * Release a TCP communication instance.
152 *
153 * @param[in] host_addr The host name or IP address (a string).
154 * @param[in] tcp_port The TCP port number.
155 *
156 * @return A @ref sr_tcp_dev_inst structure on success. #NULL otherwise.
157 *
158 * @since 6.0
159 */
160SR_PRIV void sr_tcp_dev_inst_free(struct sr_tcp_dev_inst *tcp)
161{
162
163 if (!tcp)
164 return;
165
166 (void)sr_tcp_disconnect(tcp);
167 g_free(tcp->host_addr);
168 g_free(tcp);
169}
170
171/**
172 * Construct display name for a TCP communication instance.
173 *
174 * @param[in] tcp The TCP communication instance to print the name of.
175 * @param[in] prefix An optional prefix text, or #NULL.
176 * @param[in] separator An optional separator character, or NUL.
177 * @param[out] path The caller provided buffer to fill in.
178 * @param[in] path_len The buffer's maximum length to fill in.
179 *
180 * @return SR_OK on success, SR_ERR_* otherwise.
181 *
182 * @since 6.0
183 */
184SR_PRIV int sr_tcp_get_port_path(struct sr_tcp_dev_inst *tcp,
185 const char *prefix, char separator, char *path, size_t path_len)
186{
187 char sep_text[2];
188
189 /* Only construct connection name for full parameter sets. */
190 if (!tcp || !tcp->host_addr || !tcp->tcp_port)
191 return SR_ERR_ARG;
192
193 /* Normalize input. Apply defaults. */
194 if (!prefix)
195 prefix = "";
196 if (!*prefix && !separator)
197 separator = ':';
198
199 /* Turn everything into strings. Simplifies the printf() call. */
200 sep_text[0] = separator;
201 sep_text[1] = '\0';
202
203 /* Construct the resulting connection name. */
204 snprintf(path, path_len, "%s%s%s%s%s",
205 prefix, *prefix ? sep_text : "",
206 tcp->host_addr, sep_text, tcp->tcp_port);
207 return SR_OK;
208}
209
210/**
211 * Connect to a remote TCP communication peer.
212 *
213 * @param[in] tcp The TCP communication instance to connect.
214 *
215 * @return SR_OK on success, SR_ERR_* otherwise.
216 *
217 * @since 6.0
218 */
219SR_PRIV int sr_tcp_connect(struct sr_tcp_dev_inst *tcp)
220{
221 struct addrinfo hints;
222 struct addrinfo *results, *r;
223 int ret;
224 int fd;
225
226 if (!tcp)
227 return SR_ERR_ARG;
228 if (!tcp->host_addr || !tcp->tcp_port)
229 return SR_ERR_ARG;
230
231 /* Lookup address information for the caller's spec. */
232 memset(&hints, 0, sizeof(hints));
233 hints.ai_family = AF_UNSPEC;
234 hints.ai_socktype = SOCK_STREAM;
235 hints.ai_protocol = IPPROTO_TCP;
236 ret = getaddrinfo(tcp->host_addr, tcp->tcp_port, &hints, &results);
237 if (ret != 0) {
238 sr_err("Address lookup failed: %s:%s: %s.",
239 tcp->host_addr, tcp->tcp_port, gai_strerror(ret));
240 return SR_ERR_DATA;
241 }
242
243 /* Try to connect using the resulting address details. */
244 fd = -1;
245 for (r = results; r; r = r->ai_next) {
246 fd = socket(r->ai_family, r->ai_socktype, r->ai_protocol);
247 if (fd < 0)
248 continue;
249 ret = connect(fd, r->ai_addr, r->ai_addrlen);
250 if (ret != 0) {
251 close(fd);
252 fd = -1;
253 continue;
254 }
255 break;
256 }
257 freeaddrinfo(results);
258 if (fd < 0) {
259 sr_err("Failed to connect to %s:%s: %s.",
260 tcp->host_addr, tcp->tcp_port, g_strerror(errno));
261 return SR_ERR_IO;
262 }
263
264 tcp->sock_fd = fd;
265 return SR_OK;
266}
267
268/**
269 * Disconnect from a remote TCP communication peer.
270 *
271 * @param[in] tcp The TCP communication instance to disconnect.
272 *
273 * @return SR_OK on success, SR_ERR_* otherwise.
274 *
275 * @since 6.0
276 */
277SR_PRIV int sr_tcp_disconnect(struct sr_tcp_dev_inst *tcp)
278{
279
280 if (!tcp)
281 return SR_ERR_ARG;
282
283 if (tcp->sock_fd < 0)
284 return SR_OK;
285
286 shutdown(tcp->sock_fd, SHUT_RDWR);
287 close(tcp->sock_fd);
288 tcp->sock_fd = -1;
289 return SR_OK;
290}
291
292/**
293 * Send transmit data to a TCP connection.
294 * Does a single operating system call, can return with short
295 * transmit byte counts. Will not continue after short writes,
296 * callers need to handle the condition.
297 *
298 * @param[in] tcp The TCP communication instance to send to.
299 * @param[in] data The data bytes to send.
300 * @param[in] dlen The number of bytes to send.
301 *
302 * @return Number of transmitted bytes on success, SR_ERR_* otherwise.
303 *
304 * @since 6.0
305 */
306SR_PRIV int sr_tcp_write_bytes(struct sr_tcp_dev_inst *tcp,
307 const uint8_t *data, size_t dlen)
308{
309 ssize_t rc;
310 size_t written;
311
312 if (!tcp)
313 return SR_ERR_ARG;
314 if (!dlen)
315 return 0;
316 if (!data)
317 return SR_ERR_ARG;
318
319 if (tcp->sock_fd < 0)
320 return SR_ERR_IO;
321
322 rc = send(tcp->sock_fd, data, dlen, 0);
323 if (rc < 0)
324 return SR_ERR_IO;
325 written = (size_t)rc;
326 return written;
327}
328
329/**
330 * Fetch receive data from a TCP connection.
331 * Does a single operating system call, can return with short
332 * receive byte counts. Will not continue after short reads,
333 * callers need to handle the condition.
334 *
335 * @param[in] tcp The TCP communication instance to read from.
336 * @param[in] data Caller provided buffer for receive data.
337 * @param[in] dlen The maximum number of bytes to receive.
338 * @param[in] nonblocking Whether to block for receive data.
339 *
340 * @return Number of received bytes on success, SR_ERR_* otherwise.
341 *
342 * @since 6.0
343 */
344SR_PRIV int sr_tcp_read_bytes(struct sr_tcp_dev_inst *tcp,
345 uint8_t *data, size_t dlen, gboolean nonblocking)
346{
347 ssize_t rc;
348 size_t got;
349
350 if (!tcp)
351 return SR_ERR_ARG;
352 if (!dlen)
353 return 0;
354 if (!data)
355 return SR_ERR_ARG;
356
357 if (tcp->sock_fd < 0)
358 return SR_ERR_IO;
359
360 if (nonblocking && !sr_fd_is_readable(tcp->sock_fd))
361 return 0;
362
363 rc = recv(tcp->sock_fd, data, dlen, 0);
364 if (rc < 0)
365 return SR_ERR_IO;
366 got = (size_t)rc;
367 return got;
368}
369
370/**
371 * Register receive callback for a TCP connection.
372 * The connection must have been established before. The callback
373 * gets invoked when receive data is available. Or when a timeout
374 * has expired.
375 *
376 * This is a simple wrapper around @ref sr_session_source_add().
377 *
378 * @param[in] session See @ref sr_session_source_add().
379 * @param[in] tcp The TCP communication instance to read from.
380 * @param[in] events See @ref sr_session_source_add().
381 * @param[in] timeout See @ref sr_session_source_add().
382 * @param[in] cb See @ref sr_session_source_add().
383 * @param[in] cb_data See @ref sr_session_source_add().
384 *
385 * @return SR_OK on success, SR_ERR* otherwise.
386 *
387 * @since 6.0
388 */
389SR_PRIV int sr_tcp_source_add(struct sr_session *session,
390 struct sr_tcp_dev_inst *tcp, int events, int timeout,
391 sr_receive_data_callback cb, void *cb_data)
392{
393 if (!tcp || tcp->sock_fd < 0)
394 return SR_ERR_ARG;
395 return sr_session_source_add(session, tcp->sock_fd,
396 events, timeout, cb, cb_data);
397}
398
399/**
400 * Unregister receive callback for a TCP connection.
401 *
402 * This is a simple wrapper around @ref sr_session_source_remove().
403 *
404 * @param[in] session See @ref sr_session_source_remove().
405 * @param[in] tcp The TCP communication instance to unregister.
406 *
407 * @return SR_OK on success, SR_ERR* otherwise.
408 *
409 * @since 6.0
410 */
411SR_PRIV int sr_tcp_source_remove(struct sr_session *session,
412 struct sr_tcp_dev_inst *tcp)
413{
414 if (!tcp || tcp->sock_fd < 0)
415 return SR_ERR_ARG;
416 return sr_session_source_remove(session, tcp->sock_fd);
417}