From d8239109e5f2d6b7f265b9561ae4bdce12fb5520 Mon Sep 17 00:00:00 2001 From: Jim Ramsay Date: Wed, 9 Nov 2022 09:11:21 -0500 Subject: [PATCH] Introduce an event loop mode in wayvncctl Signed-off-by: Jim Ramsay --- include/json-ipc.h | 2 + src/ctl-client.c | 116 +++++++++++++++++++++++++++++++++++++++++++-- src/json-ipc.c | 6 +++ wayvncctl.scd | 33 +++++++++++-- 4 files changed, 149 insertions(+), 8 deletions(-) diff --git a/include/json-ipc.h b/include/json-ipc.h index eac2f22..1b81218 100644 --- a/include/json-ipc.h +++ b/include/json-ipc.h @@ -52,6 +52,8 @@ struct jsonipc_request* jsonipc_request_parse_new(json_t* root, struct jsonipc_error* err); struct jsonipc_request* jsonipc_request_new(const char* method, json_t* params); struct jsonipc_request* jsonipc_event_new(const char* method, json_t* params); +struct jsonipc_request* jsonipc_event_parse_new(json_t* root, + struct jsonipc_error* err); json_t* jsonipc_request_pack(struct jsonipc_request*, json_error_t* err); void jsonipc_request_destroy(struct jsonipc_request*); diff --git a/src/ctl-client.c b/src/ctl-client.c index 52d3248..b6e01e9 100644 --- a/src/ctl-client.c +++ b/src/ctl-client.c @@ -200,6 +200,8 @@ static json_t* read_one_object(struct ctl_client* self, int timeout_ms) while (root == NULL) { int n = poll(&pfd, 1, timeout_ms); if (n == -1) { + if (errno == EINTR && self->wait_for_events) + continue; WARN("Error waiting for a response: %m"); break; } else if (n == 0) { @@ -395,17 +397,121 @@ static void setup_signals(struct ctl_client* self) sigaction(SIGTERM, &sa, NULL); } -static void ctl_client_event_loop(struct ctl_client* self) +static void print_indent(int level) +{ + for (int i = 0; i < level; ++i) + printf(" "); +} + +static bool json_has_content(json_t* root) +{ + if (!root) + return false; + size_t i; + const char* key; + json_t* value; + switch (json_typeof(root)) { + case JSON_NULL: + return false; + case JSON_INTEGER: + case JSON_REAL: + case JSON_TRUE: + case JSON_FALSE: + return true; + case JSON_STRING: + return json_string_value(root)[0] != '\0'; + case JSON_OBJECT: + json_object_foreach(root, key, value) + if (json_has_content(value)) + return true; + return false; + case JSON_ARRAY: + json_array_foreach(root, i, value) + if (json_has_content(value)) + return true; + return false; + } + return false; +} + +static void print_as_yaml(json_t* data, int level, bool needs_leading_newline) +{ + size_t i; + const char* key; + json_t* value; + bool needs_indent = needs_leading_newline; + switch(json_typeof(data)) { + case JSON_NULL: + printf("\n"); + break; + case JSON_OBJECT: + if (json_object_size(data) > 0 && needs_leading_newline) + printf("\n"); + json_object_foreach(data, key, value) { + if (!json_has_content(value)) + continue; + if (needs_indent) + print_indent(level); + else + needs_indent = true; + printf("%s: ", key); + print_as_yaml(value, level + 1, true); + } + break; + case JSON_ARRAY: + if (json_array_size(data) > 0 && needs_leading_newline) + printf("\n"); + json_array_foreach(data, i, value) { + if (!json_has_content(value)) + continue; + print_indent(level); + printf("- "); + print_as_yaml(value, level + 1, json_is_array(value)); + } + break; + case JSON_STRING: + printf("%s\n", json_string_value(data)); + break; + case JSON_INTEGER: + printf("%" JSON_INTEGER_FORMAT "\n", json_integer_value(data)); + break; + case JSON_REAL: + printf("%f\n", json_real_value(data)); + break; + case JSON_TRUE: + printf("true\n"); + break; + case JSON_FALSE: + printf("false\n"); + break; + } +} + +static void print_event(struct jsonipc_request* event, unsigned flags) +{ + if (flags & PRINT_JSON) { + print_compact_json(event->json); + } else { + printf("\n%s:", event->method); + print_as_yaml(event->params, 1, true); + } + fflush(stdout); +} + +static void ctl_client_event_loop(struct ctl_client* self, unsigned flags) { self->wait_for_events = true; setup_signals(self); while (self->wait_for_events) { DEBUG("Waiting for an event"); json_t* root = read_one_object(self, -1); - json_dumpf(root, stdout, 0); - printf("\n"); - fflush(stdout); + if (!root) + break; + struct jsonipc_error err = JSONIPC_ERR_INIT; + struct jsonipc_request* event = jsonipc_event_parse_new(root, &err); json_decref(root); + print_event(event, flags); + jsonipc_request_destroy(event); } } @@ -427,7 +533,7 @@ int ctl_client_run_command(struct ctl_client* self, result = ctl_client_print_response(self, request, response, flags); if (result == 0 && strcmp(request->method, "event-receive") == 0) - ctl_client_event_loop(self); + ctl_client_event_loop(self, flags); jsonipc_response_destroy(response); receive_failure: diff --git a/src/json-ipc.c b/src/json-ipc.c index 7ebf094..1959c54 100644 --- a/src/json-ipc.c +++ b/src/json-ipc.c @@ -111,6 +111,12 @@ struct jsonipc_request* jsonipc_event_new(const char* method, json_t* params) return jsonipc_request__new(method, params, NULL); } +struct jsonipc_request* jsonipc_event_parse_new(json_t* root, + struct jsonipc_error* err) +{ + return jsonipc_request_parse_new(root, err); +} + json_t* jsonipc_request_pack(struct jsonipc_request* self, json_error_t* err) { return json_pack_ex(err, 0, "{s:s, s:O*, s:O*}", diff --git a/wayvncctl.scd b/wayvncctl.scd index 9f40f14..f7c4458 100644 --- a/wayvncctl.scd +++ b/wayvncctl.scd @@ -46,9 +46,36 @@ command and its available parameters. While *wayvncctl* normally terminates after sending one request and receiving the corresponding reply, the *event-receive* command acts differently. Instead -of exiting immediately, *wayvncctl* waits for any events fr the server, printing -each to stdout as they arrive. This mode of operation will block until either -it receives a signal to terminate, or until the wayvnc server terminates. +of exiting immediately, *wayvncctl* waits for any events from the server, +printing each to stdout as they arrive. This mode of operation will block until +either it receives a signal to terminate, or until the wayvnc server terminates. + +In _--json_ mode, each event is printed on one line, with a newline character at +the end, for ease in scripting: + +``` +$ wayvncctl --json event-receive +{"method":"client-connected","params":{"id":"0x10ef670","hostname":null,"username":null,"connection_count":1}} +{"method":"client-disconnected","params":{"id":"0x10ef670","hostname":null,"username":null,"connection_count":0}} +``` + +The default human-readible output is a multi-line yaml-like format, with two +newline characters between each event: + +``` +$ wayvncctl event-receive + +client-connected: + id: 0x10ef670 + hostname: 192.168.1.18 + connection_count: 1 + +client-disconnected: + id: 0x10ef670 + hostname: 192.168.1.18 + connection_count: 0 + +``` # EXAMPLES