Skip to content

Commit

Permalink
Fully migrate to Rust env_init
Browse files Browse the repository at this point in the history
  • Loading branch information
henrikhorluck committed Aug 18, 2023
1 parent c866b44 commit 35888e8
Show file tree
Hide file tree
Showing 7 changed files with 6 additions and 242 deletions.
203 changes: 0 additions & 203 deletions src/env.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,6 @@
#include "wcstringutil.h"
#include "wutil.h" // IWYU pragma: keep

/// Some configuration path environment variables.
#define FISH_DATADIR_VAR L"__fish_data_dir"
#define FISH_SYSCONFDIR_VAR L"__fish_sysconf_dir"
#define FISH_HELPDIR_VAR L"__fish_help_dir"
#define FISH_BIN_DIR L"__fish_bin_dir"
#define FISH_CONFIG_DIR L"__fish_config_dir"
#define FISH_USER_DATA_DIR L"__fish_user_data_dir"

/// At init, we read all the environment variables from this array.
extern char **environ;

Expand Down Expand Up @@ -144,58 +136,6 @@ std::vector<wcstring> null_environment_t::get_names(env_mode_flags_t flags) cons
return std::move(names.vals);
}

/// Set up the USER and HOME variable.
static void setup_user(env_stack_t &vars) {
auto uid = geteuid();
auto user_var = vars.get_unless_empty(L"USER");
struct passwd userinfo;
struct passwd *result;
char buf[8192];

// If we have a $USER, we try to get the passwd entry for the name.
// If that has the same UID that we use, we assume the data is correct.
if (user_var) {
std::string unam_narrow = wcs2zstring(user_var->as_string());
int retval = getpwnam_r(unam_narrow.c_str(), &userinfo, buf, sizeof(buf), &result);
if (!retval && result) {
if (result->pw_uid == uid) {
// The uid matches but we still might need to set $HOME.
if (!vars.get_unless_empty(L"HOME")) {
if (userinfo.pw_dir) {
vars.set_one(L"HOME", ENV_GLOBAL | ENV_EXPORT,
str2wcstring(userinfo.pw_dir));
} else {
vars.set_empty(L"HOME", ENV_GLOBAL | ENV_EXPORT);
}
}
return;
}
}
}

// Either we didn't have a $USER or it had a different uid.
// We need to get the data *again* via the uid.
int retval = getpwuid_r(uid, &userinfo, buf, sizeof(buf), &result);
if (!retval && result) {
const wcstring uname = str2wcstring(userinfo.pw_name);
vars.set_one(L"USER", ENV_GLOBAL | ENV_EXPORT, uname);
// Only change $HOME if it's empty, so we allow e.g. `HOME=(mktemp -d)`.
// This is okay with common `su` and `sudo` because they set $HOME.
if (!vars.get_unless_empty(L"HOME")) {
if (userinfo.pw_dir) {
vars.set_one(L"HOME", ENV_GLOBAL | ENV_EXPORT, str2wcstring(userinfo.pw_dir));
} else {
// We cannot get $HOME. This triggers warnings for history and config.fish already,
// so it isn't necessary to warn here as well.
vars.set_empty(L"HOME", ENV_GLOBAL | ENV_EXPORT);
}
}
} else if (!vars.get_unless_empty(L"HOME")) {
// If $USER is empty as well (which we tried to set above), we can't get $HOME.
vars.set_empty(L"HOME", ENV_GLOBAL | ENV_EXPORT);
}
}

/// Various things we need to initialize at run-time that don't really fit any of the other init
/// routines.
void misc_init() {
Expand Down Expand Up @@ -252,149 +192,6 @@ void set_inheriteds_ffi() {
}
}

