summaryrefslogtreecommitdiff
path: root/tools/hi-q.c
diff options
context:
space:
mode:
authorJohn Denker <jsd@av8n.com>2012-07-20 23:15:13 (GMT)
committerJohn Denker <jsd@av8n.com>2012-07-20 23:21:08 (GMT)
commitc4741c59ea5c65dde44a3ec7ac5464709fe42cae (patch)
tree98d6312bb72fa0c540151903a0b03c783a184c28 /tools/hi-q.c
parent03abc5e03f8c4d66f3d09e42ddd75297ee1f3065 (diff)
fix up error parsing and error logging
Diffstat (limited to 'tools/hi-q.c')
-rw-r--r--tools/hi-q.c263
1 files changed, 173 insertions, 90 deletions
diff --git a/tools/hi-q.c b/tools/hi-q.c
index 369935e..f6b57e1 100644
--- a/tools/hi-q.c
+++ b/tools/hi-q.c
@@ -5,7 +5,7 @@
// Hint: For testing, see also hi-test.conf which invokes ./hi-test:
// ./hi-q hi-test.conf
-// TODO: Panic stop should signal all children.
+// TODO: Exeunt stop should signal all children.
// TODO: Possibly: Wait for all kids in parallel?
// That's because they might finish out of order.
@@ -66,9 +66,128 @@ foo_sa(TOOBIG, 98, "message was too big to process (see --max-size)"
#define bufsize 16384
-void panic(const int sts) {
+// meanings:
+// sa is a filter, using not-very-expressive exit codes: 0=ham 1=spam.
+// stub is not a filter; no stdin or stdout; just looks at environment.
+// series is a filter.
+// qq is not a filter, just an absorber.
+//
+// Note that series and stub use the same exit codes as qq.
+//
+typedef enum {series, stub, sa, qq, postspam, fail} moder;
+
+class jobber{
+public:
+ moder mode;
+ vector<string> cmd;
+
+ jobber(const moder _mode, const vector<string> _cmd)
+ : mode(_mode), cmd(_cmd)
+ {}
+
+ jobber(const string _mode, const vector<string> _cmd)
+ : mode(fail), cmd(_cmd){
+ setmode(_mode);
+ }
+
+ jobber()
+ : mode(fail), cmd(0)
+ {}
+
+ void setmode(const string _mode) {
+ if (0) {}
+ else if (_mode == "sa") mode = sa;
+ else if (_mode == "stub") mode = stub;
+ else if (_mode == "series") mode = series;
+ else if (_mode == "qq") mode = qq;
+ else if (_mode == "postspam") mode = postspam;
+ else {
+ cerr << "jobber: bad mode: " << _mode << endl;
+ mode = fail;
+ }
+ }
+};
+
+// klugey global variable:
+vector<jobber> post;
+
+// We are fussy about the argument types because we want
+// this to compile cleanly under g++ as well as gcc,
+// and each is strict about different things, such that
+// one or the other will complain unless everything is
+// done just right.
+
+// This is the way execve really behaves:
+// the characters are held constant
+// and the (char*) pointers are held constant:
+int Execve(char const * fn,
+ char const * const * argv,
+ char const * const * env) {
+// coerce the arg types to match the unwise declaration in unistd.h :
+ return execve(fn, (char*const*) argv, (char*const*) env);
+}
+
+int fork_and_wait(const jobber job){
+ pid_t kidpid = fork();
+ if (kidpid == -1) {
+ cerr << "hi-q: fork failed : ";
+ perror(0);
+ exit(ex_syserr);
+ }
+ int ntok = job.cmd.size();
+ const char* prog[1+ntok];
+ for (int jj = 0; jj < ntok; jj++){
+ prog[jj] = job.cmd[jj].c_str();
+ }
+ prog[ntok] = 0;
+
+ if (!kidpid){
+ /*** child code ***/
+ int rslt;
+ rslt = Execve(prog[0], prog, environ);
+ fprintf(stderr, "hi-q: failed to exec '%s': ", prog[0]);
+ perror(0);
+ exit(ex_syserr);
+ } else {
+ /*** parent code ***/
+ int kidstatus;
+ pid_t somekid;
+ somekid = waitpid(kidpid, &kidstatus, WUNTRACED);
+ if (WIFEXITED(kidstatus)) {
+ int sts = WEXITSTATUS(kidstatus);
+ if (sts != ex_good && sts != ex_spam) {
+ cerr << "hi-q: job " << prog[0]
+ << " unexpectedly returns status: " << sts
+ << endl;
+ exit(sts);
+ }
+ return 0;
+ } else if (WIFSIGNALED(kidstatus)) {
+ int sig = WTERMSIG(kidstatus);
+ if (sig == SIGUSR1) {/* normal, no logging required */}
+ else cerr << "hi-q: job " << prog[0]
+ << " killed by signal " << sig << endl;
+ return(ex_syserr);
+ } else {
+ /* paused, not dead */
+ }
+ }
+ return 0;
+}
+
+int fork_and_wait(vector<jobber> post){
+ for(vector<jobber>::const_iterator foo = post.begin();
+ foo != post.end(); foo++) {
+ int rslt = fork_and_wait(*foo);
+ if (rslt) return rslt;
+ }
+ return 0;
+}
+
+void exeunt(const int sts) {
// FIXME: stop other children
- cerr << "hi-q: panic called with " << sts << endl;
+ //xxxx cerr << "hi-q: exeunt called with " << sts << endl;
+ if (sts == ex_spam) fork_and_wait(post);
exit(sts);
}
@@ -83,7 +202,7 @@ void slurp(const int inch, const int ouch){
if (got < 0) {
fprintf(stderr, "hi-q: input error: ");
perror(0);
- panic(ex_comerr);
+ exeunt(ex_comerr);
}
todo = got;
@@ -92,7 +211,7 @@ void slurp(const int inch, const int ouch){
if (sent < 0 && errno != EINTR) {
fprintf(stderr, "hi-q: output error on fd%d : ", ouch);
perror(0);
- panic(ex_comerr);
+ exeunt(ex_comerr);
}
todo -= sent;
}
@@ -126,22 +245,6 @@ void blurb(const int ii, const pid_t* kidpid) {
}
-// We are fussy about the argument types because we want
-// this to compile cleanly under g++ as well as gcc,
-// and each is strict about different things, such that
-// one or the other will complain unless everything is
-// done just right.
-
-// This is the way execve really behaves:
-// the characters are held constant
-// and the (char*) pointers are held constant:
-int Execve(char const * fn,
- char const * const * argv,
- char const * const * env) {
-// coerce the arg types to match the unwise declaration in unistd.h :
- return execve(fn, (char*const*) argv, (char*const*) env);
-}
-
void usage() {
cerr << "Usage:\n"
" hi-q filter.conf\n"
@@ -172,47 +275,6 @@ int xclose(int arg){
extern char** environ;
-// meanings:
-// sa is a filter, using not-very-expressive exit codes: 0=ham 1=spam.
-// stub is not a filter; no stdin or stdout; just looks at environment.
-// series is a filter.
-// qq is not a filter, just an absorber.
-//
-// Note that series and stub use the same exit codes as qq.
-//
-typedef enum {series, stub, sa, qq, fail} moder;
-
-class jobber{
-public:
- moder mode;
- vector<string> cmd;
-
- jobber(const moder _mode, const vector<string> _cmd)
- : mode(_mode), cmd(_cmd)
- {}
-
- jobber(const string _mode, const vector<string> _cmd)
- : mode(fail), cmd(_cmd){
- setmode(_mode);
- }
-
- jobber()
- : mode(fail), cmd(0)
- {}
-
- void setmode(const string _mode) {
- if (0) {}
- else if (_mode == "sa") mode = sa;
- else if (_mode == "stub") mode = stub;
- else if (_mode == "series") mode = series;
- else if (_mode == "qq") mode = qq;
- else {
- cerr << "jobber: bad mode: " << _mode << endl;
- mode = fail;
- }
- }
-};
-
int main(int argc, char** argv) {
progname = *argv;
mypid = getpid();
@@ -278,7 +340,14 @@ bar
job.setmode(job.cmd.front());
job.cmd.erase(job.cmd.begin());
}
- if (job.cmd.size()) filter.push_back(job);
+ // here with a properly built job descriptor
+ if (job.cmd.size()) {
+ if (job.mode == postspam) {
+ post.push_back(job);
+ } else {
+ filter.push_back(job);
+ }
+ }
}
unsigned int nkids = filter.size();
@@ -337,7 +406,7 @@ bar
if (rslt < 0) {
fprintf(stderr, "hi-q: could not create datapipe: ");
perror(0);
- panic(ex_syserr);
+ exeunt(ex_syserr);
}
//xx fprintf(stderr, "pipe: %d %d\n", datapipe[0], datapipe[1]);
@@ -451,7 +520,7 @@ bar
if (kidpid[ii] < 0) {
fprintf(stderr, "hi-q: failure to fork kid#%d: ", ii);
perror(0);
- panic(ex_syserr);
+ exeunt(ex_syserr);
}
close(kid_end);
@@ -509,11 +578,20 @@ bar
// do not decrement the "alive" counter
// since that only applies to non-special kids
if (WIFEXITED(kidstatus)) {
- 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;
+ cerr << "hi-q: special kid exited early, status "
+ << WEXITSTATUS(kidstatus)
+ << " with " << alive << " kids still alive"
+ << endl;
return(ex_syserr);
+ } else if (WIFSIGNALED(kidstatus)) {
+ int sig = WTERMSIG(kidstatus);
+ if (sig == SIGUSR1) {/* normal, no logging required */}
+ else {
+ cerr << "hi-q: special kid killed by signal "
+ << sig << endl;
+ // this is not normal
+ return(ex_syserr);
+ }
} else {
/* paused, not dead */
}
@@ -542,42 +620,47 @@ bar
///////////////////
// decode the best reason why the filter-chain terminated
+ //xx cerr << "cleanup: " << best_blame << endl;
if (best_blame) {
string short_name("");
int kidno(iiofpid[argbest_blame]);
if (WIFEXITED(best_blame)) {
- string exword = "spam"; // default, for non-modern status codes
- int excode = ex_spam; // default, for non-modern status codes
+ string exword = "???"; // default, should never happen
+ int excode = ex_syserr; // default, should never happen
int sts = WEXITSTATUS(best_blame);
- if (filter[kidno].mode != sa) {
- exword = codemap[sts];
- excode = sts;
- }
- if (exword.length()) {
- cerr << "hi-q says: kid[" << kidno << "]"
- << " pid " << argbest_blame
- << " i.e. '" << filter[kidno].cmd[0] << "'"
- << " reports " << exword << endl;
- panic(excode);
- }
- if (sts != 0) {
- cerr << "hi-q says: kid " << argbest_blame
- << " exited with bad status: " << sts
- << endl;
- panic(ex_syserr);
- } else {
+ if (sts == 0){
// should never get here
// should be no accounting for blame if there was no blame
cerr << "hi-q: should never happen: no child to blame" << endl;
- panic(ex_syserr);
+ exeunt(ex_syserr);
+ }
+
+ if (filter[kidno].mode != sa) {
+ exword = codemap[sts];
+ excode = sts;
+ } else { // here to translate spamc results
+ if (sts == 1) {
+ excode = ex_spam;
+ exword = "spam";
+ } else {
+ excode = ex_syserr;
+ stringstream foo;
+ foo << "bad status: " << sts;
+ exword = foo.str();
+ }
}
+ cerr << "hi-q concludes: kid[" << kidno << "]"
+ << " pid " << argbest_blame
+ << " i.e. '" << filter[kidno].cmd[0] << "'"
+ << " reports " << exword << endl;
+ exeunt(excode);
} 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);
+ exeunt(ex_syserr);
}
}