1 /* Implementation of the EXECUTE_COMMAND_LINE intrinsic. 2 Copyright (C) 2009-2020 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 # ifdef __APPLE__ 38 # include <crt_externs.h> 39 # define environ (*_NSGetEnviron ()) 40 # else 41 extern char **environ; 42 # endif 43 #endif 44 #if defined(HAVE_POSIX_SPAWN) || defined(HAVE_FORK) 45 #include <signal.h> 46 #endif 47 48 enum { EXEC_SYNCHRONOUS = -2, EXEC_NOERROR = 0, EXEC_SYSTEMFAILED, 49 EXEC_CHILDFAILED, EXEC_INVALIDCOMMAND }; 50 static const char *cmdmsg_values[] = 51 { "", 52 "Termination status of the command-language interpreter cannot be obtained", 53 "Execution of child process impossible", 54 "Invalid command line" }; 55 56 57 58 static void 59 set_cmdstat (int *cmdstat, int value) 60 { 61 if (cmdstat) 62 *cmdstat = value; 63 else if (value > EXEC_NOERROR) 64 { 65 #define MSGLEN 200 66 char msg[MSGLEN] = "EXECUTE_COMMAND_LINE: "; 67 strncat (msg, cmdmsg_values[value], MSGLEN - strlen(msg) - 1); 68 runtime_error ("%s", msg); 69 } 70 } 71 72 73 #if defined(HAVE_WAITPID) && defined(HAVE_SIGACTION) 74 static void 75 sigchld_handler (int signum __attribute__((unused))) 76 { 77 while (waitpid ((pid_t)(-1), NULL, WNOHANG) > 0) {} 78 } 79 #endif 80 81 static void 82 execute_command_line (const char *command, bool wait, int *exitstat, 83 int *cmdstat, char *cmdmsg, 84 gfc_charlen_type command_len, 85 gfc_charlen_type cmdmsg_len) 86 { 87 /* Transform the Fortran string to a C string. */ 88 char *cmd = fc_strdup (command, command_len); 89 90 /* Flush all I/O units before executing the command. */ 91 flush_all_units(); 92 93 #if defined(HAVE_POSIX_SPAWN) || defined(HAVE_FORK) 94 if (!wait) 95 { 96 /* Asynchronous execution. */ 97 pid_t pid; 98 99 set_cmdstat (cmdstat, EXEC_NOERROR); 100 101 #if defined(HAVE_SIGACTION) && defined(HAVE_WAITPID) 102 static bool sig_init_saved; 103 bool sig_init = __atomic_load_n (&sig_init_saved, __ATOMIC_RELAXED); 104 if (!sig_init) 105 { 106 struct sigaction sa; 107 sa.sa_handler = &sigchld_handler; 108 sigemptyset(&sa.sa_mask); 109 sa.sa_flags = SA_RESTART | SA_NOCLDSTOP; 110 sigaction(SIGCHLD, &sa, 0); 111 __atomic_store_n (&sig_init_saved, true, __ATOMIC_RELAXED); 112 } 113 #endif 114 115 #ifdef HAVE_POSIX_SPAWN 116 const char * const argv[] = {"sh", "-c", cmd, NULL}; 117 if (posix_spawn (&pid, "/bin/sh", NULL, NULL, 118 (char * const* restrict) argv, environ)) 119 set_cmdstat (cmdstat, EXEC_CHILDFAILED); 120 #elif defined(HAVE_FORK) 121 if ((pid = fork()) < 0) 122 set_cmdstat (cmdstat, EXEC_CHILDFAILED); 123 else if (pid == 0) 124 { 125 /* Child process. */ 126 int res = system (cmd); 127 _exit (WIFEXITED(res) ? WEXITSTATUS(res) : res); 128 } 129 #endif 130 } 131 else 132 #endif 133 { 134 /* Synchronous execution. */ 135 int res = system (cmd); 136 137 if (res == -1) 138 set_cmdstat (cmdstat, EXEC_SYSTEMFAILED); 139 #if !defined(HAVE_POSIX_SPAWN) && !defined(HAVE_FORK) 140 else if (!wait) 141 set_cmdstat (cmdstat, EXEC_SYNCHRONOUS); 142 #endif 143 else if (res == 127 || res == 126 144 #if defined(WEXITSTATUS) && defined(WIFEXITED) 145 || (WIFEXITED(res) && WEXITSTATUS(res) == 127) 146 || (WIFEXITED(res) && WEXITSTATUS(res) == 126) 147 #endif 148 ) 149 /* Shell return codes 126 and 127 mean that the command line could 150 not be executed for various reasons. */ 151 set_cmdstat (cmdstat, EXEC_INVALIDCOMMAND); 152 else 153 set_cmdstat (cmdstat, EXEC_NOERROR); 154 155 if (res != -1) 156 { 157 #if defined(WEXITSTATUS) && defined(WIFEXITED) 158 *exitstat = WIFEXITED(res) ? WEXITSTATUS(res) : res; 159 #else 160 *exitstat = res; 161 #endif 162 } 163 } 164 165 free (cmd); 166 167 /* Now copy back to the Fortran string if needed. */ 168 if (cmdstat && *cmdstat > EXEC_NOERROR && cmdmsg) 169 fstrcpy (cmdmsg, cmdmsg_len, cmdmsg_values[*cmdstat], 170 strlen (cmdmsg_values[*cmdstat])); 171 } 172 173 174 extern void 175 execute_command_line_i4 (const char *command, GFC_LOGICAL_4 *wait, 176 GFC_INTEGER_4 *exitstat, GFC_INTEGER_4 *cmdstat, 177 char *cmdmsg, gfc_charlen_type command_len, 178 gfc_charlen_type cmdmsg_len); 179 export_proto(execute_command_line_i4); 180 181 void 182 execute_command_line_i4 (const char *command, GFC_LOGICAL_4 *wait, 183 GFC_INTEGER_4 *exitstat, GFC_INTEGER_4 *cmdstat, 184 char *cmdmsg, gfc_charlen_type command_len, 185 gfc_charlen_type cmdmsg_len) 186 { 187 bool w = wait ? *wait : true; 188 int estat, estat_initial, cstat; 189 190 if (exitstat) 191 estat_initial = estat = *exitstat; 192 193 execute_command_line (command, w, &estat, cmdstat ? &cstat : NULL, 194 cmdmsg, command_len, cmdmsg_len); 195 196 if (exitstat && estat != estat_initial) 197 *exitstat = estat; 198 if (cmdstat) 199 *cmdstat = cstat; 200 } 201 202 203 extern void 204 execute_command_line_i8 (const char *command, GFC_LOGICAL_8 *wait, 205 GFC_INTEGER_8 *exitstat, GFC_INTEGER_8 *cmdstat, 206 char *cmdmsg, gfc_charlen_type command_len, 207 gfc_charlen_type cmdmsg_len); 208 export_proto(execute_command_line_i8); 209 210 void 211 execute_command_line_i8 (const char *command, GFC_LOGICAL_8 *wait, 212 GFC_INTEGER_8 *exitstat, GFC_INTEGER_8 *cmdstat, 213 char *cmdmsg, gfc_charlen_type command_len, 214 gfc_charlen_type cmdmsg_len) 215 { 216 bool w = wait ? *wait : true; 217 int estat, estat_initial, cstat; 218 219 if (exitstat) 220 estat_initial = estat = *exitstat; 221 222 execute_command_line (command, w, &estat, cmdstat ? &cstat : NULL, 223 cmdmsg, command_len, cmdmsg_len); 224 225 if (exitstat && estat != estat_initial) 226 *exitstat = estat; 227 if (cmdstat) 228 *cmdstat = cstat; 229 } 230