xref: /netbsd-src/external/gpl3/gcc.old/dist/libgfortran/intrinsics/execute_command_line.c (revision 4c3eb207d36f67d31994830c0a694161fc1ca39b)
1627f7eb2Smrg /* Implementation of the EXECUTE_COMMAND_LINE intrinsic.
2*4c3eb207Smrg    Copyright (C) 2009-2020 Free Software Foundation, Inc.
3627f7eb2Smrg    Contributed by François-Xavier Coudert.
4627f7eb2Smrg 
5627f7eb2Smrg This file is part of the GNU Fortran runtime library (libgfortran).
6627f7eb2Smrg 
7627f7eb2Smrg Libgfortran is free software; you can redistribute it and/or modify it under
8627f7eb2Smrg the terms of the GNU General Public License as published by the Free
9627f7eb2Smrg Software Foundation; either version 3, or (at your option) any later
10627f7eb2Smrg version.
11627f7eb2Smrg 
12627f7eb2Smrg Libgfortran is distributed in the hope that it will be useful, but WITHOUT
13627f7eb2Smrg ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14627f7eb2Smrg FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15627f7eb2Smrg for more details.
16627f7eb2Smrg 
17627f7eb2Smrg Under Section 7 of GPL version 3, you are granted additional
18627f7eb2Smrg permissions described in the GCC Runtime Library Exception, version
19627f7eb2Smrg 3.1, as published by the Free Software Foundation.
20627f7eb2Smrg 
21627f7eb2Smrg You should have received a copy of the GNU General Public License and
22627f7eb2Smrg a copy of the GCC Runtime Library Exception along with this program;
23627f7eb2Smrg see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
24627f7eb2Smrg <http://www.gnu.org/licenses/>.  */
25627f7eb2Smrg 
26627f7eb2Smrg #include "libgfortran.h"
27627f7eb2Smrg #include <string.h>
28627f7eb2Smrg 
29627f7eb2Smrg #ifdef HAVE_UNISTD_H
30627f7eb2Smrg #include <unistd.h>
31627f7eb2Smrg #endif
32627f7eb2Smrg #ifdef  HAVE_SYS_WAIT_H
33627f7eb2Smrg #include <sys/wait.h>
34627f7eb2Smrg #endif
35627f7eb2Smrg #ifdef HAVE_POSIX_SPAWN
36627f7eb2Smrg #include <spawn.h>
37*4c3eb207Smrg # ifdef __APPLE__
38*4c3eb207Smrg #  include <crt_externs.h>
39*4c3eb207Smrg #  define environ (*_NSGetEnviron ())
40*4c3eb207Smrg # else
41627f7eb2Smrg extern char **environ;
42627f7eb2Smrg # endif
43*4c3eb207Smrg #endif
44627f7eb2Smrg #if defined(HAVE_POSIX_SPAWN) || defined(HAVE_FORK)
45627f7eb2Smrg #include <signal.h>
46627f7eb2Smrg #endif
47627f7eb2Smrg 
48627f7eb2Smrg enum { EXEC_SYNCHRONOUS = -2, EXEC_NOERROR = 0, EXEC_SYSTEMFAILED,
49627f7eb2Smrg        EXEC_CHILDFAILED, EXEC_INVALIDCOMMAND };
50627f7eb2Smrg static const char *cmdmsg_values[] =
51627f7eb2Smrg   { "",
52627f7eb2Smrg     "Termination status of the command-language interpreter cannot be obtained",
53627f7eb2Smrg     "Execution of child process impossible",
54627f7eb2Smrg     "Invalid command line" };
55627f7eb2Smrg 
56627f7eb2Smrg 
57627f7eb2Smrg 
58627f7eb2Smrg static void
set_cmdstat(int * cmdstat,int value)59627f7eb2Smrg set_cmdstat (int *cmdstat, int value)
60627f7eb2Smrg {
61627f7eb2Smrg   if (cmdstat)
62627f7eb2Smrg     *cmdstat = value;
63627f7eb2Smrg   else if (value > EXEC_NOERROR)
64627f7eb2Smrg     {
65627f7eb2Smrg #define MSGLEN 200
66627f7eb2Smrg       char msg[MSGLEN] = "EXECUTE_COMMAND_LINE: ";
67627f7eb2Smrg       strncat (msg, cmdmsg_values[value], MSGLEN - strlen(msg) - 1);
68627f7eb2Smrg       runtime_error ("%s", msg);
69627f7eb2Smrg     }
70627f7eb2Smrg }
71627f7eb2Smrg 
72627f7eb2Smrg 
73627f7eb2Smrg #if defined(HAVE_WAITPID) && defined(HAVE_SIGACTION)
74627f7eb2Smrg static void
sigchld_handler(int signum)75627f7eb2Smrg sigchld_handler (int signum __attribute__((unused)))
76627f7eb2Smrg {
77627f7eb2Smrg   while (waitpid ((pid_t)(-1), NULL, WNOHANG) > 0) {}
78627f7eb2Smrg }
79627f7eb2Smrg #endif
80627f7eb2Smrg 
81627f7eb2Smrg static void
execute_command_line(const char * command,bool wait,int * exitstat,int * cmdstat,char * cmdmsg,gfc_charlen_type command_len,gfc_charlen_type cmdmsg_len)82627f7eb2Smrg execute_command_line (const char *command, bool wait, int *exitstat,
83627f7eb2Smrg 		      int *cmdstat, char *cmdmsg,
84627f7eb2Smrg 		      gfc_charlen_type command_len,
85627f7eb2Smrg 		      gfc_charlen_type cmdmsg_len)
86627f7eb2Smrg {
87627f7eb2Smrg   /* Transform the Fortran string to a C string.  */
88627f7eb2Smrg   char *cmd = fc_strdup (command, command_len);
89627f7eb2Smrg 
90627f7eb2Smrg   /* Flush all I/O units before executing the command.  */
91627f7eb2Smrg   flush_all_units();
92627f7eb2Smrg 
93627f7eb2Smrg #if defined(HAVE_POSIX_SPAWN) || defined(HAVE_FORK)
94627f7eb2Smrg   if (!wait)
95627f7eb2Smrg     {
96627f7eb2Smrg       /* Asynchronous execution.  */
97627f7eb2Smrg       pid_t pid;
98627f7eb2Smrg 
99627f7eb2Smrg       set_cmdstat (cmdstat, EXEC_NOERROR);
100627f7eb2Smrg 
101627f7eb2Smrg #if defined(HAVE_SIGACTION) && defined(HAVE_WAITPID)
102627f7eb2Smrg       static bool sig_init_saved;
103627f7eb2Smrg       bool sig_init = __atomic_load_n (&sig_init_saved, __ATOMIC_RELAXED);
104627f7eb2Smrg       if (!sig_init)
105627f7eb2Smrg 	{
106627f7eb2Smrg 	  struct sigaction sa;
107627f7eb2Smrg 	  sa.sa_handler = &sigchld_handler;
108627f7eb2Smrg 	  sigemptyset(&sa.sa_mask);
109627f7eb2Smrg 	  sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
110627f7eb2Smrg 	  sigaction(SIGCHLD, &sa, 0);
111627f7eb2Smrg 	  __atomic_store_n (&sig_init_saved, true, __ATOMIC_RELAXED);
112627f7eb2Smrg 	}
113627f7eb2Smrg #endif
114627f7eb2Smrg 
115627f7eb2Smrg #ifdef HAVE_POSIX_SPAWN
116627f7eb2Smrg       const char * const argv[] = {"sh", "-c", cmd, NULL};
117627f7eb2Smrg       if (posix_spawn (&pid, "/bin/sh", NULL, NULL,
118627f7eb2Smrg 		       (char * const* restrict) argv, environ))
119627f7eb2Smrg 	set_cmdstat (cmdstat, EXEC_CHILDFAILED);
120627f7eb2Smrg #elif defined(HAVE_FORK)
121627f7eb2Smrg       if ((pid = fork()) < 0)
122627f7eb2Smrg         set_cmdstat (cmdstat, EXEC_CHILDFAILED);
123627f7eb2Smrg       else if (pid == 0)
124627f7eb2Smrg 	{
125627f7eb2Smrg 	  /* Child process.  */
126627f7eb2Smrg 	  int res = system (cmd);
127627f7eb2Smrg 	  _exit (WIFEXITED(res) ? WEXITSTATUS(res) : res);
128627f7eb2Smrg 	}
129627f7eb2Smrg #endif
130627f7eb2Smrg     }
131627f7eb2Smrg   else
132627f7eb2Smrg #endif
133627f7eb2Smrg     {
134627f7eb2Smrg       /* Synchronous execution.  */
135627f7eb2Smrg       int res = system (cmd);
136627f7eb2Smrg 
137627f7eb2Smrg       if (res == -1)
138627f7eb2Smrg 	set_cmdstat (cmdstat, EXEC_SYSTEMFAILED);
139627f7eb2Smrg #if !defined(HAVE_POSIX_SPAWN) && !defined(HAVE_FORK)
140627f7eb2Smrg       else if (!wait)
141627f7eb2Smrg 	set_cmdstat (cmdstat, EXEC_SYNCHRONOUS);
142627f7eb2Smrg #endif
143627f7eb2Smrg       else if (res == 127 || res == 126
144627f7eb2Smrg #if defined(WEXITSTATUS) && defined(WIFEXITED)
145627f7eb2Smrg 	       || (WIFEXITED(res) && WEXITSTATUS(res) == 127)
146627f7eb2Smrg 	       || (WIFEXITED(res) && WEXITSTATUS(res) == 126)
147627f7eb2Smrg #endif
148627f7eb2Smrg 	       )
149627f7eb2Smrg 	/* Shell return codes 126 and 127 mean that the command line could
150627f7eb2Smrg 	   not be executed for various reasons.  */
151627f7eb2Smrg 	set_cmdstat (cmdstat, EXEC_INVALIDCOMMAND);
152627f7eb2Smrg       else
153627f7eb2Smrg 	set_cmdstat (cmdstat, EXEC_NOERROR);
154627f7eb2Smrg 
155627f7eb2Smrg       if (res != -1)
156627f7eb2Smrg 	{
157627f7eb2Smrg #if defined(WEXITSTATUS) && defined(WIFEXITED)
158627f7eb2Smrg 	  *exitstat = WIFEXITED(res) ? WEXITSTATUS(res) : res;
159627f7eb2Smrg #else
160627f7eb2Smrg 	  *exitstat = res;
161627f7eb2Smrg #endif
162627f7eb2Smrg 	}
163627f7eb2Smrg     }
164627f7eb2Smrg 
165627f7eb2Smrg   free (cmd);
166627f7eb2Smrg 
167627f7eb2Smrg   /* Now copy back to the Fortran string if needed.  */
168627f7eb2Smrg   if (cmdstat && *cmdstat > EXEC_NOERROR && cmdmsg)
169627f7eb2Smrg     fstrcpy (cmdmsg, cmdmsg_len, cmdmsg_values[*cmdstat],
170627f7eb2Smrg 		strlen (cmdmsg_values[*cmdstat]));
171627f7eb2Smrg }
172627f7eb2Smrg 
173627f7eb2Smrg 
174627f7eb2Smrg extern void
175627f7eb2Smrg execute_command_line_i4 (const char *command, GFC_LOGICAL_4 *wait,
176627f7eb2Smrg 			 GFC_INTEGER_4 *exitstat, GFC_INTEGER_4 *cmdstat,
177627f7eb2Smrg 			 char *cmdmsg, gfc_charlen_type command_len,
178627f7eb2Smrg 			 gfc_charlen_type cmdmsg_len);
179627f7eb2Smrg export_proto(execute_command_line_i4);
180627f7eb2Smrg 
181627f7eb2Smrg void
execute_command_line_i4(const char * command,GFC_LOGICAL_4 * wait,GFC_INTEGER_4 * exitstat,GFC_INTEGER_4 * cmdstat,char * cmdmsg,gfc_charlen_type command_len,gfc_charlen_type cmdmsg_len)182627f7eb2Smrg execute_command_line_i4 (const char *command, GFC_LOGICAL_4 *wait,
183627f7eb2Smrg 			 GFC_INTEGER_4 *exitstat, GFC_INTEGER_4 *cmdstat,
184627f7eb2Smrg 			 char *cmdmsg, gfc_charlen_type command_len,
185627f7eb2Smrg 			 gfc_charlen_type cmdmsg_len)
186627f7eb2Smrg {
187627f7eb2Smrg   bool w = wait ? *wait : true;
188627f7eb2Smrg   int estat, estat_initial, cstat;
189627f7eb2Smrg 
190627f7eb2Smrg   if (exitstat)
191627f7eb2Smrg     estat_initial = estat = *exitstat;
192627f7eb2Smrg 
193627f7eb2Smrg   execute_command_line (command, w, &estat, cmdstat ? &cstat : NULL,
194627f7eb2Smrg 			cmdmsg, command_len, cmdmsg_len);
195627f7eb2Smrg 
196627f7eb2Smrg   if (exitstat && estat != estat_initial)
197627f7eb2Smrg     *exitstat = estat;
198627f7eb2Smrg   if (cmdstat)
199627f7eb2Smrg     *cmdstat = cstat;
200627f7eb2Smrg }
201627f7eb2Smrg 
202627f7eb2Smrg 
203627f7eb2Smrg extern void
204627f7eb2Smrg execute_command_line_i8 (const char *command, GFC_LOGICAL_8 *wait,
205627f7eb2Smrg 			 GFC_INTEGER_8 *exitstat, GFC_INTEGER_8 *cmdstat,
206627f7eb2Smrg 			 char *cmdmsg, gfc_charlen_type command_len,
207627f7eb2Smrg 			 gfc_charlen_type cmdmsg_len);
208627f7eb2Smrg export_proto(execute_command_line_i8);
209627f7eb2Smrg 
210627f7eb2Smrg void
execute_command_line_i8(const char * command,GFC_LOGICAL_8 * wait,GFC_INTEGER_8 * exitstat,GFC_INTEGER_8 * cmdstat,char * cmdmsg,gfc_charlen_type command_len,gfc_charlen_type cmdmsg_len)211627f7eb2Smrg execute_command_line_i8 (const char *command, GFC_LOGICAL_8 *wait,
212627f7eb2Smrg 			 GFC_INTEGER_8 *exitstat, GFC_INTEGER_8 *cmdstat,
213627f7eb2Smrg 			 char *cmdmsg, gfc_charlen_type command_len,
214627f7eb2Smrg 			 gfc_charlen_type cmdmsg_len)
215627f7eb2Smrg {
216627f7eb2Smrg   bool w = wait ? *wait : true;
217627f7eb2Smrg   int estat, estat_initial, cstat;
218627f7eb2Smrg 
219627f7eb2Smrg   if (exitstat)
220627f7eb2Smrg     estat_initial = estat = *exitstat;
221627f7eb2Smrg 
222627f7eb2Smrg   execute_command_line (command, w, &estat, cmdstat ? &cstat : NULL,
223627f7eb2Smrg 			cmdmsg, command_len, cmdmsg_len);
224627f7eb2Smrg 
225627f7eb2Smrg   if (exitstat && estat != estat_initial)
226627f7eb2Smrg     *exitstat = estat;
227627f7eb2Smrg   if (cmdstat)
228627f7eb2Smrg     *cmdstat = cstat;
229627f7eb2Smrg }
230