Refactor SignatureValidator and reduce interface headers
[platform/core/security/cert-svc.git] / vcore / src / dpl / test / src / test_runner_child.cpp
1 /*
2  * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *    Licensed under the Apache License, Version 2.0 (the "License");
5  *    you may not use this file except in compliance with the License.
6  *    You may obtain a copy of the License at
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *    Unless required by applicable law or agreed to in writing, software
11  *    distributed under the License is distributed on an "AS IS" BASIS,
12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *    See the License for the specific language governing permissions and
14  *    limitations under the License.
15  */
16 /*
17  * @file        test_runner_child.cpp
18  * @author      Bartlomiej Grzelewski (b.grzelewski@samsung.com)
19  * @version     1.0
20  * @brief       This file is the implementation file of test runner
21  */
22 #include <stddef.h>
23 #include <dpl/test/test_runner.h>
24 #include <dpl/test/test_runner_child.h>
25 #include <dpl/test/test_results_collector.h>
26 #include <dpl/binary_queue.h>
27 #include <dpl/exception.h>
28 #include <dpl/scoped_free.h>
29 #include <dpl/foreach.h>
30 #include <dpl/colors.h>
31 #include <pcrecpp.h>
32 #include <algorithm>
33 #include <cstdio>
34 #include <memory.h>
35 #include <libgen.h>
36 #include <cstring>
37 #include <cstdlib>
38 #include <ctime>
39 #include <unistd.h>
40 #include <poll.h>
41 #include <fcntl.h>
42 #include <sys/types.h>
43 #include <sys/wait.h>
44 #include <sys/stat.h>
45
46 namespace {
47 const int CHILD_TEST_FAIL    = 0;
48 const int CHILD_TEST_PASS    = 1;
49 const int CHILD_TEST_IGNORED = 2;
50
51 int closeOutput() {
52     int devnull;
53     int retcode = -1;
54     if (-1 == (devnull = TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY))))
55         return -1;
56
57     // replace stdout with /dev/null
58     if (-1 == TEMP_FAILURE_RETRY(dup2(devnull, STDOUT_FILENO)))
59         goto end;
60
61     // replace stderr with /dev/null
62     if (-1 == TEMP_FAILURE_RETRY(dup2(devnull, STDERR_FILENO)))
63         goto end;
64
65     retcode = 0;
66
67 end:
68     close(devnull);
69     return retcode;
70 }
71
72 } // namespace anonymous
73
74 namespace VcoreDPL {
75 namespace Test {
76
77 PipeWrapper::PipeWrapper()
78 {
79     if (-1 == pipe(m_pipefd)) {
80         m_pipefd[0] = PIPE_CLOSED;
81         m_pipefd[1] = PIPE_CLOSED;
82     }
83 }
84
85 PipeWrapper::~PipeWrapper()
86 {
87     closeHelp(0);
88     closeHelp(1);
89 }
90
91 bool PipeWrapper::isReady()
92 {
93     return m_pipefd[0] != PIPE_CLOSED || m_pipefd[1] != PIPE_CLOSED;
94 }
95
96 void PipeWrapper::setUsage(Usage usage)
97 {
98     if (usage == READONLY) {
99         closeHelp(1);
100     }
101     if (usage == WRITEONLY) {
102         closeHelp(0);
103     }
104 }
105
106 PipeWrapper::Status PipeWrapper::send(int code, std::string &message)
107 {
108     if (m_pipefd[1] == PIPE_CLOSED) {
109         return ERROR;
110     }
111
112     std::ostringstream output;
113     output << toBinaryString(code);
114     output << toBinaryString(static_cast<int>(message.size()));
115     output << message;
116
117     std::string binary = output.str();
118     int size = binary.size();
119
120     if ((writeHelp(&size,
121                    sizeof(int)) == ERROR) ||
122         (writeHelp(binary.c_str(), size) == ERROR))
123     {
124         return ERROR;
125     }
126     return SUCCESS;
127 }
128
129 PipeWrapper::Status PipeWrapper::receive(int &code, std::string &data, time_t deadline)
130 {
131     if (m_pipefd[0] == PIPE_CLOSED) {
132         return ERROR;
133     }
134
135     int size;
136     Status ret;
137
138     if ((ret = readHelp(&size, sizeof(int), deadline)) != SUCCESS) {
139         return ret;
140     }
141
142     std::vector<char> buffer;
143     buffer.resize(size);
144
145     if ((ret = readHelp(&buffer[0], size, deadline)) != SUCCESS) {
146         return ret;
147     }
148
149     try {
150         VcoreDPL::BinaryQueue queue;
151         queue.AppendCopy(&buffer[0], size);
152
153         queue.FlattenConsume(&code, sizeof(int));
154         queue.FlattenConsume(&size, sizeof(int));
155
156         buffer.resize(size);
157
158         queue.FlattenConsume(&buffer[0], size);
159         data.assign(buffer.begin(), buffer.end());
160     } catch (VcoreDPL::BinaryQueue::Exception::Base &e) {
161         return ERROR;
162     }
163     return SUCCESS;
164 }
165
166 void PipeWrapper::closeAll()
167 {
168     closeHelp(0);
169     closeHelp(1);
170 }
171
172 std::string PipeWrapper::toBinaryString(int data)
173 {
174     char buffer[sizeof(int)];
175     memcpy(buffer, &data, sizeof(int));
176     return std::string(buffer, buffer + sizeof(int));
177 }
178
179 void PipeWrapper::closeHelp(int desc)
180 {
181     if (m_pipefd[desc] != PIPE_CLOSED) {
182         TEMP_FAILURE_RETRY(close(m_pipefd[desc]));
183         m_pipefd[desc] = PIPE_CLOSED;
184     }
185 }
186
187 PipeWrapper::Status PipeWrapper::writeHelp(const void *buffer, int size)
188 {
189     int ready = 0;
190     const char *p = static_cast<const char *>(buffer);
191     while (ready != size) {
192         int ret = write(m_pipefd[1], &p[ready], size - ready);
193
194         if (ret == -1 && (errno == EAGAIN || errno == EINTR)) {
195             continue;
196         }
197
198         if (ret == -1) {
199             closeHelp(1);
200             return ERROR;
201         }
202
203         ready += ret;
204     }
205     return SUCCESS;
206 }
207
208 PipeWrapper::Status PipeWrapper::readHelp(void *buf, int size, time_t deadline)
209 {
210     int ready = 0;
211     char *buffer = static_cast<char*>(buf);
212     while (ready != size) {
213         time_t wait = deadline - time(0);
214         wait = wait < 1 ? 1 : wait;
215         pollfd fds = { m_pipefd[0], POLLIN, 0 };
216
217         int pollReturn = poll(&fds, 1, wait * 1000);
218
219         if (pollReturn == 0) {
220             return TIMEOUT; // Timeout
221         }
222
223         if (pollReturn < -1) {
224             return ERROR;
225         }
226
227         int ret = read(m_pipefd[0], &buffer[ready], size - ready);
228
229         if (ret == -1 && (errno == EAGAIN || errno == EINTR)) {
230             continue;
231         }
232
233         if (ret == -1 || ret == 0) {
234             closeHelp(0);
235             return ERROR;
236         }
237
238         ready += ret;
239     }
240     return SUCCESS;
241 }
242
243 void RunChildProc(TestRunner::TestCase procChild)
244 {
245     PipeWrapper pipe;
246     if (!pipe.isReady()) {
247         throw TestRunner::TestFailed("Pipe creation failed");
248     }
249
250     pid_t pid = fork();
251
252     if (pid == -1) {
253         throw TestRunner::TestFailed("Child creation failed");
254     }
255
256     if (pid != 0) {
257         // parent code
258         pipe.setUsage(PipeWrapper::READONLY);
259
260         int code;
261         std::string message;
262
263         int pipeReturn = pipe.receive(code, message, time(0) + 10);
264
265         if (pipeReturn != PipeWrapper::SUCCESS) { // Timeout or reading error
266             pipe.closeAll();
267             kill(pid, SIGKILL);
268         }
269
270         int status;
271         waitpid(pid, &status, 0);
272
273         if (pipeReturn == PipeWrapper::TIMEOUT) {
274             throw TestRunner::TestFailed("Timeout");
275         }
276
277         if (pipeReturn == PipeWrapper::ERROR) {
278             throw TestRunner::TestFailed("Reading pipe error");
279         }
280
281         if (code == CHILD_TEST_FAIL) {
282             throw TestRunner::TestFailed(message);
283         } else if (code == CHILD_TEST_IGNORED) {
284             throw TestRunner::Ignored(message);
285         }
286     } else {
287         // child code
288
289         // End Runner after current test
290         TestRunnerSingleton::Instance().Terminate();
291
292         int code = CHILD_TEST_PASS;
293         std::string msg;
294
295         bool allowLogs = TestRunnerSingleton::Instance().GetAllowChildLogs();
296
297         close(STDIN_FILENO);
298         if (!allowLogs) {
299             closeOutput(); // if fails nothing we can do
300         }
301
302         pipe.setUsage(PipeWrapper::WRITEONLY);
303
304         try {
305             procChild();
306         } catch (const VcoreDPL::Test::TestRunner::TestFailed &e) {
307             msg = e.GetMessage();
308             code = CHILD_TEST_FAIL;
309         } catch (const VcoreDPL::Test::TestRunner::Ignored &e) {
310             msg = e.GetMessage();
311             code = CHILD_TEST_IGNORED;
312         } catch (...) { // catch all exception generated by "user" code
313             msg = "unhandled exeception";
314             code = CHILD_TEST_FAIL;
315         }
316
317         if (allowLogs) {
318             closeOutput();
319         }
320
321         pipe.send(code, msg);
322     }
323 }
324 } // namespace Test
325 } // namespace VcoreDPL