toybox/lib/env.c
<<
>>
Prefs
   1// Can't trust libc not to leak enviornment variable memory, so...
   2
   3#include "toys.h"
   4
   5// In libc, populated by start code, used by getenv() and exec() and friends.
   6extern char **environ;
   7
   8// Returns the number of bytes taken by the environment variables. For use
   9// when calculating the maximum bytes of environment+argument data that can
  10// be passed to exec for find(1) and xargs(1).
  11long environ_bytes()
  12{
  13  long bytes = sizeof(char *);
  14  char **ev;
  15
  16  for (ev = environ; *ev; ev++) bytes += sizeof(char *) + strlen(*ev) + 1;
  17
  18  return bytes;
  19}
  20
  21// This will clear the inherited environment if called first thing.
  22// Use this instead of envc so we keep track of what needs to be freed.
  23void xclearenv(void)
  24{
  25  if (toys.envc) {
  26    int i;
  27
  28    for (i = 0; environ[i]; i++) if (i>=toys.envc) free(environ[i]);
  29  } else environ = xmalloc(256*sizeof(char *));
  30  toys.envc = 1;
  31  *environ = 0;
  32}
  33
  34// Frees entries we set earlier. Use with libc getenv but not setenv/putenv.
  35// if name has an equals and !val, act like putenv (name=val must be malloced!)
  36// if !val unset name. (Name with = and val is an error)
  37// returns pointer to new name=value environment string, NULL if none
  38char *xsetenv(char *name, char *val)
  39{
  40  unsigned i, j = 0, len;
  41  char *new;
  42
  43  // If we haven't snapshot initial environment state yet, do so now.
  44  if (!toys.envc) {
  45
  46    // envc is size +1 so even if env empty it's nonzero after initialization
  47    while (environ[toys.envc++]);
  48    memcpy(new = xmalloc(((toys.envc|31)+1)*sizeof(char *)), environ,
  49      toys.envc*sizeof(char *));
  50    environ = (void *)new;
  51  }
  52
  53  if (!(new = strchr(name, '='))) {
  54    len = strlen(name);
  55    if (val) new = xmprintf("%s=%s", name, val);
  56  } else {
  57    len = new-name;
  58    if (val) error_exit("xsetenv %s to %s", name, val);
  59    new = name;
  60  }
  61
  62  for (i = 0; environ[i]; i++) {
  63    // Drop old entry, freeing as appropriate. Assumes no duplicates.
  64    if (!memcmp(name, environ[i], len) && environ[i][len]=='=') {
  65      if (i<toys.envc-1) toys.envc--;
  66      else free(environ[i]);
  67      j++;
  68    }
  69
  70    // move data down to fill hole, including null terminator
  71    if (j && !(environ[i] = environ[i+1])) break;
  72  }
  73
  74  if (!new) return 0;
  75
  76  // resize and null terminate if expanding
  77  if (!j && !environ[i]) {
  78    len = i+1;
  79    if (!(len&31)) environ = xrealloc(environ, (len+32)*sizeof(char *));
  80    environ[len] = 0;
  81  }
  82
  83  return environ[i] = new;
  84}
  85
  86void xunsetenv(char *name)
  87{
  88  if (strchr(name, '=')) error_exit("xunsetenv %s name has =", name);
  89  xsetenv(name, 0);
  90}
  91
  92// remove entry and return pointer instead of freeing
  93char *xpop_env(char *name)
  94{
  95  int len, i;
  96  char *s = 0;
  97
  98  for (len = 0; name[len] && name[len]!='='; len++);
  99  for (i = 0; environ[i]; i++) {
 100    if (!s && !strncmp(name, environ[i], len) && environ[i][len] == '=') {
 101      s = environ[i];
 102      if (toys.envc-1>i) {
 103        s = xstrdup(s);
 104        toys.envc--;
 105      }
 106    }
 107    if (s) environ[i] = environ[i+1];
 108  }
 109
 110  return s;
 111}
 112
 113// reset environment for a user, optionally clearing most of it
 114void reset_env(struct passwd *p, int clear)
 115{
 116  int i;
 117
 118  if (clear) {
 119    char *s, *stuff[] = {"TERM", "DISPLAY", "COLORTERM", "XAUTHORITY"};
 120
 121    for (i=0; i<ARRAY_LEN(stuff); i++)
 122      stuff[i] = (s = getenv(stuff[i])) ? xmprintf("%s=%s", stuff[i], s) : 0;
 123    xclearenv();
 124    for (i=0; i < ARRAY_LEN(stuff); i++) if (stuff[i]) xsetenv(stuff[i], 0);
 125    if (chdir(p->pw_dir)) {
 126      perror_msg("chdir %s", p->pw_dir);
 127      xchdir("/");
 128    }
 129  } else {
 130    char **ev1, **ev2;
 131
 132    // remove LD_*, IFS, ENV, and BASH_ENV from environment
 133    for (ev1 = ev2 = environ;;) {
 134      while (*ev2 && (strstart(ev2, "LD_") || strstart(ev2, "IFS=") ||
 135        strstart(ev2, "ENV=") || strstart(ev2, "BASH_ENV="))) ev2++;
 136      if (!(*ev1++ = *ev2++)) break;
 137    }
 138  }
 139
 140  setenv("PATH", _PATH_DEFPATH, 1);
 141  setenv("HOME", p->pw_dir, 1);
 142  setenv("SHELL", p->pw_shell, 1);
 143  setenv("USER", p->pw_name, 1);
 144  setenv("LOGNAME", p->pw_name, 1);
 145}
 146