1 /* pspp - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2009, 2010, 2011 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
31 #include "data/settings.h"
32 #include "language/command.h"
33 #include "language/lexer/lexer.h"
34 #include "libpspp/assertion.h"
35 #include "libpspp/compiler.h"
36 #include "libpspp/i18n.h"
37 #include "libpspp/message.h"
38 #include "libpspp/str.h"
39 #include "libpspp/string-array.h"
40 #include "libpspp/temp-file.h"
41 #include "output/driver.h"
44 #include "gl/intprops.h"
45 #include "gl/localcharset.h"
46 #include "gl/read-file.h"
47 #include "gl/timespec.h"
48 #include "gl/xalloc.h"
49 #include "gl/xmalloca.h"
52 #define _(msgid) gettext (msgid)
53 #define N_(msgid) msgid
56 #define TIME_LIMIT_SUPPORTED 0
58 run_commands (const struct string_array *commands, double time_limit)
60 assert (time_limit == DBL_MAX);
62 for (size_t i = 0; i < commands->n; i++)
64 /* XXX No way to capture command output */
65 char *s = recode_string (locale_charset (), "UTF-8",
66 commands->strings[i], -1);
67 int retval = system (s);
72 msg (SE, _("%s: Command exited with status %d."),
73 commands->strings[i], retval);
80 #define TIME_LIMIT_SUPPORTED 1
82 run_command (const char *command, struct timespec timeout)
84 /* Same exit codes used by 'sh'. */
86 EXIT_CANNOT_INVOKE = 126,
90 /* Create a temporary file to capture command output. */
91 FILE *output_file = create_temp_file ();
94 msg (SE, _("Failed to create temporary file (%s)."), strerror (errno));
98 int dev_null_fd = open ("/dev/null", O_RDONLY);
101 msg (SE, _("/dev/null: Failed to open (%s)."), strerror (errno));
102 fclose (output_file);
106 char *locale_command = recode_string (locale_charset (), "UTF-8",
113 fclose (output_file);
114 free (locale_command);
116 msg (SE, _("Couldn't fork: %s."), strerror (errno));
121 /* Running in the child. */
124 /* Hurd doesn't support inheriting process timers in a way that works. */
125 if (setpgid (0, 0) < 0)
126 error (1, errno, _("Failed to set process group."));
128 /* Set up timeout. */
129 if (timeout.tv_sec < TYPE_MAXIMUM (time_t))
131 signal (SIGALRM, SIG_DFL);
133 struct timespec left = timespec_sub (timeout, current_timespec ());
134 if (timespec_sign (left) <= 0)
137 struct itimerval it = {
139 .tv_sec = left.tv_sec,
140 .tv_usec = left.tv_nsec / 1000
143 if (setitimer (ITIMER_REAL, &it, NULL) < 0)
144 error (1, errno, _("Failed to set timeout."));
148 /* Set up file descriptors:
149 - /dev/null for stdin
150 - Temporary file to capture stdout and stderr.
151 - Close everything else.
153 dup2 (dev_null_fd, 0);
154 dup2 (fileno (output_file), 1);
155 dup2 (fileno (output_file), 2);
157 for (int fd = 3; fd < 256; fd++)
160 /* Choose the shell. */
161 const char *shell = getenv ("SHELL");
165 /* Run subprocess. */
166 execl (shell, shell, "-c", locale_command, NULL);
168 /* Failed to start the shell. */
169 _exit (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
172 /* Running in the parent. */
174 free (locale_command);
176 /* Wait for child to exit. */
182 if (timespec_cmp (current_timespec (), timeout) >= 0)
183 kill (-pid, SIGALRM);
189 pid_t retval = waitpid (pid, &status, flags);
201 else if (retval == 0)
211 msg (SW, _("While running \"%s\", waiting for child process "
213 command, strerror (errno));
217 if (WIFSIGNALED (status))
219 int signum = WTERMSIG (status);
220 if (signum == SIGALRM)
221 msg (SW, _("Command \"%s\" timed out."), command);
223 msg (SW, _("Command \"%s\" terminated by signal %d."), command, signum);
226 else if (WIFEXITED (status) && WEXITSTATUS (status))
228 int exit_code = WEXITSTATUS (status);
229 const char *detail = (exit_code == EXIT_ENOENT
230 ? _("Command or shell not found")
231 : exit_code == EXIT_CANNOT_INVOKE
232 ? _("Could not invoke command or shell")
235 msg (SW, _("Command \"%s\" exited with status %d (%s)."),
236 command, exit_code, detail);
238 msg (SW, _("Command \"%s\" exited with status %d."),
243 rewind (output_file);
245 char *locale_output = fread_file (output_file, 0, &length);
248 msg (SW, _("Command \"%s\" output could not be read (%s)."),
249 command, strerror (errno));
254 char *output = recode_string ("UTF-8", locale_charset (),
257 /* Drop final new-line, if any. */
258 char *end = strchr (output, '\0');
259 if (end > output && end[-1] == '\n')
262 output_log_nocopy (output);
264 free (locale_output);
270 run_commands (const struct string_array *commands, double time_limit)
272 struct timespec timeout = timespec_add (dtotimespec (time_limit),
273 current_timespec ());
275 for (size_t i = 0; i < commands->n; i++)
277 if (!run_command (commands->strings[i], timeout))
286 cmd_host (struct lexer *lexer, struct dataset *ds UNUSED)
288 if (settings_get_safer_mode ())
290 lex_next_error (lexer, -1, -1,
291 _("This command not allowed when the %s option is set."),
296 if (!lex_force_match_phrase (lexer, "COMMAND=[")
297 || !lex_force_string (lexer))
300 struct string_array commands = STRING_ARRAY_INITIALIZER;
301 while (lex_token (lexer) == T_STRING)
303 string_array_append (&commands, lex_tokcstr (lexer));
306 if (!lex_force_match (lexer, T_RBRACK))
308 string_array_destroy (&commands);
312 double time_limit = DBL_MAX;
313 if (lex_match_id (lexer, "TIMELIMIT"))
315 int time_limit_start = lex_ofs (lexer) - 1;
316 if (!lex_force_match (lexer, T_EQUALS)
317 || !lex_force_num (lexer))
319 string_array_destroy (&commands);
323 double num = lex_number (lexer);
325 time_limit = num < 0.0 ? 0.0 : num;
327 int time_limit_end = lex_ofs (lexer) - 1;
328 if (!TIME_LIMIT_SUPPORTED)
330 lex_ofs_error (lexer, time_limit_start, time_limit_end,
331 _("Time limit not supported on this platform."));
332 string_array_destroy (&commands);
337 enum cmd_result result = lex_end_of_command (lexer);
338 if (result == CMD_SUCCESS && !run_commands (&commands, time_limit))
339 result = CMD_FAILURE;
340 string_array_destroy (&commands);