void env_init(const struct config_paths_t *paths, bool do_uvars, bool default_paths) {
env_stack_t &vars = env_stack_t::principal();
// Import environment variables. Walk backwards so that the first one out of any duplicates wins
// (See issue #2784).
wcstring key, val;
const char *const *envp = environ;
int i = 0;
while (envp && envp[i]) i++;
while (i--) {
const wcstring key_and_val = str2wcstring(envp[i]); // like foo=bar
size_t eql = key_and_val.find(L'=');
if (eql == wcstring::npos) {
// No equal-sign found so treat it as a defined var that has no value(s).
if (!var_is_electric(key_and_val)) {
vars.set_empty(key_and_val, ENV_EXPORT | ENV_GLOBAL);
}
inheriteds[key] = L"";
} else {
key.assign(key_and_val, 0, eql);
val.assign(key_and_val, eql + 1, wcstring::npos);
inheriteds[key] = val;
if (!var_is_electric(key)) {
// fish_user_paths should not be exported; attempting to re-import it from
// a value we previously (due to user error) exported will cause impossibly
// difficult to debug PATH problems.
if (key != L"fish_user_paths") {
vars.set(key, ENV_EXPORT | ENV_GLOBAL, {val});
}
}
}
}

// Set the given paths in the environment, if we have any.
if (paths != nullptr) {
vars.set_one(FISH_DATADIR_VAR, ENV_GLOBAL, paths->data);
vars.set_one(FISH_SYSCONFDIR_VAR, ENV_GLOBAL, paths->sysconf);
vars.set_one(FISH_HELPDIR_VAR, ENV_GLOBAL, paths->doc);
vars.set_one(FISH_BIN_DIR, ENV_GLOBAL, paths->bin);
if (default_paths) {
wcstring scstr = paths->data;
scstr.append(L"/functions");
vars.set_one(L"fish_function_path", ENV_GLOBAL, scstr);
}
}

// Set $USER, $HOME and $EUID
// This involves going to passwd and stuff.
vars.set_one(L"EUID", ENV_GLOBAL, to_string(static_cast<unsigned long long>(geteuid())));
setup_user(vars);

wcstring user_config_dir;
path_get_config(user_config_dir);
vars.set_one(FISH_CONFIG_DIR, ENV_GLOBAL, user_config_dir);

wcstring user_data_dir;
path_get_data(user_data_dir);
vars.set_one(FISH_USER_DATA_DIR, ENV_GLOBAL, user_data_dir);

// Set up a default PATH
setup_path();

// Set up $IFS - this used to be in share/config.fish, but really breaks if it isn't done.
vars.set_one(L"IFS", ENV_GLOBAL, L"\n \t");

// Ensure this var is present even before an interactive command is run so that if it is used
// in a function like `fish_prompt` or `fish_right_prompt` it is defined at the time the first
// prompt is written.
vars.set_one(L"CMD_DURATION", ENV_UNEXPORT, L"0");

// Set up the version variable.
wcstring version = str2wcstring(get_fish_version());
vars.set_one(L"version", ENV_GLOBAL, version);
vars.set_one(L"FISH_VERSION", ENV_GLOBAL, version);

// Set the $fish_pid variable.
vars.set_one(L"fish_pid", ENV_GLOBAL, to_string(getpid()));

// Set the $hostname variable
wcstring hostname = L"fish";
get_hostname_identifier(hostname);
vars.set_one(L"hostname", ENV_GLOBAL, hostname);

// Set up SHLVL variable. Not we can't use vars.get() because SHLVL is read-only, and therefore
// was not inherited from the environment.
if (is_interactive_session()) {
wcstring nshlvl_str = L"1";
if (const char *shlvl_var = getenv("SHLVL")) {
// TODO: Figure out how to handle invalid numbers better. Shouldn't we issue a
// diagnostic?
long shlvl_i = fish_wcstol(str2wcstring(shlvl_var).c_str());
if (!errno && shlvl_i >= 0) {
nshlvl_str = to_string(shlvl_i + 1);
}
}
vars.set_one(L"SHLVL", ENV_GLOBAL | ENV_EXPORT, nshlvl_str);
} else {
// If we're not interactive, simply pass the value along.
if (const char *shlvl_var = getenv("SHLVL")) {
vars.set_one(L"SHLVL", ENV_GLOBAL | ENV_EXPORT, str2wcstring(shlvl_var));
}
}

// initialize the PWD variable if necessary
// Note we may inherit a virtual PWD that doesn't match what getcwd would return; respect that
// if and only if it matches getcwd (#5647). Note we treat PWD as read-only so it was not set in
// vars.
//
// Also reject all paths that don't start with "/", this includes windows paths like "F:\foo".
// (see #7636)
const char *incoming_pwd_cstr = getenv("PWD");
wcstring incoming_pwd = incoming_pwd_cstr ? str2wcstring(incoming_pwd_cstr) : wcstring{};
if (!incoming_pwd.empty() && incoming_pwd.front() == L'/' &&
paths_are_same_file(incoming_pwd, L".")) {
vars.set_one(L"PWD", ENV_EXPORT | ENV_GLOBAL, incoming_pwd);
} else {
vars.set_pwd_from_getcwd();
}

// Initialize termsize variables.
auto termsize =
termsize_initialize_ffi(reinterpret_cast<const unsigned char *>(vars.get_impl_ffi()));
if (!vars.get_unless_empty(L"COLUMNS"))
vars.set_one(L"COLUMNS", ENV_GLOBAL, to_string(termsize.width));
if (!vars.get_unless_empty(L"LINES"))
vars.set_one(L"LINES", ENV_GLOBAL, to_string(termsize.height));

// Set fish_bind_mode to "default".
vars.set_one(FISH_BIND_MODE_VAR, ENV_GLOBAL, DEFAULT_BIND_MODE);

// Allow changes to variables to produce events.
env_dispatch_init_ffi(/* vars */);

init_input();

// Complain about invalid config paths.
// HACK: Assume the defaults are correct (in practice this is only --no-config anyway).
if (!default_paths) {
path_emit_config_directory_messages(vars);
}

rust_env_init(do_uvars);
}

bool env_stack_t::is_principal() const { return impl_->is_principal(); }

