mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-05 04:06:08 -05:00
This is used when spawning the slave, to set its current working directory just before we exec() the client. In a regular foot instance, we set the cwd from getcwd(). In a foot server instance, each connecting client sends its cwd to the server, and we use that.
158 lines
3.7 KiB
C
158 lines
3.7 KiB
C
#include "slave.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <assert.h>
|
|
#include <signal.h>
|
|
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
|
|
#define LOG_MODULE "slave"
|
|
#define LOG_ENABLE_DBG 0
|
|
#include "log.h"
|
|
|
|
#include "tokenize.h"
|
|
|
|
static void
|
|
slave_exec(int ptmx, char *const argv[], int err_fd)
|
|
{
|
|
int pts = -1;
|
|
const char *pts_name = ptsname(ptmx);
|
|
|
|
if (grantpt(ptmx) == -1) {
|
|
LOG_ERRNO("failed to grantpt()");
|
|
goto err;
|
|
}
|
|
if (unlockpt(ptmx) == -1) {
|
|
LOG_ERRNO("failed to unlockpt()");
|
|
goto err;
|
|
}
|
|
|
|
close(ptmx);
|
|
ptmx = -1;
|
|
|
|
if (setsid() == -1) {
|
|
LOG_ERRNO("failed to setsid()");
|
|
goto err;
|
|
}
|
|
|
|
pts = open(pts_name, O_RDWR);
|
|
if (pts == -1) {
|
|
LOG_ERRNO("failed to open pseudo terminal slave device");
|
|
goto err;
|
|
}
|
|
|
|
if (dup2(pts, STDIN_FILENO) == -1 ||
|
|
dup2(pts, STDOUT_FILENO) == -1 ||
|
|
dup2(pts, STDERR_FILENO) == -1)
|
|
{
|
|
LOG_ERRNO("failed to dup stdin/stdout/stderr");
|
|
goto err;
|
|
}
|
|
|
|
close(pts);
|
|
pts = -1;
|
|
|
|
execvp(argv[0], argv);
|
|
|
|
err:
|
|
(void)!write(err_fd, &errno, sizeof(errno));
|
|
if (pts != -1)
|
|
close(pts);
|
|
if (ptmx != -1)
|
|
close(ptmx);
|
|
close(err_fd);
|
|
_exit(errno);
|
|
}
|
|
|
|
pid_t
|
|
slave_spawn(int ptmx, int argc, const char *cwd, char *const *argv,
|
|
const char *term_env, const char *conf_shell)
|
|
{
|
|
int fork_pipe[2];
|
|
if (pipe2(fork_pipe, O_CLOEXEC) < 0) {
|
|
LOG_ERRNO("failed to create pipe");
|
|
return -1;
|
|
}
|
|
|
|
pid_t pid = fork();
|
|
switch (pid) {
|
|
case -1:
|
|
LOG_ERRNO("failed to fork");
|
|
close(fork_pipe[0]);
|
|
close(fork_pipe[1]);
|
|
return -1;
|
|
|
|
case 0:
|
|
/* Child */
|
|
close(fork_pipe[0]); /* Close read end */
|
|
|
|
chdir(cwd);
|
|
|
|
/* Restore signals */
|
|
const struct sigaction sa = {.sa_handler = SIG_DFL};
|
|
if (sigaction(SIGINT, &sa, NULL) < 0 ||
|
|
sigaction(SIGTERM, &sa, NULL) < 0 ||
|
|
sigaction(SIGHUP, &sa, NULL) < 0)
|
|
{
|
|
const int _errno = errno;
|
|
LOG_ERRNO_P("failed to restore signals", errno);
|
|
(void)!write(fork_pipe[1], &_errno, sizeof(_errno));
|
|
_exit(_errno);
|
|
}
|
|
|
|
setenv("TERM", term_env, 1);
|
|
|
|
char **_shell_argv = NULL;
|
|
char *const *shell_argv = argv;
|
|
|
|
if (argc == 0) {
|
|
char *shell_copy = strdup(conf_shell);
|
|
if (!tokenize_cmdline(shell_copy, &_shell_argv)) {
|
|
free(shell_copy);
|
|
(void)!write(fork_pipe[1], &errno, sizeof(errno));
|
|
_exit(0);
|
|
}
|
|
shell_argv = _shell_argv;
|
|
}
|
|
|
|
slave_exec(ptmx, shell_argv, fork_pipe[1]);
|
|
assert(false);
|
|
break;
|
|
|
|
default: {
|
|
close(fork_pipe[1]); /* Close write end */
|
|
LOG_DBG("slave has PID %d", pid);
|
|
|
|
int _errno;
|
|
static_assert(sizeof(errno) == sizeof(_errno), "errno size mismatch");
|
|
|
|
ssize_t ret = read(fork_pipe[0], &_errno, sizeof(_errno));
|
|
close(fork_pipe[0]);
|
|
|
|
if (ret < 0) {
|
|
LOG_ERRNO("failed to read from pipe");
|
|
return -1;
|
|
} else if (ret == sizeof(_errno)) {
|
|
LOG_ERRNO_P(
|
|
"%s: failed to execute", _errno, argc == 0 ? conf_shell : argv[0]);
|
|
return -1;
|
|
} else
|
|
LOG_DBG("%s: successfully started", conf_shell);
|
|
|
|
int fd_flags;
|
|
if ((fd_flags = fcntl(ptmx, F_GETFD)) < 0 ||
|
|
fcntl(ptmx, F_SETFD, fd_flags | FD_CLOEXEC) < 0)
|
|
{
|
|
LOG_ERRNO("failed to set FD_CLOEXEC on ptmx");
|
|
return -1;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return pid;
|
|
}
|