Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add cmd_env and cmd_env_unset to set and unset environment variables at config and runtime #8327

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
28 changes: 28 additions & 0 deletions include/env.h
Original file line number Diff line number Diff line change
@@ -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
2 changes: 2 additions & 0 deletions include/sway/commands.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions sway/commands.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
Expand Down
28 changes: 28 additions & 0 deletions sway/commands/env.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#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);
}
4 changes: 3 additions & 1 deletion sway/commands/exec_always.c
Original file line number Diff line number Diff line change
Expand Up @@ -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))) {
Expand Down Expand Up @@ -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);
}
Expand Down
125 changes: 125 additions & 0 deletions sway/env.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <string.h>

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;
}
5 changes: 5 additions & 0 deletions sway/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -381,6 +385,7 @@ int main(int argc, char **argv) {

free(config_path);
free_config(config);
g_strfreev(child_envp);

pango_cairo_font_map_set_default(NULL);

Expand Down
2 changes: 2 additions & 0 deletions sway/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ sway_sources = files(
'config.c',
'criteria.c',
'decoration.c',
'env.c',
'ipc-json.c',
'ipc-server.c',
'lock.c',
Expand Down Expand Up @@ -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',
Expand Down