Files
xen/tools/xenstore/xenstored_transaction.c
T
Ian Campbell debddaa56c nstore: rename public xenstore headers
The xenstore header xs.h is producing conflicts with other software[1].

xs is a too short identifier and does not matche the library. Renaming
the headers to xenstore.h and xenstore_lib.h is the easiest way to make
them easy recognizable and prevent furthe problems.

[1]: http://bugs.debian.org/668550

[ Also update QEMU_TAG, to bring in corresponding change to
qemu-xen-traditional. -iwj ]

Signed-off-by: Bastian Blank <waldi@debian.org>
Signed-off-by: Ian Campbell <ian.campbell@citrix.com>
Acked-by: Ian Jackson <ian.jackson@eu.citrix.com>
Committed-by: Ian Jackson <ian.jackson@eu.citrix.com>

--HG--
rename : tools/xenstore/xs.h => tools/xenstore/xenstore.h
rename : tools/xenstore/xs_lib.h => tools/xenstore/xenstore_lib.h
2012-05-14 17:15:36 +01:00

292 lines
7.0 KiB
C

/*
Transaction code for Xen Store Daemon.
Copyright (C) 2005 Rusty Russell IBM Corporation
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <time.h>
#include <assert.h>
#include <stdarg.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include "talloc.h"
#include "list.h"
#include "xenstored_transaction.h"
#include "xenstored_watch.h"
#include "xenstored_domain.h"
#include "xenstore_lib.h"
#include "utils.h"
struct changed_node
{
/* List of all changed nodes in the context of this transaction. */
struct list_head list;
/* The name of the node. */
char *node;
/* And the children? (ie. rm) */
bool recurse;
};
struct changed_domain
{
/* List of all changed domains in the context of this transaction. */
struct list_head list;
/* Identifier of the changed domain. */
unsigned int domid;
/* Amount by which this domain's nbentry field has changed. */
int nbentry;
};
struct transaction
{
/* List of all transactions active on this connection. */
struct list_head list;
/* Connection-local identifier for this transaction. */
uint32_t id;
/* Generation when transaction started. */
unsigned int generation;
/* TDB to work on, and filename */
TDB_CONTEXT *tdb;
char *tdb_name;
/* List of changed nodes. */
struct list_head changes;
/* List of changed domains - to record the changed domain entry number */
struct list_head changed_domains;
};
extern int quota_max_transaction;
static unsigned int generation;
/* Return tdb context to use for this connection. */
TDB_CONTEXT *tdb_transaction_context(struct transaction *trans)
{
return trans->tdb;
}
/* Callers get a change node (which can fail) and only commit after they've
* finished. This way they don't have to unwind eg. a write. */
void add_change_node(struct transaction *trans, const char *node, bool recurse)
{
struct changed_node *i;
if (!trans) {
/* They're changing the global database. */
generation++;
return;
}
list_for_each_entry(i, &trans->changes, list)
if (streq(i->node, node))
return;
i = talloc(trans, struct changed_node);
i->node = talloc_strdup(i, node);
i->recurse = recurse;
list_add_tail(&i->list, &trans->changes);
}
static int destroy_transaction(void *_transaction)
{
struct transaction *trans = _transaction;
trace_destroy(trans, "transaction");
if (trans->tdb)
tdb_close(trans->tdb);
unlink(trans->tdb_name);
return 0;
}
struct transaction *transaction_lookup(struct connection *conn, uint32_t id)
{
struct transaction *trans;
if (id == 0)
return NULL;
list_for_each_entry(trans, &conn->transaction_list, list)
if (trans->id == id)
return trans;
return ERR_PTR(-ENOENT);
}
void do_transaction_start(struct connection *conn, struct buffered_data *in)
{
struct transaction *trans, *exists;
char id_str[20];
/* We don't support nested transactions. */
if (conn->transaction) {
send_error(conn, EBUSY);
return;
}
if (conn->id && conn->transaction_started > quota_max_transaction) {
send_error(conn, ENOSPC);
return;
}
/* Attach transaction to input for autofree until it's complete */
trans = talloc(in, struct transaction);
INIT_LIST_HEAD(&trans->changes);
INIT_LIST_HEAD(&trans->changed_domains);
trans->generation = generation;
trans->tdb_name = talloc_asprintf(trans, "%s.%p",
xs_daemon_tdb(), trans);
trans->tdb = tdb_copy(tdb_context(conn), trans->tdb_name);
if (!trans->tdb) {
send_error(conn, errno);
return;
}
/* Make it close if we go away. */
talloc_steal(trans, trans->tdb);
/* Pick an unused transaction identifier. */
do {
trans->id = conn->next_transaction_id;
exists = transaction_lookup(conn, conn->next_transaction_id++);
} while (!IS_ERR(exists));
/* Now we own it. */
list_add_tail(&trans->list, &conn->transaction_list);
talloc_steal(conn, trans);
talloc_set_destructor(trans, destroy_transaction);
conn->transaction_started++;
snprintf(id_str, sizeof(id_str), "%u", trans->id);
send_reply(conn, XS_TRANSACTION_START, id_str, strlen(id_str)+1);
}
void do_transaction_end(struct connection *conn, const char *arg)
{
struct changed_node *i;
struct changed_domain *d;
struct transaction *trans;
if (!arg || (!streq(arg, "T") && !streq(arg, "F"))) {
send_error(conn, EINVAL);
return;
}
if ((trans = conn->transaction) == NULL) {
send_error(conn, ENOENT);
return;
}
conn->transaction = NULL;
list_del(&trans->list);
conn->transaction_started--;
/* Attach transaction to arg for auto-cleanup */
talloc_steal(arg, trans);
if (streq(arg, "T")) {
/* FIXME: Merge, rather failing on any change. */
if (trans->generation != generation) {
send_error(conn, EAGAIN);
return;
}
if (!replace_tdb(trans->tdb_name, trans->tdb)) {
send_error(conn, errno);
return;
}
/* Don't close this: we won! */
trans->tdb = NULL;
/* fix domain entry for each changed domain */
list_for_each_entry(d, &trans->changed_domains, list)
domain_entry_fix(d->domid, d->nbentry);
/* Fire off the watches for everything that changed. */
list_for_each_entry(i, &trans->changes, list)
fire_watches(conn, i->node, i->recurse);
generation++;
}
send_ack(conn, XS_TRANSACTION_END);
}
void transaction_entry_inc(struct transaction *trans, unsigned int domid)
{
struct changed_domain *d;
list_for_each_entry(d, &trans->changed_domains, list)
if (d->domid == domid) {
d->nbentry++;
return;
}
d = talloc(trans, struct changed_domain);
d->domid = domid;
d->nbentry = 1;
list_add_tail(&d->list, &trans->changed_domains);
}
void transaction_entry_dec(struct transaction *trans, unsigned int domid)
{
struct changed_domain *d;
list_for_each_entry(d, &trans->changed_domains, list)
if (d->domid == domid) {
d->nbentry--;
return;
}
d = talloc(trans, struct changed_domain);
d->domid = domid;
d->nbentry = -1;
list_add_tail(&d->list, &trans->changed_domains);
}
void conn_delete_all_transactions(struct connection *conn)
{
struct transaction *trans;
while ((trans = list_top(&conn->transaction_list,
struct transaction, list))) {
list_del(&trans->list);
talloc_free(trans);
}
assert(conn->transaction == NULL);
conn->transaction_started = 0;
}
/*
* Local variables:
* c-file-style: "linux"
* indent-tabs-mode: t
* c-indent-level: 8
* c-basic-offset: 8
* tab-width: 8
* End:
*/