2020-07-15 12:39:10 +02:00
|
|
|
#include "spawn.h"
|
|
|
|
|
|
2021-01-31 14:40:27 +01:00
|
|
|
#include <string.h>
|
2020-07-15 12:39:10 +02:00
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <errno.h>
|
2021-02-21 11:57:38 +01:00
|
|
|
#include <signal.h>
|
2020-07-15 12:39:10 +02:00
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
|
|
|
|
|
#define LOG_MODULE "spawn"
|
|
|
|
|
#define LOG_ENABLE_DBG 0
|
|
|
|
|
#include "log.h"
|
2021-01-15 20:39:45 +00:00
|
|
|
#include "debug.h"
|
2021-01-31 14:40:27 +01:00
|
|
|
#include "xmalloc.h"
|
2020-07-15 12:39:10 +02:00
|
|
|
|
|
|
|
|
bool
|
2020-07-15 13:33:56 +02:00
|
|
|
spawn(struct reaper *reaper, const char *cwd, char *const argv[],
|
url-mode: add support for XDG activation when opening URLs
First, add a ‘token’ argument to spawn(). When non-NULL, spawn() will
set the ‘XDG_ACTIVATION_TOKEN’ environment variable in the forked
process. If DISPLAY is non-NULL, we also set DESKTOP_STARTUP_ID, for
compatibility with X11 applications. Note that failing to set either
of these environment variables are considered non-fatal - i.e. we
ignore failures.
Next, add a helper function, wayl_get_activation_token(), to generate
an XDG activation token, and call a user-provided callback when it’s
‘done (since token generation is asynchronous). This function takes an
optional ‘seat’ and ‘serial’ arguments - when both are non-NULL/zero,
we set the serial on the token. ‘win’ is a required argument, used to
set the surface on the token.
Re-write wayl_win_set_urgent() to use the new helper function.
Finally, rewrite activate_url() to first try to get an activation
token (and spawn the URL launcher in the token callback). If that
fails, or if we don’t have XDG activation support, spawn the URL
launcher immediately (like before this patch).
Closes #1058
2022-05-03 19:37:04 +02:00
|
|
|
int stdin_fd, int stdout_fd, int stderr_fd,
|
|
|
|
|
const char *xdg_activation_token)
|
2020-07-15 12:39:10 +02:00
|
|
|
{
|
|
|
|
|
int pipe_fds[2] = {-1, -1};
|
|
|
|
|
if (pipe2(pipe_fds, O_CLOEXEC) < 0) {
|
|
|
|
|
LOG_ERRNO("failed to create pipe");
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pid_t pid = fork();
|
|
|
|
|
if (pid < 0) {
|
|
|
|
|
LOG_ERRNO("failed to fork");
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pid == 0) {
|
|
|
|
|
/* Child */
|
|
|
|
|
close(pipe_fds[0]);
|
2020-07-15 13:33:56 +02:00
|
|
|
|
2021-02-25 21:09:59 +01:00
|
|
|
if (setsid() < 0)
|
|
|
|
|
goto child_err;
|
|
|
|
|
|
2021-02-21 11:57:38 +01:00
|
|
|
/* Clear signal mask */
|
|
|
|
|
sigset_t mask;
|
|
|
|
|
sigemptyset(&mask);
|
2021-02-25 21:09:59 +01:00
|
|
|
if (sigprocmask(SIG_SETMASK, &mask, NULL) < 0)
|
|
|
|
|
goto child_err;
|
2021-02-21 11:57:38 +01:00
|
|
|
|
2021-06-02 17:48:57 +02:00
|
|
|
/* Restore ignored (SIG_IGN) signals */
|
Explicitly initialize sigaction::sa_mask members with sigemptyset(3)
Not doing so before calling sigaction(3) is "undefined" according to
POSIX[1]:
> Applications shall call either sigemptyset() or sigfillset() at least
> once for each object of type sigset_t prior to any other use of that
> object. If such an object is not initialized in this way, but is
> nonetheless supplied as an argument to any of pthread_sigmask(),
> sigaction(), sigaddset(), sigdelset(), sigismember(), sigpending(),
> sigprocmask(), sigsuspend(), sigtimedwait(), sigwait(), or
> sigwaitinfo(), the results are undefined.
The use of designated initializers means that sa_mask members were
still being initialized, but sigset_t is an opaque type and implicit
initialization doesn't necessarily produce the same results as using
sigemptyset(3) (although it typically does on most implementations).
[1]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigaddset.html
2022-02-12 12:04:57 +00:00
|
|
|
struct sigaction dfl = {.sa_handler = SIG_DFL};
|
|
|
|
|
sigemptyset(&dfl.sa_mask);
|
url-mode: add support for XDG activation when opening URLs
First, add a ‘token’ argument to spawn(). When non-NULL, spawn() will
set the ‘XDG_ACTIVATION_TOKEN’ environment variable in the forked
process. If DISPLAY is non-NULL, we also set DESKTOP_STARTUP_ID, for
compatibility with X11 applications. Note that failing to set either
of these environment variables are considered non-fatal - i.e. we
ignore failures.
Next, add a helper function, wayl_get_activation_token(), to generate
an XDG activation token, and call a user-provided callback when it’s
‘done (since token generation is asynchronous). This function takes an
optional ‘seat’ and ‘serial’ arguments - when both are non-NULL/zero,
we set the serial on the token. ‘win’ is a required argument, used to
set the surface on the token.
Re-write wayl_win_set_urgent() to use the new helper function.
Finally, rewrite activate_url() to first try to get an activation
token (and spawn the URL launcher in the token callback). If that
fails, or if we don’t have XDG activation support, spawn the URL
launcher immediately (like before this patch).
Closes #1058
2022-05-03 19:37:04 +02:00
|
|
|
if (sigaction(SIGHUP, &dfl, NULL) < 0 ||
|
|
|
|
|
sigaction(SIGPIPE, &dfl, NULL) < 0)
|
|
|
|
|
{
|
2021-06-02 17:48:57 +02:00
|
|
|
goto child_err;
|
url-mode: add support for XDG activation when opening URLs
First, add a ‘token’ argument to spawn(). When non-NULL, spawn() will
set the ‘XDG_ACTIVATION_TOKEN’ environment variable in the forked
process. If DISPLAY is non-NULL, we also set DESKTOP_STARTUP_ID, for
compatibility with X11 applications. Note that failing to set either
of these environment variables are considered non-fatal - i.e. we
ignore failures.
Next, add a helper function, wayl_get_activation_token(), to generate
an XDG activation token, and call a user-provided callback when it’s
‘done (since token generation is asynchronous). This function takes an
optional ‘seat’ and ‘serial’ arguments - when both are non-NULL/zero,
we set the serial on the token. ‘win’ is a required argument, used to
set the surface on the token.
Re-write wayl_win_set_urgent() to use the new helper function.
Finally, rewrite activate_url() to first try to get an activation
token (and spawn the URL launcher in the token callback). If that
fails, or if we don’t have XDG activation support, spawn the URL
launcher immediately (like before this patch).
Closes #1058
2022-05-03 19:37:04 +02:00
|
|
|
}
|
2021-06-02 17:48:57 +02:00
|
|
|
|
2022-09-26 19:16:40 +02:00
|
|
|
if (cwd != NULL) {
|
|
|
|
|
setenv("PWD", cwd, 1);
|
|
|
|
|
if (chdir(cwd) < 0) {
|
|
|
|
|
LOG_WARN("failed to change working directory to %s: %s",
|
|
|
|
|
cwd, strerror(errno));
|
|
|
|
|
}
|
2021-08-30 17:55:36 +02:00
|
|
|
}
|
|
|
|
|
|
url-mode: add support for XDG activation when opening URLs
First, add a ‘token’ argument to spawn(). When non-NULL, spawn() will
set the ‘XDG_ACTIVATION_TOKEN’ environment variable in the forked
process. If DISPLAY is non-NULL, we also set DESKTOP_STARTUP_ID, for
compatibility with X11 applications. Note that failing to set either
of these environment variables are considered non-fatal - i.e. we
ignore failures.
Next, add a helper function, wayl_get_activation_token(), to generate
an XDG activation token, and call a user-provided callback when it’s
‘done (since token generation is asynchronous). This function takes an
optional ‘seat’ and ‘serial’ arguments - when both are non-NULL/zero,
we set the serial on the token. ‘win’ is a required argument, used to
set the surface on the token.
Re-write wayl_win_set_urgent() to use the new helper function.
Finally, rewrite activate_url() to first try to get an activation
token (and spawn the URL launcher in the token callback). If that
fails, or if we don’t have XDG activation support, spawn the URL
launcher immediately (like before this patch).
Closes #1058
2022-05-03 19:37:04 +02:00
|
|
|
if (xdg_activation_token != NULL) {
|
|
|
|
|
setenv("XDG_ACTIVATION_TOKEN", xdg_activation_token, 1);
|
|
|
|
|
|
|
|
|
|
if (getenv("DISPLAY") != NULL)
|
|
|
|
|
setenv("DESKTOP_STARTUP_ID", xdg_activation_token, 1);
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-24 21:29:34 +01:00
|
|
|
bool close_stderr = stderr_fd >= 0;
|
|
|
|
|
bool close_stdout = stdout_fd >= 0 && stdout_fd != stderr_fd;
|
|
|
|
|
bool close_stdin = stdin_fd >= 0 && stdin_fd != stdout_fd && stdin_fd != stderr_fd;
|
|
|
|
|
|
|
|
|
|
if ((stdin_fd >= 0 && (dup2(stdin_fd, STDIN_FILENO) < 0
|
|
|
|
|
|| (close_stdin && close(stdin_fd) < 0))) ||
|
|
|
|
|
(stdout_fd >= 0 && (dup2(stdout_fd, STDOUT_FILENO) < 0
|
|
|
|
|
|| (close_stdout && close(stdout_fd) < 0))) ||
|
|
|
|
|
(stderr_fd >= 0 && (dup2(stderr_fd, STDERR_FILENO) < 0
|
|
|
|
|
|| (close_stderr && close(stderr_fd) < 0))) ||
|
2020-07-15 12:39:10 +02:00
|
|
|
execvp(argv[0], argv) < 0)
|
|
|
|
|
{
|
2021-02-25 21:09:59 +01:00
|
|
|
goto child_err;
|
2020-07-15 12:39:10 +02:00
|
|
|
}
|
2021-02-25 21:09:59 +01:00
|
|
|
|
2021-01-16 20:16:00 +00:00
|
|
|
xassert(false);
|
2020-07-15 12:39:10 +02:00
|
|
|
_exit(errno);
|
2021-02-25 21:09:59 +01:00
|
|
|
|
|
|
|
|
child_err:
|
|
|
|
|
;
|
|
|
|
|
const int errno_copy = errno;
|
|
|
|
|
(void)!write(pipe_fds[1], &errno_copy, sizeof(errno_copy));
|
|
|
|
|
_exit(errno_copy);
|
2020-07-15 12:39:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Parent */
|
|
|
|
|
close(pipe_fds[1]);
|
|
|
|
|
|
2021-02-21 20:33:07 +01:00
|
|
|
int errno_copy;
|
|
|
|
|
static_assert(sizeof(errno_copy) == sizeof(errno), "errno size mismatch");
|
2020-07-15 12:39:10 +02:00
|
|
|
|
2021-02-21 20:33:07 +01:00
|
|
|
ssize_t ret = read(pipe_fds[0], &errno_copy, sizeof(errno_copy));
|
2020-07-15 12:39:10 +02:00
|
|
|
close(pipe_fds[0]);
|
|
|
|
|
|
|
|
|
|
if (ret == 0) {
|
2020-12-26 01:26:54 +01:00
|
|
|
reaper_add(reaper, pid, NULL, NULL);
|
2020-07-15 12:39:10 +02:00
|
|
|
return true;
|
|
|
|
|
} else if (ret < 0) {
|
|
|
|
|
LOG_ERRNO("failed to read from pipe");
|
|
|
|
|
return false;
|
|
|
|
|
} else {
|
2021-02-21 20:33:07 +01:00
|
|
|
LOG_ERRNO_P(errno_copy, "%s: failed to spawn", argv[0]);
|
|
|
|
|
errno = errno_copy;
|
2020-07-15 12:39:10 +02:00
|
|
|
waitpid(pid, NULL, 0);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err:
|
|
|
|
|
if (pipe_fds[0] != -1)
|
|
|
|
|
close(pipe_fds[0]);
|
|
|
|
|
if (pipe_fds[1] != -1)
|
|
|
|
|
close(pipe_fds[1]);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-01-31 14:40:27 +01:00
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
spawn_expand_template(const struct config_spawn_template *template,
|
|
|
|
|
size_t key_count,
|
|
|
|
|
const char *key_names[static key_count],
|
|
|
|
|
const char *key_values[static key_count],
|
|
|
|
|
size_t *argc, char ***argv)
|
|
|
|
|
{
|
|
|
|
|
*argc = 0;
|
|
|
|
|
*argv = NULL;
|
|
|
|
|
|
2021-06-18 16:18:41 +02:00
|
|
|
for (; template->argv.args[*argc] != NULL; (*argc)++)
|
2021-01-31 14:40:27 +01:00
|
|
|
;
|
|
|
|
|
|
|
|
|
|
#define append(s, n) \
|
|
|
|
|
do { \
|
|
|
|
|
expanded = xrealloc(expanded, len + (n) + 1); \
|
|
|
|
|
memcpy(&expanded[len], s, n); \
|
|
|
|
|
len += n; \
|
|
|
|
|
expanded[len] = '\0'; \
|
|
|
|
|
} while (0)
|
|
|
|
|
|
2024-01-25 07:03:50 +00:00
|
|
|
*argv = xmalloc((*argc + 1) * sizeof((*argv)[0]));
|
2021-01-31 14:40:27 +01:00
|
|
|
|
|
|
|
|
/* Expand the provided keys */
|
|
|
|
|
for (size_t i = 0; i < *argc; i++) {
|
|
|
|
|
size_t len = 0;
|
|
|
|
|
char *expanded = NULL;
|
|
|
|
|
|
|
|
|
|
char *start = NULL;
|
2021-06-18 16:18:41 +02:00
|
|
|
char *last_end = template->argv.args[i];
|
2021-01-31 14:40:27 +01:00
|
|
|
|
|
|
|
|
while ((start = strstr(last_end, "${")) != NULL) {
|
|
|
|
|
/* Append everything from the last template's end to this
|
|
|
|
|
* one's beginning */
|
|
|
|
|
append(last_end, start - last_end);
|
|
|
|
|
|
|
|
|
|
/* Find end of template */
|
|
|
|
|
start += 2;
|
|
|
|
|
char *end = strstr(start, "}");
|
|
|
|
|
|
|
|
|
|
if (end == NULL) {
|
|
|
|
|
/* Ensure final append() copies the unclosed '${' */
|
|
|
|
|
last_end = start - 2;
|
|
|
|
|
LOG_WARN("notify: unclosed template: %s", last_end);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Expand template */
|
|
|
|
|
bool valid_key = false;
|
|
|
|
|
for (size_t j = 0; j < key_count; j++) {
|
|
|
|
|
if (strncmp(start, key_names[j], end - start) != 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
append(key_values[j], strlen(key_values[j]));
|
|
|
|
|
valid_key = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!valid_key) {
|
|
|
|
|
/* Unrecognized template - append it as-is */
|
|
|
|
|
start -= 2;
|
|
|
|
|
append(start, end + 1 - start);
|
|
|
|
|
LOG_WARN("notify: unrecognized template: %.*s",
|
|
|
|
|
(int)(end + 1 - start), start);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
last_end = end + 1;
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-18 16:18:41 +02:00
|
|
|
append(
|
|
|
|
|
last_end,
|
|
|
|
|
template->argv.args[i] + strlen(template->argv.args[i]) - last_end);
|
2021-01-31 14:40:27 +01:00
|
|
|
(*argv)[i] = expanded;
|
|
|
|
|
}
|
|
|
|
|
(*argv)[*argc] = NULL;
|
|
|
|
|
|
|
|
|
|
#undef append
|
|
|
|
|
return true;
|
|
|
|
|
}
|