mirror of
https://github.com/ntop/n2n.git
synced 2024-09-19 16:41:11 +02:00
Add pubsub system to edge, with one example TEST channel
This commit is contained in:
parent
509a8bcd14
commit
a40ed15d93
|
@ -1,6 +1,6 @@
|
||||||
# Management API
|
# Management API
|
||||||
|
|
||||||
This document is focused on the machine readable API interfaces.
|
This document is focused on the machine readable API interfaces.
|
||||||
|
|
||||||
Both the edge and the supernode provide a management interface UDP port.
|
Both the edge and the supernode provide a management interface UDP port.
|
||||||
These interfaces have some documentation on their non machine readable
|
These interfaces have some documentation on their non machine readable
|
||||||
|
@ -48,7 +48,7 @@ but this is intended for debugging.
|
||||||
|
|
||||||
The request is a single UDP packet containing one line of text with at least
|
The request is a single UDP packet containing one line of text with at least
|
||||||
three space separated fields. Any text after the third field is available for
|
three space separated fields. Any text after the third field is available for
|
||||||
the API method to use for additional parameters
|
the API method to use for additional parameters
|
||||||
|
|
||||||
Fields:
|
Fields:
|
||||||
- Message Type
|
- Message Type
|
||||||
|
@ -58,15 +58,23 @@ Fields:
|
||||||
|
|
||||||
The maximum length of the entire line of text is 80 octets.
|
The maximum length of the entire line of text is 80 octets.
|
||||||
|
|
||||||
|
All request packets should generate a reply. However, this reply may simply
|
||||||
|
be an error.
|
||||||
|
|
||||||
### Message Type
|
### Message Type
|
||||||
|
|
||||||
This is a single octet that is either "r" for a read (or query) method
|
This is a single octet specifying the type:
|
||||||
call or "w" for a write (or change) method call.
|
|
||||||
|
- "r" for a read-only method (or one that does not need change permissions)
|
||||||
|
- "w" for a write method (or one that makes changes)
|
||||||
|
- "s" for a subscribe method to request this socket receive some events
|
||||||
|
|
||||||
To simplify the interface, the reply from both read and write calls to the
|
To simplify the interface, the reply from both read and write calls to the
|
||||||
same method is expected to contain the same data. In the case of a write
|
same method is expected to contain the same data. In the case of a write
|
||||||
call, the reply will contain the new state after making the requested change.
|
call, the reply will contain the new state after making the requested change.
|
||||||
|
|
||||||
|
The subscribe and events message flow works with a different set of messages.
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
The options field is a colon separated set of options for this request. Only
|
The options field is a colon separated set of options for this request. Only
|
||||||
|
@ -90,8 +98,9 @@ Where possible, the error replies will also include this tag, however some
|
||||||
errors occur before the tag is parsed.
|
errors occur before the tag is parsed.
|
||||||
|
|
||||||
The tag is not interpreted by the daemon, it is simply echoed back in all
|
The tag is not interpreted by the daemon, it is simply echoed back in all
|
||||||
the replies. It is expected to be a short string that the client chooses
|
the replies. It is expected to be a short string that the client knows
|
||||||
to be unique amongst all recent or still outstanding requests.
|
will be unique amongst all recent, still outstanding or subscription requests
|
||||||
|
on a given socket.
|
||||||
|
|
||||||
One possible client implementation is a number between 0 and 999, incremented
|
One possible client implementation is a number between 0 and 999, incremented
|
||||||
for each request and wrapping around to zero when it is greater than 999.
|
for each request and wrapping around to zero when it is greater than 999.
|
||||||
|
@ -128,6 +137,9 @@ e.g:
|
||||||
Each UDP packet in the reply is a complete and valid JSON dictionary
|
Each UDP packet in the reply is a complete and valid JSON dictionary
|
||||||
containing a fragment of information related to the entire reply.
|
containing a fragment of information related to the entire reply.
|
||||||
|
|
||||||
|
Reply packets are generated both in response to requests and whenever
|
||||||
|
an event is published to a subscribed channel.
|
||||||
|
|
||||||
### Common metadata
|
### Common metadata
|
||||||
|
|
||||||
There are two keys in each dictionary containing metadata. First
|
There are two keys in each dictionary containing metadata. First
|
||||||
|
@ -177,6 +189,47 @@ packets will be required.
|
||||||
e.g:
|
e.g:
|
||||||
`{"_tag":"108","_type":"row","mode":"p2p","ip4addr":"10.135.98.84","macaddr":"86:56:21:E4:AA:39","sockaddr":"192.168.7.191:41701","desc":"client4","lastseen":1584682200}`
|
`{"_tag":"108","_type":"row","mode":"p2p","ip4addr":"10.135.98.84","macaddr":"86:56:21:E4:AA:39","sockaddr":"192.168.7.191:41701","desc":"client4","lastseen":1584682200}`
|
||||||
|
|
||||||
|
### `_type: subscribed`
|
||||||
|
|
||||||
|
Signals that the subscription request has been successfully completed.
|
||||||
|
Any future events on the requested channel will be asynchronously sent
|
||||||
|
as `event` packets using the same tag as the subscribe request.
|
||||||
|
|
||||||
|
### `_type: unsubscribed`
|
||||||
|
|
||||||
|
Only one management client can be subscribed to any given event topic, so if
|
||||||
|
another subscribe request arrives, the older client will be sent this message
|
||||||
|
to let them know that they have been replaced.
|
||||||
|
|
||||||
|
(In the future, this may also be sent as a reply to a explicit unsubscribe
|
||||||
|
request)
|
||||||
|
|
||||||
|
### `_type: replacing`
|
||||||
|
|
||||||
|
If a new subscription request will replace an existing one, this message is
|
||||||
|
sent to the new client to inform them that they have replaced an older
|
||||||
|
connection.
|
||||||
|
|
||||||
|
### `_type: event`
|
||||||
|
|
||||||
|
Asynchronous events will arrive with this message type, using the same tag as
|
||||||
|
the original subscribe request. Just like with the `row` packets, the non
|
||||||
|
metadata contents are entirely defined by the topic and the specific n2n
|
||||||
|
version.
|
||||||
|
|
||||||
|
## Subscribe API
|
||||||
|
|
||||||
|
A client can subscribe to events using a request with the type of "s".
|
||||||
|
Once a subscribe has been successfully completed, any events published
|
||||||
|
on that channel will be forwarded to the client.
|
||||||
|
|
||||||
|
Only one management client can be subscribed to any given event topic,
|
||||||
|
with newer subscriptions replacing older ones.
|
||||||
|
|
||||||
|
The special channel "debug" will receive copies of all events published.
|
||||||
|
Note that this is for debugging of events and the packets may not have
|
||||||
|
the same tag as the debug subscription.
|
||||||
|
|
||||||
## Authentication
|
## Authentication
|
||||||
|
|
||||||
Some API requests will make global changes to the running daemon and may
|
Some API requests will make global changes to the running daemon and may
|
||||||
|
|
|
@ -286,4 +286,6 @@ const char* compression_str (uint8_t cmpr);
|
||||||
const char* transop_str (enum n2n_transform tr);
|
const char* transop_str (enum n2n_transform tr);
|
||||||
|
|
||||||
void readFromMgmtSocket (n2n_edge_t *eee);
|
void readFromMgmtSocket (n2n_edge_t *eee);
|
||||||
|
|
||||||
|
void mgmt_event_post (enum n2n_event_topic topic, void *data);
|
||||||
#endif /* _N2N_H_ */
|
#endif /* _N2N_H_ */
|
||||||
|
|
|
@ -121,6 +121,11 @@ enum sn_purge {SN_PURGEABLE = 0, SN_UNPURGEABLE = 1};
|
||||||
#define N2N_EDGE_MGMT_PORT 5644
|
#define N2N_EDGE_MGMT_PORT 5644
|
||||||
#define N2N_SN_MGMT_PORT 5645
|
#define N2N_SN_MGMT_PORT 5645
|
||||||
|
|
||||||
|
enum n2n_event_topic {
|
||||||
|
N2N_EVENT_DEBUG = 0,
|
||||||
|
N2N_EVENT_TEST = 1,
|
||||||
|
};
|
||||||
|
|
||||||
#define N2N_MGMT_PASSWORD "n2n" /* default password for management port access (so far, json only) */
|
#define N2N_MGMT_PASSWORD "n2n" /* default password for management port access (so far, json only) */
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -41,23 +41,37 @@ class JsonUDP():
|
||||||
def _rx(self, tagstr):
|
def _rx(self, tagstr):
|
||||||
"""Wait for rx packets"""
|
"""Wait for rx packets"""
|
||||||
|
|
||||||
# TODO: there are no timeouts with any of the recv calls
|
seen_begin = False
|
||||||
data, _ = self.sock.recvfrom(1024)
|
while not seen_begin:
|
||||||
data = json.loads(data.decode('utf8'))
|
# TODO: there are no timeouts with any of the recv calls
|
||||||
|
data, _ = self.sock.recvfrom(1024)
|
||||||
|
data = json.loads(data.decode('utf8'))
|
||||||
|
|
||||||
# TODO: We assume the first packet we get will be tagged for us
|
# TODO: We assume the first packet we get will be tagged for us
|
||||||
# and be either an "error" or a "begin"
|
assert(data['_tag'] == tagstr)
|
||||||
assert(data['_tag'] == tagstr)
|
|
||||||
|
|
||||||
if data['_type'] == 'error':
|
if data['_type'] == 'error':
|
||||||
raise ValueError('Error: {}'.format(data['error']))
|
raise ValueError('Error: {}'.format(data['error']))
|
||||||
|
|
||||||
assert(data['_type'] == 'begin')
|
if data['_type'] == 'replacing':
|
||||||
|
# a signal that we have evicted an earlier subscribe
|
||||||
|
continue
|
||||||
|
|
||||||
# Ideally, we would confirm that this is our "begin", but that
|
if data['_type'] == 'subscribe':
|
||||||
# would need the cmd passed into this method, and that would
|
return True
|
||||||
# probably require parsing the cmdline passed to us :-(
|
|
||||||
# assert(data['cmd'] == cmd)
|
if data['_type'] == 'begin':
|
||||||
|
seen_begin = True
|
||||||
|
|
||||||
|
# Ideally, we would confirm that this is our "begin", but that
|
||||||
|
# would need the cmd passed into this method, and that would
|
||||||
|
# probably require parsing the cmdline passed to us :-(
|
||||||
|
# assert(data['cmd'] == cmd)
|
||||||
|
|
||||||
|
continue
|
||||||
|
|
||||||
|
raise ValueError('Unknown data type {} from '
|
||||||
|
'edge'.format(data['_type']))
|
||||||
|
|
||||||
result = list()
|
result = list()
|
||||||
error = None
|
error = None
|
||||||
|
@ -105,6 +119,21 @@ class JsonUDP():
|
||||||
def write(self, cmdline):
|
def write(self, cmdline):
|
||||||
return self._call('w', cmdline)
|
return self._call('w', cmdline)
|
||||||
|
|
||||||
|
def sub(self, cmdline):
|
||||||
|
return self._call('s', cmdline)
|
||||||
|
|
||||||
|
def readevent(self):
|
||||||
|
self.sock.settimeout(3600)
|
||||||
|
|
||||||
|
data, _ = self.sock.recvfrom(1024)
|
||||||
|
data = json.loads(data.decode('utf8'))
|
||||||
|
# assert(data['_tag'] == tagstr)
|
||||||
|
assert(data['_type'] == 'event')
|
||||||
|
|
||||||
|
del data['_tag']
|
||||||
|
del data['_type']
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
def str_table(rows, columns, orderby):
|
def str_table(rows, columns, orderby):
|
||||||
"""Given an array of dicts, do a simple table print"""
|
"""Given an array of dicts, do a simple table print"""
|
||||||
|
@ -203,8 +232,17 @@ def subcmd_default(rpc, args):
|
||||||
cmdline = ' '.join([args.cmd] + args.args)
|
cmdline = ' '.join([args.cmd] + args.args)
|
||||||
if args.write:
|
if args.write:
|
||||||
rows = rpc.write(cmdline)
|
rows = rpc.write(cmdline)
|
||||||
else:
|
elif args.read:
|
||||||
rows = rpc.read(cmdline)
|
rows = rpc.read(cmdline)
|
||||||
|
elif args.sub:
|
||||||
|
if not rpc.sub(cmdline):
|
||||||
|
raise ValueError('Could not subscribe')
|
||||||
|
while True:
|
||||||
|
event = rpc.readevent()
|
||||||
|
# FIXME: violates layering..
|
||||||
|
print(json.dumps(event, sort_keys=True, indent=4))
|
||||||
|
else:
|
||||||
|
raise ValueError('Unknown request type')
|
||||||
return json.dumps(rows, sort_keys=True, indent=4)
|
return json.dumps(rows, sort_keys=True, indent=4)
|
||||||
|
|
||||||
|
|
||||||
|
@ -228,6 +266,8 @@ def main():
|
||||||
group.add_argument('--write', action='store_true',
|
group.add_argument('--write', action='store_true',
|
||||||
help='Make a write request (only to non pretty'
|
help='Make a write request (only to non pretty'
|
||||||
'printed cmds)')
|
'printed cmds)')
|
||||||
|
group.add_argument('--sub', action='store_true',
|
||||||
|
help='Make a subscribe request')
|
||||||
|
|
||||||
ap.add_argument('cmd', action='store',
|
ap.add_argument('cmd', action='store',
|
||||||
help='Command to run (try "help" for list)')
|
help='Command to run (try "help" for list)')
|
||||||
|
@ -236,6 +276,9 @@ def main():
|
||||||
|
|
||||||
args = ap.parse_args()
|
args = ap.parse_args()
|
||||||
|
|
||||||
|
if not args.read and not args.write and not args.sub:
|
||||||
|
args.read = True
|
||||||
|
|
||||||
if args.raw or (args.cmd not in subcmds):
|
if args.raw or (args.cmd not in subcmds):
|
||||||
func = subcmd_default
|
func = subcmd_default
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -61,7 +61,7 @@ typedef struct mgmt_handler {
|
||||||
* Event topic names are defined in this structure
|
* Event topic names are defined in this structure
|
||||||
*/
|
*/
|
||||||
typedef struct mgmt_events {
|
typedef struct mgmt_events {
|
||||||
int topic; // topic number define
|
enum n2n_event_topic topic;
|
||||||
char *cmd;
|
char *cmd;
|
||||||
char *help;
|
char *help;
|
||||||
} mgmt_events_t;
|
} mgmt_events_t;
|
||||||
|
@ -80,22 +80,26 @@ typedef struct mgmt_events {
|
||||||
} \
|
} \
|
||||||
} while(0)
|
} while(0)
|
||||||
|
|
||||||
static void send_reply (mgmt_req_t *req, strbuf_t *buf, size_t msg_len) {
|
ssize_t send_reply (mgmt_req_t *req, strbuf_t *buf, size_t msg_len) {
|
||||||
// TODO: error handling
|
// TODO: better error handling (counters?)
|
||||||
sendto(req->eee->udp_mgmt_sock, buf->str, msg_len, 0,
|
return sendto(req->eee->udp_mgmt_sock, buf->str, msg_len, 0,
|
||||||
(struct sockaddr *) &req->sender_sock, sizeof(struct sockaddr_in));
|
(struct sockaddr *) &req->sender_sock, sizeof(struct sockaddr_in));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t gen_json_1str (strbuf_t *buf, char *tag, char *_type, char *key, char *val) {
|
||||||
|
return snprintf(buf->str, buf->size,
|
||||||
|
"{"
|
||||||
|
"\"_tag\":\"%s\","
|
||||||
|
"\"_type\":\"%s\","
|
||||||
|
"\"%s\":\"%s\"}\n",
|
||||||
|
tag,
|
||||||
|
_type,
|
||||||
|
key,
|
||||||
|
val);
|
||||||
|
}
|
||||||
|
|
||||||
static void send_json_1str (mgmt_req_t *req, strbuf_t *buf, char *_type, char *key, char *val) {
|
static void send_json_1str (mgmt_req_t *req, strbuf_t *buf, char *_type, char *key, char *val) {
|
||||||
size_t msg_len = snprintf(buf->str, buf->size,
|
size_t msg_len = gen_json_1str(buf, req->tag, _type, key, val);
|
||||||
"{"
|
|
||||||
"\"_tag\":\"%s\","
|
|
||||||
"\"_type\":\"%s\","
|
|
||||||
"\"%s\":\"%s\"}\n",
|
|
||||||
req->tag,
|
|
||||||
_type,
|
|
||||||
key,
|
|
||||||
val);
|
|
||||||
send_reply(req, buf, msg_len);
|
send_reply(req, buf, msg_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,8 +116,14 @@ static void send_json_1uint (mgmt_req_t *req, strbuf_t *buf, char *_type, char *
|
||||||
send_reply(req, buf, msg_len);
|
send_reply(req, buf, msg_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void event_debug (mgmt_req_t *req, strbuf_t *buf) {
|
size_t event_debug (strbuf_t *buf, char *tag, void *data) {
|
||||||
send_json_1str(req, buf, "event", "test", "test");
|
traceEvent(TRACE_DEBUG, "Unexpected call to event_debug");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t event_test (strbuf_t *buf, char *tag, void *data) {
|
||||||
|
size_t msg_len = gen_json_1str(buf, tag, "event", "test", (char *)data);
|
||||||
|
return msg_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mgmt_error (mgmt_req_t *req, strbuf_t *buf, char *msg) {
|
static void mgmt_error (mgmt_req_t *req, strbuf_t *buf, char *msg) {
|
||||||
|
@ -315,6 +325,12 @@ static void mgmt_packetstats (mgmt_req_t *req, strbuf_t *buf, char *argv0, char
|
||||||
send_reply(req, buf, msg_len);
|
send_reply(req, buf, msg_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void mgmt_post_test (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv) {
|
||||||
|
|
||||||
|
send_json_1str(req, buf, "row", "sending", "test");
|
||||||
|
mgmt_event_post (N2N_EVENT_TEST, argv);
|
||||||
|
}
|
||||||
|
|
||||||
static void mgmt_unimplemented (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv) {
|
static void mgmt_unimplemented (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv) {
|
||||||
|
|
||||||
mgmt_error(req, buf, "unimplemented");
|
mgmt_error(req, buf, "unimplemented");
|
||||||
|
@ -334,25 +350,60 @@ static const mgmt_handler_t mgmt_handlers[] = {
|
||||||
{ .cmd = "supernodes", .help = "List current supernodes", .func = mgmt_supernodes},
|
{ .cmd = "supernodes", .help = "List current supernodes", .func = mgmt_supernodes},
|
||||||
{ .cmd = "timestamps", .help = "Event timestamps", .func = mgmt_timestamps},
|
{ .cmd = "timestamps", .help = "Event timestamps", .func = mgmt_timestamps},
|
||||||
{ .cmd = "packetstats", .help = "traffic counters", .func = mgmt_packetstats},
|
{ .cmd = "packetstats", .help = "traffic counters", .func = mgmt_packetstats},
|
||||||
|
{ .cmd = "post.test", .help = "send a test event", .func = mgmt_post_test},
|
||||||
{ .cmd = "help", .flags = FLAG_WROK, .help = "Show JSON commands", .func = mgmt_help},
|
{ .cmd = "help", .flags = FLAG_WROK, .help = "Show JSON commands", .func = mgmt_help},
|
||||||
{ .cmd = "help.events", .help = "Show available Subscribe topics", .func = mgmt_help_events},
|
{ .cmd = "help.events", .help = "Show available Subscribe topics", .func = mgmt_help_events},
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Current subscriber for each event topic */
|
/* Current subscriber for each event topic */
|
||||||
static mgmt_req_t mgmt_event_subscribers[] = {
|
static mgmt_req_t mgmt_event_subscribers[] = {
|
||||||
[0] = { .eee = NULL, .type = N2N_MGMT_UNKNOWN, .tag = "\0" },
|
[N2N_EVENT_DEBUG] = { .eee = NULL, .type = N2N_MGMT_UNKNOWN, .tag = "\0" },
|
||||||
|
[N2N_EVENT_TEST] = { .eee = NULL, .type = N2N_MGMT_UNKNOWN, .tag = "\0" },
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Map topic number to function */
|
/* Map topic number to function */
|
||||||
static const void (*mgmt_events[])(mgmt_req_t *req, strbuf_t *buf) = {
|
static const size_t (*mgmt_events[])(strbuf_t *buf, char *tag, void *data) = {
|
||||||
[0] = event_debug,
|
[N2N_EVENT_DEBUG] = event_debug,
|
||||||
|
[N2N_EVENT_TEST] = event_test,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Allow help and subscriptions to use topic name */
|
/* Allow help and subscriptions to use topic name */
|
||||||
static const mgmt_events_t mgmt_event_names[] = {
|
static const mgmt_events_t mgmt_event_names[] = {
|
||||||
{ .cmd = "debug", .topic = 0, .help = "All events - for event debugging"},
|
{ .cmd = "debug", .topic = N2N_EVENT_DEBUG, .help = "All events - for event debugging"},
|
||||||
|
{ .cmd = "test", .topic = N2N_EVENT_TEST, .help = "Used only by post.test"},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void mgmt_event_post (enum n2n_event_topic topic, void *data) {
|
||||||
|
mgmt_req_t *debug = &mgmt_event_subscribers[N2N_EVENT_DEBUG];
|
||||||
|
mgmt_req_t *sub = &mgmt_event_subscribers[topic];
|
||||||
|
|
||||||
|
if( sub->type != N2N_MGMT_SUB && debug->type != N2N_MGMT_SUB) {
|
||||||
|
// If neither of this topic or the debug topic have a subscriber
|
||||||
|
// then we dont need to do any work
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char buf_space[100];
|
||||||
|
strbuf_t *buf;
|
||||||
|
STRBUF_INIT(buf, buf_space);
|
||||||
|
|
||||||
|
char *tag;
|
||||||
|
if (sub->type == N2N_MGMT_SUB) {
|
||||||
|
tag = sub->tag;
|
||||||
|
} else {
|
||||||
|
tag = debug->tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t msg_len = mgmt_events[topic](buf, tag, data);
|
||||||
|
|
||||||
|
if (sub->type == N2N_MGMT_SUB) {
|
||||||
|
send_reply(sub, buf, msg_len);
|
||||||
|
}
|
||||||
|
if (debug->type == N2N_MGMT_SUB) {
|
||||||
|
send_reply(debug, buf, msg_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void mgmt_help_events (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv) {
|
static void mgmt_help_events (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv) {
|
||||||
size_t msg_len;
|
size_t msg_len;
|
||||||
|
|
||||||
|
@ -364,11 +415,11 @@ static void mgmt_help_events (mgmt_req_t *req, strbuf_t *buf, char *argv0, char
|
||||||
char host[40];
|
char host[40];
|
||||||
char serv[6];
|
char serv[6];
|
||||||
|
|
||||||
if(getnameinfo(
|
if((sub->type != N2N_MGMT_SUB) ||
|
||||||
(struct sockaddr *)&sub->sender_sock, sizeof(sub->sender_sock),
|
getnameinfo((struct sockaddr *)&sub->sender_sock, sizeof(sub->sender_sock),
|
||||||
host, sizeof(host),
|
host, sizeof(host),
|
||||||
serv, sizeof(serv),
|
serv, sizeof(serv),
|
||||||
NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
|
NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
|
||||||
host[0] = '?';
|
host[0] = '?';
|
||||||
host[1] = 0;
|
host[1] = 0;
|
||||||
serv[0] = '?';
|
serv[0] = '?';
|
||||||
|
@ -533,7 +584,23 @@ static void handleMgmtJson (mgmt_req_t *req, char *udp_buf, const int recvlen) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(req->type == N2N_MGMT_SUB) {
|
if(req->type == N2N_MGMT_SUB) {
|
||||||
mgmt_error(req, buf, "unimplemented");
|
int handler;
|
||||||
|
lookup_handler(handler, mgmt_event_names, argv0);
|
||||||
|
if(handler == -1) {
|
||||||
|
mgmt_error(req, buf, "unknowntopic");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int topic = mgmt_event_names[handler].topic;
|
||||||
|
if(mgmt_event_subscribers[topic].type == N2N_MGMT_SUB) {
|
||||||
|
send_json_1str(&mgmt_event_subscribers[topic], buf,
|
||||||
|
"unsubscribed", "topic", argv0);
|
||||||
|
send_json_1str(req, buf, "replacing", "topic", argv0);
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(&mgmt_event_subscribers[topic], req, sizeof(*req));
|
||||||
|
|
||||||
|
send_json_1str(req, buf, "subscribe", "topic", argv0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user