2019-06-13 15:19:10 +02:00
|
|
|
#include "slave.h"
|
2019-11-01 21:01:15 +01:00
|
|
|
|
2019-06-13 15:19:10 +02:00
|
|
|
#include <stdlib.h>
|
2020-03-02 18:46:35 +01:00
|
|
|
#include <stdio.h>
|
2019-10-30 18:05:03 +01:00
|
|
|
#include <string.h>
|
2020-03-02 18:46:35 +01:00
|
|
|
#include <ctype.h>
|
2019-06-13 15:19:10 +02:00
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <errno.h>
|
2019-11-03 13:24:15 +01:00
|
|
|
#include <signal.h>
|
2020-05-26 18:18:11 +02:00
|
|
|
#include <termios.h>
|
2019-06-13 15:19:10 +02:00
|
|
|
|
|
|
|
|
#include <sys/stat.h>
|
2020-05-13 13:04:52 +02:00
|
|
|
#include <sys/ioctl.h>
|
2019-06-13 15:19:10 +02:00
|
|
|
#include <fcntl.h>
|
|
|
|
|
|
|
|
|
|
#define LOG_MODULE "slave"
|
2019-07-03 20:21:03 +02:00
|
|
|
#define LOG_ENABLE_DBG 0
|
2019-06-13 15:19:10 +02:00
|
|
|
#include "log.h"
|
2019-07-17 09:30:39 +02:00
|
|
|
|
2021-01-15 20:39:45 +00:00
|
|
|
#include "debug.h"
|
2021-02-10 09:18:50 +00:00
|
|
|
#include "macros.h"
|
2020-07-29 19:42:12 +02:00
|
|
|
#include "terminal.h"
|
2019-10-30 18:05:03 +01:00
|
|
|
#include "tokenize.h"
|
2020-08-08 20:34:30 +01:00
|
|
|
#include "xmalloc.h"
|
2019-10-30 18:05:03 +01:00
|
|
|
|
2022-04-11 13:24:27 +02:00
|
|
|
extern char **environ;
|
|
|
|
|
|
slave: don't skip setting environment variables when using a custom environment
When launching footclient with -E,--client-environment the environment
variables that should be set by foot, wasn't.
Those variables are:
* TERM
* COLORTERM
* PWD
* SHELL
and all variables defined by the user in the [environment] section in
foot.ini.
In the same way, we did not *unset* TERM_PROGRAM and
TERM_PROGRAM_VERSION.
This patch fixes it by "cloning" the custom environment, making it
mutable, and then adding/removing the variables above from it.
Instead of calling setenv()/unsetenv() directly, we add the wrapper
functions add_to_env() and del_from_env().
When *not* using a custom environment, they simply call
setenv()/unsetenv().
When we *are* using a custom environment, add_to_env() first loops all
existing variables, looking for a match. If a match is found, it's
updated with the new value. If it's not found, a new entry is added.
del_from_env() loops all entries, and removes it when a match is
found. If no match is found, nothing is done.
The mutable environment is allocated on the heap, but never free:d. We
don't need to free it, since it's only allocated after forking, in the
child process.
Closes #1568
2024-01-09 16:50:47 +01:00
|
|
|
struct environ {
|
|
|
|
|
size_t count;
|
|
|
|
|
char **envp;
|
|
|
|
|
};
|
|
|
|
|
|
2022-04-11 14:02:18 +02:00
|
|
|
#if defined(__FreeBSD__)
|
|
|
|
|
static char *
|
|
|
|
|
find_file_in_path(const char *file)
|
|
|
|
|
{
|
|
|
|
|
if (strchr(file, '/') != NULL)
|
|
|
|
|
return xstrdup(file);
|
|
|
|
|
|
|
|
|
|
const char *env_path = getenv("PATH");
|
|
|
|
|
char *path_list = NULL;
|
|
|
|
|
|
|
|
|
|
if (env_path != NULL && env_path[0] != '\0')
|
|
|
|
|
path_list = xstrdup(env_path);
|
|
|
|
|
else {
|
|
|
|
|
size_t sc_path_len = confstr(_CS_PATH, NULL, 0);
|
|
|
|
|
if (sc_path_len > 0) {
|
|
|
|
|
path_list = xmalloc(sc_path_len);
|
|
|
|
|
confstr(_CS_PATH, path_list, sc_path_len);
|
|
|
|
|
} else
|
|
|
|
|
return xstrdup(file);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const char *path = strtok(path_list, ":");
|
|
|
|
|
path != NULL;
|
|
|
|
|
path = strtok(NULL, ":"))
|
|
|
|
|
{
|
|
|
|
|
char *full = xasprintf("%s/%s", path, file);
|
|
|
|
|
if (access(full, F_OK) == 0) {
|
|
|
|
|
free(path_list);
|
|
|
|
|
return full;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(full);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(path_list);
|
|
|
|
|
return xstrdup(file);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
foot_execvpe(const char *file, char *const argv[], char *const envp[])
|
|
|
|
|
{
|
|
|
|
|
char *path = find_file_in_path(file);
|
|
|
|
|
int ret = execve(path, argv, envp);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Getting here is an error
|
|
|
|
|
*/
|
|
|
|
|
free(path);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#else /* !__FreeBSD__ */
|
|
|
|
|
|
|
|
|
|
#define foot_execvpe(file, argv, envp) execvpe(file, argv, envp)
|
|
|
|
|
|
|
|
|
|
#endif /* !__FreeBSD__ */
|
|
|
|
|
|
2020-03-02 18:46:35 +01:00
|
|
|
static bool
|
|
|
|
|
is_valid_shell(const char *shell)
|
|
|
|
|
{
|
|
|
|
|
FILE *f = fopen("/etc/shells", "r");
|
|
|
|
|
if (f == NULL)
|
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
|
|
char *_line = NULL;
|
|
|
|
|
size_t count = 0;
|
|
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
|
errno = 0;
|
|
|
|
|
ssize_t ret = getline(&_line, &count, f);
|
|
|
|
|
|
|
|
|
|
if (ret < 0) {
|
|
|
|
|
free(_line);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *line = _line;
|
|
|
|
|
{
|
|
|
|
|
while (isspace(*line))
|
|
|
|
|
line++;
|
|
|
|
|
if (line[0] != '\0') {
|
|
|
|
|
char *end = line + strlen(line) - 1;
|
|
|
|
|
while (isspace(*end))
|
|
|
|
|
end--;
|
|
|
|
|
*(end + 1) = '\0';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (line[0] == '#')
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (strcmp(line, shell) == 0) {
|
|
|
|
|
fclose(f);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err:
|
|
|
|
|
if (f != NULL)
|
|
|
|
|
fclose(f);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-01 09:00:18 +02:00
|
|
|
enum user_notification_ret_t {UN_OK, UN_NO_MORE, UN_FAIL};
|
|
|
|
|
|
|
|
|
|
static enum user_notification_ret_t
|
2020-07-30 18:57:21 +02:00
|
|
|
emit_one_notification(int fd, const struct user_notification *notif)
|
|
|
|
|
{
|
|
|
|
|
const char *prefix = NULL;
|
2020-08-02 23:54:04 +01:00
|
|
|
const char *postfix = "\033[m\n";
|
2020-07-30 18:57:21 +02:00
|
|
|
|
|
|
|
|
switch (notif->kind) {
|
|
|
|
|
case USER_NOTIFICATION_DEPRECATED:
|
2021-05-20 17:51:04 +02:00
|
|
|
prefix = "\033[33;1mdeprecated\033[39;22m: ";
|
2020-07-30 18:57:21 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case USER_NOTIFICATION_WARNING:
|
2021-05-20 17:51:04 +02:00
|
|
|
prefix = "\033[33;1mwarning\033[39;22m: ";
|
2020-07-30 18:57:21 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case USER_NOTIFICATION_ERROR:
|
2021-05-20 17:51:04 +02:00
|
|
|
prefix = "\033[31;1merror\033[39;22m: ";
|
2020-07-30 18:57:21 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-16 20:16:00 +00:00
|
|
|
xassert(prefix != NULL);
|
2020-07-30 18:57:21 +02:00
|
|
|
|
|
|
|
|
if (write(fd, prefix, strlen(prefix)) < 0 ||
|
|
|
|
|
write(fd, notif->text, strlen(notif->text)) < 0 ||
|
|
|
|
|
write(fd, postfix, strlen(postfix)) < 0)
|
|
|
|
|
{
|
2020-08-01 09:00:18 +02:00
|
|
|
/*
|
|
|
|
|
* The main process is blocking and waiting for us to close
|
|
|
|
|
* the error pipe. Thus, pts data will *not* be processed
|
|
|
|
|
* until we've exec:d. This means we cannot write anymore once
|
|
|
|
|
* the kernel buffer is full. Don't treat this as a fatal
|
|
|
|
|
* error.
|
|
|
|
|
*/
|
|
|
|
|
if (errno == EWOULDBLOCK || errno == EAGAIN)
|
|
|
|
|
return UN_NO_MORE;
|
|
|
|
|
else {
|
|
|
|
|
LOG_ERRNO("failed to write user-notification");
|
|
|
|
|
return UN_FAIL;
|
2020-07-31 17:15:51 +02:00
|
|
|
}
|
2020-07-30 18:57:21 +02:00
|
|
|
}
|
|
|
|
|
|
2020-08-01 09:00:18 +02:00
|
|
|
return UN_OK;
|
2020-07-30 18:57:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
2020-08-01 09:00:18 +02:00
|
|
|
emit_notifications_of_kind(int fd, const user_notifications_t *notifications,
|
|
|
|
|
enum user_notification_kind kind)
|
2020-07-30 18:57:21 +02:00
|
|
|
{
|
2020-07-31 17:10:39 +02:00
|
|
|
tll_foreach(*notifications, it) {
|
2020-08-01 09:00:18 +02:00
|
|
|
if (it->item.kind == kind) {
|
|
|
|
|
switch (emit_one_notification(fd, &it->item)) {
|
|
|
|
|
case UN_OK:
|
|
|
|
|
break;
|
|
|
|
|
case UN_NO_MORE:
|
|
|
|
|
return true;
|
|
|
|
|
case UN_FAIL:
|
2020-07-31 17:10:39 +02:00
|
|
|
return false;
|
2020-08-01 09:00:18 +02:00
|
|
|
}
|
2020-07-31 17:10:39 +02:00
|
|
|
}
|
2020-07-30 18:57:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-01 09:00:18 +02:00
|
|
|
static bool
|
|
|
|
|
emit_notifications(int fd, const user_notifications_t *notifications)
|
|
|
|
|
{
|
|
|
|
|
return
|
|
|
|
|
emit_notifications_of_kind(fd, notifications, USER_NOTIFICATION_ERROR) &&
|
|
|
|
|
emit_notifications_of_kind(fd, notifications, USER_NOTIFICATION_WARNING) &&
|
|
|
|
|
emit_notifications_of_kind(fd, notifications, USER_NOTIFICATION_DEPRECATED);
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-10 09:18:50 +00:00
|
|
|
static noreturn void
|
2022-04-11 12:19:40 +02:00
|
|
|
slave_exec(int ptmx, char *argv[], char *const envp[], int err_fd,
|
|
|
|
|
bool login_shell, const user_notifications_t *notifications)
|
2019-06-13 15:19:10 +02:00
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-13 13:04:52 +02:00
|
|
|
if (ioctl(pts, TIOCSCTTY, 0) < 0) {
|
|
|
|
|
LOG_ERRNO("failed to configure controlling terminal");
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-19 15:09:06 +00:00
|
|
|
#ifdef IUTF8
|
2020-05-26 18:18:11 +02:00
|
|
|
{
|
|
|
|
|
struct termios flags;
|
|
|
|
|
if (tcgetattr(pts, &flags) < 0) {
|
|
|
|
|
LOG_ERRNO("failed to get terminal attributes");
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
flags.c_iflag |= IUTF8;
|
|
|
|
|
if (tcsetattr(pts, TCSANOW, &flags) < 0) {
|
|
|
|
|
LOG_ERRNO("failed to set IUTF8 terminal attribute");
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-01-19 15:09:06 +00:00
|
|
|
#endif
|
2020-05-13 13:04:52 +02:00
|
|
|
|
2020-07-31 19:52:50 +02:00
|
|
|
if (tll_length(*notifications) > 0) {
|
2020-07-31 17:15:51 +02:00
|
|
|
int flags = fcntl(pts, F_GETFL);
|
|
|
|
|
if (flags < 0)
|
|
|
|
|
goto err;
|
|
|
|
|
if (fcntl(pts, F_SETFL, flags | O_NONBLOCK) < 0)
|
|
|
|
|
goto err;
|
|
|
|
|
|
2020-07-31 18:13:00 +02:00
|
|
|
if (!emit_notifications(pts, notifications))
|
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
|
|
fcntl(pts, F_SETFL, flags);
|
|
|
|
|
}
|
2020-07-29 19:42:12 +02:00
|
|
|
|
2020-08-01 09:00:18 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-13 15:19:10 +02:00
|
|
|
close(pts);
|
|
|
|
|
pts = -1;
|
|
|
|
|
|
2020-02-20 18:36:09 +01:00
|
|
|
const char *file;
|
|
|
|
|
if (login_shell) {
|
2020-08-08 20:34:30 +01:00
|
|
|
file = xstrdup(argv[0]);
|
2020-02-20 18:36:09 +01:00
|
|
|
|
2020-08-08 20:34:30 +01:00
|
|
|
char *arg0 = xmalloc(strlen(argv[0]) + 1 + 1);
|
2020-02-20 18:36:09 +01:00
|
|
|
arg0[0] = '-';
|
|
|
|
|
arg0[1] = '\0';
|
|
|
|
|
strcat(arg0, argv[0]);
|
|
|
|
|
|
|
|
|
|
argv[0] = arg0;
|
|
|
|
|
} else
|
|
|
|
|
file = argv[0];
|
|
|
|
|
|
2022-04-11 14:02:18 +02:00
|
|
|
foot_execvpe(file, argv, envp);
|
2019-06-15 22:22:44 +02:00
|
|
|
|
2019-06-13 15:19:10 +02:00
|
|
|
err:
|
2020-08-01 09:41:31 +02:00
|
|
|
(void)!write(err_fd, &errno, sizeof(errno));
|
2019-06-13 15:19:10 +02:00
|
|
|
if (pts != -1)
|
|
|
|
|
close(pts);
|
|
|
|
|
if (ptmx != -1)
|
|
|
|
|
close(ptmx);
|
2019-12-21 15:27:17 +01:00
|
|
|
close(err_fd);
|
2019-06-13 15:19:10 +02:00
|
|
|
_exit(errno);
|
|
|
|
|
}
|
2019-10-30 18:05:03 +01:00
|
|
|
|
slave: don't skip setting environment variables when using a custom environment
When launching footclient with -E,--client-environment the environment
variables that should be set by foot, wasn't.
Those variables are:
* TERM
* COLORTERM
* PWD
* SHELL
and all variables defined by the user in the [environment] section in
foot.ini.
In the same way, we did not *unset* TERM_PROGRAM and
TERM_PROGRAM_VERSION.
This patch fixes it by "cloning" the custom environment, making it
mutable, and then adding/removing the variables above from it.
Instead of calling setenv()/unsetenv() directly, we add the wrapper
functions add_to_env() and del_from_env().
When *not* using a custom environment, they simply call
setenv()/unsetenv().
When we *are* using a custom environment, add_to_env() first loops all
existing variables, looking for a match. If a match is found, it's
updated with the new value. If it's not found, a new entry is added.
del_from_env() loops all entries, and removes it when a match is
found. If no match is found, nothing is done.
The mutable environment is allocated on the heap, but never free:d. We
don't need to free it, since it's only allocated after forking, in the
child process.
Closes #1568
2024-01-09 16:50:47 +01:00
|
|
|
static bool
|
|
|
|
|
env_matches_var_name(const char *e, const char *name)
|
|
|
|
|
{
|
|
|
|
|
const size_t e_len = strlen(e);
|
|
|
|
|
const size_t name_len = strlen(name);
|
|
|
|
|
|
|
|
|
|
if (e_len <= name_len)
|
|
|
|
|
return false;
|
|
|
|
|
if (memcmp(e, name, name_len) != 0)
|
|
|
|
|
return false;
|
|
|
|
|
if (e[name_len] != '=')
|
|
|
|
|
return false;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
add_to_env(struct environ *env, const char *name, const char *value)
|
|
|
|
|
{
|
|
|
|
|
if (env->envp == NULL)
|
|
|
|
|
setenv(name, value, 1);
|
|
|
|
|
else {
|
|
|
|
|
char *e = xasprintf("%s=%s", name, value);
|
|
|
|
|
|
|
|
|
|
/* Search for existing variable. If found, replace it with the
|
|
|
|
|
new value */
|
|
|
|
|
for (size_t i = 0; i < env->count; i++) {
|
|
|
|
|
if (env_matches_var_name(env->envp[i], name)) {
|
|
|
|
|
free(env->envp[i]);
|
|
|
|
|
env->envp[i] = e;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If the variable does not already exist, add it */
|
|
|
|
|
env->envp = xrealloc(env->envp, (env->count + 2) * sizeof(env->envp[0]));
|
|
|
|
|
env->envp[env->count++] = e;
|
|
|
|
|
env->envp[env->count] = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
del_from_env(struct environ *env, const char *name)
|
|
|
|
|
{
|
|
|
|
|
if (env->envp == NULL)
|
|
|
|
|
unsetenv(name);
|
|
|
|
|
else {
|
|
|
|
|
for (size_t i = 0; i < env->count; i++) {
|
|
|
|
|
if (env_matches_var_name(env->envp[i], name)) {
|
|
|
|
|
free(env->envp[i]);
|
|
|
|
|
memmove(&env->envp[i],
|
|
|
|
|
&env->envp[i + 1],
|
|
|
|
|
(env->count - i) * sizeof(env->envp[0]));
|
|
|
|
|
env->count--;
|
|
|
|
|
xassert(env->envp[env->count] == NULL);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-30 18:05:03 +01:00
|
|
|
pid_t
|
2019-12-21 19:57:28 +01:00
|
|
|
slave_spawn(int ptmx, int argc, const char *cwd, char *const *argv,
|
slave: don't skip setting environment variables when using a custom environment
When launching footclient with -E,--client-environment the environment
variables that should be set by foot, wasn't.
Those variables are:
* TERM
* COLORTERM
* PWD
* SHELL
and all variables defined by the user in the [environment] section in
foot.ini.
In the same way, we did not *unset* TERM_PROGRAM and
TERM_PROGRAM_VERSION.
This patch fixes it by "cloning" the custom environment, making it
mutable, and then adding/removing the variables above from it.
Instead of calling setenv()/unsetenv() directly, we add the wrapper
functions add_to_env() and del_from_env().
When *not* using a custom environment, they simply call
setenv()/unsetenv().
When we *are* using a custom environment, add_to_env() first loops all
existing variables, looking for a match. If a match is found, it's
updated with the new value. If it's not found, a new entry is added.
del_from_env() loops all entries, and removes it when a match is
found. If no match is found, nothing is done.
The mutable environment is allocated on the heap, but never free:d. We
don't need to free it, since it's only allocated after forking, in the
child process.
Closes #1568
2024-01-09 16:50:47 +01:00
|
|
|
const char *const *envp, const env_var_list_t *extra_env_vars,
|
2022-05-28 19:27:29 +02:00
|
|
|
const char *term_env, const char *conf_shell, bool login_shell,
|
|
|
|
|
const user_notifications_t *notifications)
|
2019-10-30 18:05:03 +01:00
|
|
|
{
|
2020-08-01 09:41:31 +02:00
|
|
|
int fork_pipe[2];
|
|
|
|
|
if (pipe2(fork_pipe, O_CLOEXEC) < 0) {
|
|
|
|
|
LOG_ERRNO("failed to create pipe");
|
2019-10-30 18:05:03 +01:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pid_t pid = fork();
|
|
|
|
|
switch (pid) {
|
|
|
|
|
case -1:
|
|
|
|
|
LOG_ERRNO("failed to fork");
|
2020-08-01 09:41:31 +02:00
|
|
|
close(fork_pipe[0]);
|
|
|
|
|
close(fork_pipe[1]);
|
2019-10-30 18:05:03 +01:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
case 0:
|
|
|
|
|
/* Child */
|
2020-08-01 09:41:31 +02:00
|
|
|
close(fork_pipe[0]); /* Close read end */
|
2019-10-30 18:05:03 +01:00
|
|
|
|
2020-02-20 18:46:35 +01:00
|
|
|
if (chdir(cwd) < 0) {
|
2021-02-21 20:33:07 +01:00
|
|
|
const int errno_copy = errno;
|
2021-08-30 17:55:16 +02:00
|
|
|
LOG_ERRNO("failed to change working directory to %s", cwd);
|
2021-02-21 20:33:07 +01:00
|
|
|
(void)!write(fork_pipe[1], &errno_copy, sizeof(errno_copy));
|
|
|
|
|
_exit(errno_copy);
|
2020-02-20 18:46:35 +01:00
|
|
|
}
|
2019-12-21 19:57:28 +01:00
|
|
|
|
2021-06-02 17:48:57 +02:00
|
|
|
/* Restore signal mask, and SIG_IGN'd 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);
|
2020-05-21 20:22:24 +02:00
|
|
|
sigset_t mask;
|
|
|
|
|
sigemptyset(&mask);
|
2021-12-18 23:22:52 +01:00
|
|
|
|
2021-06-02 17:48:57 +02:00
|
|
|
if (sigprocmask(SIG_SETMASK, &mask, NULL) < 0 ||
|
2021-12-18 23:22:52 +01:00
|
|
|
sigaction(SIGHUP, &dfl, NULL) < 0 ||
|
|
|
|
|
sigaction(SIGPIPE, &dfl, NULL) < 0)
|
2021-06-02 17:48:57 +02:00
|
|
|
{
|
2021-02-21 20:33:07 +01:00
|
|
|
const int errno_copy = errno;
|
2020-08-23 07:44:02 +02:00
|
|
|
LOG_ERRNO_P(errno, "failed to restore signals");
|
2021-02-21 20:33:07 +01:00
|
|
|
(void)!write(fork_pipe[1], &errno_copy, sizeof(errno_copy));
|
|
|
|
|
_exit(errno_copy);
|
2019-11-03 13:24:15 +01:00
|
|
|
}
|
|
|
|
|
|
slave: don't skip setting environment variables when using a custom environment
When launching footclient with -E,--client-environment the environment
variables that should be set by foot, wasn't.
Those variables are:
* TERM
* COLORTERM
* PWD
* SHELL
and all variables defined by the user in the [environment] section in
foot.ini.
In the same way, we did not *unset* TERM_PROGRAM and
TERM_PROGRAM_VERSION.
This patch fixes it by "cloning" the custom environment, making it
mutable, and then adding/removing the variables above from it.
Instead of calling setenv()/unsetenv() directly, we add the wrapper
functions add_to_env() and del_from_env().
When *not* using a custom environment, they simply call
setenv()/unsetenv().
When we *are* using a custom environment, add_to_env() first loops all
existing variables, looking for a match. If a match is found, it's
updated with the new value. If it's not found, a new entry is added.
del_from_env() loops all entries, and removes it when a match is
found. If no match is found, nothing is done.
The mutable environment is allocated on the heap, but never free:d. We
don't need to free it, since it's only allocated after forking, in the
child process.
Closes #1568
2024-01-09 16:50:47 +01:00
|
|
|
/* Create a mutable copy of the environment */
|
|
|
|
|
struct environ custom_env = {0};
|
|
|
|
|
if (envp != NULL) {
|
|
|
|
|
for (const char *const *e = envp; *e != NULL; e++)
|
|
|
|
|
custom_env.count++;
|
|
|
|
|
|
|
|
|
|
custom_env.envp = xcalloc(
|
|
|
|
|
custom_env.count + 1, sizeof(custom_env.envp[0]));
|
|
|
|
|
|
|
|
|
|
size_t i = 0;
|
|
|
|
|
for (const char *const *e = envp; *e != NULL; e++, i++)
|
|
|
|
|
custom_env.envp[i] = xstrdup(*e);
|
|
|
|
|
xassert(custom_env.envp[custom_env.count] == NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
add_to_env(&custom_env, "TERM", term_env);
|
|
|
|
|
add_to_env(&custom_env, "COLORTERM", "truecolor");
|
|
|
|
|
add_to_env(&custom_env, "PWD", cwd);
|
2019-11-01 21:01:15 +01:00
|
|
|
|
slave: don't skip setting environment variables when using a custom environment
When launching footclient with -E,--client-environment the environment
variables that should be set by foot, wasn't.
Those variables are:
* TERM
* COLORTERM
* PWD
* SHELL
and all variables defined by the user in the [environment] section in
foot.ini.
In the same way, we did not *unset* TERM_PROGRAM and
TERM_PROGRAM_VERSION.
This patch fixes it by "cloning" the custom environment, making it
mutable, and then adding/removing the variables above from it.
Instead of calling setenv()/unsetenv() directly, we add the wrapper
functions add_to_env() and del_from_env().
When *not* using a custom environment, they simply call
setenv()/unsetenv().
When we *are* using a custom environment, add_to_env() first loops all
existing variables, looking for a match. If a match is found, it's
updated with the new value. If it's not found, a new entry is added.
del_from_env() loops all entries, and removes it when a match is
found. If no match is found, nothing is done.
The mutable environment is allocated on the heap, but never free:d. We
don't need to free it, since it's only allocated after forking, in the
child process.
Closes #1568
2024-01-09 16:50:47 +01:00
|
|
|
del_from_env(&custom_env, "TERM_PROGRAM");
|
|
|
|
|
del_from_env(&custom_env, "TERM_PROGRAM_VERSION");
|
2023-05-12 14:55:55 +02:00
|
|
|
|
terminfo: install to $datadir/foot/terminfo by default, append to TERMINFO_DIRS
As of 2021-07-31, ncurses ships its own version of foot’s terminfo.
Since:
* It doesn’t have the non-standard Sync,Tc,setrgbf,setrgbb
capabilities.
* It doesn’t set hs,fsl,dsl (statusbar).
* We want to be able to update our termminfo without waiting for an
ncurses release.
* Foot should be installable and usable on Linux systems that doesn’t
have the latest ncurses.
we still want to ship our own version. We can however not install it
to the default terminfo location (e.g. /usr/share/terminfo), since it
will collide with the ncurses provided files.
Our options are to either rename our terminfo to something else, or to
keep the name, but install our terminfo files somewhere else.
The first option would be the easy one. However, I think it makes
sense to use the same name. For example, a user that SSH’s into a
remote system that does *not* have our own version installed,
but *does* have the ncurses one, will gracefully fall back to that
one, which is better than manually having to set
e.g. TERM=xterm-256color.
Now, if we want to use the same name, we need to install it somewhere
else. But where? And how do we ensure our version is preferred over
the ncurses one?
I opted to $datadir/foot/terminfo (e.g. /usr/share/foot/terminfo) by
default. It makes it namespaced to foot (i.e. we’re not introducing a
new “standard” terminfo location), thus guaranteeing it wont collide
with ncurses.
To enable applications to find it, we export TERMINFO_DIRS. This is a
list of *additional* directories to search for terminfo files. If it’s
already defined, we *append* to it.
The nice thing with this is, if there’s no terminfo in that
location (e.g. when you SSH into a remote), the default terminfo
location is *also* searched. But only *after* having searched through
TERMINFO_DIRS.
In short: our version is preferred, but the ncurses one (or an older
version of our terminfo package!) will be used if ours cannot be
found.
To enable packagers full control over the new behavior, the existing
meson command line options have been modified, and a new option added:
-Dterminfo=disabled|enabled|auto: *build* and *install* the terminfo
files.
-Dcustom-terminfo-install-location=<path>: *where* the terminfo files
are expected to be found.
This *needs* to be set *even* if -Dterminfo=disabled. For example, if
the packaging script builds and packages the terminfo files separate
from the regular foot build. The path is *relative to $prefix*, and
defaults to $datadir/foot/terminfo.
This is the value that will be appended to TERMINFO_DIRS. Note that
you can set it to ‘no’, in which case foot will *not* set/modify
TERMINFO_DIRS. Only do this if you don’t intend to package foot’s
terminfo files at all (i.e. you plan on using the ncurses ones only).
-Ddefault-terminfo=foot. Allows overriding the default TERM
value. This should only be changed if the target platform doesn’t
support terminfo files.
Closes #671
2021-08-07 16:42:51 +02:00
|
|
|
#if defined(FOOT_TERMINFO_PATH)
|
2024-01-16 22:40:35 +02:00
|
|
|
add_to_env(&custom_env, "TERMINFO", FOOT_TERMINFO_PATH);
|
terminfo: install to $datadir/foot/terminfo by default, append to TERMINFO_DIRS
As of 2021-07-31, ncurses ships its own version of foot’s terminfo.
Since:
* It doesn’t have the non-standard Sync,Tc,setrgbf,setrgbb
capabilities.
* It doesn’t set hs,fsl,dsl (statusbar).
* We want to be able to update our termminfo without waiting for an
ncurses release.
* Foot should be installable and usable on Linux systems that doesn’t
have the latest ncurses.
we still want to ship our own version. We can however not install it
to the default terminfo location (e.g. /usr/share/terminfo), since it
will collide with the ncurses provided files.
Our options are to either rename our terminfo to something else, or to
keep the name, but install our terminfo files somewhere else.
The first option would be the easy one. However, I think it makes
sense to use the same name. For example, a user that SSH’s into a
remote system that does *not* have our own version installed,
but *does* have the ncurses one, will gracefully fall back to that
one, which is better than manually having to set
e.g. TERM=xterm-256color.
Now, if we want to use the same name, we need to install it somewhere
else. But where? And how do we ensure our version is preferred over
the ncurses one?
I opted to $datadir/foot/terminfo (e.g. /usr/share/foot/terminfo) by
default. It makes it namespaced to foot (i.e. we’re not introducing a
new “standard” terminfo location), thus guaranteeing it wont collide
with ncurses.
To enable applications to find it, we export TERMINFO_DIRS. This is a
list of *additional* directories to search for terminfo files. If it’s
already defined, we *append* to it.
The nice thing with this is, if there’s no terminfo in that
location (e.g. when you SSH into a remote), the default terminfo
location is *also* searched. But only *after* having searched through
TERMINFO_DIRS.
In short: our version is preferred, but the ncurses one (or an older
version of our terminfo package!) will be used if ours cannot be
found.
To enable packagers full control over the new behavior, the existing
meson command line options have been modified, and a new option added:
-Dterminfo=disabled|enabled|auto: *build* and *install* the terminfo
files.
-Dcustom-terminfo-install-location=<path>: *where* the terminfo files
are expected to be found.
This *needs* to be set *even* if -Dterminfo=disabled. For example, if
the packaging script builds and packages the terminfo files separate
from the regular foot build. The path is *relative to $prefix*, and
defaults to $datadir/foot/terminfo.
This is the value that will be appended to TERMINFO_DIRS. Note that
you can set it to ‘no’, in which case foot will *not* set/modify
TERMINFO_DIRS. Only do this if you don’t intend to package foot’s
terminfo files at all (i.e. you plan on using the ncurses ones only).
-Ddefault-terminfo=foot. Allows overriding the default TERM
value. This should only be changed if the target platform doesn’t
support terminfo files.
Closes #671
2021-08-07 16:42:51 +02:00
|
|
|
#endif
|
|
|
|
|
|
2022-05-28 19:27:29 +02:00
|
|
|
if (extra_env_vars != NULL) {
|
2022-12-01 19:43:38 +01:00
|
|
|
tll_foreach(*extra_env_vars, it) {
|
|
|
|
|
const char *name = it->item.name;
|
|
|
|
|
const char *value = it->item.value;
|
|
|
|
|
|
|
|
|
|
if (strlen(value) == 0)
|
slave: don't skip setting environment variables when using a custom environment
When launching footclient with -E,--client-environment the environment
variables that should be set by foot, wasn't.
Those variables are:
* TERM
* COLORTERM
* PWD
* SHELL
and all variables defined by the user in the [environment] section in
foot.ini.
In the same way, we did not *unset* TERM_PROGRAM and
TERM_PROGRAM_VERSION.
This patch fixes it by "cloning" the custom environment, making it
mutable, and then adding/removing the variables above from it.
Instead of calling setenv()/unsetenv() directly, we add the wrapper
functions add_to_env() and del_from_env().
When *not* using a custom environment, they simply call
setenv()/unsetenv().
When we *are* using a custom environment, add_to_env() first loops all
existing variables, looking for a match. If a match is found, it's
updated with the new value. If it's not found, a new entry is added.
del_from_env() loops all entries, and removes it when a match is
found. If no match is found, nothing is done.
The mutable environment is allocated on the heap, but never free:d. We
don't need to free it, since it's only allocated after forking, in the
child process.
Closes #1568
2024-01-09 16:50:47 +01:00
|
|
|
del_from_env(&custom_env, name);
|
2022-12-01 19:43:38 +01:00
|
|
|
else
|
slave: don't skip setting environment variables when using a custom environment
When launching footclient with -E,--client-environment the environment
variables that should be set by foot, wasn't.
Those variables are:
* TERM
* COLORTERM
* PWD
* SHELL
and all variables defined by the user in the [environment] section in
foot.ini.
In the same way, we did not *unset* TERM_PROGRAM and
TERM_PROGRAM_VERSION.
This patch fixes it by "cloning" the custom environment, making it
mutable, and then adding/removing the variables above from it.
Instead of calling setenv()/unsetenv() directly, we add the wrapper
functions add_to_env() and del_from_env().
When *not* using a custom environment, they simply call
setenv()/unsetenv().
When we *are* using a custom environment, add_to_env() first loops all
existing variables, looking for a match. If a match is found, it's
updated with the new value. If it's not found, a new entry is added.
del_from_env() loops all entries, and removes it when a match is
found. If no match is found, nothing is done.
The mutable environment is allocated on the heap, but never free:d. We
don't need to free it, since it's only allocated after forking, in the
child process.
Closes #1568
2024-01-09 16:50:47 +01:00
|
|
|
add_to_env(&custom_env, name, value);
|
2022-12-01 19:43:38 +01:00
|
|
|
}
|
2022-05-28 19:27:29 +02:00
|
|
|
}
|
|
|
|
|
|
2019-10-30 18:05:03 +01:00
|
|
|
char **_shell_argv = NULL;
|
2020-02-20 18:36:09 +01:00
|
|
|
char **shell_argv = NULL;
|
2019-10-30 18:05:03 +01:00
|
|
|
|
|
|
|
|
if (argc == 0) {
|
2021-06-20 14:17:31 +02:00
|
|
|
if (!tokenize_cmdline(conf_shell, &_shell_argv)) {
|
2020-08-01 09:41:31 +02:00
|
|
|
(void)!write(fork_pipe[1], &errno, sizeof(errno));
|
|
|
|
|
_exit(0);
|
2019-10-30 18:05:03 +01:00
|
|
|
}
|
|
|
|
|
shell_argv = _shell_argv;
|
2020-02-20 18:36:09 +01:00
|
|
|
} else {
|
|
|
|
|
size_t count = 0;
|
|
|
|
|
for (; argv[count] != NULL; count++)
|
|
|
|
|
;
|
2020-08-08 20:34:30 +01:00
|
|
|
shell_argv = xmalloc((count + 1) * sizeof(shell_argv[0]));
|
2020-02-20 18:36:09 +01:00
|
|
|
for (size_t i = 0; i < count; i++)
|
|
|
|
|
shell_argv[i] = argv[i];
|
|
|
|
|
shell_argv[count] = NULL;
|
2019-10-30 18:05:03 +01:00
|
|
|
}
|
|
|
|
|
|
2020-03-02 18:46:35 +01:00
|
|
|
if (is_valid_shell(shell_argv[0]))
|
slave: don't skip setting environment variables when using a custom environment
When launching footclient with -E,--client-environment the environment
variables that should be set by foot, wasn't.
Those variables are:
* TERM
* COLORTERM
* PWD
* SHELL
and all variables defined by the user in the [environment] section in
foot.ini.
In the same way, we did not *unset* TERM_PROGRAM and
TERM_PROGRAM_VERSION.
This patch fixes it by "cloning" the custom environment, making it
mutable, and then adding/removing the variables above from it.
Instead of calling setenv()/unsetenv() directly, we add the wrapper
functions add_to_env() and del_from_env().
When *not* using a custom environment, they simply call
setenv()/unsetenv().
When we *are* using a custom environment, add_to_env() first loops all
existing variables, looking for a match. If a match is found, it's
updated with the new value. If it's not found, a new entry is added.
del_from_env() loops all entries, and removes it when a match is
found. If no match is found, nothing is done.
The mutable environment is allocated on the heap, but never free:d. We
don't need to free it, since it's only allocated after forking, in the
child process.
Closes #1568
2024-01-09 16:50:47 +01:00
|
|
|
add_to_env(&custom_env, "SHELL", shell_argv[0]);
|
2020-03-02 18:46:35 +01:00
|
|
|
|
slave: don't skip setting environment variables when using a custom environment
When launching footclient with -E,--client-environment the environment
variables that should be set by foot, wasn't.
Those variables are:
* TERM
* COLORTERM
* PWD
* SHELL
and all variables defined by the user in the [environment] section in
foot.ini.
In the same way, we did not *unset* TERM_PROGRAM and
TERM_PROGRAM_VERSION.
This patch fixes it by "cloning" the custom environment, making it
mutable, and then adding/removing the variables above from it.
Instead of calling setenv()/unsetenv() directly, we add the wrapper
functions add_to_env() and del_from_env().
When *not* using a custom environment, they simply call
setenv()/unsetenv().
When we *are* using a custom environment, add_to_env() first loops all
existing variables, looking for a match. If a match is found, it's
updated with the new value. If it's not found, a new entry is added.
del_from_env() loops all entries, and removes it when a match is
found. If no match is found, nothing is done.
The mutable environment is allocated on the heap, but never free:d. We
don't need to free it, since it's only allocated after forking, in the
child process.
Closes #1568
2024-01-09 16:50:47 +01:00
|
|
|
slave_exec(ptmx, shell_argv,
|
|
|
|
|
custom_env.envp != NULL ? custom_env.envp : environ,
|
2022-04-11 12:19:40 +02:00
|
|
|
fork_pipe[1], login_shell, notifications);
|
2021-02-10 09:18:50 +00:00
|
|
|
BUG("Unexpected return from slave_exec()");
|
2019-10-30 18:05:03 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default: {
|
2024-01-03 14:07:15 +01:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Don't stay in CWD, since it may be an ephemeral path. For
|
|
|
|
|
* example, it may be a mount point of, say, a thumb drive. Us
|
|
|
|
|
* keeping it open will prevent the user from unmounting it.
|
|
|
|
|
*/
|
2024-01-05 08:12:32 +01:00
|
|
|
(void)!!chdir("/");
|
2024-01-03 14:07:15 +01:00
|
|
|
|
2020-08-01 09:41:31 +02:00
|
|
|
close(fork_pipe[1]); /* Close write end */
|
2019-10-30 20:20:56 +01:00
|
|
|
LOG_DBG("slave has PID %d", pid);
|
2019-10-30 18:05:03 +01:00
|
|
|
|
2021-02-21 20:33:07 +01:00
|
|
|
int errno_copy;
|
|
|
|
|
static_assert(sizeof(errno) == sizeof(errno_copy), "errno size mismatch");
|
2020-08-01 09:41:31 +02:00
|
|
|
|
2021-02-21 20:33:07 +01:00
|
|
|
ssize_t ret = read(fork_pipe[0], &errno_copy, sizeof(errno_copy));
|
2020-08-01 09:41:31 +02:00
|
|
|
close(fork_pipe[0]);
|
2019-10-30 18:05:03 +01:00
|
|
|
|
|
|
|
|
if (ret < 0) {
|
2020-08-01 09:41:31 +02:00
|
|
|
LOG_ERRNO("failed to read from pipe");
|
2019-10-30 18:05:03 +01:00
|
|
|
return -1;
|
2021-02-21 20:33:07 +01:00
|
|
|
} else if (ret == sizeof(errno_copy)) {
|
2019-10-30 20:21:19 +01:00
|
|
|
LOG_ERRNO_P(
|
2021-02-21 20:33:07 +01:00
|
|
|
errno_copy, "%s: failed to execute",
|
2020-08-23 07:44:02 +02:00
|
|
|
argc == 0 ? conf_shell : argv[0]);
|
2019-10-30 18:05:03 +01:00
|
|
|
return -1;
|
|
|
|
|
} else
|
2019-10-30 20:20:56 +01:00
|
|
|
LOG_DBG("%s: successfully started", conf_shell);
|
2019-11-03 01:14:02 +01:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
2019-10-30 18:05:03 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return pid;
|
|
|
|
|
}
|