Fix casting from and to void*
[platform/core/test/security-tests.git] / src / framework / src / test_runner_child.cpp
1 /*
2  * Copyright (c) 2013-2015 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_failed.h>
24 #include <dpl/test/test_ignored.h>
25 #include <dpl/test/test_runner.h>
26 #include <dpl/test/test_runner_child.h>
27 #include <dpl/test/test_results_collector.h>
28 #include <dpl/binary_queue.h>
29 #include <dpl/exception.h>
30 #include <dpl/scoped_free.h>
31 #include <dpl/colors.h>
32 #include <pcrecpp.h>
33 #include <algorithm>
34 #include <cstdio>
35 #include <memory.h>
36 #include <libgen.h>
37 #include <cstring>
38 #include <cstdlib>
39 #include <ctime>
40 #include <unistd.h>
41 #include <poll.h>
42 #include <fcntl.h>
43 #include <sys/types.h>
44 #include <sys/wait.h>
45 #include <sys/stat.h>
46
47 namespace {
48 const int CHILD_TEST_FAIL    = 0;
49 const int CHILD_TEST_PASS    = 1;
50 const int CHILD_TEST_IGNORED = 2;
51
52 const int MSG_TYPE_MESSAGE   = 0; // sizeof(Message) + Message
53 const int MSG_TYPE_PERF_TIME = 1; // perfTime + maxTime
54
55 int closeOutput() {
56     int devnull;
57     int retcode = -1;
58     if (-1 == (devnull = TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY))))
59         return -1;
60
61     // replace stdout with /dev/null
62     if (-1 == TEMP_FAILURE_RETRY(dup2(devnull, STDOUT_FILENO)))
63         goto end;
64
65     // replace stderr with /dev/null
66     if (-1 == TEMP_FAILURE_RETRY(dup2(devnull, STDERR_FILENO)))
67         goto end;
68
69     retcode = 0;
70
71 end:
72     close(devnull);
73     return retcode;
74 }
75
76 } // namespace anonymous
77
78 namespace DPL {
79 namespace Test {
80
81 PipeWrapper::PipeWrapper()
82 {
83     if (-1 == pipe(m_pipefd)) {
84         m_pipefd[0] = PIPE_CLOSED;
85         m_pipefd[1] = PIPE_CLOSED;
86     }
87 }
88
89 PipeWrapper::~PipeWrapper()
90 {
91     closeHelp(0);
92     closeHelp(1);
93 }
94
95 bool PipeWrapper::isReady()
96 {
97     return m_pipefd[0] != PIPE_CLOSED || m_pipefd[1] != PIPE_CLOSED;
98 }
99
100 void PipeWrapper::setUsage(Usage usage)
101 {
102     if (usage == READONLY) {
103         closeHelp(1);
104     }
105     if (usage == WRITEONLY) {
106         closeHelp(0);
107     }
108 }
109
110 PipeWrapper::Status PipeWrapper::send(int code, std::string &message)
111 {
112     if (m_pipefd[1] == PIPE_CLOSED) {
113         return ERROR;
114     }
115
116     std::ostringstream output;
117     output << toBinaryString(code);
118     output << toBinaryString(MSG_TYPE_MESSAGE);
119     output << toBinaryString(static_cast<int>(message.size()));
120     output << message;
121
122     std::string binary = output.str();
123     int size = binary.size();
124
125     if ((writeHelp(&size,
126                    sizeof(int)) == ERROR) ||
127         (writeHelp(binary.c_str(), size) == ERROR))
128     {
129         return ERROR;
130     }
131     return SUCCESS;
132 }
133
134 PipeWrapper::Status PipeWrapper::sendTime(int code,
135                                           std::chrono::system_clock::duration time,
136                                           std::chrono::system_clock::duration timeMax)
137 {
138     if (m_pipefd[1] == PIPE_CLOSED) {
139         return ERROR;
140     }
141
142     std::ostringstream output;
143     output << toBinaryString(code);
144     output << toBinaryString(MSG_TYPE_PERF_TIME);
145     output << toBinaryString(time);
146     output << toBinaryString(timeMax);
147
148     std::string binary = output.str();
149     int size = binary.size();
150
151     if ((writeHelp(&size,
152                    sizeof(int)) == ERROR) ||
153         (writeHelp(binary.c_str(), size) == ERROR))
154     {
155         return ERROR;
156     }
157     return SUCCESS;
158 }
159
160 PipeWrapper::Status PipeWrapper::receive(int &code,
161                                          int &msgType,
162                                          std::string &data,
163                                          std::chrono::system_clock::duration &time,
164                                          std::chrono::system_clock::duration &timeMax,
165                                          time_t deadline)
166 {
167     if (m_pipefd[0] == PIPE_CLOSED) {
168         return ERROR;
169     }
170
171     int size;
172     Status ret;
173
174     if ((ret = readHelp(&size, sizeof(int), deadline)) != SUCCESS) {
175         return ret;
176     }
177
178     std::vector<char> buffer;
179     buffer.resize(size);
180
181     if ((ret = readHelp(&buffer[0], size, deadline)) != SUCCESS) {
182         return ret;
183     }
184
185     try {
186         DPL::BinaryQueue queue;
187         queue.AppendCopy(&buffer[0], size);
188
189         queue.FlattenConsume(&code, sizeof(int));
190         queue.FlattenConsume(&msgType, sizeof(int));
191
192         switch (msgType) {
193         case MSG_TYPE_MESSAGE:
194             queue.FlattenConsume(&size, sizeof(int));
195
196             buffer.resize(size);
197
198             queue.FlattenConsume(&buffer[0], size);
199             data.assign(buffer.begin(), buffer.end());
200             break;
201         case MSG_TYPE_PERF_TIME:
202             queue.FlattenConsume(&time, sizeof(std::chrono::system_clock::duration));
203             queue.FlattenConsume(&timeMax, sizeof(std::chrono::system_clock::duration));
204             break;
205         default:
206             return ERROR;
207         }
208     } catch (DPL::BinaryQueue::Exception::Base &e) {
209         return ERROR;
210     }
211     return SUCCESS;
212 }
213
214 void PipeWrapper::closeAll()
215 {
216     closeHelp(0);
217     closeHelp(1);
218 }
219
220 std::string PipeWrapper::toBinaryString(int data)
221 {
222     char buffer[sizeof(int)];
223     memcpy(buffer, &data, sizeof(int));
224     return std::string(buffer, buffer + sizeof(int));
225 }
226
227 std::string PipeWrapper::toBinaryString(std::chrono::system_clock::duration data)
228 {
229     char buffer[sizeof(std::chrono::system_clock::duration)];
230     memcpy(buffer, &data, sizeof(std::chrono::system_clock::duration));
231     return std::string(buffer, buffer + sizeof(std::chrono::system_clock::duration));
232 }
233
234 void PipeWrapper::closeHelp(int desc)
235 {
236     if (m_pipefd[desc] != PIPE_CLOSED) {
237         TEMP_FAILURE_RETRY(close(m_pipefd[desc]));
238         m_pipefd[desc] = PIPE_CLOSED;
239     }
240 }
241
242 PipeWrapper::Status PipeWrapper::writeHelp(const void *buffer, int size)
243 {
244     int ready = 0;
245     const char *p = static_cast<const char *>(buffer);
246     while (ready != size) {
247         int ret = write(m_pipefd[1], &p[ready], size - ready);
248
249         if (ret == -1 && (errno == EAGAIN || errno == EINTR)) {
250             continue;
251         }
252
253         if (ret == -1) {
254             closeHelp(1);
255             return ERROR;
256         }
257
258         ready += ret;
259     }
260     return SUCCESS;
261 }
262
263 PipeWrapper::Status PipeWrapper::readHelp(void *buf, int size, time_t deadline)
264 {
265     int ready = 0;
266     char *buffer = static_cast<char*>(buf);
267     while (ready != size) {
268         time_t wait = deadline - time(0);
269         wait = wait < 1 ? 1 : wait;
270         pollfd fds = { m_pipefd[0], POLLIN, 0 };
271
272         int pollReturn = poll(&fds, 1, wait * 1000);
273
274         if (pollReturn == 0) {
275             return TIMEOUT; // Timeout
276         }
277
278         if (pollReturn < -1) {
279             return ERROR;
280         }
281
282         int ret = read(m_pipefd[0], &buffer[ready], size - ready);
283
284         if (ret == -1 && (errno == EAGAIN || errno == EINTR)) {
285             continue;
286         }
287
288         if (ret == -1 || ret == 0) {
289             closeHelp(0);
290             return ERROR;
291         }
292
293         ready += ret;
294     }
295     return SUCCESS;
296 }
297
298 void RunChildProc(TestRunner::TestCase procChild)
299 {
300     PipeWrapper pipe;
301     if (!pipe.isReady()) {
302         throw TestFailed("Pipe creation failed");
303     }
304
305     pid_t pid = fork();
306
307     if (pid == -1) {
308         throw TestFailed("Child creation failed");
309     }
310
311     if (pid != 0) {
312         // parent code
313         pipe.setUsage(PipeWrapper::READONLY);
314
315         int code;
316         int msgType;
317         std::chrono::system_clock::duration time_m;
318         std::chrono::system_clock::duration timeMax_m;
319         std::string message;
320
321         int pipeReturn = pipe.receive(code, msgType, message, time_m, timeMax_m, time(0) + 10);
322
323         if (pipeReturn != PipeWrapper::SUCCESS) { // Timeout or reading error
324             pipe.closeAll();
325             kill(pid, SIGKILL);
326         }
327
328         int status;
329         waitpid(pid, &status, 0);
330
331         if (pipeReturn == PipeWrapper::TIMEOUT) {
332             throw TestFailed("Timeout");
333         }
334
335         if (pipeReturn == PipeWrapper::ERROR) {
336             throw TestFailed("Reading pipe error");
337         }
338
339         if (code == CHILD_TEST_PASS && msgType == MSG_TYPE_PERF_TIME) {
340             DPL::Test::TestRunnerSingleton::Instance().setCurrentTestCasePerformanceResult(true,
341                                                                                            time_m,
342                                                                                            timeMax_m);
343         }
344
345         if (code == CHILD_TEST_FAIL) {
346             throw TestFailed(message);
347         } else if (code == CHILD_TEST_IGNORED) {
348             throw TestIgnored(message);
349         }
350     } else {
351         // child code
352
353         // End Runner after current test
354         TestRunnerSingleton::Instance().Terminate();
355
356         int code = CHILD_TEST_PASS;
357         std::string msg;
358         bool isPerformanceTest;
359         std::chrono::system_clock::duration time_m;
360         std::chrono::system_clock::duration timeMax_m;
361
362         bool allowLogs = TestRunnerSingleton::Instance().GetAllowChildLogs();
363
364         close(STDIN_FILENO);
365         if (!allowLogs) {
366             closeOutput(); // if fails nothing we can do
367         }
368
369         pipe.setUsage(PipeWrapper::WRITEONLY);
370
371         try {
372             procChild();
373         } catch (const DPL::Test::TestFailed &e) {
374             msg = e.GetMessage();
375             code = CHILD_TEST_FAIL;
376         } catch (const DPL::Test::TestIgnored &e) {
377             msg = e.GetMessage();
378             code = CHILD_TEST_IGNORED;
379         } catch (...) { // catch all exception generated by "user" code
380             msg = "unhandled exeception";
381             code = CHILD_TEST_FAIL;
382         }
383
384         if (allowLogs) {
385             closeOutput();
386         }
387
388         DPL::Test::TestRunnerSingleton::Instance().getCurrentTestCasePerformanceResult(isPerformanceTest,
389                                                                                        time_m,
390                                                                                        timeMax_m);
391
392         if (code == CHILD_TEST_PASS && isPerformanceTest){
393             pipe.sendTime(code,
394                     time_m,
395                     timeMax_m);
396         } else {
397             pipe.send(code, msg);
398         }
399     }
400 }
401 } // namespace Test
402 } // namespace DPL