xref: /netbsd-src/external/gpl3/gcc.old/dist/libgfortran/intrinsics/execute_command_line.c (revision 82d56013d7b633d116a93943de88e08335357a7c)
1 /* Implementation of the EXECUTE_COMMAND_LINE intrinsic.
2    Copyright (C) 2009-2019 Free Software Foundation, Inc.
3    Contributed by François-Xavier Coudert.
4 
5 This file is part of the GNU Fortran runtime library (libgfortran).
6 
7 Libgfortran is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 3, or (at your option) any later
10 version.
11 
12 Libgfortran is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16 
17 Under Section 7 of GPL version 3, you are granted additional
18 permissions described in the GCC Runtime Library Exception, version
19 3.1, as published by the Free Software Foundation.
20 
21 You should have received a copy of the GNU General Public License and
22 a copy of the GCC Runtime Library Exception along with this program;
23 see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
24 <http://www.gnu.org/licenses/>.  */
25 
26 #include "libgfortran.h"
27 #include <string.h>
28 
29 #ifdef HAVE_UNISTD_H
30 #include <unistd.h>
31 #endif
32 #ifdef  HAVE_SYS_WAIT_H
33 #include <sys/wait.h>
34 #endif
35 #ifdef HAVE_POSIX_SPAWN
36 #include <spawn.h>
37 extern char **environ;
38 #endif
39 #if defined(HAVE_POSIX_SPAWN) || defined(HAVE_FORK)
40 #include <signal.h>
41 #endif
42 
43 enum { EXEC_SYNCHRONOUS = -2, EXEC_NOERROR = 0, EXEC_SYSTEMFAILED,
44        EXEC_CHILDFAILED, EXEC_INVALIDCOMMAND };
45 static const char *cmdmsg_values[] =
46   { "",
47     "Termination status of the command-language interpreter cannot be obtained",
48     "Execution of child process impossible",
49     "Invalid command line" };
50 
51 
52 
53 static void
54 set_cmdstat (int *cmdstat, int value)
55 {
56   if (cmdstat)
57     *cmdstat = value;
58   else if (value > EXEC_NOERROR)
59     {
60 #define MSGLEN 200
61       char msg[MSGLEN] = "EXECUTE_COMMAND_LINE: ";
62       strncat (msg, cmdmsg_values[value], MSGLEN - strlen(msg) - 1);
63       runtime_error ("%s", msg);
64     }
65 }
66 
67 
68 #if defined(HAVE_WAITPID) && defined(HAVE_SIGACTION)
69 static void
70 sigchld_handler (int signum __attribute__((unused)))
71 {
72   while (waitpid ((pid_t)(-1), NULL, WNOHANG) > 0) {}
73 }
74 #endif
75 
76 static void
77 execute_command_line (const char *command, bool wait, int *exitstat,
78 		      int *cmdstat, char *cmdmsg,
79 		      gfc_charlen_type command_len,
80 		      gfc_charlen_type cmdmsg_len)
81 {
82   /* Transform the Fortran string to a C string.  */
83   char *cmd = fc_strdup (command, command_len);
84 
85   /* Flush all I/O units before executing the command.  */
86   flush_all_units();
87 
88 #if defined(HAVE_POSIX_SPAWN) || defined(HAVE_FORK)
89   if (!wait)
90     {
91       /* Asynchronous execution.  */
92       pid_t pid;
93 
94       set_cmdstat (cmdstat, EXEC_NOERROR);
95 
96 #if defined(HAVE_SIGACTION) && defined(HAVE_WAITPID)
97       static bool sig_init_saved;
98       bool sig_init = __atomic_load_n (&sig_init_saved, __ATOMIC_RELAXED);
99       if (!sig_init)
100 	{
101 	  struct sigaction sa;
102 	  sa.sa_handler = &sigchld_handler;
103 	  sigemptyset(&sa.sa_mask);
104 	  sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
105 	  sigaction(SIGCHLD, &sa, 0);
106 	  __atomic_store_n (&sig_init_saved, true, __ATOMIC_RELAXED);
107 	}
108 #endif
109 
110 #ifdef HAVE_POSIX_SPAWN
111       const char * const argv[] = {"sh", "-c", cmd, NULL};
112       if (posix_spawn (&pid, "/bin/sh", NULL, NULL,
113 		       (char * const* restrict) argv, environ))
114 	set_cmdstat (cmdstat, EXEC_CHILDFAILED);
115 #elif defined(HAVE_FORK)
116       if ((pid = fork()) < 0)
117         set_cmdstat (cmdstat, EXEC_CHILDFAILED);
118       else if (pid == 0)
119 	{
120 	  /* Child process.  */
121 	  int res = system (cmd);
122 	  _exit (WIFEXITED(res) ? WEXITSTATUS(res) : res);
123 	}
124 #endif
125     }
126   else
127 #endif
128     {
129       /* Synchronous execution.  */
130       int res = system (cmd);
131 
132       if (res == -1)
133 	set_cmdstat (cmdstat, EXEC_SYSTEMFAILED);
134 #if !defined(HAVE_POSIX_SPAWN) && !defined(HAVE_FORK)
135       else if (!wait)
136 	set_cmdstat (cmdstat, EXEC_SYNCHRONOUS);
137 #endif
138       else if (res == 127 || res == 126
139 #if defined(WEXITSTATUS) && defined(WIFEXITED)
140 	       || (WIFEXITED(res) && WEXITSTATUS(res) == 127)
141 	       || (WIFEXITED(res) && WEXITSTATUS(res) == 126)
142 #endif
143 	       )
144 	/* Shell return codes 126 and 127 mean that the command line could
145 	   not be executed for various reasons.  */
146 	set_cmdstat (cmdstat, EXEC_INVALIDCOMMAND);
147       else
148 	set_cmdstat (cmdstat, EXEC_NOERROR);
149 
150       if (res != -1)
151 	{
152 #if defined(WEXITSTATUS) && defined(WIFEXITED)
153 	  *exitstat = WIFEXITED(res) ? WEXITSTATUS(res) : res;
154 #else
155 	  *exitstat = res;
156 #endif
157 	}
158     }
159 
160   free (cmd);
161 
162   /* Now copy back to the Fortran string if needed.  */
163   if (cmdstat && *cmdstat > EXEC_NOERROR && cmdmsg)
164     fstrcpy (cmdmsg, cmdmsg_len, cmdmsg_values[*cmdstat],
165 		strlen (cmdmsg_values[*cmdstat]));
166 }
167 
168 
169 extern void
170 execute_command_line_i4 (const char *command, GFC_LOGICAL_4 *wait,
171 			 GFC_INTEGER_4 *exitstat, GFC_INTEGER_4 *cmdstat,
172 			 char *cmdmsg, gfc_charlen_type command_len,
173 			 gfc_charlen_type cmdmsg_len);
174 export_proto(execute_command_line_i4);
175 
176 void
177 execute_command_line_i4 (const char *command, GFC_LOGICAL_4 *wait,
178 			 GFC_INTEGER_4 *exitstat, GFC_INTEGER_4 *cmdstat,
179 			 char *cmdmsg, gfc_charlen_type command_len,
180 			 gfc_charlen_type cmdmsg_len)
181 {
182   bool w = wait ? *wait : true;
183   int estat, estat_initial, cstat;
184 
185   if (exitstat)
186     estat_initial = estat = *exitstat;
187 
188   execute_command_line (command, w, &estat, cmdstat ? &cstat : NULL,
189 			cmdmsg, command_len, cmdmsg_len);
190 
191   if (exitstat && estat != estat_initial)
192     *exitstat = estat;
193   if (cmdstat)
194     *cmdstat = cstat;
195 }
196 
197 
198 extern void
199 execute_command_line_i8 (const char *command, GFC_LOGICAL_8 *wait,
200 			 GFC_INTEGER_8 *exitstat, GFC_INTEGER_8 *cmdstat,
201 			 char *cmdmsg, gfc_charlen_type command_len,
202 			 gfc_charlen_type cmdmsg_len);
203 export_proto(execute_command_line_i8);
204 
205 void
206 execute_command_line_i8 (const char *command, GFC_LOGICAL_8 *wait,
207 			 GFC_INTEGER_8 *exitstat, GFC_INTEGER_8 *cmdstat,
208 			 char *cmdmsg, gfc_charlen_type command_len,
209 			 gfc_charlen_type cmdmsg_len)
210 {
211   bool w = wait ? *wait : true;
212   int estat, estat_initial, cstat;
213 
214   if (exitstat)
215     estat_initial = estat = *exitstat;
216 
217   execute_command_line (command, w, &estat, cmdstat ? &cstat : NULL,
218 			cmdmsg, command_len, cmdmsg_len);
219 
220   if (exitstat && estat != estat_initial)
221     *exitstat = estat;
222   if (cmdstat)
223     *cmdstat = cstat;
224 }
225