1 /* Test of posix_spawnp() function.
2    Copyright (C) 2020-2021 Free Software Foundation, Inc.
3 
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.
8 
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.
13 
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/>.  */
16 
17 #include <config.h>
18 
19 #include <spawn.h>
20 
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>
28 
29 #include "macros.h"
30 
31 #define DATA_FILENAME "test-posix_spawn-script.tmp"
32 
33 int
main()34 main ()
35 {
36   unlink (DATA_FILENAME);
37 
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;
46 
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);
53 
54   {
55     size_t i;
56 
57     for (i = 0; i < 2; i++)
58       {
59         const char *prog_path =
60           (i == 0 ? SRCDIR "executable-script" : SRCDIR "executable-script.sh");
61         const char *prog_argv[2] = { prog_path, NULL };
62 
63         int err = posix_spawnp (&child, prog_path, &actions, NULL,
64                                 (char **) prog_argv, environ);
65         if (err != ENOEXEC)
66           {
67             if (err != 0)
68               {
69                 errno = err;
70                 perror ("posix_spawn");
71                 return 1;
72               }
73 
74             /* Wait for child.  */
75             int status = 0;
76             while (waitpid (child, &status, 0) != child)
77               ;
78             if (!WIFEXITED (status))
79               {
80                 fprintf (stderr, "subprocess terminated with unexpected wait status %d\n", status);
81                 return 1;
82               }
83             int exitstatus = WEXITSTATUS (status);
84             if (exitstatus != 127)
85               {
86                 fprintf (stderr, "subprocess terminated with unexpected exit status %d\n", exitstatus);
87                 return 1;
88               }
89           }
90       }
91   }
92 
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   fprintf (stderr, "Skipping test: scripts are not executable on this platform.\n");
97   return 77;
98 #else
99   {
100     const char *prog_path = SRCDIR "executable-shell-script";
101     const char *prog_argv[2] = { prog_path, NULL };
102 
103     int err = posix_spawnp (&child, prog_path, &actions, NULL,
104                             (char **) prog_argv, environ);
105     if (err != 0)
106       {
107         errno = err;
108         perror ("posix_spawn");
109         return 1;
110       }
111 
112     posix_spawn_file_actions_destroy (&actions);
113 
114     /* Wait for child.  */
115     int status = 0;
116     while (waitpid (child, &status, 0) != child)
117       ;
118     if (!WIFEXITED (status))
119       {
120         fprintf (stderr, "subprocess terminated with unexpected wait status %d\n", status);
121         return 1;
122       }
123     int exitstatus = WEXITSTATUS (status);
124     if (exitstatus != 0)
125       {
126         fprintf (stderr, "subprocess terminated with unexpected exit status %d\n", exitstatus);
127         return 1;
128       }
129 
130     /* Check the contents of the data file.  */
131     FILE *fp = fopen (DATA_FILENAME, "rb");
132     if (fp == NULL)
133       {
134         perror ("cannot open data file");
135         return 1;
136       }
137     char buf[1024];
138     int nread = fread (buf, 1, sizeof (buf), fp);
139     if (!(nread == 11 && memcmp (buf, "Halle Potta", 11) == 0))
140       {
141         fprintf (stderr, "data file wrong: has %d bytes, expected %d bytes\n", nread, 11);
142         return 1;
143       }
144     if (fclose (fp))
145       {
146         perror ("cannot close data file");
147         return 1;
148       }
149   }
150 #endif
151 
152   /* Clean up data file.  */
153   unlink (DATA_FILENAME);
154 
155   return 0;
156 }
157