From 64f80903ec9bc8897f9b2dcebb7068c02ff9ae07 Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Fri, 9 Jan 2026 13:42:05 +0900 Subject: w32:spawn: Support the case without no helper. * src/w32-io.c (_gpgme_io_spawn_sans_helper): New. (_gpgme_io_spawn): Use _gpgme_io_spawn_sans_helper. -- GnuPG-bug-id: 7127 Signed-off-by: NIIBE Yutaka --- src/w32-io.c | 276 +++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 239 insertions(+), 37 deletions(-) diff --git a/src/w32-io.c b/src/w32-io.c index 841cbb31..48f2869f 100644 --- a/src/w32-io.c +++ b/src/w32-io.c @@ -1337,6 +1337,240 @@ build_commandline (char **argv) return buf; } +#if !defined(GPGRT_PROCESS_STDIO_NUL) /* libgpg-error is old. */ +int +_gpgme_io_spawn_sans_helper (const char *path, char *const spawn_argv[], + unsigned int spawn_flags, + struct spawn_fd_item_s *fd_list, + void (*atfork) (void *opaque, int reserved), + void *atforkvalue, assuan_pid_t *r_pid) +{ + static int spawn_warning_shown; + + if (1) + { + /* This is a common mistake for new users of gpgme not to include + gpgme-w32spawn.exe with their binary. So we want to make + this transparent to developers. If users have somehow messed + up their installation this should also be properly communicated + as otherwise calls to gnupg will result in unsupported protocol + errors that do not explain a lot. */ + if (!spawn_warning_shown) + { + char *msg; + gpgrt_asprintf (&msg, "gpgme-w32spawn.exe was not found in the " + "detected installation directory of GpgME" + "\n\t\"%s\"\n\n" + "Crypto operations will not work.\n\n" + "If you see this it indicates a problem " + "with your installation.\n" + "Please report the problem to your " + "distributor of GpgME.\n\n" + "Developer's Note: The install dir can be " + "manually set with: gpgme_set_global_flag", + _gpgme_get_inst_dir ()); + MessageBoxA (NULL, msg, "GpgME not installed correctly", MB_OK); + gpgrt_free (msg); + spawn_warning_shown = 1; + } + gpg_err_set_errno (EIO); + return TRACE_SYSRES (-1); + } +} +#else +/* Format string to represent the handle. */ +#ifdef _WIN64 +#define FMT_HD "%llu" +#else +#define FMT_HD "%u" +#endif + +/* Enough string space to put the handle with FMT_HD in 64-bit. */ +#define MAX_ARG_STR 23 + +int +_gpgme_io_spawn_sans_helper (const char *path, char *const spawn_argv[], + unsigned int spawn_flags, + struct spawn_fd_item_s *fd_list, + void (*atfork) (void *opaque, int reserved), + void *atforkvalue, assuan_pid_t *r_pid) +{ + int i; + gpg_err_code_t ec; + unsigned int flags = GPGRT_PROCESS_STDIO_NUL; + gpgrt_spawn_actions_t act; + gpgrt_process_t process; + HANDLE handle_in, handle_out, handle_err; + const char **argv; + int argc; + char *p; + HANDLE hProcess; + HANDLE handles[32]; + int inherit_hd = 0; + + TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_spawn", path, + "path=%s", path); + + (void)atfork; + (void)atforkvalue; + + i = 0; + while (spawn_argv[i]) + { + TRACE_LOG ("argv[%2i] = %s", i, spawn_argv[i]); + i++; + } + argc = i; + + argv = malloc ((sizeof (char *) + MAX_ARG_STR)* (argc + 1)); + if (!argv) + return TRACE_SYSRES (-1); + p = (char *)&argv[argc+1]; + + for (i = 0; i < argc; i++) + argv[i] = spawn_argv[i]; + argv[argc] = NULL; + + flags |= GPGRT_PROCESS_DETACHED; + if ((spawn_flags & IOSPAWN_FLAG_ALLOW_SET_FG)) + flags |= GPGRT_PROCESS_ALLOW_SET_FG; + + LOCK (fd_table_lock); + for (i = 0; fd_list[i].fd != -1; i++) + { + int fd = fd_list[i].fd; + HANDLE hd = INVALID_HANDLE_VALUE; + + if (fd >= 0 && fd < fd_table_size && fd_table[fd].used + && fd_table[fd].hdd) + hd = fd_table[fd].hdd->hd; + + fd_list[i].peer_name = hd; + } + UNLOCK (fd_table_lock); + + handle_in = handle_out = handle_err = INVALID_HANDLE_VALUE; + + for (i = 0; fd_list[i].fd != -1; i++) + { + HANDLE hd = fd_list[i].peer_name; + int idx; + int r; + int std_hd = 0; + + if (fd_list[i].dup_to == 0) + { + handle_in = hd; + std_hd++; + } + else if (fd_list[i].dup_to == 1) + { + handle_out = hd; + std_hd++; + } + else if (fd_list[i].dup_to == 2) + { + handle_err = hd; + std_hd++; + } + + idx = fd_list[i].arg_loc; + if (idx == 0) + continue; + if (idx >= argc) + /* something goes wrong, ignore. */ + continue; + + if (!std_hd) + { + if (inherit_hd < DIM (handles) - 1) + { + if (hd != INVALID_HANDLE_VALUE) + handles[inherit_hd++] = hd; + } + else + { + free (argv); + return TRACE_SYSRES (-1); + } + } + + /* Fix the arg at ARG_LOC. */ + if (spawn_argv[idx][0] == '-' && spawn_argv[idx][1] == '&') + r = snprintf (p, MAX_ARG_STR, "-&" FMT_HD, (uintptr_t)hd); + else + r = snprintf (p, MAX_ARG_STR, FMT_HD, (uintptr_t)hd); + argv[idx] = p; + + if (r < 0) + { + free (argv); + return TRACE_SYSRES (-1); + } + + p += r + 1; + } + + handles[inherit_hd] = INVALID_HANDLE_VALUE; + + ec = gpgrt_spawn_actions_new (&act); + if (ec) + { + free (argv); + return TRACE_SYSRES (-1); + } + + gpgrt_spawn_actions_set_redirect (act, handle_in, handle_out, handle_err); + gpgrt_spawn_actions_set_inherit_handles (act, handles); + + ec = gpgrt_process_spawn (path, argv+1, flags, act, &process); + gpgrt_spawn_actions_release (act); + free (argv); + if (ec) + { + gpg_err_set_errno (EIO); + return TRACE_SYSRES (-1); + } + + gpgrt_process_ctl (process, GPGRT_PROCESS_GET_P_HANDLE, &hProcess); + + TRACE_LOG ("process=%p", hProcess); + +#if ASSUAN_VERSION_NUMBER < 0x030000 + if (r_pid) + gpgrt_process_ctl (process, GPGRT_PROCESS_GET_PROC_ID, r_pid); + + /* We don't need to wait for the process. */ + close_handle (hProcess); +#else + if (r_pid) + *r_pid = (assuan_pid_t)hProcess; + else + /* We don't need to wait for the process. */ + close_handle (hProcess); +#endif + + if (!(spawn_flags & IOSPAWN_FLAG_NOCLOSE)) + { + for (i = 0; fd_list[i].fd != -1; i++) + _gpgme_io_close (fd_list[i].fd); + } + + for (i = 0; fd_list[i].fd != -1; i++) + if (fd_list[i].dup_to == -1) + TRACE_LOG ("fd[%i] = 0x%x -> %p", i, fd_list[i].fd, + fd_list[i].peer_name); + else + TRACE_LOG ("fd[%i] = 0x%x -> %p (std%s)", i, fd_list[i].fd, + fd_list[i].peer_name, (fd_list[i].dup_to == 0) ? "in" : + ((fd_list[i].dup_to == 1) ? "out" : "err")); + + gpgrt_process_release (process); + + return TRACE_SYSRES (0); +} +#endif + int _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags, @@ -1363,13 +1597,14 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags, int tmp_fd; char *tmp_name; const char *spawnhelper; - static int spawn_warning_shown = 0; TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_spawn", path, "path=%s", path); - (void)atfork; - (void)atforkvalue; + spawnhelper = _gpgme_get_w32spawn_path (); + if (!spawnhelper) + return _gpgme_io_spawn_sans_helper (path, argv, flags, + fd_list, atfork, atforkvalue, r_pid); i = 0; while (argv[i]) @@ -1391,7 +1626,7 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags, TRACE_LOG ("tmp_name = %s", tmp_name); args = calloc (2 + i + 1, sizeof (*args)); - args[0] = (char *) _gpgme_get_w32spawn_path (); + args[0] = (char *)spawnhelper; args[1] = tmp_name; args[2] = (char *)path; memcpy (&args[3], &argv[1], i * sizeof (*args)); @@ -1422,39 +1657,6 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags, if ((flags & IOSPAWN_FLAG_DETACHED)) cr_flags |= DETACHED_PROCESS; cr_flags |= GetPriorityClass (GetCurrentProcess ()); - spawnhelper = _gpgme_get_w32spawn_path (); - if (!spawnhelper) - { - /* This is a common mistake for new users of gpgme not to include - gpgme-w32spawn.exe with their binary. So we want to make - this transparent to developers. If users have somehow messed - up their installation this should also be properly communicated - as otherwise calls to gnupg will result in unsupported protocol - errors that do not explain a lot. */ - if (!spawn_warning_shown) - { - char *msg; - gpgrt_asprintf (&msg, "gpgme-w32spawn.exe was not found in the " - "detected installation directory of GpgME" - "\n\t\"%s\"\n\n" - "Crypto operations will not work.\n\n" - "If you see this it indicates a problem " - "with your installation.\n" - "Please report the problem to your " - "distributor of GpgME.\n\n" - "Developer's Note: The install dir can be " - "manually set with: gpgme_set_global_flag", - _gpgme_get_inst_dir ()); - MessageBoxA (NULL, msg, "GpgME not installed correctly", MB_OK); - gpgrt_free (msg); - spawn_warning_shown = 1; - } - gpg_err_set_errno (EIO); - close (tmp_fd); - DeleteFileA (tmp_name); - free (tmp_name); - return TRACE_SYSRES (-1); - } if (!_gpgme_create_process_utf8 (spawnhelper, arg_string, &sec_attr, /* process security attributes */ -- cgit