ia64/xen-unstable

view tools/xenstore/xenstored_transaction.c @ 7329:74d56b7ff46c

Merged
author djm@kirby.fc.hp.com
date Tue Oct 11 16:57:44 2005 -0600 (2005-10-11)
parents 61b3b357d827 015f8ae81276
children b3a255e88810
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 u32 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 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 trace_destroy(trans, "transaction");
104 if (trans->tdb)
105 tdb_close(trans->tdb);
106 unlink(trans->tdb_name);
107 return 0;
108 }
110 struct transaction *transaction_lookup(struct connection *conn, u32 id)
111 {
112 struct transaction *trans;
114 if (id == 0)
115 return NULL;
117 list_for_each_entry(trans, &conn->transaction_list, list)
118 if (trans->id == id)
119 return trans;
121 return ERR_PTR(-ENOENT);
122 }
124 void do_transaction_start(struct connection *conn, struct buffered_data *in)
125 {
126 struct transaction *trans, *exists;
127 char id_str[20];
129 /* We don't support nested transactions. */
130 if (conn->transaction) {
131 send_error(conn, EBUSY);
132 return;
133 }
135 /* Attach transaction to input for autofree until it's complete */
136 trans = talloc(in, struct transaction);
137 INIT_LIST_HEAD(&trans->changes);
138 trans->generation = generation;
139 trans->tdb_name = talloc_asprintf(trans, "%s.%p",
140 xs_daemon_tdb(), trans);
141 trans->tdb = tdb_copy(tdb_context(conn), trans->tdb_name);
142 if (!trans->tdb) {
143 send_error(conn, errno);
144 return;
145 }
146 /* Make it close if we go away. */
147 talloc_steal(trans, trans->tdb);
149 /* Pick an unused transaction identifier. */
150 do {
151 trans->id = conn->next_transaction_id;
152 exists = transaction_lookup(conn, conn->next_transaction_id++);
153 } while (!IS_ERR(exists));
155 /* Now we own it. */
156 list_add_tail(&trans->list, &conn->transaction_list);
157 talloc_steal(conn, trans);
158 talloc_set_destructor(trans, destroy_transaction);
160 sprintf(id_str, "%u", trans->id);
161 send_reply(conn, XS_TRANSACTION_START, id_str, strlen(id_str)+1);
162 }
164 void do_transaction_end(struct connection *conn, const char *arg)
165 {
166 struct changed_node *i;
167 struct transaction *trans;
169 if (!arg || (!streq(arg, "T") && !streq(arg, "F"))) {
170 send_error(conn, EINVAL);
171 return;
172 }
174 if ((trans = conn->transaction) == NULL) {
175 send_error(conn, ENOENT);
176 return;
177 }
179 conn->transaction = NULL;
180 list_del(&trans->list);
182 /* Attach transaction to arg for auto-cleanup */
183 talloc_steal(arg, trans);
185 if (streq(arg, "T")) {
186 /* FIXME: Merge, rather failing on any change. */
187 if (trans->generation != generation) {
188 send_error(conn, EAGAIN);
189 return;
190 }
191 if (!replace_tdb(trans->tdb_name, trans->tdb)) {
192 send_error(conn, errno);
193 return;
194 }
195 /* Don't close this: we won! */
196 trans->tdb = NULL;
198 /* Fire off the watches for everything that changed. */
199 list_for_each_entry(i, &trans->changes, list)
200 fire_watches(conn, i->node, i->recurse);
201 generation++;
202 }
203 send_ack(conn, XS_TRANSACTION_END);
204 }
206 /*
207 * Local variables:
208 * c-file-style: "linux"
209 * indent-tabs-mode: t
210 * c-indent-level: 8
211 * c-basic-offset: 8
212 * tab-width: 8
213 * End:
214 */