@@ -99,6 +99,7 @@ enum clientmode {
CLIENT_CHANGE_INTERFACE,
CLIENT_CHANGE_BAT_IFACE,
CLIENT_SERVER_STATUS,
+ CLIENT_EVENT_MONITOR,
};
struct interface {
@@ -118,8 +119,16 @@ struct interface {
struct list_head list;
};
+struct event_listener {
+ int fd;
+ struct epoll_handle epoll;
+
+ struct list_head list;
+};
+
struct globals {
struct list_head interfaces;
+ struct list_head event_listeners;
char *net_iface;
struct server *best_server; /* NULL if we are a server ourselves */
@@ -171,6 +180,7 @@ int alfred_client_modeswitch(struct globals *globals);
int alfred_client_change_interface(struct globals *globals);
int alfred_client_change_bat_iface(struct globals *globals);
int alfred_client_server_status(struct globals *globals);
+int alfred_client_event_monitor(struct globals *globals);
/* recv.c */
int recv_alfred_packet(struct globals *globals, struct interface *interface,
int recv_sock);
@@ -199,6 +209,9 @@ int unix_sock_open_client(struct globals *globals);
int unix_sock_close(struct globals *globals);
int unix_sock_req_data_finish(struct globals *globals,
struct transaction_head *head);
+void unix_sock_events_close_all(struct globals *globals);
+void unix_sock_event_notify(struct globals *globals, uint8_t type,\
+ const uint8_t source[ETH_ALEN]);
/* vis.c */
int vis_update_data(struct globals *globals);
/* netsock.c */
@@ -452,3 +452,61 @@ err:
unix_sock_close(globals);
return 0;
}
+
+int alfred_client_event_monitor(struct globals *globals)
+{
+ struct alfred_event_register_v0 event_register;
+ struct alfred_event_notify_v0 event_notify;
+ int ret, len;
+
+ if (unix_sock_open_client(globals))
+ return -1;
+
+ len = sizeof(event_register);
+
+ event_register.header.type = ALFRED_EVENT_REGISTER;
+ event_register.header.version = ALFRED_VERSION;
+ event_register.header.length = 0;
+
+ ret = write(globals->unix_sock, &event_register, len);
+ if (ret != len) {
+ fprintf(stderr, "%s: only wrote %d of %d bytes: %s\n",
+ __func__, ret, len, strerror(errno));
+ goto err;
+ }
+
+ while (true) {
+ len = read(globals->unix_sock, &event_notify, sizeof(event_notify));
+ if (len == 0) {
+ fprintf(stdout, "Server closed the connection\n");
+ goto err;
+ }
+
+ if (len < 0) {
+ perror("read from unix socket failed");
+ goto err;
+ }
+
+ if (len != sizeof(event_notify)) {
+ fprintf(stderr, "notify read bytes: %d (expected: %zu)\n",
+ len, sizeof(event_notify));
+ goto err;
+ }
+
+ if (event_notify.header.version != ALFRED_VERSION)
+ continue;
+
+ if (event_notify.header.type != ALFRED_EVENT_NOTIFY)
+ continue;
+
+ fprintf(stdout, "Event: type = %hhu, source = %02x:%02x:%02x:%02x:%02x:%02x\n",
+ event_notify.type,
+ event_notify.source[0], event_notify.source[1],
+ event_notify.source[2], event_notify.source[3],
+ event_notify.source[4], event_notify.source[5]);
+ }
+
+err:
+ unix_sock_close(globals);
+ return 0;
+}
@@ -39,6 +39,7 @@ static void alfred_usage(void)
printf(" -I, --change-interface [interface] change to the specified interface(s)\n");
printf(" -B, --change-bat-iface [interface] change to the specified batman-adv interface\n");
printf(" -S, --server-status request server status info such as mode & interfaces\n");
+ printf(" -E, --event-monitor monitor alfred data record update events\n");
printf("\n");
printf("server mode options:\n");
printf(" -i, --interface specify the interface (or comma separated list of interfaces) to listen on\n");
@@ -164,6 +165,7 @@ static struct globals *alfred_init(int argc, char *argv[])
{"change-interface", required_argument, NULL, 'I'},
{"change-bat-iface", required_argument, NULL, 'B'},
{"server-status", no_argument, NULL, 'S'},
+ {"event-monitor", no_argument, NULL, 'E'},
{"unix-path", required_argument, NULL, 'u'},
{"update-command", required_argument, NULL, 'c'},
{"version", no_argument, NULL, 'v'},
@@ -181,6 +183,7 @@ static struct globals *alfred_init(int argc, char *argv[])
memset(globals, 0, sizeof(*globals));
INIT_LIST_HEAD(&globals->interfaces);
+ INIT_LIST_HEAD(&globals->event_listeners);
globals->net_iface = NULL;
globals->opmode = OPMODE_SECONDARY;
globals->clientmode = CLIENT_NONE;
@@ -198,7 +201,7 @@ static struct globals *alfred_init(int argc, char *argv[])
time_random_seed();
- while ((opt = getopt_long(argc, argv, "ms:r:hi:b:vV:M:I:B:Su:dc:p:4:f", long_options,
+ while ((opt = getopt_long(argc, argv, "ms:r:hi:b:vV:M:I:B:SEu:dc:p:4:f", long_options,
&opt_ind)) != -1) {
switch (opt) {
case 'r':
@@ -263,6 +266,9 @@ static struct globals *alfred_init(int argc, char *argv[])
case 'S':
globals->clientmode = CLIENT_SERVER_STATUS;
break;
+ case 'E':
+ globals->clientmode = CLIENT_EVENT_MONITOR;
+ break;
case 'u':
globals->unix_path = optarg;
break;
@@ -328,6 +334,8 @@ int main(int argc, char *argv[])
return alfred_client_change_bat_iface(globals);
case CLIENT_SERVER_STATUS:
return alfred_client_server_status(globals);
+ case CLIENT_EVENT_MONITOR:
+ return alfred_client_event_monitor(globals);
}
return 0;
@@ -97,6 +97,9 @@ Change the alfred server to use the new \fBbatman-adv interface\fP
.TP
\fB\-S\fP, \fB\-\-server\-status\fP
Request server status information such as mode & interfaces\fP
+.TP
+\fB\-E\fP, \fB\-\-event\-monitor\fP
+Start alfred event monitor connecting to the alfred server and reporting update events\fP
.
.SH SERVER OPTIONS
.TP
@@ -59,6 +59,8 @@ struct alfred_transaction_mgmt {
* @ALFRED_STATUS_ERROR: Error was detected during the transaction
* @ALFRED_MODESWITCH: Switch between different operation modes
* @ALFRED_CHANGE_INTERFACE: Change the listening interface
+ * @ALFRED_EVENT_REGISTER: Request to be notified about alfred update events
+ * @ALFRED_EVENT_NOTIFY: Data record update has been received
*/
enum alfred_packet_type {
ALFRED_PUSH_DATA = 0,
@@ -70,6 +72,8 @@ enum alfred_packet_type {
ALFRED_CHANGE_INTERFACE = 6,
ALFRED_CHANGE_BAT_IFACE = 7,
ALFRED_SERVER_STATUS = 8,
+ ALFRED_EVENT_REGISTER = 9,
+ ALFRED_EVENT_NOTIFY = 10,
};
/* packets */
@@ -229,6 +233,29 @@ struct alfred_server_status_rep_v0 {
struct alfred_tlv header;
} __packed;
+/**
+ * struct alfred_event_register_v0 - event registration message
+ * @header: TLV header describing the complete packet
+ *
+ * Sent by the client to daemon to register for data record updates
+ */
+struct alfred_event_register_v0 {
+ struct alfred_tlv header;
+} __packed;
+
+/**
+ * struct alfred_event_notify_v0 - event notification message
+ * @header: TLV header describing the complete packet
+ * @type: Type of the data triggering the event
+ *
+ * Sent by the daemon to client on data record update
+ */
+struct alfred_event_notify_v0 {
+ struct alfred_tlv header;
+ uint8_t type;
+ uint8_t source[ETH_ALEN];
+} __packed;
+
/**
* struct alfred_status_v0 - Status info of a transaction
* @header: TLV header describing the complete packet
@@ -76,8 +76,11 @@ static int finish_alfred_push_data(struct globals *globals,
/* check that data was changed */
if (new_entry_created ||
dataset->data.header.length != data_len ||
- memcmp(dataset->buf, data->data, data_len) != 0)
+ memcmp(dataset->buf, data->data, data_len) != 0) {
changed_data_type(globals, data->header.type);
+ unix_sock_event_notify(globals, data->header.type,
+ data->source);
+ }
/* free old buffer */
if (dataset->buf) {
@@ -518,5 +518,6 @@ int alfred_server(struct globals *globals)
netsock_close_all(globals);
unix_sock_close(globals);
+ unix_sock_events_close_all(globals);
return 0;
}
@@ -13,6 +13,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <fcntl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/un.h>
@@ -26,6 +27,8 @@
static void unix_sock_read(struct globals *globals,
struct epoll_handle *handle __unused,
struct epoll_event *ev __unused);
+static int unix_sock_register_listener(struct globals *globals,
+ int client_sock);
int unix_sock_open_daemon(struct globals *globals)
{
@@ -99,6 +102,7 @@ static int unix_sock_add_data(struct globals *globals,
struct alfred_push_data_v0 *push,
int client_sock)
{
+ bool new_entry_created = false;
struct alfred_data *data;
struct dataset *dataset;
int len, data_len, ret = -1;
@@ -153,10 +157,18 @@ static int unix_sock_add_data(struct globals *globals,
free(dataset);
goto err;
}
+ new_entry_created = true;
}
dataset->data_source = SOURCE_LOCAL;
clock_gettime(CLOCK_MONOTONIC, &dataset->last_seen);
+ /* check that data was changed */
+ if (new_entry_created ||
+ dataset->data.header.length != data_len ||
+ memcmp(dataset->buf, data->data, data_len) != 0)
+ unix_sock_event_notify(globals, data->header.type,
+ data->source);
+
/* free old buffer */
free(dataset->buf);
@@ -554,6 +566,9 @@ static void unix_sock_read(struct globals *globals,
case ALFRED_SERVER_STATUS:
unix_sock_server_status(globals, client_sock);
break;
+ case ALFRED_EVENT_REGISTER:
+ unix_sock_register_listener(globals, client_sock);
+ break;
default:
/* unknown packet type */
goto err;
@@ -570,3 +585,124 @@ int unix_sock_close(struct globals *globals)
close(globals->unix_sock);
return 0;
}
+
+static void unix_sock_event_listener_free(struct event_listener *listener)
+{
+ list_del(&listener->list);
+ close(listener->fd);
+ free(listener);
+}
+
+static void unix_sock_event_listener_handle(struct globals *globals __unused,
+ struct epoll_handle *handle,
+ struct epoll_event *ev)
+{
+ struct event_listener *listener;
+ char buff[4];
+ int ret;
+
+ listener = container_of(handle, struct event_listener, epoll);
+
+ if (ev->events & EPOLLERR) {
+ fprintf(stderr, "Error on event listener detected: %d\n",
+ listener->fd);
+ unix_sock_event_listener_free(listener);
+ return;
+ }
+
+ if (ev->events & EPOLLIN) {
+ ret = recv(listener->fd, buff, sizeof(buff),
+ MSG_PEEK | MSG_DONTWAIT);
+ /* listener has hung up */
+ if (ret == 0)
+ unix_sock_event_listener_free(listener);
+ else if (ret > 0) {
+ fprintf(stderr, "Event listener has written to socket: %d - closing\n",
+ listener->fd);
+ unix_sock_event_listener_free(listener);
+ }
+ }
+}
+
+static int unix_sock_register_listener(struct globals *globals, int client_sock)
+{
+ struct event_listener *listener;
+ struct epoll_event ev;
+ int ret;
+
+ ret = fcntl(client_sock, F_GETFL, 0);
+ if (ret < 0) {
+ perror("failed to get file status flags");
+ goto err;
+ }
+
+ ret = fcntl(client_sock, F_SETFL, ret | O_NONBLOCK);
+ if (ret < 0) {
+ perror("failed to set file status flags");
+ goto err;
+ }
+
+ listener = malloc(sizeof(*listener));
+ if (!listener)
+ goto err;
+
+ ev.events = EPOLLIN;
+ ev.data.ptr = &listener->epoll;
+ listener->epoll.handler = unix_sock_event_listener_handle;
+
+ if (epoll_ctl(globals->epollfd, EPOLL_CTL_ADD, client_sock,
+ &ev) == -1) {
+ perror("Failed to add epoll for event listener");
+ goto err;
+ }
+
+ listener->fd = client_sock;
+ list_add_tail(&listener->list, &globals->event_listeners);
+ return 0;
+
+err:
+ close(client_sock);
+ return -1;
+}
+
+static void unix_sock_event_notify_listener(struct event_listener *listener,
+ uint8_t type,
+ const uint8_t source[ETH_ALEN])
+{
+ struct alfred_event_notify_v0 notify;
+ int ret;
+
+ notify.header.type = ALFRED_EVENT_NOTIFY;
+ notify.header.version = ALFRED_VERSION;
+ notify.header.length = FIXED_TLV_LEN(notify);
+ notify.type = type;
+ memcpy(¬ify.source, source, ETH_ALEN);
+
+ ret = write(listener->fd, ¬ify, sizeof(notify));
+ if (ret == sizeof(notify))
+ return;
+
+ unix_sock_event_listener_free(listener);
+}
+
+void unix_sock_events_close_all(struct globals *globals)
+{
+ struct event_listener *listener, *tmp;
+
+ list_for_each_entry_safe(listener, tmp,
+ &globals->event_listeners, list) {
+ unix_sock_event_listener_free(listener);
+ }
+}
+
+void unix_sock_event_notify(struct globals *globals, uint8_t type,
+ const uint8_t source[ETH_ALEN])
+{
+ struct event_listener *listener, *tmp;
+
+ /* if event notify is unsuccessful, listener socket is closed */
+ list_for_each_entry_safe(listener, tmp,
+ &globals->event_listeners, list) {
+ unix_sock_event_notify_listener(listener, type, source);
+ }
+}