diff options
author | NIIBE Yutaka <[email protected]> | 2024-10-11 07:17:53 +0000 |
---|---|---|
committer | NIIBE Yutaka <[email protected]> | 2024-10-11 07:29:02 +0000 |
commit | 1860f6407f834b681c21f67db7236eaad161524c (patch) | |
tree | 81e1fee4007e1b70fad278f9716fd65fcc28a7e4 /src | |
parent | w32: Fix releasing memory for UTF-8 text. (diff) | |
download | libgpg-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]>
Diffstat (limited to 'src')
-rw-r--r-- | src/gpg-error.def.in | 1 | ||||
-rw-r--r-- | src/gpg-error.h.in | 2 | ||||
-rw-r--r-- | src/gpg-error.vers | 1 | ||||
-rw-r--r-- | src/gpgrt-int.h | 2 | ||||
-rw-r--r-- | src/spawn-posix.c | 47 | ||||
-rw-r--r-- | src/spawn-w32.c | 210 | ||||
-rw-r--r-- | src/visibility.c | 7 | ||||
-rw-r--r-- | src/visibility.h | 2 |
8 files changed, 264 insertions, 8 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 |