ipn/ipnserver: provide means of setting debug env vars on Windows
Fixes
"os/exec"
"os/signal"
"os/user"
+ "path/filepath"
"runtime"
"strconv"
"strings"
@@ -915,6 +916,14 @@ func BabysitProc(ctx context.Context, args []string, logf logger.Logf) {
startTime := time.Now()
log.Printf("exec: %#v %v", executable, args)
cmd := exec.Command(executable, args...)
+ if runtime.GOOS == "windows" {
+ extraEnv, err := loadExtraEnv()
+ if err != nil {
+ logf("errors loading extra env file; ignoring: %v", err)
+ } else {
+ cmd.Env = append(os.Environ(), extraEnv...)
+ }
+ }
// Create a pipe object to use as the subproc's stdin.
// When the writer goes away, the reader gets EOF.
@@ -1184,3 +1193,47 @@ func findTrueNASTaildropDir(name string) (dir string, err error) {
}
return "", fmt.Errorf("shared folder %q not found", name)
}
+
+func loadExtraEnv() (env []string, err error) {
+ if runtime.GOOS != "windows" {
+ return nil, nil
+ }
+ name := filepath.Join(os.Getenv("ProgramData"), "Tailscale", "tailscaled-env.txt")
+ contents, err := os.ReadFile(name)
+ if os.IsNotExist(err) {
+ return nil, nil
+ }
+ if err != nil {
+ return nil, err
+ }
+ for _, line := range strings.Split(string(contents), "\n") {
+ line = strings.TrimSpace(line)
+ if line == "" || line[0] == '#' {
+ continue
+ }
+ k, v, ok := stringsCut(line, "=")
+ if !ok || k == "" {
+ continue
+ }
+ if strings.HasPrefix(v, `"`) {
+ var err error
+ v, err = strconv.Unquote(v)
+ if err != nil {
+ return nil, fmt.Errorf("invalid value in line %q: %v", line, err)
+ }
+ env = append(env, k+"="+v)
+ } else {
+ env = append(env, line)
+ }
+ }
+ return env, nil
+}
+
+// stringsCut is Go 1.18's strings.Cut.
+// TODO(bradfitz): delete this when we depend on Go 1.18.
+func stringsCut(s, sep string) (before, after string, found bool) {
+ if i := strings.Index(s, sep); i >= 0 {
+ return s[:i], s[i+len(sep):], true
+ }
+ return s, "", false
+}
