aboutsummaryrefslogtreecommitdiffstats
path: root/tools/xenstore/xenstored_core.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/xenstore/xenstored_core.c')
-rw-r--r--tools/xenstore/xenstored_core.c126
1 files changed, 102 insertions, 24 deletions
diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c
index 1df00f37b4..9f7ff2e1ce 100644
--- a/tools/xenstore/xenstored_core.c
+++ b/tools/xenstore/xenstored_core.c
@@ -122,6 +122,33 @@ void __attribute__((noreturn)) corrupt(struct connection *conn,
_exit(2);
}
+static char *sockmsg_string(enum xsd_sockmsg_type type)
+{
+ switch (type) {
+ case XS_DEBUG: return "DEBUG";
+ case XS_SHUTDOWN: return "SHUTDOWN";
+ case XS_DIRECTORY: return "DIRECTORY";
+ case XS_READ: return "READ";
+ case XS_GET_PERMS: return "GET_PERMS";
+ case XS_WATCH: return "WATCH";
+ case XS_WATCH_ACK: return "WATCH_ACK";
+ case XS_UNWATCH: return "UNWATCH";
+ case XS_TRANSACTION_START: return "TRANSACTION_START";
+ case XS_TRANSACTION_END: return "TRANSACTION_END";
+ case XS_INTRODUCE: return "INTRODUCE";
+ case XS_RELEASE: return "RELEASE";
+ case XS_GETDOMAINPATH: return "GETDOMAINPATH";
+ case XS_WRITE: return "WRITE";
+ case XS_MKDIR: return "MKDIR";
+ case XS_RM: return "RM";
+ case XS_SET_PERMS: return "SET_PERMS";
+ case XS_WATCH_EVENT: return "WATCH_EVENT";
+ case XS_ERROR: return "ERROR";
+ default:
+ return "**UNKNOWN**";
+ }
+}
+
static bool write_message(struct connection *conn)
{
int ret;
@@ -129,8 +156,9 @@ static bool write_message(struct connection *conn)
if (out->inhdr) {
if (verbose)
- xprintf("Writing msg %i out to %p\n",
- out->hdr.msg.type, conn);
+ xprintf("Writing msg %s (%s) out to %p\n",
+ sockmsg_string(out->hdr.msg.type),
+ out->buffer, conn);
ret = conn->write(conn, out->hdr.raw + out->used,
sizeof(out->hdr) - out->used);
if (ret < 0)
@@ -148,9 +176,6 @@ static bool write_message(struct connection *conn)
return true;
}
- if (verbose)
- xprintf("Writing data len %i out to %p\n",
- out->hdr.msg.len, conn);
ret = conn->write(conn, out->buffer + out->used,
out->hdr.msg.len - out->used);
@@ -162,10 +187,7 @@ static bool write_message(struct connection *conn)
return true;
conn->out = NULL;
-
- /* If this was an event, we wait for ack, otherwise we're done. */
- if (!is_watch_event(conn, out))
- talloc_free(out);
+ talloc_free(out);
queue_next_event(conn);
return true;
@@ -402,7 +424,7 @@ static bool valid_chars(const char *node)
"0123456789-/_@") == strlen(node));
}
-static bool is_valid_nodename(const char *node)
+bool is_valid_nodename(const char *node)
{
/* Must start in /. */
if (!strstarts(node, "/"))
@@ -601,17 +623,24 @@ static int check_with_parents(struct connection *conn, const char *node,
return errnum;
}
+char *canonicalize(struct connection *conn, const char *node)
+{
+ const char *prefix;
+
+ if (!node || strstarts(node, "/"))
+ return (char *)node;
+ prefix = get_implicit_path(conn);
+ if (prefix)
+ return talloc_asprintf(node, "%s/%s", prefix, node);
+ return (char *)node;
+}
+
bool check_node_perms(struct connection *conn, const char *node,
enum xs_perm_type perm)
{
struct xs_permissions *perms;
unsigned int num;
- if (!node) {
- errno = EINVAL;
- return false;
- }
-
if (!node || !is_valid_nodename(node)) {
errno = EINVAL;
return false;
@@ -651,6 +680,7 @@ static bool send_directory(struct connection *conn, const char *node)
DIR *dir;
struct dirent *dirent;
+ node = canonicalize(conn, node);
if (!check_node_perms(conn, node, XS_PERM_READ))
return send_error(conn, errno);
@@ -680,6 +710,7 @@ static bool do_read(struct connection *conn, const char *node)
unsigned int size;
int *fd;
+ node = canonicalize(conn, node);
if (!check_node_perms(conn, node, XS_PERM_READ))
return send_error(conn, errno);
@@ -750,7 +781,7 @@ static bool do_write(struct connection *conn, struct buffered_data *in)
if (get_strings(in, vec, ARRAY_SIZE(vec)) < ARRAY_SIZE(vec))
return send_error(conn, EINVAL);
- node = vec[0];
+ node = canonicalize(conn, vec[0]);
if (!within_transaction(conn->transaction, node))
return send_error(conn, EROFS);
@@ -804,6 +835,7 @@ static bool do_write(struct connection *conn, struct buffered_data *in)
static bool do_mkdir(struct connection *conn, const char *node)
{
+ node = canonicalize(conn, node);
if (!check_node_perms(conn, node, XS_PERM_WRITE|XS_PERM_CREATE))
return send_error(conn, errno);
@@ -826,6 +858,7 @@ static bool do_rm(struct connection *conn, const char *node)
{
char *tmppath, *path;
+ node = canonicalize(conn, node);
if (!check_node_perms(conn, node, XS_PERM_WRITE))
return send_error(conn, errno);
@@ -848,6 +881,7 @@ static bool do_rm(struct connection *conn, const char *node)
add_change_node(conn->transaction, node);
send_ack(conn, XS_RM);
+ /* FIXME: traverse and fire watches for ALL of them! */
fire_watches(conn->transaction, node);
return false;
}
@@ -858,6 +892,7 @@ static bool do_get_perms(struct connection *conn, const char *node)
char *strings;
unsigned int len, num;
+ node = canonicalize(conn, node);
if (!check_node_perms(conn, node, XS_PERM_READ))
return send_error(conn, errno);
@@ -883,7 +918,7 @@ static bool do_set_perms(struct connection *conn, struct buffered_data *in)
return send_error(conn, EINVAL);
/* First arg is node name. */
- node = in->buffer;
+ node = canonicalize(conn, in->buffer);
in->buffer += strlen(in->buffer) + 1;
num--;
@@ -968,10 +1003,10 @@ static bool process_message(struct connection *conn, struct buffered_data *in)
return do_watch(conn, in);
case XS_WATCH_ACK:
- return do_watch_ack(conn);
+ return do_watch_ack(conn, onearg(in));
case XS_UNWATCH:
- return do_unwatch(conn, onearg(in));
+ return do_unwatch(conn, in);
case XS_TRANSACTION_START:
return do_transaction_start(conn, onearg(in));
@@ -1015,13 +1050,13 @@ static void consider_message(struct connection *conn)
}
if (verbose)
- xprintf("Got message %i len %i from %p\n",
- type, conn->in->hdr.msg.len, conn);
+ xprintf("Got message %s len %i from %p\n",
+ sockmsg_string(type), conn->in->hdr.msg.len, conn);
/* We might get a command while waiting for an ack: this means
* the other end discarded it: we will re-transmit. */
if (type != XS_WATCH_ACK)
- reset_watch_event(conn);
+ conn->waiting_for_ack = false;
/* Careful: process_message may free connection. We detach
* "in" beforehand and allocate the new buffer to avoid
@@ -1136,7 +1171,6 @@ struct connection *new_connection(connwritefn_t *write, connreadfn_t *read)
new->blocked = false;
new->out = new->waiting_reply = NULL;
- new->event = NULL;
new->fd = -1;
new->id = 0;
new->domain = NULL;
@@ -1203,6 +1237,42 @@ static void time_relative_to_now(struct timeval *tv)
}
}
+#ifdef TESTING
+/* Useful for running under debugger. */
+void dump_connection(void)
+{
+ struct connection *i;
+
+ list_for_each_entry(i, &connections, list) {
+ printf("Connection %p:\n", i);
+ if (i->id)
+ printf(" id = %i\n", i->id);
+ if (i->blocked)
+ printf(" blocked on = %s\n", i->blocked);
+ if (i->waiting_for_ack)
+ printf(" waiting_for_ack TRUE\n");
+ if (!i->in->inhdr || i->in->used)
+ printf(" got %i bytes of %s\n",
+ i->in->used, i->in->inhdr ? "header" : "data");
+ if (i->out)
+ printf(" sending message %s (%s) out\n",
+ sockmsg_string(i->out->hdr.msg.type),
+ i->out->buffer);
+ if (i->waiting_reply)
+ printf(" ... and behind is queued %s (%s)\n",
+ sockmsg_string(i->waiting_reply->hdr.msg.type),
+ i->waiting_reply->buffer);
+#if 0
+ if (i->transaction)
+ dump_transaction(i);
+ if (i->domain)
+ dump_domain(i);
+#endif
+ dump_watches(i);
+ }
+}
+#endif
+
static struct option options[] = { { "no-fork", 0, NULL, 'N' },
{ "verbose", 0, NULL, 'V' },
{ "output-pid", 0, NULL, 'P' },
@@ -1314,6 +1384,7 @@ int main(int argc, char *argv[])
timerclear(&tv);
shortest_transaction_timeout(&tv);
+ shortest_watch_ack_timeout(&tv);
if (timerisset(&tv)) {
time_relative_to_now(&tv);
tvp = &tv;
@@ -1351,8 +1422,15 @@ int main(int argc, char *argv[])
}
}
- if (tvp)
+ /* Flush output for domain connections, */
+ list_for_each_entry(i, &connections, list)
+ if (i->domain && i->out)
+ handle_output(i);
+
+ if (tvp) {
check_transaction_timeout();
+ check_watch_ack_timeout();
+ }
/* If transactions ended, we might be able to do more work. */
unblock_connections();