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