summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Denker <jsd@av8n.com>2012-07-20 16:15:13 -0700
committerJohn Denker <jsd@av8n.com>2012-07-20 16:21:08 -0700
commitc4741c59ea5c65dde44a3ec7ac5464709fe42cae (patch)
tree98d6312bb72fa0c540151903a0b03c783a184c28
parent03abc5e03f8c4d66f3d09e42ddd75297ee1f3065 (diff)
fix up error parsing and error logging
-rw-r--r--tools/filters.conf2
-rw-r--r--tools/greylist.c37
-rw-r--r--tools/hi-q.c263
-rw-r--r--tools/hi-test2.conf7
4 files changed, 206 insertions, 103 deletions
diff --git a/tools/filters.conf b/tools/filters.conf
index dfd1180..3ef7524 100644
--- a/tools/filters.conf
+++ b/tools/filters.conf
@@ -3,3 +3,5 @@ series /var/qmail/bin/skrewt
stub /var/qmail/bin/greylist
sa /usr/local/bin/spamc -Y 0 -s 1000000
qq /var/qmail/bin/qmail-queue
+
+postspam /var/qmail/bin/greylist -penalize 86400
diff --git a/tools/greylist.c b/tools/greylist.c
index 00272d8..d1ff1a4 100644
--- a/tools/greylist.c
+++ b/tools/greylist.c
@@ -51,7 +51,6 @@ void dump(const string var){
}
-
////////////////
// little utility to help with argument parsing:
//
@@ -145,10 +144,13 @@ void scan(const string p, const int copies=1){
int ac_age = now.tv_sec - mystat.st_atime;
cout << setw(10) << time_out(mod_age)
<< " " << setw(10) << time_out(ac_age);
- if (mod_age < 0) {
+ if (0) {
+
+ } else if (mod_age < 0) {
cout << " penalty";
- }
- else if (mod_age - ac_age < minimum_age // early bird, or completely unused
+ } else if (mod_age < ac_age) {
+ cout << " parole";
+ } else if (mod_age - ac_age < minimum_age // early bird, or completely unused
&& mod_age > probation) { // did not diligently resubmit
cout << " disprobation";
if (mod_age != ac_age) cout << "!";
@@ -182,7 +184,7 @@ void whatsit::update(const string msg, const timeval new_mod,
timeval pen_mod(new_mod);
if (penalty) {
pen_mod = now;
- pen_mod.tv_sec += penalty;
+ pen_mod.tv_sec += penalty;
}
timeval upd[2] = {
// beware: access illogically comes *before* modification here:
@@ -193,6 +195,8 @@ void whatsit::update(const string msg, const timeval new_mod,
}
int main(int _argc, char** _argv){
+ progname = *_argv;
+ mypid = getpid();
int argc(_argc);
char** argv(_argv);
const string dirname("/var/qmail/greylist");
@@ -206,7 +210,8 @@ int main(int _argc, char** _argv){
scanmode++;
} else if (prefix(arg, "-copy")) {
copies++;
- } else if (prefix(arg, "-penalize")) {
+ } else if (prefix(arg, "-penalize")
+ || prefix(arg, "-penalty")) {
if (!argc){
cerr << "Option '" << arg << "' requires an argument" << endl;
exeunt(ex_syserr);
@@ -228,18 +233,24 @@ int main(int _argc, char** _argv){
int whatsit::doit(const int penalty){
char* ipvar = getenv("TCPREMOTEIP");
if (!ipvar) {
- cerr << progname << ": TCPREMOTEIP not set???" << endl;
+ cerr << progname
+ << "[" << mypid << "] "
+ << " TCPREMOTEIP not set???" << endl;
+ // should never happen
+ // although you can make it happen using a weird test-harness
exeunt(ex_syserr);
}
ipbase = ipvar;
char* hostvar = getenv("TCPREMOTEHOST");
if (!hostvar) {
- cerr << progname
- << ": from " << ipbase
- << " ... TCPREMOTEHOST not set???" << endl;
+ cerr << progname
+ << "[" << mypid << "] "
+ << " from " << ipbase
+ << " ... TCPREMOTEHOST not set???" << endl;
exeunt(ex_spam);
+ } else {
+ hostname = hostvar;
}
- hostname = hostvar;
// see if our directory exists:
struct stat dirstat;
@@ -289,6 +300,10 @@ int whatsit::doit(const int penalty){
update("penalty box", mod_orig, now, penalty);
exeunt(ex_spam);
}
+ if (mod_age < ac_age){
+ update("paroled spammer", now, now, penalty);
+ exeunt(ex_greylisting);
+ }
if (mod_age < minimum_age) {
update("early bird", mod_orig, now, penalty);
exeunt(ex_greylisting);
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);
}
}
diff --git a/tools/hi-test2.conf b/tools/hi-test2.conf
index e8e4390..51d0361 100644
--- a/tools/hi-test2.conf
+++ b/tools/hi-test2.conf
@@ -1,3 +1,6 @@
-stub hi-test x0 -snooze 10
+stub hi-test x0 -snooze 2
+stub hi-test x0 -snooze 1 -exit 0
stub greylist
-qq hi-test x1 -snooze 1 -exit 3
+qq hi-test x1 -snooze 3 -exit 3
+
+postspam /bin/echo post spam !