From c4741c59ea5c65dde44a3ec7ac5464709fe42cae Mon Sep 17 00:00:00 2001 From: John Denker Date: Fri, 20 Jul 2012 16:15:13 -0700 Subject: fix up error parsing and error logging --- tools/hi-q.c | 263 +++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 173 insertions(+), 90 deletions(-) (limited to 'tools/hi-q.c') 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 cmd; + + jobber(const moder _mode, const vector _cmd) + : mode(_mode), cmd(_cmd) + {} + + jobber(const string _mode, const vector _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 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 post){ + for(vector::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 cmd; - - jobber(const moder _mode, const vector _cmd) - : mode(_mode), cmd(_cmd) - {} - - jobber(const string _mode, const vector _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); } } -- cgit v1.2.3