ia64/xen-unstable

view tools/xenstore/xenstored_watch.c @ 6946:e703abaf6e3d

Add behaviour to the remove methods to remove the transaction's path itself. This allows us to write Remove(path) to remove the specified path rather than having to slice the path ourselves.
author emellor@ewan
date Sun Sep 18 14:42:13 2005 +0100 (2005-09-18)
parents 3233e7ecfa9f
children 2796d45c5835 872cf6ee0594
line source
1 /*
2 Watch code for Xen Store Daemon.
3 Copyright (C) 2005 Rusty Russell IBM Corporation
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.
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.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
20 #include <stdio.h>
21 #include <sys/types.h>
22 #include <stdarg.h>
23 #include <stdlib.h>
24 #include <sys/time.h>
25 #include <time.h>
26 #include <assert.h>
27 #include "talloc.h"
28 #include "list.h"
29 #include "xenstored_watch.h"
30 #include "xs_lib.h"
31 #include "utils.h"
32 #include "xenstored_test.h"
33 #include "xenstored_domain.h"
35 /* FIXME: time out unacked watches. */
36 struct watch_event
37 {
38 /* The events on this watch. */
39 struct list_head list;
41 /* Data to send (node\0token\0). */
42 unsigned int len;
43 char *data;
44 };
46 struct watch
47 {
48 /* Watches on this connection */
49 struct list_head list;
51 /* Current outstanding events applying to this watch. */
52 struct list_head events;
54 /* Is this relative to connnection's implicit path? */
55 const char *relative_path;
57 char *token;
58 char *node;
59 };
61 /* Look through our watches: if any of them have an event, queue it. */
62 void queue_next_event(struct connection *conn)
63 {
64 struct watch_event *event;
65 struct watch *watch;
67 /* We had a reply queued already? Send it: other end will
68 * discard watch. */
69 if (conn->waiting_reply) {
70 conn->out = conn->waiting_reply;
71 conn->waiting_reply = NULL;
72 conn->waiting_for_ack = NULL;
73 return;
74 }
76 /* If we're already waiting for ack, don't queue more. */
77 if (conn->waiting_for_ack)
78 return;
80 list_for_each_entry(watch, &conn->watches, list) {
81 event = list_top(&watch->events, struct watch_event, list);
82 if (event) {
83 conn->waiting_for_ack = watch;
84 send_reply(conn,XS_WATCH_EVENT,event->data,event->len);
85 break;
86 }
87 }
88 }
90 static int destroy_watch_event(void *_event)
91 {
92 struct watch_event *event = _event;
94 trace_destroy(event, "watch_event");
95 return 0;
96 }
98 static void add_event(struct connection *conn,
99 struct watch *watch, const char *node)
100 {
101 struct watch_event *event;
103 /* Check read permission: no permission, no watch event.
104 * If it doesn't exist, we need permission to read parent.
105 */
106 if (!check_node_perms(conn, node, XS_PERM_READ|XS_PERM_ENOENT_OK) &&
107 !check_event_node(node)) {
108 return;
109 }
111 if (watch->relative_path) {
112 node += strlen(watch->relative_path);
113 if (*node == '/') /* Could be "" */
114 node++;
115 }
117 event = talloc(watch, struct watch_event);
118 event->len = strlen(node) + 1 + strlen(watch->token) + 1;
119 event->data = talloc_array(event, char, event->len);
120 strcpy(event->data, node);
121 strcpy(event->data + strlen(node) + 1, watch->token);
122 talloc_set_destructor(event, destroy_watch_event);
123 list_add_tail(&event->list, &watch->events);
124 trace_create(event, "watch_event");
125 }
127 /* FIXME: we fail to fire on out of memory. Should drop connections. */
128 void fire_watches(struct connection *conn, const char *node, bool recurse)
129 {
130 struct connection *i;
131 struct watch *watch;
133 /* During transactions, don't fire watches. */
134 if (conn && conn->transaction)
135 return;
137 /* Create an event for each watch. */
138 list_for_each_entry(i, &connections, list) {
139 list_for_each_entry(watch, &i->watches, list) {
140 if (is_child(node, watch->node))
141 add_event(i, watch, node);
142 else if (recurse && is_child(watch->node, node))
143 add_event(i, watch, watch->node);
144 else
145 continue;
146 /* If connection not doing anything, queue this. */
147 if (i->state == OK)
148 queue_next_event(i);
149 }
150 }
151 }
153 static int destroy_watch(void *_watch)
154 {
155 trace_destroy(_watch, "watch");
156 return 0;
157 }
159 void shortest_watch_ack_timeout(struct timeval *tv)
160 {
161 (void)tv;
162 #if 0 /* FIXME */
163 struct watch *watch;
165 list_for_each_entry(watch, &watches, list) {
166 struct watch_event *i;
167 list_for_each_entry(i, &watch->events, list) {
168 if (!timerisset(&i->timeout))
169 continue;
170 if (!timerisset(tv) || timercmp(&i->timeout, tv, <))
171 *tv = i->timeout;
172 }
173 }
174 #endif
175 }
177 void check_watch_ack_timeout(void)
178 {
179 #if 0
180 struct watch *watch;
181 struct timeval now;
183 gettimeofday(&now, NULL);
184 list_for_each_entry(watch, &watches, list) {
185 struct watch_event *i, *tmp;
186 list_for_each_entry_safe(i, tmp, &watch->events, list) {
187 if (!timerisset(&i->timeout))
188 continue;
189 if (timercmp(&i->timeout, &now, <)) {
190 xprintf("Warning: timeout on watch event %s"
191 " token %s\n",
192 i->node, watch->token);
193 trace_watch_timeout(watch->conn, i->node,
194 watch->token);
195 timerclear(&i->timeout);
196 }
197 }
198 }
199 #endif
200 }
202 void do_watch(struct connection *conn, struct buffered_data *in)
203 {
204 struct watch *watch;
205 char *vec[2];
206 bool relative;
208 if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec)) {
209 send_error(conn, EINVAL);
210 return;
211 }
213 if (strstarts(vec[0], "@")) {
214 relative = false;
215 /* check if valid event */
216 } else {
217 relative = !strstarts(vec[0], "/");
218 vec[0] = canonicalize(conn, vec[0]);
219 if (!is_valid_nodename(vec[0])) {
220 send_error(conn, errno);
221 return;
222 }
223 }
225 watch = talloc(conn, struct watch);
226 watch->node = talloc_strdup(watch, vec[0]);
227 watch->token = talloc_strdup(watch, vec[1]);
228 if (relative)
229 watch->relative_path = get_implicit_path(conn);
230 else
231 watch->relative_path = NULL;
233 INIT_LIST_HEAD(&watch->events);
235 list_add_tail(&watch->list, &conn->watches);
236 trace_create(watch, "watch");
237 talloc_set_destructor(watch, destroy_watch);
238 send_ack(conn, XS_WATCH);
239 }
241 void do_watch_ack(struct connection *conn, const char *token)
242 {
243 struct watch_event *event;
245 if (!token) {
246 send_error(conn, EINVAL);
247 return;
248 }
250 if (!conn->waiting_for_ack) {
251 send_error(conn, ENOENT);
252 return;
253 }
255 if (!streq(conn->waiting_for_ack->token, token)) {
256 /* They're confused: this will cause us to send event again */
257 conn->waiting_for_ack = NULL;
258 send_error(conn, EINVAL);
259 return;
260 }
262 /* Remove event: after ack sent, core will call queue_next_event */
263 event = list_top(&conn->waiting_for_ack->events, struct watch_event,
264 list);
265 list_del(&event->list);
266 talloc_free(event);
268 conn->waiting_for_ack = NULL;
269 send_ack(conn, XS_WATCH_ACK);
270 }
272 void do_unwatch(struct connection *conn, struct buffered_data *in)
273 {
274 struct watch *watch;
275 char *node, *vec[2];
277 if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec)) {
278 send_error(conn, EINVAL);
279 return;
280 }
282 /* We don't need to worry if we're waiting for an ack for the
283 * watch we're deleting: conn->waiting_for_ack was reset by
284 * this command in consider_message anyway. */
285 node = canonicalize(conn, vec[0]);
286 list_for_each_entry(watch, &conn->watches, list) {
287 if (streq(watch->node, node) && streq(watch->token, vec[1])) {
288 list_del(&watch->list);
289 talloc_free(watch);
290 send_ack(conn, XS_UNWATCH);
291 return;
292 }
293 }
294 send_error(conn, ENOENT);
295 }
297 #ifdef TESTING
298 void dump_watches(struct connection *conn)
299 {
300 struct watch *watch;
301 struct watch_event *event;
303 if (conn->waiting_for_ack)
304 printf(" waiting_for_ack for watch on %s token %s\n",
305 conn->waiting_for_ack->node,
306 conn->waiting_for_ack->token);
308 list_for_each_entry(watch, &conn->watches, list) {
309 printf(" watch on %s token %s\n",
310 watch->node, watch->token);
311 list_for_each_entry(event, &watch->events, list)
312 printf(" event: %s\n", event->data);
313 }
314 }
315 #endif