From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on dcvr.yhbt.net X-Spam-Level: X-Spam-Status: No, score=-3.7 required=3.0 tests=AWL,BAYES_00,DKIM_INVALID, DKIM_SIGNED,HEADER_FROM_DIFFERENT_DOMAINS,MAILING_LIST_MULTI, RCVD_IN_DNSWL_BLOCKED,RCVD_IN_MSPIKE_H4,RCVD_IN_MSPIKE_WL, SPF_HELO_PASS,SPF_PASS shortcircuit=no autolearn=ham autolearn_force=no version=3.4.2 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dcvr.yhbt.net (Postfix) with ESMTPS id DB04A1F4B4 for ; Thu, 24 Dec 2020 22:03:41 +0000 (UTC) Received: from localhost ([::1]:52158 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ksYhw-0005Qd-Fv for normalperson@yhbt.net; Thu, 24 Dec 2020 17:03:40 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:55896) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1ksYhk-0005Q7-Eh for bug-gnulib@gnu.org; Thu, 24 Dec 2020 17:03:28 -0500 Received: from mo4-p00-ob.smtp.rzone.de ([85.215.255.24]:33872) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1ksYhd-0002uC-Ja for bug-gnulib@gnu.org; Thu, 24 Dec 2020 17:03:28 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; t=1608847398; s=strato-dkim-0002; d=clisp.org; h=Message-ID:Date:Subject:To:From:From:Subject:Sender; bh=J0K/RmEB1sKM76BskBiVuSLiwpXuhhosE3necWpTmlo=; b=WuLBIdx3JtbBojEQr2IUdsCGs1R26CXSKUYhS1QvtVMzgl2arMLFeP6is6m5PwZER+ S6B4phFdl/+Srl7/SakkOyakRqG4LkTu8eRfSkjwZSpN/Oro4PBcVMPnb1I0ckYMBfZT GZqvoDqa/DR1fvZ8nEgzqj+hS3Hf9GdnzzG6Yukr2tg0r9bF1epruEd559idm6VwnMxK q70n4PdD70Ao42iFqyM9I1nwf7A0FZSxaixTsiVif1vp1QoirXGDgHV+3lUkuPm4UWfM Si282ErINI0mEbt4UVCFEOhMPO7/8Ja33wYXjVWlbgPjGww9o7olWomSfIuEvpfq6E2A sUZg== X-RZG-AUTH: ":Ln4Re0+Ic/6oZXR1YgKryK8brlshOcZlIWs+iCP5vnk6shH+AHjwLuWOHqfzyPs=" X-RZG-CLASS-ID: mo00 Received: from bruno.haible.de by smtp.strato.de (RZmta 47.10.7 DYNA|AUTH) with ESMTPSA id e012d5wBOM3G4bg (using TLSv1 with cipher ECDHE-RSA-AES256-SHA (curve X9_62_prime256v1 with 256 ECDH bits, eq. 3072 bits RSA)) (Client did not present a certificate); Thu, 24 Dec 2020 23:03:16 +0100 (CET) From: Bruno Haible To: bug-gnulib@gnu.org Subject: posix_spawn[p]: implement for native Windows Date: Thu, 24 Dec 2020 23:03:16 +0100 Message-ID: <2007309.zio2eQqK7D@omega> User-Agent: KMail/5.1.3 (Linux/4.4.0-197-generic; KDE/5.18.0; x86_64; ; ) MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="nextPart1737435.yyEz4skuKJ" Content-Transfer-Encoding: 7Bit Received-SPF: none client-ip=85.215.255.24; envelope-from=bruno@clisp.org; helo=mo4-p00-ob.smtp.rzone.de X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=-0.001, SPF_HELO_PASS=-0.001, SPF_NONE=0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: bug-gnulib@gnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Gnulib discussion list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnulib-bounces+normalperson=yhbt.net@gnu.org Sender: "bug-gnulib" This is a multi-part message in MIME format. --nextPart1737435.yyEz4skuKJ Content-Transfer-Encoding: 7Bit Content-Type: text/plain; charset="us-ascii" 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 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 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 windows-spawn: Export some more auxiliary functions. * lib/windows-spawn.h: Include . (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 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 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 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 sh-filename: Add support for native Windows. * m4/sh-filename.m4 (gl_SH_FILENAME): Treat native Windows like Cygwin. --nextPart1737435.yyEz4skuKJ Content-Disposition: attachment; filename="0001-sh-filename-Add-support-for-native-Windows.patch" Content-Transfer-Encoding: 7Bit Content-Type: text/x-patch; charset="UTF-8"; name="0001-sh-filename-Add-support-for-native-Windows.patch" >From c9ed8d9a58bae5a90fc1410232354cf8e27e2e3c Mon Sep 17 00:00:00 2001 From: Bruno Haible 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 + + sh-filename: Add support for native Windows. + * m4/sh-filename.m4 (gl_SH_FILENAME): Treat native Windows like Cygwin. + 2020-12-24 Paul Eggert 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 --nextPart1737435.yyEz4skuKJ Content-Disposition: attachment; filename="0002-posix_spawn-tests-Add-support-for-native-Windows.patch" Content-Transfer-Encoding: 7Bit Content-Type: text/x-patch; charset="UTF-8"; name="0002-posix_spawn-tests-Add-support-for-native-Windows.patch" >From 8cf7746a2b5c5fe50e8369951619a87ecb3786bd Mon Sep 17 00:00:00 2001 From: Bruno Haible 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 + 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 + 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 --nextPart1737435.yyEz4skuKJ Content-Disposition: attachment; filename="0003-windows-spawn-Export-an-auxiliary-function.patch" Content-Transfer-Encoding: 7Bit Content-Type: text/x-patch; charset="UTF-8"; name="0003-windows-spawn-Export-an-auxiliary-function.patch" >From f8199b3ae12a94f79994e1c1e2389fe024ffa6fb Mon Sep 17 00:00:00 2001 From: Bruno Haible 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 + 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 + 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 --nextPart1737435.yyEz4skuKJ Content-Disposition: attachment; filename="0004-windows-spawn-Export-another-auxiliary-function.patch" Content-Transfer-Encoding: 7Bit Content-Type: text/x-patch; charset="UTF-8"; name="0004-windows-spawn-Export-another-auxiliary-function.patch" >From d1ae9c694e5418850c71884ccd22a293d9b3e29b Mon Sep 17 00:00:00 2001 From: Bruno Haible 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 + 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 + 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 --nextPart1737435.yyEz4skuKJ Content-Disposition: attachment; filename="0005-windows-spawn-Export-some-more-auxiliary-functions.patch" Content-Transfer-Encoding: 7Bit Content-Type: text/x-patch; charset="UTF-8"; name="0005-windows-spawn-Export-some-more-auxiliary-functions.patch" >From 2f1b8207e13b4bd1ca916626f25d8ac18bbdff33 Mon Sep 17 00:00:00 2001 From: Bruno Haible 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 . (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 + windows-spawn: Export some more auxiliary functions. + * lib/windows-spawn.h: Include . + (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 + 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 + + Default value is 512. */ + unsigned int fdmax = _getmaxstdio (); + if (fdmax < 3) + fdmax = 3; + for (; fdmax > 3; fdmax--) + { + unsigned int fd = fdmax - 1; + /* _get_osfhandle + */ + HANDLE handle = (HANDLE) _get_osfhandle (fd); + if (handle != INVALID_HANDLE_VALUE) + { + DWORD hflags; + /* 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 + */ + HANDLE handle = (HANDLE) _get_osfhandle (fd); + if (handle != INVALID_HANDLE_VALUE) + { + DWORD hflags; + /* 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 + */ + 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 + */ + 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 */ - /* Regarding handle inheritance, see - */ /* */ 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 - - Default value is 512. */ - unsigned int fdmax; - for (fdmax = _getmaxstdio (); fdmax > 0; fdmax--) - { - unsigned int fd = fdmax - 1; - /* _get_osfhandle - */ - HANDLE handle = (HANDLE) _get_osfhandle (fd); - if (handle != INVALID_HANDLE_VALUE) - { - DWORD hflags; - /* 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 - */ - HANDLE handle = (HANDLE) _get_osfhandle (fd); - if (handle != INVALID_HANDLE_VALUE) - { - DWORD hflags; - /* 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 #include /* Get declarations of the native Windows API functions. */ #define WIN32_LEAN_AND_MEAN #include + /* 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 + */ +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 --nextPart1737435.yyEz4skuKJ Content-Disposition: attachment; filename="0006-windows-spawn-Export-another-auxiliary-function.patch" Content-Transfer-Encoding: 7Bit Content-Type: text/x-patch; charset="UTF-8"; name="0006-windows-spawn-Export-another-auxiliary-function.patch" >From 48e0a23aa5ddd288856268af751b73218ba31777 Mon Sep 17 00:00:00 2001 From: Bruno Haible 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 + 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 + windows-spawn: Export some more auxiliary functions. * lib/windows-spawn.h: Include . (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 --nextPart1737435.yyEz4skuKJ Content-Disposition: attachment; filename="0007-posix_spawn-internal-Implement-for-native-Windows.patch" Content-Transfer-Encoding: 7Bit Content-Type: text/x-patch; charset="UTF-8"; name="0007-posix_spawn-internal-Implement-for-native-Windows.patch" >From f2ca14d1555c7f8de305c21993fd1f805f48a9a7 Mon Sep 17 00:00:00 2001 From: Bruno Haible 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 + 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 + 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 + +# include +# include + +# 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 + specifies: "More than two leading characters shall be treated as + a single 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 + */ + /* Open a handle to the file. + CreateFile + + */ + 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 + + + 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 + + + We don't support this case; it produces error EINVAL above. */ + /* */ + 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 + */ + /* STARTUPINFO + */ + 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 --nextPart1737435.yyEz4skuKJ--