• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
6 extern 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).
environ_bytes(void)11 long environ_bytes(void)
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.
xclearenv(void)23 void 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
xsetenv(char * name,char * val)38 char *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 
xunsetenv(char * name)86 void 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
xpop_env(char * name)93 char *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
reset_env(struct passwd * p,int clear)114 void 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