diff --git a/include/env.h b/include/env.h new file mode 100644 index 000000000..16a811a4c --- /dev/null +++ b/include/env.h @@ -0,0 +1,28 @@ +#ifndef _SWAY_ENV_H +#define _SWAY_ENV_H + +/** + * Deallocates an environment array created by + * sway_env_get_envp or sway_env_setenv. + */ +void env_destroy(char **envp); + +/** + * Gets a newly-allocated environment array pointer + * from the global environment. + */ +char **env_create(); + +/** + * Sets or overwrites an environment variable in the given environment. + * Setting a new variable will reallocate the entire array. + */ +char **env_setenv(char **envp, char *name, char *value); + +/** + * Unsets an environment variable in the given environment. + * If successful, this will reallocate the entire array. + */ +char **env_unsetenv(char **envp, char *name); + +#endif diff --git a/include/sway/commands.h b/include/sway/commands.h index 5210d3ba7..e933b3728 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -125,6 +125,8 @@ sway_cmd cmd_create_output; sway_cmd cmd_default_border; sway_cmd cmd_default_floating_border; sway_cmd cmd_default_orientation; +sway_cmd cmd_env; +sway_cmd cmd_env_unset; sway_cmd cmd_exec; sway_cmd cmd_exec_always; sway_cmd cmd_exit; diff --git a/sway/commands.c b/sway/commands.c index c2c12ee65..e19f86867 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -57,6 +57,8 @@ static const struct cmd_handler handlers[] = { { "client.urgent", cmd_client_urgent }, { "default_border", cmd_default_border }, { "default_floating_border", cmd_default_floating_border }, + { "env", cmd_env }, + { "env_unset", cmd_env_unset }, { "exec", cmd_exec }, { "exec_always", cmd_exec_always }, { "floating_maximum_size", cmd_floating_maximum_size }, diff --git a/sway/commands/env.c b/sway/commands/env.c new file mode 100644 index 000000000..aba4e33b2 --- /dev/null +++ b/sway/commands/env.c @@ -0,0 +1,28 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include "sway/commands.h" +#include "env.h" + +extern char **child_envp; + +struct cmd_results *cmd_env(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "env", EXPECTED_EQUAL_TO, 2))) { + return error; + } + + child_envp = env_setenv(child_envp, argv[0], argv[1]); + + return cmd_results_new(CMD_SUCCESS, NULL); +} + +struct cmd_results *cmd_env_unset(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "env_unset", EXPECTED_EQUAL_TO, 1))) { + return error; + } + + child_envp = env_unsetenv(child_envp, argv[0]); + + return cmd_results_new(CMD_SUCCESS, NULL); +} diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c index 8bc1048cd..502beeb30 100644 --- a/sway/commands/exec_always.c +++ b/sway/commands/exec_always.c @@ -14,6 +14,8 @@ #include "log.h" #include "stringop.h" +extern char** child_envp; + struct cmd_results *cmd_exec_validate(int argc, char **argv) { struct cmd_results *error = NULL; if ((error = checkarg(argc, argv[-1], EXPECTED_AT_LEAST, 1))) { @@ -81,7 +83,7 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) { if (ctx && !no_startup_id) { export_startup_id(ctx); } - execlp("sh", "sh", "-c", cmd, (void *)NULL); + execle("/bin/sh", "sh", "-c", cmd, (void *)NULL, child_envp); sway_log_errno(SWAY_ERROR, "execlp failed"); _exit(1); } diff --git a/sway/env.c b/sway/env.c new file mode 100644 index 000000000..8c7d63c24 --- /dev/null +++ b/sway/env.c @@ -0,0 +1,125 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include + +typedef struct { + char *ptr; + size_t idx; +} env_info; + +extern char **environ; + +int env_name_eq(char *env_name, char *cmp) { + size_t i = 0; + int reached_eq; + while (1) { + char current_char = env_name[i]; + char cmp_char = cmp[i]; + + int is_eq = current_char == '='; + int is_null = current_char == '\0'; + + if (is_eq) reached_eq = 1; + if (is_null && !reached_eq) return 0; + if (is_eq && cmp_char == '\0') return 1; + if (is_eq || cmp_char == '\0') return 0; + if (current_char != cmp_char) return 0; + + i++; + } +} + +env_info env_get(char **envp, char *name) { + char *strp; + size_t i = 0; + while ((strp = envp[i]) != NULL) { + if (env_name_eq(strp, name)) return (env_info){strp, i}; + i++; + } + + return (env_info){NULL, 0}; +} + +size_t env_len(char **envp) { + char *strp; + size_t i = 0; + + while ((strp = envp[i]) != NULL) { + i++; + } + + return i; +} + +char **env_clone(char **envp, size_t reserve, env_info exclude) { + size_t elem_count = env_len(envp) + 1 + reserve - (exclude.ptr != NULL); + char **new_envp = calloc(elem_count, sizeof(char *)); + + char *strp; + size_t i = 0; + size_t new_i = 0; + + while ((strp = envp[i]) != NULL) { + if (exclude.ptr == strp) { + i++; + continue; + } + size_t n = strlen(strp) + 1; + char *new_strp = malloc(n); + memcpy(new_strp, strp, n); + new_envp[new_i] = new_strp; + i++; + new_i++; + } + + return new_envp; +} + +void env_destroy(char **envp) { + char *strp; + size_t i = 0; + while ((strp = envp[i]) != NULL) { + free(strp); + i++; + } + + free(envp); +} + +// copy the global environment array into a newly-allocated one +// you are responsible for deallocating it after use +char **env_create() { return env_clone(environ, 0, (env_info){NULL, 0}); } + +// use env_get_envp() to acquire an envp +// might clone and deallocate the given envp +char **env_setenv(char **envp, char *name, char *value) { + size_t name_len = strlen(name); + size_t value_len = strlen(value); + char *newp = malloc(name_len + value_len + 2); + memcpy(newp, name, name_len); + memcpy(newp + name_len + 1, value, value_len); + newp[name_len] = '='; + newp[name_len + value_len + 1] = '\0'; + + env_info existing = env_get(envp, name); + if (existing.ptr != NULL) { + free(existing.ptr); + envp[existing.idx] = newp; + return envp; + } else { + char **new_envp = env_clone(envp, 1, (env_info){NULL, 0}); + new_envp[env_len(envp)] = newp; + env_destroy(envp); + return new_envp; + } +} + +char **env_unsetenv(char **envp, char *name) { + env_info existing = env_get(envp, name); + if (existing.ptr == NULL) // dont do anything if + return envp; // the variable is not set + + char **new_envp = env_clone(envp, 0, existing); + env_destroy(envp); + return new_envp; +} diff --git a/sway/main.c b/sway/main.c index accffc71f..6150804f1 100644 --- a/sway/main.c +++ b/sway/main.c @@ -24,12 +24,14 @@ #include "log.h" #include "stringop.h" #include "util.h" +#include "env.h" static bool terminate_request = false; static int exit_value = 0; static struct rlimit original_nofile_rlimit = {0}; struct sway_server server = {0}; struct sway_debug debug = {0}; +char **child_envp; void sway_terminate(int exit_code) { if (!server.wl_display) { @@ -348,6 +350,8 @@ int main(int argc, char **argv) { ipc_init(&server); setenv("WAYLAND_DISPLAY", server.socket, true); + // env_get_envp creates a newly-allocated environment buffer + child_envp = env_create(); if (!load_main_config(config_path, false, false)) { sway_terminate(EXIT_FAILURE); goto shutdown; @@ -382,6 +386,7 @@ shutdown: free(config_path); free_config(config); + g_strfreev(child_envp); pango_cairo_font_map_set_default(NULL); diff --git a/sway/meson.build b/sway/meson.build index 8042c89be..75c2410ab 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -3,6 +3,7 @@ sway_sources = files( 'config.c', 'criteria.c', 'decoration.c', + 'env.c', 'ipc-json.c', 'ipc-server.c', 'lock.c', @@ -52,6 +53,7 @@ sway_sources = files( 'commands/default_border.c', 'commands/default_floating_border.c', 'commands/default_orientation.c', + 'commands/env.c', 'commands/exit.c', 'commands/exec.c', 'commands/exec_always.c',