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