1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
|
// -*- 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 <iostream>
using namespace std;
#include <assert.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#if !defined (WIN32)
#include <sys/wait.h>
#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);
}
|