1 // Copyright (C) 2011-2015 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7 #include <config.h>
8
9 #include <util/unittests/fork.h>
10
11 #include <util/io/fd.h>
12
13 #include <sys/types.h>
14 #include <sys/wait.h>
15 #include <unistd.h>
16 #include <signal.h>
17 #include <string.h>
18 #include <cerrno>
19 #include <stdlib.h>
20 #include <stdio.h>
21
22 using namespace isc::util::io;
23
24 namespace {
25
26 // Just a NOP function to ignore a signal but let it interrupt function.
no_handler(int)27 void no_handler(int) { }
28
29 };
30
31 namespace isc {
32 namespace util {
33 namespace unittests {
34
35 bool
process_ok(pid_t process)36 process_ok(pid_t process) {
37 // Create a timeout
38 struct sigaction ignored, original;
39 memset(&ignored, 0, sizeof ignored);
40 ignored.sa_handler = no_handler;
41 if (sigaction(SIGALRM, &ignored, &original)) {
42 return false;
43 }
44 // It is long, but if everything is OK, it'll not happen
45 alarm(10);
46 int status;
47 int result(waitpid(process, &status, 0) == -1);
48 // Cancel the alarm and return the original handler
49 alarm(0);
50 if (sigaction(SIGALRM, &original, NULL)) {
51 return false;
52 }
53 // Check what we found out
54 if (result) {
55 if (errno == EINTR)
56 kill(process, SIGTERM);
57 return false;
58 }
59 return WIFEXITED(status) && WEXITSTATUS(status) == 0;
60 }
61
62 /*
63 * This creates a pipe, forks and feeds the pipe with given data.
64 * Used to provide the input in non-blocking/asynchronous way.
65 */
66 pid_t
provide_input(int * read_pipe,const void * input,const size_t length)67 provide_input(int *read_pipe, const void *input, const size_t length)
68 {
69 int pipes[2];
70 if (pipe(pipes)) {
71 return -1;
72 }
73 *read_pipe = pipes[0];
74
75 pid_t pid(fork());
76 if (pid) { // We are in the parent
77 return pid;
78 } else { // This is in the child, just puts the data there
79 close(pipes[0]);
80 if (!write_data(pipes[1], input, length)) {
81 exit(1);
82 } else {
83 close(pipes[1]);
84 exit(0);
85 }
86 }
87 }
88
89
90 /*
91 * This creates a pipe, forks and reads the pipe and compares it
92 * with given data. Used to check output of run in an asynchronous way.
93 */
94 pid_t
check_output(int * write_pipe,const void * const output,const size_t length)95 check_output(int *write_pipe, const void* const output, const size_t length)
96 {
97 int pipes[2];
98 if (pipe(pipes)) {
99 return -1;
100 }
101 *write_pipe = pipes[1];
102 pid_t pid(fork());
103 if (pid) { // We are in parent
104 close(pipes[0]);
105 return pid;
106 } else {
107 close(pipes[1]);
108 unsigned char* buffer = new unsigned char[length + 1];
109 // Try to read one byte more to see if the output ends here
110 size_t got_length(read_data(pipes[0], buffer, length + 1));
111 bool ok(true);
112 if (got_length != length) {
113 fprintf(stderr, "Different length (expected %u, got %u)\n",
114 static_cast<unsigned>(length),
115 static_cast<unsigned>(got_length));
116 ok = false;
117 }
118 if(!ok || memcmp(buffer, output, length)) {
119 const unsigned char *output_c(static_cast<const unsigned char *>(
120 output));
121 // If they differ, print what we have
122 for(size_t i(0); i != got_length; ++ i) {
123 fprintf(stderr, "%02hhx", buffer[i]);
124 }
125 fprintf(stderr, "\n");
126 for(size_t i(0); i != length; ++ i) {
127 fprintf(stderr, "%02hhx", output_c[i]);
128 }
129 fprintf(stderr, "\n");
130 delete [] buffer;
131 exit(1);
132 } else {
133 delete [] buffer;
134 exit(0);
135 }
136 }
137 }
138
139 }
140 }
141 }
142