2014-07-14 11:49:44 +00:00
|
|
|
/** Message paths.
|
2014-06-05 09:34:29 +00:00
|
|
|
*
|
|
|
|
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
2020-01-20 17:17:00 +01:00
|
|
|
* @copyright 2014-2020, Institute for Automation of Complex Power Systems, EONERC
|
2017-04-27 12:56:43 +02:00
|
|
|
* @license GNU General Public License (version 3)
|
|
|
|
*
|
|
|
|
* VILLASnode
|
|
|
|
*
|
|
|
|
* 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
|
|
|
|
* any later version.
|
2017-05-05 19:24:16 +00:00
|
|
|
*
|
2017-04-27 12:56:43 +02:00
|
|
|
* 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.
|
2017-05-05 19:24:16 +00:00
|
|
|
*
|
2017-04-27 12:56:43 +02:00
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2015-06-02 21:53:04 +02:00
|
|
|
*********************************************************************************/
|
2014-06-05 09:34:29 +00:00
|
|
|
|
2019-06-23 16:57:00 +02:00
|
|
|
#include <cstdint>
|
|
|
|
#include <cstring>
|
|
|
|
#include <cinttypes>
|
|
|
|
#include <cerrno>
|
|
|
|
|
2020-09-10 17:36:08 +02:00
|
|
|
#include <algorithm>
|
|
|
|
#include <list>
|
|
|
|
#include <map>
|
|
|
|
|
2014-06-25 17:50:30 +00:00
|
|
|
#include <unistd.h>
|
2017-08-30 23:53:35 +02:00
|
|
|
#include <poll.h>
|
2014-06-05 09:34:29 +00:00
|
|
|
|
2018-08-23 17:31:01 +02:00
|
|
|
#include <villas/node/config.h>
|
2019-04-23 13:09:50 +02:00
|
|
|
#include <villas/utils.hpp>
|
2019-04-23 13:11:08 +02:00
|
|
|
#include <villas/colors.hpp>
|
2017-12-09 02:19:28 +08:00
|
|
|
#include <villas/timing.h>
|
|
|
|
#include <villas/pool.h>
|
|
|
|
#include <villas/queue.h>
|
2019-06-23 16:13:23 +02:00
|
|
|
#include <villas/hook.hpp>
|
2019-04-23 13:12:04 +02:00
|
|
|
#include <villas/hook_list.hpp>
|
2017-12-09 02:19:28 +08:00
|
|
|
#include <villas/plugin.h>
|
|
|
|
#include <villas/memory.h>
|
|
|
|
#include <villas/node.h>
|
2018-08-20 18:31:27 +02:00
|
|
|
#include <villas/signal.h>
|
2019-02-24 09:39:41 +01:00
|
|
|
#include <villas/path.h>
|
2020-09-13 08:36:48 +02:00
|
|
|
#include <villas/kernel/rt.hpp>
|
2019-02-24 09:39:41 +01:00
|
|
|
#include <villas/path_source.h>
|
|
|
|
#include <villas/path_destination.h>
|
2018-05-08 08:16:14 +02:00
|
|
|
|
2019-04-07 16:16:58 +02:00
|
|
|
using namespace villas;
|
2019-06-23 16:13:23 +02:00
|
|
|
using namespace villas::node;
|
2019-06-04 16:55:38 +02:00
|
|
|
using namespace villas::utils;
|
2019-04-07 16:16:58 +02:00
|
|
|
|
2020-08-28 09:41:17 +02:00
|
|
|
/** Main thread function per path:
|
|
|
|
* read samples from source -> write samples to destinations
|
|
|
|
*
|
|
|
|
* This is an optimized version of path_run_poll() which is
|
|
|
|
* used for paths which only have a single source.
|
|
|
|
* In this case we case save a call to poll() and directly call
|
|
|
|
* path_source_read() / node_read().
|
|
|
|
*/
|
2018-05-08 08:16:14 +02:00
|
|
|
static void * path_run_single(void *arg)
|
|
|
|
{
|
2019-02-11 16:39:30 +01:00
|
|
|
int ret;
|
2020-06-08 02:25:07 +02:00
|
|
|
struct vpath *p = (struct vpath *) arg;
|
|
|
|
struct vpath_source *ps = (struct vpath_source *) vlist_at(&p->sources, 0);
|
2018-05-08 08:16:14 +02:00
|
|
|
|
2019-06-23 16:13:23 +02:00
|
|
|
while (p->state == State::STARTED) {
|
2019-02-11 16:39:30 +01:00
|
|
|
pthread_testcancel();
|
|
|
|
|
|
|
|
ret = path_source_read(ps, p, 0);
|
|
|
|
if (ret <= 0)
|
|
|
|
continue;
|
2018-05-08 08:16:14 +02:00
|
|
|
|
2019-01-07 10:28:55 +01:00
|
|
|
for (size_t i = 0; i < vlist_length(&p->destinations); i++) {
|
2020-06-08 02:25:07 +02:00
|
|
|
struct vpath_destination *pd = (struct vpath_destination *) vlist_at(&p->destinations, i);
|
2018-05-08 08:16:14 +02:00
|
|
|
|
|
|
|
path_destination_write(pd, p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-08 08:59:08 +02:00
|
|
|
return nullptr;
|
2018-05-08 08:16:14 +02:00
|
|
|
}
|
|
|
|
|
2020-08-28 09:41:17 +02:00
|
|
|
/** Main thread function per path:
|
|
|
|
* read samples from source -> write samples to destinations
|
|
|
|
*
|
|
|
|
* This variant of the path uses poll() to listen on an event from
|
|
|
|
* all path sources.
|
|
|
|
*/
|
2018-05-08 08:16:14 +02:00
|
|
|
static void * path_run_poll(void *arg)
|
2016-11-07 22:17:45 -05:00
|
|
|
{
|
2018-05-08 08:16:14 +02:00
|
|
|
int ret;
|
2020-06-08 02:25:07 +02:00
|
|
|
struct vpath *p = (struct vpath *) arg;
|
2016-11-07 22:17:45 -05:00
|
|
|
|
2019-06-23 16:13:23 +02:00
|
|
|
while (p->state == State::STARTED) {
|
2017-10-16 23:07:42 +02:00
|
|
|
ret = poll(p->reader.pfds, p->reader.nfds, -1);
|
|
|
|
if (ret < 0)
|
|
|
|
serror("Failed to poll");
|
|
|
|
|
2019-04-07 16:16:58 +02:00
|
|
|
p->logger->debug("Path {} returned from poll(2)", path_name(p));
|
2018-05-23 02:24:55 +02:00
|
|
|
|
2017-10-16 23:07:42 +02:00
|
|
|
for (int i = 0; i < p->reader.nfds; i++) {
|
2020-06-08 02:25:07 +02:00
|
|
|
struct vpath_source *ps = (struct vpath_source *) vlist_at(&p->sources, i);
|
2017-10-16 23:07:42 +02:00
|
|
|
|
|
|
|
if (p->reader.pfds[i].revents & POLLIN) {
|
|
|
|
/* Timeout: re-enqueue the last sample */
|
2020-03-04 13:06:28 +01:00
|
|
|
if (p->reader.pfds[i].fd == p->timeout.getFD()) {
|
|
|
|
p->timeout.wait();
|
2017-10-16 23:07:42 +02:00
|
|
|
|
|
|
|
p->last_sample->sequence = p->last_sequence++;
|
|
|
|
|
|
|
|
path_destination_enqueue(p, &p->last_sample, 1);
|
|
|
|
}
|
|
|
|
/* A source is ready to receive samples */
|
2019-02-11 16:39:30 +01:00
|
|
|
else
|
2018-05-08 08:16:14 +02:00
|
|
|
path_source_read(ps, p, i);
|
2017-10-16 23:07:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-07 10:28:55 +01:00
|
|
|
for (size_t i = 0; i < vlist_length(&p->destinations); i++) {
|
2020-06-08 02:25:07 +02:00
|
|
|
struct vpath_destination *pd = (struct vpath_destination *) vlist_at(&p->destinations, i);
|
2017-10-16 23:07:42 +02:00
|
|
|
|
2018-05-08 08:16:14 +02:00
|
|
|
path_destination_write(pd, p);
|
2017-10-16 23:07:42 +02:00
|
|
|
}
|
2016-11-07 22:17:45 -05:00
|
|
|
}
|
2014-07-18 16:05:44 +00:00
|
|
|
|
2019-04-08 08:59:08 +02:00
|
|
|
return nullptr;
|
2014-06-05 09:34:29 +00:00
|
|
|
}
|
|
|
|
|
2020-06-08 02:25:07 +02:00
|
|
|
int path_init(struct vpath *p)
|
2015-12-11 17:56:14 +01:00
|
|
|
{
|
2018-08-20 18:31:27 +02:00
|
|
|
int ret;
|
|
|
|
|
2019-04-23 11:02:29 +02:00
|
|
|
new (&p->logger) Logger;
|
2019-04-23 10:55:23 +02:00
|
|
|
new (&p->received) std::bitset<MAX_SAMPLE_LENGTH>;
|
|
|
|
new (&p->mask) std::bitset<MAX_SAMPLE_LENGTH>;
|
2020-06-08 04:03:07 +02:00
|
|
|
new (&p->timeout) Task(CLOCK_MONOTONIC);
|
2019-04-07 16:16:58 +02:00
|
|
|
|
2019-04-23 11:02:29 +02:00
|
|
|
p->logger = logging.get("path");
|
2019-04-07 16:16:58 +02:00
|
|
|
|
2020-10-16 09:25:23 +02:00
|
|
|
uuid_clear(p->uuid);
|
|
|
|
|
2019-01-07 10:28:55 +01:00
|
|
|
ret = vlist_init(&p->destinations);
|
2018-08-20 18:31:27 +02:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2019-01-07 10:28:55 +01:00
|
|
|
ret = vlist_init(&p->sources);
|
2018-08-20 18:31:27 +02:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2019-02-24 09:10:18 +01:00
|
|
|
ret = signal_list_init(&p->signals);
|
2018-08-20 18:31:27 +02:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2019-06-11 18:34:23 +02:00
|
|
|
ret = vlist_init(&p->mappings);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2019-04-04 11:45:08 +02:00
|
|
|
#ifdef WITH_HOOKS
|
2019-02-24 09:39:41 +01:00
|
|
|
ret = hook_list_init(&p->hooks);
|
2018-08-20 18:31:27 +02:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
2019-04-05 02:22:53 +02:00
|
|
|
#endif /* WITH_HOOKS */
|
|
|
|
|
2019-04-08 08:59:08 +02:00
|
|
|
p->_name = nullptr;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2019-07-30 12:01:44 +02:00
|
|
|
p->reader.pfds = nullptr;
|
|
|
|
p->reader.nfds = 0;
|
|
|
|
|
2017-03-27 12:54:24 +02:00
|
|
|
/* Default values */
|
2019-06-23 16:13:23 +02:00
|
|
|
p->mode = PathMode::ANY;
|
2017-10-16 23:07:42 +02:00
|
|
|
p->rate = 0; /* Disabled */
|
|
|
|
|
2018-04-19 14:08:35 +02:00
|
|
|
p->builtin = 1;
|
2017-03-27 12:54:24 +02:00
|
|
|
p->reverse = 0;
|
|
|
|
p->enabled = 1;
|
2018-05-23 02:24:55 +02:00
|
|
|
p->poll = -1;
|
2018-08-02 10:43:49 +02:00
|
|
|
p->queuelen = DEFAULT_QUEUE_LENGTH;
|
2019-04-02 16:01:56 +02:00
|
|
|
p->original_sequence_no = -1;
|
2020-09-13 08:36:48 +02:00
|
|
|
p->affinity = 0;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2019-06-23 16:13:23 +02:00
|
|
|
p->state = State::INITIALIZED;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2016-06-08 22:38:21 +02:00
|
|
|
return 0;
|
2014-06-05 09:34:29 +00:00
|
|
|
}
|
|
|
|
|
2020-06-08 02:25:07 +02:00
|
|
|
static int path_prepare_poll(struct vpath *p)
|
2018-05-23 02:24:55 +02:00
|
|
|
{
|
2020-06-08 04:03:07 +02:00
|
|
|
int fds[16], n = 0, m;
|
2018-05-23 02:24:55 +02:00
|
|
|
|
2019-07-30 12:01:44 +02:00
|
|
|
if (p->reader.pfds)
|
2020-01-21 16:26:51 +01:00
|
|
|
delete[] p->reader.pfds;
|
2019-07-30 12:01:44 +02:00
|
|
|
|
2019-04-08 08:59:08 +02:00
|
|
|
p->reader.pfds = nullptr;
|
2019-01-30 01:57:31 +01:00
|
|
|
p->reader.nfds = 0;
|
2018-05-23 02:24:55 +02:00
|
|
|
|
2019-04-07 15:13:40 +02:00
|
|
|
for (unsigned i = 0; i < vlist_length(&p->sources); i++) {
|
2020-06-08 02:25:07 +02:00
|
|
|
struct vpath_source *ps = (struct vpath_source *) vlist_at(&p->sources, i);
|
2018-05-23 02:24:55 +02:00
|
|
|
|
2019-01-30 01:57:31 +01:00
|
|
|
m = node_poll_fds(ps->node, fds);
|
2020-09-10 17:36:08 +02:00
|
|
|
if (m <= 0)
|
|
|
|
throw RuntimeError("Failed to get file descriptor for node {}", node_name(ps->node));
|
2019-01-30 01:57:31 +01:00
|
|
|
|
|
|
|
p->reader.nfds += m;
|
2019-04-07 15:13:40 +02:00
|
|
|
p->reader.pfds = (struct pollfd *) realloc(p->reader.pfds, p->reader.nfds * sizeof(struct pollfd));
|
2018-05-23 02:24:55 +02:00
|
|
|
|
2019-01-30 01:57:31 +01:00
|
|
|
for (int i = 0; i < m; i++) {
|
2020-08-28 09:40:14 +02:00
|
|
|
if (fds[i] < 0)
|
|
|
|
throw RuntimeError("Failed to get file descriptor for node {}", node_name(ps->node));
|
2019-01-21 15:47:34 +01:00
|
|
|
|
|
|
|
/* This slot is only used if it is not masked */
|
2019-01-30 01:57:31 +01:00
|
|
|
p->reader.pfds[n].events = POLLIN;
|
|
|
|
p->reader.pfds[n++].fd = fds[i];
|
2019-01-21 15:47:34 +01:00
|
|
|
}
|
2018-05-23 02:24:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* We use the last slot for the timeout timer. */
|
|
|
|
if (p->rate > 0) {
|
2020-06-08 04:03:07 +02:00
|
|
|
p->timeout.setRate(p->rate);
|
2018-05-23 02:24:55 +02:00
|
|
|
|
2019-01-30 01:57:31 +01:00
|
|
|
p->reader.nfds++;
|
2019-04-07 15:13:40 +02:00
|
|
|
p->reader.pfds = (struct pollfd *) realloc(p->reader.pfds, p->reader.nfds * sizeof(struct pollfd));
|
2019-01-30 01:57:31 +01:00
|
|
|
|
|
|
|
p->reader.pfds[p->reader.nfds-1].events = POLLIN;
|
2020-03-04 13:06:28 +01:00
|
|
|
p->reader.pfds[p->reader.nfds-1].fd = p->timeout.getFD();
|
2019-04-07 16:16:58 +02:00
|
|
|
if (p->reader.pfds[p->reader.nfds-1].fd < 0) {
|
|
|
|
p->logger->warn("Failed to get file descriptor for timer of path {}", path_name(p));
|
|
|
|
return -1;
|
|
|
|
}
|
2018-05-23 02:24:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-09-10 17:36:08 +02:00
|
|
|
int path_prepare(struct vpath *p, struct vlist *nodes)
|
2017-08-30 23:53:35 +02:00
|
|
|
{
|
2017-08-31 11:29:49 +02:00
|
|
|
int ret;
|
2020-09-10 17:36:08 +02:00
|
|
|
unsigned pool_size;
|
|
|
|
|
|
|
|
struct memory_type *pool_mt = memory_default;
|
2017-08-30 23:53:35 +02:00
|
|
|
|
2019-06-23 16:13:23 +02:00
|
|
|
assert(p->state == State::CHECKED);
|
2017-08-30 23:53:35 +02:00
|
|
|
|
2020-09-10 17:36:08 +02:00
|
|
|
p->mask.reset();
|
|
|
|
|
|
|
|
/* Prepare mappings */
|
|
|
|
ret = mapping_list_prepare(&p->mappings, nodes);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* Create path sources */
|
|
|
|
std::map<struct vnode *, struct vpath_source *> pss;
|
|
|
|
for (size_t i = 0; i < vlist_length(&p->mappings); i++) {
|
|
|
|
struct mapping_entry *me = (struct mapping_entry *) vlist_at(&p->mappings, i);
|
|
|
|
struct vnode *n = me->node;
|
|
|
|
struct vpath_source *ps;
|
|
|
|
|
|
|
|
if (pss.find(n) != pss.end())
|
|
|
|
/* We already have a path source for this mapping entry */
|
|
|
|
ps = pss[n];
|
|
|
|
else {
|
|
|
|
/* Create new path source */
|
|
|
|
ps = pss[n] = new struct vpath_source;
|
|
|
|
if (!ps)
|
|
|
|
throw MemoryAllocationError();
|
|
|
|
|
|
|
|
/* Depending on weather the node belonging to this mapping is already
|
|
|
|
* used by another path or not, we will create a master or secondary
|
|
|
|
* path source.
|
|
|
|
* A secondary path source uses an internal loopback node / queue
|
|
|
|
* to forward samples from on path to another.
|
|
|
|
*/
|
|
|
|
bool isSecondary = vlist_length(&n->sources) > 0;
|
|
|
|
ret = isSecondary
|
|
|
|
? path_source_init_secondary(ps, n)
|
|
|
|
: path_source_init_master(ps, n);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
if (ps->type == PathSourceType::SECONDARY) {
|
|
|
|
vlist_push(nodes, ps->node);
|
|
|
|
vlist_push(&ps->node->sources, ps);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p->mask_list.empty() || std::find(p->mask_list.begin(), p->mask_list.end(), n) != p->mask_list.end()) {
|
|
|
|
ps->masked = true;
|
|
|
|
p->mask.set(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
vlist_push(&n->sources, ps);
|
|
|
|
vlist_push(&p->sources, ps);
|
|
|
|
}
|
|
|
|
|
2020-10-21 20:56:51 +02:00
|
|
|
struct vlist *sigs = node_input_signals(me->node);
|
2020-09-10 17:36:08 +02:00
|
|
|
|
|
|
|
/* Update signals of path */
|
|
|
|
for (unsigned j = 0; j < (unsigned) me->length; j++) {
|
|
|
|
struct signal *sig;
|
|
|
|
|
|
|
|
/* For data mappings we simple refer to the existing
|
|
|
|
* signal descriptors of the source node. */
|
|
|
|
if (me->type == MappingType::DATA) {
|
|
|
|
sig = (struct signal *) vlist_at_safe(sigs, me->data.offset + j);
|
|
|
|
if (!sig) {
|
|
|
|
p->logger->warn("Failed to create signal description for path {}", path_name(p));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
signal_incref(sig);
|
|
|
|
}
|
|
|
|
/* For other mappings we create new signal descriptors */
|
|
|
|
else {
|
|
|
|
sig = new struct signal;
|
|
|
|
if (!sig)
|
|
|
|
throw MemoryAllocationError();
|
2018-07-23 22:46:03 +02:00
|
|
|
|
2020-09-10 17:36:08 +02:00
|
|
|
ret = signal_init_from_mapping(sig, me, j);
|
|
|
|
if (ret)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
vlist_extend(&p->signals, me->offset + j + 1, nullptr);
|
|
|
|
vlist_set(&p->signals, me->offset + j, sig);
|
|
|
|
}
|
|
|
|
|
|
|
|
vlist_push(&ps->mappings, me);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Prepare path destinations */
|
2019-01-07 10:28:55 +01:00
|
|
|
for (size_t i = 0; i < vlist_length(&p->destinations); i++) {
|
2020-09-10 17:36:08 +02:00
|
|
|
auto *pd = (struct vpath_destination *) vlist_at(&p->destinations, i);
|
2017-08-30 23:53:35 +02:00
|
|
|
|
2019-02-24 11:08:56 +01:00
|
|
|
if (node_type(pd->node)->pool_size > pool_size)
|
|
|
|
pool_size = node_type(pd->node)->pool_size;
|
2018-08-07 09:16:17 +02:00
|
|
|
|
2019-02-24 11:08:56 +01:00
|
|
|
if (node_type(pd->node)->memory_type)
|
2019-10-26 13:34:03 +02:00
|
|
|
pool_mt = node_memory_type(pd->node);
|
2018-07-23 22:46:03 +02:00
|
|
|
|
2020-08-28 09:37:06 +02:00
|
|
|
ret = path_destination_prepare(pd, p->queuelen);
|
2017-08-30 23:53:35 +02:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-09-10 17:36:08 +02:00
|
|
|
/* Prepare pool */
|
|
|
|
pool_size = MAX(1UL, vlist_length(&p->destinations)) * p->queuelen;
|
|
|
|
ret = pool_init(&p->pool, pool_size, SAMPLE_LENGTH(vlist_length(&p->signals)), pool_mt);
|
2019-06-11 18:34:23 +02:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2020-09-10 17:36:08 +02:00
|
|
|
/* Autodetect whether to use original sequence numbers or not */
|
|
|
|
if (p->original_sequence_no == -1)
|
|
|
|
p->original_sequence_no = vlist_length(&p->sources) == 1;
|
2017-08-30 23:53:35 +02:00
|
|
|
|
2020-09-10 17:36:08 +02:00
|
|
|
/* Autodetect whether to use poll() for this path or not */
|
|
|
|
if (p->poll == -1) {
|
|
|
|
if (p->rate > 0)
|
|
|
|
p->poll = 1;
|
|
|
|
else if (vlist_length(&p->sources) > 1)
|
|
|
|
p->poll = 1;
|
|
|
|
else
|
|
|
|
p->poll = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Prepare poll() */
|
|
|
|
if (p->poll) {
|
|
|
|
ret = path_prepare_poll(p);
|
2017-08-30 23:53:35 +02:00
|
|
|
if (ret)
|
2017-09-02 14:27:58 +02:00
|
|
|
return ret;
|
2017-08-30 23:53:35 +02:00
|
|
|
}
|
2017-09-02 14:27:58 +02:00
|
|
|
|
2019-03-26 17:01:55 +01:00
|
|
|
#ifdef WITH_HOOKS
|
2019-06-23 16:13:23 +02:00
|
|
|
int m = p->builtin ? (int) Hook::Flags::PATH | (int) Hook::Flags::BUILTIN : 0;
|
2019-03-26 17:01:55 +01:00
|
|
|
|
|
|
|
/* Add internal hooks if they are not already in the list */
|
2019-06-11 18:32:58 +02:00
|
|
|
hook_list_prepare(&p->hooks, &p->signals, m, p, nullptr);
|
2019-03-26 17:01:55 +01:00
|
|
|
#endif /* WITH_HOOKS */
|
|
|
|
|
2020-07-04 17:14:39 +02:00
|
|
|
p->logger->info("Prepared path {} with output signals:", path_name(p));
|
2020-10-21 20:56:51 +02:00
|
|
|
signal_list_dump(path_output_signals(p));
|
2020-07-04 17:14:39 +02:00
|
|
|
|
2019-06-23 16:13:23 +02:00
|
|
|
p->state = State::PREPARED;
|
2019-02-24 11:13:28 +01:00
|
|
|
|
2017-08-30 23:53:35 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-10-16 11:08:40 +02:00
|
|
|
int path_parse(struct vpath *p, json_t *cfg, struct vlist *nodes, const uuid_t sn_uuid)
|
2015-12-11 17:56:14 +01:00
|
|
|
{
|
2017-03-27 12:54:24 +02:00
|
|
|
int ret;
|
2017-08-03 00:19:27 +02:00
|
|
|
|
|
|
|
json_error_t err;
|
2017-08-28 14:38:30 +02:00
|
|
|
json_t *json_in;
|
2019-04-08 08:59:08 +02:00
|
|
|
json_t *json_out = nullptr;
|
|
|
|
json_t *json_hooks = nullptr;
|
|
|
|
json_t *json_mask = nullptr;
|
2017-10-16 23:07:42 +02:00
|
|
|
|
2020-10-16 09:25:23 +02:00
|
|
|
const char *mode = nullptr;
|
|
|
|
const char *uuid_str = nullptr;
|
2015-08-07 01:11:43 +02:00
|
|
|
|
2020-06-16 02:35:34 +02:00
|
|
|
struct vlist destinations;
|
2020-09-10 17:36:08 +02:00
|
|
|
|
|
|
|
ret = vlist_init(&destinations);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2014-06-25 17:50:30 +00:00
|
|
|
|
2020-09-13 08:36:48 +02:00
|
|
|
ret = json_unpack_ex(cfg, &err, 0, "{ s: o, s?: o, s?: o, s?: b, s?: b, s?: b, s?: i, s?: s, s?: b, s?: F, s?: o, s?: b, s?: s, s?: i }",
|
2017-08-28 14:38:30 +02:00
|
|
|
"in", &json_in,
|
|
|
|
"out", &json_out,
|
|
|
|
"hooks", &json_hooks,
|
2017-08-03 00:19:27 +02:00
|
|
|
"reverse", &p->reverse,
|
|
|
|
"enabled", &p->enabled,
|
2018-04-19 14:08:35 +02:00
|
|
|
"builtin", &p->builtin,
|
2017-10-16 23:07:42 +02:00
|
|
|
"queuelen", &p->queuelen,
|
|
|
|
"mode", &mode,
|
2018-05-23 02:24:55 +02:00
|
|
|
"poll", &p->poll,
|
2017-10-16 23:07:42 +02:00
|
|
|
"rate", &p->rate,
|
2018-10-14 14:44:56 +02:00
|
|
|
"mask", &json_mask,
|
2020-08-17 17:01:36 +02:00
|
|
|
"original_sequence_no", &p->original_sequence_no,
|
2020-10-16 09:25:23 +02:00
|
|
|
"uuid", &uuid_str,
|
2020-09-13 08:36:48 +02:00
|
|
|
"affinity", &p->affinity
|
2017-08-03 00:19:27 +02:00
|
|
|
);
|
|
|
|
if (ret)
|
2020-09-10 17:36:08 +02:00
|
|
|
throw ConfigError(cfg, err, "node-config-path", "Failed to parse path configuration");
|
2017-08-28 14:38:30 +02:00
|
|
|
|
|
|
|
/* Optional settings */
|
2017-10-16 23:07:42 +02:00
|
|
|
if (mode) {
|
|
|
|
if (!strcmp(mode, "any"))
|
2019-06-23 16:13:23 +02:00
|
|
|
p->mode = PathMode::ANY;
|
2017-10-16 23:07:42 +02:00
|
|
|
else if (!strcmp(mode, "all"))
|
2019-06-23 16:13:23 +02:00
|
|
|
p->mode = PathMode::ALL;
|
2020-08-28 09:40:14 +02:00
|
|
|
else
|
|
|
|
throw ConfigError(cfg, "node-config-path", "Invalid path mode '{}'", mode);
|
2017-10-16 23:07:42 +02:00
|
|
|
}
|
2016-10-20 18:04:18 -04:00
|
|
|
|
2020-09-10 17:36:08 +02:00
|
|
|
/* UUID */
|
2020-10-16 09:25:23 +02:00
|
|
|
if (uuid_str) {
|
|
|
|
ret = uuid_parse(uuid_str, p->uuid);
|
2020-08-17 17:01:36 +02:00
|
|
|
if (ret)
|
2020-10-16 09:25:23 +02:00
|
|
|
throw ConfigError(cfg, "node-config-path-uuid", "Failed to parse UUID: {}", uuid_str);
|
2020-08-17 17:01:36 +02:00
|
|
|
}
|
2020-10-16 09:25:23 +02:00
|
|
|
else
|
2020-08-17 17:01:36 +02:00
|
|
|
/* Generate UUID from hashed config */
|
2020-10-16 11:08:40 +02:00
|
|
|
uuid_generate_from_json(p->uuid, cfg, sn_uuid);
|
2020-08-17 17:01:36 +02:00
|
|
|
|
2020-08-28 09:42:46 +02:00
|
|
|
/* Input node(s) */
|
2020-09-10 17:36:08 +02:00
|
|
|
ret = mapping_list_parse(&p->mappings, json_in);
|
2020-08-28 09:42:46 +02:00
|
|
|
if (ret)
|
|
|
|
throw ConfigError(json_in, "node-config-path-in", "Failed to parse input mapping of path {}", path_name(p));
|
2016-11-20 12:59:37 -05:00
|
|
|
|
2020-08-28 09:42:46 +02:00
|
|
|
/* Output node(s) */
|
|
|
|
if (json_out) {
|
|
|
|
ret = node_list_parse(&destinations, json_out, nodes);
|
|
|
|
if (ret)
|
|
|
|
throw ConfigError(json_out, "node-config-path-out", "Failed to parse output nodes");
|
|
|
|
}
|
|
|
|
|
2019-01-07 10:28:55 +01:00
|
|
|
for (size_t i = 0; i < vlist_length(&destinations); i++) {
|
2020-08-25 21:00:52 +02:00
|
|
|
struct vnode *n = (struct vnode *) vlist_at(&destinations, i);
|
2017-03-25 21:23:31 +01:00
|
|
|
|
2020-07-04 15:38:17 +02:00
|
|
|
if (n->output_path)
|
2020-07-01 17:02:22 +02:00
|
|
|
throw ConfigError(cfg, "node-config-path", "Every node must only be used by a single path as destination");
|
|
|
|
|
2020-09-10 17:36:08 +02:00
|
|
|
n->output_path = p;
|
|
|
|
|
2020-06-08 02:25:07 +02:00
|
|
|
auto *pd = new struct vpath_destination;
|
2020-07-04 16:22:10 +02:00
|
|
|
if (!pd)
|
|
|
|
throw MemoryAllocationError();
|
2017-07-06 23:48:19 +02:00
|
|
|
|
2020-09-10 17:36:08 +02:00
|
|
|
ret = path_destination_init(pd, n);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2020-08-28 09:37:06 +02:00
|
|
|
|
2020-08-28 09:42:46 +02:00
|
|
|
vlist_push(&n->destinations, pd);
|
2019-01-07 10:28:55 +01:00
|
|
|
vlist_push(&p->destinations, pd);
|
2016-11-20 12:59:37 -05:00
|
|
|
}
|
|
|
|
|
2018-06-21 09:37:01 +02:00
|
|
|
#ifdef WITH_HOOKS
|
2020-08-28 09:42:46 +02:00
|
|
|
if (json_hooks)
|
2019-06-23 16:13:23 +02:00
|
|
|
hook_list_parse(&p->hooks, json_hooks, (int) Hook::Flags::PATH, p, nullptr);
|
2017-12-09 02:23:29 +08:00
|
|
|
#endif /* WITH_HOOKS */
|
2017-08-30 23:53:35 +02:00
|
|
|
|
2020-08-28 09:42:46 +02:00
|
|
|
if (json_mask)
|
2020-09-10 17:36:08 +02:00
|
|
|
path_parse_mask(p, json_mask, nodes);
|
2018-05-23 02:24:55 +02:00
|
|
|
|
2019-04-08 08:59:08 +02:00
|
|
|
ret = vlist_destroy(&destinations, nullptr, false);
|
2018-05-24 09:04:41 +02:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
2016-11-20 12:59:37 -05:00
|
|
|
|
2017-08-03 00:19:27 +02:00
|
|
|
p->cfg = cfg;
|
2019-06-23 16:13:23 +02:00
|
|
|
p->state = State::PARSED;
|
2017-08-03 00:19:27 +02:00
|
|
|
|
2017-03-11 23:50:30 -03:00
|
|
|
return 0;
|
2015-03-18 15:45:06 +01:00
|
|
|
}
|
2015-03-18 15:47:18 +01:00
|
|
|
|
2020-09-10 17:36:08 +02:00
|
|
|
void path_parse_mask(struct vpath *p, json_t *json_mask, struct vlist *nodes)
|
2020-08-28 09:42:46 +02:00
|
|
|
{
|
|
|
|
json_t *json_entry;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if (!json_is_array(json_mask))
|
|
|
|
throw ConfigError(json_mask, "node-config-path-mask", "The 'mask' setting must be a list of node names");
|
|
|
|
|
|
|
|
json_array_foreach(json_mask, i, json_entry) {
|
|
|
|
const char *name;
|
|
|
|
struct vnode *node;
|
|
|
|
|
|
|
|
name = json_string_value(json_entry);
|
|
|
|
if (!name)
|
|
|
|
throw ConfigError(json_mask, "node-config-path-mask", "The 'mask' setting must be a list of node names");
|
|
|
|
|
|
|
|
node = vlist_lookup_name<struct vnode>(nodes, name);
|
|
|
|
if (!node)
|
|
|
|
throw ConfigError(json_mask, "node-config-path-mask", "The 'mask' entry '{}' is not a valid node name", name);
|
|
|
|
|
2020-09-10 17:36:08 +02:00
|
|
|
p->mask_list.push_back(node);
|
2020-08-28 09:42:46 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-10 17:36:08 +02:00
|
|
|
void path_check(struct vpath *p)
|
2016-11-20 12:59:37 -05:00
|
|
|
{
|
2019-06-23 16:13:23 +02:00
|
|
|
assert(p->state != State::DESTROYED);
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2020-08-28 09:40:14 +02:00
|
|
|
if (p->rate < 0)
|
|
|
|
throw RuntimeError("Setting 'rate' of path {} must be a positive number.", path_name(p));
|
2017-10-16 23:07:42 +02:00
|
|
|
|
2020-09-10 17:36:08 +02:00
|
|
|
if (p->poll > 0) {
|
2019-01-30 01:56:35 +01:00
|
|
|
if (p->rate <= 0) {
|
|
|
|
/* Check that all path sources provide a file descriptor for polling */
|
|
|
|
for (size_t i = 0; i < vlist_length(&p->sources); i++) {
|
2020-06-08 02:25:07 +02:00
|
|
|
struct vpath_source *ps = (struct vpath_source *) vlist_at(&p->sources, i);
|
2019-01-30 01:56:35 +01:00
|
|
|
|
2020-08-28 09:40:14 +02:00
|
|
|
if (!node_type(ps->node)->poll_fds)
|
|
|
|
throw RuntimeError("Node {} can not be used in polling mode with path {}", node_name(ps->node), path_name(p));
|
2019-01-30 01:56:35 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* Check that we do not need to multiplex between multiple sources when polling is disabled */
|
2020-08-28 09:40:14 +02:00
|
|
|
if (vlist_length(&p->sources) > 1)
|
|
|
|
throw RuntimeError("Setting 'poll' must be active if the path has more than one source");
|
2019-01-30 01:56:35 +01:00
|
|
|
|
|
|
|
/* Check that we do not use the fixed rate feature when polling is disabled */
|
2020-08-28 09:40:14 +02:00
|
|
|
if (p->rate > 0)
|
|
|
|
throw RuntimeError("Setting 'poll' must be activated when used together with setting 'rate'");
|
2019-01-30 01:56:35 +01:00
|
|
|
}
|
|
|
|
|
2020-09-10 17:36:08 +02:00
|
|
|
if (!IS_POW2(p->queuelen)) {
|
|
|
|
p->queuelen = LOG2_CEIL(p->queuelen);
|
|
|
|
p->logger->warn("Queue length should always be a power of 2. Adjusting to {}", p->queuelen);
|
|
|
|
}
|
|
|
|
|
2019-01-07 10:28:55 +01:00
|
|
|
for (size_t i = 0; i < vlist_length(&p->sources); i++) {
|
2020-06-08 02:25:07 +02:00
|
|
|
struct vpath_source *ps = (struct vpath_source *) vlist_at(&p->sources, i);
|
2017-08-28 14:38:30 +02:00
|
|
|
|
2020-09-10 17:36:08 +02:00
|
|
|
path_source_check(ps);
|
2017-08-28 14:38:30 +02:00
|
|
|
}
|
|
|
|
|
2019-01-07 10:28:55 +01:00
|
|
|
for (size_t i = 0; i < vlist_length(&p->destinations); i++) {
|
2020-09-10 17:36:08 +02:00
|
|
|
struct vpath_destination *ps = (struct vpath_destination *) vlist_at(&p->destinations, i);
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2020-09-10 17:36:08 +02:00
|
|
|
path_destination_check(ps);
|
2017-08-28 14:38:30 +02:00
|
|
|
}
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2019-06-23 16:13:23 +02:00
|
|
|
p->state = State::CHECKED;
|
2016-11-20 12:59:37 -05:00
|
|
|
}
|
|
|
|
|
2020-06-08 02:25:07 +02:00
|
|
|
int path_start(struct vpath *p)
|
2016-11-07 22:17:45 -05:00
|
|
|
{
|
2017-03-11 23:50:30 -03:00
|
|
|
int ret;
|
2019-04-07 15:13:40 +02:00
|
|
|
const char *mode;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2019-06-23 16:13:23 +02:00
|
|
|
assert(p->state == State::PREPARED);
|
2017-03-11 23:50:30 -03:00
|
|
|
|
2017-10-16 23:07:42 +02:00
|
|
|
switch (p->mode) {
|
2019-06-23 16:13:23 +02:00
|
|
|
case PathMode::ANY:
|
2019-04-07 15:13:40 +02:00
|
|
|
mode = "any";
|
|
|
|
break;
|
|
|
|
|
2019-06-23 16:13:23 +02:00
|
|
|
case PathMode::ALL:
|
2019-04-07 15:13:40 +02:00
|
|
|
mode = "all";
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
mode = "unknown";
|
|
|
|
break;
|
2017-10-16 23:07:42 +02:00
|
|
|
}
|
|
|
|
|
2019-04-07 16:16:58 +02:00
|
|
|
p->logger->info("Starting path {}: #signals={}, #hooks={}, #sources={}, "
|
|
|
|
"#destinations={}, mode={}, poll={}, mask={:b}, rate={}, "
|
2020-09-10 17:36:08 +02:00
|
|
|
"enabled={}, reversed={}, queuelen={}, original_sequence_no={}",
|
2017-08-31 11:31:43 +02:00
|
|
|
path_name(p),
|
2019-01-07 10:28:55 +01:00
|
|
|
vlist_length(&p->signals),
|
2019-04-05 02:24:14 +02:00
|
|
|
vlist_length(&p->hooks),
|
|
|
|
vlist_length(&p->sources),
|
|
|
|
vlist_length(&p->destinations),
|
2017-10-16 23:07:42 +02:00
|
|
|
mode,
|
2018-05-23 02:24:55 +02:00
|
|
|
p->poll ? "yes" : "no",
|
2019-04-07 16:16:58 +02:00
|
|
|
p->mask.to_ullong(),
|
2017-10-16 23:07:42 +02:00
|
|
|
p->rate,
|
2019-02-24 11:10:44 +01:00
|
|
|
path_is_enabled(p) ? "yes" : "no",
|
|
|
|
path_is_reversed(p) ? "yes" : "no",
|
2018-08-20 18:31:27 +02:00
|
|
|
p->queuelen,
|
2018-10-14 14:44:56 +02:00
|
|
|
p->original_sequence_no ? "yes" : "no"
|
2017-08-31 11:31:43 +02:00
|
|
|
);
|
2017-03-11 23:50:30 -03:00
|
|
|
|
2017-12-09 02:23:29 +08:00
|
|
|
#ifdef WITH_HOOKS
|
2019-06-11 18:32:58 +02:00
|
|
|
hook_list_start(&p->hooks);
|
2017-12-09 02:23:29 +08:00
|
|
|
#endif /* WITH_HOOKS */
|
2017-09-02 14:20:38 +02:00
|
|
|
|
2017-10-16 23:07:42 +02:00
|
|
|
p->last_sequence = 0;
|
2017-10-18 09:31:31 +02:00
|
|
|
|
2019-04-07 15:44:00 +02:00
|
|
|
p->received.reset();
|
2017-03-13 23:51:38 -03:00
|
|
|
|
2018-08-20 18:31:27 +02:00
|
|
|
/* We initialize the intial sample */
|
|
|
|
p->last_sample = sample_alloc(&p->pool);
|
|
|
|
if (!p->last_sample)
|
|
|
|
return -1;
|
2017-09-23 23:57:19 -06:00
|
|
|
|
2019-04-07 15:13:40 +02:00
|
|
|
p->last_sample->length = 0;
|
2018-08-20 18:31:27 +02:00
|
|
|
p->last_sample->signals = &p->signals;
|
|
|
|
p->last_sample->sequence = 0;
|
2019-06-23 16:13:23 +02:00
|
|
|
p->last_sample->flags = p->last_sample->length > 0 ? (int) SampleFlags::HAS_DATA : 0;
|
2017-09-23 23:57:19 -06:00
|
|
|
|
2018-08-20 18:31:27 +02:00
|
|
|
for (size_t i = 0; i < p->last_sample->length; i++) {
|
2019-01-07 10:28:55 +01:00
|
|
|
struct signal *sig = (struct signal *) vlist_at(p->last_sample->signals, i);
|
2017-09-23 23:57:19 -06:00
|
|
|
|
2018-08-20 18:31:27 +02:00
|
|
|
p->last_sample->data[i] = sig->init;
|
2017-09-23 23:57:19 -06:00
|
|
|
}
|
|
|
|
|
2019-07-30 12:01:44 +02:00
|
|
|
p->state = State::STARTED;
|
|
|
|
|
2018-05-08 08:16:14 +02:00
|
|
|
/* Start one thread per path for sending to destinations
|
|
|
|
*
|
|
|
|
* Special case: If the path only has a single source and this source
|
|
|
|
* does not offer a file descriptor for polling, we will use a special
|
|
|
|
* thread function.
|
|
|
|
*/
|
2019-04-08 08:59:08 +02:00
|
|
|
ret = pthread_create(&p->tid, nullptr, p->poll ? path_run_poll : path_run_single, p);
|
2017-03-11 23:50:30 -03:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2020-09-13 08:36:48 +02:00
|
|
|
if (p->affinity)
|
|
|
|
kernel::rt::setThreadAffinity(p->tid, p->affinity);
|
|
|
|
|
2017-03-11 23:50:30 -03:00
|
|
|
return 0;
|
2016-11-07 22:17:45 -05:00
|
|
|
}
|
|
|
|
|
2020-06-08 02:25:07 +02:00
|
|
|
int path_stop(struct vpath *p)
|
2016-11-07 22:17:45 -05:00
|
|
|
{
|
2017-03-11 23:50:30 -03:00
|
|
|
int ret;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2019-06-23 16:13:23 +02:00
|
|
|
if (p->state != State::STARTED && p->state != State::STOPPING)
|
2017-04-07 17:44:20 +02:00
|
|
|
return 0;
|
2017-03-11 23:50:30 -03:00
|
|
|
|
2019-04-07 16:16:58 +02:00
|
|
|
p->logger->info("Stopping path: {}", path_name(p));
|
2017-03-11 23:50:30 -03:00
|
|
|
|
2019-06-23 16:13:23 +02:00
|
|
|
if (p->state != State::STOPPING)
|
|
|
|
p->state = State::STOPPING;
|
2017-09-03 10:52:46 +02:00
|
|
|
|
2019-03-26 17:13:42 +01:00
|
|
|
/* Cancel the thread in case is currently in a blocking syscall.
|
|
|
|
*
|
|
|
|
* We dont care if the thread has already been terminated.
|
|
|
|
*/
|
2019-02-12 18:17:09 +01:00
|
|
|
ret = pthread_cancel(p->tid);
|
2019-03-26 17:13:42 +01:00
|
|
|
if (ret && ret != ESRCH)
|
|
|
|
return ret;
|
2019-02-12 18:17:09 +01:00
|
|
|
|
2019-04-08 08:59:08 +02:00
|
|
|
ret = pthread_join(p->tid, nullptr);
|
2017-09-03 10:52:46 +02:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
2017-03-11 23:50:30 -03:00
|
|
|
|
2017-12-09 02:23:29 +08:00
|
|
|
#ifdef WITH_HOOKS
|
2019-06-11 18:32:58 +02:00
|
|
|
hook_list_stop(&p->hooks);
|
2017-12-09 02:23:29 +08:00
|
|
|
#endif /* WITH_HOOKS */
|
2017-03-11 23:50:30 -03:00
|
|
|
|
2018-08-20 18:31:27 +02:00
|
|
|
sample_decref(p->last_sample);
|
|
|
|
|
2019-06-23 16:13:23 +02:00
|
|
|
p->state = State::STOPPED;
|
2017-03-11 23:50:30 -03:00
|
|
|
|
|
|
|
return 0;
|
2016-11-07 22:17:45 -05:00
|
|
|
}
|
|
|
|
|
2020-06-08 02:25:07 +02:00
|
|
|
int path_destroy(struct vpath *p)
|
2016-06-08 22:38:21 +02:00
|
|
|
{
|
2019-02-24 09:39:41 +01:00
|
|
|
int ret;
|
|
|
|
|
2019-06-23 16:13:23 +02:00
|
|
|
if (p->state == State::DESTROYED)
|
2017-04-02 04:56:08 +02:00
|
|
|
return 0;
|
|
|
|
|
2017-12-09 02:23:29 +08:00
|
|
|
#ifdef WITH_HOOKS
|
2019-02-24 09:39:41 +01:00
|
|
|
ret = hook_list_destroy(&p->hooks);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2017-12-09 02:23:29 +08:00
|
|
|
#endif
|
2019-02-24 09:39:41 +01:00
|
|
|
ret = signal_list_destroy(&p->signals);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = vlist_destroy(&p->sources, (dtor_cb_t) path_source_destroy, true);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = vlist_destroy(&p->destinations, (dtor_cb_t) path_destination_destroy, true);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2020-09-10 17:36:08 +02:00
|
|
|
ret = vlist_destroy(&p->mappings, (dtor_cb_t) mapping_entry_destroy, true);
|
2019-06-11 18:34:23 +02:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2017-10-16 23:07:42 +02:00
|
|
|
if (p->reader.pfds)
|
2020-01-21 16:26:51 +01:00
|
|
|
delete[] p->reader.pfds;
|
2017-10-16 23:07:42 +02:00
|
|
|
|
2017-03-11 23:50:30 -03:00
|
|
|
if (p->_name)
|
|
|
|
free(p->_name);
|
2017-09-02 14:20:38 +02:00
|
|
|
|
2019-04-07 16:16:58 +02:00
|
|
|
ret = pool_destroy(&p->pool);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2019-04-23 10:55:23 +02:00
|
|
|
using bs = std::bitset<MAX_SAMPLE_LENGTH>;
|
2019-04-23 11:02:29 +02:00
|
|
|
using lg = std::shared_ptr<spdlog::logger>;
|
2019-04-07 16:16:58 +02:00
|
|
|
|
|
|
|
p->received.~bs();
|
|
|
|
p->mask.~bs();
|
2019-04-23 11:02:29 +02:00
|
|
|
p->logger.~lg();
|
2020-03-04 13:06:28 +01:00
|
|
|
p->timeout.~Task();
|
2017-03-11 23:50:30 -03:00
|
|
|
|
2019-06-23 16:13:23 +02:00
|
|
|
p->state = State::DESTROYED;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2017-03-11 23:50:30 -03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-06-08 02:25:07 +02:00
|
|
|
const char * path_name(struct vpath *p)
|
2017-03-11 23:50:30 -03:00
|
|
|
{
|
|
|
|
if (!p->_name) {
|
2017-08-28 14:38:30 +02:00
|
|
|
strcatf(&p->_name, "[");
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2019-01-07 10:28:55 +01:00
|
|
|
for (size_t i = 0; i < vlist_length(&p->sources); i++) {
|
2020-06-08 02:25:07 +02:00
|
|
|
struct vpath_source *ps = (struct vpath_source *) vlist_at(&p->sources, i);
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2017-08-28 14:38:30 +02:00
|
|
|
strcatf(&p->_name, " %s", node_name_short(ps->node));
|
|
|
|
}
|
2017-09-02 14:20:38 +02:00
|
|
|
|
2017-08-28 14:38:30 +02:00
|
|
|
strcatf(&p->_name, " ] " CLR_MAG("=>") " [");
|
2017-03-25 21:23:31 +01:00
|
|
|
|
2019-01-07 10:28:55 +01:00
|
|
|
for (size_t i = 0; i < vlist_length(&p->destinations); i++) {
|
2020-06-08 02:25:07 +02:00
|
|
|
struct vpath_destination *pd = (struct vpath_destination *) vlist_at(&p->destinations, i);
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2017-08-28 14:38:30 +02:00
|
|
|
strcatf(&p->_name, " %s", node_name_short(pd->node));
|
2017-03-11 23:50:30 -03:00
|
|
|
}
|
2017-08-28 14:38:30 +02:00
|
|
|
|
|
|
|
strcatf(&p->_name, " ]");
|
2017-03-11 23:50:30 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
return p->_name;
|
2015-03-18 15:47:18 +01:00
|
|
|
}
|
2016-01-14 22:52:08 +01:00
|
|
|
|
2020-06-08 02:25:07 +02:00
|
|
|
bool path_is_simple(const struct vpath *p)
|
2016-11-07 22:17:45 -05:00
|
|
|
{
|
2019-02-12 17:54:35 +01:00
|
|
|
int ret;
|
2019-04-08 08:59:08 +02:00
|
|
|
const char *in = nullptr, *out = nullptr;
|
2017-12-09 02:23:29 +08:00
|
|
|
|
2019-03-23 20:49:19 +01:00
|
|
|
json_error_t err;
|
|
|
|
ret = json_unpack_ex(p->cfg, &err, 0, "{ s: s, s: s }", "in", &in, "out", &out);
|
2019-02-12 17:54:35 +01:00
|
|
|
if (ret)
|
2019-02-24 11:10:44 +01:00
|
|
|
return false;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2019-02-12 17:54:35 +01:00
|
|
|
ret = node_is_valid_name(in);
|
2019-02-24 11:10:44 +01:00
|
|
|
if (!ret)
|
|
|
|
return false;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2019-02-12 17:54:35 +01:00
|
|
|
ret = node_is_valid_name(out);
|
2019-02-24 11:10:44 +01:00
|
|
|
if (!ret)
|
|
|
|
return false;
|
2019-02-11 16:39:30 +01:00
|
|
|
|
2019-02-24 11:10:44 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-06-08 02:25:07 +02:00
|
|
|
bool path_is_enabled(const struct vpath *p)
|
2019-02-24 11:10:44 +01:00
|
|
|
{
|
|
|
|
return p->enabled;
|
|
|
|
}
|
|
|
|
|
2020-06-08 02:25:07 +02:00
|
|
|
bool path_is_reversed(const struct vpath *p)
|
2019-02-24 11:10:44 +01:00
|
|
|
{
|
|
|
|
return p->reverse;
|
2017-04-05 12:40:21 +02:00
|
|
|
}
|
2019-03-08 15:21:01 +01:00
|
|
|
|
2020-10-21 20:56:51 +02:00
|
|
|
struct vlist * path_signals(struct vpath *p)
|
2019-03-08 15:21:01 +01:00
|
|
|
{
|
|
|
|
return &p->signals;
|
|
|
|
}
|
2020-08-25 20:24:18 +02:00
|
|
|
|
2020-10-21 20:56:51 +02:00
|
|
|
struct vlist * path_output_signals(struct vpath *p)
|
|
|
|
{
|
|
|
|
#ifdef WITH_HOOKS
|
|
|
|
Hook *last_hook = (Hook *) vlist_last(&p->hooks);
|
|
|
|
|
|
|
|
return last_hook->getSignals();
|
|
|
|
#else
|
|
|
|
return &p->signals;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2020-08-25 20:24:18 +02:00
|
|
|
json_t * path_to_json(struct vpath *p)
|
|
|
|
{
|
|
|
|
char uuid[37];
|
|
|
|
uuid_unparse(p->uuid, uuid);
|
|
|
|
|
|
|
|
json_t *json_signals = signal_list_to_json(&p->signals);
|
|
|
|
json_t *json_hooks = hook_list_to_json(&p->hooks);
|
|
|
|
json_t *json_sources = json_array();
|
|
|
|
json_t *json_destinations = json_array();
|
|
|
|
|
|
|
|
for (size_t i = 0; i < vlist_length(&p->sources); i++) {
|
|
|
|
struct vpath_source *pd = (struct vpath_source *) vlist_at_safe(&p->sources, i);
|
|
|
|
|
|
|
|
json_array_append_new(json_sources, json_string(node_name_short(pd->node)));
|
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t i = 0; i < vlist_length(&p->destinations); i++) {
|
|
|
|
struct vpath_destination *pd = (struct vpath_destination *) vlist_at_safe(&p->destinations, i);
|
|
|
|
|
|
|
|
json_array_append_new(json_destinations, json_string(node_name_short(pd->node)));
|
|
|
|
}
|
|
|
|
|
|
|
|
json_t *json_path = json_pack("{ s: s, s: s, s: s, s: b, s: b s: b, s: b, s: b, s: b s: i, s: o, s: o, s: o, s: o }",
|
|
|
|
"uuid", uuid,
|
|
|
|
"state", state_print(p->state),
|
|
|
|
"mode", p->mode == PathMode::ANY ? "any" : "all",
|
|
|
|
"enabled", p->enabled,
|
|
|
|
"builtin", p->builtin,
|
|
|
|
"reverse", p->reverse,
|
|
|
|
"original_sequence_no", p->original_sequence_no,
|
|
|
|
"last_sequence", p->last_sequence,
|
|
|
|
"poll", p->poll,
|
|
|
|
"queuelen", p->queuelen,
|
|
|
|
"signals", json_signals,
|
|
|
|
"hooks", json_hooks,
|
|
|
|
"in", json_sources,
|
|
|
|
"out", json_destinations
|
|
|
|
);
|
|
|
|
|
|
|
|
return json_path;
|
2020-08-28 09:42:46 +02:00
|
|
|
}
|