mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 10:11:27 +01:00
xenstore: handle memory allocation failures in xenstored
Check for failures when allocating new memory in xenstored. Signed-off-by: Juergen Gross <jgross@suse.com> Acked-by: Wei Liu <wei.liu2@citrix.com>
This commit is contained in:
1 parent
cc06124648
commit
d2e41f6dc3
4 files changed
+211
-47
No files matched your search
+163
-47
@@ -149,8 +149,10 @@ void trace(const char *fmt, ...)
|
||||
va_start(arglist, fmt);
|
||||
str = talloc_vasprintf(NULL, fmt, arglist);
|
||||
va_end(arglist);
|
||||
dummy = write(tracefd, str, strlen(str));
|
||||
talloc_free(str);
|
||||
if (str) {
|
||||
dummy = write(tracefd, str, strlen(str));
|
||||
talloc_free(str);
|
||||
}
|
||||
}
|
||||
|
||||
static void trace_io(const struct connection *conn,
|
||||
@@ -392,7 +394,16 @@ static struct node *read_node(struct connection *conn, const void *ctx,
|
||||
}
|
||||
|
||||
node = talloc(ctx, struct node);
|
||||
if (!node) {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
node->name = talloc_strdup(node, name);
|
||||
if (!node->name) {
|
||||
talloc_free(node);
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
node->parent = NULL;
|
||||
node->tdb = tdb_context(conn);
|
||||
talloc_steal(node, data.dptr);
|
||||
@@ -490,35 +501,46 @@ static enum xs_perm_type perm_for_conn(struct connection *conn,
|
||||
*/
|
||||
static char *get_parent(const void *ctx, const char *node)
|
||||
{
|
||||
char *parent;
|
||||
char *slash = strrchr(node + 1, '/');
|
||||
if (!slash)
|
||||
return talloc_strdup(ctx, "/");
|
||||
return talloc_asprintf(ctx, "%.*s", (int)(slash - node), node);
|
||||
|
||||
parent = slash ? talloc_asprintf(ctx, "%.*s", (int)(slash - node), node)
|
||||
: talloc_strdup(ctx, "/");
|
||||
if (!parent)
|
||||
errno = ENOMEM;
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
/*
|
||||
* What do parents say?
|
||||
* Temporary memory allocations are done with ctx.
|
||||
*/
|
||||
static enum xs_perm_type ask_parents(struct connection *conn, const void *ctx,
|
||||
const char *name)
|
||||
static int ask_parents(struct connection *conn, const void *ctx,
|
||||
const char *name, enum xs_perm_type *perm)
|
||||
{
|
||||
struct node *node;
|
||||
|
||||
do {
|
||||
name = get_parent(ctx, name);
|
||||
if (!name)
|
||||
return errno;
|
||||
node = read_node(conn, ctx, name);
|
||||
if (node)
|
||||
break;
|
||||
if (errno == ENOMEM)
|
||||
return errno;
|
||||
} while (!streq(name, "/"));
|
||||
|
||||
/* No permission at root? We're in trouble. */
|
||||
if (!node) {
|
||||
corrupt(conn, "No permissions file at root");
|
||||
return XS_PERM_NONE;
|
||||
*perm = XS_PERM_NONE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return perm_for_conn(conn, node->perms, node->num_perms);
|
||||
*perm = perm_for_conn(conn, node->perms, node->num_perms);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -532,11 +554,15 @@ static int errno_from_parents(struct connection *conn, const void *ctx,
|
||||
const char *node, int errnum,
|
||||
enum xs_perm_type perm)
|
||||
{
|
||||
enum xs_perm_type parent_perm = XS_PERM_NONE;
|
||||
|
||||
/* We always tell them about memory failures. */
|
||||
if (errnum == ENOMEM)
|
||||
return errnum;
|
||||
|
||||
if (ask_parents(conn, ctx, node) & perm)
|
||||
if (ask_parents(conn, ctx, node, &parent_perm))
|
||||
return errno;
|
||||
if (parent_perm & perm)
|
||||
return errnum;
|
||||
return EACCES;
|
||||
}
|
||||
@@ -566,7 +592,7 @@ struct node *get_node(struct connection *conn,
|
||||
}
|
||||
}
|
||||
/* Clean up errno if they weren't supposed to know. */
|
||||
if (!node)
|
||||
if (!node && errno != ENOMEM)
|
||||
errno = errno_from_parents(conn, ctx, name, errno, perm);
|
||||
return node;
|
||||
}
|
||||
@@ -656,11 +682,29 @@ void send_reply(struct connection *conn, enum xsd_sockmsg_type type,
|
||||
} else {
|
||||
/* Message is a child of the connection for auto-cleanup. */
|
||||
bdata = new_buffer(conn);
|
||||
|
||||
/*
|
||||
* Allocation failure here is unfortunate: we have no way to
|
||||
* tell anybody about it.
|
||||
*/
|
||||
if (!bdata)
|
||||
return;
|
||||
}
|
||||
if (len <= DEFAULT_BUFFER_SIZE)
|
||||
bdata->buffer = bdata->default_buffer;
|
||||
else
|
||||
bdata->buffer = talloc_array(bdata, char, len);
|
||||
if (!bdata->buffer) {
|
||||
if (type == XS_WATCH_EVENT) {
|
||||
/* Same as above: no way to tell someone. */
|
||||
talloc_free(bdata);
|
||||
return;
|
||||
}
|
||||
/* re-establish request buffer for sending ENOMEM. */
|
||||
conn->in = bdata;
|
||||
send_error(conn, ENOMEM);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Update relevant header fields and fill in the message body. */
|
||||
bdata->hdr.msg.type = type;
|
||||
@@ -669,6 +713,8 @@ void send_reply(struct connection *conn, enum xsd_sockmsg_type type,
|
||||
|
||||
/* Queue for later transmission. */
|
||||
list_add_tail(&bdata->list, &conn->out_list);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Some routines (write, mkdir, etc) just need a non-error return */
|
||||
@@ -730,6 +776,8 @@ static char *perms_to_strings(const void *ctx,
|
||||
|
||||
strings = talloc_realloc(ctx, strings, char,
|
||||
*len + strlen(buffer) + 1);
|
||||
if (!strings)
|
||||
return NULL;
|
||||
strcpy(strings + *len, buffer);
|
||||
*len += strlen(buffer) + 1;
|
||||
}
|
||||
@@ -876,6 +924,9 @@ static struct node *construct_node(struct connection *conn, const void *ctx,
|
||||
struct node *parent, *node;
|
||||
char *children, *parentname = get_parent(ctx, name);
|
||||
|
||||
if (!parentname)
|
||||
return NULL;
|
||||
|
||||
/* If parent doesn't exist, create it. */
|
||||
parent = read_node(conn, parentname, parentname);
|
||||
if (!parent)
|
||||
@@ -1023,6 +1074,7 @@ static void delete_node(struct connection *conn, struct node *node,
|
||||
bool changed)
|
||||
{
|
||||
unsigned int i;
|
||||
char *name;
|
||||
|
||||
/* Delete self, then delete children. If we crash, then the worst
|
||||
that can happen is the children will continue to take up space, but
|
||||
@@ -1033,17 +1085,18 @@ static void delete_node(struct connection *conn, struct node *node,
|
||||
for (i = 0; i < node->childlen; i += strlen(node->children+i) + 1) {
|
||||
struct node *child;
|
||||
|
||||
child = read_node(conn, node,
|
||||
talloc_asprintf(node, "%s/%s", node->name,
|
||||
node->children + i));
|
||||
name = talloc_asprintf(node, "%s/%s", node->name,
|
||||
node->children + i);
|
||||
child = name ? read_node(conn, node, name) : NULL;
|
||||
if (child) {
|
||||
delete_node(conn, child, false);
|
||||
}
|
||||
else {
|
||||
trace("delete_node: No child '%s/%s' found!\n",
|
||||
trace("delete_node: Error deleting child '%s/%s'!\n",
|
||||
node->name, node->children + i);
|
||||
/* Skip it, we've already deleted the parent. */
|
||||
}
|
||||
talloc_free(name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1086,9 +1139,15 @@ static int _rm(struct connection *conn, const void *ctx, struct node *node,
|
||||
/* Delete from parent first, then if we crash, the worst that can
|
||||
happen is the child will continue to take up space, but will
|
||||
otherwise be unreachable. */
|
||||
struct node *parent = read_node(conn, ctx, get_parent(ctx, name));
|
||||
struct node *parent;
|
||||
char *parentname = get_parent(ctx, name);
|
||||
|
||||
if (!parentname)
|
||||
return errno;
|
||||
|
||||
parent = read_node(conn, ctx, parentname);
|
||||
if (!parent)
|
||||
return EINVAL;
|
||||
return (errno == ENOMEM) ? ENOMEM : EINVAL;
|
||||
|
||||
if (!delete_child(conn, parent, basename(name)))
|
||||
return EINVAL;
|
||||
@@ -1114,19 +1173,24 @@ static int do_rm(struct connection *conn, struct buffered_data *in)
|
||||
struct node *node;
|
||||
int ret;
|
||||
char *name;
|
||||
char *parentname;
|
||||
|
||||
node = get_node_canonicalized(conn, in, onearg(in), &name,
|
||||
XS_PERM_WRITE);
|
||||
if (!node) {
|
||||
/* Didn't exist already? Fine, if parent exists. */
|
||||
if (errno == ENOENT) {
|
||||
node = read_node(conn, in, get_parent(in, name));
|
||||
parentname = get_parent(in, name);
|
||||
if (!parentname)
|
||||
return errno;
|
||||
node = read_node(conn, in, parentname);
|
||||
if (node) {
|
||||
send_ack(conn, XS_RM);
|
||||
return 0;
|
||||
}
|
||||
/* Restore errno, just in case. */
|
||||
errno = ENOENT;
|
||||
if (errno != ENOMEM)
|
||||
errno = ENOENT;
|
||||
}
|
||||
return errno;
|
||||
}
|
||||
@@ -1186,6 +1250,8 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in)
|
||||
num--;
|
||||
|
||||
perms = talloc_array(node, struct xs_permissions, num);
|
||||
if (!perms)
|
||||
return ENOMEM;
|
||||
if (!xs_strings_to_perms(perms, num, permstr))
|
||||
return errno;
|
||||
|
||||
@@ -1319,7 +1385,7 @@ static void handle_input(struct connection *conn)
|
||||
|
||||
if (!conn->in) {
|
||||
conn->in = new_buffer(conn);
|
||||
/* In case of no memory just try it next time again. */
|
||||
/* In case of no memory just try it again next time. */
|
||||
if (!conn->in)
|
||||
return;
|
||||
}
|
||||
@@ -1327,26 +1393,29 @@ static void handle_input(struct connection *conn)
|
||||
|
||||
/* Not finished header yet? */
|
||||
if (in->inhdr) {
|
||||
bytes = conn->read(conn, in->hdr.raw + in->used,
|
||||
sizeof(in->hdr) - in->used);
|
||||
if (bytes < 0)
|
||||
goto bad_client;
|
||||
in->used += bytes;
|
||||
if (in->used != sizeof(in->hdr))
|
||||
return;
|
||||
if (in->used != sizeof(in->hdr)) {
|
||||
bytes = conn->read(conn, in->hdr.raw + in->used,
|
||||
sizeof(in->hdr) - in->used);
|
||||
if (bytes < 0)
|
||||
goto bad_client;
|
||||
in->used += bytes;
|
||||
if (in->used != sizeof(in->hdr))
|
||||
return;
|
||||
|
||||
if (in->hdr.msg.len > XENSTORE_PAYLOAD_MAX) {
|
||||
syslog(LOG_ERR, "Client tried to feed us %i",
|
||||
in->hdr.msg.len);
|
||||
goto bad_client;
|
||||
if (in->hdr.msg.len > XENSTORE_PAYLOAD_MAX) {
|
||||
syslog(LOG_ERR, "Client tried to feed us %i",
|
||||
in->hdr.msg.len);
|
||||
goto bad_client;
|
||||
}
|
||||
}
|
||||
|
||||
if (in->hdr.msg.len <= DEFAULT_BUFFER_SIZE)
|
||||
in->buffer = in->default_buffer;
|
||||
else
|
||||
in->buffer = talloc_array(in, char, in->hdr.msg.len);
|
||||
/* In case of no memory just try it again next time. */
|
||||
if (!in->buffer)
|
||||
goto bad_client;
|
||||
return;
|
||||
in->used = 0;
|
||||
in->inhdr = false;
|
||||
}
|
||||
@@ -1469,6 +1538,9 @@ static void manual_node(const char *name, const char *child)
|
||||
struct xs_permissions perms = { .id = 0, .perms = XS_PERM_NONE };
|
||||
|
||||
node = talloc_zero(NULL, struct node);
|
||||
if (!node)
|
||||
barf_perror("Could not allocate initial node %s", name);
|
||||
|
||||
node->name = name;
|
||||
node->perms = &perms;
|
||||
node->num_perms = 1;
|
||||
@@ -1506,6 +1578,8 @@ static void setup_structure(void)
|
||||
{
|
||||
char *tdbname;
|
||||
tdbname = talloc_strdup(talloc_autofree_context(), xs_daemon_tdb());
|
||||
if (!tdbname)
|
||||
barf_perror("Could not create tdbname");
|
||||
|
||||
if (!(tdb_flags & TDB_INTERNAL))
|
||||
tdb_ctx = tdb_open_ex(tdbname, 0, tdb_flags, O_RDWR, 0,
|
||||
@@ -1584,11 +1658,14 @@ static char *child_name(const char *s1, const char *s2)
|
||||
}
|
||||
|
||||
|
||||
static void remember_string(struct hashtable *hash, const char *str)
|
||||
static int remember_string(struct hashtable *hash, const char *str)
|
||||
{
|
||||
char *k = malloc(strlen(str) + 1);
|
||||
|
||||
if (!k)
|
||||
return 0;
|
||||
strcpy(k, str);
|
||||
hashtable_insert(hash, k, (void *)1);
|
||||
return hashtable_insert(hash, k, (void *)1);
|
||||
}
|
||||
|
||||
|
||||
@@ -1605,9 +1682,10 @@ static void remember_string(struct hashtable *hash, const char *str)
|
||||
* As we go, we record each node in the given reachable hashtable. These
|
||||
* entries will be used later in clean_store.
|
||||
*/
|
||||
static void check_store_(const char *name, struct hashtable *reachable)
|
||||
static int check_store_(const char *name, struct hashtable *reachable)
|
||||
{
|
||||
struct node *node = read_node(NULL, name, name);
|
||||
int ret = 0;
|
||||
|
||||
if (node) {
|
||||
size_t i = 0;
|
||||
@@ -1615,14 +1693,24 @@ static void check_store_(const char *name, struct hashtable *reachable)
|
||||
struct hashtable * children =
|
||||
create_hashtable(16, hash_from_key_fn, keys_equal_fn);
|
||||
|
||||
remember_string(reachable, name);
|
||||
if (!remember_string(reachable, name)) {
|
||||
hashtable_destroy(children, 0);
|
||||
log("check_store: ENOMEM");
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
while (i < node->childlen) {
|
||||
while (i < node->childlen && !ret) {
|
||||
struct node *childnode;
|
||||
size_t childlen = strlen(node->children + i);
|
||||
char * childname = child_name(node->name,
|
||||
node->children + i);
|
||||
struct node *childnode = read_node(NULL, childname,
|
||||
childname);
|
||||
|
||||
if (!childname) {
|
||||
log("check_store: ENOMEM");
|
||||
ret = ENOMEM;
|
||||
break;
|
||||
}
|
||||
childnode = read_node(NULL, childname, childname);
|
||||
|
||||
if (childnode) {
|
||||
if (hashtable_search(children, childname)) {
|
||||
@@ -1636,11 +1724,18 @@ static void check_store_(const char *name, struct hashtable *reachable)
|
||||
}
|
||||
}
|
||||
else {
|
||||
remember_string(children, childname);
|
||||
check_store_(childname, reachable);
|
||||
if (!remember_string(children,
|
||||
childname)) {
|
||||
log("check_store: ENOMEM");
|
||||
talloc_free(childnode);
|
||||
talloc_free(childname);
|
||||
ret = ENOMEM;
|
||||
break;
|
||||
}
|
||||
ret = check_store_(childname,
|
||||
reachable);
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else if (errno != ENOMEM) {
|
||||
log("check_store: No child '%s' found!\n",
|
||||
childname);
|
||||
|
||||
@@ -1648,6 +1743,9 @@ static void check_store_(const char *name, struct hashtable *reachable)
|
||||
remove_child_entry(NULL, node, i);
|
||||
i -= childlen + 1;
|
||||
}
|
||||
} else {
|
||||
log("check_store: ENOMEM");
|
||||
ret = ENOMEM;
|
||||
}
|
||||
|
||||
talloc_free(childnode);
|
||||
@@ -1658,14 +1756,18 @@ static void check_store_(const char *name, struct hashtable *reachable)
|
||||
hashtable_destroy(children, 0 /* Don't free values (they are
|
||||
all (void *)1) */);
|
||||
talloc_free(node);
|
||||
}
|
||||
else {
|
||||
} else if (errno != ENOMEM) {
|
||||
/* Impossible, because no database should ever be without the
|
||||
root, and otherwise, we've just checked in our caller
|
||||
(which made a recursive call to get here). */
|
||||
|
||||
log("check_store: No child '%s' found: impossible!", name);
|
||||
} else {
|
||||
log("check_store: ENOMEM");
|
||||
ret = ENOMEM;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -1678,6 +1780,11 @@ static int clean_store_(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA val,
|
||||
struct hashtable *reachable = private;
|
||||
char * name = talloc_strndup(NULL, key.dptr, key.dsize);
|
||||
|
||||
if (!name) {
|
||||
log("clean_store: ENOMEM");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!hashtable_search(reachable, name)) {
|
||||
log("clean_store: '%s' is orphaned!", name);
|
||||
if (recovery) {
|
||||
@@ -1707,9 +1814,14 @@ static void check_store(void)
|
||||
struct hashtable * reachable =
|
||||
create_hashtable(16, hash_from_key_fn, keys_equal_fn);
|
||||
|
||||
if (!reachable) {
|
||||
log("check_store: ENOMEM");
|
||||
return;
|
||||
}
|
||||
|
||||
log("Checking store ...");
|
||||
check_store_(root, reachable);
|
||||
clean_store(reachable);
|
||||
if (!check_store_(root, reachable))
|
||||
clean_store(reachable);
|
||||
log("Checking store complete.");
|
||||
|
||||
hashtable_destroy(reachable, 0 /* Don't free values (they are all
|
||||
@@ -1763,10 +1875,14 @@ static void init_sockets(int **psock, int **pro_sock)
|
||||
|
||||
/* Create sockets for them to listen to. */
|
||||
*psock = sock = talloc(talloc_autofree_context(), int);
|
||||
if (!sock)
|
||||
barf_perror("No memory when creating sockets");
|
||||
*sock = socket(PF_UNIX, SOCK_STREAM, 0);
|
||||
if (*sock < 0)
|
||||
barf_perror("Could not create socket");
|
||||
*pro_sock = ro_sock = talloc(talloc_autofree_context(), int);
|
||||
if (!ro_sock)
|
||||
barf_perror("No memory when creating sockets");
|
||||
*ro_sock = socket(PF_UNIX, SOCK_STREAM, 0);
|
||||
if (*ro_sock < 0)
|
||||
barf_perror("Could not create socket");
|
||||
|
||||
@@ -279,10 +279,15 @@ static struct domain *new_domain(void *context, unsigned int domid,
|
||||
int rc;
|
||||
|
||||
domain = talloc(context, struct domain);
|
||||
if (!domain)
|
||||
return NULL;
|
||||
|
||||
domain->port = 0;
|
||||
domain->shutdown = 0;
|
||||
domain->domid = domid;
|
||||
domain->path = talloc_domain_path(domain, domid);
|
||||
if (!domain->path)
|
||||
return NULL;
|
||||
|
||||
list_add(&domain->list, &domains);
|
||||
talloc_set_destructor(domain, destroy_domain);
|
||||
@@ -294,6 +299,9 @@ static struct domain *new_domain(void *context, unsigned int domid,
|
||||
domain->port = rc;
|
||||
|
||||
domain->conn = new_connection(writechn, readchn);
|
||||
if (!domain->conn)
|
||||
return NULL;
|
||||
|
||||
domain->conn->domain = domain;
|
||||
domain->conn->id = domid;
|
||||
|
||||
@@ -498,6 +506,8 @@ int do_get_domain_path(struct connection *conn, struct buffered_data *in)
|
||||
return EINVAL;
|
||||
|
||||
path = talloc_domain_path(conn, atoi(domid_str));
|
||||
if (!path)
|
||||
return errno;
|
||||
|
||||
send_reply(conn, XS_GET_DOMAIN_PATH, path, strlen(path) + 1);
|
||||
|
||||
|
||||
@@ -119,7 +119,18 @@ void add_change_node(struct connection *conn, struct node *node, bool recurse)
|
||||
}
|
||||
|
||||
i = talloc(trans, struct changed_node);
|
||||
if (!i) {
|
||||
/* All we can do is let the transaction fail. */
|
||||
generation++;
|
||||
return;
|
||||
}
|
||||
i->node = talloc_strdup(i, node->name);
|
||||
if (!i->node) {
|
||||
/* All we can do is let the transaction fail. */
|
||||
generation++;
|
||||
talloc_free(i);
|
||||
return;
|
||||
}
|
||||
i->recurse = recurse;
|
||||
list_add_tail(&i->list, &trans->changes);
|
||||
}
|
||||
@@ -163,11 +174,16 @@ int do_transaction_start(struct connection *conn, struct buffered_data *in)
|
||||
|
||||
/* Attach transaction to input for autofree until it's complete */
|
||||
trans = talloc_zero(in, struct transaction);
|
||||
if (!trans)
|
||||
return ENOMEM;
|
||||
|
||||
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);
|
||||
if (!trans->tdb_name)
|
||||
return ENOMEM;
|
||||
trans->tdb = tdb_copy(tdb_context(conn), trans->tdb_name);
|
||||
if (!trans->tdb)
|
||||
return errno;
|
||||
@@ -246,6 +262,11 @@ void transaction_entry_inc(struct transaction *trans, unsigned int domid)
|
||||
}
|
||||
|
||||
d = talloc(trans, struct changed_domain);
|
||||
if (!d) {
|
||||
/* Let the transaction fail. */
|
||||
generation++;
|
||||
return;
|
||||
}
|
||||
d->domid = domid;
|
||||
d->nbentry = 1;
|
||||
list_add_tail(&d->list, &trans->changed_domains);
|
||||
@@ -262,6 +283,11 @@ void transaction_entry_dec(struct transaction *trans, unsigned int domid)
|
||||
}
|
||||
|
||||
d = talloc(trans, struct changed_domain);
|
||||
if (!d) {
|
||||
/* Let the transaction fail. */
|
||||
generation++;
|
||||
return;
|
||||
}
|
||||
d->domid = domid;
|
||||
d->nbentry = -1;
|
||||
list_add_tail(&d->list, &trans->changed_domains);
|
||||
|
||||
@@ -111,6 +111,8 @@ static void add_event(struct connection *conn,
|
||||
|
||||
len = strlen(name) + 1 + strlen(watch->token) + 1;
|
||||
data = talloc_array(ctx, char, len);
|
||||
if (!data)
|
||||
return;
|
||||
strcpy(data, name);
|
||||
strcpy(data + strlen(name) + 1, watch->token);
|
||||
send_reply(conn, XS_WATCH_EVENT, data, len);
|
||||
@@ -165,6 +167,8 @@ int do_watch(struct connection *conn, struct buffered_data *in)
|
||||
} else {
|
||||
relative = !strstarts(vec[0], "/");
|
||||
vec[0] = canonicalize(conn, in, vec[0]);
|
||||
if (!vec[0])
|
||||
return ENOMEM;
|
||||
if (!is_valid_nodename(vec[0]))
|
||||
return EINVAL;
|
||||
}
|
||||
@@ -180,8 +184,14 @@ int do_watch(struct connection *conn, struct buffered_data *in)
|
||||
return E2BIG;
|
||||
|
||||
watch = talloc(conn, struct watch);
|
||||
if (!watch)
|
||||
return ENOMEM;
|
||||
watch->node = talloc_strdup(watch, vec[0]);
|
||||
watch->token = talloc_strdup(watch, vec[1]);
|
||||
if (!watch->node || !watch->token) {
|
||||
talloc_free(watch);
|
||||
return ENOMEM;
|
||||
}
|
||||
if (relative)
|
||||
watch->relative_path = get_implicit_path(conn);
|
||||
else
|
||||
@@ -210,6 +220,8 @@ int do_unwatch(struct connection *conn, struct buffered_data *in)
|
||||
return EINVAL;
|
||||
|
||||
node = canonicalize(conn, in, vec[0]);
|
||||
if (!node)
|
||||
return ENOMEM;
|
||||
list_for_each_entry(watch, &conn->watches, list) {
|
||||
if (streq(watch->node, node) && streq(watch->token, vec[1])) {
|
||||
list_del(&watch->list);
|
||||
|
||||