]>
Commit | Line | Data |
---|---|---|
c14c8155 | 1 | /* Implementation of the EXECUTE_COMMAND_LINE intrinsic. |
8d9254fc | 2 | Copyright (C) 2009-2020 Free Software Foundation, Inc. |
f4742991 | 3 | Contributed by François-Xavier Coudert. |
c14c8155 FXC |
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> | |
74544378 | 28 | |
c14c8155 FXC |
29 | #ifdef HAVE_UNISTD_H |
30 | #include <unistd.h> | |
31 | #endif | |
32 | #ifdef HAVE_SYS_WAIT_H | |
33 | #include <sys/wait.h> | |
34 | #endif | |
f8886038 JB |
35 | #ifdef HAVE_POSIX_SPAWN |
36 | #include <spawn.h> | |
37 | extern char **environ; | |
38 | #endif | |
ef536b41 JB |
39 | #if defined(HAVE_POSIX_SPAWN) || defined(HAVE_FORK) |
40 | #include <signal.h> | |
41 | #endif | |
c14c8155 | 42 | |
b4224aec | 43 | enum { EXEC_SYNCHRONOUS = -2, EXEC_NOERROR = 0, EXEC_SYSTEMFAILED, |
1487cca0 | 44 | EXEC_CHILDFAILED, EXEC_INVALIDCOMMAND }; |
c14c8155 | 45 | static const char *cmdmsg_values[] = |
b4224aec TB |
46 | { "", |
47 | "Termination status of the command-language interpreter cannot be obtained", | |
1487cca0 FXC |
48 | "Execution of child process impossible", |
49 | "Invalid command line" }; | |
c14c8155 FXC |
50 | |
51 | ||
52 | ||
53 | static void | |
54 | set_cmdstat (int *cmdstat, int value) | |
55 | { | |
56 | if (cmdstat) | |
57 | *cmdstat = value; | |
b4224aec | 58 | else if (value > EXEC_NOERROR) |
1487cca0 FXC |
59 | { |
60 | #define MSGLEN 200 | |
61 | char msg[MSGLEN] = "EXECUTE_COMMAND_LINE: "; | |
62 | strncat (msg, cmdmsg_values[value], MSGLEN - strlen(msg) - 1); | |
f4742991 | 63 | runtime_error ("%s", msg); |
1487cca0 | 64 | } |
c14c8155 FXC |
65 | } |
66 | ||
67 | ||
ef536b41 JB |
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 | ||
c14c8155 FXC |
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. */ | |
581d2326 | 83 | char *cmd = fc_strdup (command, command_len); |
c14c8155 FXC |
84 | |
85 | /* Flush all I/O units before executing the command. */ | |
86 | flush_all_units(); | |
87 | ||
f8886038 | 88 | #if defined(HAVE_POSIX_SPAWN) || defined(HAVE_FORK) |
c14c8155 FXC |
89 | if (!wait) |
90 | { | |
91 | /* Asynchronous execution. */ | |
92 | pid_t pid; | |
93 | ||
b4224aec | 94 | set_cmdstat (cmdstat, EXEC_NOERROR); |
c14c8155 | 95 | |
ef536b41 JB |
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 | ||
f8886038 JB |
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)) | |
b4224aec | 114 | set_cmdstat (cmdstat, EXEC_CHILDFAILED); |
f8886038 JB |
115 | #elif defined(HAVE_FORK) |
116 | if ((pid = fork()) < 0) | |
117 | set_cmdstat (cmdstat, EXEC_CHILDFAILED); | |
c14c8155 FXC |
118 | else if (pid == 0) |
119 | { | |
120 | /* Child process. */ | |
121 | int res = system (cmd); | |
122 | _exit (WIFEXITED(res) ? WEXITSTATUS(res) : res); | |
123 | } | |
f8886038 | 124 | #endif |
c14c8155 FXC |
125 | } |
126 | else | |
127 | #endif | |
128 | { | |
129 | /* Synchronous execution. */ | |
130 | int res = system (cmd); | |
131 | ||
b4224aec | 132 | if (res == -1) |
c14c8155 | 133 | set_cmdstat (cmdstat, EXEC_SYSTEMFAILED); |
f8886038 | 134 | #if !defined(HAVE_POSIX_SPAWN) && !defined(HAVE_FORK) |
b4224aec TB |
135 | else if (!wait) |
136 | set_cmdstat (cmdstat, EXEC_SYNCHRONOUS); | |
43b594b4 | 137 | #endif |
1487cca0 FXC |
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); | |
c14c8155 | 147 | else |
b4224aec TB |
148 | set_cmdstat (cmdstat, EXEC_NOERROR); |
149 | ||
150 | if (res != -1) | |
c14c8155 | 151 | { |
c14c8155 FXC |
152 | #if defined(WEXITSTATUS) && defined(WIFEXITED) |
153 | *exitstat = WIFEXITED(res) ? WEXITSTATUS(res) : res; | |
154 | #else | |
155 | *exitstat = res; | |
156 | #endif | |
157 | } | |
158 | } | |
159 | ||
581d2326 JB |
160 | free (cmd); |
161 | ||
c14c8155 | 162 | /* Now copy back to the Fortran string if needed. */ |
3934b625 TK |
163 | if (cmdstat && *cmdstat > EXEC_NOERROR && cmdmsg) |
164 | fstrcpy (cmdmsg, cmdmsg_len, cmdmsg_values[*cmdstat], | |
c14c8155 | 165 | strlen (cmdmsg_values[*cmdstat])); |
c14c8155 FXC |
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 | } |