1 /*
2     Watch code for Xen Store Daemon.
3     Copyright (C) 2005 Rusty Russell IBM Corporation
4 
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9 
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with this program; If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 #include <stdio.h>
20 #include <sys/types.h>
21 #include <stdarg.h>
22 #include <stdlib.h>
23 #include <sys/time.h>
24 #include <time.h>
25 #include <assert.h>
26 #include "talloc.h"
27 #include "list.h"
28 #include "xenstored_watch.h"
29 #include "xenstore_lib.h"
30 #include "utils.h"
31 #include "xenstored_domain.h"
32 
33 extern int quota_nb_watch_per_domain;
34 
35 struct watch
36 {
37 	/* Watches on this connection */
38 	struct list_head list;
39 
40 	/* Current outstanding events applying to this watch. */
41 	struct list_head events;
42 
43 	/* Is this relative to connnection's implicit path? */
44 	const char *relative_path;
45 
46 	char *token;
47 	char *node;
48 };
49 
check_special_event(const char * name)50 static bool check_special_event(const char *name)
51 {
52 	assert(name);
53 
54 	return strstarts(name, "@");
55 }
56 
57 /* Is child a subnode of parent, or equal? */
is_child(const char * child,const char * parent)58 static bool is_child(const char *child, const char *parent)
59 {
60 	unsigned int len = strlen(parent);
61 
62 	/*
63 	 * / should really be "" for this algorithm to work, but that's a
64 	 * usability nightmare.
65 	 */
66 	if (streq(parent, "/"))
67 		return true;
68 
69 	if (strncmp(child, parent, len) != 0)
70 		return false;
71 
72 	return child[len] == '/' || child[len] == '\0';
73 }
74 
75 /*
76  * Send a watch event.
77  * Temporary memory allocations are done with ctx.
78  */
add_event(struct connection * conn,const void * ctx,struct watch * watch,const char * name)79 static void add_event(struct connection *conn,
80 		      const void *ctx,
81 		      struct watch *watch,
82 		      const char *name)
83 {
84 	/* Data to send (node\0token\0). */
85 	unsigned int len;
86 	char *data;
87 
88 	if (watch->relative_path) {
89 		name += strlen(watch->relative_path);
90 		if (*name == '/') /* Could be "" */
91 			name++;
92 	}
93 
94 	len = strlen(name) + 1 + strlen(watch->token) + 1;
95 	/* Don't try to send over-long events. */
96 	if (len > XENSTORE_PAYLOAD_MAX)
97 		return;
98 
99 	data = talloc_array(ctx, char, len);
100 	if (!data)
101 		return;
102 	strcpy(data, name);
103 	strcpy(data + strlen(name) + 1, watch->token);
104 	send_reply(conn, XS_WATCH_EVENT, data, len);
105 	talloc_free(data);
106 }
107 
108 /*
109  * Check permissions of a specific watch to fire:
110  * Either the node itself or its parent have to be readable by the connection
111  * the watch has been setup for. In case a watch event is created due to
112  * changed permissions we need to take the old permissions into account, too.
113  */
watch_permitted(struct connection * conn,const void * ctx,const char * name,struct node * node,struct node_perms * perms)114 static bool watch_permitted(struct connection *conn, const void *ctx,
115 			    const char *name, struct node *node,
116 			    struct node_perms *perms)
117 {
118 	enum xs_perm_type perm;
119 	struct node *parent;
120 	char *parent_name;
121 
122 	if (perms) {
123 		perm = perm_for_conn(conn, perms);
124 		if (perm & XS_PERM_READ)
125 			return true;
126 	}
127 
128 	if (!node) {
129 		node = read_node(conn, ctx, name);
130 		if (!node)
131 			return false;
132 	}
133 
134 	perm = perm_for_conn(conn, &node->perms);
135 	if (perm & XS_PERM_READ)
136 		return true;
137 
138 	parent = node->parent;
139 	if (!parent) {
140 		parent_name = get_parent(ctx, node->name);
141 		if (!parent_name)
142 			return false;
143 		parent = read_node(conn, ctx, parent_name);
144 		if (!parent)
145 			return false;
146 	}
147 
148 	perm = perm_for_conn(conn, &parent->perms);
149 
150 	return perm & XS_PERM_READ;
151 }
152 
153 /*
154  * Check whether any watch events are to be sent.
155  * Temporary memory allocations are done with ctx.
156  * We need to take the (potential) old permissions of the node into account
157  * as a watcher losing permissions to access a node should receive the
158  * watch event, too.
159  */
fire_watches(struct connection * conn,const void * ctx,const char * name,struct node * node,bool exact,struct node_perms * perms)160 void fire_watches(struct connection *conn, const void *ctx, const char *name,
161 		  struct node *node, bool exact, struct node_perms *perms)
162 {
163 	struct connection *i;
164 	struct watch *watch;
165 
166 	/* During transactions, don't fire watches. */
167 	if (conn && conn->transaction)
168 		return;
169 
170 	/* Create an event for each watch. */
171 	list_for_each_entry(i, &connections, list) {
172 		/* introduce/release domain watches */
173 		if (check_special_event(name)) {
174 			if (!check_perms_special(name, i))
175 				continue;
176 		} else {
177 			if (!watch_permitted(i, ctx, name, node, perms))
178 				continue;
179 		}
180 
181 		list_for_each_entry(watch, &i->watches, list) {
182 			if (exact) {
183 				if (streq(name, watch->node))
184 					add_event(i, ctx, watch, name);
185 			} else {
186 				if (is_child(name, watch->node))
187 					add_event(i, ctx, watch, name);
188 			}
189 		}
190 	}
191 }
192 
destroy_watch(void * _watch)193 static int destroy_watch(void *_watch)
194 {
195 	trace_destroy(_watch, "watch");
196 	return 0;
197 }
198 
do_watch(struct connection * conn,struct buffered_data * in)199 int do_watch(struct connection *conn, struct buffered_data *in)
200 {
201 	struct watch *watch;
202 	char *vec[2];
203 	bool relative;
204 
205 	if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec))
206 		return EINVAL;
207 
208 	if (strstarts(vec[0], "@")) {
209 		relative = false;
210 		if (strlen(vec[0]) > XENSTORE_REL_PATH_MAX)
211 			return EINVAL;
212 		/* check if valid event */
213 	} else {
214 		relative = !strstarts(vec[0], "/");
215 		vec[0] = canonicalize(conn, in, vec[0]);
216 		if (!vec[0])
217 			return ENOMEM;
218 		if (!is_valid_nodename(vec[0]))
219 			return EINVAL;
220 	}
221 
222 	/* Check for duplicates. */
223 	list_for_each_entry(watch, &conn->watches, list) {
224 		if (streq(watch->node, vec[0]) &&
225 		    streq(watch->token, vec[1]))
226 			return EEXIST;
227 	}
228 
229 	if (domain_watch(conn) > quota_nb_watch_per_domain)
230 		return E2BIG;
231 
232 	watch = talloc(conn, struct watch);
233 	if (!watch)
234 		return ENOMEM;
235 	watch->node = talloc_strdup(watch, vec[0]);
236 	watch->token = talloc_strdup(watch, vec[1]);
237 	if (!watch->node || !watch->token) {
238 		talloc_free(watch);
239 		return ENOMEM;
240 	}
241 	if (relative)
242 		watch->relative_path = get_implicit_path(conn);
243 	else
244 		watch->relative_path = NULL;
245 
246 	INIT_LIST_HEAD(&watch->events);
247 
248 	domain_watch_inc(conn);
249 	list_add_tail(&watch->list, &conn->watches);
250 	trace_create(watch, "watch");
251 	talloc_set_destructor(watch, destroy_watch);
252 	send_ack(conn, XS_WATCH);
253 
254 	/* We fire once up front: simplifies clients and restart. */
255 	add_event(conn, in, watch, watch->node);
256 
257 	return 0;
258 }
259 
do_unwatch(struct connection * conn,struct buffered_data * in)260 int do_unwatch(struct connection *conn, struct buffered_data *in)
261 {
262 	struct watch *watch;
263 	char *node, *vec[2];
264 
265 	if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec))
266 		return EINVAL;
267 
268 	node = canonicalize(conn, in, vec[0]);
269 	if (!node)
270 		return ENOMEM;
271 	list_for_each_entry(watch, &conn->watches, list) {
272 		if (streq(watch->node, node) && streq(watch->token, vec[1])) {
273 			list_del(&watch->list);
274 			talloc_free(watch);
275 			domain_watch_dec(conn);
276 			send_ack(conn, XS_UNWATCH);
277 			return 0;
278 		}
279 	}
280 	return ENOENT;
281 }
282 
conn_delete_all_watches(struct connection * conn)283 void conn_delete_all_watches(struct connection *conn)
284 {
285 	struct watch *watch;
286 
287 	while ((watch = list_top(&conn->watches, struct watch, list))) {
288 		list_del(&watch->list);
289 		talloc_free(watch);
290 		domain_watch_dec(conn);
291 	}
292 }
293 
294 /*
295  * Local variables:
296  *  mode: C
297  *  c-file-style: "linux"
298  *  indent-tabs-mode: t
299  *  c-basic-offset: 8
300  *  tab-width: 8
301  * End:
302  */
303