// -*- c++ -*- //----------------------------------------------------------------------- // // Name: sighand_test.C // // Synopsis: // % sighand_test // // Description: test program for following classes: // // o SigSet // o SigAction // o SigHandler // // o SIGINTHandler // o SIGUSR1Handler // o SIGCHLDHandler // o SIGALRMHandler // //----------------------------------------------------------------------- #include using namespace std; #include #include #include #include #include #if !defined (WIN32) #include #include "assa/SigHandler.h" #include "assa/Handlers.h" #include "assa/Logger.h" using namespace ASSA; #endif // !win32 int main() { #if !defined (WIN32) SigSet sigs; Log::open_log_file ("sighand_test.log"); trace("sighand_test"); cout << endl << "testing SigSet class:\n" << "---------------------\n"; cout << "empty set ... "; if (!sigs.empty()) cout << "pass\n"; else cout << "error\n"; cout << "fill set ... "; if (!sigs.fill()) cout << "pass\n"; else cout << "error\n"; cout << "add to set ... "; if (!sigs.add(9)) cout << "pass\n"; else cout << "error\n"; cout << "del from set ... "; if (!sigs.del(9)) cout << "pass\n"; else cout << "error\n"; cout << "add sig=11 to set ... "; if (!sigs.add(11)) cout << "pass\n"; else cout << "error\n"; cout << "test for sig=11 in set ..."; if (sigs.is_member(11)) cout << "pass\n"; else cout << "error\n"; cout << "test for prt to sigset_t ..."; sigset_t *internal; internal = sigs; cout << "pass" << endl << flush; cout << "\ntesting SigHandler class:\n"; cout << "---------------------------\n"; // -- installing handlers --- // SigHandler sha; // signal dispatcher SIGINTHandler intha; // various signal handlers SIGUSR1Handler usr1ha; SIGCHLDHandler chldha; SIGALRMHandler alrmha; DL((TRACE,"installing SIGINT\n")); assert(sha.install(SIGINT, &intha) == 0); DL((TRACE,"installing SIGUSR1\n")); assert(sha.install(SIGUSR1, &usr1ha) == 0); DL((TRACE,"installing SIGCHLD\n")); assert(sha.install(SIGCHLD, &chldha) == 0); SigAction cancelact; // cancel action // flush stdout here before fork - otherwise child will have a copy // of stdout buffer after fork and that output ends up on terminal // twice - first time from child and second time - from parent. // cout << flush; // running test: // - can parent get all 5 SIGUSR1 sent by the child? // - can parent get different signal? pid_t chldpid; if ( (chldpid = fork()) == -1) { cerr << "error: fork() failed\n"; exit(1); } else if (chldpid == 0) { // child pid_t prnt = getppid(); for (int i=0; i<5; i++) { // send SIGUSR1 five times /* * From R.Stevens, Ch 10.14, p.297 * * "additional occurrences of the same signal are usually not * queued. If the signal ocurs five times while its blocked, * when we unblock the signal the signal-handling function * for that signal will *usually* be invoked only one time." * * That is why here we need some delay between signals sent * to parent. */ DL((TRACE,"send SIGUSR1 # %d\n",i)); sleep(2); kill(prnt, SIGUSR1); if (i == 3) { // sending *different* signal DL((TRACE,"send *DIFFERENT* SIGINT\n")); kill(prnt, SIGINT); } } exit(0); } // From Linux sleep(3) manual: // // "sleep() may be implemented using SIGALRM; // mixing calls to alarm() and sleep() is a bad idea." // // We can use select(2) instead: struct timeval tv; // parent waits for 5 signals of SIGUSR1, for one signal of SIGINT // and also for the child to exit. If something goes wrong, // then alarm kicks in and test continues. DL((TRACE,"installing SIGALRM\n")); assert(sha.install(SIGALRM, &alrmha, &cancelact) == 0); alarm(20); for (int finish = 0; !alrmha.alarmed(); ) { DL((TRACE,"--------------next loop iteration------------\n")); if (usr1ha.received_count() == 5) { cout << "signal queueing test ... pass\n"; finish++; usr1ha.resetState(); } if (intha.graceful_quit()) { cout << "*different* signal test ... pass\n"; finish++; intha.resetState(); } if (chldha.child_exited()) { cout << "child exit signal test ... pass\n"; finish++; chldha.resetState(); } if (finish == 3) break; // sleep using select(2). After return from select(2), reset // tv values. tv.tv_sec = 5; tv.tv_usec = 0; DL((TRACE,"calling select\n")); if (select(0,NULL,NULL,NULL, 0) != 0) { if (errno != EINTR ) { // // we skip "Interrupted system call" (EINTR) here because // we expect it to be interrupted by signals delivered by // the child process // cout << "select(2) failed with errno - " << strerror (errno) << endl; } } } if (alrmha.alarmed()) cout << "\nsummary:\n\tsignal handler test ... failed\n"; else { alarm(0); // turn off alarm cout << "\nsummary:\n\tsignal handler test ... pass\n"; } cout << "\nSigHandler test done!\n"; #endif // !win32 exit(0); }