aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNIIBE Yutaka <[email protected]>2024-10-11 07:17:53 +0000
committerNIIBE Yutaka <[email protected]>2024-10-11 07:29:02 +0000
commit1860f6407f834b681c21f67db7236eaad161524c (patch)
tree81e1fee4007e1b70fad278f9716fd65fcc28a7e4
parentw32: Fix releasing memory for UTF-8 text. (diff)
downloadlibgpg-error-1860f6407f834b681c21f67db7236eaad161524c.tar.gz
libgpg-error-1860f6407f834b681c21f67db7236eaad161524c.zip
spawn: Add new function to modify environment.
* src/gpg-error.def.in (gpgrt_spawn_actions_set_envchange): New. * src/gpg-error.vers (gpgrt_spawn_actions_set_envchange): New. * src/gpg-error.h.in (gpgrt_spawn_actions_set_envchange): New. * src/gpgrt-int.h (_gpgrt_spawn_actions_set_envchange): New. * src/spawn-posix.c (struct gpgrt_spawn_actions): New field ENVCHANGE. (prepare_environ): New. (my_exec): Take care of ENVCHANGE. (_gpgrt_spawn_actions_set_envchange): New. * src/spawn-w32.c (struct gpgrt_spawn_actions): New field ENVCHANGE. (prepare_env_block): New. (_gpgrt_spawn_actions_set_envchange): New. (spawn_detached, _gpgrt_process_spawn): Take care of ENVCHANGE. * src/visibility.c (gpgrt_spawn_actions_set_envchange): New. * src/visibility.h (gpgrt_spawn_actions_set_envchange): New. * tests/Makefile.am (TESTS): Add t-spawn. * tests/t-spawn.c: New. -- GnuPG-bug-id: 7307 Signed-off-by: NIIBE Yutaka <[email protected]>
-rw-r--r--src/gpg-error.def.in1
-rw-r--r--src/gpg-error.h.in2
-rw-r--r--src/gpg-error.vers1
-rw-r--r--src/gpgrt-int.h2
-rw-r--r--src/spawn-posix.c47
-rw-r--r--src/spawn-w32.c210
-rw-r--r--src/visibility.c7
-rw-r--r--src/visibility.h2
-rw-r--r--tests/Makefile.am2
-rw-r--r--tests/t-spawn.c220
10 files changed, 485 insertions, 9 deletions
diff --git a/src/gpg-error.def.in b/src/gpg-error.def.in
index 31a28ef..3b9f462 100644
--- a/src/gpg-error.def.in
+++ b/src/gpg-error.def.in
@@ -254,5 +254,6 @@ EXPORTS
gpgrt_spawn_actions_set_envvars @194
gpgrt_spawn_actions_set_redirect @195
gpgrt_spawn_actions_set_inherit_handles @196
+ gpgrt_spawn_actions_set_envchange @197
;; end of file with public symbols for Windows.
diff --git a/src/gpg-error.h.in b/src/gpg-error.h.in
index 68a9347..44566ae 100644
--- a/src/gpg-error.h.in
+++ b/src/gpg-error.h.in
@@ -1120,6 +1120,8 @@ typedef struct gpgrt_process *gpgrt_process_t;
typedef struct gpgrt_spawn_actions *gpgrt_spawn_actions_t;
gpg_err_code_t gpgrt_spawn_actions_new (gpgrt_spawn_actions_t *r_act);
void gpgrt_spawn_actions_release (gpgrt_spawn_actions_t act);
+void gpgrt_spawn_actions_set_envchange (gpgrt_spawn_actions_t,
+ const char *const*);
@define:spawn_actions_functions@
enum gpgrt_process_requests
diff --git a/src/gpg-error.vers b/src/gpg-error.vers
index c4704df..2868f3c 100644
--- a/src/gpg-error.vers
+++ b/src/gpg-error.vers
@@ -221,6 +221,7 @@ GPG_ERROR_1.0 {
gpgrt_spawn_actions_set_redirect;
gpgrt_spawn_actions_set_inherit_fds;
gpgrt_spawn_actions_set_atfork;
+ gpgrt_spawn_actions_set_envchange;
local:
*;
diff --git a/src/gpgrt-int.h b/src/gpgrt-int.h
index 4a37686..55933a1 100644
--- a/src/gpgrt-int.h
+++ b/src/gpgrt-int.h
@@ -637,6 +637,8 @@ gpg_err_code_t _gpgrt_make_pipe (int filedes[2], estream_t *r_fp,
/* Actions (at spawning a child process), which is OS-specific. */
gpg_err_code_t _gpgrt_spawn_actions_new (gpgrt_spawn_actions_t *r_act);
void _gpgrt_spawn_actions_release (gpgrt_spawn_actions_t act);
+void _gpgrt_spawn_actions_set_envchange (gpgrt_spawn_actions_t,
+ const char *const*);
#ifdef HAVE_W32_SYSTEM
void _gpgrt_spawn_actions_set_envvars (gpgrt_spawn_actions_t, char *);
void _gpgrt_spawn_actions_set_redirect (gpgrt_spawn_actions_t,
diff --git a/src/spawn-posix.c b/src/spawn-posix.c
index 9787ec1..4bb90ca 100644
--- a/src/spawn-posix.c
+++ b/src/spawn-posix.c
@@ -57,9 +57,6 @@
#include "gpgrt-int.h"
-/* (Only glibc's unistd.h declares this iff _GNU_SOURCE is used.) */
-extern char **environ;
-
/* Definition for the gpgrt_spawn_actions_t. Note that there is a
* different one for Windows. */
@@ -67,6 +64,7 @@ struct gpgrt_spawn_actions {
int fd[3];
const int *except_fds;
char **environ;
+ const char *const *envchange;
void (*atfork) (void *);
void *atfork_arg;
};
@@ -318,6 +316,31 @@ posix_open_null (int for_write)
}
+static gpg_err_code_t
+prepare_environ (const char *const *envchange)
+{
+ const char *const *envp;
+ const char *e;
+
+ for (envp = envchange; (e = *envp); envp++)
+ {
+ char *name = xtrystrdup (e);
+ char *p;
+
+ if (!name)
+ return _gpg_err_code_from_syserror ();
+
+ p = strchr (name, '=');
+ if (p)
+ *p++ = 0;
+
+ _gpgrt_setenv (name, p, 1);
+ xfree (name);
+ }
+
+ return 0;
+}
+
static int
my_exec (const char *pgmname, const char *argv[], gpgrt_spawn_actions_t act)
{
@@ -345,8 +368,8 @@ my_exec (const char *pgmname, const char *argv[], gpgrt_spawn_actions_t act)
/* Close all other files. */
_gpgrt_close_all_fds (3, act->except_fds);
- if (act->environ)
- environ = act->environ;
+ if (act->envchange && prepare_environ (act->envchange))
+ goto leave;
if (act->atfork)
act->atfork (act->atfork_arg);
@@ -355,8 +378,13 @@ my_exec (const char *pgmname, const char *argv[], gpgrt_spawn_actions_t act)
if (pgmname == NULL)
return 0;
- execv (pgmname, (char *const *)argv);
+ if (act->environ)
+ execve (pgmname, (char *const *)argv, act->environ);
+ else
+ execv (pgmname, (char *const *)argv);
+
/* No way to print anything, as we have may have closed all streams. */
+ leave:
_exit (127);
return -1;
}
@@ -456,6 +484,13 @@ _gpgrt_spawn_actions_set_environ (gpgrt_spawn_actions_t act,
}
void
+_gpgrt_spawn_actions_set_envchange (gpgrt_spawn_actions_t act,
+ const char *const *envchange)
+{
+ act->envchange = envchange;
+}
+
+void
_gpgrt_spawn_actions_set_atfork (gpgrt_spawn_actions_t act,
void (*atfork)(void *), void *arg)
{
diff --git a/src/spawn-w32.c b/src/spawn-w32.c
index 909929c..c21ac4b 100644
--- a/src/spawn-w32.c
+++ b/src/spawn-w32.c
@@ -73,6 +73,7 @@ struct gpgrt_spawn_actions {
void *hd[3];
void **inherit_hds;
char *env;
+ const char *const *envchange;
};
@@ -328,6 +329,147 @@ check_windows_version (void)
return is_vista_or_later;
}
+static gpg_err_code_t
+prepare_env_block (char **r_env, const char *const *envchange)
+{
+ gpg_err_code_t ec;
+ wchar_t *orig_env_block;
+ wchar_t *wp;
+ int i;
+ size_t envlen[256];
+ wchar_t *env_block;
+ size_t env_block_len;
+
+ const char *const *envp;
+ const char *e;
+ int env_num;
+
+ wp = orig_env_block = GetEnvironmentStringsW ();
+ for (i = 0; *wp != L'\0'; i++)
+ if (i >= DIM (envlen))
+ {
+ FreeEnvironmentStringsW (orig_env_block);
+ return GPG_ERR_TOO_LARGE;
+ }
+ else
+ wp += ((envlen[i] = wcslen (wp)) + 1);
+ wp++;
+ env_num = i;
+
+ env_block_len = (char *)wp - (char *)orig_env_block;
+ env_block = xtrymalloc (env_block_len);
+ if (!env_block)
+ {
+ FreeEnvironmentStringsW (orig_env_block);
+ return _gpg_err_code_from_syserror ();
+ }
+ memcpy (env_block, orig_env_block, env_block_len);
+ FreeEnvironmentStringsW (orig_env_block);
+
+ for (envp = envchange; (e = *envp); envp++)
+ {
+ wchar_t *we;
+ int off = 0;
+
+ we = _gpgrt_utf8_to_wchar (e);
+ if (!we)
+ {
+ ec = _gpg_err_code_from_syserror ();
+ goto leave;
+ }
+
+ wp = wcschr (we, L'=');
+ if (!wp)
+ {
+ /* Remove WE entry in the environment block. */
+ for (i = 0; i < env_num; i++)
+ if (!wcsncmp (&env_block[off], we, wcslen (we))
+ && env_block[off+wcslen (we)] == L'=')
+ break;
+ else
+ off += envlen[i] + 1;
+
+ if (i == env_num)
+ /* not found */;
+ else
+ {
+ env_block_len -= (envlen[i] + 1) * sizeof (wchar_t);
+ env_num--;
+ for (; i < env_num; i++)
+ {
+ int off0 = off;
+
+ off += envlen[i] + 1;
+ memmove (&env_block[off0], &env_block[off],
+ ((envlen[i] = envlen[i+1]) + 1) * sizeof (wchar_t));
+ }
+ env_block[(env_block_len / sizeof (wchar_t)) - 1] = L'\0';
+ }
+ }
+ else
+ {
+ size_t old_env_block_len;
+
+ for (i = 0; i < env_num; i++)
+ if (!wcsncmp (&env_block[off], we, wp - we + 1))
+ break;
+ else
+ off += envlen[i] + 1;
+
+ if (i < env_num)
+ {
+ int off0 = off;
+
+ off += envlen[i] + 1;
+ /* If an existing entry, remove it. */
+ env_block_len -= (envlen[i] + 1) * sizeof (wchar_t);
+ env_num--;
+ for (; i < env_num; i++)
+ {
+ size_t len = (envlen[i] = envlen[i+1]) + 1;
+
+ memmove (&env_block[off0], &env_block[off],
+ len * sizeof (wchar_t));
+ off0 += len;
+ off += len;
+ }
+ env_block[(env_block_len / sizeof (wchar_t)) - 1] = L'\0';
+ }
+
+ if (i >= DIM (envlen) - 1)
+ {
+ ec = GPG_ERR_TOO_LARGE;
+ _gpgrt_free_wchar (we);
+ goto leave;
+ }
+
+ old_env_block_len = env_block_len;
+ env_block_len += ((envlen[i++] = wcslen (we)) + 1) * sizeof (wchar_t);
+ env_num = i;
+ env_block = xtryrealloc (env_block, env_block_len);
+ if (!env_block)
+ {
+ ec = _gpg_err_code_from_syserror ();
+ _gpgrt_free_wchar (we);
+ goto leave;
+ }
+ memmove ((char *)env_block + old_env_block_len - sizeof (wchar_t),
+ we, (envlen[env_num - 1] + 1) * sizeof (wchar_t));
+ env_block[(env_block_len / sizeof (wchar_t)) - 1] = L'\0';
+ }
+
+ _gpgrt_free_wchar (we);
+ }
+ ec = 0;
+
+ leave:
+ if (ec)
+ xfree (env_block);
+ else
+ *r_env = (char *)env_block;
+
+ return ec;
+}
static gpg_err_code_t
spawn_detached (const char *pgmname, char *cmdline, gpgrt_spawn_actions_t act)
@@ -342,6 +484,7 @@ spawn_detached (const char *pgmname, char *cmdline, gpgrt_spawn_actions_t act)
int ret;
BOOL ask_inherit = FALSE;
int i;
+ char *env = NULL;
ec = _gpgrt_access (pgmname, X_OK);
if (ec)
@@ -425,6 +568,29 @@ spawn_detached (const char *pgmname, char *cmdline, gpgrt_spawn_actions_t act)
| CREATE_NEW_PROCESS_GROUP
| DETACHED_PROCESS);
+ if (act->env)
+ {
+ /* Either ENV or ENVCHANGE can be specified, not both. */
+ if (act->envchange)
+ {
+ xfree (cmdline);
+ return GPG_ERR_INV_ARG;
+ }
+
+ env = act->env;
+ }
+ else if (act->envchange)
+ {
+ ec = prepare_env_block (&env, act->envchange);
+ if (ec)
+ {
+ xfree (cmdline);
+ return ec;
+ }
+
+ cr_flags |= CREATE_UNICODE_ENVIRONMENT;
+ }
+
/* Take care: CreateProcessW may modify wpgmname */
if (!(wpgmname = _gpgrt_utf8_to_wchar (pgmname)))
ret = 0;
@@ -437,11 +603,14 @@ spawn_detached (const char *pgmname, char *cmdline, gpgrt_spawn_actions_t act)
&sec_attr, /* Thread security attributes. */
ask_inherit, /* Inherit handles. */
cr_flags, /* Creation flags. */
- act->env, /* Environment. */
+ env, /* Environment. */
NULL, /* Use current drive/directory. */
(STARTUPINFOW *)&si, /* Startup information. */
&pi /* Returns process information. */
);
+ if (act->envchange)
+ xfree (env);
+ env = NULL;
if (!ret)
{
if (!wpgmname || !wcmdline)
@@ -505,6 +674,13 @@ _gpgrt_spawn_actions_release (gpgrt_spawn_actions_t act)
xfree (act);
}
+void
+_gpgrt_spawn_actions_set_envchange (gpgrt_spawn_actions_t act,
+ const char *const *envchange)
+{
+ act->envchange = envchange;
+}
+
/* Set the environment block for child process.
* ENV is an ASCII encoded string, terminated by two zero bytes.
*/
@@ -552,6 +728,7 @@ _gpgrt_process_spawn (const char *pgmname, const char *argv[],
int i;
BOOL ask_inherit = FALSE;
struct gpgrt_spawn_actions act_default;
+ char *env = NULL;
if (!act)
{
@@ -783,6 +960,32 @@ _gpgrt_process_spawn (const char *pgmname, const char *argv[],
| ((flags & GPGRT_PROCESS_NO_CONSOLE) ? DETACHED_PROCESS : 0)
| GetPriorityClass (GetCurrentProcess ())
| CREATE_SUSPENDED);
+
+ if (act->env)
+ {
+ /* Either ENV or ENVCHANGE can be specified, not both. */
+ if (act->envchange)
+ {
+ xfree (process);
+ xfree (cmdline);
+ return GPG_ERR_INV_ARG;
+ }
+
+ env = act->env;
+ }
+ else if (act->envchange)
+ {
+ ec = prepare_env_block (&env, act->envchange);
+ if (ec)
+ {
+ xfree (process);
+ xfree (cmdline);
+ return ec;
+ }
+
+ cr_flags |= CREATE_UNICODE_ENVIRONMENT;
+ }
+
if (!(wpgmname = _gpgrt_utf8_to_wchar (pgmname)))
ret = 0;
else if (!(wcmdline = _gpgrt_utf8_to_wchar (cmdline)))
@@ -794,11 +997,14 @@ _gpgrt_process_spawn (const char *pgmname, const char *argv[],
&sec_attr, /* Thread security attributes. */
ask_inherit, /* Inherit handles. */
cr_flags, /* Creation flags. */
- act->env, /* Environment. */
+ env, /* Environment. */
NULL, /* Use current drive/directory. */
(STARTUPINFOW *)&si, /* Startup information. */
&pi /* Returns process information. */
);
+ if (act->envchange)
+ xfree (env);
+ env = NULL;
if (!ret)
{
if (!wpgmname || !wcmdline)
diff --git a/src/visibility.c b/src/visibility.c
index 0e8121e..15b38b4 100644
--- a/src/visibility.c
+++ b/src/visibility.c
@@ -1145,6 +1145,13 @@ gpgrt_spawn_actions_release (gpgrt_spawn_actions_t act)
_gpgrt_spawn_actions_release (act);
}
+void
+gpgrt_spawn_actions_set_envchange (gpgrt_spawn_actions_t act,
+ const char *const *env)
+{
+ _gpgrt_spawn_actions_set_envchange (act, env);
+}
+
#ifdef HAVE_W32_SYSTEM
void
gpgrt_spawn_actions_set_envvars (gpgrt_spawn_actions_t act,
diff --git a/src/visibility.h b/src/visibility.h
index 29ebaee..748a42a 100644
--- a/src/visibility.h
+++ b/src/visibility.h
@@ -231,6 +231,7 @@ MARK_VISIBLE (gpgrt_absfnameconcat)
MARK_VISIBLE (gpgrt_spawn_actions_new)
MARK_VISIBLE (gpgrt_spawn_actions_release)
+MARK_VISIBLE (gpgrt_spawn_actions_set_envchange)
#ifdef HAVE_W32_SYSTEM
MARK_VISIBLE (gpgrt_spawn_actions_set_envvars)
MARK_VISIBLE (gpgrt_spawn_actions_set_redirect)
@@ -423,6 +424,7 @@ MARK_VISIBLE (gpgrt_spawn_actions_set_atfork)
#define gpgrt_spawn_actions_new _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_spawn_actions_release _gpgrt_USE_UNDERSCORED_FUNCTION
+#define gpgrt_spawn_actions_set_envchange _gpgrt_USE_UNDERSCORED_FUNCTION
#ifdef HAVE_W32_SYSTEM
#define gpgrt_spawn_actions_set_envvars _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_spawn_actions_set_redirect _gpgrt_USE_UNDERSCORED_FUNCTION
diff --git a/tests/Makefile.am b/tests/Makefile.am
index d421b3b..3e3f60c 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -24,7 +24,7 @@ EXTRA_DIST = t-argparse.conf etc/t-argparse.conf
gpg_error_lib = ../src/libgpg-error.la
TESTS = t-version t-strerror t-syserror t-lock t-printf t-poll t-b64 \
- t-argparse t-logging t-stringutils t-malloc
+ t-argparse t-logging t-stringutils t-malloc t-spawn
if HAVE_LOCK_OPTIMIZATION
TESTS += t-lock-single-posix
diff --git a/tests/t-spawn.c b/tests/t-spawn.c
new file mode 100644
index 0000000..96c890a
--- /dev/null
+++ b/tests/t-spawn.c
@@ -0,0 +1,220 @@
+/* t-spawn.c - Check the spawn functions.
+ * Copyright (C) 2024 g10 Code GmbH
+ *
+ * This file is part of libgpg-error.
+ *
+ * libgpg-error is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * libgpg-error is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define PGM "t-spawn"
+
+#include "t-common.h"
+
+#define MAXLINE 1024
+
+static void
+run_test (const char *progname)
+{
+ gpg_err_code_t rc;
+ int r;
+ gpgrt_spawn_actions_t act;
+ gpgrt_process_t process;
+ gpgrt_stream_t fp_out;
+ const char *const envchange[] = {
+ "ADD=0", /* Add */
+ "REPLACE=1", /* Replace */
+ "GNUPGHOME", /* Remove */
+ NULL,
+ };
+ const char *argv1[] = {
+ "--child",
+ NULL,
+ };
+ char line[MAXLINE];
+ int ok_add, ok_replace, ok_remove;
+
+ /* Make sure ADD is not defined in the parent process. */
+ rc = gpgrt_setenv ("ADD", NULL, 1);
+ if (rc)
+ fail ("gpgrt_setenv failed at %d: %s", __LINE__, gpg_strerror (rc));
+
+ /* Set REPLACE and GNUPGHOME in the parent process. */
+ rc = gpgrt_setenv ("REPLACE", "0", 1);
+ if (rc)
+ fail ("gpgrt_setenv failed at %d: %s", __LINE__, gpg_strerror (rc));
+ rc = gpgrt_setenv ("GNUPGHOME", "/tmp/test", 1);
+ if (rc)
+ fail ("gpgrt_setenv failed at %d: %s", __LINE__, gpg_strerror (rc));
+
+ rc = gpgrt_spawn_actions_new (&act);
+ if (rc)
+ fail ("gpgrt_spawn_actions_new failed at %d: %s",
+ __LINE__, gpg_strerror (rc));
+
+ gpgrt_spawn_actions_set_envchange (act, envchange);
+
+ rc = gpgrt_process_spawn (progname, argv1,
+ (GPGRT_PROCESS_STDIN_KEEP
+ | GPGRT_PROCESS_STDOUT_PIPE
+ | GPGRT_PROCESS_STDERR_KEEP),
+ act, &process);
+ if (rc)
+ fail ("gpgrt_process_spawn failed at %d: %s", __LINE__, gpg_strerror (rc));
+
+ gpgrt_spawn_actions_release (act);
+
+ rc = gpgrt_process_get_streams (process, 0, NULL, &fp_out, NULL);
+ if (rc)
+ fail ("gpgrt_process_get_streams failed at %d: %s",
+ __LINE__, gpg_strerror (rc));
+
+ ok_add = ok_replace = 0;
+ ok_remove = 1;
+
+ while (gpgrt_fgets (line, DIM (line), fp_out))
+ {
+#ifdef HAVE_W32_SYSTEM
+ { /* Take care of CRLF. */
+ char *p = strchr (line, '\r');
+ if (p)
+ *p = '\n';
+ }
+#endif
+ if (!strncmp (line, "ADD=", 3))
+ {
+ if (!strncmp (line+4, "0\n", 2))
+ ok_add = 1;
+ }
+ if (!strncmp (line, "REPLACE=", 8))
+ {
+ if (!strncmp (line+8, "1\n", 2))
+ ok_replace = 1;
+ }
+ if (!strncmp (line, "GNUPGHOME=", 10))
+ ok_remove = 0;
+ }
+
+ r = gpgrt_fclose (fp_out);
+ if (r)
+ fail ("gpgrt_fclose failed at %d: %d", __LINE__, r);
+
+ rc = gpgrt_process_wait (process, 1);
+ if (rc)
+ fail ("gpgrt_process_wait failed at %d: %s", __LINE__, gpg_strerror (rc));
+
+ gpgrt_process_release (process);
+
+ if (verbose)
+ show ("Result: add=%s, replace=%s, remove=%s\n",
+ ok_add? "OK" : "FAIL", ok_replace? "OK" : "FAIL",
+ ok_remove? "OK" : "FAIL");
+
+ if (!ok_add)
+ fail ("ADD failed");
+ if (!ok_replace)
+ fail ("REPLACE failed");
+ if (!ok_remove)
+ fail ("GNUPGHOME failed");
+}
+
+
+int
+main (int argc, char **argv)
+{
+ int last_argc = -1;
+ int child = 0;
+ const char *progname = argv[0];
+
+ if (argc)
+ {
+ argc--; argv++;
+ }
+ while (argc && last_argc != argc )
+ {
+ last_argc = argc;
+ if (!strcmp (*argv, "--help"))
+ {
+ puts (
+"usage: ./t-spawn [options]\n"
+"\n"
+"Options:\n"
+" --verbose Show what is going on\n"
+" --debug Flyswatter\n"
+" --child Internal use for the child process\n"
+);
+ exit (0);
+ }
+ if (!strcmp (*argv, "--verbose"))
+ {
+ verbose = 1;
+ argc--; argv++;
+ }
+ else if (!strcmp (*argv, "--debug"))
+ {
+ verbose = debug = 1;
+ argc--; argv++;
+ }
+ else if (!strcmp (*argv, "--child"))
+ {
+ child = 1;
+ argc--; argv++;
+ }
+ }
+
+ if (!gpg_error_check_version (GPG_ERROR_VERSION))
+ {
+ die ("gpg_error_check_version returned an error");
+ errorcount++;
+ }
+
+ if (child)
+ {
+ char *e;
+
+ e = gpgrt_getenv ("ADD");
+ if (e)
+ {
+ printf ("ADD=%s\n", e);
+ gpgrt_free (e);
+ }
+ e = gpgrt_getenv ("REPLACE");
+ if (e)
+ {
+ printf ("REPLACE=%s\n", e);
+ gpgrt_free (e);
+ }
+ e = gpgrt_getenv ("GNUPGHOME");
+ if (e)
+ {
+ printf ("GNUPGHOME=%s\n", e);
+ gpgrt_free (e);
+ }
+ return 0;
+ }
+
+ run_test (progname);
+ return errorcount ? 1 : 0;
+}