Add unit tests regarding execution of scripts.
authorBruno Haible <bruno@clisp.org>
Thu, 24 Dec 2020 00:14:49 +0000 (01:14 +0100)
committerBruno Haible <bruno@clisp.org>
Thu, 24 Dec 2020 05:52:20 +0000 (06:52 +0100)
* tests/executable-script: New file.
* tests/executable-shell-script: New file.
* tests/test-posix_spawn-script.c: New file.
* tests/test-posix_spawnp-script.c: New file.
* tests/test-execute-script.c: New file.
* tests/test-spawn-pipe-script.c: New file.
* modules/posix_spawn-tests (Files): Add
tests/test-posix_spawn-script.c, tests/executable-script,
tests/executable-shell-script.
(Makefile.am): Compile and run test-posix_spawn-script.
* modules/posix_spawnp-tests (Files): Add
tests/test-posix_spawnp-script.c, tests/executable-script,
tests/executable-shell-script.
(Makefile.am): Compile and run test-posix_spawnp-script.
* modules/execute-tests (Files): Add tests/test-execute-script.c,
tests/executable-script, tests/executable-shell-script.
(Makefile.am): Compile and run test-execute-script.
* modules/spawn-pipe-tests (Files): Add tests/test-spawn-pipe-script.c,
tests/executable-script, tests/executable-shell-script.
(Makefile.am): Compile and run test-spawn-pipe-script.

ChangeLog
modules/execute-tests
modules/posix_spawn-tests
modules/posix_spawnp-tests
modules/spawn-pipe-tests
tests/executable-script [new file with mode: 0755]
tests/executable-shell-script [new file with mode: 0755]
tests/test-execute-script.c [new file with mode: 0644]
tests/test-posix_spawn-script.c [new file with mode: 0644]
tests/test-posix_spawnp-script.c [new file with mode: 0644]
tests/test-spawn-pipe-script.c [new file with mode: 0644]

index b0d67daa595f23b2a3bb0d04dbfdd993e5900ca4..bfb7f882517cf323778cf87560cf4fd263f76150 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,27 @@
+2020-12-23  Bruno Haible  <bruno@clisp.org>
+
+       Add unit tests regarding execution of scripts.
+       * tests/executable-script: New file.
+       * tests/executable-shell-script: New file.
+       * tests/test-posix_spawn-script.c: New file.
+       * tests/test-posix_spawnp-script.c: New file.
+       * tests/test-execute-script.c: New file.
+       * tests/test-spawn-pipe-script.c: New file.
+       * modules/posix_spawn-tests (Files): Add
+       tests/test-posix_spawn-script.c, tests/executable-script,
+       tests/executable-shell-script.
+       (Makefile.am): Compile and run test-posix_spawn-script.
+       * modules/posix_spawnp-tests (Files): Add
+       tests/test-posix_spawnp-script.c, tests/executable-script,
+       tests/executable-shell-script.
+       (Makefile.am): Compile and run test-posix_spawnp-script.
+       * modules/execute-tests (Files): Add tests/test-execute-script.c,
+       tests/executable-script, tests/executable-shell-script.
+       (Makefile.am): Compile and run test-execute-script.
+       * modules/spawn-pipe-tests (Files): Add tests/test-spawn-pipe-script.c,
+       tests/executable-script, tests/executable-shell-script.
+       (Makefile.am): Compile and run test-spawn-pipe-script.
+
 2020-12-23  Bruno Haible  <bruno@clisp.org>
 
        Don't execute scripts without '#!' marker through /bin/sh.
index 95621821315587b6676522c6b6eb6bb89519bc8c..2213a3225abe85b396c1e42975dc64847f35152f 100644 (file)
@@ -2,6 +2,9 @@ Files:
 tests/test-execute.sh
 tests/test-execute-main.c
 tests/test-execute-child.c
+tests/test-execute-script.c
+tests/executable-script
+tests/executable-shell-script
 tests/macros.h
 
 Depends-on:
@@ -23,3 +26,8 @@ test_execute_main_LDADD = $(LDADD) @LIBINTL@ $(LIBTHREAD)
 # wrapper script, and should link against as few libraries as possible.
 # Therefore don't link it against any libraries other than -lc.
 test_execute_child_LDADD =
