*-s, --startup* <command>
Run command on startup
+*-S, --session* <command>
+ Run command on startup and terminate compositor on exit. This is useful
+ for session management as it allows the session client to terminate
+ labwc by exiting itself. This is a Wayland specific use-case because
+ under X, xinit starts the server and keeps it alive for as long as the
+ session client. Thus either the session client starts the Window
+ Manager, or the Window Manager can be launched independently first. On
+ Wayland, the Compositor is both Display Server and Window Manager, so
+ the described session management mechanisms do not work because the
+ Compositor needs to be running before the session can function. As some
+ session clients support both X11 and Wayland, this command line option
+ avoids re-writes and fragmentation.
+
*-v, --version*
Show the version number and quit
#include <sys/types.h>
+/**
+ * spawn_primary_client - execute asynchronously
+ * @command: command to be executed
+ */
+pid_t spawn_primary_client(const char *command);
+
/**
* spawn_async_no_shell - execute asynchronously
* @command: command to be executed
struct menu *menu_current;
struct wl_list menus;
+
+ pid_t primary_client_pid;
};
#define LAB_NR_LAYERS (4)
signal(SIGPIPE, SIG_DFL);
}
-static void
+static bool
set_cloexec(int fd)
{
int flags = fcntl(fd, F_GETFD);
- fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
+ if (flags == -1) {
+ wlr_log_errno(WLR_ERROR,
+ "Unable to set the CLOEXEC flag: fnctl failed");
+ return false;
+ }
+ flags = flags | FD_CLOEXEC;
+ if (fcntl(fd, F_SETFD, flags) == -1) {
+ wlr_log_errno(WLR_ERROR,
+ "Unable to set the CLOEXEC flag: fnctl failed");
+ return false;
+ }
+ return true;
}
void
g_strfreev(argv);
}
+pid_t
+spawn_primary_client(const char *command)
+{
+ assert(command);
+
+ GError *err = NULL;
+ gchar **argv = NULL;
+
+ /* Use glib's shell-parse to mimic Openbox's behaviour */
+ g_shell_parse_argv((gchar *)command, NULL, &argv, &err);
+ if (err) {
+ g_message("%s", err->message);
+ g_error_free(err);
+ return -1;
+ }
+
+ pid_t child = fork();
+ switch (child) {
+ case -1:
+ wlr_log_errno(WLR_ERROR, "Failed to fork");
+ g_strfreev(argv);
+ return -1;
+ case 0:
+ /* child */
+ close(STDIN_FILENO);
+ reset_signals_and_limits();
+ execvp(argv[0], argv);
+ wlr_log_errno(WLR_ERROR, "Failed to execute primary client %s", command);
+ _exit(1);
+ default:
+ g_strfreev(argv);
+ return child;
+ }
+}
+
pid_t
spawn_piped(const char *command, int *pipe_fd)
{
{"merge-config", no_argument, NULL, 'm'},
{"reconfigure", no_argument, NULL, 'r'},
{"startup", required_argument, NULL, 's'},
+ {"session", required_argument, NULL, 'S'},
{"version", no_argument, NULL, 'v'},
{"verbose", no_argument, NULL, 'V'},
{0, 0, 0, 0}
" -m, --merge-config Merge user config files/theme in all XDG Base Dirs\n"
" -r, --reconfigure Reload the compositor configuration\n"
" -s, --startup <command> Run command on startup\n"
+" -S, --session <command> Run command on startup and terminate on exit\n"
" -v, --version Show version number and quit\n"
" -V, --verbose Enable more verbose logging\n";
textdomain(GETTEXT_PACKAGE);
#endif
char *startup_cmd = NULL;
+ char *primary_client = NULL;
enum wlr_log_importance verbosity = WLR_ERROR;
int c;
while (1) {
int index = 0;
- c = getopt_long(argc, argv, "c:C:dehmrs:vV", long_options, &index);
+ c = getopt_long(argc, argv, "c:C:dehmrs:S:vV", long_options, &index);
if (c == -1) {
break;
}
case 's':
startup_cmd = optarg;
break;
+ case 'S':
+ primary_client = optarg;
+ break;
case 'v':
printf("labwc " LABWC_VERSION "\n");
exit(0);
menu_init(&server);
+ /* Start session-manager if one is specified by -S|--session */
+ if (primary_client) {
+ server.primary_client_pid = spawn_primary_client(primary_client);
+ if (server.primary_client_pid < 0) {
+ wlr_log(WLR_ERROR, "fatal error starting primary client: %s",
+ primary_client);
+ goto out;
+ }
+ }
+
session_autostart_init(&server);
if (startup_cmd) {
spawn_async_no_shell(startup_cmd);
wl_display_run(server.wl_display);
+out:
session_shutdown(&server);
server_finish(&server);
{
siginfo_t info;
info.si_pid = 0;
+ struct server *server = data;
/* First call waitid() with NOWAIT which doesn't consume the zombie */
if (waitid(P_ALL, /*id*/ 0, &info, WEXITED | WNOHANG | WNOWAIT) == -1) {
}
#if HAVE_XWAYLAND
- /* Verify that we do not break xwayland lazy initialization */
- struct server *server = data;
+ /* Ensure that we do not break xwayland lazy initialization */
if (server->xwayland && server->xwayland->server
&& info.si_pid == server->xwayland->server->pid) {
return 0;
" please report", (long)info.si_pid, info.si_code);
}
+ if (info.si_pid == server->primary_client_pid) {
+ wlr_log(WLR_INFO, "primary client %ld exited", (long)info.si_pid);
+ wl_display_terminate(server->wl_display);
+ }
+
return 0;
}
void
server_init(struct server *server)
{
+ server->primary_client_pid = -1;
server->wl_display = wl_display_create();
if (!server->wl_display) {
wlr_log(WLR_ERROR, "cannot allocate a wayland display");