summaryrefslogtreecommitdiffstats
path: root/src/lib/util/unittests/fork.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/util/unittests/fork.cc')
-rw-r--r--src/lib/util/unittests/fork.cc141
1 files changed, 141 insertions, 0 deletions
diff --git a/src/lib/util/unittests/fork.cc b/src/lib/util/unittests/fork.cc
new file mode 100644
index 0000000..fc8b525
--- /dev/null
+++ b/src/lib/util/unittests/fork.cc
@@ -0,0 +1,141 @@
+// Copyright (C) 2011-2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <util/unittests/fork.h>
+
+#include <util/io/fd.h>
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <cerrno>
+#include <stdlib.h>
+#include <stdio.h>
+
+using namespace isc::util::io;
+
+namespace {
+
+// Just a NOP function to ignore a signal but let it interrupt function.
+void no_handler(int) { }
+
+};
+
+namespace isc {
+namespace util {
+namespace unittests {
+
+bool
+process_ok(pid_t process) {
+ // Create a timeout
+ struct sigaction ignored, original;
+ memset(&ignored, 0, sizeof ignored);
+ ignored.sa_handler = no_handler;
+ if (sigaction(SIGALRM, &ignored, &original)) {
+ return false;
+ }
+ // It is long, but if everything is OK, it'll not happen
+ alarm(10);
+ int status;
+ int result(waitpid(process, &status, 0) == -1);
+ // Cancel the alarm and return the original handler
+ alarm(0);
+ if (sigaction(SIGALRM, &original, NULL)) {
+ return false;
+ }
+ // Check what we found out
+ if (result) {
+ if (errno == EINTR)
+ kill(process, SIGTERM);
+ return false;
+ }
+ return WIFEXITED(status) && WEXITSTATUS(status) == 0;
+}
+
+/*
+ * This creates a pipe, forks and feeds the pipe with given data.
+ * Used to provide the input in non-blocking/asynchronous way.
+ */
+pid_t
+provide_input(int *read_pipe, const void *input, const size_t length)
+{
+ int pipes[2];
+ if (pipe(pipes)) {
+ return -1;
+ }
+ *read_pipe = pipes[0];
+
+ pid_t pid(fork());
+ if (pid) { // We are in the parent
+ return pid;
+ } else { // This is in the child, just puts the data there
+ close(pipes[0]);
+ if (!write_data(pipes[1], input, length)) {
+ exit(1);
+ } else {
+ close(pipes[1]);
+ exit(0);
+ }
+ }
+}
+
+
+/*
+ * This creates a pipe, forks and reads the pipe and compares it
+ * with given data. Used to check output of run in an asynchronous way.
+ */
+pid_t
+check_output(int *write_pipe, const void* const output, const size_t length)
+{
+ int pipes[2];
+ if (pipe(pipes)) {
+ return -1;
+ }
+ *write_pipe = pipes[1];
+ pid_t pid(fork());
+ if (pid) { // We are in parent
+ close(pipes[0]);
+ return pid;
+ } else {
+ close(pipes[1]);
+ unsigned char* buffer = new unsigned char[length + 1];
+ // Try to read one byte more to see if the output ends here
+ size_t got_length(read_data(pipes[0], buffer, length + 1));
+ bool ok(true);
+ if (got_length != length) {
+ fprintf(stderr, "Different length (expected %u, got %u)\n",
+ static_cast<unsigned>(length),
+ static_cast<unsigned>(got_length));
+ ok = false;
+ }
+ if(!ok || memcmp(buffer, output, length)) {
+ const unsigned char *output_c(static_cast<const unsigned char *>(
+ output));
+ // If they differ, print what we have
+ for(size_t i(0); i != got_length; ++ i) {
+ fprintf(stderr, "%02hhx", buffer[i]);
+ }
+ fprintf(stderr, "\n");
+ for(size_t i(0); i != length; ++ i) {
+ fprintf(stderr, "%02hhx", output_c[i]);
+ }
+ fprintf(stderr, "\n");
+ delete [] buffer;
+ exit(1);
+ } else {
+ delete [] buffer;
+ exit(0);
+ }
+ }
+}
+
+}
+}
+}