Compare commits

...

15 Commits

Author SHA1 Message Date
Andri Yngvason 4fb15640e2 ctl-commands: Use slightly more helpful schemas 2023-01-15 18:10:26 +00:00
Andri Yngvason 6174ff21f7 ctl-client: Replace = with : in event description
Let's not abuse the equality sign more than necessary. ;)
2023-01-15 18:04:04 +00:00
Andri Yngvason 39ec433f87 ctl-commands: Fix typo 2023-01-15 18:01:44 +00:00
Andri Yngvason 79833dd5bf ctl-commands: Capitalise "VNC" 2023-01-15 18:00:09 +00:00
Andri Yngvason 3be07f4f40 ctl-server: Improve error messages for missing arguments 2023-01-15 17:53:34 +00:00
Andri Yngvason 9b3ca29df0 ctl-server: Fix segfault in error path
This would segfault e.g. if issuing:
$ waynvcctl client-disconnect # without argument
2023-01-15 17:49:36 +00:00
Andri Yngvason d6ebc9ecc0 ctl-client: Print trailing newline for events
If someone wants to parse this instead of using jq, a trailing
newline delimits the end of the event.
2023-01-15 17:42:01 +00:00
Andri Yngvason 018d42db8a ctl-client: Don't print "<<null>" for events with no params
It's inconsistent and doesn't seem helpful either.
2023-01-15 17:32:42 +00:00
Andri Yngvason bc647a19c0 ctl-client: Remove decorations in client/output lists
Those decorations do not appear to improve readability or
parsability.
2023-01-15 17:25:02 +00:00
Andri Yngvason b1f0146306 ctl-client: Remove messages about how many clients/outputs there are
When you type "ls", it doesn't tell you how many files there are.
If the user wants to know that, they can use "wc -l" or count them.
2023-01-15 17:20:05 +00:00
Andri Yngvason 7ac8154448 ctl-client: Rename print_as_yaml -> print_for_human
The goal is to have this human friendly, not yaml.
2023-01-15 17:15:55 +00:00
Andri Yngvason 69029d317f ctl-client: Use more descriptive response error message 2023-01-15 17:15:55 +00:00
Andri Yngvason e1378082c7 ctl-client: Add a few vertical spaces + style changes
This change is intended to make things easier to read for myself.
2023-01-15 17:15:55 +00:00
Andri Yngvason 2e62a8408b ctl-client: Rename WARNING log level to ERROR
All of these are basically errors.

A warning is often a recoverable event, an error isn't.
2023-01-15 16:45:55 +00:00
Andri Yngvason 8538aa81f8 ctl-client: Align log messages with other log messages 2023-01-15 16:40:34 +00:00
3 changed files with 111 additions and 69 deletions

View File

