#include /* for exit(), getenv() */ #include #include /* for setw */ #include #include /* for stat(), getaddrinfo() */ #include /* for stat() */ #include /* for stat() */ #include /* for perror */ #include /* for ENOENT */ #include /* for ofstream() */ #include /* for creat() */ #include /* for gettimeofday() */ #include /* for stringstream */ #include /* for kill(), SIGUSR1 */ // requires apt-get install libboost-filesystem-dev: #include #include /* for getaddrinfo() */ #include /* for getaddrinfo() */ #include /* for memset() */ #include /* for inet_ntop() */ #include "libltgrey.h" #include "utils.h" #include "qq_exit_codes.h" using namespace std; #if 0 void exeunt(const int sts){ if (sts == ex_good) exit(sts); #ifndef PENALIZE_SPAMMERS if (sts == ex_penaltybox) exit(sts); #endif #ifndef KILL_GROUP exit(sts); #endif const char* foo = getenv("HI_Q_GROUP"); if (!foo) exit(sts); // No point in signalling ourself: sighandler_t rslt = signal(SIGUSR1, SIG_IGN); if (rslt == SIG_ERR) { cerr << "error setting signal" << endl; } int k = kill(-atoi(foo), SIGUSR1); if (k) { cerr << "kill failed on group " << atoi(foo) << " ... "; perror(0); } exit(sts); } #endif // constructor whatsit::whatsit(const std::string name, const std::string _parent_dir) : parent_dir(_parent_dir), progname(name), mypid(getpid()), verbosity(0) { // expand the codes to make some names: # define foo(name) decode_40[name] = #name; box_state_macro # undef foo } void whatsit::dump(const string var){ char* str = getenv(var.c_str()); cerr << progname << "[" << mypid << "] " << var; if (str) cerr << " is set to '" << str << "'" << endl; else cerr << " is not set." << endl; } int whatsit::setup(){ stringstream foo; foo << basename(progname) << suffix << "[" << mypid << "]"; progid = foo.str(); return 0; } #if 1 void whatsit::update(const string msg, const timeval new_mod, const timeval new_ac, const int penalty, const int stain){ } #else void whatsit::update(const string msg, const timeval new_mod, const timeval new_ac, const int penalty, const int stain){ if (verbosity){ if (penalty || stain || verbosity>1) cerr << progid << ": "; if (penalty) cerr << " penalty " << penalty; if (stain) cerr << " stain " << stain; if (verbosity > 1) { if (penalty || stain) cerr << "+"; // separation, punctuation cerr << msg << ": " << ipbase; if (hostname.length()) cerr << " " << hostname; cerr << " mod_age: " << time_out(mod_age) << " ac_age: " << time_out(ac_age); } cerr << endl; } timeval pen_mod(new_mod); timeval stain_ac(new_ac); if (penalty) { pen_mod = now; pen_mod.tv_sec += penalty; } if (stain) { stain_ac = now; stain_ac.tv_sec -= stain; } timeval upd[2] = { // beware: access illogically comes *before* modification here: stain_ac, pen_mod }; if (utimes(ipname.c_str(), upd)) cerr << "oops" << endl; } #endif int whatsit::maybe_mkdir(const string subdir, const string msg){ // see if our directory exists: string mydir = parent_dir; if (subdir != ".") { mydir += "/" + subdir; } struct stat dirstat; int rslt = stat(mydir.c_str(), &dirstat); if (rslt != 0){ if (errno != ENOENT) { cerr << progid << " stat failed for " << msg << " '" << mydir << "' : "; perror(0); return(ex_syserr); } /* else it simply does not exist */ rslt = mkdir(mydir.c_str(), 0755); if (rslt != 0) { cerr << progid << " uid " << getuid() << " euid " << geteuid() << " mkdir failed for " << msg << " '" << mydir << "' : "; perror(0); return(ex_syserr); } } // here if stat succeeded return 0; } typedef vector VU; class VUx{ public: VU addr; sa_family_t fam; string str(); }; string VUx::str(){ char msgbuf[INET6_ADDRSTRLEN]; const char* rslt = inet_ntop(fam, &addr[0], msgbuf, sizeof(msgbuf)); if (!rslt) rslt = ""; return rslt; } VUx parse_sockaddr(const sockaddr* ai_addr) { void* numericAddress; VUx rslt; int addrsize; rslt.addr = VU(0); rslt.fam = ((sockaddr *)ai_addr)->sa_family; switch (rslt.fam) { case AF_INET: numericAddress = &(((sockaddr_in *)ai_addr)->sin_addr.s_addr); addrsize = sizeof(in_addr); break; case AF_INET6: numericAddress = &(((sockaddr_in6 *)ai_addr)->sin6_addr.s6_addr); addrsize = sizeof(in6_addr); break; default: cerr << "?Unknown address family " << rslt.fam << endl; return rslt; } unsigned char* foo = (unsigned char*) numericAddress; rslt.addr = VU(foo, foo+addrsize); return rslt; } int diff(const VU aaa, const VU bbb){ if(aaa.size() != bbb.size()) return 1; for (unsigned int ii=0; ii < aaa.size(); ii++){ if (aaa[ii] != bbb[ii]) return 1; } return 0; } int whatsit::check_dns(const char* ipvar, const char* namevar){ if (!ipvar) { cerr << progid << " check_dns: no addr specified." << endl; return ex_syserr; } string addr("()"), host("()"); vector checked; int rslt = check_dns_sub(ipvar, namevar, addr, host, checked); int sts = rslt; #ifndef badDNSfatal sts = 0; // demote badDNS to just a warning #endif if (rslt || verbosity) { cerr << progid; if (rslt && !sts) cerr << " (warning)"; if (rslt) cerr << " DNS inconsistency: "; else cerr << " DNS OK: "; cerr << addr << " --> " << host << " ==>"; if (!checked.size()) cerr << " ()"; else for (vector::const_iterator chk = checked.begin(); chk != checked.end(); chk++) cerr << " " << *chk; cerr << endl; } return sts; } int whatsit::check_dns_sub(const char* ipvar, const char* namevar, string &addr, string &host, vector &checked){ #ifdef badDNSfatal int ex_dns_fail = ex_syserr; #else int ex_dns_fail = ex_badDNS; #endif struct addrinfo *result; struct addrinfo *ipresult; struct addrinfo *res; addrinfo hints; int error; memset(&hints, 0, sizeof(struct addrinfo)); #if 1 // restrict to TCP only; otherwise we get N records per address hints.ai_protocol = IPPROTO_TCP; #endif // convert address-as-string to address-as-bits. // also get information about family error = getaddrinfo(ipvar, NULL, &hints, &ipresult); if (error == EAI_NONAME) return ex_badDNS; if (error) { // some unexpected error cerr << progid << " odd error " << error << " in getaddrinfo for " << ipvar << " : " << gai_strerror(error) << endl; return ex_dns_fail; } if (!ipresult) { cerr << "should never happen (addr with no addrs?)" << endl; return ex_dns_fail; } // reconvert from bits to string + family info VUx ipAddr = parse_sockaddr(ipresult->ai_addr); addr = ipAddr.str(); if (namevar) { // inverse lookup already done host = namevar; } else { // namevar not specified; do inverse lookup on our own char hostname[NI_MAXHOST] = ""; char service[NI_MAXHOST] = ""; #ifdef convert_bits_to_string const char* rslt = inet_ntop(ipAddr.fam, &ipAddr.addr[0], msgbuf, sizeof(msgbuf)); if (rslt) fprintf(stdout, "%s addrsize: %2d --> ", msgbuf, addrsize); #endif error = getnameinfo(ipresult->ai_addr, ipresult->ai_addrlen, hostname, NI_MAXHOST, service, NI_MAXHOST, 0); if (error != 0) { cerr << progid << " reverse DNS lookup failed: " << gai_strerror(error) << endl; return (ex_badDNS); } namevar = hostname; // a char*, used below host = hostname; // a string, returned to caller } error = getaddrinfo(namevar, NULL, &hints, &result); if (error == EAI_NONAME) return ex_badDNS; if (error) { cerr << progid << " getaddrinfo for " << ipvar << " error " << error << " i.e. " << gai_strerror(error) << endl; return ex_dns_fail; } // loop over all returned results and check for a match. for (res = result; res != NULL; res = res->ai_next){ VUx hostAddr = parse_sockaddr(res->ai_addr); checked.push_back(hostAddr.str()); if (!diff(hostAddr.addr, ipAddr.addr)) { ///// cerr << "match! " << ipAddr.addr.size() << endl; goto done; } } return ex_badDNS; done: return 0; } box_state whatsit::get_40(const string mid){ timeval junk[2]; return get_box(box_40, mid, junk); } box_state whatsit::get_rep(const string mid){ timeval junk[2]; return get_box(box_rep, mid, junk); } box_state whatsit::get_box(const boxer mybox, const string mid, timeval times[2]){ timeval now; gettimeofday(&now, NULL); times[0] = times[1] = now; // in case of early return string fname = parent_dir + "/" + mybox.dir + "/" + mybox.prefix + purify(mid); //xxx cerr << "get_box .... " << fname << endl; struct stat mid_stat; int rslt = stat(fname.c_str(), &mid_stat); if (rslt != 0) { if (errno == ENOENT) return unseen; cerr << progid << " stat: unexpected failure for '" << fname << "' : "; perror(0); return fail; } times[upd_ac ].tv_sec = mid_stat.st_atime; times[upd_ac ].tv_usec = 0; times[upd_mod].tv_sec = mid_stat.st_mtime; times[upd_mod].tv_usec = 0; int mod_age = now.tv_sec - mid_stat.st_mtime; // int ac_age = now.tv_sec - mid_stat.st_atime; if (mod_age < mybox.min) return green; if (mod_age < mybox.max) return ripe; return spoiled; } int whatsit::set_40(const string mid, const int shift){ return set_box(box_40, mid, shift); } int whatsit::set_rep(const string mid, const int shift){ return set_box(box_rep, mid, shift); } int whatsit::set_box(const boxer box, const string ID, const int shift){ string fname = parent_dir + "/" + box.dir + "/" + box.prefix + purify(ID); //xxx cerr << ".... " << fname << endl; int fd = creat(fname.c_str(), 0644); if (fd < 0){ cerr << progid << ": create failed for '" << fname << "' : "; perror(0); } if (!shift) { close(fd); return 0; } struct stat mid_stat; int rslt = fstat(fd, &mid_stat); close(fd); if (rslt != 0) { cerr << progid << " fstat: unexpected failure for '" << fname << "' : "; perror(0); return ex_syserr; } timeval now; gettimeofday(&now, NULL); timeval upd[2] = { now, // access now // modification }; upd[upd_mod].tv_sec += shift; if (utimes(fname.c_str(), upd)){ cerr << progid << " utimes failed!" << endl; return ex_syserr; } return 0; } void whatsit::scan_box(const boxer mybox, const int copies){ timeval now; gettimeofday(&now, NULL); using namespace boost::filesystem; string mydir = parent_dir + "/" + mybox.dir; cerr << "scan_box: scanning " << mydir << " prefix: " << mybox.prefix << endl; if (is_directory(mydir)) { for (directory_iterator itr(mydir); itr!=directory_iterator(); ++itr) { string basename = itr->path().filename().string(); if (is_regular_file(itr->status())) { if (prefix(mybox.prefix, basename)) { for (int ii = 0; ii < copies; ii++) cout << setw(20) << left << basename; // display filename only timeval times[2]; box_state rslt = get_box(mybox, basename.substr(mybox.prefix.length()), times); int mod_age = now.tv_sec - times[upd_mod].tv_sec; int ac_age = now.tv_sec - times[upd_ac ].tv_sec; cout << setw(10) << right << time_out(mod_age) << " " << setw(10) << right << time_out(ac_age); cout << " " << decode_40[rslt]; cout << endl; } else { /* filename does not begin with mybox.prefix */ } } } } else { // starting point is not a directory: cout << (exists(mydir) ? "Found: " : "Not found: ") << mydir << '\n'; } }