aboutsummaryrefslogtreecommitdiff
path: root/dbus/client
diff options
context:
space:
mode:
Diffstat (limited to 'dbus/client')
-rw-r--r--dbus/client/dbus_client.c265
-rw-r--r--dbus/client/dbus_client.h36
2 files changed, 301 insertions, 0 deletions
diff --git a/dbus/client/dbus_client.c b/dbus/client/dbus_client.c
new file mode 100644
index 0000000..2fc9511
--- /dev/null
+++ b/dbus/client/dbus_client.c
@@ -0,0 +1,265 @@
+#include "dbus_client.h"
+#include "../protocol.h"
+
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <poll.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+// TODO: Error checking for write/read
+
+static bool gsr_dbus_client_wait_for_startup(gsr_dbus_client *self) {
+ struct pollfd poll_fd = {
+ .fd = self->socket_pair[0],
+ .events = POLLIN,
+ .revents = 0
+ };
+ for(;;) {
+ int poll_res = poll(&poll_fd, 1, 100);
+ if(poll_res > 0 && (poll_fd.revents & POLLIN)) {
+ char msg;
+ read(self->socket_pair[0], &msg, 1);
+ return true;
+ } else {
+ int status = 0;
+ int wait_result = waitpid(self->pid, &status, WNOHANG);
+ if(wait_result != 0) {
+ int exit_code = -1;
+ if(WIFEXITED(status))
+ exit_code = WEXITSTATUS(status);
+ fprintf(stderr, "gsr error: gsr_dbus_client_init: server side or never started, exit code: %d\n", exit_code);
+ self->pid = 0;
+ return false;
+ }
+ }
+ }
+}
+
+bool gsr_dbus_client_init(gsr_dbus_client *self, const char *screencast_restore_token) {
+ memset(self, 0, sizeof(*self));
+
+ if(socketpair(AF_UNIX, SOCK_STREAM, 0, self->socket_pair) == -1) {
+ fprintf(stderr, "gsr error: gsr_dbus_client_init: socketpair failed, error: %s\n", strerror(errno));
+ return false;
+ }
+
+ if(screencast_restore_token) {
+ self->screencast_restore_token = strdup(screencast_restore_token);
+ if(!self->screencast_restore_token) {
+ fprintf(stderr, "gsr error: gsr_dbus_client_init: failed to clone restore token\n");
+ gsr_dbus_client_deinit(self);
+ return false;
+ }
+ }
+
+ self->pid = fork();
+ if(self->pid == -1) {
+ fprintf(stderr, "gsr error: gsr_dbus_client_init: failed to fork process\n");
+ gsr_dbus_client_deinit(self);
+ return false;
+ } else if(self->pid == 0) { /* child */
+ char socket_pair_server_str[32];
+ snprintf(socket_pair_server_str, sizeof(socket_pair_server_str), "%d", self->socket_pair[1]);
+
+ const char *args[] = { "gsr-dbus-server", socket_pair_server_str, self->screencast_restore_token ? self->screencast_restore_token : "", NULL };
+ execvp(args[0], (char *const*)args);
+
+ fprintf(stderr, "gsr error: gsr_dbus_client_init: execvp failed, error: %s\n", strerror(errno));
+ _exit(127);
+ } else { /* parent */
+ if(!gsr_dbus_client_wait_for_startup(self)) {
+ gsr_dbus_client_deinit(self);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void gsr_dbus_client_deinit(gsr_dbus_client *self) {
+ for(int i = 0; i < 2; ++i) {
+ if(self->socket_pair[i] > 0) {
+ close(self->socket_pair[i]);
+ self->socket_pair[i] = -1;
+ }
+ }
+
+ if(self->screencast_restore_token) {
+ free(self->screencast_restore_token);
+ self->screencast_restore_token = NULL;
+ }
+
+ if(self->pid > 0) {
+ kill(self->pid, SIGKILL);
+ int status = 0;
+ waitpid(self->pid, &status, 0);
+ self->pid = 0;
+ }
+}
+
+int gsr_dbus_client_screencast_create_session(gsr_dbus_client *self, char *session_handle, size_t session_handle_size) {
+ const gsr_dbus_request_message request = {
+ .protocol_version = GSR_DBUS_PROTOCOL_VERSION,
+ .type = GSR_DBUS_MESSAGE_REQ_CREATE_SESSION,
+ .create_session = (gsr_dbus_message_req_create_session) {}
+ };
+ write(self->socket_pair[0], &request, sizeof(request));
+
+ gsr_dbus_response_message response = {0};
+ read(self->socket_pair[0], &response, sizeof(response));
+
+ if(response.protocol_version != GSR_DBUS_PROTOCOL_VERSION) {
+ fprintf(stderr, "gsr error: gsr_dbus_client_screencast_create_session: server uses protocol version %d while the client is using protocol version %d", response.protocol_version, GSR_DBUS_PROTOCOL_VERSION);
+ return -1;
+ }
+
+ if(response.type == GSR_DBUS_MESSAGE_RESP_ERROR) {
+ fprintf(stderr, "gsr error: gsr_dbus_client_screencast_create_session: server return error: %s (%d)\n", response.error.message, (int)response.error.error_code);
+ return response.error.error_code;
+ }
+
+ if(response.type != GSR_DBUS_MESSAGE_RESP_CREATE_SESSION) {
+ fprintf(stderr, "gsr error: gsr_dbus_client_screencast_create_session: received incorrect response type. Expected %d got %d\n", GSR_DBUS_MESSAGE_RESP_CREATE_SESSION, response.type);
+ return -1;
+ }
+
+ snprintf(session_handle, session_handle_size, "%s", response.create_session.session_handle);
+ return 0;
+}
+
+int gsr_dbus_client_screencast_select_sources(gsr_dbus_client *self, const char *session_handle, uint32_t capture_type, uint32_t cursor_mode) {
+ gsr_dbus_request_message request = {
+ .protocol_version = GSR_DBUS_PROTOCOL_VERSION,
+ .type = GSR_DBUS_MESSAGE_REQ_SELECT_SOURCES,
+ .select_sources = (gsr_dbus_message_req_select_sources) {
+ .capture_type = capture_type,
+ .cursor_mode = cursor_mode
+ }
+ };
+ snprintf(request.select_sources.session_handle, sizeof(request.select_sources.session_handle), "%s", session_handle);
+ write(self->socket_pair[0], &request, sizeof(request));
+
+ gsr_dbus_response_message response = {0};
+ read(self->socket_pair[0], &response, sizeof(response));
+
+ if(response.protocol_version != GSR_DBUS_PROTOCOL_VERSION) {
+ fprintf(stderr, "gsr error: gsr_dbus_client_screencast_select_sources: server uses protocol version %d while the client is using protocol version %d", response.protocol_version, GSR_DBUS_PROTOCOL_VERSION);
+ return -1;
+ }
+
+ if(response.type == GSR_DBUS_MESSAGE_RESP_ERROR) {
+ fprintf(stderr, "gsr error: gsr_dbus_client_screencast_select_sources: server return error: %s (%d)\n", response.error.message, (int)response.error.error_code);
+ return response.error.error_code;
+ }
+
+ if(response.type != GSR_DBUS_MESSAGE_RESP_SELECT_SOURCES) {
+ fprintf(stderr, "gsr error: gsr_dbus_client_screencast_select_sources: received incorrect response type. Expected %d got %d\n", GSR_DBUS_MESSAGE_RESP_SELECT_SOURCES, response.type);
+ return -1;
+ }
+
+ return 0;
+}
+
+int gsr_dbus_client_screencast_start(gsr_dbus_client *self, const char *session_handle, uint32_t *pipewire_node) {
+ *pipewire_node = 0;
+
+ gsr_dbus_request_message request = {
+ .protocol_version = GSR_DBUS_PROTOCOL_VERSION,
+ .type = GSR_DBUS_MESSAGE_REQ_START,
+ .start = (gsr_dbus_message_req_start) {}
+ };
+ snprintf(request.start.session_handle, sizeof(request.start.session_handle), "%s", session_handle);
+ write(self->socket_pair[0], &request, sizeof(request));
+
+ gsr_dbus_response_message response = {0};
+ read(self->socket_pair[0], &response, sizeof(response));
+
+ if(response.protocol_version != GSR_DBUS_PROTOCOL_VERSION) {
+ fprintf(stderr, "gsr error: gsr_dbus_client_screencast_start: server uses protocol version %d while the client is using protocol version %d", response.protocol_version, GSR_DBUS_PROTOCOL_VERSION);
+ return -1;
+ }
+
+ if(response.type == GSR_DBUS_MESSAGE_RESP_ERROR) {
+ fprintf(stderr, "gsr error: gsr_dbus_client_screencast_start: server return error: %s (%d)\n", response.error.message, (int)response.error.error_code);
+ return response.error.error_code;
+ }
+
+ if(response.type != GSR_DBUS_MESSAGE_RESP_START) {
+ fprintf(stderr, "gsr error: gsr_dbus_client_screencast_start: received incorrect response type. Expected %d got %d\n", GSR_DBUS_MESSAGE_RESP_START, response.type);
+ return -1;
+ }
+
+ if(self->screencast_restore_token) {
+ free(self->screencast_restore_token);
+ if(response.start.restore_token[0] == '\0')
+ self->screencast_restore_token = NULL;
+ else
+ self->screencast_restore_token = strdup(response.start.restore_token);
+ }
+
+ *pipewire_node = response.start.pipewire_node;
+ return 0;
+}
+
+bool gsr_dbus_client_screencast_open_pipewire_remote(gsr_dbus_client *self, const char *session_handle, int *pipewire_fd) {
+ *pipewire_fd = 0;
+
+ gsr_dbus_request_message request = {
+ .protocol_version = GSR_DBUS_PROTOCOL_VERSION,
+ .type = GSR_DBUS_MESSAGE_REQ_OPEN_PIPEWIRE_REMOTE,
+ .open_pipewire_remote = (gsr_dbus_message_req_open_pipewire_remote) {}
+ };
+ snprintf(request.open_pipewire_remote.session_handle, sizeof(request.open_pipewire_remote.session_handle), "%s", session_handle);
+ write(self->socket_pair[0], &request, sizeof(request));
+
+ gsr_dbus_response_message response = {0};
+ struct iovec iov = {
+ .iov_base = &response,
+ .iov_len = sizeof(response)
+ };
+
+ char msg_control[CMSG_SPACE(sizeof(int))];
+
+ struct msghdr message = {
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = msg_control,
+ .msg_controllen = sizeof(msg_control)
+ };
+
+ const int bla = recvmsg(self->socket_pair[0], &message, MSG_WAITALL);
+ (void)bla;
+
+ if(response.protocol_version != GSR_DBUS_PROTOCOL_VERSION) {
+ fprintf(stderr, "gsr error: gsr_dbus_client_screencast_open_pipewire_remote: server uses protocol version %d while the client is using protocol version %d", response.protocol_version, GSR_DBUS_PROTOCOL_VERSION);
+ return false;
+ }
+
+ if(response.type == GSR_DBUS_MESSAGE_RESP_ERROR) {
+ fprintf(stderr, "gsr error: gsr_dbus_client_screencast_open_pipewire_remote: server return error: %s (%d)\n", response.error.message, (int)response.error.error_code);
+ return false;
+ }
+
+ if(response.type != GSR_DBUS_MESSAGE_RESP_OPEN_PIPEWIRE_REMOTE) {
+ fprintf(stderr, "gsr error: gsr_dbus_client_screencast_open_pipewire_remote: received incorrect response type. Expected %d got %d\n", GSR_DBUS_MESSAGE_RESP_OPEN_PIPEWIRE_REMOTE, response.type);
+ return false;
+ }
+
+ struct cmsghdr *cmsg = CMSG_FIRSTHDR(&message);
+ if(!cmsg || cmsg->cmsg_type != SCM_RIGHTS) {
+ fprintf(stderr, "gsr error: gsr_dbus_client_screencast_open_pipewire_remote: returned message data is missing file descriptor\n");
+ return false;
+ }
+
+ memcpy(pipewire_fd, CMSG_DATA(cmsg), sizeof(*pipewire_fd));
+ return true;
+}
+
+const char* gsr_dbus_client_screencast_get_restore_token(gsr_dbus_client *self) {
+ return self->screencast_restore_token;
+}
diff --git a/dbus/client/dbus_client.h b/dbus/client/dbus_client.h
new file mode 100644
index 0000000..c9e8182
--- /dev/null
+++ b/dbus/client/dbus_client.h
@@ -0,0 +1,36 @@
+#ifndef GSR_DBUS_CLIENT_H
+#define GSR_DBUS_CLIENT_H
+
+/*
+ Using a client-server architecture is needed for dbus because cap_sys_nice doesn't work with desktop portal.
+ The main binary has cap_sys_nice and we launch a new child-process without it which uses uses desktop portal.
+*/
+
+#include "../portal.h"
+#include <stdbool.h>
+#include <stdint.h>
+#include <signal.h>
+
+typedef struct {
+ int socket_pair[2];
+ char *screencast_restore_token;
+ pid_t pid;
+} gsr_dbus_client;
+
+/* Blocking. TODO: Make non-blocking */
+bool gsr_dbus_client_init(gsr_dbus_client *self, const char *screencast_restore_token);
+void gsr_dbus_client_deinit(gsr_dbus_client *self);
+
+/* The follow functions should be called in order to setup ScreenCast properly */
+/* These functions that return an int return the response status code */
+int gsr_dbus_client_screencast_create_session(gsr_dbus_client *self, char *session_handle, size_t session_handle_size);
+/*
+ |capture_type| is a bitmask of gsr_portal_capture_type values. gsr_portal_capture_type values that are not supported by the desktop portal will be ignored.
+ |gsr_portal_cursor_mode| is a bitmask of gsr_portal_cursor_mode values. gsr_portal_cursor_mode values that are not supported will be ignored.
+*/
+int gsr_dbus_client_screencast_select_sources(gsr_dbus_client *self, const char *session_handle, uint32_t capture_type, uint32_t cursor_mode);
+int gsr_dbus_client_screencast_start(gsr_dbus_client *self, const char *session_handle, uint32_t *pipewire_node);
+bool gsr_dbus_client_screencast_open_pipewire_remote(gsr_dbus_client *self, const char *session_handle, int *pipewire_fd);
+const char* gsr_dbus_client_screencast_get_restore_token(gsr_dbus_client *self);
+
+#endif /* GSR_DBUS_CLIENT_H */