]> sigrok.org Git - libsigrok.git/blob - src/tcp.c
output/csv: use intermediate time_t var, silence compiler warning
[libsigrok.git] / src / tcp.c
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
20 #include "config.h"
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>
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
47 #if HAVE_POLL
48 #include <poll.h>
49 #elif HAVE_SELECT
50 #include <sys/select.h>
51 #endif
52
53 #include <libsigrok/libsigrok.h>
54 #include "libsigrok-internal.h"
55
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
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  */
78 SR_PRIV gboolean sr_fd_is_readable(int fd)
79 {
80 #if HAVE_POLL
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;
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
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  */
128 SR_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  */
160 SR_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  */
184 SR_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  */
219 SR_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  */
277 SR_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  */
306 SR_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  */
344 SR_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  */
389 SR_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  */
411 SR_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 }