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