aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog.txt3
-rw-r--r--Makefile.in2
-rw-r--r--guts/execv.c8
-rw-r--r--guts/execve.c11
-rw-r--r--guts/execvp.c8
-rw-r--r--pseudo.c53
-rw-r--r--pseudo.h12
-rw-r--r--pseudo_client.c44
-rw-r--r--pseudo_db.h1
-rw-r--r--pseudo_util.c559
-rw-r--r--pseudo_wrappers.c21
-rw-r--r--pseudolog.c2
-rwxr-xr-xtest/test-env_i.sh11
13 files changed, 455 insertions, 280 deletions
diff --git a/ChangeLog.txt b/ChangeLog.txt
index 044afcb..dc6ba14 100644
--- a/ChangeLog.txt
+++ b/ChangeLog.txt
@@ -1,6 +1,9 @@
2010-08-10:
* (mhatle) add execl, execle, execlp, execv, and execvp wrappers
* (seebs) handle ... for execl, etc.
+ * (mhatle) add a local cache of variables, instead of using environ
+ * (mhatle) rewrite pseudo_setupenv, pseudo_dropenv routines
+ we now support running "/usr/bin/env -i env" in pseudo!
2010-08-06:
* (mhatle) Fix an exec program with an empty environment
diff --git a/Makefile.in b/Makefile.in
index ca282c0..b9163b3 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -59,7 +59,7 @@ LIBPSEUDO=$(LIB)/libpseudo$(SUFFIX).so
all: $(LIBPSEUDO) $(PSEUDO) $(PSEUDODB) $(PSEUDOLOG)
test: all $(BIN) $(LIB) $(LOCALSTATE)
- @./run_tests.sh
+ @./run_tests.sh -v
install-lib: $(LIBPSEUDO)
mkdir -p $(DESTDIR)$(LIBDIR)
diff --git a/guts/execv.c b/guts/execv.c
index 3fec52a..59a3f96 100644
--- a/guts/execv.c
+++ b/guts/execv.c
@@ -6,7 +6,13 @@
* wrap_execv(const char *path, char *const *argv) {
* int rc = -1;
*/
- environ = pseudo_setupenv(environ, getenv("PSEUDO_OPTS"));
+
+ if (!pseudo_get_value("PSEUDO_RELOADED"))
+ pseudo_setupenv();
+ else {
+ pseudo_setupenv();
+ pseudo_dropenv();
+ }
rc = real_execv(path, argv);
diff --git a/guts/execve.c b/guts/execve.c
index 2bad0bf..7cb6632 100644
--- a/guts/execve.c
+++ b/guts/execve.c
@@ -14,10 +14,13 @@
* design will likely be revisited.
*/
pseudo_client_op(OP_EXEC, PSA_EXEC, -1, -1, filename, 0);
- if (!getenv("PSEUDO_RELOADED"))
- new_environ = pseudo_setupenv(envp, getenv("PSEUDO_OPTS"));
- else
- new_environ = envp;
+ if (!pseudo_get_value("PSEUDO_RELOADED"))
+ new_environ = pseudo_setupenvp(envp);
+ else {
+ new_environ = pseudo_setupenvp(envp);
+ new_environ = pseudo_dropenvp(new_environ);
+ }
+
rc = real_execve(filename, argv, new_environ);
/* return rc;
diff --git a/guts/execvp.c b/guts/execvp.c
index 64e4b4b..1ab34d3 100644
--- a/guts/execvp.c
+++ b/guts/execvp.c
@@ -6,7 +6,13 @@
* wrap_execvp(const char *file, char *const *argv) {
* int rc = -1;
*/
- environ = pseudo_setupenv(environ, getenv("PSEUDO_OPTS"));
+
+ if (!pseudo_get_value("PSEUDO_RELOADED"))
+ pseudo_setupenv();
+ else {
+ pseudo_setupenv();
+ pseudo_dropenv();
+ }
rc = real_execvp(file, argv);
diff --git a/pseudo.c b/pseudo.c
index f3cd9c6..abaacfa 100644
--- a/pseudo.c
+++ b/pseudo.c
@@ -73,30 +73,32 @@ main(int argc, char *argv[]) {
opts[0] = '\0';
- s = getenv("PSEUDO_DEBUG");
+ s = pseudo_get_value("PSEUDO_DEBUG");
if (s) {
int level = atoi(s);
for (o = 0; o < level; ++o) {
pseudo_debug_verbose();
}
}
+ free(s);
if (ld_env && strstr(ld_env, "libpseudo")) {
- extern char **environ;
- char **new_environ;
-
pseudo_debug(2, "can't run daemon with libpseudo in LD_PRELOAD\n");
- if (getenv("PSEUDO_RELOADED")) {
+ s = pseudo_get_value("PSEUDO_RELOADED");
+ if (s) {
pseudo_diag("I can't seem to make LD_PRELOAD go away. Sorry.\n");
pseudo_diag("LD_PRELOAD: %s\n", ld_env);
exit(EXIT_FAILURE);
}
- setenv("PSEUDO_RELOADED", "YES", 1);
- new_environ = pseudo_dropenv(environ);
- execve(argv[0], argv, new_environ);
+ free(s);
+ pseudo_set_value("PSEUDO_RELOADED", "YES");
+ pseudo_setupenv();
+ pseudo_dropenv(); /* Drop LD_PRELOAD */
+
+ execv(argv[0], argv);
exit(EXIT_FAILURE);
}
- unsetenv("PSEUDO_RELOADED");
+ pseudo_set_value("PSEUDO_RELOADED", NULL);
/* we need cwd to canonicalize paths */
pseudo_client_getcwd();
@@ -131,20 +133,20 @@ main(int argc, char *argv[]) {
s = PSEUDO_ROOT_PATH(AT_FDCWD, optarg, AT_SYMLINK_NOFOLLOW);
if (!s)
pseudo_diag("Can't resolve passwd path '%s'\n", optarg);
- setenv("PSEUDO_PASSWD", s, 1);
+ pseudo_set_value("PSEUDO_PASSWD", s);
break;
case 'P':
s = PSEUDO_ROOT_PATH(AT_FDCWD, optarg, AT_SYMLINK_NOFOLLOW);
if (!s)
pseudo_diag("Can't resolve prefix path '%s'\n", optarg);
- setenv("PSEUDO_PREFIX", s, 1);
+ pseudo_set_value("PSEUDO_PREFIX", s);
break;
case 'r': /* FALLTHROUGH */
case 'R':
s = PSEUDO_ROOT_PATH(AT_FDCWD, optarg, AT_SYMLINK_NOFOLLOW);
if (!s)
pseudo_diag("Can't resolve root path '%s'\n", optarg);
- setenv("PSEUDO_CHROOT", s, 1);
+ pseudo_set_value("PSEUDO_CHROOT", s);
if (o == 'r')
opt_r = s;
break;
@@ -178,27 +180,14 @@ main(int argc, char *argv[]) {
break;
}
}
+ /* Options are processed, preserve them... */
+ pseudo_set_value("PSEUDO_OPTS", opts);
if (!pseudo_get_prefix(argv[0])) {
pseudo_diag("Can't figure out prefix. Set PSEUDO_PREFIX or invoke with full path.\n");
exit(EXIT_FAILURE);
}
- if (!pseudo_get_bindir()) {
- pseudo_diag("Can't figure out bindir. Set PSEUDO_BINDIR.\n");
- exit(EXIT_FAILURE);
- }
-
- if (!pseudo_get_libdir()) {
- pseudo_diag("Can't figure out libdir. Set PSEUDO_LIBDIR.\n");
- exit(EXIT_FAILURE);
- }
-
- if (!pseudo_get_localstatedir()) {
- pseudo_diag("Can't figure out localstatedir. Set PSEUDO_LOCALSTATEDIR.\n");
- exit(EXIT_FAILURE);
- }
-
if (opt_C) {
return pseudo_db_check();
}
@@ -220,8 +209,6 @@ main(int argc, char *argv[]) {
} else {
char fullpath[pseudo_path_max()];
char *path;
- extern char **environ;
- char **new_environ;
if (opt_r) {
if (chdir(opt_r) == -1) {
@@ -244,9 +231,7 @@ main(int argc, char *argv[]) {
argv[0] = "/bin/sh";
argv[1] = NULL;
}
- /* build an environment containing libpseudo */
- pseudo_debug(2, "setting up pseudo environment.\n");
- new_environ = pseudo_setupenv(environ, opts);
+
if (strchr(argv[0], '/')) {
snprintf(fullpath, pseudo_path_max(), "%s", argv[0]);
} else {
@@ -274,7 +259,9 @@ main(int argc, char *argv[]) {
exit(EXIT_FAILURE);
}
}
- rc = execve(fullpath, argv, new_environ);
+ pseudo_setupenv();
+
+ rc = execv(fullpath, argv);
if (rc == -1) {
pseudo_diag("pseudo: can't run %s: %s\n",
argv[0], strerror(errno));
diff --git a/pseudo.h b/pseudo.h
index 0564c7b..a6284e5 100644
--- a/pseudo.h
+++ b/pseudo.h
@@ -20,6 +20,12 @@
#include <stdlib.h>
#include <fcntl.h>
+struct pseudo_variables { char * key; size_t key_len; char * value; };
+
+void pseudo_dump_env(char **envp);
+int pseudo_set_value(const char * key, const char * value);
+char * pseudo_get_value(const char * key);
+
typedef enum {
OP_UNKNOWN = -1,
OP_NONE = 0,
@@ -121,8 +127,10 @@ void pseudo_new_pid(void);
/* pseudo_fix_path resolves symlinks up to this depth */
#define PSEUDO_MAX_LINK_RECURSION 16
extern char *pseudo_fix_path(const char *, const char *, size_t, size_t, size_t *, int);
-extern char **pseudo_dropenv(char * const *);
-extern char **pseudo_setupenv(char * const *, char *);
+extern void pseudo_dropenv(void);
+extern char **pseudo_dropenvp(char * const *);
+extern void pseudo_setupenv(void);
+extern char **pseudo_setupenvp(char * const *);
extern char *pseudo_prefix_path(char *);
extern char *pseudo_bindir_path(char *);
extern char *pseudo_libdir_path(char *);
diff --git a/pseudo_client.c b/pseudo_client.c
index e6bfa76..c933329 100644
--- a/pseudo_client.c
+++ b/pseudo_client.c
@@ -173,7 +173,7 @@ pseudo_client_touchuid(void) {
static char uidbuf[256];
snprintf(uidbuf, 256, "%d,%d,%d,%d",
pseudo_ruid, pseudo_euid, pseudo_suid, pseudo_fuid);
- setenv("PSEUDO_UIDS", uidbuf, 1);
+ pseudo_set_value("PSEUDO_UIDS", uidbuf);
}
void
@@ -181,7 +181,7 @@ pseudo_client_touchgid(void) {
static char gidbuf[256];
snprintf(gidbuf, 256, "%d,%d,%d,%d",
pseudo_rgid, pseudo_egid, pseudo_sgid, pseudo_fgid);
- setenv("PSEUDO_GIDS", gidbuf, 1);
+ pseudo_set_value("PSEUDO_GIDS", gidbuf);
}
int
@@ -193,7 +193,7 @@ pseudo_client_chroot(const char *path) {
if (!strcmp(path, "/")) {
pseudo_chroot_len = 0;
pseudo_chroot = 0;
- unsetenv("PSEUDO_CHROOT");
+ pseudo_set_value("PSEUDO_CHROOT", NULL);
return 0;
}
/* allocate new value */
@@ -206,7 +206,7 @@ pseudo_client_chroot(const char *path) {
return -1;
}
memcpy(pseudo_chroot, path, pseudo_chroot_len + 1);
- setenv("PSEUDO_CHROOT", pseudo_chroot, 1);
+ pseudo_set_value("PSEUDO_CHROOT", pseudo_chroot);
return 0;
}
@@ -320,19 +320,21 @@ pseudo_client_reset() {
if (!pseudo_inited) {
char *env;
- env = getenv("PSEUDO_UIDS");
+ env = pseudo_get_value("PSEUDO_UIDS");
if (env)
sscanf(env, "%d,%d,%d,%d",
&pseudo_ruid, &pseudo_euid,
&pseudo_suid, &pseudo_fuid);
+ free(env);
- env = getenv("PSEUDO_GIDS");
+ env = pseudo_get_value("PSEUDO_GIDS");
if (env)
sscanf(env, "%d,%d,%d,%d",
&pseudo_rgid, &pseudo_egid,
&pseudo_sgid, &pseudo_fuid);
+ free(env);
- env = getenv("PSEUDO_CHROOT");
+ env = pseudo_get_value("PSEUDO_CHROOT");
if (env) {
pseudo_chroot = strdup(env);
if (pseudo_chroot) {
@@ -341,11 +343,13 @@ pseudo_client_reset() {
pseudo_diag("can't store chroot path (%s)\n", env);
}
}
+ free(env);
- env = getenv("PSEUDO_PASSWD");
+ env = pseudo_get_value("PSEUDO_PASSWD");
if (env) {
pseudo_passwd = strdup(env);
}
+ free(env);
pseudo_inited = 1;
}
@@ -358,7 +362,6 @@ static int
client_spawn_server(void) {
int status;
FILE *fp;
- extern char **environ;
char * pseudo_pidfile;
if ((server_pid = fork()) != 0) {
@@ -390,15 +393,14 @@ client_spawn_server(void) {
} else {
char *base_args[] = { NULL, NULL, NULL };
char **argv;
- char **new_environ;
+ char *option_string = pseudo_get_value("PSEUDO_OPTS");
int args;
int fd;
pseudo_new_pid();
base_args[0] = pseudo_bindir_path("pseudo");
base_args[1] = "-d";
- if (getenv("PSEUDO_OPTS")) {
- char *option_string = strdup(getenv("PSEUDO_OPTS"));
+ if (option_string) {
char *s;
int arg;
@@ -425,6 +427,7 @@ client_spawn_server(void) {
} else {
argv = base_args;
}
+
/* close any higher-numbered fds which might be open,
* such as sockets. We don't have to worry about 0 and 1;
* the server closes them already, and more importantly,
@@ -440,9 +443,14 @@ client_spawn_server(void) {
close(fd);
}
/* and now, execute the server */
- new_environ = pseudo_dropenv(environ);
- pseudo_debug(4, "calling execve on %s\n", argv[0]);
- execve(argv[0], argv, new_environ);
+
+ pseudo_set_value("PSEUDO_RELOADED", "YES");
+ pseudo_setupenv();
+ pseudo_dropenv(); /* drop LD_PRELOAD */
+
+ pseudo_debug(4, "calling execv on %s\n", argv[0]);
+
+ execv(argv[0], argv);
pseudo_diag("critical failure: exec of pseudo daemon failed: %s\n", strerror(errno));
exit(1);
}
@@ -453,18 +461,19 @@ client_ping(void) {
pseudo_msg_t ping;
pseudo_msg_t *ack;
char tagbuf[pseudo_path_max()];
- char *tag = getenv("PSEUDO_TAG");
+ char *tag = pseudo_get_value("PSEUDO_TAG");
ping.type = PSEUDO_MSG_PING;
ping.op = OP_NONE;
if (!tag)
- tag = "";
+ tag = strdup("");
ping.pathlen = snprintf(tagbuf, sizeof(tagbuf), "%s%c%s",
program_invocation_name ? program_invocation_name : "<unknown>",
0,
tag);
+ free(tag);
ping.client = getpid();
ping.result = 0;
errno = 0;
@@ -570,7 +579,6 @@ pseudo_client_setup(void) {
char * pseudo_pidfile;
FILE *fp;
server_pid = 0;
- int cwd_fd;
/* avoid descriptor leak, I hope */
if (connect_fd >= 0) {
diff --git a/pseudo_db.h b/pseudo_db.h
index 77ff5b5..2e7dbb9 100644
--- a/pseudo_db.h
+++ b/pseudo_db.h
@@ -43,6 +43,7 @@ extern int pdb_unlink_file_dev(pseudo_msg_t *msg);
extern int pdb_update_file(pseudo_msg_t *msg);
extern int pdb_update_file_path(pseudo_msg_t *msg);
extern int pdb_update_inode(pseudo_msg_t *msg);
+extern int pdb_unlink_contents(pseudo_msg_t *msg);
extern int pdb_rename_file(const char *oldpath, pseudo_msg_t *msg);
extern int pdb_find_file_exact(pseudo_msg_t *msg);
extern int pdb_find_file_path(pseudo_msg_t *msg);
diff --git a/pseudo_util.c b/pseudo_util.c
index 889630e..b44f000 100644
--- a/pseudo_util.c
+++ b/pseudo_util.c
@@ -33,6 +33,114 @@
#include "pseudo_ipc.h"
#include "pseudo_db.h"
+/* The order below is not arbitrary, but based on the assumption
+ * of how often things will be used.
+ */
+const struct pseudo_variables pseudo_env[] = {
+ { "PSEUDO_PREFIX", 13, NULL },
+ { "PSEUDO_BINDIR", 13, NULL },
+ { "PSEUDO_LIBDIR", 13, NULL },
+ { "PSEUDO_LOCALSTATEDIR", 20, NULL },
+ { "PSEUDO_PASSWD", 13, NULL },
+ { "PSEUDO_CHROOT", 13, NULL },
+ { "PSEUDO_UIDS", 11, NULL },
+ { "PSEUDO_GIDS", 11, NULL },
+ { "PSEUDO_OPTS", 11, NULL },
+ { "PSEUDO_DEBUG", 12, NULL },
+ { "PSEUDO_DEBUG_FILE", 17, NULL },
+ { "PSEUDO_TAG", 10, NULL },
+ { "PSEUDO_ENOSYS_ABORT", 19, NULL },
+ { "PSEUDO_NOSYMLINKEXP", 19, NULL },
+ { "PSEUDO_RELOADED", 15, NULL },
+ { NULL, 0, NULL } /* Magic terminator */
+};
+
+/* -1 - init hasn't been run yet
+ * 0 - init has been run
+ * 1 - init is running
+ *
+ * There are cases where the constructor is run AFTER the
+ * program starts playing with things, so we need to do our
+ * best to handle that case.
+ */
+int _in_init = -1; /* Not yet run */
+
+static void _libpseudo_init(void) __attribute__ ((constructor));
+
+void dump_env(char **envp) {
+ size_t i = 0;
+ for (i = 0; envp[i]; i++) {
+ pseudo_debug(0,"dump_envp: [%d]%s\n", i,envp[i]);
+ }
+
+ for (i = 0; pseudo_env[i].key; i++) {
+ pseudo_debug(0,"dump_envp: {%d}%s=%s\n", i, pseudo_env[i].key, pseudo_env[i].value);
+ }
+
+ pseudo_debug(0, "dump_envp: _in_init %d\n", _in_init);
+}
+
+/* Caller must free memory! */
+char * pseudo_get_value(const char * key) {
+ size_t i = 0;
+ char * value;
+
+ if (_in_init == -1) _libpseudo_init();
+
+ for (i = 0; pseudo_env[i].key && memcmp(pseudo_env[i].key, key, pseudo_env[i].key_len + 1); i++) ;
+
+ /* 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))
+ _libpseudo_init();
+
+ if (pseudo_env[i].value) value = strdup(pseudo_env[i].value);
+ else value = NULL;
+
+ if (!pseudo_env[i].key)
+ pseudo_diag("Unknown variable %s.\n", key);
+
+ return value;
+}
+
+/* We make a copy, so the original values should be freed. */
+int pseudo_set_value(const char * key, const char * value) {
+ int rc = 0;
+ size_t i = 0;
+
+ if (_in_init == -1) _libpseudo_init();
+
+ for (i = 0; pseudo_env[i].key && memcmp(pseudo_env[i].key, key, pseudo_env[i].key_len + 1); i++) ;
+
+ if (pseudo_env[i].key) {
+ if (pseudo_env[i].value) free(pseudo_env[i].value);
+ if (value)
+ ((struct pseudo_variables *)pseudo_env)[i].value = strdup(value);
+ else
+ ((struct pseudo_variables *)pseudo_env)[i].value = NULL;
+ } else {
+ if (!_in_init) pseudo_diag("Unknown variable %s.\n", key);
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+static void _libpseudo_init(void) {
+ size_t i = 0;
+
+ _in_init = 1;
+
+ for (i = 0; pseudo_env[i].key; i++) {
+ if (pseudo_env[i].key)
+ if (getenv(pseudo_env[i].key))
+ pseudo_set_value(pseudo_env[i].key, getenv(pseudo_env[i].key));
+ }
+
+ _in_init = 0;
+}
+
/* 5 = ridiculous levels of duplication
* 4 = exhaustive detail
* 3 = detailed protocol analysis
@@ -65,7 +173,7 @@ static ssize_t pseudo_sys_max_pathlen = -1;
* the end of the string or a space after it.
*/
static char *libpseudo_name = "libpseudo.so";
-static char *libpseudo_pattern = "(=| )libpseudo[^ ]*\\.so($| )";
+static char *libpseudo_pattern = "(^|=| )libpseudo[^ ]*\\.so($| )";
static regex_t libpseudo_regex;
static int libpseudo_regex_compiled = 0;
@@ -88,9 +196,13 @@ static char *
without_libpseudo(char *list) {
regmatch_t pmatch[1];
int counter = 0;
+ int skip_start = 0;
+
if (libpseudo_regex_init())
return NULL;
+ if (list[0] == '=' || list[0] == ' ')
+ skip_start = 1;
if (regexec(&libpseudo_regex, list, 1, pmatch, 0)) {
return list;
@@ -100,7 +212,7 @@ without_libpseudo(char *list) {
char *start = list + pmatch[0].rm_so;
char *end = list + pmatch[0].rm_eo;
/* don't copy over the space or = */
- ++start;
+ start += skip_start;
memmove(start, end, strlen(end) + 1);
++counter;
if (counter > 5) {
@@ -135,11 +247,19 @@ void
pseudo_debug_terse(void) {
if (max_debug_level > 0)
--max_debug_level;
+
+ char s[16];
+ snprintf(s, 16, "%d", max_debug_level);
+ pseudo_set_value("PSEUDO_DEBUG", s);
}
void
pseudo_debug_verbose(void) {
++max_debug_level;
+
+ char s[16];
+ snprintf(s, 16, "%d", max_debug_level);
+ pseudo_set_value("PSEUDO_DEBUG", s);
}
int
@@ -437,195 +557,221 @@ pseudo_fix_path(const char *base, const char *path, size_t rootlen, size_t basel
}
}
-/* remove the pseudo stuff from the environment (leaving other preloads
+/* remove the libpseudo stuff from the environment (leaving other preloads
* alone).
* There's an implicit memory leak here, but this is called only right
* before an exec(), or at most once in a given run.
*
* we don't try to fix the library path.
*/
+void pseudo_dropenv() {
+ char * ld_preload = getenv("LD_PRELOAD");
+
+ if (ld_preload) {
+ ld_preload = without_libpseudo(ld_preload);
+ if (!ld_preload) {
+ pseudo_diag("fatal: can't allocate new LD_PRELOAD variable.\n");
+ }
+ if (ld_preload && strlen(ld_preload))
+ setenv("LD_PRELOAD", ld_preload, 1);
+ else
+ unsetenv("LD_PRELOAD");
+ }
+}
+
char **
-pseudo_dropenv(char * const *environ) {
- char **new_environ;
- int env_count = 0;
+pseudo_dropenvp(char * const *envp) {
+ char **new_envp;
int i, j;
- for (i = 0; environ[i]; ++i)
- ++env_count;
- new_environ = malloc((env_count + 1) * sizeof(*new_environ));
- if (!new_environ) {
+ for (i = 0; envp[i]; ++i) ;
+
+ new_envp = malloc((i + 1) * sizeof(*new_envp));
+ if (!new_envp) {
pseudo_diag("fatal: can't allocate new environment.\n");
return NULL;
}
+
j = 0;
- for (i = 0; environ[i]; ++i) {
- if (!memcmp(environ[i], "LD_PRELOAD=", 11)) {
- char *new_val = without_libpseudo(environ[i]);
+ for (i = 0; envp[i]; ++i) {
+ if (!memcmp(envp[i], "LD_PRELOAD=", 11)) {
+ char *new_val = without_libpseudo(envp[i]);
if (!new_val) {
pseudo_diag("fatal: can't allocate new environment variable.\n");
return 0;
} else {
/* don't keep an empty value */
- if (strcmp(new_val, "LD_PRELOAD=")) {
- new_environ[j++] = new_val;
+ if (memcmp(new_val, "LD_PRELOAD=", 11)) {
+ new_envp[j++] = new_val;
}
}
} else {
- new_environ[j++] = environ[i];
+ new_envp[j++] = envp[i];
}
}
- new_environ[j++] = NULL;
- return new_environ;
+ new_envp[j++] = NULL;
+ return new_envp;
+}
+
+/* add pseudo stuff to the environment.
+ */
+void
+pseudo_setupenv() {
+ size_t i = 0;
+
+ pseudo_debug(2, "setting up pseudo environment.\n");
+
+ /* Make sure everything has been evaluated */
+ free(pseudo_get_prefix(NULL));
+ free(pseudo_get_bindir());
+ free(pseudo_get_libdir());
+ free(pseudo_get_localstatedir());
+
+ while (pseudo_env[i].key) {
+ if (pseudo_env[i].value)
+ setenv(pseudo_env[i].key, strdup(pseudo_env[i].value), 0);
+ i++;
+ }
+
+ char * ld_preload = getenv("LD_PRELOAD");
+ if (ld_preload) {
+ ld_preload = with_libpseudo(ld_preload);
+ if (!ld_preload) {
+ pseudo_diag("fatal: can't allocate new LD_PRELOAD variable.\n");
+ }
+ setenv("LD_PRELOAD", ld_preload, 1);
+ } else {
+ setenv("LD_PRELOAD", libpseudo_name, 1);
+ }
+
+ const char * ld_library_path = getenv("LD_LIBRARY_PATH");
+ char * libdir_path = pseudo_libdir_path(NULL);
+ if (ld_library_path && !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);
+ if (!newenv) {
+ pseudo_diag("fatal: can't allocate new LD_LIBRARY_PATH variable.\n");
+ }
+ snprintf(newenv, len, "%s:%s:%s64", ld_preload, libdir_path, libdir_path);
+ setenv("LD_LIBRARY_PATH", newenv, 1);
+ } else {
+ size_t len = strlen(libdir_path) + 1 + (strlen(libdir_path) + 2) + 1;
+ char *newenv = malloc(len);
+ if (!newenv) {
+ pseudo_diag("fatal: can't allocate new LD_LIBRARY_PATH variable.\n");
+ }
+ snprintf(newenv, len, "%s:%s64", libdir_path, libdir_path);
+ setenv("LD_LIBRARY_PATH", newenv, 1);
+ }
+ free(libdir_path);
}
/* add pseudo stuff to the environment.
* We can't just use setenv(), because one use case is that we're trying
* to modify the environment of a process about to be forked through
- * exec().
+ * execve().
*/
char **
-pseudo_setupenv(char * const *environ, char *opts) {
- char **new_environ;
- int env_count = 0;
- int found_preload = 0, found_libpath = 0, found_debug = 0, found_opts = 0;
- int found_prefix = 0, found_bindir = 0, found_libdir = 0, found_localstatedir = 0;
- int i, j;
- size_t len;
- char *newenv;
-
- for (i = 0; environ[i]; ++i) {
- if (!memcmp(environ[i], "LD_PRELOAD=", 11))
- found_preload = 1;
- if (!memcmp(environ[i], "PSEUDO_OPTS=", 12))
- found_opts = 1;
- if (!memcmp(environ[i], "PSEUDO_DEBUG=", 13))
- found_debug = 1;
- if (!memcmp(environ[i], "LD_LIBRARY_PATH=", 16))
- found_libpath = 1;
- if (!memcmp(environ[i], "PSEUDO_PREFIX=", 14))
- found_prefix = 1;
- if (!memcmp(environ[i], "PSEUDO_BINDIR=", 14))
- found_bindir = 1;
- if (!memcmp(environ[i], "PSEUDO_LIBDIR=", 14))
- found_libdir = 1;
- if (!memcmp(environ[i], "PSEUDO_LOCALSTATEDIR=", 21))
- found_localstatedir = 1;
+pseudo_setupenvp(char * const *envp) {
+ char **new_envp;
+
+ size_t i, j, k;
+ size_t env_count = 0;
+
+ size_t size_pseudoenv = 0;
+
+ char * ld_preload=NULL, * ld_library_path=NULL;
+
+ pseudo_debug(2, "setting up envp environment.\n");
+
+ /* Make sure everything has been evaluated */
+ free(pseudo_get_prefix(NULL));
+ free(pseudo_get_bindir());
+ free(pseudo_get_libdir());
+ free(pseudo_get_localstatedir());
+
+ for (i = 0; envp[i]; ++i) {
+ if (!memcmp(envp[i], "LD_PRELOAD=", 11)) {
+ ld_preload = envp[i];
+ }
+ if (!memcmp(envp[i], "LD_LIBRARY_PATH=", 11)) {
+ ld_library_path = envp[i];
+ }
++env_count;
}
- env_count += 8 - (found_preload + found_libpath + found_debug + found_opts + found_prefix + found_bindir + found_libdir + found_localstatedir);
- new_environ = malloc((env_count + 1) * sizeof(*new_environ));
- if (!new_environ) {
+ for (i = 0; pseudo_env[i].key; i++) {
+ size_pseudoenv++;
+ }
+
+ env_count += size_pseudoenv; /* We're going to over allocate */
+
+ j = 0;
+ new_envp = malloc((env_count + 1) * sizeof(*new_envp));
+ if (!new_envp) {
pseudo_diag("fatal: can't allocate new environment.\n");
return NULL;
- }
+ }
- j = 0;
- for (i = 0; environ[i]; ++i) {
- if (!memcmp(environ[i], "LD_PRELOAD=", 11)) {
- newenv = with_libpseudo(environ[i]);
- if (!newenv) {
- pseudo_diag("fatal: can't allocate new environment variable.\n");
- return NULL;
- }
- new_environ[j++] = newenv;
- } else if (!memcmp(environ[i], "LD_LIBRARY_PATH=", 16)) {
- if (!strstr(environ[i], pseudo_libdir_path(NULL))) {
- char *e1;
- e1 = pseudo_libdir_path(NULL);
- len = strlen(environ[i]) + strlen(e1) + (strlen(e1) + 2) + 3;
- newenv = malloc(len);
- if (!newenv) {
- pseudo_diag("fatal: can't allocate new environment variable.\n");
- }
- snprintf(newenv, len, "%s:%s:%s64", environ[i], e1, e1);
- free(e1);
- new_environ[j++] = newenv;
- } else {
- new_environ[j++] = environ[i];
- }
- } else {
- new_environ[j++] = environ[i];
+ if (ld_preload) {
+ ld_preload = with_libpseudo(ld_preload);
+ if (!ld_preload) {
+ pseudo_diag("fatal: can't allocate new LD_PRELOAD variable.\n");
}
+ new_envp[j++] = ld_preload;
+ } else {
+ size_t len = strlen("LD_PRELOAD=") + strlen(libpseudo_name) + 1;
+ char *newenv = malloc(len);
+ snprintf(newenv, len, "LD_PRELOAD=%s", libpseudo_name);
+ new_envp[j++] = newenv;
}
- if (!found_libpath) {
- char *e1;
- e1 = pseudo_libdir_path(NULL);
- len = 16 + strlen(e1) + (strlen(e1) + 2) + 2;
- newenv = malloc(len);
- if (!newenv) {
- pseudo_diag("fatal: can't allocate new environment variable.\n");
- }
- snprintf(newenv, len, "LD_LIBRARY_PATH=%s:%s64", e1, e1);
- new_environ[j++] = newenv;
- }
- if (!found_preload) {
- new_environ[j++] = "LD_PRELOAD=libpseudo.so";
- }
- if (!found_debug && max_debug_level > 0) {
- len = 16;
- newenv = malloc(len);
- if (!newenv) {
- pseudo_diag("fatal: can't allocate new environment variable.\n");
- }
- snprintf(newenv, len, "PSEUDO_DEBUG=%d", max_debug_level);
- new_environ[j++] = newenv;
- }
- if (!found_opts && opts) {
- len = 12 + strlen(opts) + 1;
- newenv = malloc(len);
+
+ char * libdir_path = pseudo_libdir_path(NULL);
+ if (ld_library_path && !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);
if (!newenv) {
- pseudo_diag("fatal: can't allocate new environment variable.\n");
+ pseudo_diag("fatal: can't allocate new LD_LIBRARY_PATH variable.\n");
}
- snprintf(newenv, len, "PSEUDO_OPTS=%s", opts);
- new_environ[j++] = newenv;
- }
- if (!found_prefix) {
- char * prefix_path = pseudo_prefix_path(NULL);
- len = 14 + strlen(prefix_path) + 1;
- newenv = malloc(len);
+ snprintf(newenv, len, "%s:%s:%s64", ld_library_path, libdir_path, libdir_path);
+ new_envp[j++] = newenv;
+ } else {
+ size_t len = strlen("LD_LIBRARY_PATH=") + strlen(libdir_path) + 1 + (strlen(libdir_path) + 2) + 1;
+ char *newenv = malloc(len);
if (!newenv) {
- pseudo_diag("fatal: can't allocate new environment variable.\n");
+ pseudo_diag("fatal: can't allocate new LD_LIBRARY_PATH variable.\n");
}
- snprintf(newenv, len, "PSEUDO_PREFIX=%s", prefix_path);
- new_environ[j++] = newenv;
- free(prefix_path);
+ snprintf(newenv, len, "LD_LIBRARY_PATH=%s:%s64", libdir_path, libdir_path);
+ new_envp[j++] = newenv;
}
- if (!found_bindir) {
- char * bindir_path = pseudo_bindir_path(NULL);
- len = 14 + strlen(bindir_path) + 1;
- newenv = malloc(len);
- if (!newenv) {
- pseudo_diag("fatal: can't allocate new environment variable.\n");
- }
- snprintf(newenv, len, "PSEUDO_BINDIR=%s", bindir_path);
- new_environ[j++] = newenv;
- free(bindir_path);
+ free(libdir_path);
+
+ for (i = 0; envp[i]; ++i) {
+ if (!memcmp(envp[i], "LD_PRELOAD=", 11)) continue;
+ if (!memcmp(envp[i], "LD_LIBRARY_PATH=", 16)) continue;
+ new_envp[j++] = envp[i];
}
- if (!found_libdir) {
- char * libdir_path = pseudo_libdir_path(NULL);
- len = 14 + strlen(libdir_path) + 1;
- newenv = malloc(len);
- if (!newenv) {
- pseudo_diag("fatal: can't allocate new environment variable.\n");
+
+ for (i = 0; pseudo_env[i].key; i++) {
+ int found = 0;
+ for (k = 0; k < j; k++) {
+ if (!strncmp(pseudo_env[i].key,new_envp[k],strlen(pseudo_env[i].key))) {
+ found = 1;
+ break;
+ }
}
- snprintf(newenv, len, "PSEUDO_LIBDIR=%s", libdir_path);
- new_environ[j++] = newenv;
- free(libdir_path);
- }
- if (!found_localstatedir) {
- char * localstatedir_path = pseudo_localstatedir_path(NULL);
- len = 21 + strlen(localstatedir_path) + 1;
- newenv = malloc(len);
- if (!newenv) {
- pseudo_diag("fatal: can't allocate new environment variable.\n");
+ if (!found && pseudo_env[i].key && pseudo_env[i].value) {
+ size_t len = strlen(pseudo_env[i].key) + 1 + strlen(pseudo_env[i].value) + 1;
+ char *newenv = malloc(len);
+ if (!newenv) {
+ pseudo_diag("fatal: can't allocate new variable.\n");
+ }
+ snprintf(newenv, len, "%s=%s", pseudo_env[i].key, pseudo_env[i].value);
+ new_envp[j++] = newenv;
}
- snprintf(newenv, len, "PSEUDO_LOCALSTATEDIR=%s", localstatedir_path);
- new_environ[j++] = newenv;
- free(localstatedir_path);
}
- new_environ[j++] = NULL;
- return new_environ;
+ new_envp[j++] = NULL;
+ return new_envp;
}
/* Append the file value to the prefix value. */
@@ -665,80 +811,77 @@ pseudo_append_path(const char * prefix, size_t prefix_len, char *file) {
*/
char *
pseudo_prefix_path(char *file) {
- static char *prefix = NULL;
- static size_t prefix_len;
+ char * rc;
+ char * prefix = pseudo_get_prefix(NULL);
if (!prefix) {
- prefix = getenv("PSEUDO_PREFIX");
- if (!prefix) {
- pseudo_diag("You must set the PSEUDO_PREFIX environment variable to run pseudo.\n");
- exit(1);
- }
- prefix_len = strlen(prefix);
+ pseudo_diag("You must set the PSEUDO_PREFIX environment variable to run pseudo.\n");
+ exit(1);
}
- return pseudo_append_path(prefix, prefix_len, file);
+ rc = pseudo_append_path(prefix, strlen(prefix), file);
+ free(prefix);
+
+ return rc;
}
/* get the full path to a file under $PSEUDO_BINDIR. */
char *
pseudo_bindir_path(char *file) {
- static char *bindir = NULL;
- static size_t bindir_len;
+ char * rc;
+ char * bindir = pseudo_get_bindir(NULL);
if (!bindir) {
- bindir = pseudo_get_bindir();
- if (!bindir) {
- pseudo_diag("You must set the PSEUDO_BINDIR environment variable to run pseudo.\n");
- exit(1);
- }
- bindir_len = strlen(bindir);
+ pseudo_diag("You must set the PSEUDO_BINDIR environment variable to run pseudo.\n");
+ exit(1);
}
- return pseudo_append_path(bindir, bindir_len, file);
+ rc = pseudo_append_path(bindir, strlen(bindir), file);
+ free(bindir);
+
+ return rc;
}
/* get the full path to a file under $PSEUDO_LIBDIR. */
char *
pseudo_libdir_path(char *file) {
- static char *libdir = NULL;
- static size_t libdir_len;
+ char * rc;
+ char * libdir = pseudo_get_libdir(NULL);
if (!libdir) {
- libdir = pseudo_get_libdir();
- if (!libdir) {
- pseudo_diag("You must set the PSEUDO_LIBDIR environment variable to run pseudo.\n");
- exit(1);
- }
- libdir_len = strlen(libdir);
+ pseudo_diag("You must set the PSEUDO_LIBDIR environment variable to run pseudo.\n");
+ exit(1);
}
- return pseudo_append_path(libdir, libdir_len, file);
+ rc = pseudo_append_path(libdir, strlen(libdir), file);
+ free(libdir);
+
+ return rc;
}
/* get the full path to a file under $PSEUDO_LOCALSTATEDIR. */
char *
pseudo_localstatedir_path(char *file) {
- static char *localstatedir = NULL;
- static size_t localstatedir_len;
+ char * rc;
+ char * localstatedir = pseudo_get_localstatedir(NULL);
if (!localstatedir) {
- localstatedir = pseudo_get_localstatedir();
- if (!localstatedir) {
- pseudo_diag("You must set the PSEUDO_LOCALSTATEDIR environment variable to run pseudo.\n");
- exit(1);
- }
- localstatedir_len = strlen(localstatedir);
+ pseudo_diag("You must set the PSEUDO_LOCALSTATEDIR environment variable to run pseudo.\n");
+ exit(1);
}
- return pseudo_append_path(localstatedir, localstatedir_len, file);
+ rc = pseudo_append_path(localstatedir, strlen(localstatedir), file);
+ free(localstatedir);
+
+ return rc;
}
char *
pseudo_get_prefix(char *pathname) {
- char *s;
- s = getenv("PSEUDO_PREFIX");
- if (!s) {
+ char *s = pseudo_get_value("PSEUDO_PREFIX");
+
+ /* Generate the PSEUDO_PREFIX if necessary, and possible... */
+ if (!s && pathname) {
char mypath[pseudo_path_max()];
char *dir;
char *tmp_path;
@@ -783,22 +926,20 @@ pseudo_get_prefix(char *pathname) {
pseudo_diag("Warning: PSEUDO_PREFIX unset, defaulting to %s.\n",
mypath);
- setenv("PSEUDO_PREFIX", mypath, 1);
- s = getenv("PSEUDO_PREFIX");
+ pseudo_set_value("PSEUDO_PREFIX", mypath);
+ s = pseudo_get_value("PSEUDO_PREFIX");
}
return s;
}
char *
pseudo_get_bindir() {
- char *s;
- s = getenv("PSEUDO_BINDIR");
+ char *s = pseudo_get_value("PSEUDO_BINDIR");
if (!s) {
- char *pseudo_bindir;
- pseudo_bindir = pseudo_prefix_path(PSEUDO_BINDIR);
+ char *pseudo_bindir = pseudo_prefix_path(PSEUDO_BINDIR);;
if (pseudo_bindir) {
- setenv("PSEUDO_BINDIR", pseudo_bindir, 1);
- s = getenv("PSEUDO_BINDIR");
+ pseudo_set_value("PSEUDO_BINDIR", pseudo_bindir);
+ s = pseudo_bindir;
}
}
return s;
@@ -806,26 +947,20 @@ pseudo_get_bindir() {
char *
pseudo_get_libdir() {
- char *s;
- s = getenv("PSEUDO_LIBDIR");
+ char *s = pseudo_get_value("PSEUDO_LIBDIR");
if (!s) {
- char *pseudo_libdir;
- pseudo_libdir = pseudo_prefix_path(PSEUDO_LIBDIR);
+ char *pseudo_libdir = pseudo_prefix_path(PSEUDO_LIBDIR);
if (pseudo_libdir) {
- setenv("PSEUDO_LIBDIR", pseudo_libdir, 1);
- s = getenv("PSEUDO_LIBDIR");
+ pseudo_set_value("PSEUDO_LIBDIR", pseudo_libdir);
+ s = pseudo_libdir;
}
}
/* If we somehow got lib64 in there, clean it down to just lib... */
if (s) {
size_t len = strlen(s);
if (s[len-2] == '6' && s[len-1] == '4') {
- char mypath[pseudo_path_max()];
- snprintf(mypath, pseudo_path_max(), "%s", s);
- s = mypath + (strlen(mypath) - 2);
- s[0] = '\0';
- setenv("PSEUDO_LIBDIR", mypath, 1);
- s = getenv("PSEUDO_LIBDIR");
+ s[len-2] = '\0';
+ pseudo_set_value("PSEUDO_LIBDIR", s);
}
}
@@ -834,14 +969,12 @@ pseudo_get_libdir() {
char *
pseudo_get_localstatedir() {
- char *s;
- s = getenv("PSEUDO_LOCALSTATEDIR");
+ char *s = pseudo_get_value("PSEUDO_LOCALSTATEDIR");
if (!s) {
- char *pseudo_localstatedir;
- pseudo_localstatedir = pseudo_prefix_path(PSEUDO_LOCALSTATEDIR);
+ char *pseudo_localstatedir = pseudo_prefix_path(PSEUDO_LOCALSTATEDIR);
if (pseudo_localstatedir) {
- setenv("PSEUDO_LOCALSTATEDIR", pseudo_localstatedir, 1);
- s = getenv("PSEUDO_LOCALSTATEDIR");
+ pseudo_set_value("PSEUDO_LOCALSTATEDIR", pseudo_localstatedir);
+ s = pseudo_localstatedir;
}
}
return s;
@@ -1002,11 +1135,12 @@ pseudo_etc_file(const char *file, char *realname, int flags, char **search_dirs,
int
pseudo_logfile(char *defname) {
char *pseudo_path;
- char *filename, *s;
+ char *filename = pseudo_get_value("PSEUDO_DEBUG_FILE");
+ char *s;
extern char *program_invocation_short_name; /* glibcism */
int fd;
- if ((filename = getenv("PSEUDO_DEBUG_FILE")) == NULL) {
+ if (!filename) {
if (!defname) {
pseudo_debug(3, "no special log file requested, using stderr.\n");
return -1;
@@ -1077,7 +1211,8 @@ pseudo_logfile(char *defname) {
} else {
strcpy(pseudo_path, filename);
}
- }
+ free(filename);
+ }
fd = open(pseudo_path, O_WRONLY | O_APPEND | O_CREAT, 0644);
if (fd == -1) {
pseudo_diag("help: can't open log file %s: %s\n", pseudo_path, strerror(errno));
diff --git a/pseudo_wrappers.c b/pseudo_wrappers.c
index 4e98efa..d7ed9f6 100644
--- a/pseudo_wrappers.c
+++ b/pseudo_wrappers.c
@@ -1,6 +1,7 @@
/* wrapper code -- this is the shared code used around the pseduo
* wrapper functions, which are in pseudo_wrapfuncs.c.
*/
+#include <assert.h>
#include <stdlib.h>
#include <limits.h>
#include <stdarg.h>
@@ -84,8 +85,10 @@ pseudo_magic() {
static void
pseudo_enosys(const char *func) {
pseudo_diag("pseudo: ENOSYS for '%s'.\n", func ? func : "<nil>");
- if (getenv("PSEUDO_ENOSYS_ABORT"))
+ char * value = pseudo_get_value("PSEUDO_ENOSYS_ABORT");
+ if (value)
abort();
+ free(value);
}
/* de-chroot a string.
@@ -143,14 +146,15 @@ pseudo_populate_wrappers(void) {
}
}
done = 1;
- debug = getenv("PSEUDO_DEBUG");
+ debug = pseudo_get_value("PSEUDO_DEBUG");
if (debug) {
int level = atoi(debug);
for (i = 0; i < level; ++i) {
pseudo_debug_verbose();
}
}
- no_symlink_exp = getenv("PSEUDO_NOSYMLINKEXP");
+ free(debug);
+ no_symlink_exp = pseudo_get_value("PSEUDO_NOSYMLINKEXP");
if (no_symlink_exp) {
char *endptr;
/* if the environment variable is not an empty string,
@@ -171,6 +175,7 @@ pseudo_populate_wrappers(void) {
} else {
pseudo_nosymlinkexp = 0;
}
+ free(no_symlink_exp);
/* if PSEUDO_DEBUG_FILE is set up, redirect logging there.
*/
pseudo_logfile(NULL);
@@ -185,33 +190,35 @@ pseudo_populate_wrappers(void) {
if (pseudo_path) {
pseudo_prefix_dir_fd = open(pseudo_path, O_RDONLY);
pseudo_prefix_dir_fd = pseudo_fd(pseudo_prefix_dir_fd, MOVE_FD);
- free(pseudo_path);
} else {
pseudo_diag("No prefix available to to find server.\n");
exit(1);
}
if (pseudo_prefix_dir_fd == -1) {
- pseudo_diag("Can't open prefix path (%s) for server.\n",
+ pseudo_diag("Can't open prefix path (%s) for server: %s\n",
+ pseudo_path,
strerror(errno));
exit(1);
}
}
+ free(pseudo_path);
pseudo_path = pseudo_localstatedir_path(NULL);
if (pseudo_localstate_dir_fd == -1) {
if (pseudo_path) {
pseudo_localstate_dir_fd = open(pseudo_path, O_RDONLY);
pseudo_localstate_dir_fd = pseudo_fd(pseudo_localstate_dir_fd, MOVE_FD);
- free(pseudo_path);
} else {
pseudo_diag("No prefix available to to find server.\n");
exit(1);
}
if (pseudo_localstate_dir_fd == -1) {
- pseudo_diag("Can't open prefix path (%s) for server.\n",
+ pseudo_diag("Can't open prefix path (%s) for server: %s\n",
+ pseudo_path,
strerror(errno));
exit(1);
}
}
+ free(pseudo_path);
pseudo_debug(2, "(%s) set up wrappers\n", program_invocation_short_name);
pseudo_magic();
pseudo_droplock();
diff --git a/pseudolog.c b/pseudolog.c
index e2161c4..903c161 100644
--- a/pseudolog.c
+++ b/pseudolog.c
@@ -527,7 +527,7 @@ main(int argc, char **argv) {
s = PSEUDO_ROOT_PATH(AT_FDCWD, optarg, AT_SYMLINK_NOFOLLOW);
if (!s)
pseudo_diag("Can't resolve prefix path '%s'\n", optarg);
- setenv("PSEUDO_PREFIX", s, 1);
+ pseudo_set_value("PSEUDO_PREFIX", s);
break;
case 'v':
pseudo_debug_verbose();
diff --git a/test/test-env_i.sh b/test/test-env_i.sh
new file mode 100755
index 0000000..c38cf1d
--- /dev/null
+++ b/test/test-env_i.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+env -i A=A B=B C=C env | grep -q "PSEUDO_"
+
+if [ "$?" = "0" ]
+then
+ #echo "Passed."
+ exit 0
+fi
+#echo "Failed"
+exit 1