summaryrefslogtreecommitdiff
path: root/ubusd_monitor.c
blob: bba741e86987172197583331f13d8cc10e09036c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/*
 * Copyright (C) 2015 Felix Fietkau <nbd@openwrt.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 2.1
 * as published by the Free Software Foundation
 *
 * 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.
 */

#include "ubusd.h"

static struct ubus_object *monitor_obj;
static LIST_HEAD(monitors);

struct ubus_monitor {
	struct list_head list;
	struct ubus_client *cl;
	uint32_t seq;
};

static void
ubusd_monitor_free(struct ubus_monitor *m)
{
	list_del(&m->list);
	free(m);
}

static bool
ubusd_monitor_connect(struct ubus_client *cl, struct ubus_msg_buf *ub)
{
	struct ubus_monitor *m;

	ubusd_monitor_disconnect(cl);

	m = calloc(1, sizeof(*m));
	if (!m)
		return false;

	m->cl = cl;
	list_add_tail(&m->list, &monitors);

	return true;
}

static struct ubus_monitor*
ubusd_monitor_find(struct ubus_client *cl)
{
	struct ubus_monitor *m, *tmp;

	list_for_each_entry_safe(m, tmp, &monitors, list) {
		if (m->cl != cl)
			continue;

		return m;
	}

	return NULL;
}

void
ubusd_monitor_disconnect(struct ubus_client *cl)
{
	struct ubus_monitor *m;

	m = ubusd_monitor_find(cl);
	if (!m)
		return;

	ubusd_monitor_free(m);
}

void
ubusd_monitor_message(struct ubus_client *cl, struct ubus_msg_buf *ub, bool send)
{
	static struct blob_buf mb;
	struct ubus_monitor *m;

	if (list_empty(&monitors))
		return;

	blob_buf_init(&mb, 0);
	blob_put_int32(&mb, UBUS_MONITOR_CLIENT, cl->id.id);
	blob_put_int32(&mb, UBUS_MONITOR_PEER, ub->hdr.peer);
	blob_put_int32(&mb, UBUS_MONITOR_SEQ, ub->hdr.seq);
	blob_put_int32(&mb, UBUS_MONITOR_TYPE, ub->hdr.type);
	blob_put_int8(&mb, UBUS_MONITOR_SEND, send);
	blob_put(&mb, UBUS_MONITOR_DATA, blob_data(ub->data), blob_len(ub->data));

	ub = ubus_msg_new(mb.head, blob_raw_len(mb.head), true);
	ub->hdr.type = UBUS_MSG_MONITOR;

	list_for_each_entry(m, &monitors, list) {
		ub->hdr.seq = ++m->seq;
		ubus_msg_send(m->cl, ub);
	}

	ubus_msg_free(ub);
}

static int
ubusd_monitor_recv(struct ubus_client *cl, struct ubus_msg_buf *ub,
		   const char *method, struct blob_attr *msg)
{
	/* Only root is allowed for now */
	if (cl->uid != 0 || cl->gid != 0)
		return UBUS_STATUS_PERMISSION_DENIED;

	if (!strcmp(method, "add")) {
		if (!ubusd_monitor_connect(cl, ub))
			return UBUS_STATUS_UNKNOWN_ERROR;

		return UBUS_STATUS_OK;
	}

	if (!strcmp(method, "remove")) {
		ubusd_monitor_disconnect(cl);
		return UBUS_STATUS_OK;
	}

	return UBUS_STATUS_METHOD_NOT_FOUND;
}

void
ubusd_monitor_init(void)
{
	monitor_obj = ubusd_create_object_internal(NULL, UBUS_SYSTEM_OBJECT_MONITOR);
	if (monitor_obj != NULL)
		monitor_obj->recv_msg = ubusd_monitor_recv;
}