ia64/xen-unstable

view tools/xenstore/xenstored_transaction.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 b7dce4fe2488
line source
1 /*
2 Transaction 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 <sys/stat.h>
23 #include <sys/wait.h>
24 #include <sys/time.h>
25 #include <time.h>
26 #include <stdarg.h>
27 #include <stdlib.h>
28 #include <fcntl.h>
29 #include <unistd.h>
30 #include "talloc.h"
31 #include "list.h"
32 #include "xenstored_transaction.h"
33 #include "xenstored_watch.h"
34 #include "xs_lib.h"
35 #include "utils.h"
36 #include "xenstored_test.h"
38 struct changed_node
39 {
40 /* The list within this transaction. */
41 struct list_head list;
43 /* The name of the node. */
44 char *node;
46 /* And the children? (ie. rm) */
47 bool recurse;
48 };
50 struct transaction
51 {
52 /* Global list of transactions. */
53 struct list_head list;
55 /* Generation when transaction started. */
56 unsigned int generation;
58 /* My owner (conn->transaction == me). */
59 struct connection *conn;
61 /* TDB to work on, and filename */
62 TDB_CONTEXT *tdb;
63 char *tdb_name;
65 /* List of changed nodes. */
66 struct list_head changes;
67 };
68 static LIST_HEAD(transactions);
69 static unsigned int generation;
71 /* Return tdb context to use for this connection. */
72 TDB_CONTEXT *tdb_transaction_context(struct transaction *trans)
73 {
74 return trans->tdb;
75 }
77 /* Callers get a change node (which can fail) and only commit after they've
78 * finished. This way they don't have to unwind eg. a write. */
79 void add_change_node(struct transaction *trans, const char *node, bool recurse)
80 {
81 struct changed_node *i;
83 if (!trans) {
84 /* They're changing the global database. */
85 generation++;
86 return;
87 }
89 list_for_each_entry(i, &trans->changes, list)
90 if (streq(i->node, node))
91 return;
93 i = talloc(trans, struct changed_node);
94 i->node = talloc_strdup(i, node);
95 i->recurse = recurse;
96 list_add_tail(&i->list, &trans->changes);
97 }
99 static int destroy_transaction(void *_transaction)
100 {
101 struct transaction *trans = _transaction;
103 list_del(&trans->list);
104 trace_destroy(trans, "transaction");
105 if (trans->tdb)
106 tdb_close(trans->tdb);
107 unlink(trans->tdb_name);
108 return 0;
109 }
111 void do_transaction_start(struct connection *conn, struct buffered_data *in)
112 {
113 struct transaction *trans;
115 if (conn->transaction) {
116 send_error(conn, EBUSY);
117 return;
118 }
120 /* Attach transaction to input for autofree until it's complete */
121 trans = talloc(in, struct transaction);
122 INIT_LIST_HEAD(&trans->changes);
123 trans->conn = conn;
124 trans->generation = generation;
125 trans->tdb_name = talloc_asprintf(trans, "%s.%p",
126 xs_daemon_tdb(), trans);
127 trans->tdb = tdb_copy(tdb_context(conn), trans->tdb_name);
128 if (!trans->tdb) {
129 send_error(conn, errno);
130 return;
131 }
132 /* Make it close if we go away. */
133 talloc_steal(trans, trans->tdb);
135 /* Now we own it. */
136 conn->transaction = talloc_steal(conn, trans);
137 list_add_tail(&trans->list, &transactions);
138 talloc_set_destructor(trans, destroy_transaction);
139 send_ack(conn, XS_TRANSACTION_START);
140 }
142 void do_transaction_end(struct connection *conn, const char *arg)
143 {
144 struct changed_node *i;
145 struct transaction *trans;
147 if (!arg || (!streq(arg, "T") && !streq(arg, "F"))) {
148 send_error(conn, EINVAL);
149 return;
150 }
152 if (!conn->transaction) {
153 send_error(conn, ENOENT);
154 return;
155 }
157 /* Set to NULL so fire_watches sends events, tdb_context works. */
158 trans = conn->transaction;
159 conn->transaction = NULL;
160 /* Attach transaction to arg for auto-cleanup */
161 talloc_steal(arg, trans);
163 if (streq(arg, "T")) {
164 /* FIXME: Merge, rather failing on any change. */
165 if (trans->generation != generation) {
166 send_error(conn, EAGAIN);
167 return;
168 }
169 if (!replace_tdb(trans->tdb_name, trans->tdb)) {
170 send_error(conn, errno);
171 return;
172 }
173 /* Don't close this: we won! */
174 trans->tdb = NULL;
176 /* Fire off the watches for everything that changed. */
177 list_for_each_entry(i, &trans->changes, list)
178 fire_watches(conn, i->node, i->recurse);
179 generation++;
180 }
181 send_ack(conn, XS_TRANSACTION_END);
182 }