@ -37,17 +37,17 @@
#include "table-printer.h" #include "table-printer.h"
#define LOG(level, fmt, ...) \ #define LOG(level, fmt, ...) \
fprintf(stderr, "[%s:%d] <" level "> " fmt "\n", __FILE__, __LINE__, \ fprintf(stderr, level ": %s: %d: " fmt "\n", __FILE__, __LINE__, \
##__VA_ARGS__) ##__VA_ARGS__)
#define WARN(fmt, ...) \ #define ERROR(fmt, ...) \
LOG("WARNING", fmt, ##__VA_ARGS__) LOG("ERROR", fmt, ##__VA_ARGS__)
static bool do_debug = false; static bool do_debug = false;
#define DEBUG(fmt, ...) \ #define DEBUG(fmt, ...) \
if (do_debug) \ if (do_debug) \
LOG("DEBUG", fmt, ##__VA_ARGS__) LOG("DEBUG", fmt, ##__VA_ARGS__)
static struct cmd_info internal_events[] = { static struct cmd_info internal_events[] = {
{ .name = "wayvnc-startup", { .name = "wayvnc-startup",
@ -85,13 +85,14 @@ struct ctl_client* ctl_client_new(const char* socket_path, void* userdata)
{ {
if (!socket_path) if (!socket_path)
socket_path = default_ctl_socket_path(); socket_path = default_ctl_socket_path();
struct ctl_client* new = calloc(1, sizeof(*new)); struct ctl_client* new = calloc(1, sizeof(*new));
new->userdata = userdata; new->userdata = userdata;
new->fd = -1; new->fd = -1;
if (strlen(socket_path) >= sizeof(new->addr.sun_path)) { if (strlen(socket_path) >= sizeof(new->addr.sun_path)) {
errno = ENAMETOOLONG; errno = ENAMETOOLONG;
WARN("Failed to create unix socket: %m"); ERROR("Failed to create unix socket: %m");
goto socket_failure; goto socket_failure;
} }
strcpy(new->addr.sun_path, socket_path); strcpy(new->addr.sun_path, socket_path);
@ -108,9 +109,10 @@ static int wait_for_socket(const char* socket_path, int timeout)
{ {
bool needs_log = true; bool needs_log = true;
struct stat sb; struct stat sb;
while (stat(socket_path, &sb) != 0) { while (stat(socket_path, &sb) != 0) {
if (timeout == 0) { if (timeout == 0) {
WARN("Failed to find socket path \"%s\": %m", ERROR("Failed to find socket path \"%s\": %m",
socket_path); socket_path);
return 1; return 1;
} }
@ -120,17 +122,19 @@ static int wait_for_socket(const char* socket_path, int timeout)
socket_path); socket_path);
} }
if (usleep(50000) == -1) { if (usleep(50000) == -1) {
WARN("Failed to wait for socket path: %m"); ERROR("Failed to wait for socket path: %m");
return -1; return -1;
} }
} }
if (S_ISSOCK(sb.st_mode)) { if (S_ISSOCK(sb.st_mode)) {
DEBUG("Found socket \"%s\"", socket_path); DEBUG("Found socket \"%s\"", socket_path);
} else { } else {
WARN("Path \"%s\" exists but is not a socket (0x%x)", ERROR("Path \"%s\" exists but is not a socket (0x%x)",
socket_path, sb.st_mode); socket_path, sb.st_mode);
return -1; return -1;
} }
return 0; return 0;
} }
@ -138,23 +142,27 @@ static int try_connect(struct ctl_client* self, int timeout)
{ {
if (self->fd != -1) if (self->fd != -1)
close(self->fd); close(self->fd);
self->fd = socket(AF_UNIX, SOCK_STREAM, 0); self->fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (self->fd < 0) { if (self->fd < 0) {
WARN("Failed to create unix socket: %m"); ERROR("Failed to create unix socket: %m");
return 1; return 1;
} }
while (connect(self->fd, (struct sockaddr*)&self->addr, while (connect(self->fd, (struct sockaddr*)&self->addr,
sizeof(self->addr)) != 0) { sizeof(self->addr)) != 0) {
if (timeout == 0 || errno != ENOENT) { if (timeout == 0 || errno != ENOENT) {
WARN("Failed to connect to unix socket \"%s\": %m", ERROR("Failed to connect to unix socket \"%s\": %m",
self->addr.sun_path); self->addr.sun_path);
return 1; return 1;
} }
if (usleep(50000) == -1) { if (usleep(50000) == -1) {
WARN("Failed to wait for connect to succeed: %m"); ERROR("Failed to wait for connect to succeed: %m");
return 1; return 1;
} }
} }
return 0; return 0;
} }
@ -189,12 +197,14 @@ static struct jsonipc_request* ctl_client_parse_args(struct ctl_client* self,
struct jsonipc_request* request = NULL; struct jsonipc_request* request = NULL;
json_t* params = json_object(); json_t* params = json_object();
struct cmd_info* info = ctl_command_by_type(*cmd); struct cmd_info* info = ctl_command_by_type(*cmd);
if (option_parser_get_value(options, "help")) { if (option_parser_get_value(options, "help")) {
json_object_set_new(params, "command", json_string(info->name)); json_object_set_new(params, "command", json_string(info->name));
*cmd = CMD_HELP; *cmd = CMD_HELP;
info = ctl_command_by_type(*cmd); info = ctl_command_by_type(*cmd);
goto out; goto out;
} }
for (int i = 0; info->params[i].name != NULL; ++i) { for (int i = 0; info->params[i].name != NULL; ++i) {
const char* key = info->params[i].name; const char* key = info->params[i].name;
const char* value = option_parser_get_value(options, key); const char* value = option_parser_get_value(options, key);
@ -202,6 +212,7 @@ static struct jsonipc_request* ctl_client_parse_args(struct ctl_client* self,
continue; continue;
json_object_set_new(params, key, json_string(value)); json_object_set_new(params, key, json_string(value));
} }
out: out:
request = jsonipc_request_new(info->name, params); request = jsonipc_request_new(info->name, params);
json_decref(params); json_decref(params);
@ -215,20 +226,22 @@ static json_t* json_from_buffer(struct ctl_client* self)
errno = ENODATA; errno = ENODATA;
return NULL; return NULL;
} }
json_error_t err; json_error_t err;
json_t* root = json_loadb(self->read_buffer, self->read_len, 0, &err); json_t* root = json_loadb(self->read_buffer, self->read_len, 0, &err);
if (root) { if (root) {
advance_read_buffer(&self->read_buffer, &self->read_len, err.position); advance_read_buffer(&self->read_buffer, &self->read_len,
err.position);
} else if (json_error_code(&err) == json_error_premature_end_of_input) { } else if (json_error_code(&err) == json_error_premature_end_of_input) {
if (self->read_len == sizeof(self->read_buffer)) { if (self->read_len == sizeof(self->read_buffer)) {
WARN("Response message is too long"); ERROR("Response message is too long");
errno = EMSGSIZE; errno = EMSGSIZE;
} else { } else {
DEBUG("Awaiting more data"); DEBUG("Awaiting more data");
errno = ENODATA; errno = ENODATA;
} }
} else { } else {
WARN("Json parsing failed: %s", err.text); ERROR("Json parsing failed: %s", err.text);
errno = EINVAL; errno = EINVAL;
} }
return root; return root;
@ -241,36 +254,43 @@ static json_t* read_one_object(struct ctl_client* self, int timeout_ms)
return root; return root;
if (errno != ENODATA) if (errno != ENODATA)
return NULL; return NULL;
struct pollfd pfd = { struct pollfd pfd = {
.fd = self->fd, .fd = self->fd,
.events = POLLIN, .events = POLLIN,
.revents = 0 .revents = 0
}; };
while (root == NULL) {
while (!root) {
int n = poll(&pfd, 1, timeout_ms); int n = poll(&pfd, 1, timeout_ms);
if (n == -1) { if (n == -1) {
if (errno == EINTR && self->wait_for_events) if (errno == EINTR && self->wait_for_events)
continue; continue;
WARN("Error waiting for a response: %m"); ERROR("Error waiting for a response: %m");
break; break;
} else if (n == 0) { } else if (n == 0) {
WARN("Timeout waiting for a response"); ERROR("Timeout waiting for a response");
break; break;
} }
char* readptr = self->read_buffer + self->read_len; char* readptr = self->read_buffer + self->read_len;
size_t remainder = sizeof(self->read_buffer) - self->read_len; size_t remainder = sizeof(self->read_buffer) - self->read_len;
n = recv(self->fd, readptr, remainder, 0); n = recv(self->fd, readptr, remainder, 0);
if (n == -1) { if (n == -1) {
WARN("Read failed: %m"); ERROR("Read failed: %m");
break; break;
} else if (n == 0) { } else if (n == 0) {
WARN("Disconnected"); ERROR("Disconnected");
errno = ECONNRESET; errno = ECONNRESET;
break; break;
} }
DEBUG("Read %d bytes", n); DEBUG("Read %d bytes", n);
DEBUG("<< %.*s", n, readptr); DEBUG("<< %.*s", n, readptr);
self->read_len += n; self->read_len += n;
root = json_from_buffer(self); root = json_from_buffer(self);
if (!root && errno != ENODATA) if (!root && errno != ENODATA)
break; break;
@ -284,14 +304,17 @@ static struct jsonipc_response* ctl_client_wait_for_response(struct ctl_client*
json_t* root = read_one_object(self, 1000); json_t* root = read_one_object(self, 1000);
if (!root) if (!root)
return NULL; return NULL;
struct jsonipc_error jipc_err = JSONIPC_ERR_INIT; struct jsonipc_error jipc_err = JSONIPC_ERR_INIT;
struct jsonipc_response* response = jsonipc_response_parse_new(root, struct jsonipc_response* response = jsonipc_response_parse_new(root,
&jipc_err); &jipc_err);
if (!response) { if (!response) {
char* msg = json_dumps(jipc_err.data, JSON_EMBED); char* msg = json_dumps(jipc_err.data, JSON_EMBED);
WARN("Could not parse json: %s", msg); ERROR("Could not parse json: %s", msg);
free(msg); free(msg);
} }
json_decref(root); json_decref(root);
jsonipc_error_cleanup(&jipc_err); jsonipc_error_cleanup(&jipc_err);
return response; return response;
@ -299,9 +322,10 @@ static struct jsonipc_response* ctl_client_wait_for_response(struct ctl_client*
static void print_error(struct jsonipc_response* response, const char* method) static void print_error(struct jsonipc_response* response, const char* method)
{ {
printf("Error (%d)", response->code); printf("ERROR: Failed to execute command: %s", method);
if (!response->data) if (!response->data)
goto out; goto out;
json_t* data = response->data; json_t* data = response->data;
if (json_is_string(data)) if (json_is_string(data))
printf(": %s", json_string_value(data)); printf(": %s", json_string_value(data));
@ -310,6 +334,7 @@ static void print_error(struct jsonipc_response* response, const char* method)
printf(": %s", json_string_value(json_object_get(data, "error"))); printf(": %s", json_string_value(json_object_get(data, "error")));
else else
json_dumpf(response->data, stdout, JSON_INDENT(2)); json_dumpf(response->data, stdout, JSON_INDENT(2));
out: out:
printf("\n"); printf("\n");
} }
@ -325,29 +350,26 @@ static void pretty_version(json_t* data)
static void pretty_client_list(json_t* data) 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) ? ":" : ".");
size_t i; size_t i;
json_t* value; json_t* value;
json_array_foreach(data, i, value) { json_array_foreach(data, i, value) {
char* id = NULL; char* id = NULL;
char* hostname = NULL; char* hostname = NULL;
char* username = NULL; char* username = NULL;
json_unpack(value, "{s:s, s?s, s?s}", "id", &id, "hostname", json_unpack(value, "{s:s, s?s, s?s}", "id", &id, "hostname",
&hostname, "username", &username); &hostname, "username", &username);
printf(" client[%s]: ", id); printf(" %s: ", id);
if (username) if (username)
printf("%s@", username); printf("%s@", username);
printf("%s\n", hostname ? hostname : "<unknown>"); printf("%s\n", hostname ? hostname : "<unknown>");
} }
} }
static void pretty_output_list(json_t* data) static void pretty_output_list(json_t* data)
{ {
int n = json_array_size(data);
printf("There %s %d output%s%s\n", (n == 1) ? "is" : "are",
n, (n == 1) ? "" : "s", (n > 0) ? ":" : ".");
size_t i; size_t i;
json_t* value; json_t* value;
json_array_foreach(data, i, value) { json_array_foreach(data, i, value) {
@ -356,16 +378,18 @@ static void pretty_output_list(json_t* data)
int height = -1; int height = -1;
int width = -1; int width = -1;
int captured = false; int captured = false;
json_unpack(value, "{s:s, s:s, s:i, s:i, s:b}", "name", &name, json_unpack(value, "{s:s, s:s, s:i, s:i, s:b}", "name", &name,
"description", &description, "description", &description,
"height", &height, "height", &height,
"width", &width, "width", &width,
"captured", &captured); "captured", &captured);
printf("%s output[%s]: %s (%dx%d)\n", printf("%s %s: \"%s\" (%dx%d)\n",
captured ? "*" : " ", name, description, width, captured ? "*" : " ", name, description, width,
height); height);
} }
} }
static void pretty_print(json_t* data, static void pretty_print(json_t* data,
struct jsonipc_request* request) struct jsonipc_request* request)
{ {
@ -405,6 +429,7 @@ static int ctl_client_print_response(struct ctl_client* self,
struct jsonipc_response* response) struct jsonipc_response* response)
{ {
DEBUG("Response code: %d", response->code); DEBUG("Response code: %d", response->code);
if (response->data) { if (response->data) {
if (self->flags & CTL_CLIENT_PRINT_JSON) if (self->flags & CTL_CLIENT_PRINT_JSON)
print_compact_json(response->data); print_compact_json(response->data);
@ -413,10 +438,12 @@ static int ctl_client_print_response(struct ctl_client* self,
else else
print_error(response, request->method); print_error(response, request->method);
} }
return response->code; return response->code;
} }
static struct ctl_client* sig_target = NULL; static struct ctl_client* sig_target = NULL;
static void stop_loop(int signal) static void stop_loop(int signal)
{ {
sig_target->wait_for_events = false; sig_target->wait_for_events = false;
@ -441,9 +468,11 @@ static bool json_has_content(json_t* root)
{ {
if (!root) if (!root)
return false; return false;
size_t i; size_t i;
const char* key; const char* key;
json_t* value; json_t* value;
switch (json_typeof(root)) { switch (json_typeof(root)) {
case JSON_NULL: case JSON_NULL:
return false; return false;
@ -468,39 +497,34 @@ static bool json_has_content(json_t* root)
return false; return false;
} }
static void print_as_yaml(json_t* data, int level, bool needs_leading_newline) static void print_for_human(json_t* data, int level)
{ {
size_t i; size_t i;
const char* key; const char* key;
json_t* value; json_t* value;
bool needs_indent = needs_leading_newline;
switch(json_typeof(data)) { switch(json_typeof(data)) {
case JSON_NULL: case JSON_NULL:
printf("<null>\n"); printf("<null>\n");
break; break;
case JSON_OBJECT: case JSON_OBJECT:
if (json_object_size(data) > 0 && needs_leading_newline)
printf("\n");
json_object_foreach(data, key, value) { json_object_foreach(data, key, value) {
if (!json_has_content(value)) if (!json_has_content(value))
continue; continue;
if (needs_indent)
print_indent(level); print_indent(level);
else
needs_indent = true;
printf("%s: ", key); printf("%s: ", key);
print_as_yaml(value, level + 1, true); print_for_human(value, level + 1);
} }
break; break;
case JSON_ARRAY: case JSON_ARRAY:
if (json_array_size(data) > 0 && needs_leading_newline)
printf("\n");
json_array_foreach(data, i, value) { json_array_foreach(data, i, value) {
if (!json_has_content(value)) if (!json_has_content(value))
continue; continue;
print_indent(level); print_indent(level);
printf("- "); printf("- ");
print_as_yaml(value, level + 1, json_is_array(value)); print_for_human(value, level + 1);
} }
break; break;
case JSON_STRING: case JSON_STRING:
@ -526,11 +550,10 @@ static void print_event(struct jsonipc_request* event, unsigned flags)
if (flags & CTL_CLIENT_PRINT_JSON) { if (flags & CTL_CLIENT_PRINT_JSON) {
print_compact_json(event->json); print_compact_json(event->json);
} else { } else {
printf("\n%s:", event->method); printf("%s:\n", event->method);
if (event->params) if (event->params)
print_as_yaml(event->params, 1, true); print_for_human(event->params, 1);
else printf("\n");
printf("<<null>\n");
} }
fflush(stdout); fflush(stdout);
} }
@ -559,13 +582,15 @@ static ssize_t ctl_client_send_request(struct ctl_client* self,
json_error_t err; json_error_t err;
json_t* packed = jsonipc_request_pack(request, &err); json_t* packed = jsonipc_request_pack(request, &err);
if (!packed) { if (!packed) {
WARN("Could not encode json: %s", err.text); ERROR("Could not encode json: %s", err.text);
return -1; return -1;
} }
char buffer[512]; char buffer[512];
int len = json_dumpb(packed, buffer, sizeof(buffer), JSON_COMPACT); int len = json_dumpb(packed, buffer, sizeof(buffer), JSON_COMPACT);
json_decref(packed); json_decref(packed);
DEBUG(">> %.*s", len, buffer); DEBUG(">> %.*s", len, buffer);
return send(self->fd, buffer, len, MSG_NOSIGNAL); return send(self->fd, buffer, len, MSG_NOSIGNAL);
} }
@ -589,6 +614,7 @@ static int ctl_client_register_for_events(struct ctl_client* self,
jsonipc_response_destroy(response); jsonipc_response_destroy(response);
if (result == 0) if (result == 0)
send_startup_event(self); send_startup_event(self);
return result; return result;
} }
@ -597,6 +623,7 @@ static int ctl_client_reconnect_event_loop(struct ctl_client* self,
{ {
if (ctl_client_connect(self, timeout) != 0) if (ctl_client_connect(self, timeout) != 0)
return -1; return -1;
return ctl_client_register_for_events(self, request); return ctl_client_register_for_events(self, request);
} }
@ -628,6 +655,7 @@ static int ctl_client_event_loop(struct ctl_client* self,
print_event(event, self->flags); print_event(event, self->flags);
jsonipc_request_destroy(event); jsonipc_request_destroy(event);
} }
return 0; return 0;
} }
@ -638,6 +666,7 @@ static int ctl_client_print_single_command(struct ctl_client* self,
request); request);
if (!response) if (!response)
return 1; return 1;
int result = ctl_client_print_response(self, request, response); int result = ctl_client_print_response(self, request, response);
jsonipc_response_destroy(response); jsonipc_response_destroy(response);
return result; return result;
@ -652,6 +681,7 @@ void ctl_client_print_command_list(FILE* stream)
continue; continue;
max_namelen = MAX(max_namelen, strlen(ctl_command_list[i].name)); max_namelen = MAX(max_namelen, strlen(ctl_command_list[i].name));
} }
struct table_printer printer; struct table_printer printer;
table_printer_init(&printer, stdout, max_namelen); table_printer_init(&printer, stdout, max_namelen);
for (size_t i = 0; i < CMD_LIST_LEN; ++i) { for (size_t i = 0; i < CMD_LIST_LEN; ++i) {
@ -660,6 +690,7 @@ void ctl_client_print_command_list(FILE* stream)
table_printer_print_line(&printer, ctl_command_list[i].name, table_printer_print_line(&printer, ctl_command_list[i].name,
ctl_command_list[i].description); ctl_command_list[i].description);
} }
fprintf(stream, "\nRun 'wayvncctl command-name --help' for command-specific details.\n"); fprintf(stream, "\nRun 'wayvncctl command-name --help' for command-specific details.\n");
} }
@ -683,7 +714,8 @@ static void print_event_info(const struct cmd_info* info)
for (int i = 0; info->params[i].name != NULL; ++i) for (int i = 0; info->params[i].name != NULL; ++i)
table_printer_print_fmtline(&printer, table_printer_print_fmtline(&printer,
info->params[i].description, info->params[i].description,
"%s=%s", info->params[i].name, info->params[i].schema); "%s: %s", info->params[i].name,
info->params[i].schema);
printf("\n"); printf("\n");
} }
} }
@ -695,13 +727,15 @@ static int print_event_details(const char* evt_name)
print_event_info(info); print_event_info(info);
return 0; return 0;
} }
for (size_t i = 0; i < INTERNAL_EVT_LEN; ++i) { for (size_t i = 0; i < INTERNAL_EVT_LEN; ++i) {
if (strcmp(evt_name, internal_events[i].name) == 0) { if (strcmp(evt_name, internal_events[i].name) == 0) {
print_event_info(&internal_events[i]); print_event_info(&internal_events[i]);
return 0; return 0;
} }
} }
WARN("No such event \"%s\"\n", evt_name);
ERROR("No such event \"%s\"\n", evt_name);
return 1; return 1;
} }
@ -720,6 +754,7 @@ void ctl_client_print_event_list(FILE* stream)
for (size_t i = 0; i < EVT_LIST_LEN; ++i) for (size_t i = 0; i < EVT_LIST_LEN; ++i)
table_printer_print_line(&printer, ctl_event_list[i].name, table_printer_print_line(&printer, ctl_event_list[i].name,
ctl_event_list[i].description); ctl_event_list[i].description);
for (size_t i = 0; i < INTERNAL_EVT_LEN; ++i) for (size_t i = 0; i < INTERNAL_EVT_LEN; ++i)
table_printer_print_line(&printer, internal_events[i].name, table_printer_print_line(&printer, internal_events[i].name,
internal_events[i].description); internal_events[i].description);
@ -731,20 +766,23 @@ static int print_command_usage(struct ctl_client* self,
struct option_parser* parent_options) struct option_parser* parent_options)
{ {
if (self->flags & CTL_CLIENT_PRINT_JSON) { if (self->flags & CTL_CLIENT_PRINT_JSON) {
WARN("JSON output is not supported for \"help\" output"); ERROR("JSON output is not supported for \"help\" output");
return 1; return 1;
} }
struct cmd_info* info = ctl_command_by_type(cmd); struct cmd_info* info = ctl_command_by_type(cmd);
if (!info) { if (!info) {
WARN("No such command"); ERROR("No such command");
return 1; return 1;
} }
printf("Usage: wayvncctl [options] %s", info->name); printf("Usage: wayvncctl [options] %s", info->name);
option_parser_print_usage(cmd_options, stdout); option_parser_print_usage(cmd_options, stdout);
printf("\n"); printf("\n");
option_parser_print_cmd_summary(info->description, stdout); option_parser_print_cmd_summary(info->description, stdout);
if (option_parser_print_arguments(cmd_options, stdout)) if (option_parser_print_arguments(cmd_options, stdout))
printf("\n"); printf("\n");
option_parser_print_options(cmd_options, stdout); option_parser_print_options(cmd_options, stdout);
printf("\n"); printf("\n");
option_parser_print_options(parent_options, stdout); option_parser_print_options(parent_options, stdout);
@ -753,6 +791,7 @@ static int print_command_usage(struct ctl_client* self,
ctl_client_print_event_list(stdout); ctl_client_print_event_list(stdout);
printf("\n"); printf("\n");
} }
return 0; return 0;
} }
@ -772,8 +811,9 @@ int ctl_client_init_cmd_parser(struct option_parser* parser, enum cmd_type cmd)
size_t alloc_count = param_count + 2; size_t alloc_count = param_count + 2;
if (cmd == CMD_EVENT_RECEIVE) if (cmd == CMD_EVENT_RECEIVE)
alloc_count++; alloc_count++;
struct wv_option* options = calloc(alloc_count,
sizeof(struct wv_option)); struct wv_option* options = calloc(alloc_count, sizeof(*options));
size_t i = 0; size_t i = 0;
for (; i < param_count; ++i) { for (; i < param_count; ++i) {
struct wv_option* option = &options[i]; struct wv_option* option = &options[i];
@ -787,17 +827,20 @@ int ctl_client_init_cmd_parser(struct option_parser* parser, enum cmd_type cmd)
option->schema = param->schema; option->schema = param->schema;
} }
} }
if (cmd == CMD_EVENT_RECEIVE) { if (cmd == CMD_EVENT_RECEIVE) {
options[i].long_opt = "show"; options[i].long_opt = "show";
options[i].schema = "<event-name>"; options[i].schema = "<event-name>";
options[i].help = "Display details about the given event"; options[i].help = "Display details about the given event";
i++; i++;
} }
options[i].long_opt = "help"; options[i].long_opt = "help";
options[i].short_opt = 'h'; options[i].short_opt = 'h';
options[i].help = "Display this help text"; options[i].help = "Display this help text";
option_parser_init(parser, options); option_parser_init(parser, options);
parser->name = "Parameters"; parser->name = "Parameters";
return 0; return 0;
} }
@ -816,7 +859,7 @@ int ctl_client_run_command(struct ctl_client* self,
const char* method = option_parser_get_value(parent_options, "command"); const char* method = option_parser_get_value(parent_options, "command");
enum cmd_type cmd = ctl_command_parse_name(method); enum cmd_type cmd = ctl_command_parse_name(method);
if (cmd == CMD_UNKNOWN || cmd == CMD_HELP) { if (cmd == CMD_UNKNOWN || cmd == CMD_HELP) {
WARN("No such command \"%s\"\n", method); ERROR("No such command \"%s\"\n", method);
return 1; return 1;
} }
@ -833,6 +876,7 @@ int ctl_client_run_command(struct ctl_client* self,
&cmd_options, parent_options); &cmd_options, parent_options);
goto help_printed; goto help_printed;
} }
if (cmd == CMD_EVENT_RECEIVE && option_parser_get_value(&cmd_options, "show")) { if (cmd == CMD_EVENT_RECEIVE && option_parser_get_value(&cmd_options, "show")) {
result = print_event_details(option_parser_get_value(&cmd_options, "show")); result = print_event_details(option_parser_get_value(&cmd_options, "show"));
goto help_printed; goto help_printed;

View File

@ -50,7 +50,7 @@ struct cmd_info ctl_command_list[] = {
{ {
{ "id", { "id",
"The ID of the client to disconnect", "The ID of the client to disconnect",
"<id>", true }, "<integer>", true },
{}, {},
} }
}, },
@ -67,7 +67,7 @@ struct cmd_info ctl_command_list[] = {
{ {
{ "output-name", { "output-name",
"The specific output name to capture", "The specific output name to capture",
"<name>", true }, "<string>", true },
{}, {},
} }
}, },
@ -80,34 +80,34 @@ struct cmd_info ctl_command_list[] = {
#define CLIENT_EVENT_PARAMS(including) \ #define CLIENT_EVENT_PARAMS(including) \
{ "id", \ { "id", \
"A unique identifier for this client", \ "A unique identifier for this client", \
"<id>" }, \ "<integer>" }, \
{ "connection_count", \ { "connection_count", \
"The total number of connected VNC clients " including " this one.", \ "The total number of connected VNC clients " including " this one.", \
"<count>" }, \ "<integer>" }, \
{ "hostname", \ { "hostname", \
"The hostname or IP address of this client (may be null)", \ "The hostname or IP address of this client (may be null)", \
"<name|ip>" }, \ "<name|ip>" }, \
{ "username", \ { "username", \
"The username used to authentice this client (may be null).", \ "The username used to authentice this client (may be null).", \
"<name>" }, \ "<string>" }, \
{}, {},
struct cmd_info ctl_event_list[] = { struct cmd_info ctl_event_list[] = {
[EVT_CAPTURE_CHANGED] = {"capture-changed", [EVT_CAPTURE_CHANGED] = {"capture-changed",
"Sent by wayvnc when the catured output is changed", "Sent by wayvnc when the captured output is changed",
{ {
{ "output-name", { "output-name",
"The name of the output now being captured", "The name of the output now being captured",
"<name>" }, "<string>" },
{}, {},
}, },
}, },
[EVT_CLIENT_CONNECTED] = {"client-connected", [EVT_CLIENT_CONNECTED] = {"client-connected",
"Sent by wayvnc when a new vnc client connects", "Sent by wayvnc when a new VNC client connects",
{ CLIENT_EVENT_PARAMS("including") } { CLIENT_EVENT_PARAMS("including") }
}, },
[EVT_CLIENT_DISCONNECTED] = {"client-disconnected", [EVT_CLIENT_DISCONNECTED] = {"client-disconnected",
"Sent by waynvc when a vnc client disconnects", "Sent by waynvc when a VNC client disconnects",
{ CLIENT_EVENT_PARAMS("not including") } { CLIENT_EVENT_PARAMS("not including") }
}, },
}; };

View File

@ -138,8 +138,7 @@ static struct cmd_set_output* cmd_set_output_new(json_t* args,
{ {
const char* target = NULL; const char* target = NULL;
if (json_unpack(args, "{s:s}", "output-name", &target) == -1) { if (json_unpack(args, "{s:s}", "output-name", &target) == -1) {
jsonipc_error_printf(err, EINVAL, jsonipc_error_printf(err, EINVAL, "Missing output name");
"required: \"output-name\"");
return NULL; return NULL;
} }
struct cmd_set_output* cmd = calloc(1, sizeof(*cmd)); struct cmd_set_output* cmd = calloc(1, sizeof(*cmd));
@ -152,8 +151,7 @@ static struct cmd_disconnect_client* cmd_disconnect_client_new(json_t* args,
{ {
const char* id = NULL; const char* id = NULL;
if (json_unpack(args, "{s:s}", "id", &id) == -1) { if (json_unpack(args, "{s:s}", "id", &id) == -1) {
jsonipc_error_printf(err, EINVAL, jsonipc_error_printf(err, EINVAL, "Missing client id");
"required: \"id\"");
return NULL; return NULL;
} }
struct cmd_disconnect_client* cmd = calloc(1, sizeof(*cmd)); struct cmd_disconnect_client* cmd = calloc(1, sizeof(*cmd));
@ -202,7 +200,6 @@ static struct cmd* parse_command(struct jsonipc_request* ipc,
case CMD_OUTPUT_LIST: case CMD_OUTPUT_LIST:
case CMD_OUTPUT_CYCLE: case CMD_OUTPUT_CYCLE:
case CMD_WAYVNC_EXIT: case CMD_WAYVNC_EXIT:
cmd = calloc(1, sizeof(*cmd));
break; break;
case CMD_UNKNOWN: case CMD_UNKNOWN:
jsonipc_error_set_new(err, ENOENT, jsonipc_error_set_new(err, ENOENT,
@ -211,9 +208,10 @@ static struct cmd* parse_command(struct jsonipc_request* ipc,
jprintf("Unknown command \"%s\"", jprintf("Unknown command \"%s\"",
ipc->method), ipc->method),
"commands", list_allowed_commands())); "commands", list_allowed_commands()));
return NULL; break;
} }
cmd->type = cmd_type; if (cmd)
cmd->type = cmd_type;
return cmd; return cmd;
} }