+
+TESTS += test-execute-script
+check_PROGRAMS += test-execute-script
+test_execute_script_LDADD = $(LDADD) @LIBINTL@ $(LIBTHREAD)
+test_execute_script_CPPFLAGS = $(AM_CPPFLAGS) -DSRCDIR=\"$(srcdir)/\"
index 2c8b4a519a518c687d2648e0abcdd055421eb4b6..451dbd1ec47554ce45c9a227141fb58b9a541560 100644 (file)
@@ -3,6 +3,9 @@ tests/test-posix_spawn-open1.c
 tests/test-posix_spawn-open2.c
 tests/test-posix_spawn-inherit0.c
 tests/test-posix_spawn-inherit1.c
+tests/test-posix_spawn-script.c
+tests/executable-script
+tests/executable-shell-script
 tests/signature.h
 
 Depends-on:
@@ -31,10 +34,13 @@ TESTS += \
   test-posix_spawn-open1 \
   test-posix_spawn-open2 \
   test-posix_spawn-inherit0 \
-  test-posix_spawn-inherit1
+  test-posix_spawn-inherit1 \
+  test-posix_spawn-script
 check_PROGRAMS += \
   test-posix_spawn-open1 \
   test-posix_spawn-open2 \
   test-posix_spawn-inherit0 \
-  test-posix_spawn-inherit1
+  test-posix_spawn-inherit1 \
+  test-posix_spawn-script
+test_posix_spawn_script_CPPFLAGS = $(AM_CPPFLAGS) -DSRCDIR=\"$(srcdir)/\"
 endif
index 1f6bbc7b6ed42eb0c97b1d1d757402361e9971d4..04ccdbb6cd8a966fd9e395700b9578da02e455d9 100644 (file)
@@ -3,6 +3,9 @@ tests/test-posix_spawn-dup2-stdout.c
 tests/test-posix_spawn-dup2-stdout.in.sh
 tests/test-posix_spawn-dup2-stdin.c
 tests/test-posix_spawn-dup2-stdin.in.sh
+tests/test-posix_spawnp-script.c
+tests/executable-script
+tests/executable-shell-script
 tests/signature.h
 
 Depends-on:
@@ -35,8 +38,14 @@ 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
-check_PROGRAMS += test-posix_spawn-dup2-stdout test-posix_spawn-dup2-stdin
+TESTS += \
+  test-posix_spawn-dup2-stdout \
+  test-posix_spawn-dup2-stdin \
+  test-posix_spawnp-script
+check_PROGRAMS += \
+  test-posix_spawn-dup2-stdout \
+  test-posix_spawn-dup2-stdin \
+  test-posix_spawnp-script
 
 BUILT_SOURCES += test-posix_spawn-dup2-stdout.sh
 test-posix_spawn-dup2-stdout.sh: test-posix_spawn-dup2-stdout.in.sh
@@ -51,4 +60,6 @@ test-posix_spawn-dup2-stdin.sh: test-posix_spawn-dup2-stdin.in.sh
        cp $(srcdir)/test-posix_spawn-dup2-stdin.in.sh $@-t && \
        mv $@-t $@
 MOSTLYCLEANFILES += test-posix_spawn-dup2-stdin.sh test-posix_spawn-dup2-stdin.sh-t
+
+test_posix_spawnp_script_CPPFLAGS = $(AM_CPPFLAGS) -DSRCDIR=\"$(srcdir)/\"
 endif
index 5d1372cc0245b6622b83fae7058ed5cdf768daa8..80614a93bcfb51966545061624ed9271fc686952 100644 (file)
@@ -2,6 +2,9 @@ Files:
 tests/test-spawn-pipe.sh
 tests/test-spawn-pipe-main.c
 tests/test-spawn-pipe-child.c
+tests/test-spawn-pipe-script.c
+tests/executable-script
+tests/executable-shell-script
 tests/macros.h
 
 Depends-on:
@@ -19,3 +22,8 @@ test_spawn_pipe_main_LDADD = $(LDADD) @LIBINTL@ $(LIBTHREAD)
 # wrapper script, and should link against as few libraries as possible.
 # Therefore don't link it against any libraries other than -lc.
 test_spawn_pipe_child_LDADD =
