jehanne/sys/src/lib/posix/environment.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);
}
}