Fix rpc file getting deleted when launching gsr-ui twice. Use unix domain socket instead

This commit is contained in:
dec05eba
2025-10-03 13:01:40 +02:00
parent 9e03cd0354
commit 0b4af1e6bb
4 changed files with 204 additions and 85 deletions

View File

@@ -4,31 +4,47 @@
#include <functional> #include <functional>
#include <unordered_map> #include <unordered_map>
#include <string> #include <string>
#include <poll.h>
typedef struct _IO_FILE FILE; #define GSR_RPC_MAX_CONNECTIONS 8
#define GSR_RPC_MAX_POLLS (1 + GSR_RPC_MAX_CONNECTIONS) /* +1 to include the socket_fd itself for accept */
#define GSR_RPC_MAX_MESSAGE_SIZE 128
namespace gsr { namespace gsr {
using RpcCallback = std::function<void(const std::string &name)>; using RpcCallback = std::function<void(const std::string &name)>;
enum class RpcOpenResult {
OK,
CONNECTION_REFUSED,
ERROR
};
class Rpc { class Rpc {
public: public:
Rpc() = default; struct PollData {
char buffer[GSR_RPC_MAX_MESSAGE_SIZE];
int buffer_size = 0;
};
Rpc();
Rpc(const Rpc&) = delete; Rpc(const Rpc&) = delete;
Rpc& operator=(const Rpc&) = delete; Rpc& operator=(const Rpc&) = delete;
~Rpc(); ~Rpc();
bool create(const char *name); bool create(const char *name);
bool open(const char *name); RpcOpenResult open(const char *name);
bool write(const char *str, size_t size); bool write(const char *str, size_t size);
void poll(); void poll();
bool add_handler(const std::string &name, RpcCallback callback); bool add_handler(const std::string &name, RpcCallback callback);
private: private:
bool open_filepath(const char *filepath); void handle_client_data(int client_fd, PollData &poll_data);
private: private:
int fd = 0; int socket_fd = 0;
FILE *file = nullptr; std::string socket_filepath;
std::string fifo_filepath; struct pollfd polls[GSR_RPC_MAX_POLLS];
PollData polls_data[GSR_RPC_MAX_POLLS];
int num_polls = 0;
std::unordered_map<std::string, RpcCallback> handlers_by_name; std::unordered_map<std::string, RpcCallback> handlers_by_name;
}; };
} }

View File