+
+TESTS += test-spawn-pipe-script
+check_PROGRAMS += test-spawn-pipe-script
+test_spawn_pipe_script_LDADD = $(LDADD) @LIBINTL@ $(LIBTHREAD)
+test_spawn_pipe_script_CPPFLAGS = $(AM_CPPFLAGS) -DSRCDIR=\"$(srcdir)/\"
diff --git a/tests/executable-script b/tests/executable-script
new file mode 100755 (executable)
index 0000000..7d8b9cc
--- /dev/null
@@ -0,0 +1,4 @@
+printf 'Halle '
+printf "Potta"
+# This script is intentionally not immediately recognizable as a shell script.
+# Don't add a .sh suffix. Don't add a #! header in the first line.
diff --git a/tests/executable-shell-script b/tests/executable-shell-script
new file mode 100755 (executable)
index 0000000..1e38117
--- /dev/null
@@ -0,0 +1,4 @@
+#!/bin/sh
+# This script is a proper shell script.
+printf 'Halle '
+printf "Potta"
diff --git a/tests/test-execute-script.c b/tests/test-execute-script.c
new file mode 100644 (file)
index 0000000..060f0c5
--- /dev/null
@@ -0,0 +1,88 @@
+/* Test of execute.
+   Copyright (C) 2020 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <https://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+#include "execute.h"
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "read-file.h"
+#include "macros.h"
+
+#define DATA_FILENAME "test-execute-script.tmp"
+
+int
+main ()
+{
+  unlink (DATA_FILENAME);
+
+  /* Check an invocation of an executable script.
+     This should only be supported if the script has a '#!' marker; otherwise
+     it is unsecure: <https://sourceware.org/bugzilla/show_bug.cgi?id=13134>.
+     POSIX says that the execlp() and execvp() functions support executing
+     shell scripts
+     <https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html>,
+     but this is considered an antiquated feature.  */
+
+  /* This test is an extension of
+     "Check stdout is inherited, part 1 (regular file)"
+     in test-execute-main.c.  */
+  FILE *fp = freopen (DATA_FILENAME, "w", stdout);
+  ASSERT (fp != NULL);
+
+  {
+    const char *progname = "executable-script";
+    const char *prog_path = SRCDIR "executable-script";
+    const char *prog_argv[2] = { prog_path, NULL };
+
+    int ret = execute (progname, prog_argv[0], prog_argv, NULL,
+                       false, false, false, false, true, false, NULL);
+    ASSERT (ret == 127);
+  }
+
+#if defined _WIN32 && !defined __CYGWIN__
+  /* On native Windows, scripts - even with '#!' marker - are not executable.
+     Only .bat and .cmd files are.  */
+  ASSERT (fclose (fp) == 0);
+  ASSERT (unlink (DATA_FILENAME) == 0);
+  fprintf (stderr, "Skipping test: scripts are not executable on this platform.\n");
+  return 77;
+#else
+  {
+    const char *progname = "executable-shell-script";
+    const char *prog_path = SRCDIR "executable-shell-script";
+    const char *prog_argv[2] = { prog_path, NULL };
+
+    int ret = execute (progname, prog_argv[0], prog_argv, NULL,
+                       false, false, false, false, true, false, NULL);
+    ASSERT (ret == 0);
+
+    ASSERT (fclose (fp) == 0);
+
+    size_t length;
+    char *contents = read_file (DATA_FILENAME, 0, &length);
+    ASSERT (length == 11 && memcmp (contents, "Halle Potta", 11) == 0);
+  }
+
+  ASSERT (unlink (DATA_FILENAME) == 0);
+
+  return 0;
+#endif
+}
diff --git a/tests/test-posix_spawn-script.c b/tests/test-posix_spawn-script.c
new file mode 100644 (file)
index 0000000..a632841
--- /dev/null
@@ -0,0 +1,143 @@
+/* Test of posix_spawn() function.
+   Copyright (C) 2020 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <https://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+#include <spawn.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "macros.h"
+
+#define DATA_FILENAME "test-posix_spawn-script.tmp"
+
+int
+main ()
+{
+  unlink (DATA_FILENAME);
+
+  /* Check an invocation of an executable script.
+     This should only be supported if the script has a '#!' marker; otherwise
+     it is unsecure: <https://sourceware.org/bugzilla/show_bug.cgi?id=13134>.
+     POSIX says that the execlp() and execvp() functions support executing
+     shell scripts
+     <https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html>,
+     but this is considered an antiquated feature.  */
+  pid_t child;
+
+  posix_spawn_file_actions_t actions;
+  ASSERT (posix_spawn_file_actions_init (&actions) == 0);
+  ASSERT (posix_spawn_file_actions_addopen (&actions, STDOUT_FILENO,
+                                            DATA_FILENAME,
+                                            O_RDWR | O_CREAT | O_TRUNC, 0600)
+          == 0);
+
+  {
+    const char *prog_path = SRCDIR "executable-script";
+    const char *prog_argv[2] = { prog_path, NULL };
+
+    int err = posix_spawn (&child, prog_path, &actions, NULL,
+                           (char **) prog_argv, environ);
+    if (err != ENOEXEC)
+      {
+        if (err != 0)
+          {
+            errno = err;
+            perror ("posix_spawn");
+            return 1;
+          }
+
+        /* Wait for child.  */
+        int status = 0;
+        while (waitpid (child, &status, 0) != child)
+          ;
+        if (!WIFEXITED (status))
+          {
+            fprintf (stderr, "subprocess terminated with unexpected wait status %d\n", status);
+            return 1;
+          }
+        int exitstatus = WEXITSTATUS (status);
+        if (exitstatus != 127)
+          {
+            fprintf (stderr, "subprocess terminated with unexpected exit status %d\n", exitstatus);
+            return 1;
+          }
+      }
+  }
+
+  {
+    const char *prog_path = SRCDIR "executable-shell-script";
+    const char *prog_argv[2] = { prog_path, NULL };
+
+    int err = posix_spawn (&child, prog_path, &actions, NULL,
+                           (char **) prog_argv, environ);
+    if (err != 0)
+      {
+        errno = err;
+        perror ("posix_spawn");
+        return 1;
+      }
+
+    posix_spawn_file_actions_destroy (&actions);
+
+    /* Wait for child.  */
+    int status = 0;
+    while (waitpid (child, &status, 0) != child)
+      ;
+    if (!WIFEXITED (status))
+      {
+        fprintf (stderr, "subprocess terminated with unexpected wait status %d\n", status);
+        return 1;
+      }
+    int exitstatus = WEXITSTATUS (status);
+    if (exitstatus != 0)
+      {
+        fprintf (stderr, "subprocess terminated with unexpected exit status %d\n", exitstatus);
+        return 1;
+      }
+
+    /* Check the contents of the data file.  */
+    FILE *fp = fopen (DATA_FILENAME, "rb");
+    if (fp == NULL)
+      {
+        perror ("cannot open data file");
+        return 1;
+      }
+    char buf[1024];
+    int nread = fread (buf, 1, sizeof (buf), fp);
+    if (!(nread == 11 && memcmp (buf, "Halle Potta", 11) == 0))
+      {
+        fprintf (stderr, "data file wrong: has %d bytes, expected %d bytes\n", nread, 11);
+        return 1;
+      }
+    if (fclose (fp))
+      {
+        perror ("cannot close data file");
+        return 1;
+      }
+  }
+
+  /* Clean up data file.  */
+  unlink (DATA_FILENAME);
+
+  return 0;
+}
diff --git a/tests/test-posix_spawnp-script.c b/tests/test-posix_spawnp-script.c
new file mode 100644 (file)
index 0000000..04bf496
--- /dev/null
@@ -0,0 +1,143 @@
+/* Test of posix_spawnp() function.
+   Copyright (C) 2020 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <https://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+#include <spawn.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "macros.h"
+
+#define DATA_FILENAME "test-posix_spawn-script.tmp"
+
+int
+main ()
+{
+  unlink (DATA_FILENAME);
+
+  /* Check an invocation of an executable script.
+     This should only be supported if the script has a '#!' marker; otherwise
+     it is unsecure: <https://sourceware.org/bugzilla/show_bug.cgi?id=13134>.
+     POSIX says that the execlp() and execvp() functions support executing
+     shell scripts
+     <https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html>,
+     but this is considered an antiquated feature.  */
+  pid_t child;
+
+  posix_spawn_file_actions_t actions;
+  ASSERT (posix_spawn_file_actions_init (&actions) == 0);
+  ASSERT (posix_spawn_file_actions_addopen (&actions, STDOUT_FILENO,
+                                            DATA_FILENAME,
+                                            O_RDWR | O_CREAT | O_TRUNC, 0600)
+          == 0);
+
+  {
+    const char *prog_path = SRCDIR "executable-script";
+    const char *prog_argv[2] = { prog_path, NULL };
+
+    int err = posix_spawnp (&child, prog_path, &actions, NULL,
+                            (char **) prog_argv, environ);
+    if (err != ENOEXEC)
+      {
+        if (err != 0)
+          {
+            errno = err;
+            perror ("posix_spawn");
+            return 1;
+          }
+
+        /* Wait for child.  */
+        int status = 0;
+        while (waitpid (child, &status, 0) != child)
+          ;
+        if (!WIFEXITED (status))
+          {
+            fprintf (stderr, "subprocess terminated with unexpected wait status %d\n", status);
+            return 1;
+          }
+        int exitstatus = WEXITSTATUS (status);
+        if (exitstatus != 127)
+          {
+            fprintf (stderr, "subprocess terminated with unexpected exit status %d\n", exitstatus);
+            return 1;
+          }
+      }
+  }
+
+  {
+    const char *prog_path = SRCDIR "executable-shell-script";
+    const char *prog_argv[2] = { prog_path, NULL };
+
+    int err = posix_spawnp (&child, prog_path, &actions, NULL,
+                            (char **) prog_argv, environ);
+    if (err != 0)
+      {
+        errno = err;
+        perror ("posix_spawn");
+        return 1;
+      }
+
+    posix_spawn_file_actions_destroy (&actions);
+
+    /* Wait for child.  */
+    int status = 0;
+    while (waitpid (child, &status, 0) != child)
+      ;
+    if (!WIFEXITED (status))
+      {
+        fprintf (stderr, "subprocess terminated with unexpected wait status %d\n", status);
+        return 1;
+      }
+    int exitstatus = WEXITSTATUS (status);
+    if (exitstatus != 0)
+      {
+        fprintf (stderr, "subprocess terminated with unexpected exit status %d\n", exitstatus);
+        return 1;
+      }
+
+    /* Check the contents of the data file.  */
+    FILE *fp = fopen (DATA_FILENAME, "rb");
+    if (fp == NULL)
+      {
+        perror ("cannot open data file");
+        return 1;
+      }
+    char buf[1024];
+    int nread = fread (buf, 1, sizeof (buf), fp);
+    if (!(nread == 11 && memcmp (buf, "Halle Potta", 11) == 0))
+      {
+        fprintf (stderr, "data file wrong: has %d bytes, expected %d bytes\n", nread, 11);
+        return 1;
+      }
+    if (fclose (fp))
+      {
+        perror ("cannot close data file");
+        return 1;
+      }
+  }
+
+  /* Clean up data file.  */
+  unlink (DATA_FILENAME);
+
+  return 0;
+}
diff --git a/tests/test-spawn-pipe-script.c b/tests/test-spawn-pipe-script.c
new file mode 100644 (file)
index 0000000..44f9d2c
--- /dev/null
@@ -0,0 +1,100 @@
+/* Test of create_pipe_in/wait_subprocess.
+   Copyright (C) 2020 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <https://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+#include "spawn-pipe.h"
+#include "wait-process.h"
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "macros.h"
+
+int
+main ()
+{
+  /* Check an invocation of an executable script.
+     This should only be supported if the script has a '#!' marker; otherwise
+     it is unsecure: <https://sourceware.org/bugzilla/show_bug.cgi?id=13134>.
+     POSIX says that the execlp() and execvp() functions support executing
+     shell scripts
+     <https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html>,
+     but this is considered an antiquated feature.  */
+  int fd[1];
+  pid_t pid;
+
+  {
+    const char *progname = "executable-script";
+    const char *prog_path = SRCDIR "executable-script";
+    const char *prog_argv[2] = { prog_path, NULL };
+
+    pid = create_pipe_in (progname, prog_argv[0], prog_argv, NULL,
+                          NULL, false, true, false, fd);
+    if (pid >= 0)
+      {
+        /* Wait for child.  */
+        ASSERT (wait_subprocess (pid, progname, true, true, true, false, NULL)
+                == 127);
+      }
+    else
+      {
+        ASSERT (pid == -1);
+#if defined _WIN32 && !defined __CYGWIN__
+        ASSERT (errno == ENOENT);
+#else
+        ASSERT (errno == ENOEXEC);
+#endif
+      }
+  }
+
+#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 *progname = "executable-shell-script";
+    const char *prog_path = SRCDIR "executable-shell-script";
+    const char *prog_argv[2] = { prog_path, NULL };
+
+    pid = create_pipe_in (progname, prog_argv[0], prog_argv, NULL,
+                          NULL, false, true, false, fd);
+    ASSERT (pid >= 0);
+    ASSERT (fd[0] > STDERR_FILENO);
+
+    /* Get child's output.  */
+    char buffer[1024];
+    FILE *fp = fdopen (fd[0], "r");
+    ASSERT (fp != NULL);
+    ASSERT (fread (buffer, 1, sizeof (buffer), fp) == 11);
+
+    /* Check the result.  */
+    ASSERT (memcmp (buffer, "Halle Potta", 11) == 0);
+
+    /* Wait for child.  */
+    ASSERT (wait_subprocess (pid, progname, true, false, true, true, NULL) == 0);
+
+    ASSERT (fclose (fp) == 0);
+  }
+
+  return 0;
+#endif
+}