mirror of https://github.com/nostar/urfd.git
commit
cbb5a3362b
@ -0,0 +1,6 @@
|
||||
RewriteEngine On
|
||||
RewriteBase /json/
|
||||
RewriteRule ^index\\.php$ - [L]
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule . /json/index.php [L]
|
||||
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
header('Content-Type: application/json');
|
||||
|
||||
if (file_exists($_SERVER['DOCUMENT_ROOT'] . "/pgs/functions.php")) require_once($_SERVER['DOCUMENT_ROOT'] . "/pgs/functions.php");
|
||||
if (file_exists($_SERVER['DOCUMENT_ROOT'] . "/pgs/config.inc.php")) require_once($_SERVER['DOCUMENT_ROOT'] . "/pgs/config.inc.php");
|
||||
|
||||
if (!class_exists('ParseXML')) require_once($_SERVER['DOCUMENT_ROOT'] . "/pgs/class.parsexml.php");
|
||||
if (!class_exists('Node')) require_once($_SERVER['DOCUMENT_ROOT'] . "/pgs/class.node.php");
|
||||
if (!class_exists('xReflector')) require_once($_SERVER['DOCUMENT_ROOT'] . "/pgs/class.reflector.php");
|
||||
if (!class_exists('Station')) require_once($_SERVER['DOCUMENT_ROOT'] . "/pgs/class.station.php");
|
||||
if (!class_exists('Peer')) require_once($_SERVER['DOCUMENT_ROOT'] . "/pgs/class.peer.php");
|
||||
|
||||
$Reflector = new xReflector();
|
||||
$Reflector->SetXMLFile($Service['XMLFile']);
|
||||
$Reflector->SetPIDFile($Service['PIDFile']);
|
||||
$Reflector->LoadXML();
|
||||
$Reflector->SetFlagFile($_SERVER['DOCUMENT_ROOT'] . "/pgs/country.csv");
|
||||
$Reflector->LoadFlags();
|
||||
|
||||
$Request = $_SERVER['REQUEST_URI'];
|
||||
$ViewDir = '/views/';
|
||||
|
||||
switch ($Request) {
|
||||
case '/json/links':
|
||||
require __DIR__ . $ViewDir . 'links.php';
|
||||
break;
|
||||
|
||||
case '/json/metadata':
|
||||
require __DIR__ . $ViewDir . 'metadata.php';
|
||||
break;
|
||||
|
||||
case '/json/modulesinuse':
|
||||
require __DIR__ . $ViewDir . 'modulesinuse.php';
|
||||
break;
|
||||
|
||||
case '/json/peers':
|
||||
require __DIR__ . $ViewDir . 'peers.php';
|
||||
break;
|
||||
|
||||
case '/json/reflector':
|
||||
require __DIR__ . $ViewDir . 'reflector.php';
|
||||
break;
|
||||
|
||||
case '/json/stations':
|
||||
require __DIR__ . $ViewDir . 'stations.php';
|
||||
break;
|
||||
|
||||
case '/json/status':
|
||||
require __DIR__ . $ViewDir . 'status.php';
|
||||
break;
|
||||
|
||||
default:
|
||||
header('Content-Type: text/plain');
|
||||
http_response_code(404);
|
||||
echo('404 page not found');
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
/*** Add links to payload ***/
|
||||
for ($i=0;$i<$Reflector->NodeCount();$i++) {
|
||||
|
||||
// craft payload array
|
||||
$payload[$i] = array(
|
||||
'callsign' => $Reflector->Nodes[$i]->GetCallSign() . ' ' . $Reflector->Nodes[$i]->GetSuffix(),
|
||||
'ip' => $Reflector->Nodes[$i]->GetIP(),
|
||||
'linkedmodule' => $Reflector->Nodes[$i]->GetLinkedModule(),
|
||||
'protocol' => $Reflector->Nodes[$i]->GetProtocol(),
|
||||
'connecttime' => gmdate('Y-m-d\TH:i:sp', $Reflector->Nodes[$i]->GetConnectTime()),
|
||||
'lastheardtime' => gmdate('Y-m-d\TH:i:sp', $Reflector->Nodes[$i]->GetLastHeardTime())
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// json encode payload array
|
||||
$records = json_encode($payload);
|
||||
|
||||
echo $records;
|
||||
|
||||
?>
|
||||
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
$Url = @parse_url($CallingHome['MyDashBoardURL']);
|
||||
$Net4 = @dns_get_record($Url['host'], DNS_A);
|
||||
$Net6 = @dns_get_record($Url['host'], DNS_AAAA);
|
||||
|
||||
/*** add metadata to payload ***/
|
||||
$payload = array(
|
||||
'dashboard_version' => $PageOptions['DashboardVersion'],
|
||||
'ipV4' => @$Net4[0]['ip'],
|
||||
'ipV6' => @$Net6[0]['ipv6'],
|
||||
'reflector_callsign' => str_replace("XLX", "URF", $Reflector->GetReflectorName()),
|
||||
'reflector_version' => $Reflector->GetVersion(),
|
||||
'sysop_email' => $PageOptions['ContactEmail']
|
||||
);
|
||||
|
||||
|
||||
// json encode payload array
|
||||
$records = json_encode($payload);
|
||||
|
||||
echo $records;
|
||||
|
||||
?>
|
||||
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
$Modules = $Reflector->GetModules();
|
||||
sort($Modules, SORT_STRING);
|
||||
|
||||
|
||||
/*** Add modules to payload ***/
|
||||
for ($i=0;$i<count($Modules);$i++) {
|
||||
|
||||
$payload[$i] = array(
|
||||
'name' => $Modules[$i]
|
||||
);
|
||||
|
||||
$Users = $Reflector->GetNodesInModulesByID($Modules[$i]);
|
||||
|
||||
for ($j=0;$j<count($Users);$j++) {
|
||||
|
||||
$payload[$i]['callsigns'][$j] = $Reflector->GetCallsignAndSuffixByID($Users[$j]);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// json encode payload array
|
||||
$records = json_encode($payload);
|
||||
|
||||
echo $records;
|
||||
|
||||
?>
|
||||
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
/*** Add links to payload ***/
|
||||
for ($i=0;$i<$Reflector->PeerCount();$i++) {
|
||||
|
||||
$payload[$i] = array(
|
||||
'callsign' => $Reflector->Peers[$i]->GetCallSign(),
|
||||
'ip' => $Reflector->Peers[$i]->GetIP(),
|
||||
'linkedmodule' => $Reflector->Peers[$i]->GetLinkedModule(),
|
||||
'connecttime' => gmdate('Y-m-d\TH:i:sp', $Reflector->Peers[$i]->GetConnectTime()),
|
||||
'lastheardtime' => gmdate('Y-m-d\TH:i:sp', $Reflector->Peers[$i]->GetLastHeardTime())
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// json encode payload array
|
||||
$records = json_encode($payload);
|
||||
|
||||
echo $records;
|
||||
|
||||
?>
|
||||
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
$Status = (file_exists($Service['PIDFile'])) ? 'up' : 'down';
|
||||
|
||||
$payload = array(
|
||||
'lastupdatechecktime' => date('Y-m-d\TH:i:sp', time()),
|
||||
'status' => $Status,
|
||||
'uptime' => $Reflector->GetServiceUptime()
|
||||
);
|
||||
|
||||
|
||||
/*** add data to payload ***/
|
||||
$payload['data'] = array(
|
||||
'filetime' => date('Y-m-d\TH:i:sp', filemtime($Service['XMLFile'])),
|
||||
'callsign' => str_replace("XLX", "URF", $Reflector->GetReflectorName()),
|
||||
'version' => $Reflector->GetVersion()
|
||||
);
|
||||
|
||||
|
||||
/*** Add peers to payload ***/
|
||||
for ($i=0;$i<$Reflector->PeerCount();$i++) {
|
||||
|
||||
$payload['data']['peers'][] = array(
|
||||
'callsign' => $Reflector->Peers[$i]->GetCallSign(),
|
||||
'ip' => $Reflector->Peers[$i]->GetIP(),
|
||||
'linkedmodule' => $Reflector->Peers[$i]->GetLinkedModule(),
|
||||
'connecttime' => date('Y-m-d\TH:i:sp', $Reflector->Peers[$i]->GetConnectTime()),
|
||||
'lastheardtime' => date('Y-m-d\TH:i:sp', $Reflector->Peers[$i]->GetLastHeardTime())
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*** Add nodes to payload ***/
|
||||
for ($i=0;$i<$Reflector->NodeCount();$i++) {
|
||||
|
||||
// craft payload array
|
||||
$payload['data']['nodes'][] = array(
|
||||
'callsign' => $Reflector->Nodes[$i]->GetCallSign() . ' ' . $Reflector->Nodes[$i]->GetSuffix(),
|
||||
'ip' => $Reflector->Nodes[$i]->GetIP(),
|
||||
'linkedmodule' => $Reflector->Nodes[$i]->GetLinkedModule(),
|
||||
'protocol' => $Reflector->Nodes[$i]->GetProtocol(),
|
||||
'connecttime' => gmdate('Y-m-d\TH:i:sp', $Reflector->Nodes[$i]->GetConnectTime()),
|
||||
'lastheardtime' => gmdate('Y-m-d\TH:i:sp', $Reflector->Nodes[$i]->GetLastHeardTime())
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*** Add stations to payload ***/
|
||||
for ($i=0;$i<$Reflector->StationCount();$i++) {
|
||||
|
||||
// craft payload array
|
||||
$payload['data']['stations'][] = array(
|
||||
'callsign' => $Reflector->Stations[$i]->GetCallSign(),
|
||||
'vianode' => $Reflector->Stations[$i]->GetVia(),
|
||||
'onmodule' => $Reflector->Stations[$i]->GetModule(),
|
||||
'viapeer' => $Reflector->Stations[$i]->GetPeer(),
|
||||
'lastheardtime' => gmdate('Y-m-d\TH:i:sp', $Reflector->Stations[$i]->GetLastHeardTime())
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// json encode payload array
|
||||
$records = json_encode($payload);
|
||||
|
||||
echo $records;
|
||||
|
||||
?>
|
||||
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
/*** Add stations to payload ***/
|
||||
for ($i=0;$i<$Reflector->StationCount();$i++) {
|
||||
|
||||
$tmp = preg_split('/\s+/', $Reflector->Stations[$i]->GetCallSign(), -1, PREG_SPLIT_NO_EMPTY);
|
||||
$Callsign = $tmp[0];
|
||||
|
||||
$tmp = preg_split('/\s+/', $Reflector->Stations[$i]->GetVia(), -1, PREG_SPLIT_NO_EMPTY);
|
||||
$CallsignSuffix = $tmp[1];
|
||||
|
||||
// craft payload array
|
||||
$payload['stations'][$i] = array(
|
||||
'callsign' => $Callsign,
|
||||
'callsignsuffix' => $CallsignSuffix,
|
||||
'vianode' => $Reflector->Stations[$i]->GetVia(),
|
||||
'onmodule' => $Reflector->Stations[$i]->GetModule(),
|
||||
'lastheard' => gmdate('Y-m-d\TH:i:sp', $Reflector->Stations[$i]->GetLastHeardTime())
|
||||
);
|
||||
|
||||
list ($CountryCode, $Country) = $Reflector->GetFlag($Reflector->Stations[$i]->GetCallSign());
|
||||
|
||||
$payload['stations'][$i]['country'] = array (
|
||||
'country' => $Country,
|
||||
'countrycode' => $CountryCode
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// json encode payload array
|
||||
$records = json_encode($payload);
|
||||
|
||||
echo $records;
|
||||
|
||||
?>
|
||||
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
$ReflectorStatus = (file_exists($Service['PIDFile'])) ? 'up' : 'down';
|
||||
|
||||
$payload = array(
|
||||
'lastupdate' => gmdate('U', time()),
|
||||
'lasturfdupdate' => gmdate('U', filemtime($Service['XMLFile'])),
|
||||
'reflectorstatus' => $ReflectorStatus,
|
||||
'reflectoruptimeseconds' => $Reflector->GetServiceUptime()
|
||||
);
|
||||
|
||||
|
||||
// json encode payload array
|
||||
$records = json_encode($payload);
|
||||
|
||||
echo $records;
|
||||
|
||||
?>
|
||||
@ -0,0 +1,498 @@
|
||||
// urfd -- The universal reflector
|
||||
// Copyright © 2024 Thomas A. Early N7TAE
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program 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 General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <iostream>
|
||||
#include <unistd.h>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <csignal>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/select.h>
|
||||
|
||||
#include "TCSocket.h"
|
||||
|
||||
void CTCSocket::Close()
|
||||
{
|
||||
for (auto &item : m_Pfd)
|
||||
{
|
||||
if (item.fd >= 0)
|
||||
{
|
||||
Close(item.fd);
|
||||
}
|
||||
}
|
||||
m_Pfd.clear();
|
||||
}
|
||||
|
||||
void CTCSocket::Close(char mod)
|
||||
{
|
||||
auto pos = m_Modules.find(mod);
|
||||
if (std::string::npos == pos)
|
||||
{
|
||||
std::cerr << "Could not find module '" << mod << "'" << std::endl;
|
||||
return;
|
||||
}
|
||||
if (m_Pfd[pos].fd < 0)
|
||||
{
|
||||
std::cerr << "Close(" << mod << ") is already closed" << std::endl;
|
||||
return;
|
||||
}
|
||||
Close(m_Pfd[pos].fd);
|
||||
m_Pfd[pos].fd = -1;
|
||||
}
|
||||
|
||||
void CTCSocket::Close(int fd)
|
||||
{
|
||||
if (fd < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
for (auto &p : m_Pfd)
|
||||
{
|
||||
if (fd == p.fd)
|
||||
{
|
||||
if (shutdown(p.fd, SHUT_RDWR))
|
||||
{
|
||||
perror("shutdown");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (close(p.fd))
|
||||
{
|
||||
std::cerr << "Error while closing " << fd << ": ";
|
||||
perror("close");
|
||||
}
|
||||
else
|
||||
p.fd = -1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
std::cerr << "Could not find a file descriptor with a value of " << fd << std::endl;
|
||||
}
|
||||
|
||||
int CTCSocket::GetFD(char module) const
|
||||
{
|
||||
auto pos = m_Modules.find(module);
|
||||
if (std::string::npos == pos)
|
||||
return -1;
|
||||
return m_Pfd[pos].fd;
|
||||
}
|
||||
|
||||
char CTCSocket::GetMod(int fd) const
|
||||
{
|
||||
for (unsigned i=0; i<m_Pfd.size(); i++)
|
||||
{
|
||||
if (fd == m_Pfd[i].fd)
|
||||
{
|
||||
return m_Modules[i];
|
||||
}
|
||||
}
|
||||
return '?';
|
||||
}
|
||||
|
||||
bool CTCServer::AnyAreClosed() const
|
||||
{
|
||||
for (auto &fds : m_Pfd)
|
||||
{
|
||||
if (0 > fds.fd)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CTCSocket::Send(const STCPacket *packet)
|
||||
{
|
||||
const auto pos = m_Modules.find(packet->module);
|
||||
if (pos == std::string::npos)
|
||||
{
|
||||
std::cerr << "Can't Send() this packet to unconfigured module '" << packet->module << "'" << std::endl;
|
||||
return true;
|
||||
}
|
||||
unsigned count = 0;
|
||||
auto data = (const unsigned char *)packet;
|
||||
do {
|
||||
auto n = send(m_Pfd[pos].fd, data+count, sizeof(STCPacket)-count, 0);
|
||||
if (n <= 0)
|
||||
{
|
||||
if (0 == n)
|
||||
{
|
||||
std::cerr << "CTCSocket::Send: socket on module '" << packet->module << "' has been closed!" << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
perror("CTCSocket::Send");
|
||||
}
|
||||
Close(packet->module);
|
||||
return true;
|
||||
}
|
||||
count += n;
|
||||
} while (count < sizeof(STCPacket));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CTCSocket::receive(int fd, STCPacket *packet)
|
||||
{
|
||||
auto n = recv(fd, packet, sizeof(STCPacket), MSG_WAITALL);
|
||||
if (n < 0)
|
||||
{
|
||||
perror("Receive recv");
|
||||
Close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (0 == n)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (n != sizeof(STCPacket))
|
||||
std::cout << "receive() only read " << n << " bytes of the transcoder packet from module '" << GetMod(fd) << "'" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// returns true if there is data to return
|
||||
bool CTCServer::Receive(char module, STCPacket *packet, int ms)
|
||||
{
|
||||
bool rv = false;
|
||||
const auto pos = m_Modules.find(module);
|
||||
if (pos == std::string::npos)
|
||||
{
|
||||
std::cerr << "Can't receive on unconfigured module '" << module << "'" << std::endl;
|
||||
return rv;
|
||||
}
|
||||
|
||||
auto pfds = &m_Pfd[pos];
|
||||
if (pfds->fd < 0)
|
||||
{
|
||||
return rv;
|
||||
}
|
||||
|
||||
auto n = poll(pfds, 1, ms);
|
||||
if (n < 0)
|
||||
{
|
||||
perror("Recieve poll");
|
||||
Close(pfds->fd);
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (0 == n)
|
||||
return rv; // timeout
|
||||
|
||||
if (pfds->revents & POLLIN)
|
||||
{
|
||||
rv = receive(pfds->fd, packet);
|
||||
}
|
||||
|
||||
// It's possible that even if we read the data, the socket can have an error after the read...
|
||||
// So we'll check...
|
||||
if (pfds->revents & POLLERR || pfds->revents & POLLHUP)
|
||||
{
|
||||
if (pfds->revents & POLLERR)
|
||||
std::cerr << "POLLERR received on module '" << module << "', closing socket" << std::endl;
|
||||
if (pfds->revents & POLLHUP)
|
||||
std::cerr << "POLLHUP received on module '" << module << "', closing socket" << std::endl;
|
||||
Close(pfds->fd);
|
||||
}
|
||||
if (pfds->revents & POLLNVAL)
|
||||
{
|
||||
std::cerr << "POLLNVAL received on module " << module << "'" << std::endl;
|
||||
}
|
||||
|
||||
if (rv)
|
||||
Close(pfds->fd);
|
||||
return ! rv;
|
||||
}
|
||||
|
||||
bool CTCServer::Open(const std::string &address, const std::string &modules, uint16_t port)
|
||||
{
|
||||
m_Modules.assign(modules);
|
||||
|
||||
m_Ip = CIp(address.c_str(), AF_UNSPEC, SOCK_STREAM, port);
|
||||
|
||||
m_Pfd.resize(m_Modules.size());
|
||||
for (auto &pf : m_Pfd)
|
||||
{
|
||||
pf.fd = -1;
|
||||
pf.events = POLLIN;
|
||||
pf.revents = 0;
|
||||
}
|
||||
|
||||
return Accept();
|
||||
}
|
||||
|
||||
bool CTCServer::Accept()
|
||||
{
|
||||
auto fd = socket(m_Ip.GetFamily(), SOCK_STREAM, 0);
|
||||
if (fd < 0)
|
||||
{
|
||||
perror("Open socket");
|
||||
return true;
|
||||
}
|
||||
|
||||
int yes = 1;
|
||||
auto rv = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
|
||||
if (rv < 0)
|
||||
{
|
||||
close(fd);
|
||||
perror("Open setsockopt");
|
||||
return true;
|
||||
}
|
||||
|
||||
rv = bind(fd, m_Ip.GetCPointer(), m_Ip.GetSize());
|
||||
if (rv < 0)
|
||||
{
|
||||
close(fd);
|
||||
perror("Open bind");
|
||||
return true;
|
||||
}
|
||||
|
||||
rv = listen(fd, 3);
|
||||
if (rv < 0)
|
||||
{
|
||||
perror("Open listen");
|
||||
close(fd);
|
||||
Close();
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string wmod;
|
||||
for (const char c : m_Modules)
|
||||
{
|
||||
if (GetFD(c) < 0)
|
||||
wmod.append(1, c);
|
||||
}
|
||||
|
||||
std::cout << "Waiting at " << m_Ip << " for transcoder connection";
|
||||
if (wmod.size() > 1)
|
||||
{
|
||||
std::cout << "s for modules ";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << " for module ";
|
||||
}
|
||||
std::cout << wmod << "..." << std::endl;
|
||||
|
||||
while (AnyAreClosed())
|
||||
{
|
||||
if (acceptone(fd))
|
||||
{
|
||||
close(fd);
|
||||
Close();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CTCServer::acceptone(int fd)
|
||||
{
|
||||
CIp their_addr; // connector's address information
|
||||
|
||||
socklen_t sin_size = sizeof(struct sockaddr_storage);
|
||||
|
||||
auto newfd = accept(fd, their_addr.GetPointer(), &sin_size);
|
||||
if (newfd < 0)
|
||||
{
|
||||
perror("Accept accept");
|
||||
return true;
|
||||
}
|
||||
|
||||
char mod;
|
||||
int rv = recv(newfd, &mod, 1, MSG_WAITALL); // block to get the identification byte
|
||||
if (rv != 1)
|
||||
{
|
||||
if (rv < 0)
|
||||
perror("Accept recv");
|
||||
else
|
||||
std::cerr << "recv got no identification byte!" << std::endl;
|
||||
close(newfd);
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto pos = m_Modules.find(mod);
|
||||
if (std::string::npos == pos)
|
||||
{
|
||||
std::cerr << "New connection for module '" << mod << "', but it's not configured!" << std::endl;
|
||||
std::cerr << "The transcoded modules need to be configured identically for both urfd and tcd." << std::endl;
|
||||
close(newfd);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::cout << "File descriptor " << newfd << " opened TCP port for module '" << mod << "' on " << their_addr << std::endl;
|
||||
|
||||
m_Pfd[pos].fd = newfd;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CTCClient::Open(const std::string &address, const std::string &modules, uint16_t port)
|
||||
{
|
||||
m_Address.assign(address);
|
||||
m_Modules.assign(modules);
|
||||
m_Port = port;
|
||||
|
||||
m_Pfd.resize(m_Modules.size());
|
||||
for (auto &pf : m_Pfd)
|
||||
{
|
||||
pf.fd = -1;
|
||||
pf.events = POLLIN;
|
||||
}
|
||||
|
||||
std::cout << "Connecting to the TCP server..." << std::endl;
|
||||
|
||||
for (char c : modules)
|
||||
{
|
||||
if (Connect(c))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CTCClient::Connect(char module)
|
||||
{
|
||||
const auto pos = m_Modules.find(module);
|
||||
if (pos == std::string::npos)
|
||||
{
|
||||
std::cerr << "CTCClient::Connect: could not find module '" << module << "' in configured modules!" << std::endl;
|
||||
return true;
|
||||
}
|
||||
CIp ip(m_Address.c_str(), AF_UNSPEC, SOCK_STREAM, m_Port);
|
||||
|
||||
auto fd = socket(ip.GetFamily(), SOCK_STREAM, 0);
|
||||
if (fd < 0)
|
||||
{
|
||||
std::cerr << "Could not open socket for module '" << module << "'" << std::endl;
|
||||
perror("TC client socket");
|
||||
return true;
|
||||
}
|
||||
|
||||
int yes = 1;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)))
|
||||
{
|
||||
std::cerr << "Moudule " << module << " error:";
|
||||
perror("setsockopt");
|
||||
close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned count = 0;
|
||||
while (connect(fd, ip.GetCPointer(), ip.GetSize()))
|
||||
{
|
||||
if (ECONNREFUSED == errno)
|
||||
{
|
||||
if (0 == ++count % 100) std::cout << "Connection refused! Restart the reflector." << std::endl;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "Module " << module << " error: ";
|
||||
perror("connect");
|
||||
close(fd);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
int sent = send(fd, &module, 1, 0); // send the identification byte
|
||||
if (sent < 0)
|
||||
{
|
||||
std::cerr << "Error sending ID byte to module '" << module << "':" << std::endl;
|
||||
perror("send");
|
||||
close(fd);
|
||||
return true;
|
||||
}
|
||||
else if (0 == sent)
|
||||
{
|
||||
std::cerr << "Could not set ID byte to module '" << module << "'" << std::endl;
|
||||
close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::cout << "File descriptor " << fd << " on " << ip << " opened for module '" << module << "'" << std::endl;
|
||||
|
||||
m_Pfd[pos].fd = fd;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CTCClient::ReConnect()
|
||||
{
|
||||
for (char m : m_Modules)
|
||||
{
|
||||
if (0 > GetFD(m))
|
||||
{
|
||||
std::cout << "Reconnecting module " << m << "..." << std::endl;
|
||||
if (Connect(m))
|
||||
{
|
||||
raise(SIGINT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CTCClient::Receive(std::queue<std::unique_ptr<STCPacket>> &queue, int ms)
|
||||
{
|
||||
for (auto &pfd : m_Pfd)
|
||||
pfd.revents = 0;
|
||||
|
||||
auto rv = poll(m_Pfd.data(), m_Pfd.size(), ms);
|
||||
|
||||
if (rv < 0)
|
||||
{
|
||||
perror("Receive poll");
|
||||
return;
|
||||
}
|
||||
|
||||
if (0 == rv)
|
||||
return;
|
||||
|
||||
for (auto &pfd : m_Pfd)
|
||||
{
|
||||
if (pfd.fd < 0)
|
||||
continue;
|
||||
|
||||
if (pfd.revents & POLLIN)
|
||||
{
|
||||
auto p_tcpack = std::make_unique<STCPacket>();
|
||||
if (receive(pfd.fd, p_tcpack.get()))
|
||||
{
|
||||
p_tcpack.reset();
|
||||
Close(pfd.fd);
|
||||
}
|
||||
else
|
||||
{
|
||||
queue.push(std::move(p_tcpack));
|
||||
}
|
||||
}
|
||||
|
||||
if (pfd.revents & POLLERR || pfd.revents & POLLHUP)
|
||||
{
|
||||
std::cerr << "IO ERROR on Receive module " << GetMod(pfd.fd) << std::endl;
|
||||
Close(pfd.fd);
|
||||
}
|
||||
if (pfd.revents & POLLNVAL)
|
||||
{
|
||||
std::cerr << "POLLNVAL received on fd " << pfd.fd << ", resetting to -1" << std::endl;
|
||||
pfd.fd = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,82 @@
|
||||
// urfd -- The universal reflector
|
||||
// Copyright © 2024 Thomas A. Early N7TAE
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program 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 General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
#include <memory>
|
||||
#include <poll.h>
|
||||
|
||||
#include "IP.h"
|
||||
#include "TCPacketDef.h"
|
||||
|
||||
class CTCSocket
|
||||
{
|
||||
public:
|
||||
CTCSocket() {}
|
||||
virtual ~CTCSocket() { Close(); }
|
||||
|
||||
virtual bool Open(const std::string &address, const std::string &modules, uint16_t port) = 0;
|
||||
void Close(); // close all open sockets
|
||||
void Close(char module); // close a specific module
|
||||
void Close(int fd); // close a specific file descriptor
|
||||
bool receive(int fd, STCPacket *packet);
|
||||
|
||||
// All bool functions, except Server Receive, return true if there was an error
|
||||
bool Send(const STCPacket *packet);
|
||||
|
||||
int GetFD(char module) const; // can return -1!
|
||||
char GetMod(int fd) const;
|
||||
|
||||
protected:
|
||||
std::vector<struct pollfd> m_Pfd;
|
||||
std::string m_Modules;
|
||||
};
|
||||
|
||||
class CTCServer : public CTCSocket
|
||||
{
|
||||
public:
|
||||
CTCServer() : CTCSocket() {}
|
||||
~CTCServer() {}
|
||||
bool Open(const std::string &address, const std::string &modules, uint16_t port);
|
||||
// Returns true if there is data
|
||||
bool Receive(char module, STCPacket *packet, int ms);
|
||||
bool AnyAreClosed() const;
|
||||
bool Accept();
|
||||
|
||||
private:
|
||||
CIp m_Ip;
|
||||
bool acceptone(int fd);
|
||||
};
|
||||
|
||||
class CTCClient : public CTCSocket
|
||||
{
|
||||
public:
|
||||
CTCClient() : CTCSocket(), m_Port(0) {}
|
||||
~CTCClient() {}
|
||||
bool Open(const std::string &address, const std::string &modules, uint16_t port);
|
||||
void Receive(std::queue<std::unique_ptr<STCPacket>> &queue, int ms);
|
||||
void ReConnect();
|
||||
|
||||
private:
|
||||
std::string m_Address;
|
||||
uint16_t m_Port;
|
||||
bool Connect(char module);
|
||||
};
|
||||
@ -1,163 +0,0 @@
|
||||
// Copyright © 2021 Thomas A. Early N7TAE
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program 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 General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <iostream>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <cstring>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "UnixDgramSocket.h"
|
||||
|
||||
CUnixDgramReader::CUnixDgramReader() : fd(-1) {}
|
||||
|
||||
CUnixDgramReader::~CUnixDgramReader()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
bool CUnixDgramReader::Open(const char *path) // returns true on failure
|
||||
{
|
||||
fd = socket(AF_UNIX, SOCK_DGRAM, 0);
|
||||
if (fd < 0)
|
||||
{
|
||||
std::cerr << "socket() failed for " << path << ": " << strerror(errno) << std::endl;
|
||||
return true;
|
||||
}
|
||||
//fcntl(fd, F_SETFL, O_NONBLOCK);
|
||||
|
||||
struct sockaddr_un addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
strncpy(addr.sun_path+1, path, sizeof(addr.sun_path)-2);
|
||||
|
||||
int rval = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
|
||||
if (rval < 0)
|
||||
{
|
||||
std::cerr << "bind() failed for " << path << ": " << strerror(errno) << std::endl;
|
||||
close(fd);
|
||||
fd = -1;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CUnixDgramReader::Receive(STCPacket *pack, unsigned timeout) const
|
||||
{
|
||||
// socket valid ?
|
||||
if ( 0 > fd )
|
||||
return false;
|
||||
|
||||
// control socket
|
||||
fd_set FdSet;
|
||||
FD_ZERO(&FdSet);
|
||||
FD_SET(fd, &FdSet);
|
||||
struct timeval tv;
|
||||
tv.tv_sec = timeout / 1000;
|
||||
tv.tv_usec = (timeout % 1000) * 1000;
|
||||
|
||||
auto rval = select(fd + 1, &FdSet, 0, 0, &tv);
|
||||
if (rval <= 0) {
|
||||
if (rval < 0) {
|
||||
std::cerr << "select() error on transcoder socket: " << strerror(errno) << std::endl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return Read(pack);
|
||||
}
|
||||
|
||||
bool CUnixDgramReader::Read(STCPacket *pack) const
|
||||
{
|
||||
auto len = read(fd, pack, sizeof(STCPacket));
|
||||
if (len != sizeof(STCPacket)) {
|
||||
std::cerr << "Received transcoder packet is wrong size: " << len << " but should be " << sizeof(STCPacket) << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CUnixDgramReader::Close()
|
||||
{
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
fd = -1;
|
||||
}
|
||||
|
||||
int CUnixDgramReader::GetFD() const
|
||||
{
|
||||
return fd;
|
||||
}
|
||||
|
||||
CUnixDgramWriter::CUnixDgramWriter() {}
|
||||
|
||||
CUnixDgramWriter::~CUnixDgramWriter() {}
|
||||
|
||||
void CUnixDgramWriter::SetUp(const char *path) // returns true on failure
|
||||
{
|
||||
// setup the socket address
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
strncpy(addr.sun_path+1, path, sizeof(addr.sun_path)-2);
|
||||
}
|
||||
|
||||
bool CUnixDgramWriter::Send(const STCPacket *pack) const
|
||||
{
|
||||
auto len = Write(pack, sizeof(STCPacket));
|
||||
|
||||
if (len != sizeof(STCPacket))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ssize_t CUnixDgramWriter::Write(const void *buf, ssize_t size) const
|
||||
{
|
||||
// open the socket
|
||||
int fd = socket(AF_UNIX, SOCK_DGRAM, 0);
|
||||
if (fd < 0)
|
||||
{
|
||||
std::cerr << "socket() failed for " << addr.sun_path+1 << ": " << strerror(errno) << std::endl;
|
||||
return -1;
|
||||
}
|
||||
// connect to the receiver
|
||||
int rval = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
|
||||
if (rval < 0)
|
||||
{
|
||||
std::cerr << "connect() failed for " << addr.sun_path+1 << ": " << strerror(errno) << std::endl;
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto written = write(fd, buf, size);
|
||||
if (written != size) {
|
||||
std::cerr << "write on " << addr.sun_path+1;
|
||||
if (written < 0)
|
||||
std::cerr << " returned error: " << strerror(errno) << std::endl;
|
||||
else if (written == 0)
|
||||
std::cerr << " returned zero" << std::endl;
|
||||
else
|
||||
std::cerr << " only wrote " << written << " bytes, should be " << size << std::endl;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return written;
|
||||
}
|
||||
@ -1,50 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// Copyright © 2021 Thomas A. Early N7TAE
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program 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 General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include "TCPacketDef.h"
|
||||
|
||||
class CUnixDgramReader
|
||||
{
|
||||
public:
|
||||
CUnixDgramReader();
|
||||
~CUnixDgramReader();
|
||||
bool Open(const char *path);
|
||||
bool Read(STCPacket *pack) const;
|
||||
bool Receive(STCPacket *pack, unsigned timeout) const;
|
||||
void Close();
|
||||
int GetFD() const;
|
||||
private:
|
||||
int fd;
|
||||
};
|
||||
|
||||
class CUnixDgramWriter
|
||||
{
|
||||
public:
|
||||
CUnixDgramWriter();
|
||||
~CUnixDgramWriter();
|
||||
void SetUp(const char *path);
|
||||
bool Send(const STCPacket *pack) const;
|
||||
private:
|
||||
ssize_t Write(const void *buf, ssize_t size) const;
|
||||
|
||||
struct sockaddr_un addr;
|
||||
};
|
||||
@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (c) 2022-2024 by Thomas A. Early N7TAE
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <opendht.h>
|
||||
|
||||
// comment out sections you don't need for your application
|
||||
//#define USE_MREFD_VALUES
|
||||
#define USE_URFD_VALUES
|
||||
|
||||
// a typesafe way to extract the numeric value from a enum class
|
||||
// note that this is a constexpr and so can even be used in an
|
||||
// array declaration or as a tuple index
|
||||
template<typename E> constexpr auto toUType(E enumerator) noexcept
|
||||
{
|
||||
return static_cast<std::underlying_type_t<E>>(enumerator);
|
||||
} // Item #10 in "Effective Modern C++", by Scott Meyers, O'REILLY
|
||||
|
||||
#ifdef USE_MREFD_VALUES
|
||||
|
||||
// DHT::value::user_type for mrefd values
|
||||
// These are the value release version strings in a
|
||||
// preprocessor definition for your convience
|
||||
// if your app needs a particular part, then it should handle all versions of that part
|
||||
#define MREFD_CONFIG_1 "mrefd-config-1"
|
||||
|
||||
// dht::Value ids of the different parts of the mrefd document
|
||||
// can be assigned any unsigned value except 0
|
||||
// more parts can be added, but don't change the value of any existing part
|
||||
// using toUType, you can set or query a user_type to determine the value part
|
||||
// this can be done before unpacking the MSGPACK
|
||||
enum class EMrefdValueID : uint64_t { Config=1, Peers=2, Clients=3, Users=4 };
|
||||
|
||||
///////////////// MREFD PART VALUES ///////////////
|
||||
|
||||
// the configuration part
|
||||
struct SMrefdConfig1 // user_type is MREFD_CONFIG_1
|
||||
{
|
||||
std::time_t timestamp; // when this value was set
|
||||
std::string callsign; // the callsign of the mrefd reflector
|
||||
std::string ipv4addr; // the external IPv4 address
|
||||
std::string ipv6addr; // the external IPv6 address
|
||||
std::string modules; // all the configured modules, [A-Z]
|
||||
std::string encryptedmods; // modules that will pass encrypted streams
|
||||
std::string url; // the URL of the dashboard
|
||||
std::string email; // the email of the responsible owner
|
||||
std::string sponsor; // the organization or individual sponsoring the reflector
|
||||
std::string country; // the 2-letter country code
|
||||
std::string version; // the version of the reflector software
|
||||
uint16_t port; // the connection listening UDP port, usually 17000
|
||||
|
||||
// the order in which MSGPACK serializes the data
|
||||
MSGPACK_DEFINE(timestamp, callsign, ipv4addr, ipv6addr, modules, encryptedmods, url, email, sponsor, country, version, port)
|
||||
};
|
||||
|
||||
#endif // USE_MREFD_VALUES
|
||||
|
||||
#ifdef USE_URFD_VALUES
|
||||
|
||||
// DHT::value::user_type for urfd values
|
||||
// These are the value release version strings in a
|
||||
// preprocessor definition for your convience
|
||||
// if your app needs a particular part, then it should handle all versions of that part
|
||||
#define URFD_CONFIG_1 "urfd-config-1"
|
||||
#define URFD_CONFIG_2 "urfd-config-2"
|
||||
|
||||
// dht::Value::id of the different parts of the urfd document
|
||||
// can be assigned any unsigned value except 0
|
||||
// more parts can be added, but don't change the value of any existing part
|
||||
// using toUType, you can set or query a user_type to determine the value part
|
||||
// this can be done before unpacking the MSGPACK
|
||||
enum class EUrfdValueID : uint64_t { Config=1, Peers=2, Clients=3, Users=4 };
|
||||
|
||||
// the following enum classes can be used to reference a particular value in a fixed array
|
||||
// 'SIZE' has to be last value for these scoped enums as this is used to declare these arrays
|
||||
//
|
||||
// all the configurable ports in urfd (G3 and BM are not configurable)
|
||||
enum class EUrfdPorts : unsigned { dcs, dextra, dmrplus, dplus, m17, mmdvm, nxdn, p25, urf, ysf, SIZE };
|
||||
// autolink modules for these protocols
|
||||
enum class EUrfdAlMod : unsigned { nxdn, p25, ysf, SIZE };
|
||||
// default TX/RX values for ysf
|
||||
enum class EUrfdTxRx : unsigned { rx, tx, SIZE };
|
||||
// reflector ID values for these two modes
|
||||
enum class EUrfdRefId : unsigned { nxdn, p25, SIZE };
|
||||
|
||||
struct SUrfdConfig1 // user_type is URFD_CONFIG_1
|
||||
{
|
||||
std::time_t timestamp;
|
||||
std::string callsign, ipv4addr, ipv6addr, modules, transcodedmods, url, email, sponsor, country, version;
|
||||
// transcodedmods are those modules that support full transcoding
|
||||
std::array<uint16_t, toUType(EUrfdPorts::SIZE)> port;
|
||||
std::array<char, toUType(EUrfdAlMod::SIZE)> almod;
|
||||
std::array<unsigned long, toUType(EUrfdTxRx::SIZE)> ysffreq;
|
||||
std::array<unsigned, toUType(EUrfdRefId::SIZE)> refid;
|
||||
std::unordered_map<char, std::string> description;
|
||||
bool g3enabled;
|
||||
|
||||
MSGPACK_DEFINE(timestamp, callsign, ipv4addr, ipv6addr, modules, transcodedmods, url, email, sponsor, country, version, almod, ysffreq, refid, g3enabled, port, description)
|
||||
};
|
||||
|
||||
enum class EUrfdPorts2 : unsigned { dcs, dextra, dmrplus, dplus, dsd, m17, mmdvm, nxdn, p25, urf, ysf, SIZE };
|
||||
|
||||
struct SUrfdConfig2
|
||||
{
|
||||
std::time_t timestamp;
|
||||
std::string callsign, ipv4addr, ipv6addr, modules, transcodedmods, url, email, sponsor, country, version;
|
||||
std::array<uint16_t, toUType(EUrfdPorts2::SIZE)> port;
|
||||
std::array<char, toUType(EUrfdAlMod::SIZE)> almod;
|
||||
std::array<unsigned long, toUType(EUrfdTxRx::SIZE)> ysffreq;
|
||||
std::array<unsigned, toUType(EUrfdRefId::SIZE)> refid;
|
||||
std::unordered_map<char, std::string> description;
|
||||
bool g3enabled;
|
||||
|
||||
MSGPACK_DEFINE(timestamp, callsign, ipv4addr, ipv6addr, modules, transcodedmods, url, email, sponsor, country, version, almod, ysffreq, refid, g3enabled, port, description)
|
||||
};
|
||||
|
||||
#endif // USE_URFD_VALUES
|
||||
@ -1,89 +0,0 @@
|
||||
// Copyright © 2023 Thomas A. Early, N7TAE
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// This file is part of urfd.
|
||||
//
|
||||
// M17Refd is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// M17Refd 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 General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// with this software. If not, see <http://www.gnu.org/licenses/>.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <opendht.h>
|
||||
|
||||
/* HELPERS */
|
||||
#ifndef TO_U_TYPE_DEF
|
||||
#define TO_U_TYPE_DEF
|
||||
template<typename E> constexpr auto toUType(E enumerator) noexcept
|
||||
{
|
||||
return static_cast<std::underlying_type_t<E>>(enumerator);
|
||||
} // Item #10 in "Effective Modern C++", by Scott Meyers, O'REILLY
|
||||
#endif
|
||||
|
||||
enum class EUrfdValueID : uint64_t { Config=1, Peers=2, Clients=3, Users=4 };
|
||||
|
||||
/* PEERS */
|
||||
using UrfdPeerTuple = std::tuple<std::string, std::string, std::time_t>;
|
||||
enum class EUrfdPeerFields { Callsign, Modules, ConnectTime };
|
||||
struct SUrfdPeers1
|
||||
{
|
||||
std::time_t timestamp;
|
||||
unsigned int sequence;
|
||||
std::list<UrfdPeerTuple> list;
|
||||
|
||||
MSGPACK_DEFINE(timestamp, sequence, list)
|
||||
};
|
||||
|
||||
/* CLIENTS */
|
||||
using UrfdClientTuple = std::tuple<std::string, std::string, char, std::time_t, std::time_t>;
|
||||
enum class EUrfdClientFields { Callsign, Ip, Module, ConnectTime, LastHeardTime };
|
||||
struct SUrfdClients1
|
||||
{
|
||||
std::time_t timestamp;
|
||||
unsigned int sequence;
|
||||
std::list<UrfdClientTuple> list;
|
||||
|
||||
MSGPACK_DEFINE(timestamp, sequence, list)
|
||||
};
|
||||
|
||||
/* USERS */
|
||||
using UrfdUserTuple = std::tuple<std::string, std::string, char, std::string, std::time_t>;
|
||||
enum class EUrfdUserFields { Callsign, ViaNode, OnModule, ViaPeer, LastHeardTime };
|
||||
struct SUrfdUsers1
|
||||
{
|
||||
std::time_t timestamp;
|
||||
unsigned int sequence;
|
||||
std::list<UrfdUserTuple> list;
|
||||
|
||||
MSGPACK_DEFINE(timestamp, sequence, list)
|
||||
};
|
||||
|
||||
/* CONFIGURATION */
|
||||
// 'SIZE' has to be last for these scoped enums
|
||||
enum class EUrfdPorts : unsigned { dcs, dextra, dmrplus, dplus, m17, mmdvm, nxdn, p25, urf, ysf, SIZE };
|
||||
enum class EUrfdAlMod : unsigned { nxdn, p25, ysf, SIZE };
|
||||
enum class EUrfdTxRx : unsigned { rx, tx, SIZE };
|
||||
enum class EUrfdRefId : unsigned { nxdn, p25, SIZE };
|
||||
struct SUrfdConfig1
|
||||
{
|
||||
std::time_t timestamp;
|
||||
std::string cs, ipv4, ipv6, mods, tcmods, url, email, sponsor, country, version;
|
||||
std::array<uint16_t, toUType(EUrfdPorts::SIZE)> port;
|
||||
std::array<char, toUType(EUrfdAlMod::SIZE)> almod;
|
||||
std::array<unsigned long, toUType(EUrfdTxRx::SIZE)> ysffreq;
|
||||
std::array<unsigned, toUType(EUrfdRefId::SIZE)> refid;
|
||||
std::unordered_map<char, std::string> description;
|
||||
bool g3enabled;
|
||||
|
||||
MSGPACK_DEFINE(timestamp, cs, ipv4, ipv6, mods, tcmods, url, email, sponsor, country, version, almod, ysffreq, refid, g3enabled, port, description)
|
||||
};
|
||||
Loading…
Reference in new issue