From d02124902b4e27c3ed3ef48442ce17c5e0af9d56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 11 Apr 2022 12:19:40 +0200 Subject: [PATCH 1/7] client: add -E,--client-environment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When this option is used, the child process in the new terminal instance will inherit its environment from the footclient process, instead of the foot server’s. Implemented by sending (yet another) dynamic string list as part of the client -> server setup packet. When the new option is *not* used, the setup packet is now 2 bytes larger than before. On the server side, the slave process now uses execvpe() instead of execvp(). There’s plumbing to propagate a new ‘envp’ argument from term_init() all the way down to slave_exec(). If ‘envp’ is NULL, we use ‘environ’ instead (thus matching the old behavior of execvp()). Closes #1004 --- CHANGELOG.md | 4 +++ client-protocol.h | 4 ++- client.c | 85 ++++++++++++++++++++++++++++++++++------------- main.c | 2 +- server.c | 21 ++++++++++-- slave.c | 13 ++++---- slave.h | 4 +-- terminal.c | 4 +-- terminal.h | 2 +- 9 files changed, 100 insertions(+), 39 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e068bb2..573b409b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,10 @@ allows setting/saving/restoring/querying the keypad mode. * Sixel support can be disabled by setting `[tweak].sixel=no` (https://codeberg.org/dnkl/foot/issues/950). +* footclient: `-E,--client-environment` command line option. When + used, the child process in the new terminal instance inherits the + environment from the footclient process instead of the server’s + (https://codeberg.org/dnkl/foot/issues/1004). ### Changed diff --git a/client-protocol.h b/client-protocol.h index b0f39055..505825f6 100644 --- a/client-protocol.h +++ b/client-protocol.h @@ -19,11 +19,13 @@ struct client_data { uint16_t cwd_len; uint16_t override_count; uint16_t argc; + uint16_t env_count; /* char cwd[static cwd_len]; */ /* char token[static token_len]; */ /* struct client_string overrides[static override_count]; */ /* struct client_string argv[static argc]; */ + /* struct client_string envp[static env_count]; */ } __attribute__((packed)); -_Static_assert(sizeof(struct client_data) == 8, "protocol struct size error"); +_Static_assert(sizeof(struct client_data) == 10, "protocol struct size error"); diff --git a/client.c b/client.c index a76d759a..ad88fe5b 100644 --- a/client.c +++ b/client.c @@ -25,11 +25,11 @@ #include "version.h" #include "xmalloc.h" -struct override { +struct string { size_t len; char *str; }; -typedef tll(struct override) override_list_t; +typedef tll(struct string) string_list_t; static volatile sig_atomic_t aborted = 0; @@ -91,6 +91,7 @@ print_usage(const char *prog_name) " -H,--hold remain open after child process exits\n" " -N,--no-wait detach the client process from the running terminal, exiting immediately\n" " -o,--override=[section.]key=value override configuration option\n" + " -E, --client-environment exec shell using footclient's environment, instead of the server's\n" " -d,--log-level={info|warning|error|none} log level (info)\n" " -l,--log-colorize=[{never|always|auto}] enable/disable colorization of log output on stderr\n" " -v,--version show the version number and quit\n" @@ -102,20 +103,29 @@ print_usage(const char *prog_name) } static bool NOINLINE -push_override(override_list_t *overrides, const char *s, uint64_t *total_len) +push_string(string_list_t *string_list, const char *s, uint64_t *total_len) { size_t len = strlen(s) + 1; - if (len >= 1 << (8 * sizeof(struct client_string))) { - LOG_ERR("override length overflow"); + if (len >= 1 << (8 * sizeof(uint16_t))) { + LOG_ERR("string length overflow"); return false; } - struct override o = {len, xstrdup(s)}; - tll_push_back(*overrides, o); + struct string o = {len, xstrdup(s)}; + tll_push_back(*string_list, o); *total_len += sizeof(struct client_string) + o.len; return true; } +static void +free_string_list(string_list_t *string_list) +{ + tll_foreach(*string_list, it) { + free(it->item.str); + tll_remove(*string_list, it); + } +} + int main(int argc, char *const *argv) { @@ -140,6 +150,7 @@ main(int argc, char *const *argv) {"hold", no_argument, NULL, 'H'}, {"no-wait", no_argument, NULL, 'N'}, {"override", required_argument, NULL, 'o'}, + {"client-environment", no_argument, NULL, 'E'}, {"log-level", required_argument, NULL, 'd'}, {"log-colorize", optional_argument, NULL, 'l'}, {"version", no_argument, NULL, 'v'}, @@ -152,6 +163,7 @@ main(int argc, char *const *argv) enum log_class log_level = LOG_CLASS_INFO; enum log_colorize log_colorize = LOG_COLORIZE_AUTO; bool hold = false; + bool client_environment = false; /* Used to format overrides */ bool no_wait = false; @@ -169,35 +181,36 @@ main(int argc, char *const *argv) /* malloc:ed and needs to be in scope of all goto's */ int fd = -1; char *_cwd = NULL; - override_list_t overrides = tll_init(); struct client_string *cargv = NULL; + string_list_t overrides = tll_init(); + string_list_t envp = tll_init(); while (true) { - int c = getopt_long(argc, argv, "+t:T:a:w:W:mFLD:s:HNo:d:l::veh", longopts, NULL); + int c = getopt_long(argc, argv, "+t:T:a:w:W:mFLD:s:HNo:Ed:l::veh", longopts, NULL); if (c == -1) break; switch (c) { case 't': snprintf(buf, sizeof(buf), "term=%s", optarg); - if (!push_override(&overrides, buf, &total_len)) + if (!push_string(&overrides, buf, &total_len)) goto err; break; case 'T': snprintf(buf, sizeof(buf), "title=%s", optarg); - if (!push_override(&overrides, buf, &total_len)) + if (!push_string(&overrides, buf, &total_len)) goto err; break; case 'a': snprintf(buf, sizeof(buf), "app-id=%s", optarg); - if (!push_override(&overrides, buf, &total_len)) + if (!push_string(&overrides, buf, &total_len)) goto err; break; case 'L': - if (!push_override(&overrides, "login-shell=yes", &total_len)) + if (!push_string(&overrides, "login-shell=yes", &total_len)) goto err; break; @@ -219,7 +232,7 @@ main(int argc, char *const *argv) } snprintf(buf, sizeof(buf), "initial-window-size-pixels=%ux%u", width, height); - if (!push_override(&overrides, buf, &total_len)) + if (!push_string(&overrides, buf, &total_len)) goto err; break; } @@ -232,18 +245,18 @@ main(int argc, char *const *argv) } snprintf(buf, sizeof(buf), "initial-window-size-chars=%ux%u", width, height); - if (!push_override(&overrides, buf, &total_len)) + if (!push_string(&overrides, buf, &total_len)) goto err; break; } case 'm': - if (!push_override(&overrides, "initial-window-mode=maximized", &total_len)) + if (!push_string(&overrides, "initial-window-mode=maximized", &total_len)) goto err; break; case 'F': - if (!push_override(&overrides, "initial-window-mode=fullscreen", &total_len)) + if (!push_string(&overrides, "initial-window-mode=fullscreen", &total_len)) goto err; break; @@ -260,10 +273,14 @@ main(int argc, char *const *argv) break; case 'o': - if (!push_override(&overrides, optarg, &total_len)) + if (!push_string(&overrides, optarg, &total_len)) goto err; break; + case 'E': + client_environment = true; + break; + case 'd': { int lvl = log_level_from_string(optarg); if (unlikely(lvl < 0)) { @@ -373,9 +390,17 @@ main(int argc, char *const *argv) cwd = _cwd; } + if (client_environment) { + for (char **e = environ; *e != NULL; e++) { + if (!push_string(&envp, *e, &total_len)) + goto err; + } + } + /* String lengths, including NULL terminator */ const size_t cwd_len = strlen(cwd) + 1; const size_t override_count = tll_length(overrides); + const size_t env_count = tll_length(envp); const struct client_data data = { .hold = hold, @@ -385,6 +410,7 @@ main(int argc, char *const *argv) .cwd_len = cwd_len, .override_count = override_count, .argc = argc, + .env_count = env_count, }; /* Total packet length, not (yet) including argv[] */ @@ -409,7 +435,8 @@ main(int argc, char *const *argv) cwd_len >= 1 << (8 * sizeof(data.cwd_len)) || token_len >= 1 << (8 * sizeof(data.token_len)) || override_count > (size_t)(unsigned int)data.override_count || - argc > (int)(unsigned int)data.argc) + argc > (int)(unsigned int)data.argc || + env_count > (size_t)(unsigned int)data.env_count) { LOG_ERR("size overflow"); goto err; @@ -435,7 +462,7 @@ main(int argc, char *const *argv) /* Send overrides */ tll_foreach(overrides, it) { - const struct override *o = &it->item; + const struct string *o = &it->item; struct client_string s = {o->len}; if (sendall(fd, &s, sizeof(s)) < 0 || sendall(fd, o->str, o->len) < 0) @@ -455,6 +482,18 @@ main(int argc, char *const *argv) } } + /* Send environment */ + tll_foreach(envp, it) { + const struct string *e = &it->item; + struct client_string s = {e->len}; + if (sendall(fd, &s, sizeof(s)) < 0 || + sendall(fd, e->str, e->len) < 0) + { + LOG_ERRNO("failed to send setup packet (envp) to server"); + goto err; + } + } + struct sigaction sa = {.sa_handler = &sig_handler}; sigemptyset(&sa.sa_mask); if (sigaction(SIGINT, &sa, NULL) < 0 || sigaction(SIGTERM, &sa, NULL) < 0) { @@ -473,10 +512,8 @@ main(int argc, char *const *argv) ret = exit_code; err: - tll_foreach(overrides, it) { - free(it->item.str); - tll_remove(overrides, it); - } + free_string_list(&envp); + free_string_list(&overrides); free(cargv); free(_cwd); if (fd != -1) diff --git a/main.c b/main.c index 4011dcde..6da82ef2 100644 --- a/main.c +++ b/main.c @@ -608,7 +608,7 @@ main(int argc, char *const *argv) if (!as_server && (term = term_init( &conf, fdm, reaper, wayl, "foot", cwd, token, - argc, argv, + argc, argv, NULL, &term_shutdown_cb, &shutdown_ctx)) == NULL) { goto out; } diff --git a/server.c b/server.c index 2c41b479..31b8c633 100644 --- a/server.c +++ b/server.c @@ -146,6 +146,7 @@ fdm_client(struct fdm *fdm, int fd, int events, void *data) char **argv = NULL; config_override_t overrides = tll_init(); + char **envp = NULL; if (events & EPOLLHUP) goto shutdown; @@ -281,7 +282,21 @@ fdm_client(struct fdm *fdm, int fd, int events, void *data) argv[i] = (char *)p; p += arg.len; LOG_DBG("argv[%hu] = %.*s", i, arg.len, argv[i]); } - argv[cdata.argc] = NULL; + + /* envp */ + envp = cdata.env_count != 0 + ? xcalloc(cdata.env_count + 1, sizeof(envp[0])) + : NULL; + + for (uint16_t i = 0; i < cdata.env_count; i++) { + struct client_string e; + CHECK_BUF(sizeof(e)); + memcpy(&e, p, sizeof(e)); p += sizeof(e); + + CHECK_BUF_AND_NULL(e.len); + envp[i] = (char *)p; p += e.len; + LOG_DBG("env[%hu] = %.*s", i, e.len, envp[i]); + } #undef CHECK_BUF_AND_NULL #undef CHECK_BUF @@ -317,7 +332,7 @@ fdm_client(struct fdm *fdm, int fd, int events, void *data) instance->terminal = term_init( conf != NULL ? conf : server->conf, server->fdm, server->reaper, server->wayl, "footclient", cwd, token, - cdata.argc, argv, &term_shutdown_handler, instance); + cdata.argc, argv, envp, &term_shutdown_handler, instance); if (instance->terminal == NULL) { LOG_ERR("failed to instantiate new terminal"); @@ -336,6 +351,7 @@ fdm_client(struct fdm *fdm, int fd, int events, void *data) instance->client = client; client->instance = instance; free(argv); + free(envp); tll_free_and_free(overrides, free); } @@ -345,6 +361,7 @@ shutdown: LOG_DBG("client FD=%d: disconnected", client->fd); free(argv); + free(envp); tll_free_and_free(overrides, free); fdm_del(fdm, fd); client->fd = -1; diff --git a/slave.c b/slave.c index 4fbd5c9b..4e8610ce 100644 --- a/slave.c +++ b/slave.c @@ -145,8 +145,8 @@ emit_notifications(int fd, const user_notifications_t *notifications) } static noreturn void -slave_exec(int ptmx, char *argv[], int err_fd, bool login_shell, - const user_notifications_t *notifications) +slave_exec(int ptmx, char *argv[], char *const envp[], int err_fd, + bool login_shell, const user_notifications_t *notifications) { int pts = -1; const char *pts_name = ptsname(ptmx); @@ -232,7 +232,7 @@ slave_exec(int ptmx, char *argv[], int err_fd, bool login_shell, } else file = argv[0]; - execvp(file, argv); + execvpe(file, argv, envp); err: (void)!write(err_fd, &errno, sizeof(errno)); @@ -246,8 +246,8 @@ err: pid_t slave_spawn(int ptmx, int argc, const char *cwd, char *const *argv, - const char *term_env, const char *conf_shell, bool login_shell, - const user_notifications_t *notifications) + char *const *envp, const char *term_env, const char *conf_shell, + bool login_shell, const user_notifications_t *notifications) { int fork_pipe[2]; if (pipe2(fork_pipe, O_CLOEXEC) < 0) { @@ -319,7 +319,8 @@ slave_spawn(int ptmx, int argc, const char *cwd, char *const *argv, if (is_valid_shell(shell_argv[0])) setenv("SHELL", shell_argv[0], 1); - slave_exec(ptmx, shell_argv, fork_pipe[1], login_shell, notifications); + slave_exec(ptmx, shell_argv, envp != NULL ? envp : environ, + fork_pipe[1], login_shell, notifications); BUG("Unexpected return from slave_exec()"); break; diff --git a/slave.h b/slave.h index 538588b1..4b47bfde 100644 --- a/slave.h +++ b/slave.h @@ -6,6 +6,6 @@ #include "user-notification.h" pid_t slave_spawn( - int ptmx, int argc, const char *cwd, char *const *argv, const char *term_env, - const char *conf_shell, bool login_shell, + int ptmx, int argc, const char *cwd, char *const *argv, char *const *envp, + const char *term_env, const char *conf_shell, bool login_shell, const user_notifications_t *notifications); diff --git a/terminal.c b/terminal.c index 84edec65..b78279e4 100644 --- a/terminal.c +++ b/terminal.c @@ -1034,7 +1034,7 @@ static void fdm_client_terminated( struct terminal * term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper, struct wayland *wayl, const char *foot_exe, const char *cwd, - const char *token, int argc, char *const *argv, + const char *token, int argc, char *const *argv, char *const *envp, void (*shutdown_cb)(void *data, int exit_code), void *shutdown_data) { int ptmx = -1; @@ -1241,7 +1241,7 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper, /* Start the slave/client */ if ((term->slave = slave_spawn( - term->ptmx, argc, term->cwd, argv, + term->ptmx, argc, term->cwd, argv, envp, conf->term, conf->shell, conf->login_shell, &conf->notifications)) == -1) { diff --git a/terminal.h b/terminal.h index 03297678..5b7d6d07 100644 --- a/terminal.h +++ b/terminal.h @@ -670,7 +670,7 @@ struct config; struct terminal *term_init( const struct config *conf, struct fdm *fdm, struct reaper *reaper, struct wayland *wayl, const char *foot_exe, const char *cwd, - const char *token, int argc, char *const *argv, + const char *token, int argc, char *const *argv, char *const *envp, void (*shutdown_cb)(void *data, int exit_code), void *shutdown_data); bool term_shutdown(struct terminal *term); From fd1e5feda46c36dbddbd68bf970605aedbf84c5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 11 Apr 2022 12:31:45 +0200 Subject: [PATCH 2/7] doc: footclient: add -E,--client-environment --- doc/footclient.1.scd | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/footclient.1.scd b/doc/footclient.1.scd index 9cb4ef69..6ebd05e3 100644 --- a/doc/footclient.1.scd +++ b/doc/footclient.1.scd @@ -69,6 +69,10 @@ terminal has terminated. Override an option set in the configuration file. If _SECTION_ is not given, defaults to _main_. +*-E*,*--client-environment* + The child process in the new terminal instace will use + footclient's enviromnent, instead of the server's. + *-d*,*--log-level*={*info*,*warning*,*error*,*none*} Log level, used both for log output on stderr as well as syslog. Default: _info_. From f48955b26ec69c4885c3069a62285c55c5058a79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 11 Apr 2022 12:31:52 +0200 Subject: [PATCH 3/7] completions: footclient: add -E,--client-environment --- completions/bash/footclient | 1 + completions/fish/footclient.fish | 1 + completions/zsh/_footclient | 1 + 3 files changed, 3 insertions(+) diff --git a/completions/bash/footclient b/completions/bash/footclient index 9f752641..b672c247 100644 --- a/completions/bash/footclient +++ b/completions/bash/footclient @@ -14,6 +14,7 @@ _footclient() "--log-colorize" "--maximized" "--override" + "--client-environment" "--server-socket" "--term" "--title" diff --git a/completions/fish/footclient.fish b/completions/fish/footclient.fish index 8133f663..df3e1273 100644 --- a/completions/fish/footclient.fish +++ b/completions/fish/footclient.fish @@ -12,6 +12,7 @@ complete -c footclient -F -s s -l server-socket complete -c footclient -s H -l hold -d "remain open after child process exits" complete -c footclient -s N -l no-wait -d "detach the client process from the running terminal, exiting immediately" complete -c footclient -x -s o -l override -d "configuration option to override, in form SECTION.KEY=VALUE" +complete -c footclient -s E -l client-environment -d "child process inherits footclient's environment, instead of the server's" complete -c footclient -x -s d -l log-level -a "info warning error none" -d "log-level (info)" complete -c footclient -x -s l -l log-colorize -a "always never auto" -d "enable or disable colorization of log output on stderr" complete -c footclient -s v -l version -d "show the version number and quit" diff --git a/completions/zsh/_footclient b/completions/zsh/_footclient index 81b2ea95..b36644c6 100644 --- a/completions/zsh/_footclient +++ b/completions/zsh/_footclient @@ -15,6 +15,7 @@ _arguments \ '(-H --hold)'{-H,--hold}'[remain open after child process exits]' \ '(-N --no-wait)'{-N,--no-wait}'[detach the client process from the running terminal, exiting immediately]' \ '(-o --override)'{-o,--override}'[configuration option to override, in form SECTION.KEY=VALUE]:()' \ + '(-E --client-environment)'{-E,--client-environment}"[child process inherits footclient's environment, instead of the server's]" \ '(-d --log-level)'{-d,--log-level}'[log level (info)]:loglevel:(info warning error none)' \ '(-l --log-colorize)'{-l,--log-colorize}'[enable or disable colorization of log output on stderr]:logcolor:(never always auto)' \ '(-v --version)'{-v,--version}'[show the version number and quit]' \ From 3c6836e32d131e0a0d82fabb0d4f0d5cdbaafeb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 11 Apr 2022 12:34:38 +0200 Subject: [PATCH 4/7] doc: footclient: codespell --- doc/footclient.1.scd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/footclient.1.scd b/doc/footclient.1.scd index 6ebd05e3..285ff184 100644 --- a/doc/footclient.1.scd +++ b/doc/footclient.1.scd @@ -70,8 +70,8 @@ terminal has terminated. given, defaults to _main_. *-E*,*--client-environment* - The child process in the new terminal instace will use - footclient's enviromnent, instead of the server's. + The child process in the new terminal instance will use + footclient's environment, instead of the server's. *-d*,*--log-level*={*info*,*warning*,*error*,*none*} Log level, used both for log output on stderr as well as From 261797ec5692b4958d806cf5f639f1cf8f890928 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 11 Apr 2022 12:38:36 +0200 Subject: [PATCH 5/7] client: refactor: add send_string_list() function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use it to send ‘overrides’ and ‘envp’ --- client.c | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/client.c b/client.c index ad88fe5b..7d524204 100644 --- a/client.c +++ b/client.c @@ -126,6 +126,22 @@ free_string_list(string_list_t *string_list) } } +static bool +send_string_list(int fd, const string_list_t *string_list) +{ + tll_foreach(*string_list, it) { + const struct client_string s = {it->item.len}; + if (sendall(fd, &s, sizeof(s)) < 0 || + sendall(fd, it->item.str, s.len) < 0) + { + LOG_ERRNO("failed to send setup packet to server"); + return false; + } + } + + return true; +} + int main(int argc, char *const *argv) { @@ -461,16 +477,8 @@ main(int argc, char *const *argv) } /* Send overrides */ - tll_foreach(overrides, it) { - const struct string *o = &it->item; - struct client_string s = {o->len}; - if (sendall(fd, &s, sizeof(s)) < 0 || - sendall(fd, o->str, o->len) < 0) - { - LOG_ERRNO("failed to send setup packet (overrides) to server"); - goto err; - } - } + if (!send_string_list(fd, &overrides)) + goto err; /* Send argv[] */ for (size_t i = 0; i < argc; i++) { @@ -483,16 +491,8 @@ main(int argc, char *const *argv) } /* Send environment */ - tll_foreach(envp, it) { - const struct string *e = &it->item; - struct client_string s = {e->len}; - if (sendall(fd, &s, sizeof(s)) < 0 || - sendall(fd, e->str, e->len) < 0) - { - LOG_ERRNO("failed to send setup packet (envp) to server"); - goto err; - } - } + if (!send_string_list(fd, &envp)) + goto err; struct sigaction sa = {.sa_handler = &sig_handler}; sigemptyset(&sa.sa_mask); From fd414f79be177b23a67ce87b81657c2bfd80a35e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 11 Apr 2022 13:24:27 +0200 Subject: [PATCH 6/7] =?UTF-8?q?client/slave:=20explictly=20add=20=E2=80=98?= =?UTF-8?q?extern=20char=20**environ=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It’s not in any header files on FreeBSD. --- client.c | 2 ++ slave.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/client.c b/client.c index 7d524204..7624e7db 100644 --- a/client.c +++ b/client.c @@ -25,6 +25,8 @@ #include "version.h" #include "xmalloc.h" +extern char **environ; + struct string { size_t len; char *str; diff --git a/slave.c b/slave.c index 4e8610ce..dc4a8bc6 100644 --- a/slave.c +++ b/slave.c @@ -23,6 +23,8 @@ #include "tokenize.h" #include "xmalloc.h" +extern char **environ; + static bool is_valid_shell(const char *shell) { From b3d0cdd4b20855c62a759b66da94907f6bf81cb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 11 Apr 2022 14:02:18 +0200 Subject: [PATCH 7/7] =?UTF-8?q?slave:=20roll=20our=20own=20=E2=80=98execvp?= =?UTF-8?q?e()=E2=80=99=20on=20FreeBSD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- slave.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/slave.c b/slave.c index dc4a8bc6..db60d7b5 100644 --- a/slave.c +++ b/slave.c @@ -25,6 +25,63 @@ extern char **environ; +#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__ */ + static bool is_valid_shell(const char *shell) { @@ -234,7 +291,7 @@ slave_exec(int ptmx, char *argv[], char *const envp[], int err_fd, } else file = argv[0]; - execvpe(file, argv, envp); + foot_execvpe(file, argv, envp); err: (void)!write(err_fd, &errno, sizeof(errno));