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_event_node(const char * node)50 static bool check_event_node(const char *node)
51 {
52 	if (!node || !strstarts(node, "@")) {
53 		errno = EINVAL;
54 		return false;
55 	}
56 	return true;
57 }
58 
59 /* Is child a subnode of parent, or equal? */
is_child(const char * child,const char * parent)60 static bool is_child(const char *child, const char *parent)
61 {
62 	unsigned int len = strlen(parent);
63 
64 	/*
65 	 * / should really be "" for this algorithm to work, but that's a
66 	 * usability nightmare.
67 	 */
68 	if (streq(parent, "/"))
69 		return true;
70 
71 	if (strncmp(child, parent, len) != 0)
72 		return false;
73 
74 	return child[len] == '/' || child[len] == '\0';
75 }
76 
77 /*
78  * Send a watch event.
79  * Temporary memory allocations are done with ctx.
80  */
add_event(struct connection * conn,void * ctx,struct watch * watch,const char * name)81 static void add_event(struct connection *conn,
82 		      void *ctx,
83 		      struct watch *watch,
84 		      const char *name)
85 {
86 	/* Data to send (node\0token\0). */
87 	unsigned int len;
88 	char *data;
89 
90 	if (!check_event_node(name)) {
91 		/* Can this conn load node, or see that it doesn't exist? */
92 		struct node *node = get_node(conn, ctx, name, XS_PERM_READ);
93 		/*
94 		 * XXX We allow EACCES here because otherwise a non-dom0
95 		 * backend driver cannot watch for disappearance of a frontend
96 		 * xenstore directory. When the directory disappears, we
97 		 * revert to permissions of the parent directory for that path,
98 		 * which will typically disallow access for the backend.
99 		 * But this breaks device-channel teardown!
100 		 * Really we should fix this better...
101 		 */
102 		if (!node && errno != ENOENT && errno != EACCES)
103 			return;
104 	}
105 
106 	if (watch->relative_path) {
107 		name += strlen(watch->relative_path);
108 		if (*name == '/') /* Could be "" */
109 			name++;
110 	}
111 
112 	len = strlen(name) + 1 + strlen(watch->token) + 1;
113 	data = talloc_array(ctx, char, len);
114 	if (!data)
115 		return;
116 	strcpy(data, name);
117 	strcpy(data + strlen(name) + 1, watch->token);
118 	send_reply(conn, XS_WATCH_EVENT, data, len);
119 	talloc_free(data);
120 }
121 
122 /*
123  * Check whether any watch events are to be sent.
124  * Temporary memory allocations are done with ctx.
125  */
fire_watches(struct connection * conn,void * ctx,const char * name,bool recurse)126 void fire_watches(struct connection *conn, void *ctx, const char *name,
127 		  bool recurse)
128 {
129 	struct connection *i;
130 	struct watch *watch;
131 
132 	/* During transactions, don't fire watches. */
133 	if (conn && conn->transaction)
134 		return;
135 
136 	/* Create an event for each watch. */
137 	list_for_each_entry(i, &connections, list) {
138 		list_for_each_entry(watch, &i->watches, list) {
139 			if (is_child(name, watch->node))
140 				add_event(i, ctx, watch, name);
141 			else if (recurse && is_child(watch->node, name))
142 				add_event(i, ctx, watch, watch->node);
143 		}
144 	}
145 }
146 
destroy_watch(void * _watch)147 static int destroy_watch(void *_watch)
148 {
149 	trace_destroy(_watch, "watch");
150 	return 0;
151 }
152 
do_watch(struct connection * conn,struct buffered_data * in)153 int do_watch(struct connection *conn, struct buffered_data *in)
154 {
155 	struct watch *watch;
156 	char *vec[2];
157 	bool relative;
158 
159 	if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec))
160 		return EINVAL;
161 
162 	if (strstarts(vec[0], "@")) {
163 		relative = false;
164 		if (strlen(vec[0]) > XENSTORE_REL_PATH_MAX)
165 			return EINVAL;
166 		/* check if valid event */
167 	} else {
168 		relative = !strstarts(vec[0], "/");
169 		vec[0] = canonicalize(conn, in, vec[0]);
170 		if (!vec[0])
171 			return ENOMEM;
172 		if (!is_valid_nodename(vec[0]))
173 			return EINVAL;
174 	}
175 
176 	/* Check for duplicates. */
177 	list_for_each_entry(watch, &conn->watches, list) {
178 		if (streq(watch->node, vec[0]) &&
179 		    streq(watch->token, vec[1]))
180 			return EEXIST;
181 	}
182 
183 	if (domain_watch(conn) > quota_nb_watch_per_domain)
184 		return E2BIG;
185 
186 	watch = talloc(conn, struct watch);
187 	if (!watch)
188 		return ENOMEM;
189 	watch->node = talloc_strdup(watch, vec[0]);
190 	watch->token = talloc_strdup(watch, vec[1]);
191 	if (!watch->node || !watch->token) {
192 		talloc_free(watch);
193 		return ENOMEM;
194 	}
195 	if (relative)
196 		watch->relative_path = get_implicit_path(conn);
197 	else
198 		watch->relative_path = NULL;
199 
200 	INIT_LIST_HEAD(&watch->events);
201 
202 	domain_watch_inc(conn);
203 	list_add_tail(&watch->list, &conn->watches);
204 	trace_create(watch, "watch");
205 	talloc_set_destructor(watch, destroy_watch);
206 	send_ack(conn, XS_WATCH);
207 
208 	/* We fire once up front: simplifies clients and restart. */
209 	add_event(conn, in, watch, watch->node);
210 
211 	return 0;
212 }
213 
do_unwatch(struct connection * conn,struct buffered_data * in)214 int do_unwatch(struct connection *conn, struct buffered_data *in)
215 {
216 	struct watch *watch;
217 	char *node, *vec[2];
218 
219 	if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec))
220 		return EINVAL;
221 
222 	node = canonicalize(conn, in, vec[0]);
223 	if (!node)
224 		return ENOMEM;
225 	list_for_each_entry(watch, &conn->watches, list) {
226 		if (streq(watch->node, node) && streq(watch->token, vec[1])) {
227 			list_del(&watch->list);
228 			talloc_free(watch);
229 			domain_watch_dec(conn);
230 			send_ack(conn, XS_UNWATCH);
231 			return 0;
232 		}
233 	}
234 	return ENOENT;
235 }
236 
conn_delete_all_watches(struct connection * conn)237 void conn_delete_all_watches(struct connection *conn)
238 {
239 	struct watch *watch;
240 
241 	while ((watch = list_top(&conn->watches, struct watch, list))) {
242 		list_del(&watch->list);
243 		talloc_free(watch);
244 		domain_watch_dec(conn);
245 	}
246 }
247 
248 /*
249  * Local variables:
250  *  c-file-style: "linux"
251  *  indent-tabs-mode: t
252  *  c-indent-level: 8
253  *  c-basic-offset: 8
254  *  tab-width: 8
255  * End:
256  */
257