Skip to content

Commit

Permalink
src/wl-restart: add support for alternative cli-based and systemd-bas…
Browse files Browse the repository at this point in the history
…ed handover
  • Loading branch information
Ferdi265 committed Nov 13, 2024
1 parent cbbd450 commit ef5124b
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 13 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,11 @@ in the [KDE wiki](https://invent.kde.org/plasma/kwin/-/wikis/Restarting).
- Don't lose your session (for programs that support seamless Wayland reconnect)
- Configurable max number of crashes before giving up
- Sets `WL_RESTART_COUNT` to the current restart counter on compositor restart.
- Support for two different experimental socket handover mechanisms
- Support for four different experimental socket handover mechanisms
- `--kde`: `--socket` and `--wayland-fd`
- `--cli`: `--wayland-socket` and `--wayland-fd`
- `--env`: `WAYLAND_SOCKET_NAME` and `WAYLAND_SOCKET_FD`
- `--systemd`: [systemd socket activation](https://man.archlinux.org/man/sd_listen_fds.3.en) via `LISTEN_PID`, `LISTEN_FDS`, and `LISTEN_FDNAMES`

## Usage

Expand All @@ -60,7 +62,9 @@ options:
-h, --help show this help
-n N, --max-restarts N restart a maximum of N times (default 10)
--kde pass socket via cli options --socket and --wayland-fd (default)
--cli pass socket via cli options --wayland-socket and --wayland-fd
--env pass socket via env vars WAYLAND_SOCKET_NAME and WAYLAND_SOCKET_FD
--systemd pass socket via env vars LISTEN_PID, LISTEN_FDS, and LISTEN_FDNAMES
```

For example, run this in your TTY instead of normally starting your compositor:
Expand Down
13 changes: 13 additions & 0 deletions man/wl-restart.1.scd
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,22 @@ details, see *CLIENT AND TOOLKIT SUPPORT* below.
Pass socket via cli options --socket and --wayland-fd. (default)
This passes the socket in the same form as KDE's *kwin_wayland_wrapper*.

*--cli*
Pass socket via cli options --wayland-socket and --wayland-fd.

*--env*
Pass socket via env vars WAYLAND_SOCKET_NAME and WAYLAND_SOCKET_FD.

*--systemd*
Pass socket via env vars LISTEN_PID, LISTEN_FDS, and LISTEN_FDNAMES.
This passes the socket in the same form as the systemd socket activation
protocol. Further information can be found in *sd_listen_fds*(3).

The only difference from regular systemd socket activation is that in this
case *wl-restart* will have already created the Wayland socket lock file,
which would not be created by regular systemd socket activation as it is
Wayland-specific.

# BEHAVIOUR

*wl-restart* first finds a free wayland socket name (e.g. *wayland-1*) and
Expand Down
68 changes: 56 additions & 12 deletions src/wl-restart.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,21 @@
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/signal.h>
#include <sys/errno.h>
#include "wl-socket.h"

// taken from libsystemd <systemd/sd-daemon.h>
// part of the systemd socket activation protocol
#define SD_LISTEN_FDS_START 3

typedef enum {
MODE_KDE = 0,
MODE_CLI = 1,
MODE_ENV = 2,
MODE_SYSTEMD = 3
} pass_mode_t;

typedef struct {
Expand Down Expand Up @@ -85,8 +92,8 @@ void create_socket(ctx_t * ctx, int argc, char ** argv) {
}

// prepare arguments for cli modes
if (ctx->mode == MODE_KDE) {
const char * socket_name_arg = "--socket";
if (ctx->mode == MODE_KDE || ctx->mode == MODE_CLI) {
const char * socket_name_arg = ctx->mode == MODE_KDE ? "--socket" : "--wayland-socket";
const char * socket_fd_arg = "--wayland-fd";

// add extra arguments
Expand All @@ -105,27 +112,58 @@ void create_socket(ctx_t * ctx, int argc, char ** argv) {
}

// prepare environment for env modes
if (ctx->mode == MODE_ENV) {
const char * socket_name_var = "WAYLAND_SOCKET_NAME";
const char * socket_fd_var = "WAYLAND_SOCKET_FD";
if (ctx->mode == MODE_ENV || ctx->mode == MODE_SYSTEMD) {
const char * socket_name_var = ctx->mode == MODE_ENV ? "WAYLAND_SOCKET_NAME" : "LISTEN_FDNAMES";
const char * socket_fd_var = ctx->mode == MODE_ENV ? "WAYLAND_SOCKET_FD" : "LISTEN_FDS";

// set environment vars
setenv(socket_name_var, wl_socket_get_display_name(ctx->socket), true);

char * socket_fd = NULL;
if (asprintf(&socket_fd, "%d", wl_socket_get_fd(ctx->socket)) == -1) {
fprintf(stderr, "error: failed to convert fd to string\n");
exit_fail(ctx);
}
if (ctx->mode == MODE_ENV) {
char * socket_fd = NULL;
if (asprintf(&socket_fd, "%d", wl_socket_get_fd(ctx->socket)) == -1) {
fprintf(stderr, "error: failed to convert fd to string\n");
exit_fail(ctx);
}

setenv(socket_fd_var, socket_fd, true);
free(socket_fd);
setenv(socket_fd_var, socket_fd, true);
free(socket_fd);
} else if (ctx->mode == MODE_SYSTEMD) {
// systemd socket activation has a hardcoded fd number
// and only passes the number of fds
setenv(socket_fd_var, "1", true);
}
}
}

void start_compositor(ctx_t * ctx) {
pid_t pid = fork();
if (pid == 0) {
if (ctx->mode == MODE_SYSTEMD) {
// set up systemd socket activation

// set LISTEN_PID env var
char * listen_pid = NULL;
if (asprintf(&listen_pid, "%d", getpid()) == -1) {
fprintf(stderr, "error: failed to convert compositor pid to string\n");
exit(1);
}
setenv("LISTEN_PID", listen_pid, true);
free(listen_pid);

// move socket fd to SD_LISTEN_FDS_START
int socket_fd = wl_socket_get_fd(ctx->socket);
if (socket_fd != SD_LISTEN_FDS_START) {
if (dup2(socket_fd, SD_LISTEN_FDS_START) == -1) {
fprintf(stderr, "error: failed to move socket fd to SD_LISTEN_FDS_START\n");
exit(1);
}

// close original fd so it doesn't leak
close(socket_fd);
}
}

// exec into compositor process
execvp(ctx->compositor_argv[0], ctx->compositor_argv);

Expand Down Expand Up @@ -255,7 +293,9 @@ void usage(ctx_t * ctx) {
printf(" -h, --help show this help\n");
printf(" -n N, --max-restarts N restart a maximum of N times (default 10)\n");
printf(" --kde pass socket via cli options --socket and --wayland-fd (default)\n");
printf(" --cli pass socket via cli options --wayland-socket and --wayland-fd\n");
printf(" --env pass socket via env vars WAYLAND_SOCKET_NAME and WAYLAND_SOCKET_FD\n");
printf(" --systemd pass socket via env vars LISTEN_PID, LISTEN_FDS, and LISTEN_FDNAMES\n");
cleanup(ctx);
exit(0);
}
Expand Down Expand Up @@ -286,8 +326,12 @@ int main(int argc, char ** argv) {
ctx.max_restarts = atoi(arg);
} else if (strcmp(opt, "--kde") == 0) {
ctx.mode = MODE_KDE;
} else if (strcmp(opt, "--cli") == 0) {
ctx.mode = MODE_CLI;
} else if (strcmp(opt, "--env") == 0) {
ctx.mode = MODE_ENV;
} else if (strcmp(opt, "--systemd") == 0) {
ctx.mode = MODE_SYSTEMD;
} else if (strcmp(opt, "--") == 0) {
break;
} else {
Expand Down

0 comments on commit ef5124b

Please sign in to comment.