aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNIIBE Yutaka <[email protected]>2026-01-09 05:42:05 +0100
committerNIIBE Yutaka <[email protected]>2026-01-09 05:46:31 +0100
commit64f80903ec9bc8897f9b2dcebb7068c02ff9ae07 (patch)
tree250ad360bbed82f17b84a1a0c228d980603ee649
parentw32: Use SetHandleInformation for _gpgme_io_pipe. (diff)
downloadgpgme-64f80903ec9bc8897f9b2dcebb7068c02ff9ae07.tar.gz
gpgme-64f80903ec9bc8897f9b2dcebb7068c02ff9ae07.zip
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 <[email protected]>
-rw-r--r--src/w32-io.c276
1 files 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 */