aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Seebach <peter.seebach@windriver.com>2016-03-03 14:26:37 -0600
committerPeter Seebach <peter.seebach@windriver.com>2016-03-09 14:28:07 -0600
commit80c6334a2e4204cdc7132216f64d1660eed62fd9 (patch)
tree49fb0375db32d36f1f15f3b12ce36310c936ca0e
parent197230695d54da2804c6ea7d1ffa041a486587e8 (diff)
downloadpseudo-80c6334a2e4204cdc7132216f64d1660eed62fd9.tar.gz
pseudo-80c6334a2e4204cdc7132216f64d1660eed62fd9.tar.bz2
pseudo-80c6334a2e4204cdc7132216f64d1660eed62fd9.zip
make bash stop bullying me
So we had this really strange problem where, sometimes but not always, pseudo would have strange problems on startup, where the pseudo server would end up running under pseudo. And this produced the most fascinating thing, which was: unsetenv("LD_PRELOAD"); assert(getenv("LD_PRELOAD") == NULL); for (int i = 0; environ[i]; ++i) { assert(strncmp(environ[i], "LD_PRELOAD=", 11)); } (pseudocode untested) This would crash on the environ search. Because getenv() was not searching environ. WHAT. So it turns out, *bash overrides getenv, setenv, and so on*. Under those names. Hiding the glibc ones. And this creates horrible problems if you assumed that your code could call those functions and expect them to work. So as a workaround, pseudo now uses dlsym to find getenv, etc., from glibc, and invokes those directly if possible. Also the client now uses unwrapped fork/exec for spawning the server, which cleans up the behavior of that code quite a bit.
-rw-r--r--ChangeLog.txt3
-rw-r--r--ports/common/guts/execv.c1
-rw-r--r--pseudo_client.c18
-rw-r--r--pseudo_client.h8
-rw-r--r--pseudo_util.c46
-rw-r--r--pseudo_wrappers.c18
6 files changed, 66 insertions, 28 deletions
diff --git a/ChangeLog.txt b/ChangeLog.txt
index 5d2e118..ede9969 100644
--- a/ChangeLog.txt
+++ b/ChangeLog.txt
@@ -1,3 +1,6 @@
+2016-03-09:
+ * (seebs) workaround for bash redefining getenv/unsetenv/etc.
+
2016-03-02:
* (seebs) more server launch rework and updates
* (seebs) make dup/dup2 less verbose in client
diff --git a/ports/common/guts/execv.c b/ports/common/guts/execv.c
index 6093e44..c071626 100644
--- a/ports/common/guts/execv.c
+++ b/ports/common/guts/execv.c
@@ -19,6 +19,7 @@
pseudo_setupenv();
if (pseudo_has_unload(NULL)) {
+ /* and here we attach */
pseudo_dropenv();
}
/* if exec() fails, we may end up taking signals unexpectedly...
diff --git a/pseudo_client.c b/pseudo_client.c
index 371a5b8..6a08df3 100644
--- a/pseudo_client.c
+++ b/pseudo_client.c
@@ -105,6 +105,9 @@ gid_t pseudo_egid;
gid_t pseudo_sgid;
gid_t pseudo_fgid;
+int (*pseudo_real_fork)(void) = fork;
+int (*pseudo_real_execv)(const char *, char * const *) = execv;
+
#define PSEUDO_ETC_FILE(filename, realname, flags) pseudo_etc_file(filename, realname, flags, passwd_paths, npasswd_paths)
/* helper function to make a directory, just like mkdir -p.
@@ -939,7 +942,7 @@ client_spawn_server(void) {
FILE *fp;
char * pseudo_pidfile;
- if ((server_pid = fork()) != 0) {
+ if ((server_pid = pseudo_real_fork()) != 0) {
if (server_pid == -1) {
pseudo_diag("couldn't fork server: %s\n", strerror(errno));
return 1;
@@ -1041,14 +1044,13 @@ client_spawn_server(void) {
*/
pseudo_client_logging = 0;
- /* execve will call setupenv, then call dropenv if
- * PSEUDO_UNLOAD is set. We call execve, not execv, due
- * to unsetenv changing the responses given by getenv,
- * but not changing the contents of the variable environ,
- * in some cases.
+ /* manual setup of environment, so we can call real-execv
+ * instead of the wrapper.
*/
- pseudo_set_value("PSEUDO_UNLOAD", "1");
- execve(argv[0], argv, environ);
+ pseudo_set_value("PSEUDO_UNLOAD", "YES");
+ pseudo_setupenv();
+ pseudo_dropenv();
+ pseudo_real_execv(argv[0], argv);
pseudo_diag("critical failure: exec of pseudo daemon failed: %s\n", strerror(errno));
exit(1);
}
diff --git a/pseudo_client.h b/pseudo_client.h
index e732bae..68e5160 100644
--- a/pseudo_client.h
+++ b/pseudo_client.h
@@ -58,6 +58,14 @@ extern int pseudo_pwd_lck_close(void);
extern FILE *pseudo_pwd;
extern FILE *pseudo_grp;
+/* pseudo_wrappers will try to initialize these */
+extern int (*pseudo_real_lstat)(const char *path, PSEUDO_STATBUF *buf);
+extern int (*pseudo_real_unsetenv)(const char *);
+extern char * (*pseudo_real_getenv)(const char *);
+extern int (*pseudo_real_setenv)(const char *, const char *, int);
+extern int (*pseudo_real_fork)(void);
+extern int (*pseudo_real_execv)(const char *, char * const *);
+
/* support related to chroot/getcwd/etc. */
extern int pseudo_client_getcwd(void);
extern int pseudo_client_chroot(const char *);
diff --git a/pseudo_util.c b/pseudo_util.c
index be07951..0c156cf 100644
--- a/pseudo_util.c
+++ b/pseudo_util.c
@@ -81,6 +81,16 @@ typedef struct {
char *data;
} pseudo_evlog_entry;
+/* so bash overrides getenv/unsetenv/etcetera, preventing them from
+ * actually modifying environ, so we have pseudo_wrappers try to dlsym
+ * the right values. This could fail, in which case we'd get null
+ * pointers, and we'll just call whatever the linker gives us and
+ * hope for the best.
+ */
+#define SETENV(x, y, z) (pseudo_real_setenv ? pseudo_real_setenv : setenv)(x, y, z)
+#define GETENV(x) (pseudo_real_getenv ? pseudo_real_getenv : getenv)(x)
+#define UNSETENV(x) (pseudo_real_unsetenv ? pseudo_real_unsetenv : unsetenv)(x)
+
#define PSEUDO_EVLOG_ENTRIES 250
#define PSEUDO_EVLOG_LENGTH 256
static pseudo_evlog_entry event_log[PSEUDO_EVLOG_ENTRIES];
@@ -102,6 +112,10 @@ static int pseudo_util_initted = -1; /* Not yet run */
/* bypass wrapper logic on path computations */
int (*pseudo_real_lstat)(const char *path, PSEUDO_STATBUF *buf) = NULL;
+/* bash workaround */
+int (*pseudo_real_unsetenv)(const char *) = unsetenv;
+char * (*pseudo_real_getenv)(const char *) = getenv;
+int (*pseudo_real_setenv)(const char *, const char *, int) = setenv;
#if 0
static void
@@ -126,7 +140,7 @@ pseudo_has_unload(char * const *envp) {
size_t i = 0;
/* Is it in the caller environment? */
- if (NULL != getenv(unload))
+ if (NULL != GETENV(unload))
return 1;
/* Is it in the environment cache? */
@@ -161,7 +175,7 @@ pseudo_get_value(const char *key) {
/* Check if the environment has it and we don't ...
* if so, something went wrong... so we'll attempt to recover
*/
- if (pseudo_env[i].key && !pseudo_env[i].value && getenv(pseudo_env[i].key))
+ if (pseudo_env[i].key && !pseudo_env[i].value && GETENV(pseudo_env[i].key))
pseudo_init_util();
if (pseudo_env[i].value)
@@ -215,8 +229,8 @@ pseudo_init_util(void) {
pseudo_util_initted = 1;
for (i = 0; pseudo_env[i].key; i++) {
- if (getenv(pseudo_env[i].key))
- pseudo_set_value(pseudo_env[i].key, getenv(pseudo_env[i].key));
+ if (GETENV(pseudo_env[i].key))
+ pseudo_set_value(pseudo_env[i].key, GETENV(pseudo_env[i].key));
}
pseudo_util_initted = 0;
@@ -820,7 +834,7 @@ pseudo_fix_path(const char *base, const char *path, size_t rootlen, size_t basel
* we don't try to fix the library path.
*/
void pseudo_dropenv() {
- char *ld_preload = getenv(PRELINK_LIBRARIES);
+ char *ld_preload = GETENV(PRELINK_LIBRARIES);
if (ld_preload) {
ld_preload = without_libpseudo(ld_preload);
@@ -828,9 +842,9 @@ void pseudo_dropenv() {
pseudo_diag("fatal: can't allocate new %s variable.\n", PRELINK_LIBRARIES);
}
if (ld_preload && strlen(ld_preload)) {
- setenv(PRELINK_LIBRARIES, ld_preload, 1);
+ SETENV(PRELINK_LIBRARIES, ld_preload, 1);
} else {
- unsetenv(PRELINK_LIBRARIES);
+ UNSETENV(PRELINK_LIBRARIES);
}
}
}
@@ -886,14 +900,14 @@ pseudo_setupenv() {
while (pseudo_env[i].key) {
if (pseudo_env[i].value) {
- setenv(pseudo_env[i].key, pseudo_env[i].value, 0);
+ SETENV(pseudo_env[i].key, pseudo_env[i].value, 0);
pseudo_debug(PDBGF_ENV | PDBGF_VERBOSE, "pseudo_env: %s => %s\n",
pseudo_env[i].key, pseudo_env[i].value);
}
i++;
}
- const char *ld_library_path = getenv(PRELINK_PATH);
+ const char *ld_library_path = GETENV(PRELINK_PATH);
char *libdir_path = pseudo_libdir_path(NULL);
if (!ld_library_path) {
size_t len = strlen(libdir_path) + 1 + (strlen(libdir_path) + 2) + 1;
@@ -902,7 +916,7 @@ pseudo_setupenv() {
pseudo_diag("fatal: can't allocate new %s variable.\n", PRELINK_PATH);
}
snprintf(newenv, len, "%s:%s64", libdir_path, libdir_path);
- setenv(PRELINK_PATH, newenv, 1);
+ SETENV(PRELINK_PATH, newenv, 1);
} else if (!strstr(ld_library_path, libdir_path)) {
size_t len = strlen(ld_library_path) + 1 + strlen(libdir_path) + 1 + (strlen(libdir_path) + 2) + 1;
char *newenv = malloc(len);
@@ -910,26 +924,26 @@ pseudo_setupenv() {
pseudo_diag("fatal: can't allocate new %s variable.\n", PRELINK_PATH);
}
snprintf(newenv, len, "%s:%s:%s64", ld_library_path, libdir_path, libdir_path);
- setenv(PRELINK_PATH, newenv, 1);
+ SETENV(PRELINK_PATH, newenv, 1);
} else {
/* nothing to do, ld_library_path exists and contains
* our preferred path */
}
- char *ld_preload = getenv(PRELINK_LIBRARIES);
+ char *ld_preload = GETENV(PRELINK_LIBRARIES);
if (ld_preload) {
ld_preload = with_libpseudo(ld_preload, libdir_path);
if (!ld_preload) {
pseudo_diag("fatal: can't allocate new %s variable.\n", PRELINK_LIBRARIES);
}
- setenv(PRELINK_LIBRARIES, ld_preload, 1);
+ SETENV(PRELINK_LIBRARIES, ld_preload, 1);
free(ld_preload);
} else {
ld_preload = with_libpseudo("", libdir_path);
if (!ld_preload) {
pseudo_diag("fatal: can't allocate new %s variable.\n", PRELINK_LIBRARIES);
}
- setenv(PRELINK_LIBRARIES, ld_preload, 1);
+ SETENV(PRELINK_LIBRARIES, ld_preload, 1);
free(ld_preload);
}
@@ -940,9 +954,9 @@ pseudo_setupenv() {
#if PSEUDO_PORT_DARWIN
- char *force_flat = getenv("DYLD_FORCE_FLAT_NAMESPACE");
+ char *force_flat = GETENV("DYLD_FORCE_FLAT_NAMESPACE");
if (!force_flat) {
- setenv("DYLD_FORCE_FLAT_NAMESPACE", "1", 1);
+ SETENV("DYLD_FORCE_FLAT_NAMESPACE", "1", 1);
}
#endif
}
diff --git a/pseudo_wrappers.c b/pseudo_wrappers.c
index 353a5a4..4a38bef 100644
--- a/pseudo_wrappers.c
+++ b/pseudo_wrappers.c
@@ -86,16 +86,15 @@ extern struct timeval *pseudo_wrapper_time;
#define PROFILE_DONE do {} while(0)
#endif
+/* later, the init code can change these to refer to the real calls and
+ * skip the wrappers.
+ */
#ifdef PSEUDO_XATTRDB
extern ssize_t (*pseudo_real_lgetxattr)(const char *, const char *, void *, size_t);
extern ssize_t (*pseudo_real_fgetxattr)(int, const char *, void *, size_t);
extern int (*pseudo_real_lsetxattr)(const char *, const char *, const void *, size_t, int);
extern int (*pseudo_real_fsetxattr)(int, const char *, const void *, size_t, int);
#endif
-/* later, the init code can change these to refer to the real calls and
- * skip the wrappers.
- */
-extern int (*pseudo_real_lstat)(const char *path, PSEUDO_STATBUF *buf);
static void
_libpseudo_init(void) {
@@ -178,6 +177,17 @@ pseudo_init_wrappers(void) {
pseudo_real_fsetxattr = real_fsetxattr;
#endif
pseudo_real_lstat = base_lstat;
+ /* bash has its own local copies of these which it uses
+ * instead of ours...
+ */
+ pseudo_real_unsetenv = dlsym(RTLD_NEXT, "unsetenv");
+ pseudo_real_getenv = dlsym(RTLD_NEXT, "getenv");
+ pseudo_real_setenv = dlsym(RTLD_NEXT, "setenv");
+ /* and these are used so the client's server spawn can bypass
+ * wrappers.
+ */
+ pseudo_real_fork = dlsym(RTLD_NEXT, "fork");
+ pseudo_real_execv = dlsym(RTLD_NEXT, "execv");
/* Once the wrappers are setup, we can now use open... so
* setup the logfile, if necessary...