]> git.ipfire.org Git - thirdparty/gcc.git/blame - libgfortran/intrinsics/execute_command_line.c
Update copyright years.
[thirdparty/gcc.git] / libgfortran / intrinsics / execute_command_line.c
CommitLineData
c14c8155 1/* Implementation of the EXECUTE_COMMAND_LINE intrinsic.
83ffe9cd 2 Copyright (C) 2009-2023 Free Software Foundation, Inc.
f4742991 3 Contributed by François-Xavier Coudert.
c14c8155
FXC
4
5This file is part of the GNU Fortran runtime library (libgfortran).
6
7Libgfortran is free software; you can redistribute it and/or modify it under
8the terms of the GNU General Public License as published by the Free
9Software Foundation; either version 3, or (at your option) any later
10version.
11
12Libgfortran is distributed in the hope that it will be useful, but WITHOUT
13ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15for more details.
16
17Under Section 7 of GPL version 3, you are granted additional
18permissions described in the GCC Runtime Library Exception, version
193.1, as published by the Free Software Foundation.
20
21You should have received a copy of the GNU General Public License and
22a copy of the GCC Runtime Library Exception along with this program;
23see 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>
a3454130
IS
37# ifdef __APPLE__
38# include <crt_externs.h>
39# define environ (*_NSGetEnviron ())
40# else
f8886038 41extern char **environ;
a3454130 42# endif
f8886038 43#endif
ef536b41
JB
44#if defined(HAVE_POSIX_SPAWN) || defined(HAVE_FORK)
45#include <signal.h>
46#endif
c14c8155 47
b4224aec 48enum { EXEC_SYNCHRONOUS = -2, EXEC_NOERROR = 0, EXEC_SYSTEMFAILED,
1487cca0 49 EXEC_CHILDFAILED, EXEC_INVALIDCOMMAND };
c14c8155 50static const char *cmdmsg_values[] =
b4224aec
TB
51 { "",
52 "Termination status of the command-language interpreter cannot be obtained",
1487cca0
FXC
53 "Execution of child process impossible",
54 "Invalid command line" };
c14c8155
FXC
55
56
57
58static void
59set_cmdstat (int *cmdstat, int value)
60{
61 if (cmdstat)
62 *cmdstat = value;
b4224aec 63 else if (value > EXEC_NOERROR)
1487cca0
FXC
64 {
65#define MSGLEN 200
66 char msg[MSGLEN] = "EXECUTE_COMMAND_LINE: ";
67 strncat (msg, cmdmsg_values[value], MSGLEN - strlen(msg) - 1);
f4742991 68 runtime_error ("%s", msg);
1487cca0 69 }
c14c8155
FXC
70}
71
72
ef536b41
JB
73#if defined(HAVE_WAITPID) && defined(HAVE_SIGACTION)
74static void
75sigchld_handler (int signum __attribute__((unused)))
76{
77 while (waitpid ((pid_t)(-1), NULL, WNOHANG) > 0) {}
78}
79#endif
80
c14c8155
FXC
81static void
82execute_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. */
581d2326 88 char *cmd = fc_strdup (command, command_len);
c14c8155
FXC
89
90 /* Flush all I/O units before executing the command. */
91 flush_all_units();
92
f8886038 93#if defined(HAVE_POSIX_SPAWN) || defined(HAVE_FORK)
c14c8155
FXC
94 if (!wait)
95 {
96 /* Asynchronous execution. */
97 pid_t pid;
98
b4224aec 99 set_cmdstat (cmdstat, EXEC_NOERROR);
c14c8155 100
ef536b41
JB
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
f8886038
JB
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))
b4224aec 119 set_cmdstat (cmdstat, EXEC_CHILDFAILED);
f8886038
JB
120#elif defined(HAVE_FORK)
121 if ((pid = fork()) < 0)
122 set_cmdstat (cmdstat, EXEC_CHILDFAILED);
c14c8155
FXC
123 else if (pid == 0)
124 {
125 /* Child process. */
126 int res = system (cmd);
127 _exit (WIFEXITED(res) ? WEXITSTATUS(res) : res);
128 }
f8886038 129#endif
c14c8155
FXC
130 }
131 else
132#endif
133 {
134 /* Synchronous execution. */
135 int res = system (cmd);
136
b4224aec 137 if (res == -1)
c14c8155 138 set_cmdstat (cmdstat, EXEC_SYSTEMFAILED);
f8886038 139#if !defined(HAVE_POSIX_SPAWN) && !defined(HAVE_FORK)
b4224aec
TB
140 else if (!wait)
141 set_cmdstat (cmdstat, EXEC_SYNCHRONOUS);
43b594b4 142#endif
1487cca0
FXC
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);
c14c8155 152 else
b4224aec
TB
153 set_cmdstat (cmdstat, EXEC_NOERROR);
154
155 if (res != -1)
c14c8155 156 {
c14c8155
FXC
157#if defined(WEXITSTATUS) && defined(WIFEXITED)
158 *exitstat = WIFEXITED(res) ? WEXITSTATUS(res) : res;
159#else
160 *exitstat = res;
161#endif
162 }
163 }
164
581d2326
JB
165 free (cmd);
166
c14c8155 167 /* Now copy back to the Fortran string if needed. */
3934b625
TK
168 if (cmdstat && *cmdstat > EXEC_NOERROR && cmdmsg)
169 fstrcpy (cmdmsg, cmdmsg_len, cmdmsg_values[*cmdstat],
c14c8155 170 strlen (cmdmsg_values[*cmdstat]));
c14c8155
FXC
171}
172
173
174extern void
175execute_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);
179export_proto(execute_command_line_i4);
180
181void
182execute_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
203extern void
204execute_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);
208export_proto(execute_command_line_i8);
209
210void
211execute_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}