1 /* The customized getenv() function itself. */
3 /* We keep a cached environment for the latest client, with only
4 * the interesting variables picked out. */
5 /* FIXME: We should keep the old clients' environments as well,
6 * in case the app uses stale getenv() pointers! */
16 #include "screenenv.h"
18 /* Just add more if you need. */
19 const static char *grabenv
[] = {
21 // "SSH_AUTH_SOCK", - enable if you use ssh agent forwarding rigoriously
30 /* getenv() upcall setup. */
32 static char *(*up_getenv
)(const char *);
33 static int in_screen
= 0;
35 static void __attribute__((constructor
))
38 /* XXX: We do this here instead of lazily on first call, since
39 * there is a possibility of dlsym()-malloc() race when called
40 * within a getenv() call. */
41 up_getenv
= dlsym(RTLD_NEXT
, "getenv");
43 char *term
= up_getenv("TERM");
44 in_screen
= term
&& !strcmp(term
, "screen");
48 /* The cached environment. */
52 char *vars
[sizeof(grabenv
) / sizeof(*grabenv
)];
56 static struct env env0
;
59 /* Parse environment in /proc/.../environ and save it in *env. */
61 int loadenv(pid_t pid
, struct env
*env
)
65 /* XXX: All the code below is utterly stupid since we should've just
66 * mmap()'d the file and parse it in place. */
69 snprintf(envname
, 128, "/proc/%d/environ", pid
);
70 int fd
= open(envname
, O_RDONLY
);
71 if (fd
< 0) return -1;
73 char *envp
= env
->buf_
;
75 char buf
[8192], *bufp
;
78 /* What do we do in this read() iteration; we keep enough context
79 * necessary in the buffer from the previous iteration; if the
80 * variable is not interesting or is too long for our taste, we
81 * just skip to next one. */
82 enum { VARNAME_LOAD
, VALUE_LOAD
, VAR_SKIP
} state
= VARNAME_LOAD
;
88 l
= read(fd
, buf
+ bl
, sizeof(buf
) - bl
);
101 tok
= memchr(bufp
, '=', bl
);
107 memmove(buf
, bufp
, bl
);
112 for (var
= 0; grabenv
[var
]; var
++)
113 if (!strcmp(bufp
, grabenv
[var
]))
115 bl
-= tok
- bufp
; bufp
= tok
;
123 tok
= memchr(bufp
, 0, bl
);
129 bl
-= tok
- bufp
; bufp
= tok
;
130 state
= VARNAME_LOAD
;
133 tok
= memchr(bufp
, 0, bl
);
139 memmove(buf
, bufp
, bl
);
144 int vallen
= tok
- bufp
;
145 if (sizeof(env
->buf_
) - (envp
- env
->buf_
) < vallen
) {
146 /* Environment space full. */
150 memcpy(envp
, bufp
, vallen
);
151 // printf("[%d] `%s'\n", var, envp);
152 env
->vars
[var
] = envp
; envp
+= vallen
;
153 bl
-= tok
- bufp
; bufp
= tok
;
154 state
= VARNAME_LOAD
;
165 /* The getenv() wrapper itself! */
167 __attribute__((visibility("default"))) char *
168 getenv(const char *env
)
170 /* We may be called by a crazy
171 * up_getenv_setup() -> dlsym() -> calloc() -> getenv()
173 if (__builtin_expect(up_getenv
== NULL
, 0))
176 /* The fast way out, not to hog non-screen processes. */
177 if (__builtin_expect(!in_screen
, 1))
178 return up_getenv(env
);
180 /* TERM=screen - so probably we will get client pid? */
181 pid_t pid
= get_client_pid(getpid());
182 if (__builtin_expect(pid
<= 0, 0))
183 return up_getenv(env
);
185 /* Do we look for a hijacked variable? */
187 for (var
= 0; grabenv
[var
]; var
++)
188 if (__builtin_expect(!strcmp(env
, grabenv
[var
]), 0))
190 if (__builtin_expect(!grabenv
[var
], 1))
191 return up_getenv(env
);
193 /* Should we reload the environment? */
194 if (env0
.pid
!= pid
) {
195 memset(&env0
, 0, sizeof(env0
));
196 if (loadenv(pid
, &env0
) < 0)
197 return up_getenv(env
);
200 return env0
.vars
[var
];
204 void putsn(char *str
)
206 puts(str
? str
: "(null)");
211 putsn(getenv("USER"));
212 putsn(getenv("DISPLAY"));
213 putsn(getenv("TERM"));
214 putsn(getenv("XDG_SESSION_COOKIE"));