kms: remove unix domain socket and only use socketpair

This commit is contained in:
dec05eba
2025-07-06 17:27:46 +02:00
parent 469e234841
commit 46febed35e
5 changed files with 25 additions and 226 deletions

6
TODO
View File

@@ -308,4 +308,8 @@ Add proper check if opengl functions are supported. dlsym for the symbol will re
Colors are offset to bottom left by 1 pixel or so on steam deck in landscape mode. Colors are offset to bottom left by 1 pixel or so on steam deck in landscape mode.
When constant framerate is used (and for audio) multiple frames need to be encoded after resuming from suspend. When constant framerate is used (and for audio) multiple frames need to be encoded after resuming from suspend.
The clock jumps forward by around 2-3 seconds (on my machine). Is there a way to make sure the clock doesn't jump forward? The clock jumps forward by around 2-3 seconds (on my machine). Is there a way to make sure the clock doesn't jump forward?
Colors are correct, but they look incorrect for thin elements, such as colored text. This can be improved by sampling neighbor pixels for color average.
Record first video/audio frame immediately.

View File

@@ -1,5 +1,4 @@
#include "kms_client.h" #include "kms_client.h"
#include "../../include/utils.h"
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
@@ -9,17 +8,13 @@
#include <stdbool.h> #include <stdbool.h>
#include <fcntl.h> #include <fcntl.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/un.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <poll.h>
#include <sys/stat.h>
#include <sys/capability.h> #include <sys/capability.h>
#define GSR_SOCKET_PAIR_LOCAL 0 #define GSR_SOCKET_PAIR_LOCAL 0
#define GSR_SOCKET_PAIR_REMOTE 1 #define GSR_SOCKET_PAIR_REMOTE 1
static void cleanup_socket(gsr_kms_client *self, bool kill_server); static void cleanup_socket(gsr_kms_client *self, bool kill_server);
static int gsr_kms_client_replace_connection(gsr_kms_client *self);
static void close_fds(gsr_kms_response *response) { static void close_fds(gsr_kms_response *response) {
for(int i = 0; i < response->num_items; ++i) { for(int i = 0; i < response->num_items; ++i) {
@@ -117,21 +112,6 @@ static int recv_msg_from_server(int server_pid, int server_fd, gsr_kms_response
return res; return res;
} }
/* We have to use $HOME because in flatpak there is no simple path that is accessible, read and write, that multiple flatpak instances can access */
static bool create_socket_path(char *output_path, size_t output_path_size) {
const char *home = getenv("HOME");
if(!home)
home = "/tmp";
char random_characters[11];
random_characters[10] = '\0';
if(!generate_random_characters_standard_alphabet(random_characters, 10))
return false;
snprintf(output_path, output_path_size, "%s/.gsr-kms-socket-%s", home, random_characters);
return true;
}
static bool readlink_realpath(const char *filepath, char *buffer) { static bool readlink_realpath(const char *filepath, char *buffer) {
char symlinked_path[PATH_MAX]; char symlinked_path[PATH_MAX];
ssize_t bytes_written = readlink(filepath, symlinked_path, sizeof(symlinked_path) - 1); ssize_t bytes_written = readlink(filepath, symlinked_path, sizeof(symlinked_path) - 1);
@@ -206,18 +186,8 @@ static bool find_program_in_path(const char *program_name, char *filepath, int f
int gsr_kms_client_init(gsr_kms_client *self, const char *card_path) { int gsr_kms_client_init(gsr_kms_client *self, const char *card_path) {
int result = -1; int result = -1;
self->kms_server_pid = -1; self->kms_server_pid = -1;
self->initial_socket_fd = -1;
self->initial_client_fd = -1;
self->initial_socket_path[0] = '\0';
self->socket_pair[0] = -1; self->socket_pair[0] = -1;
self->socket_pair[1] = -1; self->socket_pair[1] = -1;
struct sockaddr_un local_addr = {0};
struct sockaddr_un remote_addr = {0};
if(!create_socket_path(self->initial_socket_path, sizeof(self->initial_socket_path))) {
fprintf(stderr, "gsr error: gsr_kms_client_init: failed to create path to kms socket\n");
return -1;
}
char server_filepath[PATH_MAX]; char server_filepath[PATH_MAX];
if(!readlink_realpath("/proc/self/exe", server_filepath)) { if(!readlink_realpath("/proc/self/exe", server_filepath)) {
@@ -239,7 +209,7 @@ int gsr_kms_client_init(gsr_kms_client *self, const char *card_path) {
} }
} }
fprintf(stderr, "gsr info: gsr_kms_client_init: setting up connection to %s\n", server_filepath); fprintf(stderr, "gsr info: gsr_kms_client_init: launching gsr-kms-server at %s\n", server_filepath);
const bool inside_flatpak = getenv("FLATPAK_ID") != NULL; const bool inside_flatpak = getenv("FLATPAK_ID") != NULL;
const char *home = getenv("HOME"); const char *home = getenv("HOME");
@@ -274,90 +244,34 @@ int gsr_kms_client_init(gsr_kms_client *self, const char *card_path) {
goto err; goto err;
} }
self->initial_socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if(self->initial_socket_fd == -1) {
fprintf(stderr, "gsr error: gsr_kms_client_init: socket failed, error: %s\n", strerror(errno));
goto err;
}
local_addr.sun_family = AF_UNIX;
snprintf(local_addr.sun_path, sizeof(local_addr.sun_path), "%s", (const char*)self->initial_socket_path);
const mode_t prev_mask = umask(0000);
const int bind_res = bind(self->initial_socket_fd, (struct sockaddr*)&local_addr, sizeof(local_addr.sun_family) + strlen(local_addr.sun_path));
umask(prev_mask);
if(bind_res == -1) {
fprintf(stderr, "gsr error: gsr_kms_client_init: failed to bind socket, error: %s\n", strerror(errno));
goto err;
}
if(listen(self->initial_socket_fd, 1) == -1) {
fprintf(stderr, "gsr error: gsr_kms_client_init: failed to listen on socket, error: %s\n", strerror(errno));
goto err;
}
pid_t pid = fork(); pid_t pid = fork();
if(pid == -1) { if(pid == -1) {
fprintf(stderr, "gsr error: gsr_kms_client_init: fork failed, error: %s\n", strerror(errno)); fprintf(stderr, "gsr error: gsr_kms_client_init: fork failed, error: %s\n", strerror(errno));
goto err; goto err;
} else if(pid == 0) { /* child */ } else if(pid == 0) { /* child */
char socket_pair_remote_str[32];
snprintf(socket_pair_remote_str, sizeof(socket_pair_remote_str), "%d", self->socket_pair[GSR_SOCKET_PAIR_REMOTE]);
if(inside_flatpak) { if(inside_flatpak) {
const char *args[] = { "flatpak-spawn", "--host", "/var/lib/flatpak/app/com.dec05eba.gpu_screen_recorder/current/active/files/bin/kms-server-proxy", self->initial_socket_path, card_path, home, NULL }; char forward_fd_arg[128];
snprintf(forward_fd_arg, sizeof(forward_fd_arg), "--forward-fd=%s", socket_pair_remote_str);
const char *args[] = { "flatpak-spawn", "--host", forward_fd_arg, "/var/lib/flatpak/app/com.dec05eba.gpu_screen_recorder/current/active/files/bin/kms-server-proxy", socket_pair_remote_str, card_path, home, NULL };
execvp(args[0], (char *const*)args); execvp(args[0], (char *const*)args);
} else if(has_perm) { } else if(has_perm) {
const char *args[] = { server_filepath, self->initial_socket_path, card_path, NULL }; const char *args[] = { server_filepath, socket_pair_remote_str, card_path, NULL };
execvp(args[0], (char *const*)args); execvp(args[0], (char *const*)args);
} else { } else {
const char *args[] = { "pkexec", server_filepath, self->initial_socket_path, card_path, NULL }; const char *args[] = { "pkexec", server_filepath, socket_pair_remote_str, card_path, NULL };
execvp(args[0], (char *const*)args); execvp(args[0], (char *const*)args);
} }
fprintf(stderr, "gsr error: gsr_kms_client_init: failed to launch \"gsr-kms-server\", error: %s\n", strerror(errno)); fprintf(stderr, "gsr error: gsr_kms_client_init: failed to launch \"gsr-kms-server\", error: %s\n", strerror(errno));
_exit(127); _exit(127);
} else { /* parent */ } else { /* parent */
self->kms_server_pid = pid; self->kms_server_pid = pid;
} }
fprintf(stderr, "gsr info: gsr_kms_client_init: waiting for server to connect\n");
struct pollfd poll_fd = {
.fd = self->initial_socket_fd,
.events = POLLIN,
.revents = 0
};
for(;;) {
int poll_res = poll(&poll_fd, 1, 100);
if(poll_res > 0 && (poll_fd.revents & POLLIN)) {
socklen_t sock_len = 0;
self->initial_client_fd = accept(self->initial_socket_fd, (struct sockaddr*)&remote_addr, &sock_len);
if(self->initial_client_fd == -1) {
fprintf(stderr, "gsr error: gsr_kms_client_init: accept failed on socket, error: %s\n", strerror(errno));
goto err;
}
break;
} else {
int status = 0;
int wait_result = waitpid(self->kms_server_pid, &status, WNOHANG);
if(wait_result != 0) {
int exit_code = -1;
if(WIFEXITED(status))
exit_code = WEXITSTATUS(status);
fprintf(stderr, "gsr error: gsr_kms_client_init: kms server died or never started, exit code: %d\n", exit_code);
self->kms_server_pid = -1;
if(exit_code != 0)
result = exit_code;
goto err;
}
}
}
fprintf(stderr, "gsr info: gsr_kms_client_init: server connected\n");
fprintf(stderr, "gsr info: replacing file-backed unix domain socket with socketpair\n");
if(gsr_kms_client_replace_connection(self) != 0)
goto err;
cleanup_socket(self, false);
fprintf(stderr, "gsr info: using socketpair\n");
return 0; return 0;
err: err:
@@ -366,16 +280,6 @@ int gsr_kms_client_init(gsr_kms_client *self, const char *card_path) {
} }
void cleanup_socket(gsr_kms_client *self, bool kill_server) { void cleanup_socket(gsr_kms_client *self, bool kill_server) {
if(self->initial_client_fd > 0) {
close(self->initial_client_fd);
self->initial_client_fd = -1;
}
if(self->initial_socket_fd > 0) {
close(self->initial_socket_fd);
self->initial_socket_fd = -1;
}
if(kill_server) { if(kill_server) {
for(int i = 0; i < 2; ++i) { for(int i = 0; i < 2; ++i) {
if(self->socket_pair[i] > 0) { if(self->socket_pair[i] > 0) {
@@ -392,50 +296,12 @@ void cleanup_socket(gsr_kms_client *self, bool kill_server) {
//waitpid(self->kms_server_pid, &status, 0); //waitpid(self->kms_server_pid, &status, 0);
self->kms_server_pid = -1; self->kms_server_pid = -1;
} }
if(self->initial_socket_path[0] != '\0') {
remove(self->initial_socket_path);
self->initial_socket_path[0] = '\0';
}
} }
void gsr_kms_client_deinit(gsr_kms_client *self) { void gsr_kms_client_deinit(gsr_kms_client *self) {
cleanup_socket(self, true); cleanup_socket(self, true);
} }
int gsr_kms_client_replace_connection(gsr_kms_client *self) {
gsr_kms_response response;
response.version = 0;
response.result = KMS_RESULT_FAILED_TO_SEND;
response.err_msg[0] = '\0';
gsr_kms_request request;
request.version = GSR_KMS_PROTOCOL_VERSION;
request.type = KMS_REQUEST_TYPE_REPLACE_CONNECTION;
request.new_connection_fd = self->socket_pair[GSR_SOCKET_PAIR_REMOTE];
if(send_msg_to_server(self->initial_client_fd, &request) == -1) {
fprintf(stderr, "gsr error: gsr_kms_client_replace_connection: failed to send request message to server\n");
return -1;
}
const int recv_res = recv_msg_from_server(self->kms_server_pid, self->socket_pair[GSR_SOCKET_PAIR_LOCAL], &response);
if(recv_res == 0) {
fprintf(stderr, "gsr warning: gsr_kms_client_replace_connection: kms server shut down\n");
return -1;
} else if(recv_res == -1) {
fprintf(stderr, "gsr error: gsr_kms_client_replace_connection: failed to receive response\n");
return -1;
}
if(response.version != GSR_KMS_PROTOCOL_VERSION) {
fprintf(stderr, "gsr error: gsr_kms_client_replace_connection: expected gsr-kms-server protocol version to be %u, but it's %u. please reinstall gpu screen recorder\n", GSR_KMS_PROTOCOL_VERSION, response.version);
/*close_fds(response);*/
return -1;
}
return 0;
}
int gsr_kms_client_get_kms(gsr_kms_client *self, gsr_kms_response *response) { int gsr_kms_client_get_kms(gsr_kms_client *self, gsr_kms_response *response) {
response->version = 0; response->version = 0;
response->result = KMS_RESULT_FAILED_TO_SEND; response->result = KMS_RESULT_FAILED_TO_SEND;

View File

@@ -9,9 +9,6 @@ typedef struct gsr_kms_client gsr_kms_client;
struct gsr_kms_client { struct gsr_kms_client {
pid_t kms_server_pid; pid_t kms_server_pid;
int initial_socket_fd;
int initial_client_fd;
char initial_socket_path[PATH_MAX];
int socket_pair[2]; int socket_pair[2];
}; };

View File

@@ -15,7 +15,6 @@ typedef struct gsr_kms_response_item gsr_kms_response_item;
typedef struct gsr_kms_response gsr_kms_response; typedef struct gsr_kms_response gsr_kms_response;
typedef enum { typedef enum {
KMS_REQUEST_TYPE_REPLACE_CONNECTION,
KMS_REQUEST_TYPE_GET_KMS KMS_REQUEST_TYPE_GET_KMS
} gsr_kms_request_type; } gsr_kms_request_type;

View File

@@ -6,16 +6,14 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <errno.h>
#include <stdlib.h> #include <stdlib.h>
#include <errno.h>
#include <locale.h> #include <locale.h>
#include <unistd.h> #include <unistd.h>
#include <limits.h> #include <limits.h>
#include <fcntl.h> #include <fcntl.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/un.h>
#include <time.h>
#include <xf86drm.h> #include <xf86drm.h>
#include <xf86drmMode.h> #include <xf86drmMode.h>
@@ -426,14 +424,6 @@ static int kms_get_fb(gsr_drm *drm, gsr_kms_response *response) {
return result; return result;
} }
static double clock_get_monotonic_seconds(void) {
struct timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = 0;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (double)ts.tv_sec + (double)ts.tv_nsec * 0.000000001;
}
int main(int argc, char **argv) { int main(int argc, char **argv) {
setlocale(LC_ALL, "C"); // Sigh... stupid C setlocale(LC_ALL, "C"); // Sigh... stupid C
@@ -443,19 +433,19 @@ int main(int argc, char **argv) {
drm.drmfd = 0; drm.drmfd = 0;
if(argc != 3) { if(argc != 3) {
fprintf(stderr, "usage: gsr-kms-server <domain_socket_path> <card_path>\n"); fprintf(stderr, "usage: gsr-kms-server <socket_fd> <card_path>\n");
return 1; return 1;
} }
const char *domain_socket_path = argv[1]; const char *socket_fd_str = argv[1];
socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if(socket_fd == -1) {
fprintf(stderr, "kms server error: failed to create socket, error: %s\n", strerror(errno));
return 2;
}
const char *card_path = argv[2]; const char *card_path = argv[2];
socket_fd = atoi(socket_fd_str);
if(socket_fd <= 0) {
fprintf(stderr, "kms server error: received invalid socket fd for argument 1, expected a number got \"%s\"\n", socket_fd_str);
return 1;
}
drm.drmfd = open(card_path, O_RDONLY); drm.drmfd = open(card_path, O_RDONLY);
if(drm.drmfd < 0) { if(drm.drmfd < 0) {
fprintf(stderr, "kms server error: failed to open %s, error: %s", card_path, strerror(errno)); fprintf(stderr, "kms server error: failed to open %s, error: %s", card_path, strerror(errno));
@@ -473,40 +463,6 @@ int main(int argc, char **argv) {
fprintf(stderr, "kms server warning: drmSetClientCap DRM_CLIENT_CAP_ATOMIC failed, error: %s. The wrong monitor may be captured as a result\n", strerror(errno)); fprintf(stderr, "kms server warning: drmSetClientCap DRM_CLIENT_CAP_ATOMIC failed, error: %s. The wrong monitor may be captured as a result\n", strerror(errno));
} }
fprintf(stderr, "kms server info: connecting to the client\n");
bool connected = false;
const double connect_timeout_sec = 5.0;
const double start_time = clock_get_monotonic_seconds();
while(clock_get_monotonic_seconds() - start_time < connect_timeout_sec) {
struct sockaddr_un remote_addr = {0};
remote_addr.sun_family = AF_UNIX;
snprintf(remote_addr.sun_path, sizeof(remote_addr.sun_path), "%s", domain_socket_path);
// TODO: Check if parent disconnected
if(connect(socket_fd, (struct sockaddr*)&remote_addr, sizeof(remote_addr.sun_family) + strlen(remote_addr.sun_path)) == -1) {
if(errno == ECONNREFUSED || errno == ENOENT) {
goto next;
} else if(errno == EISCONN) {
connected = true;
break;
}
fprintf(stderr, "kms server error: connect failed, error: %s (%d)\n", strerror(errno), errno);
res = 2;
goto done;
}
next:
usleep(30 * 1000); // 30 milliseconds
}
if(connected) {
fprintf(stderr, "kms server info: connected to the client\n");
} else {
fprintf(stderr, "kms server error: failed to connect to the client in %f seconds\n", connect_timeout_sec);
res = 2;
goto done;
}
for(;;) { for(;;) {
gsr_kms_request request; gsr_kms_request request;
request.version = 0; request.version = 0;
@@ -539,29 +495,6 @@ int main(int argc, char **argv) {
} }
switch(request.type) { switch(request.type) {
case KMS_REQUEST_TYPE_REPLACE_CONNECTION: {
gsr_kms_response response;
response.version = GSR_KMS_PROTOCOL_VERSION;
response.num_items = 0;
if(request.new_connection_fd > 0) {
if(socket_fd > 0)
close(socket_fd);
socket_fd = request.new_connection_fd;
response.result = KMS_RESULT_OK;
if(send_msg_to_client(socket_fd, &response) == -1)
fprintf(stderr, "kms server error: failed to respond to client KMS_REQUEST_TYPE_REPLACE_CONNECTION request\n");
} else {
response.result = KMS_RESULT_INVALID_REQUEST;
snprintf(response.err_msg, sizeof(response.err_msg), "received invalid connection fd");
fprintf(stderr, "kms server error: %s\n", response.err_msg);
if(send_msg_to_client(socket_fd, &response) == -1)
fprintf(stderr, "kms server error: failed to respond to client request\n");
}
break;
}
case KMS_REQUEST_TYPE_GET_KMS: { case KMS_REQUEST_TYPE_GET_KMS: {
gsr_kms_response response; gsr_kms_response response;
response.version = GSR_KMS_PROTOCOL_VERSION; response.version = GSR_KMS_PROTOCOL_VERSION;