From 2f45bcf78b77193ad6107fbb92ad0f93eb1ad4fc Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 11 Sep 2021 13:57:42 +0100 Subject: Restrict updates to the newest updater Prevent old update connections from providing updates, closing them if they attempt an update. In doing so, this also limits resource usage when we have multiple updaters connected (which should never happen.) Signed-off-by: Russell King --- event-httpd.c | 14 +++++++++++++- resource.c | 34 ++++++++++++++++++++++++++++------ resource.h | 3 +++ 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/event-httpd.c b/event-httpd.c index 27590dd..865f82f 100644 --- a/event-httpd.c +++ b/event-httpd.c @@ -97,19 +97,28 @@ static void update(GObject *source, GAsyncResult *res, gpointer user_data) GError *error = NULL; char *line; gsize len; + int r; line = g_data_input_stream_read_line_finish(c->data, res, &len, &error); if (error || !line) { + if (c->resource->ops->update_close) + c->resource->ops->update_close(c, c->resource); + if (error) g_free(error); close_client(c); return; } - c->resource->ops->update(c, c->resource, line); + r = c->resource->ops->update(c, c->resource, line); g_free(line); + // If the update function returns an error, close this connection. + // This could be because we have a "newer" updater, or an error. + if (r == -1) + close_client(c); + g_data_input_stream_read_line_async(c->data, 0, NULL, update, c); } @@ -257,6 +266,9 @@ static void receive(GObject *source, GAsyncResult *res, gpointer user_data) return; } + if (resource->ops->update_open) + resource->ops->update_open(c, resource); + g_data_input_stream_read_line_async(c->data, 0, NULL, update, c); break; diff --git a/resource.c b/resource.c index bc5dfdd..8c67635 100644 --- a/resource.c +++ b/resource.c @@ -73,6 +73,20 @@ static int object_v1_get(struct client *c, struct resource *r) return 1; } +static void object_v1_close(struct client *c, struct resource *r) +{ + struct resource_object *obj = r->data; + + // Remove this client from the object list + resource_obj_del_client(obj, c); +} + +static void object_v1_update_open(struct client *c, struct resource *r) +{ + // Keep track of the latest updater + r->updater = c; +} + // Update all attached clients with the new resource object data static int object_v1_update(struct client *c, struct resource *r, const char *m) { @@ -80,6 +94,12 @@ static int object_v1_update(struct client *c, struct resource *r, const char *m) GString *string; char *n; + // Only allow the latest updater to provide updates, in case we + // end up with multiple connections. This also allows us to quickly + // release the resources of a stale connection. + if (r->updater != c) + return -1; + // Remove any trailing whitespace. n = g_strchomp(g_strdup(m)); @@ -97,18 +117,20 @@ static int object_v1_update(struct client *c, struct resource *r, const char *m) return 0; } -static void object_v1_close(struct client *c, struct resource *r) +static void object_v1_update_close(struct client *c, struct resource *r) { - struct resource_object *obj = r->data; - - // Remove this client from the object list - resource_obj_del_client(obj, c); + // If the updater goes away, clear the data so we don't serve + // stale data. + if (r->updater == c) + r->updater = NULL; } static const struct resource_ops resource_v1_ops = { .get = object_v1_get, - .update = object_v1_update, .close = object_v1_close, + .update_open = object_v1_update_open, + .update = object_v1_update, + .update_close = object_v1_update_close, }; static struct resource resource_position1 = { diff --git a/resource.h b/resource.h index c3a1907..657397b 100644 --- a/resource.h +++ b/resource.h @@ -9,11 +9,14 @@ struct resource; struct resource_ops { int (*get)(struct client *c, struct resource *r); void (*close)(struct client *c, struct resource *r); + void (*update_open)(struct client *c, struct resource *r); int (*update)(struct client *c, struct resource *r, const char *m); + void (*update_close)(struct client *c, struct resource *r); }; struct resource { const struct resource_ops *ops; + struct client *updater; void *data; }; -- cgit