2 * @brief Run an external filter and capture its output in a std::string.
4 /* Copyright (C) 2003,2006,2007,2009,2010,2011,2013,2015,2017 Olly Betts
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 #include "runfilter.h"
29 #include <sys/types.h>
30 #include "safefcntl.h"
34 #ifdef HAVE_SYS_TIME_H
35 # include <sys/time.h>
37 #ifdef HAVE_SYS_RESOURCE_H
38 # include <sys/resource.h>
40 #include "safesysselect.h"
41 #ifdef HAVE_SYS_SOCKET_H
42 # include <sys/socket.h>
44 #include "safesyswait.h"
45 #include "safeunistd.h"
47 #if defined HAVE_FORK && defined HAVE_SOCKETPAIR
52 #include "stringutils.h"
56 # define pclose _pclose
61 #if defined HAVE_FORK && defined HAVE_SOCKETPAIR
63 command_needs_shell(const char * p
)
66 // Probably overly conservative, but suitable for
68 if (strchr("!\"#$&()*;<>?[\\]^`{|}~", *p
) != NULL
) {
76 unquote(string
& s
, size_t & j
)
84 j
= s
.find('\'', j
+ 1);
86 // Unmatched ' in command string.
87 // dash exits 2 in this case, bash exits 1.
90 // Replace four character sequence '\'' with ' - this is
91 // how a single quote inside single quotes gets escaped.
92 if (s
[j
+ 1] != '\\' ||
99 if (j
+ 1 != s
.size()) {
101 if (ch
!= ' ' && ch
!= '\t' && ch
!= '\n') {
102 // Handle the expansion of e.g.: --input=%f,html
109 j
= s
.find_first_of(" \t\n'", j
+ 1);
110 // Handle the expansion of e.g.: --input=%f
111 if (j
!= s
.npos
&& s
[j
] == '\'') goto single_quoted
;
119 static pid_t pid_to_kill_on_signal
;
121 #ifdef HAVE_SIGACTION
122 static struct sigaction old_hup_handler
;
123 static struct sigaction old_int_handler
;
124 static struct sigaction old_quit_handler
;
125 static struct sigaction old_term_handler
;
130 handle_signal(int signum
)
132 if (pid_to_kill_on_signal
) {
133 kill(pid_to_kill_on_signal
, SIGKILL
);
134 pid_to_kill_on_signal
= 0;
138 sigaction(signum
, &old_hup_handler
, NULL
);
141 sigaction(signum
, &old_int_handler
, NULL
);
144 sigaction(signum
, &old_quit_handler
, NULL
);
147 sigaction(signum
, &old_term_handler
, NULL
);
161 sa
.sa_handler
= handle_signal
;
162 sigemptyset(&sa
.sa_mask
);
165 sigaction(SIGHUP
, &sa
, &old_hup_handler
);
166 sigaction(SIGINT
, &sa
, &old_int_handler
);
167 sigaction(SIGQUIT
, &sa
, &old_quit_handler
);
168 sigaction(SIGTERM
, &sa
, &old_term_handler
);
171 static sighandler_t old_hup_handler
;
172 static sighandler_t old_int_handler
;
173 static sighandler_t old_quit_handler
;
174 static sighandler_t old_term_handler
;
179 handle_signal(int signum
)
181 if (pid_to_kill_on_signal
) {
182 kill(pid_to_kill_on_signal
, SIGKILL
);
183 pid_to_kill_on_signal
= 0;
187 signal(signum
, old_hup_handler
);
190 signal(signum
, old_int_handler
);
193 signal(signum
, old_quit_handler
);
196 signal(signum
, old_term_handler
);
209 old_hup_handler
= signal(SIGHUP
, handle_signal
);
210 old_int_handler
= signal(SIGINT
, handle_signal
);
211 old_quit_handler
= signal(SIGQUIT
, handle_signal
);
212 old_term_handler
= signal(SIGTERM
, handle_signal
);
217 command_needs_shell(const char *)
219 // We don't try to avoid the shell on this platform, so don't waste time
220 // analysing commands to see if they could.
231 stdout_to_string(const string
&cmd
, bool use_shell
, int alt_status
)
234 #if defined HAVE_FORK && defined HAVE_SOCKETPAIR
235 // We want to be able to get the exit status of the child process.
236 signal(SIGCHLD
, SIG_DFL
);
239 if (socketpair(AF_UNIX
, SOCK_STREAM
, PF_UNSPEC
, fds
) < 0)
240 throw ReadError("socketpair failed");
242 pid_t child
= fork();
244 // We're the child process.
247 // Put the child process into its own process group, so that we can
248 // easily kill it and any children it in turn forks if we need to.
250 pid_to_kill_on_signal
= -child
;
252 pid_to_kill_on_signal
= child
;
255 // Close the parent's side of the socket pair.
258 // Connect stdout to our side of the socket pair.
261 #ifdef HAVE_SETRLIMIT
262 // Impose some pretty generous resource limits to prevent run-away
263 // filter programs from causing problems.
265 // Limit CPU time to 300 seconds (5 minutes).
266 struct rlimit cpu_limit
= { 300, RLIM_INFINITY
};
267 setrlimit(RLIMIT_CPU
, &cpu_limit
);
269 #if defined RLIMIT_AS || defined RLIMIT_VMEM || defined RLIMIT_DATA
270 // Limit process data to free physical memory.
271 long mem
= get_free_physical_memory();
273 struct rlimit ram_limit
= {
274 static_cast<rlim_t
>(mem
),
278 setrlimit(RLIMIT_AS
, &ram_limit
);
279 #elif defined RLIMIT_VMEM
280 setrlimit(RLIMIT_VMEM
, &ram_limit
);
282 // Only limits the data segment rather than the total address
283 // space, but that's better than nothing.
284 setrlimit(RLIMIT_DATA
, &ram_limit
);
291 #if !defined HAVE_SETENV && !defined HAVE_PUTENV
294 execl("/bin/sh", "/bin/sh", "-c", cmd
.c_str(), (void*)NULL
);
299 // Handle any environment variable assignments.
300 // Name must start with alpha or '_', contain only alphanumerics and
301 // '_', and there must be no quoting of either the name or the '='.
304 j
= s
.find_first_not_of(" \t\n", j
);
305 if (!(C_isalnum(s
[j
]) || s
[j
] == '_')) break;
307 do ++j
; while (C_isalnum(s
[j
]) || s
[j
] == '_');
317 setenv(&s
[i
], &s
[eq
+ 1], 1);
318 j
= s
.find_first_not_of(" \t\n", j
);
319 #elif defined HAVE_PUTENV
323 goto use_shell_after_all
;
327 vector
<const char *> argv
;
329 size_t i
= s
.find_first_not_of(" \t\n", j
);
330 if (i
== string::npos
) break;
331 bool quoted
= unquote(s
, j
);
332 const char * word
= s
.c_str() + i
;
334 // Handle simple cases of redirection.
335 if (strcmp(word
, ">/dev/null") == 0) {
336 int fd
= open(word
+ 1, O_WRONLY
);
337 if (fd
!= -1 && fd
!= 1) dup2(fd
, 1);
341 if (strcmp(word
, "2>/dev/null") == 0) {
342 int fd
= open(word
+ 2, O_WRONLY
);
343 if (fd
!= -1 && fd
!= 2) dup2(fd
, 2);
347 if (strcmp(word
, "2>&1") == 0) {
351 if (strcmp(word
, "1>&2") == 0) {
356 argv
.push_back(word
);
358 if (argv
.empty()) _exit(0);
359 argv
.push_back(NULL
);
361 execvp(argv
[0], const_cast<char **>(&argv
[0]));
362 // Emulate shell behaviour and exit with status 127 if the command
363 // isn't found, and status 126 for other problems. In particular, we
364 // rely on 127 below to throw NoSuchFilter.
365 _exit(errno
== ENOENT
? 127 : 126);
368 // We're the parent process.
370 // Close the child's side of the socket pair.
375 throw ReadError("fork failed");
383 // If we wait 300 seconds (5 minutes) without getting data from the
384 // filter, then give up to avoid waiting forever for a filter which
385 // has ended up blocked waiting for something which will never happen.
389 FD_SET(fd
, &readfds
);
390 int r
= select(fd
+ 1, &readfds
, NULL
, NULL
, &tv
);
393 if (errno
== EINTR
|| errno
== EAGAIN
) {
394 // select() interrupted by a signal, so retry.
397 cerr
<< "Reading from filter failed (" << strerror(errno
) << ")"
400 cerr
<< "Filter inactive for too long" << endl
;
403 kill(-child
, SIGKILL
);
405 kill(child
, SIGKILL
);
409 while (waitpid(child
, &status
, 0) < 0 && errno
== EINTR
) { }
410 pid_to_kill_on_signal
= 0;
411 throw ReadError(status
);
415 ssize_t res
= read(fd
, buf
, sizeof(buf
));
418 if (errno
== EINTR
) {
419 // read() interrupted by a signal, so retry.
424 kill(-child
, SIGKILL
);
427 while (waitpid(child
, &status
, 0) < 0 && errno
== EINTR
) { }
428 pid_to_kill_on_signal
= 0;
429 throw ReadError(status
);
431 out
.append(buf
, res
);
436 kill(-child
, SIGKILL
);
439 while (waitpid(child
, &status
, 0) < 0) {
441 throw ReadError("wait pid failed");
443 pid_to_kill_on_signal
= 0;
446 FILE * fh
= popen(cmd
.c_str(), "r");
447 if (fh
== NULL
) throw ReadError("popen failed");
450 size_t len
= fread(buf
, 1, 4096, fh
);
453 throw ReadError("fread failed");
455 out
.append(buf
, len
);
457 int status
= pclose(fh
);
460 if (WIFEXITED(status
)) {
461 int exit_status
= WEXITSTATUS(status
);
462 if (exit_status
== 0 || exit_status
== alt_status
)
464 if (exit_status
== 127)
465 throw NoSuchFilter();
468 if (WIFSIGNALED(status
) && WTERMSIG(status
) == SIGXCPU
) {
469 cerr
<< "Filter process consumed too much CPU time" << endl
;
472 throw ReadError(status
);