ia64/xen-unstable

view tools/xenstore/xenstored_watch.c @ 6756:f752e0c873a6

merge?
author cl349@firebug.cl.cam.ac.uk
date Mon Sep 12 12:32:20 2005 +0000 (2005-09-12)
parents b594bb976a74 8db9c5873b9b
children f1bb1316b26f f804b28871ba
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 fprintf(stderr, "No permission for %s\n", node);
109 return;
110 }
112 if (watch->relative_path) {
113 node += strlen(watch->relative_path);
114 if (*node == '/') /* Could be "" */
115 node++;
116 }
118 event = talloc(watch, struct watch_event);
119 event->len = strlen(node) + 1 + strlen(watch->token) + 1;
120 event->data = talloc_array(event, char, event->len);
121 strcpy(event->data, node);
122 strcpy(event->data + strlen(node) + 1, watch->token);
123 talloc_set_destructor(event, destroy_watch_event);
124 list_add_tail(&event->list, &watch->events);
125 trace_create(event, "watch_event");
126 }
128 /* FIXME: we fail to fire on out of memory. Should drop connections. */
129 void fire_watches(struct connection *conn, const char *node, bool recurse)
130 {
131 struct connection *i;
132 struct watch *watch;
134 /* During transactions, don't fire watches. */
135 if (conn && conn->transaction)
136 return;
138 /* Create an event for each watch. */
139 list_for_each_entry(i, &connections, list) {
140 list_for_each_entry(watch, &i->watches, list) {
141 if (is_child(node, watch->node))
142 add_event(i, watch, node);
143 else if (recurse && is_child(watch->node, node))
144 add_event(i, watch, watch->node);
145 else
146 continue;
147 /* If connection not doing anything, queue this. */
148 if (!i->out)
149 queue_next_event(i);
150 }
151 }
152 }
154 static int destroy_watch(void *_watch)
155 {
156 trace_destroy(_watch, "watch");
157 return 0;
158 }
160 void shortest_watch_ack_timeout(struct timeval *tv)
161 {
162 (void)tv;
163 #if 0 /* FIXME */
164 struct watch *watch;
166 list_for_each_entry(watch, &watches, list) {
167 struct watch_event *i;
168 list_for_each_entry(i, &watch->events, list) {
169 if (!timerisset(&i->timeout))
170 continue;
171 if (!timerisset(tv) || timercmp(&i->timeout, tv, <))
172 *tv = i->timeout;
173 }
174 }
175 #endif
176 }
178 void check_watch_ack_timeout(void)
179 {
180 #if 0
181 struct watch *watch;
182 struct timeval now;
184 gettimeofday(&now, NULL);
185 list_for_each_entry(watch, &watches, list) {
186 struct watch_event *i, *tmp;
187 list_for_each_entry_safe(i, tmp, &watch->events, list) {
188 if (!timerisset(&i->timeout))
189 continue;
190 if (timercmp(&i->timeout, &now, <)) {
191 xprintf("Warning: timeout on watch event %s"
192 " token %s\n",
193 i->node, watch->token);
194 trace_watch_timeout(watch->conn, i->node,
195 watch->token);
196 timerclear(&i->timeout);
197 }
198 }
199 }
200 #endif
201 }
203 void do_watch(struct connection *conn, struct buffered_data *in)
204 {
205 struct watch *watch;
206 char *vec[2];
207 bool relative;
209 if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec)) {
210 send_error(conn, EINVAL);
211 return;
212 }
214 if (strstarts(vec[0], "@")) {
215 relative = false;
216 /* check if valid event */
217 } else {
218 relative = !strstarts(vec[0], "/");
219 vec[0] = canonicalize(conn, vec[0]);
220 if (!is_valid_nodename(vec[0])) {
221 send_error(conn, errno);
222 return;
223 }
224 }
226 watch = talloc(conn, struct watch);
227 watch->node = talloc_strdup(watch, vec[0]);
228 watch->token = talloc_strdup(watch, vec[1]);
229 if (relative)
230 watch->relative_path = get_implicit_path(conn);
231 else
232 watch->relative_path = NULL;
234 INIT_LIST_HEAD(&watch->events);
236 list_add_tail(&watch->list, &conn->watches);
237 trace_create(watch, "watch");
238 talloc_set_destructor(watch, destroy_watch);
239 send_ack(conn, XS_WATCH);
240 }
242 void do_watch_ack(struct connection *conn, const char *token)
243 {
244 struct watch_event *event;
246 if (!token) {
247 send_error(conn, EINVAL);
248 return;
249 }
251 if (!conn->waiting_for_ack) {
252 send_error(conn, ENOENT);
253 return;
254 }
256 if (!streq(conn->waiting_for_ack->token, token)) {
257 /* They're confused: this will cause us to send event again */
258 conn->waiting_for_ack = NULL;
259 send_error(conn, EINVAL);
260 return;
261 }
263 /* Remove event: after ack sent, core will call queue_next_event */
264 event = list_top(&conn->waiting_for_ack->events, struct watch_event,
265 list);
266 list_del(&event->list);
267 talloc_free(event);
269 conn->waiting_for_ack = NULL;
270 send_ack(conn, XS_WATCH_ACK);
271 }
273 void do_unwatch(struct connection *conn, struct buffered_data *in)
274 {
275 struct watch *watch;
276 char *node, *vec[2];
278 if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec)) {
279 send_error(conn, EINVAL);
280 return;
281 }
283 /* We don't need to worry if we're waiting for an ack for the
284 * watch we're deleting: conn->waiting_for_ack was reset by
285 * this command in consider_message anyway. */
286 node = canonicalize(conn, vec[0]);
287 list_for_each_entry(watch, &conn->watches, list) {
288 if (streq(watch->node, node) && streq(watch->token, vec[1])) {
289 list_del(&watch->list);
290 talloc_free(watch);
291 send_ack(conn, XS_UNWATCH);
292 return;
293 }
294 }
295 send_error(conn, ENOENT);
296 }
298 #ifdef TESTING
299 void dump_watches(struct connection *conn)
300 {
301 struct watch *watch;
302 struct watch_event *event;
304 if (conn->waiting_for_ack)
305 printf(" waiting_for_ack for watch on %s token %s\n",
306 conn->waiting_for_ack->node,
307 conn->waiting_for_ack->token);
309 list_for_each_entry(watch, &conn->watches, list) {
310 printf(" watch on %s token %s\n",
311 watch->node, watch->token);
312 list_for_each_entry(event, &watch->events, list)
313 printf(" event: %s\n", event->data);
314 }
315 }
316 #endif