source: trunk/packages/xen-3.1/xen-3.1/tools/xenstore/xenstored_transaction.c @ 34

Last change on this file since 34 was 34, checked in by hartmans, 18 years ago

Add xen and xen-common

File size: 5.7 KB
Line 
1/*
2    Transaction code for Xen Store Daemon.
3    Copyright (C) 2005 Rusty Russell IBM Corporation
4
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.
9
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.
14
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*/
19
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 <assert.h>
27#include <stdarg.h>
28#include <stdlib.h>
29#include <fcntl.h>
30#include <unistd.h>
31#include "talloc.h"
32#include "list.h"
33#include "xenstored_transaction.h"
34#include "xenstored_watch.h"
35#include "xs_lib.h"
36#include "utils.h"
37#include "xenstored_test.h"
38
39struct changed_node
40{
41        /* List of all changed nodes in the context of this transaction. */
42        struct list_head list;
43
44        /* The name of the node. */
45        char *node;
46
47        /* And the children? (ie. rm) */
48        bool recurse;
49};
50
51struct transaction
52{
53        /* List of all transactions active on this connection. */
54        struct list_head list;
55
56        /* Connection-local identifier for this transaction. */
57        uint32_t id;
58
59        /* Generation when transaction started. */
60        unsigned int generation;
61
62        /* TDB to work on, and filename */
63        TDB_CONTEXT *tdb;
64        char *tdb_name;
65
66        /* List of changed nodes. */
67        struct list_head changes;
68};
69
70extern int quota_max_transaction;
71static unsigned int generation;
72
73/* Return tdb context to use for this connection. */
74TDB_CONTEXT *tdb_transaction_context(struct transaction *trans)
75{
76        return trans->tdb;
77}
78
79/* Callers get a change node (which can fail) and only commit after they've
80 * finished.  This way they don't have to unwind eg. a write. */
81void add_change_node(struct transaction *trans, const char *node, bool recurse)
82{
83        struct changed_node *i;
84
85        if (!trans) {
86                /* They're changing the global database. */
87                generation++;
88                return;
89        }
90
91        list_for_each_entry(i, &trans->changes, list)
92                if (streq(i->node, node))
93                        return;
94
95        i = talloc(trans, struct changed_node);
96        i->node = talloc_strdup(i, node);
97        i->recurse = recurse;
98        list_add_tail(&i->list, &trans->changes);
99}
100
101static int destroy_transaction(void *_transaction)
102{
103        struct transaction *trans = _transaction;
104
105        trace_destroy(trans, "transaction");
106        if (trans->tdb)
107                tdb_close(trans->tdb);
108        unlink(trans->tdb_name);
109        return 0;
110}
111
112struct transaction *transaction_lookup(struct connection *conn, uint32_t id)
113{
114        struct transaction *trans;
115
116        if (id == 0)
117                return NULL;
118
119        list_for_each_entry(trans, &conn->transaction_list, list)
120                if (trans->id == id)
121                        return trans;
122
123        return ERR_PTR(-ENOENT);
124}
125
126void do_transaction_start(struct connection *conn, struct buffered_data *in)
127{
128        struct transaction *trans, *exists;
129        char id_str[20];
130
131        /* We don't support nested transactions. */
132        if (conn->transaction) {
133                send_error(conn, EBUSY);
134                return;
135        }
136
137        if (conn->id && conn->transaction_started > quota_max_transaction) {
138                send_error(conn, ENOSPC);
139                return;
140        }
141
142        /* Attach transaction to input for autofree until it's complete */
143        trans = talloc(in, struct transaction);
144        INIT_LIST_HEAD(&trans->changes);
145        trans->generation = generation;
146        trans->tdb_name = talloc_asprintf(trans, "%s.%p",
147                                          xs_daemon_tdb(), trans);
148        trans->tdb = tdb_copy(tdb_context(conn), trans->tdb_name);
149        if (!trans->tdb) {
150                send_error(conn, errno);
151                return;
152        }
153        /* Make it close if we go away. */
154        talloc_steal(trans, trans->tdb);
155
156        /* Pick an unused transaction identifier. */
157        do {
158                trans->id = conn->next_transaction_id;
159                exists = transaction_lookup(conn, conn->next_transaction_id++);
160        } while (!IS_ERR(exists));
161
162        /* Now we own it. */
163        list_add_tail(&trans->list, &conn->transaction_list);
164        talloc_steal(conn, trans);
165        talloc_set_destructor(trans, destroy_transaction);
166        conn->transaction_started++;
167
168        sprintf(id_str, "%u", trans->id);
169        send_reply(conn, XS_TRANSACTION_START, id_str, strlen(id_str)+1);
170}
171
172void do_transaction_end(struct connection *conn, const char *arg)
173{
174        struct changed_node *i;
175        struct transaction *trans;
176
177        if (!arg || (!streq(arg, "T") && !streq(arg, "F"))) {
178                send_error(conn, EINVAL);
179                return;
180        }
181
182        if ((trans = conn->transaction) == NULL) {
183                send_error(conn, ENOENT);
184                return;
185        }
186
187        conn->transaction = NULL;
188        list_del(&trans->list);
189        conn->transaction_started--;
190
191        /* Attach transaction to arg for auto-cleanup */
192        talloc_steal(arg, trans);
193
194        if (streq(arg, "T")) {
195                /* FIXME: Merge, rather failing on any change. */
196                if (trans->generation != generation) {
197                        send_error(conn, EAGAIN);
198                        return;
199                }
200                if (!replace_tdb(trans->tdb_name, trans->tdb)) {
201                        send_error(conn, errno);
202                        return;
203                }
204                /* Don't close this: we won! */
205                trans->tdb = NULL;
206
207                /* Fire off the watches for everything that changed. */
208                list_for_each_entry(i, &trans->changes, list)
209                        fire_watches(conn, i->node, i->recurse);
210                generation++;
211        }
212        send_ack(conn, XS_TRANSACTION_END);
213}
214
215void conn_delete_all_transactions(struct connection *conn)
216{
217        struct transaction *trans;
218
219        while ((trans = list_top(&conn->transaction_list,
220                                 struct transaction, list))) {
221                list_del(&trans->list);
222                talloc_free(trans);
223        }
224
225        assert(conn->transaction == NULL);
226
227        conn->transaction_started = 0;
228}
229
230/*
231 * Local variables:
232 *  c-file-style: "linux"
233 *  indent-tabs-mode: t
234 *  c-indent-level: 8
235 *  c-basic-offset: 8
236 *  tab-width: 8
237 * End:
238 */
Note: See TracBrowser for help on using the repository browser.