bsd-user: Add generic env variable handling

Based on 04a6dfebb6b52532a1e0bd637899f1eba14e94c6.

Adds support for qemu to modify target process environment
variables using -E and -U commandline switches. This replaces
eventually the -drop-ld-preload flag.

Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
diff --git a/bsd-user/main.c b/bsd-user/main.c
index 56c075a..56710ec 100644
--- a/bsd-user/main.c
+++ b/bsd-user/main.c
@@ -31,6 +31,9 @@
 /* For tb_lock */
 #include "exec-all.h"
 
+
+#include "envlist.h"
+
 #define DEBUG_LOGFILE "/tmp/qemu.log"
 
 int singlestep;
@@ -602,6 +605,8 @@
            "-s size           set the stack size in bytes (default=%ld)\n"
            "-cpu model        select CPU (-cpu ? for list)\n"
            "-drop-ld-preload  drop LD_PRELOAD for target process\n"
+           "-E var=value      sets/modifies targets environment variable(s)\n"
+           "-U var            unsets targets environment variable(s)\n"
            "-bsd type         select emulated BSD type FreeBSD/NetBSD/OpenBSD (default)\n"
            "\n"
            "Debug options:\n"
@@ -613,6 +618,12 @@
            "Environment variables:\n"
            "QEMU_STRACE       Print system calls and arguments similar to the\n"
            "                  'strace' program.  Enable by setting to any value.\n"
+           "You can use -E and -U options to set/unset environment variables\n"
+           "for target process.  It is possible to provide several variables\n"
+           "by repeating the option.  For example:\n"
+           "    -E var1=val2 -E var2=val2 -U LD_PRELOAD -U LD_DEBUG\n"
+           "Note that if you provide several changes to single variable\n"
+           "last change will stay in effect.\n"
            ,
            TARGET_ARCH,
            interp_prefix,
@@ -647,8 +658,8 @@
     int optind;
     const char *r;
     int gdbstub_port = 0;
-    int drop_ld_preload = 0, environ_count = 0;
-    char **target_environ, **wrk, **dst;
+    char **target_environ, **wrk;
+    envlist_t *envlist = NULL;
     enum BSDType bsd_type = target_openbsd;
 
     if (argc <= 1)
@@ -657,6 +668,16 @@
     /* init debug */
     cpu_set_log_filename(DEBUG_LOGFILE);
 
+    if ((envlist = envlist_create()) == NULL) {
+        (void) fprintf(stderr, "Unable to allocate envlist\n");
+        exit(1);
+    }
+
+    /* add current environment into the list */
+    for (wrk = environ; *wrk != NULL; wrk++) {
+        (void) envlist_setenv(envlist, *wrk);
+    }
+
     cpu_model = NULL;
     optind = 1;
     for(;;) {
@@ -686,6 +707,14 @@
                 exit(1);
             }
             cpu_set_log(mask);
+        } else if (!strcmp(r, "E")) {
+            r = argv[optind++];
+            if (envlist_setenv(envlist, r) != 0)
+                usage();
+        } else if (!strcmp(r, "U")) {
+            r = argv[optind++];
+            if (envlist_unsetenv(envlist, r) != 0)
+                usage();
         } else if (!strcmp(r, "s")) {
             r = argv[optind++];
             x86_stack_size = strtol(r, (char **)&r, 0);
@@ -718,7 +747,7 @@
                 exit(1);
             }
         } else if (!strcmp(r, "drop-ld-preload")) {
-            drop_ld_preload = 1;
+            (void) envlist_unsetenv(envlist, "LD_PRELOAD");
         } else if (!strcmp(r, "bsd")) {
             if (!strcasecmp(argv[optind], "freebsd")) {
                 bsd_type = target_freebsd;
@@ -783,19 +812,9 @@
         do_strace = 1;
     }
 
-    wrk = environ;
-    while (*(wrk++))
-        environ_count++;
+    target_environ = envlist_to_environ(envlist, NULL);
+    envlist_free(envlist);
 
-    target_environ = malloc((environ_count + 1) * sizeof(char *));
-    if (!target_environ)
-        abort();
-    for (wrk = environ, dst = target_environ; *wrk; wrk++) {
-        if (drop_ld_preload && !strncmp(*wrk, "LD_PRELOAD=", 11))
-            continue;
-        *(dst++) = strdup(*wrk);
-    }
-    *dst = NULL; /* NULL terminate target_environ */
 
     if (loader_exec(filename, argv+optind, target_environ, regs, info) != 0) {
         printf("Error loading %s\n", filename);