1 #ifndef TUT_FORK_H_GUARD
2 #define TUT_FORK_H_GUARD
3 #include <tut/tut_config.hpp>
4 
5 #if __cplusplus >= 201103L
6 #define TUT_UNIQUE_PTR std::unique_ptr
7 #else
8 #define TUT_UNIQUE_PTR std::auto_ptr
9 #endif
10 
11 #if defined(TUT_USE_POSIX)
12 #include <errno.h>
13 #include <unistd.h>
14 #include <signal.h>
15 #include <sys/wait.h>
16 #include <sys/time.h>
17 #include <sys/types.h>
18 
19 #include <cstring>
20 #include <cstdlib>
21 #include <map>
22 #include <iterator>
23 #include <functional>
24 
25 #include "tut_result.hpp"
26 #include "tut_assert.hpp"
27 #include "tut_runner.hpp"
28 
29 namespace tut
30 {
31 
32 template<typename, int>
33 class test_group;
34 
35 template<typename T>
36 class test_object;
37 
38 class test_group_posix
39 {
40 private:
41     template<typename, int>
42     friend class test_group;
43 
44     template<typename T>
send_result_(const T * obj,const test_result & tr)45     void send_result_(const T *obj, const test_result &tr)
46     {
47         if(obj->get_pipe_() == -1)
48         {
49             return;
50         }
51 
52         if(tr.result != test_result::ok)
53         {
54             std::ostringstream ss;
55             ss << int(tr.result) << "\n"
56                 << tr.group << "\n"
57                 << tr.test << "\n"
58                 << tr.name << "\n"
59                 << tr.exception_typeid << "\n";
60             std::copy( tr.message.begin(), tr.message.end(), std::ostreambuf_iterator<char>(ss.rdbuf()) );
61 
62             int size = ss.str().length();
63             int w = write(obj->get_pipe_(), ss.str().c_str(), size);
64             ensure_errno("write() failed", w == size);
65         }
66     }
67 
~test_group_posix()68     virtual ~test_group_posix()
69     {
70     }
71 };
72 
73 template<typename T>
74 struct tut_posix
75 {
forktut::tut_posix76     pid_t fork()
77     {
78 #ifdef TUT_USE_RTTI
79         test_object<T> *self = dynamic_cast< tut::test_object<T>* >(this);
80         ensure("trying to call 'tut_fork' in ctor of test object", self != NULL);
81 #else
82         test_object<T> *self = static_cast< tut::test_object<T>* >(this);
83 #endif
84 
85         return self->fork_();
86     }
87 
waitpidtut::tut_posix88     pid_t waitpid(pid_t pid, int *status, int flags = 0)
89     {
90 #ifdef TUT_USE_RTTI
91         test_object<T> *self = dynamic_cast< tut::test_object<T>* >(this);
92         ensure("trying to call 'tut_waitpid' in ctor of test object", self != NULL);
93 #else
94         test_object<T> *self = static_cast< tut::test_object<T>* >(this);
95 #endif
96 
97         return self->waitpid_(pid, status, flags);
98     }
99 
ensure_child_exittut::tut_posix100     void ensure_child_exit(pid_t pid, int exit_status = 0)
101     {
102 #ifdef TUT_USE_RTTI
103         test_object<T> *self = dynamic_cast< tut::test_object<T>* >(this);
104         ensure("trying to call 'ensure_child_exit' in ctor of test object", self != NULL);
105 #else
106         test_object<T> *self = static_cast< tut::test_object<T>* >(this);
107 #endif
108 
109         int status;
110         self->waitpid_(pid, &status);
111 
112         self->ensure_child_exit_(status, exit_status);
113     }
114 
115 
ensure_child_signaltut::tut_posix116     void ensure_child_signal(pid_t pid, int signal = SIGTERM)
117     {
118 #ifdef TUT_USE_RTTI
119         test_object<T> *self = dynamic_cast< tut::test_object<T>* >(this);
120         ensure("trying to call 'ensure_child_signal' in ctor of test object", self != NULL);
121 #else
122         test_object<T> *self = static_cast< tut::test_object<T>* >(this);
123 #endif
124 
125         int status;
126         self->waitpid_(pid, &status);
127 
128         self->ensure_child_signal_(status, signal);
129     }
130 
get_pidstut::tut_posix131     std::set<pid_t> get_pids() const
132     {
133 #ifdef TUT_USE_RTTI
134         const test_object<T> *self = dynamic_cast< const tut::test_object<T>* >(this);
135         ensure("trying to call 'get_pids' in ctor of test object", self != NULL);
136 #else
137         const test_object<T> *self = static_cast< const tut::test_object<T>* >(this);
138 #endif
139 
140         return self->get_pids_();
141     }
142 
~tut_posixtut::tut_posix143     virtual ~tut_posix()
144     {
145     }
146 
147 };
148 
149 class test_object_posix
150 {
151 public:
152     typedef std::map<pid_t, int> pid_map;
153 
154     /**
155      * Default constructor
156      */
test_object_posix()157     test_object_posix()
158         : pids_(),
159           pipe_(-1)
160     {
161     }
162 
163 
~test_object_posix()164     virtual ~test_object_posix()
165     {
166         // we have forked
167         if(pipe_ != -1)
168         {
169             // in child, force exit
170             std::exit(0);
171         }
172 
173         if(!pids_.empty())
174         {
175             std::ostringstream ss;
176 
177             // in parent, reap children
178             for(std::map<pid_t, int>::iterator i = pids_.begin(); i != pids_.end(); ++i)
179             {
180                 try {
181                     kill_child_(i->first);
182                 } catch(const rethrown &ex) {
183                     ss << std::endl << "child " << ex.tr.pid << " has thrown an exception: " << ex.what();
184                 } catch(const failure &ex) {
185                     ss << std::endl << ex.what();
186                 }
187             }
188 
189             if(!ss.str().empty())
190             {
191 #if __cplusplus >= 201103L
192                 std::cerr << ss.rdbuf() << std::endl;
193 #else
194                 fail(ss.str().c_str());
195 #endif
196             }
197         }
198     }
199 
200 private:
201     template<typename T>
202     friend class tut_posix;
203 
204     friend class test_group_posix;
205 
get_pipe_() const206     int get_pipe_() const
207     {
208         return pipe_;
209     }
210 
211 
fork_()212     pid_t fork_()
213     {
214         // create pipe
215         int fds[2];
216         ensure_errno("pipe() failed", ::pipe(fds) == 0);
217 
218         pid_t pid = ::fork();
219 
220         ensure_errno("fork() failed", pid >= 0);
221 
222         if(pid != 0)
223         {
224             // in parent, register pid
225             ensure("duplicated child", pids_.insert( std::make_pair(pid, fds[0]) ).second);
226 
227             // close writing side
228             close(fds[1]);
229         }
230         else
231         {
232             // in child, shutdown reporter
233             tut::runner.get().clear_callbacks();
234 
235             // close reading side
236             close(fds[0]);
237             pipe_ = fds[1];
238         }
239 
240         return pid;
241     }
242 
kill_child_(pid_t pid)243     void kill_child_(pid_t pid)
244     {
245         int status;
246 
247         if(waitpid_(pid, &status, WNOHANG) == pid)
248         {
249             ensure_child_exit_(status, 0);
250             return;
251         }
252 
253         if(::kill(pid, SIGTERM) != 0)
254         {
255             if(errno == ESRCH)
256             {
257                 // no such process
258                 return;
259             }
260             else
261             {
262                 // cannot kill, we are in trouble
263                 std::ostringstream ss;
264                 char e[1024];
265                 ss << "child " << pid << " could not be killed with SIGTERM, " << strerror_r(errno, e, sizeof(e)) << std::endl;
266                 fail(ss.str());
267             }
268         }
269 
270         if(waitpid_(pid, &status, WNOHANG) == pid)
271         {
272             // child killed, check signal
273             ensure_child_signal_(status, SIGTERM);
274 
275             ensure_equals("child process exists after SIGTERM", ::kill(pid, 0), -1);
276             return;
277         }
278 
279         // child seems to be still exiting, give it some time
280         sleep(2);
281 
282         if(waitpid_(pid, &status, WNOHANG) != pid)
283         {
284             // child is still running, kill it
285             if(::kill(pid, SIGKILL) != 0)
286             {
287                 if(errno == ESRCH)
288                 {
289                     // no such process
290                     return;
291                 }
292                 else
293                 {
294                     std::ostringstream ss;
295                     char e[1024];
296                     ss << "child " << pid << " could not be killed with SIGKILL, " << strerror_r(errno, e, sizeof(e)) << std::endl;
297                     fail(ss.str());
298                 }
299             }
300 
301             ensure_equals("wait after SIGKILL", waitpid_(pid, &status), pid);
302             ensure_child_signal_(status, SIGKILL);
303 
304             ensure_equals("child process exists after SIGKILL", ::kill(pid, 0), -1);
305 
306             std::ostringstream ss;
307             ss << "child " << pid << " had to be killed with SIGKILL";
308             fail(ss.str());
309         }
310     }
311 
receive_result_(std::istream & ss,pid_t pid)312     test_result receive_result_(std::istream &ss, pid_t pid)
313     {
314         test_result tr;
315 
316         int type;
317         ss >> type;
318         tr.result = test_result::result_type(type);
319         ss.ignore(1024, '\n');
320 
321         std::getline(ss, tr.group);
322         ss >> tr.test;
323         ss.ignore(1024, '\n');
324         std::getline(ss, tr.name);
325         std::getline(ss, tr.exception_typeid);
326         std::copy( std::istreambuf_iterator<char>(ss.rdbuf()),
327                    std::istreambuf_iterator<char>(),
328                    std::back_inserter(tr.message) );
329 
330         tr.pid = pid;
331 
332         return tr;
333     }
334 
335     struct fdclose
336     {
fdclosetut::test_object_posix::fdclose337         fdclose(int fd): fd_(fd) { }
~fdclosetut::test_object_posix::fdclose338         ~fdclose()
339         {
340             close(fd_);
341         }
342     private:
343         int fd_;
344     };
345 
waitpid_(pid_t pid,int * status,int flags=0)346     pid_t waitpid_(pid_t pid, int *status, int flags = 0)
347     {
348 
349         ensure("trying to wait for unknown pid", pids_.count(pid) > 0);
350 
351         pid_t p = ::waitpid(pid, status, flags);
352         if( (flags & WNOHANG) && (p != pid) )
353         {
354             return p;
355         }
356 
357         // read child result from pipe
358         fd_set fdset;
359         timeval tv;
360         tv.tv_sec = 0;
361         tv.tv_usec = 0;
362 
363         FD_ZERO(&fdset);
364 
365         int pipe = pids_[pid];
366         fdclose guard(pipe);
367 
368         FD_SET(pipe, &fdset);
369 
370         int result = select(pipe+1, &fdset, NULL, NULL, &tv);
371         ensure_errno("sanity check on select() failed", result >= 0);
372 
373         if(result > 0)
374         {
375             ensure("sanity check on FD_ISSET() failed", FD_ISSET(pipe, &fdset) );
376 
377             std::stringstream ss;
378 
379             //TODO: max failure length
380             char buffer[1024];
381             int r = read(pipe, buffer, sizeof(buffer));
382             ensure_errno("sanity check on read() failed", r >= 0);
383 
384             if(r > 0)
385             {
386                 ss.write(buffer, r);
387                 throw rethrown( receive_result_(ss, pid) );
388             }
389         }
390 
391         return pid;
392     }
393 
ensure_child_exit_(int status,int exit_status)394     void ensure_child_exit_(int status, int exit_status)
395     {
396         if(WIFSIGNALED(status))
397         {
398             std::ostringstream ss;
399             ss << "child killed by signal " << WTERMSIG(status)
400                 << ": expected exit with code " << exit_status;
401 
402             throw failure(ss.str().c_str());
403         }
404 
405         if(WIFEXITED(status))
406         {
407             if(WEXITSTATUS(status) != exit_status)
408             {
409                 std::ostringstream ss;
410                 ss << "child exited, expected '"
411                     << exit_status
412                     << "' actual '"
413                     << WEXITSTATUS(status)
414                     << '\'';
415 
416                 throw failure(ss.str().c_str());
417             }
418         }
419 
420         if(WIFSTOPPED(status))
421         {
422             std::ostringstream ss;
423             ss << "child stopped by signal " << WTERMSIG(status)
424                 << ": expected exit with code " << exit_status;
425             throw failure(ss.str().c_str());
426         }
427     }
428 
ensure_child_signal_(int status,int signal)429     void ensure_child_signal_(int status, int signal)
430     {
431         if(WIFSIGNALED(status))
432         {
433             if(WTERMSIG(status) != signal)
434             {
435                 std::ostringstream ss;
436                 ss << "child killed by signal, expected '"
437                     << signal
438                     << "' actual '"
439                     << WTERMSIG(status)
440                     << '\'';
441                 throw failure(ss.str().c_str());
442             }
443         }
444 
445         if(WIFEXITED(status))
446         {
447             std::ostringstream ss;
448             ss << "child exited with code " << WEXITSTATUS(status)
449                 << ": expected signal " << signal;
450 
451             throw failure(ss.str().c_str());
452         }
453 
454         if(WIFSTOPPED(status))
455         {
456             std::ostringstream ss;
457             ss << "child stopped by signal " << WTERMSIG(status)
458                 << ": expected kill by signal " << signal;
459 
460             throw failure(ss.str().c_str());
461         }
462     }
463 
get_pids_() const464     std::set<pid_t> get_pids_() const
465     {
466         using namespace std;
467 
468         set<pid_t> pids;
469         for(pid_map::const_iterator i = pids_.begin(); i != pids_.end(); ++i)
470         {
471             pids.insert( i->first );
472         }
473 
474         return pids;
475     }
476 
477     pid_map         pids_;
478     int             pipe_;
479 };
480 
481 } // namespace tut
482 
483 #else
484 
485 namespace tut
486 {
487 
488 struct test_object_posix
489 {
~test_object_posixtut::test_object_posix490     virtual ~test_object_posix()
491     {
492     }
493 };
494 
495 struct test_group_posix
496 {
497     template<typename T>
send_result_tut::test_group_posix498     void send_result_(const T*, const test_result &)
499     {
500     }
501 
~test_group_posixtut::test_group_posix502     virtual ~test_group_posix()
503     {
504     }
505 };
506 
507 } // namespace tut
508 
509 #endif
510 
511 
512 #endif
513 
514