openat: don’t close (-1)
[gnulib.git] / tests / test-posix_spawn-script.c
blobe00b38da07a3047011b8fc75c363c38aeb487309
1 /* Test of posix_spawn() function.
2 Copyright (C) 2020-2024 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3, or (at your option)
7 any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, see <https://www.gnu.org/licenses/>. */
17 #include <config.h>
19 #include <spawn.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <sys/types.h>
27 #include <sys/wait.h>
29 #include "macros.h"
31 #define DATA_FILENAME "test-posix_spawn-script.tmp"
33 int
34 main ()
36 unlink (DATA_FILENAME);
38 /* Check an invocation of an executable script.
39 This should only be supported if the script has a '#!' marker; otherwise
40 it is unsecure: <https://sourceware.org/bugzilla/show_bug.cgi?id=13134>.
41 POSIX says that the execlp() and execvp() functions support executing
42 shell scripts
43 <https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html>,
44 but this is considered an antiquated feature. */
45 pid_t child;
47 posix_spawn_file_actions_t actions;
48 ASSERT (posix_spawn_file_actions_init (&actions) == 0);
49 ASSERT (posix_spawn_file_actions_addopen (&actions, STDOUT_FILENO,
50 DATA_FILENAME,
51 O_RDWR | O_CREAT | O_TRUNC, 0600)
52 == 0);
55 size_t i;
57 for (i = 0; i < 2; i++)
59 const char *prog_path =
60 (i == 0 ? SRCDIR "executable-script" : SRCDIR "executable-script.sh");
61 const char *prog_argv[2] = { prog_path, NULL };
63 int err = posix_spawn (&child, prog_path, &actions, NULL,
64 (char **) prog_argv, environ);
65 if (err != ENOEXEC)
67 if (err != 0)
69 errno = err;
70 perror ("posix_spawn");
71 return 1;
74 /* Wait for child. */
75 int status = 0;
76 while (waitpid (child, &status, 0) != child)
78 if (!WIFEXITED (status))
80 fprintf (stderr, "subprocess terminated with unexpected wait status %d\n", status);
81 return 1;
83 int exitstatus = WEXITSTATUS (status);
84 if (exitstatus != 127)
86 fprintf (stderr, "subprocess terminated with unexpected exit status %d\n", exitstatus);
87 return 1;
93 #if defined _WIN32 && !defined __CYGWIN__
94 /* On native Windows, scripts - even with '#!' marker - are not executable.
95 Only .bat and .cmd files are. */
96 if (test_exit_status != EXIT_SUCCESS)
97 return test_exit_status;
98 fprintf (stderr, "Skipping test: scripts are not executable on this platform.\n");
99 return 77;
100 #else
102 const char *prog_path = SRCDIR "executable-shell-script";
103 const char *prog_argv[2] = { prog_path, NULL };
105 int err = posix_spawn (&child, prog_path, &actions, NULL,
106 (char **) prog_argv, environ);
107 if (err != 0)
109 errno = err;
110 perror ("posix_spawn");
111 return 1;
114 posix_spawn_file_actions_destroy (&actions);
116 /* Wait for child. */
117 int status = 0;
118 while (waitpid (child, &status, 0) != child)
120 if (!WIFEXITED (status))
122 fprintf (stderr, "subprocess terminated with unexpected wait status %d\n", status);
123 return 1;
125 int exitstatus = WEXITSTATUS (status);
126 if (exitstatus != 0)
128 fprintf (stderr, "subprocess terminated with unexpected exit status %d\n", exitstatus);
129 return 1;
132 /* Check the contents of the data file. */
133 FILE *fp = fopen (DATA_FILENAME, "rb");
134 if (fp == NULL)
136 perror ("cannot open data file");
137 return 1;
139 char buf[1024];
140 int nread = fread (buf, 1, sizeof (buf), fp);
141 if (!(nread == 11 && memcmp (buf, "Halle Potta", 11) == 0))
143 fprintf (stderr, "data file wrong: has %d bytes, expected %d bytes\n", nread, 11);
144 return 1;
146 if (fclose (fp))
148 perror ("cannot close data file");
149 return 1;
152 #endif
154 /* Clean up data file. */
155 unlink (DATA_FILENAME);
157 return test_exit_status;