summaryrefslogtreecommitdiff
path: root/tools/hi-q.c
diff options
context:
space:
mode:
authorJohn Denker <jsd@av8n.com>2012-07-15 19:11:15 (GMT)
committerJohn Denker <jsd@av8n.com>2012-07-15 19:13:13 (GMT)
commit827bfdd5050bda8ad8954d8231e7836dd7aac867 (patch)
treecde67ea6b18a12f34b2341dc9fbdaeddb581212c /tools/hi-q.c
parentffc4b5ce605253957b2a0ce82cf924c669806590 (diff)
allow filters to terminate in arbitrary order;
report best blame (not first blame)
Diffstat (limited to 'tools/hi-q.c')
-rw-r--r--tools/hi-q.c143
1 files changed, 88 insertions, 55 deletions
diff --git a/tools/hi-q.c b/tools/hi-q.c
index 914bb57..1896bf4 100644
--- a/tools/hi-q.c
+++ b/tools/hi-q.c
@@ -133,7 +133,6 @@ extern char** environ;
int main(int argc, char** argv) {
int verbose(1);
int kidstatus;
- pid_t somekid;
int rslt;
int loose_end = 0;
@@ -207,6 +206,12 @@ int main(int argc, char** argv) {
if (job.size()) filter.push_back(job);
}
unsigned int nkids = filter.size();
+
+// Check for nothing to do.
+// This is important, because the "last kid" is a special case.
+// This makes it safe to assume that nkids-1 is non-negative.
+ if (nkids == 0) exit(0); // nothing to do
+
if (0 && verbose) for (unsigned int ii = 0; ii < nkids; ii++) {
cerr << "hi-q filter[" << ii << "] :; ";
for (VS::const_iterator token = filter[ii].begin();
@@ -407,51 +412,80 @@ int main(int argc, char** argv) {
cerr << endl;
}
-// loop over N-1 kids ... _not_ including last kid
- for (unsigned int ii=0; ii<nkids-1; ii++){
-#ifdef testing
- blurb(ii, kidpid);
-#else
- for (;;){
- somekid = waitpid(kidpid[ii], &kidstatus, WUNTRACED);
- if (somekid) {} // avoid silly compiler warning
+ pid_t special_kid = kidpid[nkids-1];
+ int alive(nkids-1); // not counting the special kid
+ int best_blame(0); // best reason, even if not a great reason
+ pid_t argbest_blame(-1); // kid# associated with best blame
+
+ for (;;) {
+ if (alive == 0) break;
+ pid_t somekid = waitpid(-1, &kidstatus, WUNTRACED);
+ if (somekid == special_kid){
+ // do not decrement the "alive" counter
+ // since that only applies to non-special kids
if (WIFEXITED(kidstatus)) {
- int sts = WEXITSTATUS(kidstatus);
- if (sts == 1) {
- cerr << "hi-q says: kid " << ii
- << " i.e. '" << filter[ii][0] << "' reports spam."
- << endl;
- panic(ex_spam);
- }
- if (sts != 0) {
- cerr << "hi-q says: kid " << ii
- << " i.e. '" << filter[ii][0] << "'"
- << " exited with bad status: " << sts
- << endl;
- panic(ex_syserr);
- }
- break; // kidstatus==0 means clean exit;
- // go check other kids
- } else if (WIFSIGNALED(kidstatus)) {
- int sig = WTERMSIG(kidstatus);
- cerr << "hi-q says: kid " << ii
- << " == " << kidpid[ii]
- << " == '" << filter[ii][0] << "'"
- << " was killed by signal " << sig
- << endl;
- if (sig == SIGUSR1) exit(ex_spam);
- panic(ex_syserr); // any kill, not a normal exit
+ cerr << "hi-q: special kid exited early" << endl;
+ return(ex_syserr);
+ } else if (WIFSIGNALED(kidstatus) && WTERMSIG(kidstatus) != SIGUSR1) {
+ cerr << "hi-q: special kid exited early" << endl;
+ return(ex_syserr);
} else {
- // some status change other than exit or kill
- // perhaps stopped for terminal input
+ /* paused, not dead */
}
+ continue;
+ }
+// here if somekid is not the special kid
+ if (WIFEXITED(kidstatus)) {
+ alive--;
+ if (WEXITSTATUS(kidstatus)) {
+ argbest_blame = somekid;
+ best_blame = kidstatus;
+ break;
+ }
+ } else if (WIFSIGNALED(kidstatus)) {
+ alive--;
+ argbest_blame = somekid;
+ best_blame = kidstatus;
+ if (WTERMSIG(kidstatus) != SIGUSR1) break;
+ } else {
+ /* kid is paused, not dead */
+ /* not a problem */
+ }
+ }
+// here if all kids have exited normally
+// *or* if there is a great reason for quitting early
+
+///////////////////
+// decode the best reason why the filter-chain terminated
+ if (best_blame) {
+ if (WIFEXITED(best_blame)) {
+ int sts = WEXITSTATUS(best_blame);
+ if (sts == 1) {
+ cerr << "hi-q says: kid " << argbest_blame
+ << " reports spam." << endl;
+ panic(ex_spam);
+ }
+ if (sts != 0) {
+ cerr << "hi-q says: kid " << argbest_blame
+ << " exited with bad status: " << sts
+ << endl;
+ panic(ex_syserr);
+ } else {
+ // should never get here unless exit status was nonzero
+ cerr << "hi-q: should never happen" << endl;
+ panic(ex_syserr);
+ }
+ } else if (WIFSIGNALED(best_blame)) {
+ int sig = WTERMSIG(best_blame);
+ cerr << "hi-q says: kid " << argbest_blame
+ << " was killed by signal " << sig
+ << endl;
+ // if the *best* blame is a kill, that's not normal
+ panic(ex_syserr);
}
-#endif
}
-//xx fprintf(stderr, "slurping %d %d\n", 1, loose_end);
-
-// All filters agree this is not spam.
+// Here if all filters agree this is not spam.
// Now it is safe to transfer the envelope information:
slurp(1, loose_end);
close(1);
@@ -460,25 +494,24 @@ int main(int argc, char** argv) {
// now that the envelope information has been transfered,
// wait for the last kid in the usual way
{
- int ii = nkids-1;
#ifdef moretesting
fprintf(stderr, "About to wait for kid #%d (%d)\n",
- ii, kidpid[ii]);
- blurb(nkids-1, kidpid);
+ special_kid, kidpid[special_kid]);
#endif
- somekid = waitpid(kidpid[ii], &kidstatus, WUNTRACED);
- if (WIFEXITED(kidstatus)) {
- int sts = WEXITSTATUS(kidstatus);
- cerr << "hi-q ends with status: " << sts << endl;
- return sts;
+ for(;;) {
+ waitpid(special_kid, &kidstatus, WUNTRACED);
+ if (WIFEXITED(kidstatus)) {
+ int sts = WEXITSTATUS(kidstatus);
+ cerr << "hi-q ends with status: " << sts << endl;
+ return sts;
+ } else if (WIFSIGNALED(kidstatus)) {
+ cerr << "hi-q: special kid was killed by signal "
+ << WTERMSIG(kidstatus) << endl;
+ return ex_syserr;
+ } else {
+ /* paused, not dead */
+ }
}
}
-
-#ifdef testing
- sleep(1);
-#endif
-// the last kid did not exit but was killed:
- return ex_syserr;
-
}