ia64/xen-unstable

view tools/xenstore/xenstored_watch.c @ 7238:971e7c7411b3

Raise an exception if an error appears on the pipes to our children, and make
sure that the child's pipes are closed even under that exception. Move the
handling of POLLHUP to the end of the loop, so that we guarantee to read any
remaining data from the child if POLLHUP and POLLIN appear at the same time.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author emellor@ewan
date Thu Oct 06 10:13:11 2005 +0100 (2005-10-06)
parents ef9591d03fdd
children 93e27f7ca8a8 61b3b357d827 8016551fde98
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,
100 const char *name)
101 {
102 struct watch_event *event;
104 if (!check_event_node(name)) {
105 /* Can this conn load node, or see that it doesn't exist? */
106 struct node *node;
108 node = get_node(conn, name, XS_PERM_READ);
109 if (!node && errno != ENOENT)
110 return;
111 }
113 if (watch->relative_path) {
114 name += strlen(watch->relative_path);
115 if (*name == '/') /* Could be "" */
116 name++;
117 }
119 event = talloc(watch, struct watch_event);
120 event->len = strlen(name) + 1 + strlen(watch->token) + 1;
121 event->data = talloc_array(event, char, event->len);
122 strcpy(event->data, name);
123 strcpy(event->data + strlen(name) + 1, watch->token);
124 talloc_set_destructor(event, destroy_watch_event);
125 list_add_tail(&event->list, &watch->events);
126 trace_create(event, "watch_event");
127 }
129 /* FIXME: we fail to fire on out of memory. Should drop connections. */
130 void fire_watches(struct connection *conn, const char *name, bool recurse)
131 {
132 struct connection *i;
133 struct watch *watch;
135 /* During transactions, don't fire watches. */
136 if (conn && conn->transaction)
137 return;
139 /* Create an event for each watch. */
140 list_for_each_entry(i, &connections, list) {
141 list_for_each_entry(watch, &i->watches, list) {
142 if (is_child(name, watch->node))
143 add_event(i, watch, name);
144 else if (recurse && is_child(watch->node, name))
145 add_event(i, watch, watch->node);
146 else
147 continue;
148 /* If connection not doing anything, queue this. */
149 if (i->state == OK)
150 queue_next_event(i);
151 }
152 }
153 }
155 static int destroy_watch(void *_watch)
156 {
157 trace_destroy(_watch, "watch");
158 return 0;
159 }
161 void do_watch(struct connection *conn, struct buffered_data *in)
162 {
163 struct watch *watch;
164 char *vec[2];
165 bool relative;
167 if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec)) {
168 send_error(conn, EINVAL);
169 return;
170 }
172 if (strstarts(vec[0], "@")) {
173 relative = false;
174 /* check if valid event */
175 } else {
176 relative = !strstarts(vec[0], "/");
177 vec[0] = canonicalize(conn, vec[0]);
178 if (!is_valid_nodename(vec[0])) {
179 send_error(conn, errno);
180 return;
181 }
182 }
184 watch = talloc(conn, struct watch);
185 watch->node = talloc_strdup(watch, vec[0]);
186 watch->token = talloc_strdup(watch, vec[1]);
187 if (relative)
188 watch->relative_path = get_implicit_path(conn);
189 else
190 watch->relative_path = NULL;
192 INIT_LIST_HEAD(&watch->events);
194 list_add_tail(&watch->list, &conn->watches);
195 trace_create(watch, "watch");
196 talloc_set_destructor(watch, destroy_watch);
197 send_ack(conn, XS_WATCH);
199 /* We fire once up front: simplifies clients and restart. */
200 add_event(conn, watch, watch->node);
201 }
203 void do_watch_ack(struct connection *conn, const char *token)
204 {
205 struct watch_event *event;
207 if (!token) {
208 send_error(conn, EINVAL);
209 return;
210 }
212 if (!conn->waiting_for_ack) {
213 send_error(conn, ENOENT);
214 return;
215 }
217 if (!streq(conn->waiting_for_ack->token, token)) {
218 /* They're confused: this will cause us to send event again */
219 conn->waiting_for_ack = NULL;
220 send_error(conn, EINVAL);
221 return;
222 }
224 /* Remove event: after ack sent, core will call queue_next_event */
225 event = list_top(&conn->waiting_for_ack->events, struct watch_event,
226 list);
227 list_del(&event->list);
228 talloc_free(event);
230 conn->waiting_for_ack = NULL;
231 send_ack(conn, XS_WATCH_ACK);
232 }
234 void do_unwatch(struct connection *conn, struct buffered_data *in)
235 {
236 struct watch *watch;
237 char *node, *vec[2];
239 if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec)) {
240 send_error(conn, EINVAL);
241 return;
242 }
244 /* We don't need to worry if we're waiting for an ack for the
245 * watch we're deleting: conn->waiting_for_ack was reset by
246 * this command in consider_message anyway. */
247 node = canonicalize(conn, vec[0]);
248 list_for_each_entry(watch, &conn->watches, list) {
249 if (streq(watch->node, node) && streq(watch->token, vec[1])) {
250 list_del(&watch->list);
251 talloc_free(watch);
252 send_ack(conn, XS_UNWATCH);
253 return;
254 }
255 }
256 send_error(conn, ENOENT);
257 }
259 #ifdef TESTING
260 void dump_watches(struct connection *conn)
261 {
262 struct watch *watch;
263 struct watch_event *event;
265 if (conn->waiting_for_ack)
266 printf(" waiting_for_ack for watch on %s token %s\n",
267 conn->waiting_for_ack->node,
268 conn->waiting_for_ack->token);
270 list_for_each_entry(watch, &conn->watches, list) {
271 printf(" watch on %s token %s\n",
272 watch->node, watch->token);
273 list_for_each_entry(event, &watch->events, list)
274 printf(" event: %s\n", event->data);
275 }
276 }
277 #endif