Hamlib/src/network.c

1921 wiersze
52 KiB
C
Czysty Zwykły widok Historia

/*
* Hamlib Interface - network communication low-level support
* Copyright (c) 2021-2023 by Mikael Nousiainen
* Copyright (c) 2000-2012 by Stephane Fillod
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
/**
* \addtogroup rig_internal
* @{
*/
/**
* \brief Network port IO
* \file network.c
*/
/* Forcing WINVER in MinGW yanks in getaddrinfo(), but locks out Win95/Win98 */
/* #define WINVER 0x0501 */
#include <hamlib/config.h>
#include <stdlib.h>
#include <stdio.h> /* Standard input/output definitions */
#include <string.h> /* String function definitions */
#include <unistd.h> /* UNIX standard function definitions */
#include <fcntl.h> /* File control definitions */
#include <errno.h> /* Error number definitions */
#include <sys/types.h>
#include <signal.h>
#include <pthread.h>
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#if HAVE_NETDB_H
# include <netdb.h>
#endif
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif
#if defined (HAVE_SYS_SOCKET_H) && defined (HAVE_SYS_IOCTL_H)
# include <sys/socket.h>
# include <sys/ioctl.h>
#elif HAVE_WS2TCPIP_H
#undef _WIN32_WINNT
// We need inet_pton to get defined and 0x0600 does it
// Eventually we should be able to get rid of this hack
#define _WIN32_WINNT 0x0600
# include <ws2tcpip.h>
#undef _WIN32_WINNT
// Then we'll go back to Server 2003
#define _WIN32_WINNT 0x0502
# if defined(HAVE_WSPIAPI_H)
# include <wspiapi.h>
# endif
#endif
2017-08-05 14:09:12 +00:00
#include <hamlib/rig.h>
#include "network.h"
#include "misc.h"
#include "asyncpipe.h"
#include "snapshot_data.h"
#ifdef HAVE_WINDOWS_H
2023-10-12 04:22:42 +00:00
// cppcheck-suppress missingInclude
#include "io.h"
#endif
#ifdef __MINGW32__
#include <winsock2.h>
#include <windows.h>
#include <iphlpapi.h>
static int wsstarted;
static int is_networked(char *address, int address_length);
#endif
2020-04-09 22:56:19 +00:00
//! @cond Doxygen_Suppress
2020-04-22 17:26:08 +00:00
#define NET_BUFFER_SIZE 8192
2020-04-09 22:56:19 +00:00
//! @endcond
#define MULTICAST_PUBLISHER_DATA_PACKET_TYPE_POLL 0x01
#define MULTICAST_PUBLISHER_DATA_PACKET_TYPE_TRANSCEIVE 0x02
#define MULTICAST_PUBLISHER_DATA_PACKET_TYPE_SPECTRUM 0x03
#pragma pack(push,1)
typedef struct multicast_publisher_data_packet_s
{
uint8_t type;
uint8_t padding;
uint16_t data_length;
} __attribute__((packed)) multicast_publisher_data_packet;
#pragma pack(pop)
typedef struct multicast_publisher_args_s
{
RIG *rig;
int socket_fd;
const char *multicast_addr;
int multicast_port;
#if defined(WIN32) && defined(HAVE_WINDOWS_H)
hamlib_async_pipe_t *data_pipe;
#else
int data_write_fd;
int data_read_fd;
#endif
#ifdef HAVE_PTHREAD
pthread_mutex_t write_lock;
#endif
} multicast_publisher_args;
typedef struct multicast_publisher_priv_data_s
{
pthread_t thread_id;
multicast_publisher_args args;
} multicast_publisher_priv_data;
typedef struct multicast_receiver_args_s
{
RIG *rig;
int socket_fd;
const char *multicast_addr;
int multicast_port;
} multicast_receiver_args;
typedef struct multicast_receiver_priv_data_s
{
pthread_t thread_id;
multicast_receiver_args args;
} multicast_receiver_priv_data;
2017-08-05 14:09:12 +00:00
static void handle_error(enum rig_debug_level_e lvl, const char *msg)
{
2017-08-05 14:09:12 +00:00
int e;
#ifdef __MINGW32__
2017-08-05 14:09:12 +00:00
LPVOID lpMsgBuf;
lpMsgBuf = (LPVOID)"Unknown error";
e = WSAGetLastError();
if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
e,
2017-08-05 14:09:12 +00:00
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
// Default language
(LPTSTR)&lpMsgBuf,
0,
NULL))
{
2020-02-23 17:26:09 +00:00
rig_debug(lvl, "%s: Network error %d: %s\n", msg, e, (char *)lpMsgBuf);
2017-08-05 14:09:12 +00:00
LocalFree(lpMsgBuf);
}
else
{
2017-08-05 14:09:12 +00:00
rig_debug(lvl, "%s: Network error %d\n", msg, e);
}
#else
2017-08-05 14:09:12 +00:00
e = errno;
rig_debug(lvl, "%s: Network error %d: %s\n", msg, e, strerror(e));
#endif
}
2023-10-20 16:52:59 +00:00
#define TRACE rig_debug(RIG_DEBUG_ERR, "TRACE %s(%d)\n", __func__,__LINE__);
int network_init()
{
2023-10-13 22:36:29 +00:00
int retval = -RIG_EINTERNAL;
#ifdef __MINGW32__
2017-08-05 14:09:12 +00:00
WSADATA wsadata;
if (wsstarted == 0)
{
2023-10-12 04:22:42 +00:00
retval = WSAStartup(MAKEWORD(1, 1), &wsadata);
2023-10-12 04:22:42 +00:00
if (retval == 0)
{
wsstarted = 1;
rig_debug(RIG_DEBUG_VERBOSE, "%s: WSAStartup OK\n", __func__);
}
else
{
rig_debug(RIG_DEBUG_ERR, "%s: error creating socket, WSAStartup ret=%d\n",
2023-10-12 04:22:42 +00:00
__func__, retval);
2022-02-05 21:27:43 +00:00
return (-RIG_EIO);
}
2017-08-05 14:09:12 +00:00
}
else // already started
{
retval = RIG_OK;
}
2023-12-16 17:37:58 +00:00
2023-10-13 22:36:29 +00:00
#else
retval = RIG_OK;
2017-08-05 14:09:12 +00:00
#endif
2023-10-12 04:22:42 +00:00
return retval;
}
/**
* \brief Open network port using rig.state data
*
* Open Open network port using rig.state data.
* NB: The signal PIPE will be ignored for the whole application.
*
* \param rp Port data structure (must spec port id eg hostname:port)
* \param default_port Default network socket port
* \return RIG_OK or < 0 if error
*/
int network_open(hamlib_port_t *rp, int default_port)
{
int fd; /* File descriptor for the port */
int status;
struct addrinfo hints, *res, *saved_res;
struct in6_addr serveraddr;
struct sockaddr_in client;
char hoststr[256], portstr[6] = "";
2021-10-24 05:03:48 +00:00
#ifdef __MINGW32__
status = network_init();
2022-02-05 21:27:43 +00:00
if (status != RIG_OK) { return (status); }
2021-11-28 18:41:10 +00:00
2021-10-24 05:03:48 +00:00
#endif
if (!rp)
{
2022-02-05 21:27:43 +00:00
return (-RIG_EINVAL);
2017-08-05 14:09:12 +00:00
}
memset(&hints, 0, sizeof(hints));
hints.ai_flags = NI_NUMERICSERV;
hints.ai_family = AF_UNSPEC;
2017-08-05 14:09:12 +00:00
if (rp->type.rig == RIG_PORT_UDP_NETWORK)
{
2017-08-05 14:09:12 +00:00
hints.ai_socktype = SOCK_DGRAM;
}
else
{
2017-08-05 14:09:12 +00:00
hints.ai_socktype = SOCK_STREAM;
}
if (rp->pathname[0] == ':' && rp->pathname[1] != ':')
{
SNPRINTF(portstr, sizeof(portstr) - 1, "%s", rp->pathname + 1);
}
else
{
if (strlen(rp->pathname))
{
status = parse_hoststr(rp->pathname, sizeof(rp->pathname), hoststr, portstr);
2022-02-05 21:27:43 +00:00
if (status != RIG_OK) { return (status); }
2021-01-17 14:19:12 +00:00
rig_debug(RIG_DEBUG_TRACE, "%s: hoststr=%s, portstr=%s\n", __func__, hoststr,
portstr);
2017-08-05 14:09:12 +00:00
}
if (strlen(portstr) == 0)
{
SNPRINTF(portstr, sizeof(portstr), "%d", default_port);
}
}
status = inet_pton(AF_INET, hoststr, &serveraddr);
if (status == 1) /* valid IPv4 address */
{
hints.ai_family = AF_INET;
hints.ai_flags |= AI_NUMERICHOST;
}
else
{
status = inet_pton(AF_INET6, hoststr, &serveraddr);
2020-06-23 04:46:27 +00:00
if (status == 1) /* valid IPv6 address */
{
hints.ai_family = AF_INET6;
hints.ai_flags |= AI_NUMERICHOST;
2017-08-05 14:09:12 +00:00
}
}
status = getaddrinfo(hoststr, portstr, &hints, &res);
if (status == 0 && res->ai_family == AF_INET6)
{
2021-02-05 05:47:59 +00:00
rig_debug(RIG_DEBUG_TRACE, "%s: Using IPV6\n", __func__);
//inet_pton(AF_INET6, hoststr, &h_addr.sin6_addr);
}
if (status != 0)
{
2017-08-05 14:09:12 +00:00
rig_debug(RIG_DEBUG_ERR,
"%s: cannot get host \"%s\": %s\n",
__func__,
rp->pathname,
gai_strerror(status));
2022-02-05 21:27:43 +00:00
return (-RIG_ECONF);
2017-08-05 14:09:12 +00:00
}
saved_res = res;
/* we don't want a signal when connection get broken */
#ifdef SIGPIPE
2017-08-05 14:09:12 +00:00
signal(SIGPIPE, SIG_IGN);
#endif
do
{
char msg[1024];
2017-08-05 14:09:12 +00:00
fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (fd < 0)
{
2017-08-05 14:09:12 +00:00
handle_error(RIG_DEBUG_ERR, "socket");
freeaddrinfo(saved_res);
2022-02-05 21:27:43 +00:00
return (-RIG_EIO);
2017-08-05 14:09:12 +00:00
}
2020-01-15 05:36:01 +00:00
if (connect(fd, res->ai_addr, res->ai_addrlen) == 0)
{
2017-08-05 14:09:12 +00:00
break;
}
2019-02-07 17:46:52 +00:00
SNPRINTF(msg, sizeof(msg), "connect to %s failed, (trying next interface)",
2019-02-07 17:46:52 +00:00
rp->pathname);
2018-04-14 15:46:13 +00:00
handle_error(RIG_DEBUG_WARN, msg);
#ifdef __MINGW32__
2017-08-05 14:09:12 +00:00
closesocket(fd);
#else
2017-08-05 14:09:12 +00:00
close(fd);
#endif
}
while ((res = res->ai_next) != NULL);
2017-08-05 14:09:12 +00:00
freeaddrinfo(saved_res);
if (NULL == res)
{
2017-08-05 14:09:12 +00:00
rig_debug(RIG_DEBUG_ERR,
"%s: failed to connect to %s\n",
__func__,
rp->pathname);
2022-02-05 21:27:43 +00:00
return (-RIG_EIO);
2017-08-05 14:09:12 +00:00
}
2017-08-05 14:09:12 +00:00
rp->fd = fd;
socklen_t clientLen = sizeof(client);
getsockname(rp->fd, (struct sockaddr *)&client, &clientLen);
rig_debug(RIG_DEBUG_TRACE, "%s: client port=%d\n", __func__, client.sin_port);
rp->client_port = client.sin_port;
2022-02-05 21:27:43 +00:00
return (RIG_OK);
}
2017-08-05 14:09:12 +00:00
/**
* \brief Clears any data in the read buffer of the socket
*
* \param rp Port data structure
*/
2017-08-05 14:09:12 +00:00
void network_flush(hamlib_port_t *rp)
{
#ifdef __MINGW32__
2020-01-15 05:36:01 +00:00
ULONG len;
#else
2020-01-15 05:36:01 +00:00
uint len;
#endif
2017-08-05 14:09:12 +00:00
char buffer[NET_BUFFER_SIZE] = { 0 };
rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);
for (;;)
{
2019-12-08 23:09:08 +00:00
int ret;
len = 0;
#ifdef __MINGW32__
ret = ioctlsocket(rp->fd, FIONREAD, &len);
#else
ret = ioctl(rp->fd, FIONREAD, &len);
#endif
2019-02-07 17:46:52 +00:00
if (ret != 0)
{
2019-11-30 16:16:28 +00:00
rig_debug(RIG_DEBUG_ERR, "%s: ioctl err '%s'\n", __func__, strerror(errno));
2019-02-07 17:46:52 +00:00
break;
}
2019-02-07 17:46:52 +00:00
if (len > 0)
{
int len_read = 0;
2017-08-05 14:09:12 +00:00
rig_debug(RIG_DEBUG_WARN,
"%s: network data clear d: ret=%d, len=%d, '%s'\n",
2017-08-05 14:09:12 +00:00
__func__,
ret, (int)len, buffer);
2019-02-07 17:46:52 +00:00
len_read = recv(rp->fd, buffer, len < NET_BUFFER_SIZE ? len : NET_BUFFER_SIZE,
0);
if (len_read < 0) // -1 indicates error occurred
{
rig_debug(RIG_DEBUG_ERR, "%s: read error '%s'\n", __func__, strerror(errno));
break;
}
2019-02-07 17:46:52 +00:00
rig_debug(RIG_DEBUG_WARN,
2021-11-28 18:41:10 +00:00
"%s: network data cleared: ret=%d, len_read=%d/0x%x\n",
__func__,
2021-11-28 18:41:10 +00:00
ret, len_read, len_read);
dump_hex((unsigned char *)buffer, len_read);
}
2019-11-30 16:19:08 +00:00
else
{
break;
}
}
}
2017-08-05 14:09:12 +00:00
2020-04-09 22:56:19 +00:00
//! @cond Doxygen_Suppress
int network_close(hamlib_port_t *rp)
{
int ret = 0;
2017-08-05 14:09:12 +00:00
if (rp->fd > 0)
{
#ifdef __MINGW32__
ret = closesocket(rp->fd);
#else
ret = close(rp->fd);
#endif
rig_debug(RIG_DEBUG_VERBOSE, "%s: close socket ret=%d\n", __func__, ret);
rp->fd = 0;
}
#ifdef __MINGW32__
2017-08-05 14:09:12 +00:00
if (wsstarted)
{
ret = WSACleanup();
rig_debug(RIG_DEBUG_VERBOSE, "%s: WSACleanup ret=%d\n", __func__, ret);
wsstarted = 0;
2017-08-05 14:09:12 +00:00
}
#endif
2022-02-05 21:27:43 +00:00
return (ret);
}
2020-04-09 22:56:19 +00:00
//! @endcond
extern void sync_callback(int lock);
#ifdef HAVE_PTHREAD
//! @cond Doxygen_Suppress
#define MULTICAST_DATA_PIPE_TIMEOUT_MILLIS 1000
#define MULTICAST_DATA_PIPE_TIMEOUT_USEC 100000
#if defined(WIN32) && defined(HAVE_WINDOWS_H)
2022-02-05 21:27:43 +00:00
static int multicast_publisher_create_data_pipe(multicast_publisher_priv_data
*mcast_publisher_priv)
{
int status;
2022-02-05 21:27:43 +00:00
status = async_pipe_create(&mcast_publisher_priv->args.data_pipe,
PIPE_BUFFER_SIZE_DEFAULT, MULTICAST_DATA_PIPE_TIMEOUT_MILLIS);
if (status != 0)
{
2022-02-05 21:27:43 +00:00
rig_debug(RIG_DEBUG_ERR,
"%s: multicast publisher data pipe creation failed with status=%d, err=%s\n",
__func__,
status, strerror(errno));
return (-RIG_EINTERNAL);
}
2022-02-05 21:27:43 +00:00
return (RIG_OK);
}
2022-02-05 21:27:43 +00:00
static void multicast_publisher_close_data_pipe(multicast_publisher_priv_data
*mcast_publisher_priv)
{
2022-02-05 21:27:43 +00:00
if (mcast_publisher_priv->args.data_pipe != NULL)
{
async_pipe_close(mcast_publisher_priv->args.data_pipe);
mcast_publisher_priv->args.data_pipe = NULL;
}
}
2022-02-05 21:27:43 +00:00
static int multicast_publisher_write_data(multicast_publisher_args
*mcast_publisher_args, size_t length, const unsigned char *data)
{
ssize_t result;
2022-02-05 21:27:43 +00:00
result = async_pipe_write(mcast_publisher_args->data_pipe, data, length,
MULTICAST_DATA_PIPE_TIMEOUT_MILLIS);
if (result < 0)
{
2022-02-05 21:27:43 +00:00
rig_debug(RIG_DEBUG_ERR,
"%s: error writing to multicast publisher data pipe, result=%d\n", __func__,
(int)result);
return (-RIG_EIO);
}
if (result != length)
{
2022-02-05 21:27:43 +00:00
rig_debug(RIG_DEBUG_ERR,
"%s: could not write to multicast publisher data pipe, expected %d bytes, wrote %d bytes\n",
__func__, (int)length, (int)result);
return (-RIG_EIO);
}
2022-02-05 21:27:43 +00:00
return (RIG_OK);
}
2022-02-05 21:27:43 +00:00
static int multicast_publisher_read_data(multicast_publisher_args
2023-10-15 13:09:19 +00:00
const *mcast_publisher_args, size_t length, unsigned char *data)
{
ssize_t result;
result = async_pipe_wait_for_data(mcast_publisher_args->data_pipe, 100);
2022-02-05 21:27:43 +00:00
if (result < 0)
{
// Timeout is expected when there is no data
if (result != -RIG_ETIMEOUT)
{
2022-02-05 21:27:43 +00:00
rig_debug(RIG_DEBUG_ERR,
"%s: error waiting for multicast publisher data, result=%ld\n", __func__,
(long) result);
}
2022-02-05 21:27:43 +00:00
return (result);
}
2022-02-05 21:27:43 +00:00
result = async_pipe_read(mcast_publisher_args->data_pipe, data, length,
MULTICAST_DATA_PIPE_TIMEOUT_MILLIS);
if (result < 0)
{
2022-02-05 21:27:43 +00:00
rig_debug(RIG_DEBUG_ERR,
"%s: error reading multicast publisher data, result=%ld\n", __func__,
(long) result);
return (-RIG_EIO);
}
if (result != length)
{
2022-02-05 21:27:43 +00:00
rig_debug(RIG_DEBUG_ERR,
"%s: could not read from multicast publisher data pipe, expected %ld bytes, read %ld bytes\n",
__func__, (long) length, (long) result);
return (-RIG_EIO);
}
2022-02-05 21:27:43 +00:00
return (RIG_OK);
}
#else
2022-02-05 21:27:43 +00:00
static int multicast_publisher_create_data_pipe(multicast_publisher_priv_data
*mcast_publisher_priv)
{
int data_pipe_fds[2];
int status;
status = pipe(data_pipe_fds);
2022-02-05 21:27:43 +00:00
if (status != 0)
{
2022-02-05 21:27:43 +00:00
rig_debug(RIG_DEBUG_ERR,
"%s: multicast publisher data pipe creation failed with status=%d, err=%s\n",
__func__,
status, strerror(errno));
return (-RIG_EINTERNAL);
}
int flags = fcntl(data_pipe_fds[0], F_GETFD);
flags |= O_NONBLOCK;
2022-02-05 21:27:43 +00:00
if (fcntl(data_pipe_fds[0], F_SETFD, flags))
{
2022-02-05 21:27:43 +00:00
rig_debug(RIG_DEBUG_ERR, "%s: error setting O_NONBLOCK on pipe=%s\n", __func__,
strerror(errno));
}
mcast_publisher_priv->args.data_read_fd = data_pipe_fds[0];
mcast_publisher_priv->args.data_write_fd = data_pipe_fds[1];
2022-02-05 21:27:43 +00:00
return (RIG_OK);
}
2022-02-05 21:27:43 +00:00
static void multicast_publisher_close_data_pipe(multicast_publisher_priv_data
*mcast_publisher_priv)
{
2022-02-05 21:27:43 +00:00
if (mcast_publisher_priv->args.data_read_fd != -1)
{
close(mcast_publisher_priv->args.data_read_fd);
mcast_publisher_priv->args.data_read_fd = -1;
}
2022-02-05 21:27:43 +00:00
if (mcast_publisher_priv->args.data_write_fd != -1)
{
close(mcast_publisher_priv->args.data_write_fd);
mcast_publisher_priv->args.data_write_fd = -1;
}
}
2023-10-12 04:22:42 +00:00
static int multicast_publisher_write_data(const multicast_publisher_args
2022-02-05 21:27:43 +00:00
*mcast_publisher_args, size_t length, const unsigned char *data)
{
int fd = mcast_publisher_args->data_write_fd;
ssize_t result;
result = write(fd, data, length);
2022-02-05 21:27:43 +00:00
if (result < 0)
{
2022-02-05 21:27:43 +00:00
rig_debug(RIG_DEBUG_ERR,
"%s: error writing to multicast publisher data pipe, result=%d, err=%s\n",
__func__,
(int)result, strerror(errno));
return (-RIG_EIO);
}
if (result != length)
{
2022-02-05 21:27:43 +00:00
rig_debug(RIG_DEBUG_ERR,
"%s: could not write to multicast publisher data pipe, expected %ld bytes, wrote %ld bytes\n",
__func__, (long) length, (long) result);
return (-RIG_EIO);
}
2022-02-05 21:27:43 +00:00
return (RIG_OK);
}
2023-10-12 04:22:42 +00:00
static int multicast_publisher_read_data(const multicast_publisher_args
2022-02-05 21:27:43 +00:00
*mcast_publisher_args, size_t length, unsigned char *data)
{
int fd = mcast_publisher_args->data_read_fd;
fd_set rfds, efds;
struct timeval timeout;
ssize_t result;
int retval;
int retries = 2;
size_t offset = 0;
size_t length_left = length;
retry:
timeout.tv_sec = 0;
timeout.tv_usec = MULTICAST_DATA_PIPE_TIMEOUT_USEC;
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
efds = rfds;
retval = select(fd + 1, &rfds, NULL, &efds, &timeout);
2022-02-05 21:27:43 +00:00
if (retval == 0)
{
2022-02-05 21:27:43 +00:00
return (-RIG_ETIMEOUT);
}
if (retval < 0)
{
rig_debug(RIG_DEBUG_ERR,
2022-02-05 21:27:43 +00:00
"%s(): select() failed when reading multicast publisher data: %s\n",
__func__,
strerror(errno));
return -RIG_EIO;
}
if (FD_ISSET(fd, &efds))
{
2022-02-05 21:27:43 +00:00
rig_debug(RIG_DEBUG_ERR,
2023-11-29 18:45:11 +00:00
"%s(): fd error when reading multicast publisher data: %s\n",
__func__,
strerror(errno));
return -RIG_EIO;
}
result = read(fd, data + offset, length_left);
2022-02-05 21:27:43 +00:00
if (result < 0)
{
if (errno == EAGAIN)
{
2022-02-05 21:27:43 +00:00
return (-RIG_ETIMEOUT);
}
2022-02-05 21:27:43 +00:00
rig_debug(RIG_DEBUG_ERR, "%s: error reading multicast publisher data: %s\n",
__func__, strerror(errno));
return (-RIG_EIO);
}
offset += result;
length_left -= result;
if (length_left > 0)
{
if (retries > 0)
{
// Execution of this routine may time out between writes to pipe, retry to get more data
rig_debug(RIG_DEBUG_VERBOSE,
"%s: could not read from multicast publisher data pipe, expected %ld bytes, read %ld bytes, retrying...\n",
__func__, (long) length, (long) offset);
retries--;
goto retry;
}
2022-02-05 21:27:43 +00:00
rig_debug(RIG_DEBUG_ERR,
"%s: could not read from multicast publisher data pipe even after retries, expected %ld bytes, read %ld bytes\n",
__func__, (long) length, (long) offset);
2022-02-05 21:27:43 +00:00
return (-RIG_EIO);
}
2022-02-05 21:27:43 +00:00
return (RIG_OK);
}
#endif
2024-01-21 22:38:19 +00:00
static void multicast_publisher_write_lock(RIG *rig)
{
struct rig_state *rs = &rig->state;
multicast_publisher_priv_data *priv_data = (multicast_publisher_priv_data *)
rs->multicast_publisher_priv_data;
pthread_mutex_lock(&priv_data->args.write_lock);
}
static void multicast_publisher_write_unlock(RIG *rig)
{
struct rig_state *rs = &rig->state;
multicast_publisher_priv_data *priv_data = (multicast_publisher_priv_data *)
rs->multicast_publisher_priv_data;
pthread_mutex_unlock(&priv_data->args.write_lock);
}
2022-02-05 21:27:43 +00:00
static int multicast_publisher_write_packet_header(RIG *rig,
multicast_publisher_data_packet *packet)
{
struct rig_state *rs = &rig->state;
multicast_publisher_priv_data *mcast_publisher_priv;
multicast_publisher_args *mcast_publisher_args;
ssize_t result;
if (rs->multicast_publisher_priv_data == NULL)
{
// Silently ignore if multicast publisher is not enabled
return RIG_OK;
}
2022-02-05 21:27:43 +00:00
mcast_publisher_priv = (multicast_publisher_priv_data *)
rs->multicast_publisher_priv_data;
mcast_publisher_args = &mcast_publisher_priv->args;
result = multicast_publisher_write_data(
2022-02-05 21:27:43 +00:00
mcast_publisher_args, sizeof(multicast_publisher_data_packet),
(unsigned char *) packet);
if (result != RIG_OK)
{
return result;
}
return RIG_OK;
}
2023-10-13 22:36:29 +00:00
// cppcheck-suppress unusedFunction
int network_publish_rig_poll_data(RIG *rig)
{
2023-10-12 04:22:42 +00:00
const struct rig_state *rs = &rig->state;
int result;
2022-02-05 21:27:43 +00:00
multicast_publisher_data_packet packet =
{
.type = MULTICAST_PUBLISHER_DATA_PACKET_TYPE_POLL,
.padding = 0,
.data_length = 0,
};
if (rs->multicast_publisher_priv_data == NULL)
{
// Silently ignore call if multicast publisher is not enabled
return RIG_OK;
}
multicast_publisher_write_lock(rig);
result = multicast_publisher_write_packet_header(rig, &packet);
multicast_publisher_write_unlock(rig);
return result;
}
2023-10-13 22:36:29 +00:00
// cppcheck-suppress unusedFunction
int network_publish_rig_transceive_data(RIG *rig)
{
2023-10-12 04:22:42 +00:00
const struct rig_state *rs = &rig->state;
int result;
2022-02-05 21:27:43 +00:00
multicast_publisher_data_packet packet =
{
.type = MULTICAST_PUBLISHER_DATA_PACKET_TYPE_TRANSCEIVE,
.padding = 0,
.data_length = 0,
};
if (rs->multicast_publisher_priv_data == NULL)
{
// Silently ignore call if multicast publisher is not enabled
return RIG_OK;
}
multicast_publisher_write_lock(rig);
result = multicast_publisher_write_packet_header(rig, &packet);
multicast_publisher_write_unlock(rig);
return result;
}
int network_publish_rig_spectrum_data(RIG *rig, struct rig_spectrum_line *line)
{
int result;
struct rig_state *rs = &rig->state;
multicast_publisher_priv_data *mcast_publisher_priv;
multicast_publisher_args *mcast_publisher_args;
2022-02-05 21:27:43 +00:00
multicast_publisher_data_packet packet =
{
.type = MULTICAST_PUBLISHER_DATA_PACKET_TYPE_SPECTRUM,
.padding = 0,
.data_length = sizeof(struct rig_spectrum_line) + line->spectrum_data_length,
};
if (rs->multicast_publisher_priv_data == NULL)
{
// Silently ignore call if multicast publisher is not enabled
return RIG_OK;
}
// Acquire write lock to write all data in one go to the pipe
multicast_publisher_write_lock(rig);
result = multicast_publisher_write_packet_header(rig, &packet);
2022-02-05 21:27:43 +00:00
if (result != RIG_OK)
{
multicast_publisher_write_unlock(rig);
2022-02-13 22:16:02 +00:00
RETURNFUNC2(result);
}
2022-02-05 21:27:43 +00:00
mcast_publisher_priv = (multicast_publisher_priv_data *)
rs->multicast_publisher_priv_data;
mcast_publisher_args = &mcast_publisher_priv->args;
result = multicast_publisher_write_data(
2022-02-05 21:27:43 +00:00
mcast_publisher_args, sizeof(struct rig_spectrum_line), (unsigned char *) line);
if (result != RIG_OK)
{
multicast_publisher_write_unlock(rig);
2022-02-13 22:16:02 +00:00
RETURNFUNC2(result);
}
result = multicast_publisher_write_data(
2022-02-05 21:27:43 +00:00
mcast_publisher_args, line->spectrum_data_length, line->spectrum_data);
multicast_publisher_write_unlock(rig);
if (result != RIG_OK)
{
2022-02-13 22:16:02 +00:00
RETURNFUNC2(result);
}
2022-02-13 22:16:02 +00:00
RETURNFUNC2(RIG_OK);
}
2023-10-15 13:09:19 +00:00
static int multicast_publisher_read_packet(multicast_publisher_args
const *mcast_publisher_args,
2022-02-05 21:27:43 +00:00
uint8_t *type, struct rig_spectrum_line *spectrum_line,
unsigned char *spectrum_data)
{
int result;
multicast_publisher_data_packet packet;
2022-02-05 21:27:43 +00:00
result = multicast_publisher_read_data(mcast_publisher_args, sizeof(packet),
(unsigned char *) &packet);
if (result < 0)
{
2022-02-05 21:27:43 +00:00
return (result);
}
switch (packet.type)
{
2022-02-05 21:27:43 +00:00
case MULTICAST_PUBLISHER_DATA_PACKET_TYPE_POLL:
case MULTICAST_PUBLISHER_DATA_PACKET_TYPE_TRANSCEIVE:
break;
2022-02-05 21:27:43 +00:00
case MULTICAST_PUBLISHER_DATA_PACKET_TYPE_SPECTRUM:
result = multicast_publisher_read_data(
mcast_publisher_args, sizeof(struct rig_spectrum_line),
(unsigned char *) spectrum_line);
2022-02-05 21:27:43 +00:00
if (result < 0)
{
return (result);
}
2022-02-05 21:27:43 +00:00
if (packet.data_length - sizeof(struct rig_spectrum_line) !=
spectrum_line->spectrum_data_length)
{
rig_debug(RIG_DEBUG_ERR,
"%s: multicast publisher data error, expected %d bytes of spectrum data, got %d bytes\n",
__func__, (int)spectrum_line->spectrum_data_length,
(int)(packet.data_length - sizeof(struct rig_spectrum_line)));
return (-RIG_EPROTO);
}
spectrum_line->spectrum_data = spectrum_data;
result = multicast_publisher_read_data(mcast_publisher_args,
spectrum_line->spectrum_data_length, spectrum_data);
if (result < 0)
{
return (result);
}
break;
default:
rig_debug(RIG_DEBUG_ERR,
"%s: unexpected multicast publisher data packet type: %d\n", __func__,
packet.type);
return (-RIG_EPROTO);
}
*type = packet.type;
2022-02-05 21:27:43 +00:00
return (RIG_OK);
}
void *multicast_publisher(void *arg)
{
unsigned char spectrum_data[HAMLIB_MAX_SPECTRUM_DATA];
char snapshot_buffer[HAMLIB_MAX_SNAPSHOT_PACKET_SIZE];
#ifdef __MINGW32__
char ip4[32];
#endif
2022-02-05 21:27:43 +00:00
struct multicast_publisher_args_s *args = (struct multicast_publisher_args_s *)
arg;
RIG *rig = args->rig;
struct rig_state *rs = &rig->state;
struct rig_spectrum_line spectrum_line;
uint8_t packet_type = MULTICAST_PUBLISHER_DATA_PACKET_TYPE_SPECTRUM;
2023-12-18 14:25:15 +00:00
multicast_publisher_priv_data *mcast_publisher_priv =
(multicast_publisher_priv_data *)
rs->multicast_publisher_priv_data;
struct sockaddr_in dest_addr;
int socket_fd = args->socket_fd;
ssize_t send_result;
2022-02-05 21:27:43 +00:00
rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): Starting multicast publisher\n", __FILE__,
__LINE__);
#ifdef __MINGW32__
if (!is_networked(ip4, sizeof(ip4)))
{
rig_debug(RIG_DEBUG_WARN, "%s: no IPV4 network detected...multicast disabled\n",
__func__);
return NULL;
}
#endif
snapshot_init();
memset(&dest_addr, 0, sizeof(dest_addr));
dest_addr.sin_family = AF_INET;
dest_addr.sin_addr.s_addr = inet_addr(args->multicast_addr);
dest_addr.sin_port = htons(args->multicast_port);
rs->multicast_publisher_run = 1;
2023-12-19 16:03:12 +00:00
while (rs->multicast_publisher_run)
{
2023-10-12 04:22:42 +00:00
int result;
2022-02-05 21:27:43 +00:00
result = multicast_publisher_read_packet(args, &packet_type, &spectrum_line,
spectrum_data);
2023-12-16 17:37:58 +00:00
if (result != RIG_OK)
{
if (result == -RIG_ETIMEOUT)
{
continue;
}
// TODO: how to detect closing of pipe, indicate with error code
// TODO: error handling, flush pipe in case of error?
2023-11-07 07:41:29 +00:00
hl_usleep(100 * 1000);
continue;
}
result = snapshot_serialize(sizeof(snapshot_buffer), snapshot_buffer, rig,
2022-02-05 21:27:43 +00:00
packet_type == MULTICAST_PUBLISHER_DATA_PACKET_TYPE_SPECTRUM ? &spectrum_line :
NULL);
if (result != RIG_OK)
{
2022-02-05 21:27:43 +00:00
rig_debug(RIG_DEBUG_ERR, "%s: error serializing rig snapshot data, result=%d\n",
__func__, result);
continue;
}
rig_debug(RIG_DEBUG_CACHE, "%s: sending rig snapshot data: %s\n", __func__,
2022-02-05 21:27:43 +00:00
snapshot_buffer);
send_result = sendto(
2022-02-05 21:27:43 +00:00
socket_fd,
snapshot_buffer,
strlen(snapshot_buffer),
0,
(struct sockaddr *) &dest_addr,
sizeof(dest_addr)
);
if (send_result < 0)
{
static int flag = 0;
if (errno != 0 || flag == 0)
{
rig_debug(RIG_DEBUG_ERR,
"%s: error sending UDP packet: %s\n", __func__,
strerror(errno));
flag = 1;
}
}
}
2023-12-19 16:03:12 +00:00
rs->multicast_publisher_run = 0;
mcast_publisher_priv->thread_id = 0;
rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): Stopped multicast publisher\n", __FILE__,
__LINE__);
return NULL;
}
#ifdef __MINGW32__
static int is_networked(char *address, int address_length)
{
int count = 0;
DWORD dwSize = 0;
DWORD dwRetVal = 0;
ULONG flags = GAA_FLAG_INCLUDE_PREFIX;
PIP_ADAPTER_ADDRESSES pAddresses = NULL;
PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL;
PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL;
char ipString[INET6_ADDRSTRLEN]; // large enough for both IPv4 and IPv6
address[0] = 0;
// First call to determine actual memory size needed
GetAdaptersAddresses(AF_UNSPEC, flags, NULL, NULL, &dwSize);
pAddresses = (IP_ADAPTER_ADDRESSES *)malloc(dwSize);
// Second call to get the actual data
dwRetVal = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, pAddresses, &dwSize);
if (dwRetVal == NO_ERROR)
{
for (pCurrAddresses = pAddresses; pCurrAddresses != NULL;
pCurrAddresses = pCurrAddresses->Next)
{
// if (pCurrAddresses->IfType == IF_TYPE_IEEE80211) // Wireless adapter
{
char friendlyName[256];
wcstombs(friendlyName, pCurrAddresses->FriendlyName, sizeof(friendlyName));
rig_debug(RIG_DEBUG_VERBOSE, "%s: network IfType = %d, name=%s\n", __func__,
(int)pCurrAddresses->IfType, friendlyName);
for (pUnicast = pCurrAddresses->FirstUnicastAddress; pUnicast != NULL;
pUnicast = pUnicast->Next)
{
void *addr = NULL;
if (pUnicast->Address.lpSockaddr->sa_family == AF_INET) // IPv4 address
{
struct sockaddr_in *sa_in = (struct sockaddr_in *)pUnicast->Address.lpSockaddr;
addr = &(sa_in->sin_addr);
}
#if 0 // going to skip IPV6 for now -- should never need it on a local network
else if (pUnicast->Address.lpSockaddr->sa_family == AF_INET6) // IPv6 address
{
struct sockaddr_in6 *sa_in6 = (struct sockaddr_in6 *)
pUnicast->Address.lpSockaddr;
addr = &(sa_in6->sin6_addr);
}
#endif
// Convert IP address to string and ignore bad ones
if (addr)
{
if (inet_ntop(pUnicast->Address.lpSockaddr->sa_family, addr, ipString,
sizeof(ipString)) != NULL)
{
// Use IP address if not 169.x.x.x
if (strncmp(ipString, "169", 3) != 0)
{
count++;
if (count > 1)
{
rig_debug(RIG_DEBUG_WARN,
"%s: more than 1 address found...multicast may not work\n", __func__);
}
rig_debug(RIG_DEBUG_VERBOSE, "%s: Address: %s\n", ipString, ipString);
strncpy(address, ipString, address_length);
}
}
}
}
free(pAddresses);
return 1; // Wireless and addresses printed
}
}
}
if (pAddresses)
{
free(pAddresses);
}
return 0; // Not wireless or no addresses found
}
int is_wireless()
{
DWORD dwSize = 0;
DWORD dwRetVal = 0;
ULONG flags = GAA_FLAG_INCLUDE_PREFIX;
PIP_ADAPTER_ADDRESSES pAddresses = NULL, pCurrAddresses = NULL;
// First call to determine actual memory size needed
GetAdaptersAddresses(AF_UNSPEC, flags, NULL, pAddresses, &dwSize);
pAddresses = (IP_ADAPTER_ADDRESSES *)malloc(dwSize);
// Second call to get the actual data
dwRetVal = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, pAddresses, &dwSize);
if (dwRetVal == NO_ERROR)
{
for (pCurrAddresses = pAddresses; pCurrAddresses != NULL;
pCurrAddresses = pCurrAddresses->Next)
{
// printf("Adapter name: %s\n", pCurrAddresses->AdapterName);
// printf("Adapter description: %ls\n", pCurrAddresses->Description);
// printf("Adapter type: ");
if (pCurrAddresses->IfType == IF_TYPE_IEEE80211)
{
// printf("Wireless\n\n");
return 1;
}
else
{
// printf("Not Wireless\n\n");
}
}
}
else
{
//printf("GetAdaptersAddresses failed with error: %lu\n", dwRetVal);
}
if (pAddresses)
{
free(pAddresses);
}
return 0;
}
#else
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <unistd.h>
#include <ifaddrs.h>
int is_networked(char *ipv4, int ipv4_length)
{
struct ifaddrs *interfaces, *iface;
char addr_str[INET_ADDRSTRLEN];
// Get a list of all network interfaces
if (getifaddrs(&interfaces) == -1)
{
perror("getifaddrs");
exit(EXIT_FAILURE);
}
// Iterate through the list of interfaces
for (iface = interfaces; iface != NULL; iface = iface->ifa_next)
{
if (iface->ifa_addr
&& iface->ifa_addr->sa_family == AF_INET) // Check it is IP4
{
// Convert the linked list of interfaces to a human readable string
struct sockaddr_in *sa = (struct sockaddr_in *) iface->ifa_addr;
inet_ntop(AF_INET, &(sa->sin_addr), addr_str, INET_ADDRSTRLEN);
if (strncmp(addr_str, "127", 3) == 0 && ipv4[0] == 0)
{
strncpy(ipv4, addr_str, ipv4_length);
rig_debug(RIG_DEBUG_VERBOSE, "%s: Can use %s\n", __func__, ipv4);
}
else if (strncmp(addr_str, "127", 3) != 0)
{
strncpy(ipv4, addr_str, ipv4_length);
rig_debug(RIG_DEBUG_VERBOSE, "%s: Will use %s\n", __func__, ipv4);
}
}
}
freeifaddrs(interfaces); // Free the linked list
return strlen(ipv4) > 0 ;
}
#ifdef __linux__
#include <linux/wireless.h>
int is_wireless_linux(const char *ifname)
{
int sock = socket(AF_INET, SOCK_DGRAM, 0);
struct iwreq pwrq;
memset(&pwrq, 0, sizeof(pwrq));
2023-12-16 17:37:58 +00:00
strncpy(pwrq.ifr_name, ifname, IFNAMSIZ - 1);
if (ioctl(sock, SIOCGIWNAME, &pwrq) != -1)
{
close(sock);
return 1; // Wireless
}
close(sock);
return 0; // Not wireless
}
int is_wireless()
{
struct ifaddrs *ifaddr, *ifa;
if (getifaddrs(&ifaddr) == -1)
{
perror("getifaddrs");
return 0;
}
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
{
if (ifa->ifa_addr == NULL)
{
continue;
}
int iswireless = is_wireless_linux(ifa->ifa_name);
//printf("%s is %s\n", ifa->ifa_name, iswireless ? "wireless" : "not wireless");
if (iswireless) {freeifaddrs(ifaddr); return 1;}
}
freeifaddrs(ifaddr);
return 0;
}
#endif
#endif
void *multicast_receiver(void *arg)
{
char data[4096];
char ip4[INET6_ADDRSTRLEN];
struct multicast_receiver_args_s *args = (struct multicast_receiver_args_s *)
arg;
RIG *rig = args->rig;
struct rig_state *rs = &rig->state;
2023-12-19 16:03:12 +00:00
multicast_receiver_priv_data *mcast_receiver_priv =
(multicast_receiver_priv_data *)
rs->multicast_receiver_priv_data;
struct sockaddr_in dest_addr;
int socket_fd = args->socket_fd;
rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): Starting multicast receiver\n", __FILE__,
2023-12-16 17:37:58 +00:00
__LINE__);
if (!is_networked(ip4, sizeof(ip4)))
{
rig_debug(RIG_DEBUG_WARN,
"%s: no network detected...disabling multicast receive\n", __func__);
return NULL;
}
2023-11-01 22:32:05 +00:00
int optval = 1;
#ifdef __MINGW32__
2023-12-16 17:37:58 +00:00
if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, (PCHAR)&optval,
sizeof(optval)) < 0)
#else
2023-12-16 17:37:58 +00:00
if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &optval,
sizeof(optval)) < 0)
#endif
{
2023-11-01 22:18:27 +00:00
rig_debug(RIG_DEBUG_ERR, "%s: error enabling UDP address reuse: %s\n", __func__,
2023-12-16 17:37:58 +00:00
strerror(errno));
return NULL;
}
// Windows does not have SO_REUSEPORT. However, SO_REUSEADDR works in a similar way.
#if defined(SO_REUSEPORT)
2023-12-16 17:37:58 +00:00
if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEPORT, &optval,
sizeof(optval)) < 0)
{
rig_debug(RIG_DEBUG_ERR, "%s: error enabling UDP port reuse: %s\n", __func__,
2023-12-16 17:37:58 +00:00
strerror(errno));
return NULL;
}
2023-12-16 17:37:58 +00:00
2023-11-01 22:35:47 +00:00
#endif
memset(&dest_addr, 0, sizeof(dest_addr));
dest_addr.sin_family = AF_INET;
#ifdef __MINGW32__
2023-12-16 17:37:58 +00:00
2023-12-18 05:13:32 +00:00
// Windows wireless cannot bind to multicast group addresses for some unknown reason
// Update: it's not wireless causing the error we see but we'll leave the detection in place
if (is_wireless())
{
2023-12-16 17:37:58 +00:00
rig_debug(RIG_DEBUG_VERBOSE,
"%s: wireless detected\n", __func__);
// dest_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
}
else
{
2023-12-16 17:37:58 +00:00
rig_debug(RIG_DEBUG_VERBOSE,
"%s: no wireless detected so INADDR_ANY is being used\n", __func__);
}
2023-12-16 17:37:58 +00:00
#else
// dest_addr.sin_addr.s_addr = inet_addr(args->multicast_addr);
dest_addr.sin_addr.s_addr = inet_addr(args->multicast_addr);
#endif
dest_addr.sin_port = htons(args->multicast_port);
2023-11-01 22:32:05 +00:00
if (bind(socket_fd, (struct sockaddr *) &dest_addr, sizeof(dest_addr)) < 0)
{
2023-12-16 17:37:58 +00:00
rig_debug(RIG_DEBUG_ERR, "%s: error binding UDP socket to %s:%d: %s\n",
__func__,
args->multicast_addr, args->multicast_port, strerror(errno));
return NULL;
}
struct ip_mreq mreq;
2023-12-16 17:37:58 +00:00
memset(&mreq, 0, sizeof(mreq));
2023-12-16 17:37:58 +00:00
mreq.imr_multiaddr.s_addr = inet_addr(args->multicast_addr);
2023-12-16 17:37:58 +00:00
#ifdef __MINGW32__
// we're not worrying about IPV6 right now as that will likely never occur on home network
if (strlen(ip4) > 0)
{
rig_debug(RIG_DEBUG_VERBOSE, "%s: multicast binding to %s\n", __func__, ip4);
mreq.imr_interface.s_addr = inet_addr(ip4);
}
else
#endif
{
rig_debug(RIG_DEBUG_VERBOSE, "%s: multicast binding to INADDR_ANY\n", __func__);
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
}
#ifdef __MINGW32__
2023-12-16 17:37:58 +00:00
if (setsockopt(socket_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (PCHAR)&mreq,
sizeof(mreq)) < 0)
#else
2023-12-16 17:37:58 +00:00
if (setsockopt(socket_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,
sizeof(mreq)) < 0)
#endif
{
2023-12-16 17:37:58 +00:00
rig_debug(RIG_DEBUG_ERR, "%s: error joining multicast group %s:%d: %s\n",
__func__,
args->multicast_addr, args->multicast_port, strerror(errno));
if (errno != 0)
{
return NULL;
}
rig_debug(RIG_DEBUG_VERBOSE, "%s: errno==0 so trying to continue\n", __func__);
}
rs->multicast_receiver_run = 1;
2023-12-19 16:03:12 +00:00
while (rs->multicast_receiver_run)
{
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
fd_set rfds, efds;
struct timeval timeout;
int select_result;
ssize_t result;
timeout.tv_sec = 0;
2023-12-19 16:03:12 +00:00
timeout.tv_usec = MULTICAST_DATA_PIPE_TIMEOUT_USEC;
FD_ZERO(&rfds);
FD_SET(socket_fd, &rfds);
efds = rfds;
select_result = select(socket_fd + 1, &rfds, NULL, &efds, &timeout);
2023-11-29 18:45:11 +00:00
2023-12-19 16:03:12 +00:00
if (!rs->multicast_receiver_run)
2023-11-29 18:45:11 +00:00
{
rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): pselect signal\n", __func__, __LINE__);
break;
}
if (select_result == 0)
{
// Select timed out
//rig_debug(RIG_DEBUG_ERR, "%s: select timeout\n", __FILE__);
continue;
}
2023-11-29 18:45:11 +00:00
if (select_result <= 0)
{
rig_debug(RIG_DEBUG_ERR,
2023-12-16 17:37:58 +00:00
"%s((%d): select() failed when reading UDP multicast socket data: %s\n",
__func__,
__LINE__,
strerror(errno));
break;
}
2023-11-29 18:45:11 +00:00
if ((result = FD_ISSET(socket_fd, &efds)))
{
rig_debug(RIG_DEBUG_ERR,
2023-12-16 17:37:58 +00:00
"%s(%d): fd error when reading UDP multicast socket data: (%d)=%s\n", __func__,
__LINE__, (int)result, strerror(errno));
break;
}
2023-12-16 17:37:58 +00:00
result = recvfrom(socket_fd, data, sizeof(data), 0,
(struct sockaddr *) &client_addr, &client_len);
if (result <= 0)
{
if (result < 0)
{
if (errno == 0 || errno == EAGAIN || errno == EWOULDBLOCK)
{
continue;
}
2023-12-16 17:37:58 +00:00
rig_debug(RIG_DEBUG_ERR, "%s: error receiving from UDP socket %s:%d: %s\n",
__func__,
args->multicast_addr, args->multicast_port, strerror(errno));
}
2023-12-16 17:37:58 +00:00
break;
}
// TODO: handle commands from multicast clients
2023-12-16 17:37:58 +00:00
rig_debug(RIG_DEBUG_VERBOSE, "%s: received %ld bytes of data: %.*s\n", __func__,
(long) result, (int) result, data);
// TODO: if a new snapshot needs to be sent, call network_publish_rig_poll_data() and the publisher routine will send out a snapshot
// TODO: new logic in publisher needs to be written for other types of responses
}
2023-11-29 18:45:11 +00:00
rs->multicast_receiver_run = 0;
2023-12-19 16:03:12 +00:00
mcast_receiver_priv->thread_id = 0;
rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): Stopped multicast receiver\n", __FILE__,
2023-12-16 17:37:58 +00:00
__LINE__);
return NULL;
}
//! @endcond
/**
* \brief Start multicast publisher
*
* Start multicast publisher.
*
* \param multicast_addr UDP address
* \param multicast_port UDP socket port
* \return RIG_OK or < 0 if error
*/
int network_multicast_publisher_start(RIG *rig, const char *multicast_addr,
int multicast_port, enum multicast_item_e items)
{
struct rig_state *rs = &rig->state;
multicast_publisher_priv_data *mcast_publisher_priv;
int socket_fd;
int status;
int mutex_status;
#ifdef __MINGW32__
char ip4[32];
#endif
ENTERFUNC;
2023-12-19 16:03:12 +00:00
if (rs->multicast_publisher_priv_data != NULL)
{
rig_debug(RIG_DEBUG_WARN, "%s(%d): multicast publisher already running\n",
__FILE__, __LINE__);
RETURNFUNC(-RIG_EINVAL);
}
2023-12-16 17:37:58 +00:00
rig_debug(RIG_DEBUG_VERBOSE,
"%s(%d): multicast publisher address=%s, port=%d\n", __FILE__,
2022-07-27 22:16:17 +00:00
__LINE__,
2022-02-05 21:27:43 +00:00
multicast_addr, multicast_port);
#ifdef __MINGW32__
if (!is_networked(ip4, sizeof(ip4)))
{
rig_debug(RIG_DEBUG_WARN, "%s: No network found...multicast disabled\n",
__func__);
return RIG_OK;
}
#endif
if (multicast_addr == NULL || strcmp(multicast_addr, "0.0.0.0") == 0)
{
2022-02-05 21:27:43 +00:00
rig_debug(RIG_DEBUG_TRACE, "%s(%d): not starting multicast publisher\n",
__FILE__, __LINE__);
return RIG_OK;
}
status = network_init();
#ifdef __MINGW32__ // always RIG_OK if not Windows
2023-12-18 14:25:15 +00:00
if (status != RIG_OK)
{
RETURNFUNC(status);
}
#endif
socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
2022-02-05 21:27:43 +00:00
if (socket_fd < 0)
{
rig_debug(RIG_DEBUG_ERR, "%s: error opening new UDP socket: %s", __func__,
strerror(errno));
RETURNFUNC(-RIG_EIO);
}
// Enable non-blocking mode
u_long mode = 1;
#ifdef __MINGW32__
2023-12-16 17:37:58 +00:00
if (ioctlsocket(socket_fd, FIONBIO, &mode) == SOCKET_ERROR)
{
2023-12-16 17:37:58 +00:00
rig_debug(RIG_DEBUG_ERR, "%s: error enabling non-blocking mode for socket: %s",
__func__,
strerror(errno));
RETURNFUNC(-RIG_EIO);
}
2023-12-16 17:37:58 +00:00
#else
2023-12-16 17:37:58 +00:00
if (ioctl(socket_fd, FIONBIO, &mode) < 0)
{
2023-12-16 17:37:58 +00:00
rig_debug(RIG_DEBUG_ERR, "%s: error enabling non-blocking mode for socket: %s",
__func__,
strerror(errno));
RETURNFUNC(-RIG_EIO);
}
2023-12-16 17:37:58 +00:00
#endif
2021-08-12 04:10:44 +00:00
if (items & RIG_MULTICAST_TRANSCEIVE)
{
2022-02-05 21:27:43 +00:00
rig_debug(RIG_DEBUG_VERBOSE, "%s(%d) MULTICAST_TRANSCEIVE enabled\n", __FILE__,
__LINE__);
}
2021-08-12 04:10:44 +00:00
if (items & RIG_MULTICAST_SPECTRUM)
{
2022-02-05 21:27:43 +00:00
rig_debug(RIG_DEBUG_VERBOSE, "%s(%d) MULTICAST_SPECTRUM enabled\n", __FILE__,
__LINE__);
}
rs->snapshot_packet_sequence_number = 0;
2023-12-19 16:03:12 +00:00
rs->multicast_publisher_run = 0;
2022-02-05 21:27:43 +00:00
rs->multicast_publisher_priv_data = calloc(1,
sizeof(multicast_publisher_priv_data));
if (rs->multicast_publisher_priv_data == NULL)
{
close(socket_fd);
RETURNFUNC(-RIG_ENOMEM);
}
2022-02-05 21:27:43 +00:00
mcast_publisher_priv = (multicast_publisher_priv_data *)
rs->multicast_publisher_priv_data;
mcast_publisher_priv->args.socket_fd = socket_fd;
mcast_publisher_priv->args.multicast_addr = multicast_addr;
mcast_publisher_priv->args.multicast_port = multicast_port;
mcast_publisher_priv->args.rig = rig;
mutex_status = pthread_mutex_init(&mcast_publisher_priv->args.write_lock, NULL);
status = multicast_publisher_create_data_pipe(mcast_publisher_priv);
2022-02-05 21:27:43 +00:00
if (status < 0 || mutex_status != 0)
{
free(rs->multicast_publisher_priv_data);
rs->multicast_publisher_priv_data = NULL;
close(socket_fd);
2022-02-05 21:27:43 +00:00
rig_debug(RIG_DEBUG_ERR,
"%s: multicast publisher data pipe creation failed, result=%d\n", __func__,
status);
RETURNFUNC(-RIG_EINTERNAL);
}
2022-02-05 21:27:43 +00:00
int err = pthread_create(&mcast_publisher_priv->thread_id, NULL,
multicast_publisher,
&mcast_publisher_priv->args);
if (err)
{
rig_debug(RIG_DEBUG_ERR, "%s(%d) pthread_create error %s\n", __FILE__, __LINE__,
strerror(errno));
multicast_publisher_close_data_pipe(mcast_publisher_priv);
free(mcast_publisher_priv);
rs->multicast_publisher_priv_data = NULL;
close(socket_fd);
RETURNFUNC(-RIG_EINTERNAL);
}
RETURNFUNC(RIG_OK);
}
/**
* \brief Stop multicast publisher
*
* Stop multicast publisher
*
* \return RIG_OK or < 0 if error
*/
int network_multicast_publisher_stop(RIG *rig)
{
struct rig_state *rs = &rig->state;
multicast_publisher_priv_data *mcast_publisher_priv;
ENTERFUNC;
rs->multicast_publisher_run = 0;
2022-02-05 21:27:43 +00:00
mcast_publisher_priv = (multicast_publisher_priv_data *)
rs->multicast_publisher_priv_data;
if (mcast_publisher_priv == NULL)
{
RETURNFUNC(RIG_OK);
}
if (mcast_publisher_priv->thread_id != 0)
{
int err = pthread_join(mcast_publisher_priv->thread_id, NULL);
if (err)
{
rig_debug(RIG_DEBUG_ERR, "%s(%d): pthread_join error %s\n", __FILE__, __LINE__,
2022-02-05 21:27:43 +00:00
strerror(errno));
// just ignore it
}
mcast_publisher_priv->thread_id = 0;
}
multicast_publisher_close_data_pipe(mcast_publisher_priv);
if (mcast_publisher_priv->args.socket_fd >= 0)
{
close(mcast_publisher_priv->args.socket_fd);
mcast_publisher_priv->args.socket_fd = -1;
}
pthread_mutex_destroy(&mcast_publisher_priv->args.write_lock);
free(rs->multicast_publisher_priv_data);
rs->multicast_publisher_priv_data = NULL;
RETURNFUNC(RIG_OK);
}
/**
* \brief Start multicast receiver
*
* Start multicast receiver.
*
* \param multicast_addr UDP address
* \param multicast_port UDP socket port
* \return RIG_OK or < 0 if error
*/
2023-12-16 17:37:58 +00:00
int network_multicast_receiver_start(RIG *rig, const char *multicast_addr,
int multicast_port)
{
struct rig_state *rs = &rig->state;
multicast_receiver_priv_data *mcast_receiver_priv;
int socket_fd;
int status;
ENTERFUNC;
2023-12-19 16:03:12 +00:00
if (rs->multicast_receiver_priv_data != NULL)
{
rig_debug(RIG_DEBUG_ERR, "%s(%d): multicast receiver already running\n",
__FILE__,
__LINE__);
RETURNFUNC(-RIG_EINVAL);
}
2023-12-16 17:37:58 +00:00
rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): multicast receiver address=%s, port=%d\n",
__FILE__,
__LINE__,
multicast_addr, multicast_port);
if (multicast_addr == NULL || strcmp(multicast_addr, "0.0.0.0") == 0)
{
rig_debug(RIG_DEBUG_TRACE, "%s(%d): not starting multicast receiver\n",
__FILE__, __LINE__);
2023-12-19 16:03:12 +00:00
RETURNFUNC(RIG_OK);
}
status = network_init();
if (status != RIG_OK)
{
RETURNFUNC(status);
}
socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (socket_fd < 0)
{
rig_debug(RIG_DEBUG_ERR, "%s: error opening new UDP socket: %s", __func__,
strerror(errno));
RETURNFUNC(-RIG_EIO);
}
// Enable non-blocking mode
u_long mode = 1;
#ifdef __MINGW32__
2023-12-16 17:37:58 +00:00
if (ioctlsocket(socket_fd, FIONBIO, &mode) == SOCKET_ERROR)
{
2023-12-16 17:37:58 +00:00
rig_debug(RIG_DEBUG_ERR, "%s: error enabling non-blocking mode for socket: %s",
__func__,
strerror(errno));
RETURNFUNC(-RIG_EIO);
}
2023-12-16 17:37:58 +00:00
#else
2023-12-16 17:37:58 +00:00
if (ioctl(socket_fd, FIONBIO, &mode) < 0)
{
2023-12-16 17:37:58 +00:00
rig_debug(RIG_DEBUG_ERR, "%s: error enabling non-blocking mode for socket: %s",
__func__,
strerror(errno));
RETURNFUNC(-RIG_EIO);
}
2023-12-16 17:37:58 +00:00
#endif
2023-12-19 16:03:12 +00:00
rs->multicast_receiver_run = 0;
rs->multicast_receiver_priv_data = calloc(1,
sizeof(multicast_receiver_priv_data));
if (rs->multicast_receiver_priv_data == NULL)
{
close(socket_fd);
RETURNFUNC(-RIG_ENOMEM);
}
mcast_receiver_priv = (multicast_receiver_priv_data *)
2023-12-16 17:37:58 +00:00
rs->multicast_receiver_priv_data;
mcast_receiver_priv->args.socket_fd = socket_fd;
mcast_receiver_priv->args.multicast_addr = multicast_addr;
mcast_receiver_priv->args.multicast_port = multicast_port;
mcast_receiver_priv->args.rig = rig;
int err = pthread_create(&mcast_receiver_priv->thread_id, NULL,
multicast_receiver,
&mcast_receiver_priv->args);
if (err)
{
rig_debug(RIG_DEBUG_ERR, "%s(%d) pthread_create error %s\n", __FILE__, __LINE__,
strerror(errno));
free(mcast_receiver_priv);
rs->multicast_receiver_priv_data = NULL;
close(socket_fd);
RETURNFUNC(-RIG_EINTERNAL);
}
RETURNFUNC(RIG_OK);
}
/**
* \brief Stop multicast receiver
*
* Stop multicast receiver
*
* \return RIG_OK or < 0 if error
*/
int network_multicast_receiver_stop(RIG *rig)
{
struct rig_state *rs = &rig->state;
multicast_receiver_priv_data *mcast_receiver_priv;
ENTERFUNC;
rs->multicast_receiver_run = 0;
mcast_receiver_priv = (multicast_receiver_priv_data *)
2023-12-16 17:37:58 +00:00
rs->multicast_receiver_priv_data;
if (mcast_receiver_priv == NULL)
{
RETURNFUNC(RIG_OK);
}
// Close the socket first to stop the routine
if (mcast_receiver_priv->args.socket_fd >= 0)
{
#ifdef __MINGW32__
shutdown(mcast_receiver_priv->args.socket_fd, SD_BOTH);
#else
shutdown(mcast_receiver_priv->args.socket_fd, SHUT_RDWR);
#endif
close(mcast_receiver_priv->args.socket_fd);
}
if (mcast_receiver_priv->thread_id != 0)
{
int err = pthread_join(mcast_receiver_priv->thread_id, NULL);
if (err)
{
rig_debug(RIG_DEBUG_ERR, "%s(%d): pthread_join error %s\n", __FILE__, __LINE__,
strerror(errno));
// just ignore it
}
mcast_receiver_priv->thread_id = 0;
}
if (mcast_receiver_priv->args.socket_fd >= 0)
{
mcast_receiver_priv->args.socket_fd = -1;
}
free(rs->multicast_receiver_priv_data);
rs->multicast_receiver_priv_data = NULL;
RETURNFUNC(RIG_OK);
}
#endif
/** @} */