ia64/xen-unstable

view tools/xenstore/xenstored_transaction.c @ 10878:c471b326b75e

Add a transaction_started field in xenstored connection structure instead of
browsing the list of transaction each time
Bump the default to 10, and make it configurable through the command line.

Signed-off-by: Vincent Hanquez <vincent@xensource.com>
author vhanquez@gwig.uk.xensource.com
date Mon Jul 31 09:30:36 2006 +0000 (2006-07-31)
parents d2bf1a7cc131
children b0ee6789e428
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 /* List of all changed nodes in the context of 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 /* List of all transactions active on this connection. */
53 struct list_head list;
55 /* Connection-local identifier for this transaction. */
56 uint32_t id;
58 /* Generation when transaction started. */
59 unsigned int generation;
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 };
69 extern int quota_max_transaction;
70 static unsigned int generation;
72 /* Return tdb context to use for this connection. */
73 TDB_CONTEXT *tdb_transaction_context(struct transaction *trans)
74 {
75 return trans->tdb;
76 }
78 /* Callers get a change node (which can fail) and only commit after they've
79 * finished. This way they don't have to unwind eg. a write. */
80 void add_change_node(struct transaction *trans, const char *node, bool recurse)
81 {
82 struct changed_node *i;
84 if (!trans) {
85 /* They're changing the global database. */
86 generation++;
87 return;
88 }
90 list_for_each_entry(i, &trans->changes, list)
91 if (streq(i->node, node))
92 return;
94 i = talloc(trans, struct changed_node);
95 i->node = talloc_strdup(i, node);
96 i->recurse = recurse;
97 list_add_tail(&i->list, &trans->changes);
98 }
100 static int destroy_transaction(void *_transaction)
101 {
102 struct transaction *trans = _transaction;
104 trace_destroy(trans, "transaction");
105 if (trans->tdb)
106 tdb_close(trans->tdb);
107 unlink(trans->tdb_name);
108 return 0;
109 }
111 struct transaction *transaction_lookup(struct connection *conn, uint32_t id)
112 {
113 struct transaction *trans;
115 if (id == 0)
116 return NULL;
118 list_for_each_entry(trans, &conn->transaction_list, list)
119 if (trans->id == id)
120 return trans;
122 return ERR_PTR(-ENOENT);
123 }
125 void do_transaction_start(struct connection *conn, struct buffered_data *in)
126 {
127 struct transaction *trans, *exists;
128 char id_str[20];
130 /* We don't support nested transactions. */
131 if (conn->transaction) {
132 send_error(conn, EBUSY);
133 return;
134 }
136 if (conn->transaction_started > quota_max_transaction) {
137 send_error(conn, ENOSPC);
138 return;
139 }
141 /* Attach transaction to input for autofree until it's complete */
142 trans = talloc(in, struct transaction);
143 INIT_LIST_HEAD(&trans->changes);
144 trans->generation = generation;
145 trans->tdb_name = talloc_asprintf(trans, "%s.%p",
146 xs_daemon_tdb(), trans);
147 trans->tdb = tdb_copy(tdb_context(conn), trans->tdb_name);
148 if (!trans->tdb) {
149 send_error(conn, errno);
150 return;
151 }
152 /* Make it close if we go away. */
153 talloc_steal(trans, trans->tdb);
155 /* Pick an unused transaction identifier. */
156 do {
157 trans->id = conn->next_transaction_id;
158 exists = transaction_lookup(conn, conn->next_transaction_id++);
159 } while (!IS_ERR(exists));
161 /* Now we own it. */
162 list_add_tail(&trans->list, &conn->transaction_list);
163 talloc_steal(conn, trans);
164 talloc_set_destructor(trans, destroy_transaction);
165 conn->transaction_started++;
167 sprintf(id_str, "%u", trans->id);
168 send_reply(conn, XS_TRANSACTION_START, id_str, strlen(id_str)+1);
169 }
171 void do_transaction_end(struct connection *conn, const char *arg)
172 {
173 struct changed_node *i;
174 struct transaction *trans;
176 if (!arg || (!streq(arg, "T") && !streq(arg, "F"))) {
177 send_error(conn, EINVAL);
178 return;
179 }
181 if ((trans = conn->transaction) == NULL) {
182 send_error(conn, ENOENT);
183 return;
184 }
186 conn->transaction = NULL;
187 list_del(&trans->list);
188 conn->transaction_started--;
190 /* Attach transaction to arg for auto-cleanup */
191 talloc_steal(arg, trans);
193 if (streq(arg, "T")) {
194 /* FIXME: Merge, rather failing on any change. */
195 if (trans->generation != generation) {
196 send_error(conn, EAGAIN);
197 return;
198 }
199 if (!replace_tdb(trans->tdb_name, trans->tdb)) {
200 send_error(conn, errno);
201 return;
202 }
203 /* Don't close this: we won! */
204 trans->tdb = NULL;
206 /* Fire off the watches for everything that changed. */
207 list_for_each_entry(i, &trans->changes, list)
208 fire_watches(conn, i->node, i->recurse);
209 generation++;
210 }
211 send_ack(conn, XS_TRANSACTION_END);
212 }
214 /*
215 * Local variables:
216 * c-file-style: "linux"
217 * indent-tabs-mode: t
218 * c-indent-level: 8
219 * c-basic-offset: 8
220 * tab-width: 8
221 * End:
222 */