aboutsummaryrefslogtreecommitdiff
path: root/lib/env.c
blob: 3e05f4e4a981d66c8b540b5fa19432f36b9aeed3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
// Can't trust libc not to leak enviornment variable memory, so...

#include "toys.h"

// In libc, populated by start code,used by getenv() and exec() and friends.
extern char **environ;

// Returns the number of bytes taken by the environment variables. For use
// when calculating the maximum bytes of environment+argument data that can
// be passed to exec for find(1) and xargs(1).
long environ_bytes()
{
  long bytes = sizeof(char *);
  char **ev;

  for (ev = environ; *ev; ev++) bytes += sizeof(char *) + strlen(*ev) + 1;

  return bytes;
}

// This will clear the inherited environment if called first thing.
// Use this instead of envc so we keep track of what needs to be freed.
void xclearenv(void)
{
  toys.envc = 0;
  *environ = 0;
}

// Frees entries we set earlier. Use with libc getenv but not setenv/putenv.
// if name has an equals and !val, act like putenv (name=val must be malloced!)
// if !val unset name. (Name with = and val is an error)
void xsetenv(char *name, char *val)
{
  unsigned i, len, envc;
  char *new;

  // If we haven't snapshot initial environment state yet, do so now.
  if (!toys.envc) {
    // envc is size +1 so even if env empty it's nonzero after initialization
    while (environ[toys.envc++]);
    memcpy(new = xmalloc(((toys.envc|0xff)+1)*sizeof(char *)),
      environ, toys.envc*sizeof(char *));
    environ = (void *)new;
  }

  new = strchr(name, '=');
  if (new) {
    len = new-name;
    if (val) error_exit("xsetenv %s to %s", name, val);
    new = name;
  } else {
    len = strlen(name);
    if (val) new = xmprintf("%s=%s", name, val);
  }

  envc = toys.envc-1;  // compensate for size +1 above
  for (i = 0; environ[i]; i++) {
    // Drop old entry, freeing as appropriate. Assumes no duplicates.
    if (!memcmp(name, environ[i], len) && environ[i][len]=='=') {
      if (i>=envc) free(environ[i]);
      else {
        // move old entries down, add at end of old data
        toys.envc = envc--;
        for (; new ? i<envc : !!environ[i]; i++) environ[i] = environ[i+1];
        i = envc;
      }
      break;
    }
  }

  if (!new) return;

  // resize and null terminate if expanding
  if (!environ[i]) {
    len = i+1;
    if (!(len&255)) environ = xrealloc(environ, len*sizeof(char *));
    environ[len] = 0;
  }
  environ[i] = new;
}

void xunsetenv(char *name)
{
  if (strchr(name, '=')) error_exit("xunsetenv %s name has =", name);
  xsetenv(name, 0);
}

// reset environment for a user, optionally clearing most of it
void reset_env(struct passwd *p, int clear)
{
  int i;

  if (clear) {
    char *s, *stuff[] = {"TERM", "DISPLAY", "COLORTERM", "XAUTHORITY"};

    for (i=0; i<ARRAY_LEN(stuff); i++)
      stuff[i] = (s = getenv(stuff[i])) ? xmprintf("%s=%s", stuff[i], s) : 0;
    xclearenv();
    for (i=0; i < ARRAY_LEN(stuff); i++) if (stuff[i]) xsetenv(stuff[i], 0);
    if (chdir(p->pw_dir)) {
      perror_msg("chdir %s", p->pw_dir);
      xchdir("/");
    }
  } else {
    char **ev1, **ev2;

    // remove LD_*, IFS, ENV, and BASH_ENV from environment
    for (ev1 = ev2 = environ;;) {
      while (*ev2 && (strstart(ev2, "LD_") || strstart(ev2, "IFS=") ||
        strstart(ev2, "ENV=") || strstart(ev2, "BASH_ENV="))) ev2++;
      if (!(*ev1++ = *ev2++)) break;
    }
  }

  setenv("PATH", _PATH_DEFPATH, 1);
  setenv("HOME", p->pw_dir, 1);
  setenv("SHELL", p->pw_shell, 1);
  setenv("USER", p->pw_name, 1);
  setenv("LOGNAME", p->pw_name, 1);
}