* posix_spawn[p]: implement for native Windows
@ 2020-12-24 22:03 Bruno Haible
0 siblings, 0 replies; only message in thread
From: Bruno Haible @ 2020-12-24 22:03 UTC (permalink / raw)
To: bug-gnulib
[-- Attachment #1: Type: text/plain, Size: 3673 bytes --]
The attached patches implement posix_spawn and posix_spawnp for native
Windows.
To my knowledge, it's a world's first. While the posix_spawn facility
was designed to be portable to other operating systems [1], the mingw
people did not and do not provide it.
[1] https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn.html
section "RATIONALE"
2020-12-24 Bruno Haible <bruno@clisp.org>
posix_spawn-internal: Implement for native Windows.
* lib/spawni.c (grow_inheritable_handles, shrink_inheritable_handles,
close_inheritable_handles, memiszero, sigisempty, open_handle, do_open,
do_dup2, do_close): New functions.
(__spawni): Implement on native Windows.
* modules/posix_spawn-internal (Depends-on): Add filename,
concat-filename, findprog-in, malloca, windows-spawn.
* doc/posix-functions/posix_spawn.texi: Update.
* doc/posix-functions/posix_spawnp.texi: Likewise.
2020-12-24 Bruno Haible <bruno@clisp.org>
windows-spawn: Export another auxiliary function.
* lib/windows-spawn.h (convert_CreateProcess_error): New declaration.
* lib/windows-spawn.c (convert_CreateProcess_error): New function,
extracted from spawnpvech.
(spawnpvech): Use it.
2020-12-24 Bruno Haible <bruno@clisp.org>
windows-spawn: Export some more auxiliary functions.
* lib/windows-spawn.h: Include <stdbool.h>.
(struct inheritable_handles): New type.
(init_inheritable_handles, compose_handles_block,
free_inheritable_handles): New declarations.
* lib/windows-spawn.c (init_inheritable_handles, compose_handles_block):
New functions, based on spawnvech.
(free_inheritable_handles): New function.
(spawnpvech): Use them.
2020-12-24 Bruno Haible <bruno@clisp.org>
windows-spawn: Export another auxiliary function.
* lib/windows-spawn.h (compose_envblock): New declaration.
* lib/windows-spawn.c (compose_envblock): New function, extracted from
spawnpvech.
(spawnpvech): Use it.
2020-12-24 Bruno Haible <bruno@clisp.org>
windows-spawn: Export an auxiliary function.
* lib/windows-spawn.h (compose_command): New declaration.
* lib/windows-spawn.c (compose_command): New function, extracted from
spawnpvech.
(spawnpvech): Use it.
2020-12-24 Bruno Haible <bruno@clisp.org>
posix_spawn* tests: Add support for native Windows.
* tests/test-posix_spawn-open1.c (DATA_FILENAME): Treat native Windows
like Cygwin.
* tests/test-posix_spawn-dup2-stdin.c (main): Don't assume the signals
SIGHUP and SIGPIPE. On native Windows, don't call
posix_spawnattr_setsigmask.
* tests/test-posix_spawn-dup2-stdout.c (main): Likewise.
* tests/test-posix_spawn-fchdir.c (main): Likewise.
* tests/test-posix_spawn-chdir.c (test): Likewise. Accept the child
output from Cygwin's 'pwd' program.
* tests/test-posix_spawn-script.c (main): On native Windows, skip the
executable-shell-script part of the test.
* tests/test-posix_spawnp-script.c (main): Likewise.
* modules/posix_spawn-tests (Depends-on): Add freopen, waitpid.
(configure.ac): Don't define the POSIX_SPAWN_PORTED conditional.
(Makefile.am): Don't test the POSIX_SPAWN_PORTED conditional.
* modules/posix_spawnp-tests (Depends-on): Add waitpid.
(configure.ac): Don't define the POSIX_SPAWN_PORTED conditional.
(Makefile.am): Don't test the POSIX_SPAWN_PORTED conditional.
* modules/posix_spawn_file_actions_addchdir-tests (Makefile.am): Don't
test the POSIX_SPAWN_PORTED conditional.
* modules/posix_spawn_file_actions_addfchdir-tests (configure.ac):
Define the POSIX_SPAWN_PORTED conditional here.
2020-12-24 Bruno Haible <bruno@clisp.org>
sh-filename: Add support for native Windows.
* m4/sh-filename.m4 (gl_SH_FILENAME): Treat native Windows like Cygwin.
[-- Attachment #2: 0001-sh-filename-Add-support-for-native-Windows.patch --]
[-- Type: text/x-patch, Size: 1742 bytes --]
From c9ed8d9a58bae5a90fc1410232354cf8e27e2e3c Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Thu, 24 Dec 2020 22:13:49 +0100
Subject: [PATCH 1/7] sh-filename: Add support for native Windows.
* m4/sh-filename.m4 (gl_SH_FILENAME): Treat native Windows like Cygwin.
---
ChangeLog | 5 +++++
m4/sh-filename.m4 | 6 ++++--
2 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index 5b05bb3..d93aa47 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2020-12-24 Bruno Haible <bruno@clisp.org>
+
+ sh-filename: Add support for native Windows.
+ * m4/sh-filename.m4 (gl_SH_FILENAME): Treat native Windows like Cygwin.
+
2020-12-24 Paul Eggert <eggert@cs.ucla.edu>
careadlinkat: improve warning line number
diff --git a/m4/sh-filename.m4 b/m4/sh-filename.m4
index f7b3154..63bbd67 100644
--- a/m4/sh-filename.m4
+++ b/m4/sh-filename.m4
@@ -1,4 +1,4 @@
-# sh-filename.m4 serial 2
+# sh-filename.m4 serial 3
dnl Copyright (C) 2018-2020 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
@@ -10,8 +10,10 @@ AC_DEFUN([gl_SH_FILENAME],
[
AH_VERBATIM([SH_FILENAME],
[/* File name of the Bourne shell. */
-#if defined __CYGWIN__ || defined __ANDROID__
+#if (defined _WIN32 && !defined __CYGWIN__) || defined __CYGWIN__ || defined __ANDROID__
/* Omit the directory part because
+ - For native Windows programs in a Cygwin environment, the Cygwin mounts
+ are not visible.
- For 32-bit Cygwin programs in a 64-bit Cygwin environment, the Cygwin
mounts are not visible.
- On Android, /bin/sh does not exist. It's /system/bin/sh instead. */
--
2.7.4
[-- Attachment #3: 0002-posix_spawn-tests-Add-support-for-native-Windows.patch --]
[-- Type: text/x-patch, Size: 16415 bytes --]
From 8cf7746a2b5c5fe50e8369951619a87ecb3786bd Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Thu, 24 Dec 2020 22:14:38 +0100
Subject: [PATCH 2/7] posix_spawn* tests: Add support for native Windows.
* tests/test-posix_spawn-open1.c (DATA_FILENAME): Treat native Windows
like Cygwin.
* tests/test-posix_spawn-dup2-stdin.c (main): Don't assume the signals
SIGHUP and SIGPIPE. On native Windows, don't call
posix_spawnattr_setsigmask.
* tests/test-posix_spawn-dup2-stdout.c (main): Likewise.
* tests/test-posix_spawn-fchdir.c (main): Likewise.
* tests/test-posix_spawn-chdir.c (test): Likewise. Accept the child
output from Cygwin's 'pwd' program.
* tests/test-posix_spawn-script.c (main): On native Windows, skip the
executable-shell-script part of the test.
* tests/test-posix_spawnp-script.c (main): Likewise.
* modules/posix_spawn-tests (Depends-on): Add freopen, waitpid.
(configure.ac): Don't define the POSIX_SPAWN_PORTED conditional.
(Makefile.am): Don't test the POSIX_SPAWN_PORTED conditional.
* modules/posix_spawnp-tests (Depends-on): Add waitpid.
(configure.ac): Don't define the POSIX_SPAWN_PORTED conditional.
(Makefile.am): Don't test the POSIX_SPAWN_PORTED conditional.
* modules/posix_spawn_file_actions_addchdir-tests (Makefile.am): Don't
test the POSIX_SPAWN_PORTED conditional.
* modules/posix_spawn_file_actions_addfchdir-tests (configure.ac):
Define the POSIX_SPAWN_PORTED conditional here.
---
ChangeLog | 26 +++++++++++++++++
modules/posix_spawn-tests | 12 ++------
modules/posix_spawn_file_actions_addchdir-tests | 13 ++++-----
modules/posix_spawn_file_actions_addfchdir-tests | 8 ++++++
modules/posix_spawnp-tests | 11 +-------
tests/test-posix_spawn-chdir.c | 36 ++++++++++++++++++------
tests/test-posix_spawn-dup2-stdin.c | 11 +++++++-
tests/test-posix_spawn-dup2-stdout.c | 13 +++++++--
tests/test-posix_spawn-fchdir.c | 13 +++++++--
tests/test-posix_spawn-open1.c | 5 ++--
tests/test-posix_spawn-script.c | 7 +++++
tests/test-posix_spawnp-script.c | 7 +++++
12 files changed, 120 insertions(+), 42 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index d93aa47..b716a26 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,31 @@
2020-12-24 Bruno Haible <bruno@clisp.org>
+ posix_spawn* tests: Add support for native Windows.
+ * tests/test-posix_spawn-open1.c (DATA_FILENAME): Treat native Windows
+ like Cygwin.
+ * tests/test-posix_spawn-dup2-stdin.c (main): Don't assume the signals
+ SIGHUP and SIGPIPE. On native Windows, don't call
+ posix_spawnattr_setsigmask.
+ * tests/test-posix_spawn-dup2-stdout.c (main): Likewise.
+ * tests/test-posix_spawn-fchdir.c (main): Likewise.
+ * tests/test-posix_spawn-chdir.c (test): Likewise. Accept the child
+ output from Cygwin's 'pwd' program.
+ * tests/test-posix_spawn-script.c (main): On native Windows, skip the
+ executable-shell-script part of the test.
+ * tests/test-posix_spawnp-script.c (main): Likewise.
+ * modules/posix_spawn-tests (Depends-on): Add freopen, waitpid.
+ (configure.ac): Don't define the POSIX_SPAWN_PORTED conditional.
+ (Makefile.am): Don't test the POSIX_SPAWN_PORTED conditional.
+ * modules/posix_spawnp-tests (Depends-on): Add waitpid.
+ (configure.ac): Don't define the POSIX_SPAWN_PORTED conditional.
+ (Makefile.am): Don't test the POSIX_SPAWN_PORTED conditional.
+ * modules/posix_spawn_file_actions_addchdir-tests (Makefile.am): Don't
+ test the POSIX_SPAWN_PORTED conditional.
+ * modules/posix_spawn_file_actions_addfchdir-tests (configure.ac):
+ Define the POSIX_SPAWN_PORTED conditional here.
+
+2020-12-24 Bruno Haible <bruno@clisp.org>
+
sh-filename: Add support for native Windows.
* m4/sh-filename.m4 (gl_SH_FILENAME): Treat native Windows like Cygwin.
diff --git a/modules/posix_spawn-tests b/modules/posix_spawn-tests
index 06a8434..ee505c0 100644
--- a/modules/posix_spawn-tests
+++ b/modules/posix_spawn-tests
@@ -18,19 +18,12 @@ unistd
sys_wait
environ
fflush
+freopen
+waitpid
configure.ac:
-AC_EGREP_CPP([notposix], [[
-#if defined _MSC_VER || defined __MINGW32__
- notposix
-#endif
- ]],
- [posix_spawn_ported=no],
- [posix_spawn_ported=yes])
-AM_CONDITIONAL([POSIX_SPAWN_PORTED], [test $posix_spawn_ported = yes])
Makefile.am:
-if POSIX_SPAWN_PORTED
TESTS += \
test-posix_spawn-open1 \
test-posix_spawn-open2 \
@@ -44,4 +37,3 @@ check_PROGRAMS += \
test-posix_spawn-inherit1 \
test-posix_spawn-script
test_posix_spawn_script_CPPFLAGS = $(AM_CPPFLAGS) -DSRCDIR=\"$(srcdir)/\"
-endif
diff --git a/modules/posix_spawn_file_actions_addchdir-tests b/modules/posix_spawn_file_actions_addchdir-tests
index 400c1f7..1c56b1c 100644
--- a/modules/posix_spawn_file_actions_addchdir-tests
+++ b/modules/posix_spawn_file_actions_addchdir-tests
@@ -12,10 +12,9 @@ findprog
configure.ac:
Makefile.am:
-TESTS += test-posix_spawn_file_actions_addchdir
-check_PROGRAMS += test-posix_spawn_file_actions_addchdir
-
-if POSIX_SPAWN_PORTED
-TESTS += test-posix_spawn-chdir
-check_PROGRAMS += test-posix_spawn-chdir
-endif
+TESTS += \
+ test-posix_spawn_file_actions_addchdir \
+ test-posix_spawn-chdir
+check_PROGRAMS += \
+ test-posix_spawn_file_actions_addchdir \
+ test-posix_spawn-chdir
diff --git a/modules/posix_spawn_file_actions_addfchdir-tests b/modules/posix_spawn_file_actions_addfchdir-tests
index 0d8c990..45f4230 100644
--- a/modules/posix_spawn_file_actions_addfchdir-tests
+++ b/modules/posix_spawn_file_actions_addfchdir-tests
@@ -10,6 +10,14 @@ posix_spawnp-tests
findprog
configure.ac:
+AC_EGREP_CPP([notposix], [[
+#if defined _MSC_VER || defined __MINGW32__
+ notposix
+#endif
+ ]],
+ [posix_spawn_ported=no],
+ [posix_spawn_ported=yes])
+AM_CONDITIONAL([POSIX_SPAWN_PORTED], [test $posix_spawn_ported = yes])
Makefile.am:
TESTS += test-posix_spawn_file_actions_addfchdir
diff --git a/modules/posix_spawnp-tests b/modules/posix_spawnp-tests
index 887569c..e84d89d 100644
--- a/modules/posix_spawnp-tests
+++ b/modules/posix_spawnp-tests
@@ -26,19 +26,11 @@ dup
environ
sh-filename
sigprocmask
+waitpid
configure.ac:
-AC_EGREP_CPP([notposix], [[
-#if defined _MSC_VER || defined __MINGW32__
- notposix
-#endif
- ]],
- [posix_spawn_ported=no],
- [posix_spawn_ported=yes])
-AM_CONDITIONAL([POSIX_SPAWN_PORTED], [test $posix_spawn_ported = yes])
Makefile.am:
-if POSIX_SPAWN_PORTED
TESTS += \
test-posix_spawn-dup2-stdout \
test-posix_spawn-dup2-stdin \
@@ -63,4 +55,3 @@ test-posix_spawn-dup2-stdin.sh: test-posix_spawn-dup2-stdin.in.sh
MOSTLYCLEANFILES += test-posix_spawn-dup2-stdin.sh test-posix_spawn-dup2-stdin.sh-t
test_posix_spawnp_script_CPPFLAGS = $(AM_CPPFLAGS) -DSRCDIR=\"$(srcdir)/\"
-endif
diff --git a/tests/test-posix_spawn-chdir.c b/tests/test-posix_spawn-chdir.c
index 91a5497..62c60ee 100644
--- a/tests/test-posix_spawn-chdir.c
+++ b/tests/test-posix_spawn-chdir.c
@@ -64,6 +64,7 @@ test (const char *pwd_prog)
int fd;
FILE *fp;
char line[80];
+ int line_len;
int status;
int exitstatus;
@@ -76,8 +77,12 @@ test (const char *pwd_prog)
sigemptyset (&fatal_signal_set);
sigaddset (&fatal_signal_set, SIGINT);
sigaddset (&fatal_signal_set, SIGTERM);
+ #ifdef SIGHUP
sigaddset (&fatal_signal_set, SIGHUP);
+ #endif
+ #ifdef SIGPIPE
sigaddset (&fatal_signal_set, SIGPIPE);
+ #endif
sigprocmask (SIG_BLOCK, &fatal_signal_set, NULL);
actions_allocated = false;
attrs_allocated = false;
@@ -90,8 +95,13 @@ test (const char *pwd_prog)
|| (err = posix_spawn_file_actions_addchdir (&actions, "/")) != 0
|| (err = posix_spawnattr_init (&attrs)) != 0
|| (attrs_allocated = true,
+ #if defined _WIN32 && !defined __CYGWIN__
+ 0
+ #else
(err = posix_spawnattr_setsigmask (&attrs, &blocked_signals)) != 0
- || (err = posix_spawnattr_setflags (&attrs, POSIX_SPAWN_SETSIGMASK)) != 0)
+ || (err = posix_spawnattr_setflags (&attrs, POSIX_SPAWN_SETSIGMASK)) != 0
+ #endif
+ )
|| (err = posix_spawnp (&child, pwd_prog, &actions, &attrs, argv, environ)) != 0))
{
if (actions_allocated)
@@ -108,22 +118,32 @@ test (const char *pwd_prog)
sigprocmask (SIG_UNBLOCK, &fatal_signal_set, NULL);
close (ifd[1]);
fd = ifd[0];
- fp = fdopen (fd, "r");
+ fp = fdopen (fd, "rb");
if (fp == NULL)
{
fprintf (stderr, "fdopen() failed\n");
exit (1);
}
- if (fread (line, 1, 80, fp) < 2)
+ line_len = fread (line, 1, 80, fp);
+ if (line_len < 2)
{
fprintf (stderr, "could not read expected output\n");
exit (1);
}
- if (memcmp (line, "/\n", 2) != 0)
- {
- fprintf (stderr, "read output is not the expected output");
- exit (1);
- }
+ if (!(line_len == 2 && memcmp (line, "/\n", 2) == 0))
+#if defined _WIN32 && !defined __CYGWIN__
+ /* If the pwd program is Cygwin's pwd, its output in the root directory is
+ "/cygdrive/N", where N is a lowercase letter. */
+ if (!(line_len > 11
+ && memcmp (line, "/cygdrive/", 10) == 0
+ && line[10] >= 'a' && line[10] <= 'z'
+ && ((line_len == 12 && line[11] == '\n')
+ || (line_len == 13 && line[11] == '\r' && line[12] == '\n'))))
+#endif
+ {
+ fprintf (stderr, "read output is not the expected output\n");
+ exit (1);
+ }
fclose (fp);
status = 0;
while (waitpid (child, &status, 0) != child)
diff --git a/tests/test-posix_spawn-dup2-stdin.c b/tests/test-posix_spawn-dup2-stdin.c
index 8af1e3f..fe039bf 100644
--- a/tests/test-posix_spawn-dup2-stdin.c
+++ b/tests/test-posix_spawn-dup2-stdin.c
@@ -76,8 +76,12 @@ main ()
sigemptyset (&fatal_signal_set);
sigaddset (&fatal_signal_set, SIGINT);
sigaddset (&fatal_signal_set, SIGTERM);
+ #ifdef SIGHUP
sigaddset (&fatal_signal_set, SIGHUP);
+ #endif
+ #ifdef SIGPIPE
sigaddset (&fatal_signal_set, SIGPIPE);
+ #endif
sigprocmask (SIG_BLOCK, &fatal_signal_set, NULL);
actions_allocated = false;
attrs_allocated = false;
@@ -88,8 +92,13 @@ main ()
|| (err = posix_spawn_file_actions_addclose (&actions, ofd[1])) != 0
|| (err = posix_spawnattr_init (&attrs)) != 0
|| (attrs_allocated = true,
+ #if defined _WIN32 && !defined __CYGWIN__
+ 0
+ #else
(err = posix_spawnattr_setsigmask (&attrs, &blocked_signals)) != 0
- || (err = posix_spawnattr_setflags (&attrs, POSIX_SPAWN_SETSIGMASK)) != 0)
+ || (err = posix_spawnattr_setflags (&attrs, POSIX_SPAWN_SETSIGMASK)) != 0
+ #endif
+ )
|| (err = posix_spawnp (&child, BOURNE_SHELL, &actions, &attrs, argv, environ)) != 0))
{
if (actions_allocated)
diff --git a/tests/test-posix_spawn-dup2-stdout.c b/tests/test-posix_spawn-dup2-stdout.c
index c67e143..ce09844 100644
--- a/tests/test-posix_spawn-dup2-stdout.c
+++ b/tests/test-posix_spawn-dup2-stdout.c
@@ -98,8 +98,12 @@ main ()
sigemptyset (&fatal_signal_set);
sigaddset (&fatal_signal_set, SIGINT);
sigaddset (&fatal_signal_set, SIGTERM);
+ #ifdef SIGHUP
sigaddset (&fatal_signal_set, SIGHUP);
+ #endif
+ #ifdef SIGPIPE
sigaddset (&fatal_signal_set, SIGPIPE);
+ #endif
sigprocmask (SIG_BLOCK, &fatal_signal_set, NULL);
actions_allocated = false;
attrs_allocated = false;
@@ -111,8 +115,13 @@ main ()
|| (err = posix_spawn_file_actions_addopen (&actions, STDIN_FILENO, "/dev/null", O_RDONLY, 0)) != 0
|| (err = posix_spawnattr_init (&attrs)) != 0
|| (attrs_allocated = true,
+ #if defined _WIN32 && !defined __CYGWIN__
+ 0
+ #else
(err = posix_spawnattr_setsigmask (&attrs, &blocked_signals)) != 0
- || (err = posix_spawnattr_setflags (&attrs, POSIX_SPAWN_SETSIGMASK)) != 0)
+ || (err = posix_spawnattr_setflags (&attrs, POSIX_SPAWN_SETSIGMASK)) != 0
+ #endif
+ )
|| (err = posix_spawnp (&child, BOURNE_SHELL, &actions, &attrs, argv, environ)) != 0))
{
if (actions_allocated)
@@ -142,7 +151,7 @@ main ()
}
if (memcmp (line, "Halle Potta", 11) != 0)
{
- fprintf (stderr, "read output is not the expected output");
+ fprintf (stderr, "read output is not the expected output\n");
exit (1);
}
fclose (fp);
diff --git a/tests/test-posix_spawn-fchdir.c b/tests/test-posix_spawn-fchdir.c
index c5ca2a3..3206ad5 100644
--- a/tests/test-posix_spawn-fchdir.c
+++ b/tests/test-posix_spawn-fchdir.c
@@ -83,8 +83,12 @@ test (const char *pwd_prog)
sigemptyset (&fatal_signal_set);
sigaddset (&fatal_signal_set, SIGINT);
sigaddset (&fatal_signal_set, SIGTERM);
+ #ifdef SIGHUP
sigaddset (&fatal_signal_set, SIGHUP);
+ #endif
+ #ifdef SIGPIPE
sigaddset (&fatal_signal_set, SIGPIPE);
+ #endif
sigprocmask (SIG_BLOCK, &fatal_signal_set, NULL);
actions_allocated = false;
attrs_allocated = false;
@@ -97,8 +101,13 @@ test (const char *pwd_prog)
|| (err = posix_spawn_file_actions_addfchdir (&actions, rootfd)) != 0
|| (err = posix_spawnattr_init (&attrs)) != 0
|| (attrs_allocated = true,
+ #if defined _WIN32 && !defined __CYGWIN__
+ 0
+ #else
(err = posix_spawnattr_setsigmask (&attrs, &blocked_signals)) != 0
- || (err = posix_spawnattr_setflags (&attrs, POSIX_SPAWN_SETSIGMASK)) != 0)
+ || (err = posix_spawnattr_setflags (&attrs, POSIX_SPAWN_SETSIGMASK)) != 0
+ #endif
+ )
|| (err = posix_spawnp (&child, pwd_prog, &actions, &attrs, argv, environ)) != 0))
{
if (actions_allocated)
@@ -128,7 +137,7 @@ test (const char *pwd_prog)
}
if (memcmp (line, "/\n", 2) != 0)
{
- fprintf (stderr, "read output is not the expected output");
+ fprintf (stderr, "read output is not the expected output\n");
exit (1);
}
fclose (fp);
diff --git a/tests/test-posix_spawn-open1.c b/tests/test-posix_spawn-open1.c
index e08c113..648fc1d 100644
--- a/tests/test-posix_spawn-open1.c
+++ b/tests/test-posix_spawn-open1.c
@@ -40,8 +40,9 @@ SIGNATURE_CHECK (posix_spawn, int, (pid_t *, char const *,
#define CHILD_PROGRAM_FILENAME "test-posix_spawn-open1"
#define DATA_FILENAME "t!#$%&'()*+,-;=?@[\\]^_`{|}~.tmp"
-/* On Cygwin, '*' '?' '\\' '|' cannot be used in file names. */
-#if defined __CYGWIN__
+/* On Windows (including Cygwin), '*' '?' '\\' '|' cannot be used in file
+ names. */
+#if defined _WIN32 || defined __CYGWIN__
# undef DATA_FILENAME
# define DATA_FILENAME "t!#$%&'()+,-;=@[]^_`{}~.tmp"
#endif
diff --git a/tests/test-posix_spawn-script.c b/tests/test-posix_spawn-script.c
index e17c3b8..6fa8e48 100644
--- a/tests/test-posix_spawn-script.c
+++ b/tests/test-posix_spawn-script.c
@@ -90,6 +90,12 @@ main ()
}
}
+#if defined _WIN32 && !defined __CYGWIN__
+ /* On native Windows, scripts - even with '#!' marker - are not executable.
+ Only .bat and .cmd files are. */
+ fprintf (stderr, "Skipping test: scripts are not executable on this platform.\n");
+ return 77;
+#else
{
const char *prog_path = SRCDIR "executable-shell-script";
const char *prog_argv[2] = { prog_path, NULL };
@@ -141,6 +147,7 @@ main ()
return 1;
}
}
+#endif
/* Clean up data file. */
unlink (DATA_FILENAME);
diff --git a/tests/test-posix_spawnp-script.c b/tests/test-posix_spawnp-script.c
index b48e791..c2c3e57 100644
--- a/tests/test-posix_spawnp-script.c
+++ b/tests/test-posix_spawnp-script.c
@@ -90,6 +90,12 @@ main ()
}
}
+#if defined _WIN32 && !defined __CYGWIN__
+ /* On native Windows, scripts - even with '#!' marker - are not executable.
+ Only .bat and .cmd files are. */
+ fprintf (stderr, "Skipping test: scripts are not executable on this platform.\n");
+ return 77;
+#else
{
const char *prog_path = SRCDIR "executable-shell-script";
const char *prog_argv[2] = { prog_path, NULL };
@@ -141,6 +147,7 @@ main ()
return 1;
}
}
+#endif
/* Clean up data file. */
unlink (DATA_FILENAME);
--
2.7.4
[-- Attachment #4: 0003-windows-spawn-Export-an-auxiliary-function.patch --]
[-- Type: text/x-patch, Size: 4293 bytes --]
From f8199b3ae12a94f79994e1c1e2389fe024ffa6fb Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Thu, 24 Dec 2020 22:18:10 +0100
Subject: [PATCH 3/7] windows-spawn: Export an auxiliary function.
* lib/windows-spawn.h (compose_command): New declaration.
* lib/windows-spawn.c (compose_command): New function, extracted from
spawnpvech.
(spawnpvech): Use it.
---
ChangeLog | 8 ++++++
lib/windows-spawn.c | 74 ++++++++++++++++++++++++++++++++---------------------
lib/windows-spawn.h | 7 +++++
3 files changed, 60 insertions(+), 29 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index b716a26..ba8a7d9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,13 @@
2020-12-24 Bruno Haible <bruno@clisp.org>
+ windows-spawn: Export an auxiliary function.
+ * lib/windows-spawn.h (compose_command): New declaration.
+ * lib/windows-spawn.c (compose_command): New function, extracted from
+ spawnpvech.
+ (spawnpvech): Use it.
+
+2020-12-24 Bruno Haible <bruno@clisp.org>
+
posix_spawn* tests: Add support for native Windows.
* tests/test-posix_spawn-open1.c (DATA_FILENAME): Treat native Windows
like Cygwin.
diff --git a/lib/windows-spawn.c b/lib/windows-spawn.c
index 0385b1c..cc29196 100644
--- a/lib/windows-spawn.c
+++ b/lib/windows-spawn.c
@@ -203,6 +203,47 @@ prepare_spawn (const char * const *argv, char **mem_to_free)
return new_argv;
}
+char *
+compose_command (const char * const *argv)
+{
+ /* Just concatenate the argv[] strings, separated by spaces. */
+ char *command;
+
+ /* Determine the size of the needed block of memory. */
+ size_t total_size = 0;
+ const char * const *ap;
+ const char *p;
+ for (ap = argv; (p = *ap) != NULL; ap++)
+ total_size += strlen (p) + 1;
+ size_t command_size = (total_size > 0 ? total_size : 1);
+
+ /* Allocate the block of memory. */
+ command = (char *) malloc (command_size);
+ if (command == NULL)
+ {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ /* Fill it. */
+ if (total_size > 0)
+ {
+ char *cp = command;
+ for (ap = argv; (p = *ap) != NULL; ap++)
+ {
+ size_t size = strlen (p) + 1;
+ memcpy (cp, p, size - 1);
+ cp += size;
+ cp[-1] = ' ';
+ }
+ cp[-1] = '\0';
+ }
+ else
+ *command = '\0';
+
+ return command;
+}
+
intptr_t
spawnpvech (int mode,
const char *progname, const char * const *argv,
@@ -227,35 +268,10 @@ spawnpvech (int mode,
if (resolved_progname == NULL)
return -1;
- /* Compose the command.
- Just concatenate the argv[] strings, separated by spaces. */
- char *command;
- {
- /* Determine the size of the needed block of memory. */
- size_t total_size = 0;
- const char * const *ap;
- const char *p;
- for (ap = argv; (p = *ap) != NULL; ap++)
- total_size += strlen (p) + 1;
- size_t command_size = (total_size > 0 ? total_size : 1);
- command = (char *) malloc (command_size);
- if (command == NULL)
- goto out_of_memory_1;
- if (total_size > 0)
- {
- char *cp = command;
- for (ap = argv; (p = *ap) != NULL; ap++)
- {
- size_t size = strlen (p) + 1;
- memcpy (cp, p, size - 1);
- cp += size;
- cp[-1] = ' ';
- }
- cp[-1] = '\0';
- }
- else
- *command = '\0';
- }
+ /* Compose the command. */
+ char *command = compose_command (argv);
+ if (command == NULL)
+ goto out_of_memory_1;
/* Copy *ENVP into a contiguous block of memory. */
char *envblock;
diff --git a/lib/windows-spawn.h b/lib/windows-spawn.h
index 77720d5..cb9d3fa 100644
--- a/lib/windows-spawn.h
+++ b/lib/windows-spawn.h
@@ -65,6 +65,13 @@
extern const char ** prepare_spawn (const char * const *argv,
char **mem_to_free);
+/* Composes the command to be passed to CreateProcess().
+ ARGV must contain appropriately quoted arguments, as returned by
+ prepare_spawn.
+ Returns a freshly allocated string. In case of memory allocation failure,
+ NULL is returned, with errno set. */
+extern char * compose_command (const char * const *argv);
+
/* Creates a subprocess.
MODE is either P_WAIT or P_NOWAIT.
PROGNAME is the program to invoke.
--
2.7.4
[-- Attachment #5: 0004-windows-spawn-Export-another-auxiliary-function.patch --]
[-- Type: text/x-patch, Size: 5914 bytes --]
From d1ae9c694e5418850c71884ccd22a293d9b3e29b Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Thu, 24 Dec 2020 22:18:18 +0100
Subject: [PATCH 4/7] windows-spawn: Export another auxiliary function.
* lib/windows-spawn.h (compose_envblock): New declaration.
* lib/windows-spawn.c (compose_envblock): New function, extracted from
spawnpvech.
(spawnpvech): Use it.
---
ChangeLog | 8 +++++
lib/windows-spawn.c | 96 +++++++++++++++++++++++++++++++++--------------------
lib/windows-spawn.h | 7 ++++
3 files changed, 75 insertions(+), 36 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index ba8a7d9..d140497 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,13 @@
2020-12-24 Bruno Haible <bruno@clisp.org>
+ windows-spawn: Export another auxiliary function.
+ * lib/windows-spawn.h (compose_envblock): New declaration.
+ * lib/windows-spawn.c (compose_envblock): New function, extracted from
+ spawnpvech.
+ (spawnpvech): Use it.
+
+2020-12-24 Bruno Haible <bruno@clisp.org>
+
windows-spawn: Export an auxiliary function.
* lib/windows-spawn.h (compose_command): New declaration.
* lib/windows-spawn.c (compose_command): New function, extracted from
diff --git a/lib/windows-spawn.c b/lib/windows-spawn.c
index cc29196..b0a6dda 100644
--- a/lib/windows-spawn.c
+++ b/lib/windows-spawn.c
@@ -244,6 +244,65 @@ compose_command (const char * const *argv)
return command;
}
+char *
+compose_envblock (const char * const *envp)
+{
+ /* This is a bit hairy, because we don't have a lock that would prevent other
+ threads from making modifications in ENVP. So, just make sure we don't
+ crash; but if other threads are making modifications, part of the result
+ may be wrong. */
+ retry:
+ {
+ /* Guess the size of the needed block of memory.
+ The guess will be exact if other threads don't make modifications. */
+ size_t total_size = 0;
+ const char * const *ep;
+ const char *p;
+ for (ep = envp; (p = *ep) != NULL; ep++)
+ total_size += strlen (p) + 1;
+ size_t envblock_size = total_size;
+
+ /* Allocate the block of memory. */
+ char *envblock = (char *) malloc (envblock_size + 1);
+ if (envblock == NULL)
+ {
+ errno = ENOMEM;
+ return NULL;
+ }
+ size_t envblock_used = 0;
+ for (ep = envp; (p = *ep) != NULL; ep++)
+ {
+ size_t size = strlen (p) + 1;
+ if (envblock_used + size > envblock_size)
+ {
+ /* Other threads did modifications. Need more memory. */
+ envblock_size += envblock_size / 2;
+ if (envblock_used + size > envblock_size)
+ envblock_size = envblock_used + size;
+
+ char *new_envblock = (char *) realloc (envblock, envblock_size + 1);
+ if (new_envblock == NULL)
+ {
+ free (envblock);
+ errno = ENOMEM;
+ return NULL;
+ }
+ envblock = new_envblock;
+ }
+ memcpy (envblock + envblock_used, p, size);
+ envblock_used += size;
+ if (envblock[envblock_used - 1] != '\0')
+ {
+ /* Other threads did modifications. Restart. */
+ free (envblock);
+ goto retry;
+ }
+ }
+ envblock[envblock_used] = '\0';
+ return envblock;
+ }
+}
+
intptr_t
spawnpvech (int mode,
const char *progname, const char * const *argv,
@@ -278,45 +337,10 @@ spawnpvech (int mode,
if (envp == NULL)
envblock = NULL;
else
- retry:
{
- /* Guess the size of the needed block of memory.
- The guess will be exact if other threads don't make modifications. */
- size_t total_size = 0;
- const char * const *ep;
- const char *p;
- for (ep = envp; (p = *ep) != NULL; ep++)
- total_size += strlen (p) + 1;
- size_t envblock_size = total_size;
- envblock = (char *) malloc (envblock_size + 1);
+ envblock = compose_envblock (envp);
if (envblock == NULL)
goto out_of_memory_2;
- size_t envblock_used = 0;
- for (ep = envp; (p = *ep) != NULL; ep++)
- {
- size_t size = strlen (p) + 1;
- if (envblock_used + size > envblock_size)
- {
- /* Other threads did modifications. Need more memory. */
- envblock_size += envblock_size / 2;
- if (envblock_used + size > envblock_size)
- envblock_size = envblock_used + size;
-
- char *new_envblock = (char *) realloc (envblock, envblock_size + 1);
- if (new_envblock == NULL)
- goto out_of_memory_3;
- envblock = new_envblock;
- }
- memcpy (envblock + envblock_used, p, size);
- envblock_used += size;
- if (envblock[envblock_used - 1] != '\0')
- {
- /* Other threads did modifications. Restart. */
- free (envblock);
- goto retry;
- }
- }
- envblock[envblock_used] = '\0';
}
/* CreateProcess
diff --git a/lib/windows-spawn.h b/lib/windows-spawn.h
index cb9d3fa..90f45e1 100644
--- a/lib/windows-spawn.h
+++ b/lib/windows-spawn.h
@@ -72,6 +72,13 @@ extern const char ** prepare_spawn (const char * const *argv,
NULL is returned, with errno set. */
extern char * compose_command (const char * const *argv);
+/* Composes the block of memory that contains the environment variables.
+ ENVP must contain an environment (a NULL-terminated array of string of the
+ form VARIABLE=VALUE).
+ Returns a freshly allocated block of memory. In case of memory allocation
+ failure, NULL is returned, with errno set. */
+extern char * compose_envblock (const char * const *envp);
+
/* Creates a subprocess.
MODE is either P_WAIT or P_NOWAIT.
PROGNAME is the program to invoke.
--
2.7.4
[-- Attachment #6: 0005-windows-spawn-Export-some-more-auxiliary-functions.patch --]
[-- Type: text/x-patch, Size: 20793 bytes --]
From 2f1b8207e13b4bd1ca916626f25d8ac18bbdff33 Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Thu, 24 Dec 2020 22:18:21 +0100
Subject: [PATCH 5/7] windows-spawn: Export some more auxiliary functions.
* lib/windows-spawn.h: Include <stdbool.h>.
(struct inheritable_handles): New type.
(init_inheritable_handles, compose_handles_block,
free_inheritable_handles): New declarations.
* lib/windows-spawn.c (init_inheritable_handles, compose_handles_block):
New functions, based on spawnvech.
(free_inheritable_handles): New function.
(spawnpvech): Use them.
---
ChangeLog | 12 ++
lib/windows-spawn.c | 394 ++++++++++++++++++++++++++++++++++------------------
lib/windows-spawn.h | 43 ++++++
3 files changed, 311 insertions(+), 138 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index d140497..39629f3 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,17 @@
2020-12-24 Bruno Haible <bruno@clisp.org>
+ windows-spawn: Export some more auxiliary functions.
+ * lib/windows-spawn.h: Include <stdbool.h>.
+ (struct inheritable_handles): New type.
+ (init_inheritable_handles, compose_handles_block,
+ free_inheritable_handles): New declarations.
+ * lib/windows-spawn.c (init_inheritable_handles, compose_handles_block):
+ New functions, based on spawnvech.
+ (free_inheritable_handles): New function.
+ (spawnpvech): Use them.
+
+2020-12-24 Bruno Haible <bruno@clisp.org>
+
windows-spawn: Export another auxiliary function.
* lib/windows-spawn.h (compose_envblock): New declaration.
* lib/windows-spawn.c (compose_envblock): New function, extracted from
diff --git a/lib/windows-spawn.c b/lib/windows-spawn.c
index b0a6dda..a2f63b9 100644
--- a/lib/windows-spawn.c
+++ b/lib/windows-spawn.c
@@ -303,6 +303,229 @@ compose_envblock (const char * const *envp)
}
}
+int
+init_inheritable_handles (struct inheritable_handles *inh_handles,
+ bool duplicate)
+{
+ /* Determine the minimal count of handles we need to care about. */
+ size_t handles_count;
+ {
+ /* _getmaxstdio
+ <https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/getmaxstdio>
+ Default value is 512. */
+ unsigned int fdmax = _getmaxstdio ();
+ if (fdmax < 3)
+ fdmax = 3;
+ for (; fdmax > 3; fdmax--)
+ {
+ unsigned int fd = fdmax - 1;
+ /* _get_osfhandle
+ <https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/get-osfhandle> */
+ HANDLE handle = (HANDLE) _get_osfhandle (fd);
+ if (handle != INVALID_HANDLE_VALUE)
+ {
+ DWORD hflags;
+ /* GetHandleInformation
+ <https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-gethandleinformation> */
+ if (GetHandleInformation (handle, &hflags))
+ {
+ if ((hflags & HANDLE_FLAG_INHERIT) != 0)
+ /* fd denotes an inheritable descriptor. */
+ break;
+ }
+ }
+ }
+ handles_count = fdmax;
+ }
+ /* Note: handles_count >= 3. */
+
+ /* Allocate the arrays. */
+ size_t handles_allocated = handles_count;
+ HANDLE *handles_array =
+ (HANDLE *) malloc (handles_allocated * sizeof (HANDLE));
+ if (handles_array == NULL)
+ {
+ errno = ENOMEM;
+ return -1;
+ }
+ unsigned char *flags_array =
+ (unsigned char *) malloc (handles_allocated * sizeof (unsigned char));
+ if (flags_array == NULL)
+ {
+ free (handles_array);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ /* Fill in the two arrays. */
+ {
+ HANDLE curr_process = (duplicate ? GetCurrentProcess () : INVALID_HANDLE_VALUE);
+ unsigned int fd;
+ for (fd = 0; fd < handles_count; fd++)
+ {
+ handles_array[fd] = INVALID_HANDLE_VALUE;
+ /* _get_osfhandle
+ <https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/get-osfhandle> */
+ HANDLE handle = (HANDLE) _get_osfhandle (fd);
+ if (handle != INVALID_HANDLE_VALUE)
+ {
+ DWORD hflags;
+ /* GetHandleInformation
+ <https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-gethandleinformation> */
+ if (GetHandleInformation (handle, &hflags))
+ {
+ if ((hflags & HANDLE_FLAG_INHERIT) != 0)
+ {
+ /* fd denotes an inheritable descriptor. */
+ if (duplicate)
+ {
+ if (!DuplicateHandle (curr_process, handle,
+ curr_process, &handles_array[fd],
+ 0, TRUE, DUPLICATE_SAME_ACCESS))
+ {
+ unsigned int i;
+ for (i = 0; i < fd; i++)
+ if (handles_array[i] != INVALID_HANDLE_VALUE)
+ CloseHandle (handles_array[i]);
+ free (flags_array);
+ free (handles_array);
+ errno = EBADF; /* arbitrary */
+ return -1;
+ }
+ }
+ else
+ handles_array[fd] = handle;
+
+ flags_array[fd] = 0;
+ }
+ }
+ }
+ }
+ }
+
+ /* Return the result. */
+ inh_handles->count = handles_count;
+ inh_handles->allocated = handles_allocated;
+ inh_handles->handles = handles_array;
+ inh_handles->flags = flags_array;
+ return 0;
+}
+
+int
+compose_handles_block (const struct inheritable_handles *inh_handles,
+ STARTUPINFO *sinfo)
+{
+ /* STARTUPINFO
+ <https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa> */
+ sinfo->dwFlags = STARTF_USESTDHANDLES;
+ sinfo->hStdInput = inh_handles->handles[0];
+ sinfo->hStdOutput = inh_handles->handles[1];
+ sinfo->hStdError = inh_handles->handles[2];
+
+ /* On newer versions of Windows, more file descriptors / handles than the
+ first three can be passed.
+ The format is as follows: Let N be an exclusive upper bound for the file
+ descriptors to be passed. Two arrays are constructed in memory:
+ - flags[0..N-1], of element type 'unsigned char',
+ - handles[0..N-1], of element type 'HANDLE' or 'intptr_t'.
+ For used entries, handles[i] is the handle, and flags[i] is a set of flags,
+ a combination of:
+ 1 for open file descriptors,
+ 64 for handles of type FILE_TYPE_CHAR,
+ 8 for handles of type FILE_TYPE_PIPE,
+ 32 for O_APPEND.
+ For unused entries - this may include any of the first three, since they
+ are already passed above -, handles[i] is INVALID_HANDLE_VALUE and flags[i]
+ is zero.
+ lpReserved2 now is a pointer to the concatenation (without padding) of:
+ - an 'unsigned int' whose value is N,
+ - the contents of the flags[0..N-1] array,
+ - the contents of the handles[0..N-1] array.
+ cbReserved2 is the size (in bytes) of the object at lpReserved2. */
+
+ size_t handles_count = inh_handles->count;
+
+ sinfo->cbReserved2 =
+ sizeof (unsigned int)
+ + handles_count * sizeof (unsigned char)
+ + handles_count * sizeof (HANDLE);
+ /* Add some padding, so that we can work with a properly aligned HANDLE
+ array. */
+ char *hblock = (char *) malloc (sinfo->cbReserved2 + (sizeof (HANDLE) - 1));
+ if (hblock == NULL)
+ {
+ errno = ENOMEM;
+ return -1;
+ }
+ unsigned char *flags = (unsigned char *) (hblock + sizeof (unsigned int));
+ char *handles = (char *) (flags + handles_count);
+ HANDLE *handles_aligned =
+ (HANDLE *) (((uintptr_t) handles + (sizeof (HANDLE) - 1))
+ & - (uintptr_t) sizeof (HANDLE));
+
+ * (unsigned int *) hblock = handles_count;
+ {
+ unsigned int fd;
+ for (fd = 0; fd < handles_count; fd++)
+ {
+ handles_aligned[fd] = INVALID_HANDLE_VALUE;
+ flags[fd] = 0;
+
+ HANDLE handle = inh_handles->handles[fd];
+ if (handle != INVALID_HANDLE_VALUE
+ /* The first three are possibly already passed above.
+ But they need to passed here as well, if they have some flags. */
+ && (fd >= 3 || inh_handles->flags[fd] != 0))
+ {
+ DWORD hflags;
+ /* GetHandleInformation
+ <https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-gethandleinformation> */
+ if (GetHandleInformation (handle, &hflags))
+ {
+ if ((hflags & HANDLE_FLAG_INHERIT) != 0)
+ {
+ /* fd denotes an inheritable descriptor. */
+ handles_aligned[fd] = handle;
+ /* On Microsoft Windows, it would be sufficient to set
+ flags[fd] = 1. But on ReactOS or Wine, adding the bit
+ that indicates the handle type may be necessary. So,
+ just do it everywhere. */
+ flags[fd] = 1 | inh_handles->flags[fd];
+ switch (GetFileType (handle))
+ {
+ case FILE_TYPE_CHAR:
+ flags[fd] |= 64;
+ break;
+ case FILE_TYPE_PIPE:
+ flags[fd] |= 8;
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ /* We shouldn't have any non-inheritable handles in
+ inh_handles->handles. */
+ abort ();
+ }
+ }
+ }
+ }
+ if (handles != (char *) handles_aligned)
+ memmove (handles, (char *) handles_aligned, handles_count * sizeof (HANDLE));
+
+ sinfo->lpReserved2 = (BYTE *) hblock;
+
+ return 0;
+}
+
+void
+free_inheritable_handles (struct inheritable_handles *inh_handles)
+{
+ free (inh_handles->flags);
+ free (inh_handles->handles);
+}
+
intptr_t
spawnpvech (int mode,
const char *progname, const char * const *argv,
@@ -343,10 +566,25 @@ spawnpvech (int mode,
goto out_of_memory_2;
}
+ /* Collect the inheritable handles. */
+ struct inheritable_handles inh_handles;
+ if (init_inheritable_handles (&inh_handles, false) < 0)
+ {
+ int saved_errno = errno;
+ if (envblock != NULL)
+ free (envblock);
+ free (command);
+ if (resolved_progname != progname)
+ free ((char *) resolved_progname);
+ errno = saved_errno;
+ return -1;
+ }
+ inh_handles.handles[0] = stdin_handle; inh_handles.flags[0] = 0;
+ inh_handles.handles[1] = stdout_handle; inh_handles.flags[1] = 0;
+ inh_handles.handles[2] = stderr_handle; inh_handles.flags[2] = 0;
+
/* CreateProcess
<https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa> */
- /* Regarding handle inheritance, see
- <https://docs.microsoft.com/en-us/windows/win32/sysinfo/handle-inheritance> */
/* <https://docs.microsoft.com/en-us/windows/win32/procthread/process-creation-flags> */
DWORD process_creation_flags = (mode == P_DETACH ? DETACHED_PROCESS : 0);
/* STARTUPINFO
@@ -356,130 +594,18 @@ spawnpvech (int mode,
sinfo.lpReserved = NULL;
sinfo.lpDesktop = NULL;
sinfo.lpTitle = NULL;
- sinfo.dwFlags = STARTF_USESTDHANDLES;
- sinfo.hStdInput = stdin_handle;
- sinfo.hStdOutput = stdout_handle;
- sinfo.hStdError = stderr_handle;
-
- char *hblock = NULL;
-#if 0
- sinfo.cbReserved2 = 0;
- sinfo.lpReserved2 = NULL;
-#else
- /* On newer versions of Windows, more file descriptors / handles than the
- first three can be passed.
- The format is as follows: Let N be an exclusive upper bound for the file
- descriptors to be passed. Two arrays are constructed in memory:
- - flags[0..N-1], of element type 'unsigned char',
- - handles[0..N-1], of element type 'HANDLE' or 'intptr_t'.
- For used entries, handles[i] is the handle, and flags[i] is a set of flags,
- a combination of:
- 1 for open file descriptors,
- 64 for handles of type FILE_TYPE_CHAR,
- 8 for handles of type FILE_TYPE_PIPE.
- For unused entries - this includes the first three, since they are already
- passed above -, handles[i] is INVALID_HANDLE_VALUE and flags[i] is zero.
- lpReserved2 now is a pointer to the concatenation (without padding) of:
- - an 'unsigned int' whose value is N,
- - the contents of the flags[0..N-1] array,
- - the contents of the handles[0..N-1] array.
- cbReserved2 is the size (in bytes) of the object at lpReserved2. */
- {
- /* _getmaxstdio
- <https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/getmaxstdio>
- Default value is 512. */
- unsigned int fdmax;
- for (fdmax = _getmaxstdio (); fdmax > 0; fdmax--)
- {
- unsigned int fd = fdmax - 1;
- /* _get_osfhandle
- <https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/get-osfhandle> */
- HANDLE handle = (HANDLE) _get_osfhandle (fd);
- if (handle != INVALID_HANDLE_VALUE)
- {
- DWORD hflags;
- /* GetHandleInformation
- <https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-gethandleinformation> */
- if (GetHandleInformation (handle, &hflags))
- {
- if ((hflags & HANDLE_FLAG_INHERIT) != 0)
- /* fd denotes an inheritable descriptor. */
- break;
- }
- }
- }
- if (fdmax > 0)
- {
- sinfo.cbReserved2 =
- sizeof (unsigned int)
- + fdmax * sizeof (unsigned char)
- + fdmax * sizeof (HANDLE);
- /* Add some padding, so that we can work with a properly HANDLE array. */
- hblock = (char *) malloc (sinfo.cbReserved2 + (sizeof (HANDLE) - 1));
- if (hblock == NULL)
- goto out_of_memory_3;
- * (unsigned int *) hblock = fdmax;
- unsigned char *flags = (unsigned char *) (hblock + sizeof (unsigned int));
- char *handles = (char *) (flags + fdmax);
- HANDLE *handles_aligned =
- (HANDLE *) (((uintptr_t) handles + (sizeof (HANDLE) - 1))
- & - (uintptr_t) sizeof (HANDLE));
-
- unsigned int fd;
- for (fd = 0; fd < fdmax; fd++)
- {
- flags[fd] = 0;
- handles_aligned[fd] = INVALID_HANDLE_VALUE;
- /* The first three are already passed above. */
- if (fd >= 3)
- {
- /* _get_osfhandle
- <https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/get-osfhandle> */
- HANDLE handle = (HANDLE) _get_osfhandle (fd);
- if (handle != INVALID_HANDLE_VALUE)
- {
- DWORD hflags;
- /* GetHandleInformation
- <https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-gethandleinformation> */
- if (GetHandleInformation (handle, &hflags))
- {
- if ((hflags & HANDLE_FLAG_INHERIT) != 0)
- {
- /* fd denotes an inheritable descriptor. */
- /* On Microsoft Windows, it would be sufficient to
- set flags[fd] = 1. But on ReactOS or Wine,
- adding the bit that indicates the handle type
- may be necessary. So, just do it everywhere. */
- switch (GetFileType (handle))
- {
- case FILE_TYPE_CHAR:
- flags[fd] = 64 | 1;
- break;
- case FILE_TYPE_PIPE:
- flags[fd] = 8 | 1;
- break;
- default:
- flags[fd] = 1;
- break;
- }
- handles_aligned[fd] = handle;
- }
- }
- }
- }
- }
-
- if (handles != (char *) handles_aligned)
- memmove (handles, (char *) handles_aligned, fdmax * sizeof (HANDLE));
- sinfo.lpReserved2 = (BYTE *) hblock;
- }
- else
- {
- sinfo.cbReserved2 = 0;
- sinfo.lpReserved2 = NULL;
- }
- }
-#endif
+ if (compose_handles_block (&inh_handles, &sinfo) < 0)
+ {
+ int saved_errno = errno;
+ free_inheritable_handles (&inh_handles);
+ if (envblock != NULL)
+ free (envblock);
+ free (command);
+ if (resolved_progname != progname)
+ free ((char *) resolved_progname);
+ errno = saved_errno;
+ return -1;
+ }
PROCESS_INFORMATION pinfo;
if (!CreateProcess (resolved_progname, command, NULL, NULL, TRUE,
@@ -488,8 +614,8 @@ spawnpvech (int mode,
{
DWORD error = GetLastError ();
- if (hblock != NULL)
- free (hblock);
+ free (sinfo.lpReserved2);
+ free_inheritable_handles (&inh_handles);
if (envblock != NULL)
free (envblock);
free (command);
@@ -537,8 +663,8 @@ spawnpvech (int mode,
if (pinfo.hThread)
CloseHandle (pinfo.hThread);
- if (hblock != NULL)
- free (hblock);
+ free (sinfo.lpReserved2);
+ free_inheritable_handles (&inh_handles);
if (envblock != NULL)
free (envblock);
free (command);
@@ -586,14 +712,6 @@ spawnpvech (int mode,
}
/*NOTREACHED*/
-#if 0
- out_of_memory_4:
- if (hblock != NULL)
- free (hblock);
-#endif
- out_of_memory_3:
- if (envblock != NULL)
- free (envblock);
out_of_memory_2:
free (command);
out_of_memory_1:
diff --git a/lib/windows-spawn.h b/lib/windows-spawn.h
index 90f45e1..6e06652 100644
--- a/lib/windows-spawn.h
+++ b/lib/windows-spawn.h
@@ -18,12 +18,14 @@
#ifndef _WINDOWS_SPAWN_H
#define _WINDOWS_SPAWN_H
+#include <stdbool.h>
#include <stdint.h>
/* Get declarations of the native Windows API functions. */
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
+
/* Prepares an argument vector before calling spawn().
Note that spawn() does not by itself call the command interpreter
@@ -79,6 +81,47 @@ extern char * compose_command (const char * const *argv);
failure, NULL is returned, with errno set. */
extern char * compose_envblock (const char * const *envp);
+
+/* This struct keeps track of which handles to pass to a subprocess, and with
+ which flags. All of the handles here are inheritable.
+ Regarding handle inheritance, see
+ <https://docs.microsoft.com/en-us/windows/win32/sysinfo/handle-inheritance> */
+struct inheritable_handles
+{
+ /* The number of occupied entries in the two arrays below.
+ 3 <= count <= allocated. */
+ size_t count;
+ /* The number of allocated entries in the two arrays below. */
+ size_t allocated;
+ /* handles[0..count-1] are the occupied entries.
+ handles[fd] is either INVALID_HANDLE_VALUE or an inheritable handle. */
+ HANDLE *handles;
+ /* flags[0..count-1] are the occupied entries.
+ flags[fd] is only relevant if handles[fd] != INVALID_HANDLE_VALUE.
+ It is a bit mask consisting of:
+ - 32 for O_APPEND.
+ */
+ unsigned char *flags;
+};
+
+/* Initializes a set of inheritable handles, filling in all inheritable handles
+ assigned to file descriptors.
+ If DUPLICATE is true, the handles stored in the set are duplicates.
+ Returns 0 upon success. In case of failure, -1 is returned, with errno set.
+ */
+extern int init_inheritable_handles (struct inheritable_handles *inh_handles,
+ bool duplicate);
+
+/* Fills a set of inheritable handles into a STARTUPINFO for CreateProcess().
+ Returns 0 upon success. In case of failure, -1 is returned, with errno set.
+ */
+extern int compose_handles_block (const struct inheritable_handles *inh_handles,
+ STARTUPINFO *sinfo);
+
+/* Frees the memory held by a set of inheritable handles. */
+extern void free_inheritable_handles (struct inheritable_handles *inh_handles);
+
+
/* Creates a subprocess.
MODE is either P_WAIT or P_NOWAIT.
PROGNAME is the program to invoke.
--
2.7.4
[-- Attachment #7: 0006-windows-spawn-Export-another-auxiliary-function.patch --]
[-- Type: text/x-patch, Size: 4074 bytes --]
From 48e0a23aa5ddd288856268af751b73218ba31777 Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Thu, 24 Dec 2020 22:18:27 +0100
Subject: [PATCH 6/7] windows-spawn: Export another auxiliary function.
* lib/windows-spawn.h (convert_CreateProcess_error): New declaration.
* lib/windows-spawn.c (convert_CreateProcess_error): New function,
extracted from spawnpvech.
(spawnpvech): Use it.
---
ChangeLog | 8 ++++++
lib/windows-spawn.c | 77 ++++++++++++++++++++++++++++-------------------------
lib/windows-spawn.h | 5 ++++
3 files changed, 54 insertions(+), 36 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index 39629f3..fceaaa6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,13 @@
2020-12-24 Bruno Haible <bruno@clisp.org>
+ windows-spawn: Export another auxiliary function.
+ * lib/windows-spawn.h (convert_CreateProcess_error): New declaration.
+ * lib/windows-spawn.c (convert_CreateProcess_error): New function,
+ extracted from spawnpvech.
+ (spawnpvech): Use it.
+
+2020-12-24 Bruno Haible <bruno@clisp.org>
+
windows-spawn: Export some more auxiliary functions.
* lib/windows-spawn.h: Include <stdbool.h>.
(struct inheritable_handles): New type.
diff --git a/lib/windows-spawn.c b/lib/windows-spawn.c
index a2f63b9..a781090 100644
--- a/lib/windows-spawn.c
+++ b/lib/windows-spawn.c
@@ -526,6 +526,46 @@ free_inheritable_handles (struct inheritable_handles *inh_handles)
free (inh_handles->handles);
}
+int
+convert_CreateProcess_error (DWORD error)
+{
+ /* Some of these errors probably cannot happen. But who knows... */
+ switch (error)
+ {
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ case ERROR_BAD_PATHNAME:
+ case ERROR_BAD_NET_NAME:
+ case ERROR_INVALID_NAME:
+ case ERROR_DIRECTORY:
+ return ENOENT;
+ break;
+
+ case ERROR_ACCESS_DENIED:
+ case ERROR_SHARING_VIOLATION:
+ return EACCES;
+ break;
+
+ case ERROR_OUTOFMEMORY:
+ return ENOMEM;
+ break;
+
+ case ERROR_BUFFER_OVERFLOW:
+ case ERROR_FILENAME_EXCED_RANGE:
+ return ENAMETOOLONG;
+ break;
+
+ case ERROR_BAD_FORMAT:
+ case ERROR_BAD_EXE_FORMAT:
+ return ENOEXEC;
+ break;
+
+ default:
+ return EINVAL;
+ break;
+ }
+}
+
intptr_t
spawnpvech (int mode,
const char *progname, const char * const *argv,
@@ -622,42 +662,7 @@ spawnpvech (int mode,
if (resolved_progname != progname)
free ((char *) resolved_progname);
- /* Some of these errors probably cannot happen. But who knows... */
- switch (error)
- {
- case ERROR_FILE_NOT_FOUND:
- case ERROR_PATH_NOT_FOUND:
- case ERROR_BAD_PATHNAME:
- case ERROR_BAD_NET_NAME:
- case ERROR_INVALID_NAME:
- case ERROR_DIRECTORY:
- errno = ENOENT;
- break;
-
- case ERROR_ACCESS_DENIED:
- case ERROR_SHARING_VIOLATION:
- errno = EACCES;
- break;
-
- case ERROR_OUTOFMEMORY:
- errno = ENOMEM;
- break;
-
- case ERROR_BUFFER_OVERFLOW:
- case ERROR_FILENAME_EXCED_RANGE:
- errno = ENAMETOOLONG;
- break;
-
- case ERROR_BAD_FORMAT:
- case ERROR_BAD_EXE_FORMAT:
- errno = ENOEXEC;
- break;
-
- default:
- errno = EINVAL;
- break;
- }
-
+ errno = convert_CreateProcess_error (error);
return -1;
}
diff --git a/lib/windows-spawn.h b/lib/windows-spawn.h
index 6e06652..1ff76f6 100644
--- a/lib/windows-spawn.h
+++ b/lib/windows-spawn.h
@@ -122,6 +122,11 @@ extern int compose_handles_block (const struct inheritable_handles *inh_handles,
extern void free_inheritable_handles (struct inheritable_handles *inh_handles);
+/* Converts a CreateProcess() error code (retrieved through GetLastError()) to
+ an errno value. */
+extern int convert_CreateProcess_error (DWORD error);
+
+
/* Creates a subprocess.
MODE is either P_WAIT or P_NOWAIT.
PROGNAME is the program to invoke.
--
2.7.4
[-- Attachment #8: 0007-posix_spawn-internal-Implement-for-native-Windows.patch --]
[-- Type: text/x-patch, Size: 23914 bytes --]
From f2ca14d1555c7f8de305c21993fd1f805f48a9a7 Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Thu, 24 Dec 2020 22:18:36 +0100
Subject: [PATCH 7/7] posix_spawn-internal: Implement for native Windows.
* lib/spawni.c (grow_inheritable_handles, shrink_inheritable_handles,
close_inheritable_handles, memiszero, sigisempty, open_handle, do_open,
do_dup2, do_close): New functions.
(__spawni): Implement on native Windows.
* modules/posix_spawn-internal (Depends-on): Add filename,
concat-filename, findprog-in, malloca, windows-spawn.
* doc/posix-functions/posix_spawn.texi: Update.
* doc/posix-functions/posix_spawnp.texi: Likewise.
---
ChangeLog | 12 +
doc/posix-functions/posix_spawn.texi | 2 +-
doc/posix-functions/posix_spawnp.texi | 2 +-
lib/spawni.c | 631 +++++++++++++++++++++++++++++++++-
modules/posix_spawn-internal | 5 +
5 files changed, 645 insertions(+), 7 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index fceaaa6..531c416 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,17 @@
2020-12-24 Bruno Haible <bruno@clisp.org>
+ posix_spawn-internal: Implement for native Windows.
+ * lib/spawni.c (grow_inheritable_handles, shrink_inheritable_handles,
+ close_inheritable_handles, memiszero, sigisempty, open_handle, do_open,
+ do_dup2, do_close): New functions.
+ (__spawni): Implement on native Windows.
+ * modules/posix_spawn-internal (Depends-on): Add filename,
+ concat-filename, findprog-in, malloca, windows-spawn.
+ * doc/posix-functions/posix_spawn.texi: Update.
+ * doc/posix-functions/posix_spawnp.texi: Likewise.
+
+2020-12-24 Bruno Haible <bruno@clisp.org>
+
windows-spawn: Export another auxiliary function.
* lib/windows-spawn.h (convert_CreateProcess_error): New declaration.
* lib/windows-spawn.c (convert_CreateProcess_error): New function,
diff --git a/doc/posix-functions/posix_spawn.texi b/doc/posix-functions/posix_spawn.texi
index e0b39b0..d0cf1b9 100644
--- a/doc/posix-functions/posix_spawn.texi
+++ b/doc/posix-functions/posix_spawn.texi
@@ -26,7 +26,7 @@ Portability problems not fixed by Gnulib:
@itemize
@item
This function does not work on some platforms:
-AIX 6.1 (under particular circumstances), mingw.
+AIX 6.1 (under particular circumstances).
@end itemize
The Gnulib modules @code{posix_spawn_file_actions_addchdir} and
diff --git a/doc/posix-functions/posix_spawnp.texi b/doc/posix-functions/posix_spawnp.texi
index 2dd9f68..9e65191 100644
--- a/doc/posix-functions/posix_spawnp.texi
+++ b/doc/posix-functions/posix_spawnp.texi
@@ -26,7 +26,7 @@ Portability problems not fixed by Gnulib:
@itemize
@item
This function does not work on some platforms:
-AIX 6.1 (under particular circumstances), mingw.
+AIX 6.1 (under particular circumstances).
@end itemize
The Gnulib modules @code{posix_spawn_file_actions_addchdir} and
diff --git a/lib/spawni.c b/lib/spawni.c
index 182d13f..2f0d25f 100644
--- a/lib/spawni.c
+++ b/lib/spawni.c
@@ -87,16 +87,637 @@
#if defined _WIN32 && ! defined __CYGWIN__
-
/* Native Windows API. */
+
+/* Get declarations of the native Windows API functions. */
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+
+# include <stdbool.h>
+# include <stdio.h>
+
+# include "filename.h"
+# include "concat-filename.h"
+# include "findprog.h"
+# include "malloca.h"
+# include "windows-spawn.h"
+
+/* Don't assume that UNICODE is not defined. */
+# undef CreateFile
+# define CreateFile CreateFileA
+# undef STARTUPINFO
+# define STARTUPINFO STARTUPINFOA
+# undef CreateProcess
+# define CreateProcess CreateProcessA
+
+/* Grows inh_handles->count so that it becomes > newfd.
+ Returns 0 upon success. In case of failure, -1 is returned, with errno set.
+ */
+static int
+grow_inheritable_handles (struct inheritable_handles *inh_handles, int newfd)
+{
+ if (inh_handles->allocated <= newfd)
+ {
+ size_t new_allocated = 2 * inh_handles->allocated + 1;
+ if (new_allocated <= newfd)
+ new_allocated = newfd + 1;
+ HANDLE *new_handles_array =
+ (HANDLE *)
+ realloc (inh_handles->handles, new_allocated * sizeof (HANDLE));
+ if (new_handles_array == NULL)
+ {
+ errno = ENOMEM;
+ return -1;
+ }
+ unsigned char *new_flags_array =
+ (unsigned char *)
+ realloc (inh_handles->flags, new_allocated * sizeof (unsigned char));
+ if (new_flags_array == NULL)
+ {
+ free (new_handles_array);
+ errno = ENOMEM;
+ return -1;
+ }
+ inh_handles->allocated = new_allocated;
+ inh_handles->handles = new_handles_array;
+ inh_handles->flags = new_flags_array;
+ }
+
+ HANDLE *handles = inh_handles->handles;
+
+ for (; inh_handles->count <= newfd; inh_handles->count++)
+ handles[inh_handles->count] = INVALID_HANDLE_VALUE;
+
+ return 0;
+}
+
+/* Reduces inh_handles->count to the minimum needed. */
+static void
+shrink_inheritable_handles (struct inheritable_handles *inh_handles)
+{
+ HANDLE *handles = inh_handles->handles;
+
+ while (inh_handles->count > 3
+ && handles[inh_handles->count - 1] == INVALID_HANDLE_VALUE)
+ inh_handles->count--;
+}
+
+/* Closes all handles in inh_handles. */
+static void
+close_inheritable_handles (struct inheritable_handles *inh_handles)
+{
+ HANDLE *handles = inh_handles->handles;
+ size_t handles_count = inh_handles->count;
+ unsigned int fd;
+
+ for (fd = 0; fd < handles_count; fd++)
+ {
+ HANDLE handle = handles[fd];
+
+ if (handle != INVALID_HANDLE_VALUE)
+ CloseHandle (handle);
+ }
+}
+
+/* Tests whether a memory region, starting at P and N bytes long, contains only
+ zeroes. */
+static bool
+memiszero (const void *p, size_t n)
+{
+ const char *cp = p;
+ for (; n > 0; cp++, n--)
+ if (*cp != 0)
+ return 0;
+ return 1;
+}
+
+/* Tests whether *S contains no signals. */
+static bool
+sigisempty (const sigset_t *s)
+{
+ return memiszero (s, sizeof (sigset_t));
+}
+
+/* Opens a HANDLE to a file.
+ Upon failure, returns INVALID_HANDLE_VALUE with errno set. */
+static HANDLE
+open_handle (const char *name, int flags, mode_t mode)
+{
+ /* To ease portability. Like in open.c. */
+ if (strcmp (name, "/dev/null") == 0)
+ name = "NUL";
+
+ /* POSIX <https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13>
+ specifies: "More than two leading <slash> characters shall be treated as
+ a single <slash> character." */
+ if (ISSLASH (name[0]) && ISSLASH (name[1]) && ISSLASH (name[2]))
+ {
+ name += 2;
+ while (ISSLASH (name[1]))
+ name++;
+ }
+
+ size_t len = strlen (name);
+ size_t drive_prefix_len = (HAS_DEVICE (name) ? 2 : 0);
+
+ /* Remove trailing slashes (except the very first one, at position
+ drive_prefix_len), but remember their presence. */
+ size_t rlen;
+ bool check_dir = false;
+
+ rlen = len;
+ while (rlen > drive_prefix_len && ISSLASH (name[rlen-1]))
+ {
+ check_dir = true;
+ if (rlen == drive_prefix_len + 1)
+ break;
+ rlen--;
+ }
+
+ /* Handle '' and 'C:'. */
+ if (!check_dir && rlen == drive_prefix_len)
+ {
+ errno = ENOENT;
+ return INVALID_HANDLE_VALUE;
+ }
+
+ /* Handle '\\'. */
+ if (rlen == 1 && ISSLASH (name[0]) && len >= 2)
+ {
+ errno = ENOENT;
+ return INVALID_HANDLE_VALUE;
+ }
+
+ const char *rname;
+ char *malloca_rname;
+ if (rlen == len)
+ {
+ rname = name;
+ malloca_rname = NULL;
+ }
+ else
+ {
+ malloca_rname = malloca (rlen + 1);
+ if (malloca_rname == NULL)
+ {
+ errno = ENOMEM;
+ return INVALID_HANDLE_VALUE;
+ }
+ memcpy (malloca_rname, name, rlen);
+ malloca_rname[rlen] = '\0';
+ rname = malloca_rname;
+ }
+
+ /* For the meaning of the flags, see
+ <https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/open-wopen> */
+ /* Open a handle to the file.
+ CreateFile
+ <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-createfilea>
+ <https://docs.microsoft.com/en-us/windows/desktop/FileIO/creating-and-opening-files> */
+ HANDLE handle =
+ CreateFile (rname,
+ ((flags & (O_WRONLY | O_RDWR)) != 0
+ ? GENERIC_READ | GENERIC_WRITE
+ : GENERIC_READ),
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ ((flags & O_CREAT) != 0
+ ? ((flags & O_EXCL) != 0
+ ? CREATE_NEW
+ : ((flags & O_TRUNC) != 0 ? CREATE_ALWAYS : OPEN_ALWAYS))
+ : ((flags & O_TRUNC) != 0
+ ? TRUNCATE_EXISTING
+ : OPEN_EXISTING)),
+ /* FILE_FLAG_BACKUP_SEMANTICS is useful for opening directories,
+ which is out-of-scope here. */
+ /* FILE_FLAG_POSIX_SEMANTICS (treat file names that differ only
+ in case as different) makes sense only when applied to *all*
+ filesystem operations. */
+ /* FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS */
+ FILE_ATTRIBUTE_NORMAL
+ | ((flags & O_TEMPORARY) != 0 ? FILE_FLAG_DELETE_ON_CLOSE : 0)
+ | ((flags & O_SEQUENTIAL ) != 0 ? FILE_FLAG_SEQUENTIAL_SCAN : 0)
+ | ((flags & O_RANDOM) != 0 ? FILE_FLAG_RANDOM_ACCESS : 0),
+ NULL);
+ if (handle == INVALID_HANDLE_VALUE)
+ switch (GetLastError ())
+ {
+ /* Some of these errors probably cannot happen with the specific flags
+ that we pass to CreateFile. But who knows... */
+ case ERROR_FILE_NOT_FOUND: /* The last component of rname does not exist. */
+ case ERROR_PATH_NOT_FOUND: /* Some directory component in rname does not exist. */
+ case ERROR_BAD_PATHNAME: /* rname is such as '\\server'. */
+ case ERROR_BAD_NETPATH: /* rname is such as '\\nonexistentserver\share'. */
+ case ERROR_BAD_NET_NAME: /* rname is such as '\\server\nonexistentshare'. */
+ case ERROR_INVALID_NAME: /* rname contains wildcards, misplaced colon, etc. */
+ case ERROR_DIRECTORY:
+ errno = ENOENT;
+ break;
+
+ case ERROR_ACCESS_DENIED: /* rname is such as 'C:\System Volume Information\foo'. */
+ case ERROR_SHARING_VIOLATION: /* rname is such as 'C:\pagefile.sys'. */
+ /* XXX map to EACCES or EPERM? */
+ errno = EACCES;
+ break;
+
+ case ERROR_OUTOFMEMORY:
+ errno = ENOMEM;
+ break;
+
+ case ERROR_WRITE_PROTECT:
+ errno = EROFS;
+ break;
+
+ case ERROR_WRITE_FAULT:
+ case ERROR_READ_FAULT:
+ case ERROR_GEN_FAILURE:
+ errno = EIO;
+ break;
+
+ case ERROR_BUFFER_OVERFLOW:
+ case ERROR_FILENAME_EXCED_RANGE:
+ errno = ENAMETOOLONG;
+ break;
+
+ case ERROR_DELETE_PENDING: /* XXX map to EACCES or EPERM? */
+ errno = EPERM;
+ break;
+
+ default:
+ errno = EINVAL;
+ break;
+ }
+
+ if (malloca_rname != NULL)
+ {
+ int saved_errno = errno;
+ freea (malloca_rname);
+ errno = saved_errno;
+ }
+ return handle;
+}
+
+/* Executes an 'open' action.
+ Returns 0 upon success. In case of failure, -1 is returned, with errno set.
+ */
+static int
+do_open (struct inheritable_handles *inh_handles, int newfd,
+ const char *filename, const char *directory,
+ int flags, mode_t mode, HANDLE curr_process)
+{
+ if (!(newfd >= 0 && newfd < _getmaxstdio ()))
+ {
+ errno = EBADF;
+ return -1;
+ }
+ if (grow_inheritable_handles (inh_handles, newfd) < 0)
+ return -1;
+ if (inh_handles->handles[newfd] != INVALID_HANDLE_VALUE
+ && !CloseHandle (inh_handles->handles[newfd]))
+ {
+ errno = EIO;
+ return -1;
+ }
+ if (filename == NULL)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ char *filename_to_free = NULL;
+ if (directory != NULL && IS_RELATIVE_FILE_NAME (filename))
+ {
+ char *real_filename = concatenated_filename (directory, filename, NULL);
+ if (real_filename == NULL)
+ {
+ errno = ENOMEM;
+ return -1;
+ }
+ filename = real_filename;
+ filename_to_free = real_filename;
+ }
+ HANDLE handle = open_handle (filename, flags, mode);
+ if (handle == INVALID_HANDLE_VALUE)
+ {
+ int saved_errno = errno;
+ free (filename_to_free);
+ errno = saved_errno;
+ return -1;
+ }
+ free (filename_to_free);
+ /* Duplicate the handle, so that it becomes inheritable. */
+ if (!DuplicateHandle (curr_process, handle,
+ curr_process, &inh_handles->handles[newfd],
+ 0, TRUE,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS))
+ {
+ errno = EBADF; /* arbitrary */
+ return -1;
+ }
+ inh_handles->flags[newfd] = ((flags & O_APPEND) != 0 ? 32 : 0);
+ return 0;
+}
+
+/* Executes a 'dup2' action.
+ Returns 0 upon success. In case of failure, -1 is returned, with errno set.
+ */
+static int
+do_dup2 (struct inheritable_handles *inh_handles, int oldfd, int newfd,
+ HANDLE curr_process)
+{
+ if (!(oldfd >= 0 && oldfd < inh_handles->count
+ && inh_handles->handles[oldfd] != INVALID_HANDLE_VALUE))
+ {
+ errno = EBADF;
+ return -1;
+ }
+ if (!(newfd >= 0 && newfd < _getmaxstdio ()))
+ {
+ errno = EBADF;
+ return -1;
+ }
+ if (newfd != oldfd)
+ {
+ if (grow_inheritable_handles (inh_handles, newfd) < 0)
+ return -1;
+ if (inh_handles->handles[newfd] != INVALID_HANDLE_VALUE
+ && !CloseHandle (inh_handles->handles[newfd]))
+ {
+ errno = EIO;
+ return -1;
+ }
+ /* Duplicate the handle, so that it a forthcoming do_close action on oldfd
+ has no effect on newfd. */
+ if (!DuplicateHandle (curr_process, inh_handles->handles[oldfd],
+ curr_process, &inh_handles->handles[newfd],
+ 0, TRUE, DUPLICATE_SAME_ACCESS))
+ {
+ errno = EBADF; /* arbitrary */
+ return -1;
+ }
+ inh_handles->flags[newfd] = 0;
+ }
+ return 0;
+}
+
+/* Executes a 'close' action.
+ Returns 0 upon success. In case of failure, -1 is returned, with errno set.
+ */
+static int
+do_close (struct inheritable_handles *inh_handles, int fd)
+{
+ if (!(fd >= 0 && fd < inh_handles->count
+ && inh_handles->handles[fd] != INVALID_HANDLE_VALUE))
+ {
+ errno = EBADF;
+ return -1;
+ }
+ if (!CloseHandle (inh_handles->handles[fd]))
+ {
+ errno = EIO;
+ return -1;
+ }
+ inh_handles->handles[fd] = INVALID_HANDLE_VALUE;
+ return 0;
+}
+
int
-__spawni (pid_t *pid, const char *file,
+__spawni (pid_t *pid, const char *prog_filename,
const posix_spawn_file_actions_t *file_actions,
- const posix_spawnattr_t *attrp, const char *const argv[],
+ const posix_spawnattr_t *attrp, const char *const prog_argv[],
const char *const envp[], int use_path)
{
- /* Not yet implemented. */
- return ENOSYS;
+ /* Validate the arguments. */
+ if (prog_filename == NULL
+ || (attrp != NULL
+ && ((attrp->_flags & ~POSIX_SPAWN_SETPGROUP) != 0
+ || attrp->_pgrp != 0
+ || ! sigisempty (&attrp->_sd)
+ || ! sigisempty (&attrp->_ss)
+ || attrp->_sp.sched_priority != 0
+ || attrp->_policy != 0)))
+ return EINVAL;
+
+ /* Process group handling:
+ Native Windows does not have the concept of process group, but it has the
+ concept of a console attached to a process.
+ So, we interpret the three cases as follows:
+ - Flag POSIX_SPAWN_SETPGROUP not set: Means, the child process is in the
+ same process group as the parent process. We interpret this as a
+ request to reuse the same console.
+ - Flag POSIX_SPAWN_SETPGROUP set with attrp->_pgrp == 0: Means the child
+ process starts a process group of its own. See
+ <https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_getpgroup.html>
+ <https://pubs.opengroup.org/onlinepubs/9699919799/functions/setpgrp.html>
+ We interpret this as a request to detach from the current console.
+ - Flag POSIX_SPAWN_SETPGROUP set with attrp->_pgrp != 0: Means the child
+ process joins another, existing process group. See
+ <https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_getpgroup.html>
+ <https://pubs.opengroup.org/onlinepubs/9699919799/functions/setpgid.html>
+ We don't support this case; it produces error EINVAL above. */
+ /* <https://docs.microsoft.com/en-us/windows/win32/procthread/process-creation-flags> */
+ DWORD process_creation_flags =
+ (attrp != NULL && (attrp->_flags & POSIX_SPAWN_SETPGROUP) != 0 ? DETACHED_PROCESS : 0);
+
+ char *argv_mem_to_free;
+ const char **argv = prepare_spawn (prog_argv, &argv_mem_to_free);
+ if (argv == NULL)
+ return errno; /* errno is set here */
+ argv++;
+
+ /* Compose the command. */
+ char *command = compose_command (argv);
+ if (command == NULL)
+ {
+ free (argv_mem_to_free);
+ return ENOMEM;
+ }
+
+ /* Copy *ENVP into a contiguous block of memory. */
+ char *envblock;
+ if (envp == NULL)
+ envblock = NULL;
+ else
+ {
+ envblock = compose_envblock (envp);
+ if (envblock == NULL)
+ {
+ free (command);
+ free (argv_mem_to_free);
+ return ENOMEM;
+ }
+ }
+
+ /* Set up the array of handles to inherit.
+ Duplicate each handle, so that a spawn_do_close action (below) has no
+ effect on the file descriptors of the current process. Alternatively,
+ we could store, for each handle, a bit that tells whether it is shared
+ with the current process. But this is simpler. */
+ struct inheritable_handles inh_handles;
+ if (init_inheritable_handles (&inh_handles, true) < 0)
+ goto failed_1;
+
+ /* Directory in which to execute the new process. */
+ const char *directory = NULL;
+
+ /* Execute the file_actions, modifying the inh_handles instead of the
+ file descriptors of the current process. */
+ if (file_actions != NULL)
+ {
+ HANDLE curr_process = GetCurrentProcess ();
+ int cnt;
+
+ for (cnt = 0; cnt < file_actions->_used; ++cnt)
+ {
+ struct __spawn_action *action = &file_actions->_actions[cnt];
+
+ switch (action->tag)
+ {
+ case spawn_do_close:
+ {
+ int fd = action->action.close_action.fd;
+ if (do_close (&inh_handles, fd) < 0)
+ goto failed_2;
+ }
+ break;
+
+ case spawn_do_open:
+ {
+ int newfd = action->action.open_action.fd;
+ const char *filename = action->action.open_action.path;
+ int flags = action->action.open_action.oflag;
+ mode_t mode = action->action.open_action.mode;
+ if (do_open (&inh_handles, newfd, filename, directory,
+ flags, mode, curr_process)
+ < 0)
+ goto failed_2;
+ }
+ break;
+
+ case spawn_do_dup2:
+ {
+ int oldfd = action->action.dup2_action.fd;
+ int newfd = action->action.dup2_action.newfd;
+ if (do_dup2 (&inh_handles, oldfd, newfd, curr_process) < 0)
+ goto failed_2;
+ }
+ break;
+
+ case spawn_do_chdir:
+ {
+ char *newdir = action->action.chdir_action.path;
+ if (directory != NULL && IS_RELATIVE_FILE_NAME (newdir))
+ {
+ newdir = concatenated_filename (directory, newdir, NULL);
+ if (newdir == NULL)
+ {
+ errno = ENOMEM;
+ goto failed_2;
+ }
+ }
+ directory = newdir;
+ }
+ break;
+
+ case spawn_do_fchdir:
+ /* Not supported in this implementation. */
+ errno = EINVAL;
+ goto failed_2;
+ }
+ }
+ }
+
+ /* Reduce inh_handles.count to the minimum needed. */
+ shrink_inheritable_handles (&inh_handles);
+
+ /* CreateProcess
+ <https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa> */
+ /* STARTUPINFO
+ <https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa> */
+ STARTUPINFO sinfo;
+ sinfo.cb = sizeof (STARTUPINFO);
+ sinfo.lpReserved = NULL;
+ sinfo.lpDesktop = NULL;
+ sinfo.lpTitle = NULL;
+ if (compose_handles_block (&inh_handles, &sinfo) < 0)
+ goto failed_2;
+
+ /* Perform the PATH search now, considering the final DIRECTORY. */
+ char *resolved_prog_filename_to_free = NULL;
+ {
+ const char *resolved_prog_filename =
+ find_in_given_path (prog_filename, use_path ? getenv ("PATH") : "",
+ directory, false);
+ if (resolved_prog_filename == NULL)
+ goto failed_3;
+ if (resolved_prog_filename != prog_filename)
+ resolved_prog_filename_to_free = (char *) resolved_prog_filename;
+ prog_filename = resolved_prog_filename;
+ }
+
+ PROCESS_INFORMATION pinfo;
+ if (!CreateProcess (prog_filename, command, NULL, NULL, TRUE,
+ process_creation_flags, envblock, directory, &sinfo,
+ &pinfo))
+ {
+ DWORD error = GetLastError ();
+
+ free (resolved_prog_filename_to_free);
+ free (sinfo.lpReserved2);
+ close_inheritable_handles (&inh_handles);
+ free_inheritable_handles (&inh_handles);
+ free (envblock);
+ free (command);
+ free (argv_mem_to_free);
+
+ return convert_CreateProcess_error (error);
+ }
+
+ if (pinfo.hThread)
+ CloseHandle (pinfo.hThread);
+
+ free (resolved_prog_filename_to_free);
+ free (sinfo.lpReserved2);
+ close_inheritable_handles (&inh_handles);
+ free_inheritable_handles (&inh_handles);
+ free (envblock);
+ free (command);
+ free (argv_mem_to_free);
+
+ if (pid != NULL)
+ *pid = (intptr_t) pinfo.hProcess;
+ return 0;
+
+ failed_3:
+ {
+ int saved_errno = errno;
+ free (sinfo.lpReserved2);
+ close_inheritable_handles (&inh_handles);
+ free_inheritable_handles (&inh_handles);
+ free (envblock);
+ free (command);
+ free (argv_mem_to_free);
+ return saved_errno;
+ }
+
+ failed_2:
+ {
+ int saved_errno = errno;
+ close_inheritable_handles (&inh_handles);
+ free_inheritable_handles (&inh_handles);
+ free (envblock);
+ free (command);
+ free (argv_mem_to_free);
+ return saved_errno;
+ }
+
+ failed_1:
+ {
+ int saved_errno = errno;
+ free (envblock);
+ free (command);
+ free (argv_mem_to_free);
+ return saved_errno;
+ }
}
#else
diff --git a/modules/posix_spawn-internal b/modules/posix_spawn-internal
index 920b4c4..2aeb019 100644
--- a/modules/posix_spawn-internal
+++ b/modules/posix_spawn-internal
@@ -15,6 +15,11 @@ open
sh-filename
strchrnul
unistd
+filename [test $HAVE_POSIX_SPAWN = 0]
+concat-filename [test $HAVE_POSIX_SPAWN = 0]
+findprog-in [test $HAVE_POSIX_SPAWN = 0]
+malloca [test $HAVE_POSIX_SPAWN = 0]
+windows-spawn [test $HAVE_POSIX_SPAWN = 0]
configure.ac:
gl_POSIX_SPAWN
--
2.7.4
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2020-12-24 22:03 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-12-24 22:03 posix_spawn[p]: implement for native Windows Bruno Haible
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).