Add wayvncctl --wait option

Signed-off-by: Jim Ramsay <i.am@jimramsay.com>
pull/186/head
Jim Ramsay 2022-11-15 04:57:26 -05:00 committed by Andri Yngvason
parent 372b530d3a
commit 6e13974b27
4 changed files with 79 additions and 19 deletions

View File

@ -28,5 +28,6 @@ void* ctl_client_userdata(struct ctl_client*);
#define PRINT_JSON 0x00000001 #define PRINT_JSON 0x00000001
int ctl_client_connect(struct ctl_client* self, int timeout);
int ctl_client_run_command(struct ctl_client* self, int ctl_client_run_command(struct ctl_client* self,
int argc, char* argv[], unsigned flags); int argc, char* argv[], unsigned flags);

View File

@ -20,8 +20,10 @@
#include <errno.h> #include <errno.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/un.h> #include <sys/un.h>
#include <sys/stat.h>
#include <poll.h> #include <poll.h>
#include <signal.h> #include <signal.h>
#include <assert.h>
#include <jansson.h> #include <jansson.h>
#include "json-ipc.h" #include "json-ipc.h"
@ -39,11 +41,12 @@ static bool do_debug = false;
if (do_debug) \ if (do_debug) \
fprintf(stderr, "[%s:%d] " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__); fprintf(stderr, "[%s:%d] " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__);
#define FAILED_TO(action) \ #define FAILED_TO(action, ...) \
WARN("Failed to " action ": %m"); WARN("Failed to " action ": %m", ##__VA_ARGS__);
struct ctl_client { struct ctl_client {
void* userdata; void* userdata;
struct sockaddr_un addr;
char read_buffer[512]; char read_buffer[512];
size_t read_len; size_t read_len;
@ -65,37 +68,76 @@ struct ctl_client* ctl_client_new(const char* socket_path, void* userdata)
struct ctl_client* new = calloc(1, sizeof(*new)); struct ctl_client* new = calloc(1, sizeof(*new));
new->userdata = userdata; new->userdata = userdata;
struct sockaddr_un addr = { if (strlen(socket_path) >= sizeof(new->addr.sun_path)) {
.sun_family = AF_UNIX,
};
if (strlen(socket_path) >= sizeof(addr.sun_path)) {
errno = ENAMETOOLONG; errno = ENAMETOOLONG;
FAILED_TO("create unix socket"); FAILED_TO("create unix socket");
goto socket_failure; goto socket_failure;
} }
strcpy(addr.sun_path, socket_path); strcpy(new->addr.sun_path, socket_path);
new->addr.sun_family = AF_UNIX;
new->fd = socket(AF_UNIX, SOCK_STREAM, 0); new->fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (new->fd < 0) { if (new->fd < 0) {
FAILED_TO("create unix socket"); FAILED_TO("create unix socket");
goto socket_failure; goto socket_failure;
} }
if (connect(new->fd, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
FAILED_TO("connect to unix socket");
goto connect_failure;
}
return new; return new;
connect_failure:
close(new->fd);
socket_failure: socket_failure:
free(new); free(new);
return NULL; return NULL;
} }
int ctl_client_connect(struct ctl_client* self, int timeout)
{
// TODO: Support arbitrary timeouts?
assert(timeout == 0 || timeout == -1);
// TODO: Use inotify instead of polling stat()
bool needs_log = true;
struct stat sb;
while (stat(self->addr.sun_path, &sb) != 0) {
if (timeout == 0) {
FAILED_TO("find socket path \"%s\"",
self->addr.sun_path);
goto stat_failure;
}
if (needs_log) {
needs_log = false;
DEBUG("Waiting for socket path \"%s\" to appear",
self->addr.sun_path);
}
if (usleep(50000) == -1) {
FAILED_TO("wait for socket path");
goto stat_failure;
}
}
if (S_ISSOCK(sb.st_mode)) {
DEBUG("Found socket \"%s\"", self->addr.sun_path);
} else {
WARN("Path \"%s\" exists but is not a socket (0x%x)",
self->addr.sun_path, sb.st_mode);
goto stat_failure;
}
while (connect(self->fd, (struct sockaddr*)&self->addr,
sizeof(self->addr)) != 0) {
if (timeout == 0 || errno != ENOENT) {
FAILED_TO("connect to unix socket \"%s\"",
self->addr.sun_path);
goto stat_failure;
return 1;
}
if (usleep(50000) == -1) {
FAILED_TO("wait for connect to succeed");
return 1;
}
}
return 0;
stat_failure:
return 1;
}
void ctl_client_destroy(struct ctl_client* self) void ctl_client_destroy(struct ctl_client* self)
{ {
close(self->fd); close(self->fd);
@ -107,7 +149,7 @@ void* ctl_client_userdata(struct ctl_client* self)
return self->userdata; return self->userdata;
} }
static struct jsonipc_request* ctl_client_parse_args(struct ctl_client* self, static struct jsonipc_request* ctl_client_parse_args(struct ctl_client* self,
int argc, char* argv[]) int argc, char* argv[])
{ {
struct jsonipc_request* request = NULL; struct jsonipc_request* request = NULL;

View File

@ -53,6 +53,8 @@ static int wayvncctl_usage(FILE* stream, int rc)
"Options:\n" "Options:\n"
" -S,--socket=<path> Wayvnc control socket path.\n" " -S,--socket=<path> Wayvnc control socket path.\n"
" Default: $XDG_RUNTIME_DIR/wayvncctl\n" " Default: $XDG_RUNTIME_DIR/wayvncctl\n"
" -w,--wait Wait for wayvnc to start up if it's\n"
" not already running.\n"
" -j,--json Output json on stdout.\n" " -j,--json Output json on stdout.\n"
" -V,--version Show version info.\n" " -V,--version Show version info.\n"
" -v,--verbose Be more verbose.\n" " -v,--verbose Be more verbose.\n"
@ -74,15 +76,17 @@ int main(int argc, char* argv[])
{ {
struct wayvncctl self = { 0 }; struct wayvncctl self = { 0 };
static const char* shortopts = "+S:hVvj"; static const char* shortopts = "+S:hVvjw";
bool verbose = false; bool verbose = false;
const char* socket_path = NULL; const char* socket_path = NULL;
int wait_for_socket = 0;
unsigned flags = 0; unsigned flags = 0;
static const struct option longopts[] = { static const struct option longopts[] = {
{ "socket", required_argument, NULL, 'S' }, { "socket", required_argument, NULL, 'S' },
{ "wait", no_argument, NULL, 'w' },
{ "json", no_argument, NULL, 'j' }, { "json", no_argument, NULL, 'j' },
{ "help", no_argument, NULL, 'h' }, { "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' }, { "version", no_argument, NULL, 'V' },
@ -99,6 +103,9 @@ int main(int argc, char* argv[])
case 'S': case 'S':
socket_path = optarg; socket_path = optarg;
break; break;
case 'w':
wait_for_socket = -1;
break;
case 'j': case 'j':
flags |= PRINT_JSON; flags |= PRINT_JSON;
break; break;
@ -117,12 +124,18 @@ int main(int argc, char* argv[])
argv = &argv[optind]; argv = &argv[optind];
ctl_client_debug_log(verbose); ctl_client_debug_log(verbose);
self.ctl = ctl_client_new(socket_path, &self); self.ctl = ctl_client_new(socket_path, &self);
if (!self.ctl) if (!self.ctl)
goto ctl_client_failure; goto ctl_client_failure;
int result = ctl_client_run_command(self.ctl, argc, argv, flags); int result = ctl_client_connect(self.ctl, wait_for_socket);
if (result != 0)
goto socket_failure;
result = ctl_client_run_command(self.ctl, argc, argv, flags);
socket_failure:
ctl_client_destroy(self.ctl); ctl_client_destroy(self.ctl);
return result; return result;

View File

@ -14,6 +14,10 @@ wayvncctl - A command line control client for wayvnc(1)
Set wayvnc control socket path. Default: $XDG_RUNTIME_DIR/wayvncctl Set wayvnc control socket path. Default: $XDG_RUNTIME_DIR/wayvncctl
or /tmp/wayvncctl-$UID or /tmp/wayvncctl-$UID
*-w, --wait*
Wait for wayvnc to start up if it's not already running. Default: Exit
immediately with an error if wayvnc is not running.
*-j, --json* *-j, --json*
Produce json output to stdout. Produce json output to stdout.