@@ -5,11 +5,12 @@
#include <limits.h> #include <limits.h>
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <sys/stat.h> #include <poll.h>
#include <fcntl.h> #include <sys/socket.h>
#include <sys/un.h>
namespace gsr { namespace gsr {
static void get_runtime_filepath(char *buffer, size_t buffer_size, const char *filename) { static void get_socket_filepath(char *buffer, size_t buffer_size, const char *filename) {
char dir[PATH_MAX]; char dir[PATH_MAX];
const char *runtime_dir = getenv("XDG_RUNTIME_DIR"); const char *runtime_dir = getenv("XDG_RUNTIME_DIR");
@@ -24,79 +25,117 @@ namespace gsr {
snprintf(buffer, buffer_size, "%s/%s", dir, filename); snprintf(buffer, buffer_size, "%s/%s", dir, filename);
} }
static int create_socket(const char *name, struct sockaddr_un *addr, std::string &socket_filepath) {
char socket_filepath_tmp[PATH_MAX];
get_socket_filepath(socket_filepath_tmp, sizeof(socket_filepath_tmp), name);
socket_filepath = socket_filepath_tmp;
memset(addr, 0, sizeof(*addr));
if(strlen(name) > sizeof(addr->sun_path))
return false;
addr->sun_family = AF_UNIX;
snprintf(addr->sun_path, sizeof(addr->sun_path), "%s", socket_filepath.c_str());
return socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0);
}
Rpc::Rpc() {
num_polls = 0;
}
Rpc::~Rpc() { Rpc::~Rpc() {
if(fd > 0) if(socket_fd > 0)
close(fd); close(socket_fd);
if(file) if(!socket_filepath.empty())
fclose(file); unlink(socket_filepath.c_str());
if(!fifo_filepath.empty())
unlink(fifo_filepath.c_str());
} }
bool Rpc::create(const char *name) { bool Rpc::create(const char *name) {
if(file) { if(socket_fd > 0) {
fprintf(stderr, "Error: Rpc::create: already created/opened\n"); fprintf(stderr, "Error: Rpc::create: already created/opened\n");
return false; return false;
} }
char fifo_filepath_tmp[PATH_MAX]; struct sockaddr_un addr;
get_runtime_filepath(fifo_filepath_tmp, sizeof(fifo_filepath_tmp), name); socket_fd = create_socket(name, &addr, socket_filepath);
fifo_filepath = fifo_filepath_tmp; if(socket_fd <= 0) {
unlink(fifo_filepath.c_str()); fprintf(stderr, "Error: Rpc::create: failed to create socket, error: %s\n", strerror(errno));
if(mkfifo(fifo_filepath.c_str(), 0600) != 0) {
fprintf(stderr, "Error: mkfifo failed, error: %s, %s\n", strerror(errno), fifo_filepath.c_str());
fifo_filepath.clear();
return false; return false;
} }
if(!open_filepath(fifo_filepath.c_str())) { unlink(socket_filepath.c_str());
unlink(fifo_filepath.c_str());
fifo_filepath.clear(); if(bind(socket_fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
const int err = errno;
close(socket_fd);
socket_fd = 0;
fprintf(stderr, "Error: Rpc::create: failed to bind, error: %s\n", strerror(err));
return false; return false;
} }
if(listen(socket_fd, GSR_RPC_MAX_CONNECTIONS) == -1) {
const int err = errno;
close(socket_fd);
socket_fd = 0;
fprintf(stderr, "Error: Rpc::create: failed to listen, error: %s\n", strerror(err));
return false;
}
polls[0].fd = socket_fd;
polls[0].events = POLLIN;
polls[0].revents = 0;
++num_polls;
return true; return true;
} }
bool Rpc::open(const char *name) { RpcOpenResult Rpc::open(const char *name) {
if(file) { if(socket_fd > 0) {
fprintf(stderr, "Error: Rpc::open: already created/opened\n"); fprintf(stderr, "Error: Rpc::open: already created/opened\n");
return false; return RpcOpenResult::ERROR;
} }
char fifo_filepath_tmp[PATH_MAX]; struct sockaddr_un addr;
get_runtime_filepath(fifo_filepath_tmp, sizeof(fifo_filepath_tmp), name); socket_fd = create_socket(name, &addr, socket_filepath);
return open_filepath(fifo_filepath_tmp); socket_filepath.clear(); /* We dont want to delete the socket on exit as the client */
} if(socket_fd <= 0) {
fprintf(stderr, "Error: Rpc::open: failed to create socket, error: %s\n", strerror(errno));
bool Rpc::open_filepath(const char *filepath) { return RpcOpenResult::ERROR;
fd = ::open(filepath, O_RDWR | O_NONBLOCK);
if(fd <= 0)
return false;
file = fdopen(fd, "r+");
if(!file) {
close(fd);
fd = 0;
return false;
} }
fd = 0;
return true; while(true) {
if(connect(socket_fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
const int err = errno;
if(err == EWOULDBLOCK) {
usleep(10 * 1000);
} else {
close(socket_fd);
socket_fd = 0;
if(err != ENOENT && err != ECONNREFUSED)
fprintf(stderr, "Error: Rpc::create: failed to connect, error: %s\n", strerror(err));
return RpcOpenResult::ERROR;
}
} else {
break;
}
}
return RpcOpenResult::OK;
} }
bool Rpc::write(const char *str, size_t size) { bool Rpc::write(const char *str, size_t size) {
if(!file) { if(socket_fd <= 0) {
fprintf(stderr, "Error: Rpc::write: fifo not created/opened yet\n"); fprintf(stderr, "Error: Rpc::write: unix domain socket not created/opened yet\n");
return false; return false;
} }
ssize_t offset = 0; ssize_t offset = 0;
while(offset < (ssize_t)size) { while(offset < (ssize_t)size) {
const ssize_t bytes_written = fwrite(str + offset, 1, size - offset, file); const ssize_t bytes_written = ::write(socket_fd, str + offset, size - offset);
fflush(file);
if(bytes_written > 0) if(bytes_written > 0)
offset += bytes_written; offset += bytes_written;
} }
@@ -104,30 +143,73 @@ namespace gsr {
} }
void Rpc::poll() { void Rpc::poll() {
if(!file) { if(socket_fd <= 0) {
//fprintf(stderr, "Error: Rpc::poll: fifo not created/opened yet\n"); //fprintf(stderr, "Error: Rpc::poll: unix domain socket not created/opened yet\n");
return; return;
} }
std::string name; std::string name;
char line[1024]; while(::poll(polls, num_polls, 0) > 0) {
while(fgets(line, sizeof(line), file)) { for(int i = 0; i < num_polls; ++i) {
int line_len = strlen(line); if(polls[i].fd == socket_fd) {
if(line_len == 0) if(polls[i].revents & (POLLERR|POLLHUP)) {
continue; close(socket_fd);
socket_fd = 0;
return;
}
if(line[line_len - 1] == '\n') { const int client_fd = accept(socket_fd, NULL, NULL);
line[line_len - 1] = '\0'; if(num_polls >= GSR_RPC_MAX_POLLS) {
--line_len; if(errno != EWOULDBLOCK)
fprintf(stderr, "Error: Rpc::poll: unable to accept more clients, error: %s\n", strerror(errno));
} else {
polls[num_polls].fd = client_fd;
polls[num_polls].events = POLLIN;
polls[num_polls].revents = 0;
++num_polls;
}
continue;
}
if(polls[i].revents & POLLIN)
handle_client_data(polls[i].fd, polls_data[i]);
if(polls[i].revents & (POLLERR|POLLHUP)) {
close(polls[i].fd);
polls[i] = polls[num_polls - 1];
memcpy(polls_data[i].buffer, polls_data[num_polls - 1].buffer, polls_data[num_polls - 1].buffer_size);
polls_data[i].buffer_size = polls_data[num_polls - 1].buffer_size;
--num_polls;
--i;
}
} }
name = line;
auto it = handlers_by_name.find(name);
if(it != handlers_by_name.end())
it->second(name);
} }
} }
void Rpc::handle_client_data(int client_fd, PollData &poll_data) {
char *write_buffer = poll_data.buffer + poll_data.buffer_size;
const ssize_t num_bytes_read = read(client_fd, write_buffer, sizeof(poll_data.buffer) - poll_data.buffer_size);
if(num_bytes_read <= 0)
return;
poll_data.buffer_size += num_bytes_read;
const char *newline_p = (const char*)memchr(write_buffer, '\n', num_bytes_read);
if(!newline_p)
return;
const size_t command_size = newline_p - poll_data.buffer;
std::string name;
name.assign(poll_data.buffer, command_size);
memmove(poll_data.buffer, newline_p + 1, poll_data.buffer_size - (command_size + 1));
poll_data.buffer_size -= (command_size + 1);
auto it = handlers_by_name.find(name);
if(it != handlers_by_name.end())
it->second(name);
}
bool Rpc::add_handler(const std::string &name, RpcCallback callback) { bool Rpc::add_handler(const std::string &name, RpcCallback callback) {
return handlers_by_name.insert(std::make_pair(name, std::move(callback))).second; return handlers_by_name.insert(std::make_pair(name, std::move(callback))).second;
} }

View File

@@ -226,16 +226,13 @@ int main(int argc, char **argv) {
set_display_server_environment_variables(); set_display_server_environment_variables();
auto rpc = std::make_unique<gsr::Rpc>(); auto rpc = std::make_unique<gsr::Rpc>();
const bool rpc_created = rpc->create("gsr-ui"); const gsr::RpcOpenResult rpc_open_result = rpc->open("gsr-ui");
if(!rpc_created)
fprintf(stderr, "Error: Failed to create rpc\n");
if(is_gsr_ui_virtual_keyboard_running() || !rpc_created) { if(is_gsr_ui_virtual_keyboard_running() || rpc_open_result == gsr::RpcOpenResult::OK) {
if(launch_action == LaunchAction::LAUNCH_DAEMON) if(launch_action == LaunchAction::LAUNCH_DAEMON)
return 1; return 1;
rpc = std::make_unique<gsr::Rpc>(); if(rpc->write("show_ui\n", 8)) {
if(rpc->open("gsr-ui") && rpc->write("show_ui\n", 8)) {
fprintf(stderr, "Error: another instance of gsr-ui is already running, opening that one instead\n"); fprintf(stderr, "Error: another instance of gsr-ui is already running, opening that one instead\n");
} else { } else {
fprintf(stderr, "Error: failed to send command to running gsr-ui instance, user will have to open the UI manually with Alt+Z\n"); fprintf(stderr, "Error: failed to send command to running gsr-ui instance, user will have to open the UI manually with Alt+Z\n");
@@ -245,6 +242,9 @@ int main(int argc, char **argv) {
return 1; return 1;
} }
if(!rpc->create("gsr-ui"))
fprintf(stderr, "Error: Failed to create rpc\n");
if(gsr::pidof("gpu-screen-recorder", -1) != -1) { if(gsr::pidof("gpu-screen-recorder", -1) != -1) {
const char *args[] = { "gsr-notify", "--text", "GPU Screen Recorder is already running in another process.\nPlease close it before using GPU Screen Recorder UI.", "--timeout", "5.0", "--icon-color", "ff0000", "--bg-color", "ff0000", nullptr }; const char *args[] = { "gsr-notify", "--text", "GPU Screen Recorder is already running in another process.\nPlease close it before using GPU Screen Recorder UI.", "--timeout", "5.0", "--icon-color", "ff0000", "--bg-color", "ff0000", nullptr };
gsr::exec_program_daemonized(args); gsr::exec_program_daemonized(args);

View File

@@ -5,9 +5,11 @@
#include <stdbool.h> #include <stdbool.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <errno.h>
#include <sys/socket.h>
#include <sys/un.h>
static void get_runtime_filepath(char *buffer, size_t buffer_size, const char *filename) { static void get_socket_filepath(char *buffer, size_t buffer_size, const char *filename) {
char dir[PATH_MAX]; char dir[PATH_MAX];
const char *runtime_dir = getenv("XDG_RUNTIME_DIR"); const char *runtime_dir = getenv("XDG_RUNTIME_DIR");
@@ -23,7 +25,7 @@ static void get_runtime_filepath(char *buffer, size_t buffer_size, const char *f
} }
/* Assumes |str| size is less than 256 */ /* Assumes |str| size is less than 256 */
static void fifo_write_all(int file_fd, const char *str) { static void file_write_all(int file_fd, const char *str) {
char command[256]; char command[256];
const ssize_t command_size = snprintf(command, sizeof(command), "%s\n", str); const ssize_t command_size = snprintf(command, sizeof(command), "%s\n", str);
if(command_size >= (ssize_t)sizeof(command)) { if(command_size >= (ssize_t)sizeof(command)) {
@@ -33,7 +35,7 @@ static void fifo_write_all(int file_fd, const char *str) {
ssize_t offset = 0; ssize_t offset = 0;
while(offset < (ssize_t)command_size) { while(offset < (ssize_t)command_size) {
const ssize_t bytes_written = write(file_fd, str + offset, command_size - offset); const ssize_t bytes_written = write(file_fd, command + offset, command_size - offset);
if(bytes_written > 0) if(bytes_written > 0)
offset += bytes_written; offset += bytes_written;
} }
@@ -112,15 +114,34 @@ int main(int argc, char **argv) {
usage(); usage();
} }
char fifo_filepath[PATH_MAX]; char socket_filepath[PATH_MAX];
get_runtime_filepath(fifo_filepath, sizeof(fifo_filepath), "gsr-ui"); get_socket_filepath(socket_filepath, sizeof(socket_filepath), "gsr-ui");
const int fifo_fd = open(fifo_filepath, O_RDWR | O_NONBLOCK);
if(fifo_fd <= 0) { const int socket_fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0);
fprintf(stderr, "Error: failed to open fifo file %s. Maybe gsr-ui is not running?\n", fifo_filepath); if(socket_fd <= 0) {
fprintf(stderr, "Error: failed to create socket\n");
exit(2); exit(2);
} }
fifo_write_all(fifo_fd, command); struct sockaddr_un addr = {0};
close(fifo_fd); addr.sun_family = AF_UNIX;
snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socket_filepath);
for(;;) {
if(connect(socket_fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
const int err = errno;
if(err == EWOULDBLOCK) {
usleep(10 * 1000);
} else {
fprintf(stderr, "Error: failed to connect, error: %s. Maybe gsr-ui is not running?\n", strerror(err));
exit(2);
}
} else {
break;
}
}
file_write_all(socket_fd, command);
close(socket_fd);
return 0; return 0;
} }