aboutsummaryrefslogtreecommitdiff
path: root/mod
diff options
context:
space:
mode:
Diffstat (limited to 'mod')
-rw-r--r--mod/external-handler.cxx322
1 files changed, 161 insertions, 161 deletions
diff --git a/mod/external-handler.cxx b/mod/external-handler.cxx
index d05e5bf..e88e4b4 100644
--- a/mod/external-handler.cxx
+++ b/mod/external-handler.cxx
@@ -76,209 +76,209 @@ namespace brep
string ref (data_dir.leaf ().string ());
for (;;) // Breakout loop.
+ try
+ {
+ fdpipe pipe (fdopen_pipe ()); // Can throw io_error.
+
+ // Redirect the diagnostics to the web server error log.
+ //
+ process pr (
+ process_start_callback ([&trace] (const char* args[], size_t n)
+ {
+ if (trace != nullptr)
+ *trace << process_args {args, n};
+ },
+ 0 /* stdin */,
+ pipe /* stdout */,
+ 2 /* stderr */,
+ handler,
+ args,
+ data_dir));
+ pipe.out.close ();
+
+ auto kill = [&pr, &warn, &handler, &ref] ()
+ {
+ // We may still end up well (see below), thus this is a warning.
+ //
+ warn << "ref " << ref << ": process " << handler
+ << " execution timeout expired";
+
+ pr.kill ();
+ };
+
try
{
- fdpipe pipe (fdopen_pipe ()); // Can throw io_error.
+ ifdstream is (move (pipe.in), fdstream_mode::non_blocking);
- // Redirect the diagnostics to the web server error log.
- //
- process pr (
- process_start_callback ([&trace] (const char* args[], size_t n)
- {
- if (trace != nullptr)
- *trace << process_args {args, n};
- },
- 0 /* stdin */,
- pipe /* stdout */,
- 2 /* stderr */,
- handler,
- args,
- data_dir));
- pipe.out.close ();
-
- auto kill = [&pr, &warn, &handler, &ref] ()
- {
- // We may still end up well (see below), thus this is a warning.
- //
- warn << "ref " << ref << ": process " << handler
- << " execution timeout expired";
-
- pr.kill ();
- };
+ const size_t nbuf (8192);
+ char buf[nbuf];
- try
+ while (is.is_open ())
{
- ifdstream is (move (pipe.in), fdstream_mode::non_blocking);
+ time_point start;
+ milliseconds wd (10); // Max time to wait for the data portion.
- const size_t nbuf (8192);
- char buf[nbuf];
-
- while (is.is_open ())
+ if (timeout)
{
- time_point start;
- milliseconds wd (10); // Max time to wait for the data portion.
+ start = system_clock::now ();
- if (timeout)
- {
- start = system_clock::now ();
+ if (*timeout < wd)
+ wd = *timeout;
+ }
- if (*timeout < wd)
- wd = *timeout;
- }
+ timeval tm {wd.count () / 1000 /* seconds */,
+ wd.count () % 1000 * 1000 /* microseconds */};
+
+ fd_set rd;
+ FD_ZERO (&rd);
+ FD_SET (is.fd (), &rd);
- timeval tm {wd.count () / 1000 /* seconds */,
- wd.count () % 1000 * 1000 /* microseconds */};
+ int r (select (is.fd () + 1, &rd, nullptr, nullptr, &tm));
- fd_set rd;
- FD_ZERO (&rd);
- FD_SET (is.fd (), &rd);
+ if (r == -1)
+ {
+ // Don't fail if the select() call was interrupted by the
+ // signal.
+ //
+ if (errno != EINTR)
+ throw_system_ios_failure (errno, "select failed");
+ }
+ else if (r != 0) // Is data available?
+ {
+ assert (FD_ISSET (is.fd (), &rd));
- int r (select (is.fd () + 1, &rd, nullptr, nullptr, &tm));
+ // The only leagal way to read from non-blocking ifdstream.
+ //
+ streamsize n (is.readsome (buf, nbuf));
- if (r == -1)
+ // Close the stream (and bail out) if the end of the data is
+ // reached. Otherwise cache the read data.
+ //
+ if (is.eof ())
+ is.close ();
+ else
{
- // Don't fail if the select() call was interrupted by the
- // signal.
+ // The data must be available.
//
- if (errno != EINTR)
- throw_system_ios_failure (errno, "select failed");
- }
- else if (r != 0) // Is data available?
- {
- assert (FD_ISSET (is.fd (), &rd));
-
- // The only leagal way to read from non-blocking ifdstream.
+ // Note that we could keep reading until the readsome() call
+ // returns 0. However, this way we could potentially exceed
+ // the timeout significantly for some broken handler that
+ // floods us with data. So instead, we will be checking the
+ // process execution time after every data chunk read.
//
- streamsize n (is.readsome (buf, nbuf));
+ assert (n != 0);
- // Close the stream (and bail out) if the end of the data is
- // reached. Otherwise cache the read data.
- //
- if (is.eof ())
- is.close ();
- else
- {
- // The data must be available.
- //
- // Note that we could keep reading until the readsome() call
- // returns 0. However, this way we could potentially exceed
- // the timeout significantly for some broken handler that
- // floods us with data. So instead, we will be checking the
- // process execution time after every data chunk read.
- //
- assert (n != 0);
-
- ss.write (buf, n);
- }
+ ss.write (buf, n);
}
- else // Timeout occured.
+ }
+ else // Timeout occured.
+ {
+ // Normally, we don't expect timeout to occur on the pipe read
+ // operation if the process has terminated successfully, as
+ // all its output must already be buffered (including eof).
+ // However, there can be some still running handler's child
+ // that has inherited the parent's stdout. In this case we
+ // assume that we have read all the handler's output, close
+ // the stream, log the warning and bail out.
+ //
+ if (pr.exit)
{
- // Normally, we don't expect timeout to occur on the pipe read
- // operation if the process has terminated successfully, as
- // all its output must already be buffered (including eof).
- // However, there can be some still running handler's child
- // that has inherited the parent's stdout. In this case we
- // assume that we have read all the handler's output, close
- // the stream, log the warning and bail out.
+ // We keep reading only upon successful handler termination.
//
- if (pr.exit)
- {
- // We keep reading only upon successful handler termination.
- //
- assert (*pr.exit);
+ assert (*pr.exit);
- is.close ();
+ is.close ();
- warn << "ref " << ref << ": process " << handler
- << " stdout is not closed after termination (possibly "
- << "handler's child still running)";
- }
+ warn << "ref " << ref << ": process " << handler
+ << " stdout is not closed after termination (possibly "
+ << "handler's child still running)";
}
+ }
+
+ if (timeout)
+ {
+ time_point now (system_clock::now ());
+
+ // Assume we have waited the full amount if the time
+ // adjustment is detected.
+ //
+ duration d (now > start ? now - start : wd);
- if (timeout)
+ // If the timeout is not fully exhausted, then decrement it and
+ // try to read some more data from the handler' stdout.
+ // Otherwise, kill the process, if not done yet.
+ //
+ // Note that it may happen that we are killing an already
+ // terminated process, in which case kill() just sets the
+ // process exit information. On the other hand it's guaranteed
+ // that the process is terminated after the kill() call, and
+ // so the pipe is presumably closed on the write end (see
+ // above for details). Thus, if the process terminated
+ // successfully, we will continue reading until eof is
+ // reached or read timeout occurred. Yes, it may happen that
+ // we will succeed even with the kill.
+ //
+ if (*timeout > d)
+ *timeout -= duration_cast<milliseconds> (d);
+ else if (!pr.exit)
{
- time_point now (system_clock::now ());
+ kill ();
- // Assume we have waited the full amount if the time
- // adjustment is detected.
- //
- duration d (now > start ? now - start : wd);
+ assert (pr.exit);
- // If the timeout is not fully exhausted, then decrement it and
- // try to read some more data from the handler' stdout.
- // Otherwise, kill the process, if not done yet.
+ // Close the stream (and bail out) if the process hasn't
+ // terminate successfully.
//
- // Note that it may happen that we are killing an already
- // terminated process, in which case kill() just sets the
- // process exit information. On the other hand it's guaranteed
- // that the process is terminated after the kill() call, and
- // so the pipe is presumably closed on the write end (see
- // above for details). Thus, if the process terminated
- // successfully, we will continue reading until eof is
- // reached or read timeout occurred. Yes, it may happen that
- // we will succeed even with the kill.
- //
- if (*timeout > d)
- *timeout -= duration_cast<milliseconds> (d);
- else if (!pr.exit)
- {
- kill ();
-
- assert (pr.exit);
-
- // Close the stream (and bail out) if the process hasn't
- // terminate successfully.
- //
- if (!*pr.exit)
- is.close ();
-
- *timeout = milliseconds::zero ();
- }
+ if (!*pr.exit)
+ is.close ();
+
+ *timeout = milliseconds::zero ();
}
}
+ }
- assert (!is.is_open ());
-
- if (!timeout)
- pr.wait ();
+ assert (!is.is_open ());
- // If the process is not terminated yet, then wait for its
- // termination for the remaining time. Kill it if the timeout has
- // been exceeded and the process still hasn't terminate.
- //
- else if (!pr.exit && !pr.timed_wait (*timeout))
- kill ();
+ if (!timeout)
+ pr.wait ();
- assert (pr.exit); // The process must finally be terminated.
+ // If the process is not terminated yet, then wait for its
+ // termination for the remaining time. Kill it if the timeout has
+ // been exceeded and the process still hasn't terminate.
+ //
+ else if (!pr.exit && !pr.timed_wait (*timeout))
+ kill ();
- if (*pr.exit)
- break; // Get out of the breakout loop.
+ assert (pr.exit); // The process must finally be terminated.
- error << "ref " << ref << ": process " << handler << " "
- << *pr.exit;
+ if (*pr.exit)
+ break; // Get out of the breakout loop.
- // Fall through.
- }
- catch (const io_error& e)
- {
- if (pr.wait ())
- error << "ref " << ref << ": unable to read handler's output: "
- << e;
+ error << "ref " << ref << ": process " << handler << " "
+ << *pr.exit;
- // Fall through.
- }
+ // Fall through.
+ }
+ catch (const io_error& e)
+ {
+ if (pr.wait ())
+ error << "ref " << ref << ": unable to read handler's output: "
+ << e;
- return nullopt;
+ // Fall through.
}
+
+ return nullopt;
+ }
// Handle process_error and io_error (both derive from system_error).
//
- catch (const system_error& e)
- {
- error << "ref " << ref << ": unable to execute '" << handler
- << "': " << e;
+ catch (const system_error& e)
+ {
+ error << "ref " << ref << ": unable to execute '" << handler
+ << "': " << e;
- return nullopt;
- }
+ return nullopt;
+ }
result_manifest r;