248 lines
4.8 KiB
C
248 lines
4.8 KiB
C
/*
|
|
* This file is part of Jehanne.
|
|
*
|
|
* Copyright (C) 2017 Giacomo Tesio <giacomo@tesio.it>
|
|
*
|
|
* This is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as
|
|
* published by the Free Software Foundation, version 3 of the License.
|
|
*
|
|
* Jehanne is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include <u.h>
|
|
#include <lib9.h>
|
|
#include <envvars.h>
|
|
#include <posix.h>
|
|
#include "internal.h"
|
|
|
|
static char* __env[1] = { nil };
|
|
char **environ = &__env[0]; /* how to map this to #e? */
|
|
|
|
/* In POSIX, getenv returns strings that must not be freed by the caller.
|
|
* In Jehanne (and Plan9) getenv returns strings obtained with malloc().
|
|
*
|
|
* Thus we need a more complex managememnt to avoid that calling
|
|
* getenv("PATH") several times would leak memory.
|
|
*
|
|
* We use a sorted linked list of outputs returned by Jehanne's getenv
|
|
* and search it before actually calling it.
|
|
*/
|
|
|
|
#define MAX_ENVNAME_LEN (127+5+1)
|
|
|
|
typedef struct EnvVar EnvVar;
|
|
struct EnvVar
|
|
{
|
|
char* name;
|
|
char* value;
|
|
EnvVar* next;
|
|
};
|
|
|
|
static RWLock list_lock;
|
|
static EnvVar* list_start;
|
|
|
|
static void
|
|
free_env(EnvVar* e)
|
|
{
|
|
free(e->name);
|
|
free(e->value);
|
|
free(e);
|
|
}
|
|
|
|
static void
|
|
put_in_list(EnvVar* newenv)
|
|
{
|
|
int cmp;
|
|
EnvVar *e, **o;
|
|
|
|
wlock(&list_lock);
|
|
|
|
e = list_start;
|
|
o = &list_start;
|
|
while(e != nil){
|
|
cmp = strcmp(e->name, newenv->name);
|
|
if(cmp < 0){
|
|
// check next (alphabetically sorted)
|
|
o = &e->next;
|
|
e = e->next;
|
|
} else {
|
|
if(cmp > 0){
|
|
// reached the next variable
|
|
newenv->next = e;
|
|
} else {
|
|
// found the variable to replace
|
|
newenv->next = e->next;
|
|
free_env(e);
|
|
}
|
|
e = nil; // quit the lookup
|
|
}
|
|
}
|
|
|
|
*o = newenv;
|
|
|
|
wunlock(&list_lock);
|
|
}
|
|
|
|
static int
|
|
write_env_file(const char *name, const char *value, int size)
|
|
{
|
|
int f;
|
|
char buf[MAX_ENVNAME_LEN];
|
|
|
|
snprint(buf, sizeof(buf), "/env/%s", name);
|
|
f = ocreate(buf, OWRITE, 664);
|
|
if(f < 0)
|
|
return 0;
|
|
if(write(f, value, size) < 0){
|
|
close(f);
|
|
return 0;
|
|
}
|
|
close(f);
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
POSIX_setenv(int *errnop, const char *name, const char *value, int overwrite)
|
|
{
|
|
EnvVar* e;
|
|
if(name == nil || name[0] == 0 || strchr(name, '=') != nil){
|
|
*errnop = PosixEINVAL;
|
|
return -1;
|
|
}
|
|
if(!overwrite && POSIX_getenv(errnop, name) != nil)
|
|
return 0;
|
|
if(strlen(name) > 127){
|
|
*errnop = PosixENOMEM;
|
|
return -1;
|
|
}
|
|
e = malloc(sizeof(EnvVar));
|
|
e->next = nil;
|
|
e->value = strdup(value); // see free_env
|
|
e->name = strdup(name);
|
|
if(!write_env_file(name, e->value, strlen(value) + 1)){
|
|
free_env(e);
|
|
*errnop = PosixENOMEM;
|
|
return -1;
|
|
}
|
|
put_in_list(e);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
POSIX_unsetenv(int *errnop, const char *name)
|
|
{
|
|
EnvVar* e, **o;
|
|
char buf[MAX_ENVNAME_LEN];
|
|
if(name == nil || name[0] == 0 || strchr(name, '=') != nil){
|
|
*errnop = PosixEINVAL;
|
|
return -1;
|
|
}
|
|
if(POSIX_getenv(errnop, name) == nil)
|
|
return 0;
|
|
e = malloc(sizeof(EnvVar));
|
|
|
|
wlock(&list_lock);
|
|
|
|
e = list_start;
|
|
o = &list_start;
|
|
while(e != nil){
|
|
if(strcmp(e->name, name) == 0){
|
|
*o = e->next;
|
|
free(e);
|
|
e = nil; // done
|
|
} else {
|
|
o = &e->next;
|
|
e = *o;
|
|
}
|
|
}
|
|
|
|
wunlock(&list_lock);
|
|
|
|
snprint(buf, sizeof(buf), "/env/%s", name);
|
|
remove(buf);
|
|
return 0;
|
|
}
|
|
|
|
char *
|
|
POSIX_getenv(int *errnop, const char *name)
|
|
{
|
|
EnvVar* e;
|
|
if(name == nil || name[0] == 0 || strchr(name, '=') != nil){
|
|
*errnop = PosixEINVAL;
|
|
return nil;
|
|
}
|
|
if(strlen(name) > 127){
|
|
*errnop = PosixEINVAL;
|
|
return nil;
|
|
}
|
|
|
|
rlock(&list_lock);
|
|
e = list_start;
|
|
while(e != nil){
|
|
if(strcmp(name, e->name) == 0)
|
|
break;
|
|
e = e->next;
|
|
}
|
|
runlock(&list_lock);
|
|
if(e != nil)
|
|
return e->value;
|
|
|
|
e = malloc(sizeof(EnvVar));
|
|
if(e == nil){
|
|
*errnop = PosixEINVAL;
|
|
return nil;
|
|
}
|
|
e->next = nil;
|
|
e->value = nil; // see free_env
|
|
e->name = strdup(name);
|
|
if(e->name == nil){
|
|
free_env(e);
|
|
*errnop = PosixEINVAL;
|
|
return nil;
|
|
}
|
|
e->value = getenv(name);
|
|
if(e->value == nil){
|
|
free_env(e);
|
|
return nil;
|
|
}
|
|
|
|
put_in_list(e);
|
|
return e->value;
|
|
}
|
|
|
|
void
|
|
__libposix_setup_exec_environment(char * const *env)
|
|
{
|
|
int fd, len;
|
|
char **e, *start, *end;
|
|
char ename[MAX_ENVNAME_LEN];
|
|
|
|
if(env == nil || env[0] == nil)
|
|
return;
|
|
strcpy(ename, "#e/");
|
|
for(e = (char **)env; (start = *e); e++) {
|
|
end = strchr(start, '=');
|
|
if(!end || start==end)
|
|
continue; /* not in "var=val" format */
|
|
len = end-start;
|
|
if(len > 127)
|
|
len = 127;
|
|
memcpy(ename+3, start, len);
|
|
ename[3+len] = 0;
|
|
fd = ocreate(ename, OWRITE, 0666);
|
|
if(fd < 0)
|
|
continue;
|
|
end++; /* after '=' */
|
|
len = strlen(end);
|
|
sys_pwrite(fd, end, len, -1);
|
|
sys_close(fd);
|
|
}
|
|
}
|
|
|