Commit 623e7de1 authored by Brad Fitzpatrick's avatar Brad Fitzpatrick

os: make Setenv update C environment variables

Fixes #1569

R=rsc, bradfitzwork
CC=golang-dev
https://golang.org/cl/4456045
parent f985638b
...@@ -10,6 +10,7 @@ CGOFILES=\ ...@@ -10,6 +10,7 @@ CGOFILES=\
align.go\ align.go\
basic.go\ basic.go\
callback.go\ callback.go\
env.go\
issue1222.go\ issue1222.go\
issue1328.go\ issue1328.go\
issue1560.go\ issue1560.go\
......
...@@ -25,3 +25,4 @@ func TestZeroArgCallback(t *testing.T) { testZeroArgCallback(t) } ...@@ -25,3 +25,4 @@ func TestZeroArgCallback(t *testing.T) { testZeroArgCallback(t) }
func TestBlocking(t *testing.T) { testBlocking(t) } func TestBlocking(t *testing.T) { testBlocking(t) }
func Test1328(t *testing.T) { test1328(t) } func Test1328(t *testing.T) { test1328(t) }
func TestParallelSleep(t *testing.T) { testParallelSleep(t) } func TestParallelSleep(t *testing.T) { testParallelSleep(t) }
func TestSetEnv(t *testing.T) { testSetEnv(t) }
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package cgotest
/*
#include <stdlib.h>
*/
import "C"
import (
"os"
"testing"
"unsafe"
)
// This is really an os package test but here for convenience.
func testSetEnv(t *testing.T) {
const key = "CGO_OS_TEST_KEY"
const val = "CGO_OS_TEST_VALUE"
os.Setenv(key, val)
keyc := C.CString(key)
defer C.free(unsafe.Pointer(keyc))
v := C.getenv(keyc)
if v == (*C.char)(unsafe.Pointer(uintptr(0))) {
t.Fatal("getenv returned NULL")
}
vs := C.GoString(v)
if vs != val {
t.Fatalf("getenv() = %q; want %q", vs, val)
}
}
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
package os package os
func setenv_c(k, v string)
// Expand replaces ${var} or $var in the string based on the mapping function. // Expand replaces ${var} or $var in the string based on the mapping function.
// Invocations of undefined variables are replaced with the empty string. // Invocations of undefined variables are replaced with the empty string.
func Expand(s string, mapping func(string) string) string { func Expand(s string, mapping func(string) string) string {
......
...@@ -29,6 +29,8 @@ func copyenv() { ...@@ -29,6 +29,8 @@ func copyenv() {
} }
} }
var envLock sync.RWMutex
// Getenverror retrieves the value of the environment variable named by the key. // Getenverror retrieves the value of the environment variable named by the key.
// It returns the value and an error, if any. // It returns the value and an error, if any.
func Getenverror(key string) (value string, err Error) { func Getenverror(key string) (value string, err Error) {
...@@ -37,6 +39,10 @@ func Getenverror(key string) (value string, err Error) { ...@@ -37,6 +39,10 @@ func Getenverror(key string) (value string, err Error) {
if len(key) == 0 { if len(key) == 0 {
return "", EINVAL return "", EINVAL
} }
envLock.RLock()
defer envLock.RUnlock()
v, ok := env[key] v, ok := env[key]
if !ok { if !ok {
return "", ENOENV return "", ENOENV
...@@ -55,24 +61,36 @@ func Getenv(key string) string { ...@@ -55,24 +61,36 @@ func Getenv(key string) string {
// It returns an Error, if any. // It returns an Error, if any.
func Setenv(key, value string) Error { func Setenv(key, value string) Error {
once.Do(copyenv) once.Do(copyenv)
if len(key) == 0 { if len(key) == 0 {
return EINVAL return EINVAL
} }
envLock.Lock()
defer envLock.Unlock()
env[key] = value env[key] = value
setenv_c(key, value) // is a no-op if cgo isn't loaded
return nil return nil
} }
// Clearenv deletes all environment variables. // Clearenv deletes all environment variables.
func Clearenv() { func Clearenv() {
once.Do(copyenv) // prevent copyenv in Getenv/Setenv once.Do(copyenv) // prevent copyenv in Getenv/Setenv
envLock.Lock()
defer envLock.Unlock()
env = make(map[string]string) env = make(map[string]string)
// TODO(bradfitz): pass through to C
} }
// Environ returns an array of strings representing the environment, // Environ returns an array of strings representing the environment,
// in the form "key=value". // in the form "key=value".
func Environ() []string { func Environ() []string {
once.Do(copyenv) once.Do(copyenv)
envLock.RLock()
defer envLock.RUnlock()
a := make([]string, len(env)) a := make([]string, len(env))
i := 0 i := 0
for k, v := range env { for k, v := range env {
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
#include "libcgo.h" #include "libcgo.h"
#include <stdlib.h>
/* Stub for calling malloc from Go */ /* Stub for calling malloc from Go */
static void static void
x_cgo_malloc(void *p) x_cgo_malloc(void *p)
...@@ -49,3 +51,12 @@ xlibcgo_thread_start(ThreadStart *arg) ...@@ -49,3 +51,12 @@ xlibcgo_thread_start(ThreadStart *arg)
} }
void (*libcgo_thread_start)(ThreadStart*) = xlibcgo_thread_start; void (*libcgo_thread_start)(ThreadStart*) = xlibcgo_thread_start;
/* Stub for calling setenv */
static void
xlibcgo_setenv(char **arg)
{
setenv(arg[0], arg[1], 1);
}
void (*libcgo_setenv)(char**) = xlibcgo_setenv;
...@@ -1343,3 +1343,26 @@ runtime·setcpuprofilerate(void (*fn)(uintptr*, int32), int32 hz) ...@@ -1343,3 +1343,26 @@ runtime·setcpuprofilerate(void (*fn)(uintptr*, int32), int32 hz)
if(hz != 0) if(hz != 0)
runtime·resetcpuprofiler(hz); runtime·resetcpuprofiler(hz);
} }
void (*libcgo_setenv)(byte**);
void
os·setenv_c(String k, String v)
{
byte *arg[2];
if(libcgo_setenv == nil)
return;
arg[0] = runtime·malloc(k.len + 1);
runtime·mcpy(arg[0], k.str, k.len);
arg[0][k.len] = 0;
arg[1] = runtime·malloc(v.len + 1);
runtime·mcpy(arg[1], v.str, v.len);
arg[1][v.len] = 0;
runtime·asmcgocall(libcgo_setenv, arg);
runtime·free(arg[0]);
runtime·free(arg[1]);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment