diff --git a/include/ctl-server.h b/include/ctl-server.h index df4af84..5158080 100644 --- a/include/ctl-server.h +++ b/include/ctl-server.h @@ -17,17 +17,28 @@ #pragma once #include "output.h" -#include struct ctl; struct cmd_response; +struct ctl_server_vnc_client { + char id[64]; + char hostname[256]; + char username[256]; +}; + struct ctl_server_actions { void* userdata; struct cmd_response* (*on_output_cycle)(struct ctl*, enum output_cycle_direction direction); struct cmd_response* (*on_output_switch)(struct ctl*, const char* output_name); + + // Return number of elements created + // Allocate 'clients' array or set ton ULL if none + // Receiver will free(clients) when done. + int (*get_client_list)(struct ctl*, + struct ctl_server_vnc_client** clients); }; struct ctl* ctl_server_new(const char* socket_path, diff --git a/src/ctl-client.c b/src/ctl-client.c index b49ee3b..810eb78 100644 --- a/src/ctl-client.c +++ b/src/ctl-client.c @@ -392,6 +392,26 @@ static void pretty_version(json_t* data) printf(" %s: %s\n", key, json_string_value(value)); } +static void pretty_client_list(json_t* data) +{ + int n = json_array_size(data); + printf("There %s %d VNC client%s connected%s\n", (n == 1) ? "is" : "are", + n, (n == 1) ? "" : "s", (n > 0) ? ":" : "."); + int i; + json_t* value; + json_array_foreach(data, i, value) { + char* id = NULL; + char* hostname = NULL; + char* username = NULL; + json_unpack(value, "{s:s, s?s, s?s}", "id", &id, "hostname", + &hostname, "username", &username); + printf(" client[%s]: ", id); + if (username) + printf("%s@", username); + printf("%s\n", hostname ? hostname : ""); + } +} + static void pretty_print(json_t* data, struct jsonipc_request* request) { @@ -400,6 +420,8 @@ static void pretty_print(json_t* data, print_help(data, request->params); else if (strcmp(method, "version") == 0) pretty_version(data); + else if (strcmp(method, "get-clients") == 0) + pretty_client_list(data); else json_dumpf(data, stdout, JSON_INDENT(2)); } diff --git a/src/ctl-server.c b/src/ctl-server.c index 8b8fd82..8fd5e31 100644 --- a/src/ctl-server.c +++ b/src/ctl-server.c @@ -46,6 +46,7 @@ enum cmd_type { CMD_VERSION, CMD_EVENT_RECEIVE, CMD_SET_OUTPUT, + CMD_GET_CLIENTS, CMD_UNKNOWN, }; #define CMD_LIST_LEN CMD_UNKNOWN @@ -94,6 +95,11 @@ static struct cmd_info cmd_list[] = { {NULL, NULL}, } }, + [CMD_GET_CLIENTS] = { "get-clients", + "Return a list of all currently connected VNC sessions", + {{NULL, NULL}} + }, + }; #define CLIENT_EVENT_PARAMS(including) \ @@ -283,6 +289,7 @@ static struct cmd* parse_command(struct jsonipc_request* ipc, break; case CMD_VERSION: case CMD_EVENT_RECEIVE: + case CMD_GET_CLIENTS: cmd = calloc(1, sizeof(*cmd)); cmd->type = cmd_type; break; @@ -308,6 +315,7 @@ static void client_destroy(struct ctl_client* self) wl_list_remove(&self->link); free(self); } + static void set_internal_error(struct cmd_response** err, int code, const char* fmt, ...) { @@ -419,6 +427,27 @@ static struct cmd_response* generate_version_object() return response; } +static struct cmd_response* generate_vnc_client_list(struct ctl* self) +{ + struct ctl_server_vnc_client* clients; + size_t num_clients = self->actions.get_client_list(self, &clients); + struct cmd_response* response = cmd_ok(); + + response->data = json_array(); + for (int i = 0; i < num_clients; ++i) { + json_t* packed = json_pack("{s:s}", "id", clients[i].id); + if (clients[i].hostname[0] != '\0') + json_object_set_new(packed, "hostname", + json_string(clients[i].hostname)); + if (clients[i].username[0] != '\0') + json_object_set_new(packed, "username", + json_string(clients[i].username)); + json_array_append_new(response->data, packed); + } + free(clients); + return response; +} + static struct cmd_response* ctl_server_dispatch_cmd(struct ctl* self, struct ctl_client* client, struct cmd* cmd) { @@ -447,6 +476,9 @@ static struct cmd_response* ctl_server_dispatch_cmd(struct ctl* self, client->accept_events = true; response = cmd_ok(); break; + case CMD_GET_CLIENTS: + response = generate_vnc_client_list(self); + break; case CMD_UNKNOWN: break; } diff --git a/src/main.c b/src/main.c index 3b9b868..2ae966b 100644 --- a/src/main.c +++ b/src/main.c @@ -464,6 +464,35 @@ struct cmd_response* on_output_switch(struct ctl* ctl, return cmd_ok(); } +static int get_client_list(struct ctl* ctl, + struct ctl_server_vnc_client** clients) +{ + struct wayvnc* self = ctl_server_userdata(ctl); + if (self->nr_clients == 0) { + *clients = NULL; + return 0; + } + *clients = calloc(self->nr_clients, sizeof(**clients)); + struct nvnc_client* nvnc_client = nvnc_client_first(self->nvnc); + for (int i = 0; i < self->nr_clients && nvnc_client; ++i) { + struct wayvnc_client* client = nvnc_get_userdata(nvnc_client); + struct ctl_server_vnc_client* ctl_client =&(*clients)[i]; + + snprintf(ctl_client->id, sizeof(ctl_client->id), "%u", + client->id); + const char* hostname = nvnc_client_get_hostname(nvnc_client); + if (hostname) + strlcpy(ctl_client->hostname, hostname, + sizeof(ctl_client->hostname)); + const char* username = nvnc_client_get_auth_username(nvnc_client); + if (username) + strlcpy(ctl_client->username, username, + sizeof(ctl_client->username)); + nvnc_client = nvnc_client_next(nvnc_client); + } + return self->nr_clients; +} + int init_main_loop(struct wayvnc* self) { struct aml* loop = aml_get_default(); @@ -1304,6 +1333,7 @@ int main(int argc, char* argv[]) .userdata = &self, .on_output_cycle = on_output_cycle, .on_output_switch = on_output_switch, + .get_client_list = get_client_list, }; self.ctl = ctl_server_new(socket_path, &ctl_actions); if (!self.ctl) diff --git a/wayvnc.scd b/wayvnc.scd index d999734..412f94f 100644 --- a/wayvnc.scd +++ b/wayvnc.scd @@ -199,6 +199,11 @@ which parameters are supplied: *switch-to=output-name* Switch to a specific output by name. +_GET-CLIENTS_ + +The *get-clients* command retrieves a list of all VNC clients currently +connected to wayvnc. + ## IPC EVENTS _CLIENT-CONNECTED_