std::vector<rust::Box<Event>> env_stack_t::universal_sync(bool always) {
Expand Down
13 changes: 0 additions & 13 deletions src/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,6 @@ using env_mode_flags_t = uint16_t;
/// Return values for `env_stack_t::set()`.
enum { ENV_OK, ENV_PERM, ENV_SCOPE, ENV_INVALID, ENV_NOT_FOUND };

/// A struct of configuration directories, determined in main() that fish will optionally pass to
/// env_init.
struct config_paths_t {
wcstring data; // e.g., /usr/local/share
wcstring sysconf; // e.g., /usr/local/etc
wcstring doc; // e.g., /usr/local/share/doc/fish
wcstring bin; // e.g., /usr/local/bin
};

/// A collection of status and pipestatus.
struct statuses_t {
/// Status of the last job to exit.
Expand All @@ -114,10 +105,6 @@ struct statuses_t {
}
};

/// Initialize environment variable data.
void env_init(const struct config_paths_t *paths = nullptr, bool do_uvars = true,
bool default_paths = false);

/// Various things we need to initialize at run-time that don't really fit any of the other init
/// routines.
void misc_init();
Expand Down
19 changes: 0 additions & 19 deletions src/env_universal_common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -879,25 +879,6 @@ void env_universal_t::parse_message_2x_internal(const wcstring &msgstr, var_tabl
}
}

/// Maximum length of hostname. Longer hostnames are truncated.
#define HOSTNAME_LEN 255

/// Function to get an identifier based on the hostname.
bool get_hostname_identifier(wcstring &result) {
// The behavior of gethostname if the buffer size is insufficient differs by implementation and
// libc version Work around this by using a "guaranteed" sufficient buffer size then truncating
// the result.
bool success = false;
char hostname[256] = {};
if (gethostname(hostname, sizeof(hostname)) == 0) {
result.assign(str2wcstring(hostname));
result.assign(truncate(result, HOSTNAME_LEN));
// Don't return an empty hostname, we may attempt to open a directory instead.
success = !result.empty();
}
return success;
}

namespace {
class universal_notifier_shmem_poller_t final : public universal_notifier_t {
#ifdef __CYGWIN__
Expand Down
2 changes: 0 additions & 2 deletions src/env_universal_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,6 @@ struct var_table_ffi_t {
// This is exposed for testing.
enum class uvar_format_t { fish_2_x, fish_3_0, future };

bool get_hostname_identifier(wcstring &result);

/// Class representing universal variables.
class env_universal_t {
public:
Expand Down
3 changes: 2 additions & 1 deletion src/fish_indent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA

#include "ast.h"
#include "env.h"
#include "env/env_ffi.rs.h"
#include "fds.h"
#include "ffi_baggage.h"
#include "ffi_init.rs.h"
Expand Down Expand Up @@ -296,7 +297,7 @@ int main(int argc, char *argv[]) {
// (e.g., "# vim: set fileencoding=<encoding-name>:") or an emacs style comment
// (e.g., "# -*- coding: <encoding-name> -*-").
setlocale(LC_ALL, "");
env_init();
rust_env_init(true);

if (auto features_var = env_stack_t::globals().get(L"fish_features")) {
for (const wcstring &s : features_var->as_list()) {
Expand Down
5 changes: 2 additions & 3 deletions src/fish_key_reader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "common.h"
#include "cxxgen.h"
#include "env.h"
#include "env/env_ffi.rs.h"
#include "fallback.h" // IWYU pragma: keep
#include "ffi_baggage.h"
#include "ffi_init.rs.h"
Expand All @@ -36,8 +37,6 @@
#include "signals.h"
#include "wutil.h" // IWYU pragma: keep

struct config_paths_t determine_config_directory_paths(const char *argv0);

static const wchar_t *ctrl_symbolic_names[] = {
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
L"\\b", L"\\t", L"\\n", nullptr, nullptr, L"\\r", nullptr, nullptr,
Expand Down Expand Up @@ -273,7 +272,7 @@ static void process_input(bool continuous_mode, bool verbose) {
[[noreturn]] static void setup_and_process_keys(bool continuous_mode, bool verbose) {
set_interactive_session(true);
rust_init();
env_init();
rust_env_init(true);
reader_init();
parser_t &parser = parser_t::principal_parser();
scoped_push<bool> interactive{&parser.libdata().is_interactive, true};
Expand Down
3 changes: 2 additions & 1 deletion src/fish_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
#include "cxxgen.h"
#include "enum_set.h"
#include "env.h"
#include "env/env_ffi.rs.h"
#include "env_universal_common.h"
#include "expand.h"
#include "fallback.h" // IWYU pragma: keep
Expand Down Expand Up @@ -5683,7 +5684,7 @@ int main(int argc, char **argv) {
say(L"Testing low-level functionality");
rust_init();
proc_init();
env_init();
rust_env_init(true);
misc_init();
reader_init();

Expand Down

0 comments on commit 35888e8

Please sign in to comment.