diff --git a/CHANGELOG b/CHANGELOG index 7b389160f..b45d02cf5 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -13,8 +13,23 @@ o Updated the Windows installer to give an option checkbox for performing the Nmap performance registry changes. The default is to do so. Thanks to Adam Vartanian (flooey(a)gmail.com) for the patch. +o Nmap now provides progress statistics in the XML output in verbose + mode. Here are some examples of the format (etc is "estimated time + until completion) and times are in unix time_t (seconds since 1970) format: + + + + + + Thanks to Adam Vartanian (flooey(a)gmail.com) for the patch. + o Applied several code cleanup patches from Marek Majkowski. +o Added --release-memory option, which causes Nmap to release all + accessible memory buffers before quitting (rather than let the OS do + it). This is only useful for debugging memory leaks. + o Fixed a bug related to bogus completion time estimates when you request an estimate (through runtime interaction) right when Nmap is starting.a subsystem (such as a port scan or version detection). diff --git a/NmapOps.h b/NmapOps.h index 694ab0a9d..be969c275 100644 --- a/NmapOps.h +++ b/NmapOps.h @@ -300,6 +300,7 @@ class NmapOps { stype current_scantype; bool noninteractive; + bool release_memory; /* suggest to release memory before quitting. used to find memory leaks. */ private: int max_rtt_timeout; int min_rtt_timeout; diff --git a/charpool.cc b/charpool.cc index be470c98a..65b286ab4 100644 --- a/charpool.cc +++ b/charpool.cc @@ -122,6 +122,16 @@ static int cp_init(void) { return 0; } +void cp_free(void) { + int ccp; + for(ccp=0; ccp <= currentcharpool; ccp++) + if(charpool[ccp]){ + free(charpool[ccp]); + charpool[ccp] = NULL; + } + currentcharpool = 0; +} + static inline void cp_grow(void) { /* Doh! We've got to make room */ if (++currentcharpool > 15) { diff --git a/charpool.h b/charpool.h index 10344fb0b..910f1330b 100644 --- a/charpool.h +++ b/charpool.h @@ -107,4 +107,7 @@ void *cp_alloc(int sz); char *cp_strdup(const char *src); + +void cp_free(void); + #endif diff --git a/docs/nmap.dtd b/docs/nmap.dtd index 42b4e51f4..91eb9d7e6 100644 --- a/docs/nmap.dtd +++ b/docs/nmap.dtd @@ -45,6 +45,7 @@ + @@ -71,7 +72,7 @@ - + + + + + + + + + + + + + @@ -202,6 +227,11 @@ fingerprint CDATA #REQUIRED > + + + NameIP()); + ScanProgressMeter SPM(scanname); if (numports == 0) return; /* nothing to scan for */ if (!proxyName) fatal("Idlescan requires a proxy host"); @@ -988,9 +991,6 @@ void idle_scan(Target *target, u16 *portarray, int numports, initialize_idleproxy(&proxy, proxyName, target->v4hostip()); } - if (o.debugging || o.verbose) { - log_write(LOG_STDOUT, "Initiating Idlescan against %s\n", target->NameIP()); - } starttime = time(NULL); /* If we don't have timing infoz for the new target, we'll use values @@ -1019,11 +1019,9 @@ void idle_scan(Target *target, u16 *portarray, int numports, } - if (o.verbose) { - long timediff = time(NULL) - starttime; - log_write(LOG_STDOUT, "The Idlescan took %ld %s to scan %d ports.\n", - timediff, (timediff == 1)? "second" : "seconds", numports); - } + char additional_info[14]; + snprintf(additional_info, sizeof(additional_info), "%d ports", numports); + SPM.endTask(NULL, additional_info); /* Now we go through the ports which were not determined were scanned but not determined to be open, and add them in the "closed" state */ diff --git a/nmap.cc b/nmap.cc index f217989fc..c53ed36b1 100644 --- a/nmap.cc +++ b/nmap.cc @@ -109,6 +109,7 @@ #include "NmapOps.h" #include "MACLookup.h" #include "nmap_tty.h" +#include "nmap_dns.h" #ifdef WIN32 #include "winfix.h" #endif @@ -491,6 +492,8 @@ int nmap_main(int argc, char *argv[]) { {"debug", optional_argument, 0, 'd'}, {"help", no_argument, 0, 'h'}, {"iflist", no_argument, 0, 0}, + {"release_memory", no_argument, 0, 0}, + {"release-memory", no_argument, 0, 0}, {"max_parallelism", required_argument, 0, 'M'}, {"max-parallelism", required_argument, 0, 'M'}, {"min_parallelism", required_argument, 0, 0}, @@ -640,6 +643,8 @@ int nmap_main(int argc, char *argv[]) { } } else if (strcmp(long_options[option_index].name, "iflist") == 0 ) { iflist = true; + } else if (strcmp(long_options[option_index].name, "release-memory") == 0 ) { + o.release_memory = true; } else if (optcmp(long_options[option_index].name, "min-parallelism") == 0 ) { o.min_parallelism = atoi(optarg); if (o.min_parallelism < 1) fatal("Argument to --min-parallelism must be at least 1!"); @@ -1626,23 +1631,35 @@ int nmap_main(int argc, char *argv[]) { printfinaloutput(); - if (ports) { - free(ports->tcp_ports); - free(ports->udp_ports); - free(ports->prots); - free(ports); - } + free_scan_lists(ports); eth_close_cached(); - /* Free fake argv */ - for(i=0; i < argc; i++) - free(fakeargv[i]); - free(fakeargv); + if(o.release_memory || o.interactivemode) { + /* Free fake argv */ + for(i=0; i < argc; i++) + free(fakeargv[i]); + free(fakeargv); + nmap_free_mem(); + } return 0; } +// Free some global memory allocations. +// This is used for detecting memory leaks. +void nmap_free_mem() { + PortList::freePortMap(); + cp_free(); + free_dns_servers(); + free_etchosts(); + if(o.reference_FPs){ + free_fingerprint_file(o.reference_FPs); + o.reference_FPs = NULL; + } + AllProbes::service_scan_free(); + +} /* Reads in a (normal or machine format) Nmap log file and gathers enough state to allow Nmap to continue where it left off. The important things @@ -1927,6 +1944,14 @@ struct scan_lists *getpts(char *origexpr) { return ports; } +void free_scan_lists(struct scan_lists *ports) { + if (ports) { + free(ports->tcp_ports); + free(ports->udp_ports); + free(ports->prots); + free(ports); + } +} void printinteractiveusage() { printf( diff --git a/nmap.h b/nmap.h index 75267cc7f..a11f874b8 100644 --- a/nmap.h +++ b/nmap.h @@ -452,6 +452,7 @@ int ftp_anon_connect(struct ftpinfo *ftp); /* port manipulators */ void getprobepts(char *expr); struct scan_lists *getpts(char *expr); /* someone stole the name getports()! */ +void free_scan_lists(struct scan_lists *ports); int getidentinfoz(struct in_addr target, u16 localport, u16 remoteport, char *owner, int ownersz); @@ -467,6 +468,8 @@ int listen_icmp(int icmpsock, unsigned short outports[], /* Renamed main so that interactive mode could preprocess when neccessary */ int nmap_main(int argc, char *argv[]); +void nmap_free_mem(); + /* general helper functions */ int parse_targets(struct targets *targets, char *h); char *statenum2str(int state); diff --git a/nmap_dns.cc b/nmap_dns.cc index 49e72a7fb..70438f68e 100644 --- a/nmap_dns.cc +++ b/nmap_dns.cc @@ -816,6 +816,21 @@ static void add_dns_server(char *ipaddrs) { } +void free_dns_servers() { + std::list::iterator servI; + dns_server *tpserv; + + for(servI = servs.begin(); servI != servs.end();servI++){ + tpserv = *servI; + if(tpserv){ + if(tpserv->hostname) + free(tpserv->hostname); + delete tpserv; + } + } + servs.clear(); +} + // Creates a new nsi for each DNS server void connect_dns_servers() { @@ -969,6 +984,23 @@ static void parse_etchosts(char *fname) { fclose(fp); } +void free_etchosts() { + host_elem *he; + std::list::iterator hi; + int i; + + for(i=0; i < HASH_TABLE_SIZE; i++){ + for(hi = etchosts[i].begin(); hi != etchosts[i].end(); hi++) { + he = *hi; + if(he) { + free(he->name); + delete he; + } + } + etchosts[i].clear(); + } +} + static char *lookup_etchosts(u32 ip) { std::list::iterator hostI; @@ -1058,6 +1090,7 @@ static void nmap_mass_rdns_core(Target **targets, int num_targets) { } } + SPM->endTask(NULL, NULL); delete SPM; return; @@ -1137,6 +1170,8 @@ static void nmap_mass_rdns_core(Target **targets, int num_targets) { nsock_loop(dnspool, timeout); } + SPM->endTask(NULL, NULL); + delete SPM; close_dns_servers(); @@ -1172,6 +1207,7 @@ static void nmap_mass_rdns_core(Target **targets, int num_targets) { } + SPM->endTask(NULL, NULL); delete SPM; cname_reqs.clear(); diff --git a/nmap_dns.h b/nmap_dns.h index 6b0bda397..3a5d351e8 100644 --- a/nmap_dns.h +++ b/nmap_dns.h @@ -98,3 +98,7 @@ #include "Target.h" void nmap_mass_rdns(Target ** targets, int num_targets); +void free_dns_servers(); + +void free_etchosts(); + diff --git a/osscan.cc b/osscan.cc index 541f82fd5..92e4d761e 100644 --- a/osscan.cc +++ b/osscan.cc @@ -1925,6 +1925,33 @@ FingerPrint *parse_single_fingerprint(char *fprint_orig) { return FP; } + +void free_fingerprint_file(FingerPrint **FPs) { + FingerPrint **current; + FingerPrint *c, *d; + struct AVal *avc; + struct AVal *avd; + + for(current = FPs; *current != NULL; current++){ + for(c = *current; c; c=d){ + d = c->next; + if(c->name) + free((void*)c->name); //strdup + if(c->results){ + for(avc = c->results; avc; avc = avd) { + avd = avc->next; + if(avc->attribute) + free(avc->attribute); + } + free(c->results); + } + free(c); + } + } + free(FPs); +} + + FingerPrint **parse_fingerprint_file(char *fname) { FingerPrint **FPs; FingerPrint *current; diff --git a/osscan.h b/osscan.h index 6c159a843..001b2d437 100644 --- a/osscan.h +++ b/osscan.h @@ -131,6 +131,8 @@ FingerPrint *parse_single_fingerprint(char *fprint_orig); FingerPrint **parse_fingerprint_file(char *fname); FingerPrint **parse_fingerprint_reference_file(char *dbname); +void free_fingerprint_file(FingerPrint **FPs); + /* Compares 2 fingerprints -- a referenceFP (can have expression attributes) with an observed fingerprint (no expressions). If verbose is nonzero, differences will be printed. The comparison diff --git a/portlist.cc b/portlist.cc index cc1a5952c..ffdb0d705 100644 --- a/portlist.cc +++ b/portlist.cc @@ -582,6 +582,17 @@ void PortList::setPortEntry(u16 portno, u8 protocol, Port *port) { port_list[proto][mapped_pno] = port; } +/* Just free memory used by PortList::port_map[]. Should be done somewhere + * before closing nmap. */ +void PortList::freePortMap(){ + int proto; + for(proto=0; proto < PORTLIST_PROTO_MAX; proto++) + if(port_map[proto]){ + free(port_map[proto]); + port_map[proto] = NULL; + } +} + u16 *PortList::port_map[PORTLIST_PROTO_MAX]; int PortList::port_list_count[PORTLIST_PROTO_MAX]; diff --git a/portlist.h b/portlist.h index b5425bc92..321d3ffba 100644 --- a/portlist.h +++ b/portlist.h @@ -268,6 +268,9 @@ class PortList { /* Set ports that will be scanned for each protocol. This function * must be called before any PortList object will be created. */ static void initializePortMap(int protocol, u16 *ports, int portcount); + /* Free memory used by port_map. It should be done somewhere before quitting*/ + static void PortList::freePortMap(); + /* Add a new port to this list. If the state has changed, it is OK to call this function to effect the change */ int addPort(u16 portno, u8 protocol, char *owner, int state); diff --git a/scan_engine.cc b/scan_engine.cc index d588d1fe8..eb16c29fd 100644 --- a/scan_engine.cc +++ b/scan_engine.cc @@ -3358,7 +3358,6 @@ static void startTimeOutClocks(vector &Targets) { void ultra_scan(vector &Targets, struct scan_lists *ports, stype scantype) { UltraScanInfo *USI = NULL; - time_t starttime; o.current_scantype = scantype; if (Targets.size() == 0) { @@ -3377,14 +3376,11 @@ void ultra_scan(vector &Targets, struct scan_lists *ports, if (o.verbose) { char targetstr[128]; - struct tm *tm; bool plural = (Targets.size() != 1); if (!plural) { (*(Targets.begin()))->NameIP(targetstr, sizeof(targetstr)); } else snprintf(targetstr, sizeof(targetstr), "%d hosts", (int) Targets.size()); - starttime = USI->now.tv_sec; - tm = localtime(&starttime); - log_write(LOG_STDOUT, "Initiating %s against %s [%d port%s%s] at %02d:%02d\n", scantype2str(scantype), targetstr, USI->gstats->numprobes, (USI->gstats->numprobes != 1)? "s" : "", plural? "/host" : "", tm->tm_hour, tm->tm_min); + log_write(LOG_STDOUT, "Scanning %s [%d port%s%s]\n", targetstr, USI->gstats->numprobes, (USI->gstats->numprobes != 1)? "s" : "", plural? "/host" : ""); } begin_sniffer(USI, Targets); @@ -3433,17 +3429,15 @@ void ultra_scan(vector &Targets, struct scan_lists *ports, } if (o.verbose) { + char additional_info[128]; if (USI->gstats->num_hosts_timedout == 0) - log_write(LOG_STDOUT, "The %s took %.2fs to scan %lu total %s.\n", - scantype2str(scantype), - TIMEVAL_MSEC_SUBTRACT(USI->now, USI->SPM->begin) / 1000.0, + snprintf(additional_info, sizeof(additional_info), "%lu total %s", (unsigned long) USI->gstats->numprobes * Targets.size(), (scantype == PING_SCAN_ARP)? "hosts" : "ports"); - else log_write(LOG_STDOUT, "Finished %s in %.2fs, but %d %s timed out.\n", - scantype2str(scantype), - TIMEVAL_MSEC_SUBTRACT(USI->now, USI->SPM->begin) / 1000.0, + else snprintf(additional_info, sizeof(additional_info), "%d %s timed out", USI->gstats->num_hosts_timedout, (USI->gstats->num_hosts_timedout == 1)? "host" : "hosts"); + USI->SPM->endTask(NULL, additional_info); } delete USI; USI = NULL; @@ -3629,7 +3623,6 @@ void pos_scan(Target *target, u16 *portarray, int numports, stype scantype) { struct scanstats ss; int senddelay = 0; int rpcportsscanned = 0; - bool printedinitialmsg = false; int tries = 0; time_t starttime; struct timeval starttm; @@ -3643,6 +3636,8 @@ void pos_scan(Target *target, u16 *portarray, int numports, stype scantype) { struct serviceDeductions sd; bool doingOpenFiltered = false; + ScanProgressMeter *SPM = NULL; + if (target->timedOut(NULL)) return; @@ -3759,11 +3754,10 @@ void pos_scan(Target *target, u16 *portarray, int numports, stype scantype) { // This initial message is way down here because we don't want to print it if // no RPC ports need scanning. - if (o.verbose && !printedinitialmsg) { - struct tm *tm = localtime(&starttime); - assert(tm); - log_write(LOG_STDOUT, "Initiating %s against %s at %02d:%02d\n", scantype2str(scantype), target->NameIP(hostname, sizeof(hostname)), tm->tm_hour, tm->tm_min); - printedinitialmsg = true; + if (!SPM) { + char scanname[32]; + snprintf(scanname, sizeof(scanname), "%s against %s", scantype2str(scantype), target->NameIP()); + SPM = new ScanProgressMeter(scanname); } while(pil.testinglist != NULL) /* While we have live queries or more ports to scan */ @@ -3919,13 +3913,18 @@ void pos_scan(Target *target, u16 *portarray, int numports, stype scantype) { } numports = rpcportsscanned; - if (o.verbose && numports > 0) { - gettimeofday(&now, NULL); - log_write(LOG_STDOUT, "The %s took %.2fs to scan %d ports on %s.\n", scantype2str(scantype), TIMEVAL_MSEC_SUBTRACT(now, starttm) / 1000.0, numports, target->NameIP()); + if (SPM && o.verbose && (numports > 0)) { + char scannedportsstr[14]; + snprintf(scannedportsstr, sizeof(scannedportsstr), "%d %s", numports, (numports > 1)? "ports" : "port"); + SPM->endTask(NULL, scannedportsstr); } posscan_timedout: target->stopTimeOutClock(NULL); free(scan); close_rpc_query_sockets(); + if (SPM) { + delete SPM; + SPM = NULL; + } return; } diff --git a/service_scan.cc b/service_scan.cc index c74f38679..8cef6a1ac 100644 --- a/service_scan.cc +++ b/service_scan.cc @@ -305,6 +305,7 @@ void ServiceProbeMatch::InitMatch(const char *matchtext, int lineno) { const char *pcre_errptr = NULL; int pcre_erroffset = 0; unsigned int tmpbuflen = 0; + char **curr_tmp = NULL; if (isInitialized) fatal("Sorry ... ServiceProbeMatch::InitMatch does not yet support reinitializion"); if (!matchtext || !*matchtext) @@ -414,13 +415,27 @@ void ServiceProbeMatch::InitMatch(const char *matchtext, int lineno) { tmptemplate[tmpbuflen] = '\0'; } - if (modechar == 'p') product_template = tmptemplate; - else if (modechar == 'v') version_template = tmptemplate; - else if (modechar == 'i') info_template = tmptemplate; - else if (modechar == 'h') hostname_template = tmptemplate; - else if (modechar == 'o') ostype_template = tmptemplate; - else if (modechar == 'd') devicetype_template = tmptemplate; - else fatal("ServiceProbeMatch::InitMatch: Unknown template specifier '%c' on line %d of nmap-service-probes", modechar, lineno); + switch(modechar){ + case 'p': curr_tmp = &product_template; break; + case 'v': curr_tmp = &version_template; break; + case 'i': curr_tmp = &info_template; break; + case 'h': curr_tmp = &hostname_template; break; + case 'o': curr_tmp = &ostype_template; break; + case 'd': curr_tmp = &devicetype_template; break; + default: + fatal("ServiceProbeMatch::InitMatch: Unknown template specifier '%c' on line %d of nmap-service-probes", modechar, lineno); + } + if(*curr_tmp){ + if(o.debugging) + error("WARNING: Template \"%c/%s/\" replaced with \"%c/%s/\" on line %d of nmap-service-probes", + modechar, + *curr_tmp, + modechar, + tmptemplate, + lineno); + free(*curr_tmp); + } + *curr_tmp = tmptemplate; matchtext = p + 1; } @@ -1133,17 +1148,26 @@ void parse_nmap_service_probes(AllProbes *AP) { parse_nmap_service_probe_file(AP, filename); } -static AllProbes *service_scan_init(void) +AllProbes *AllProbes::global_AP; +AllProbes *AllProbes::service_scan_init(void) { - static AllProbes *AP; + if(global_AP) + return global_AP; + global_AP = new AllProbes(); + parse_nmap_service_probes(global_AP); - if (AP) return AP; - AP = new AllProbes(); - parse_nmap_service_probes(AP); - - return AP; + return global_AP; } +void AllProbes::service_scan_free(void) +{ + if(global_AP){ + delete global_AP; + global_AP = NULL; + } +} + + // If the buf (of length buflen) matches one of the regexes in this // ServiceProbe, returns the details of the match (service name, // version number if applicable, and whether this is a "soft" match. @@ -1175,8 +1199,12 @@ AllProbes::~AllProbes() { vector::iterator vi; // Delete all the ServiceProbe's inside the probes vector - for(vi = probes.begin(); vi != probes.end(); vi++) + for(vi = probes.begin(); vi != probes.end(); vi++) { delete *vi; + } + if(nullProbe) + delete nullProbe; + free_scan_lists(excludedports); } // Tries to find the probe in this AllProbes class which have the @@ -2338,7 +2366,7 @@ int service_scan(vector &Targets) { if (Targets.size() == 0) return 1; - AP = service_scan_init(); + AP = AllProbes::service_scan_init(); // Now I convert the targets into a new ServiceGroup @@ -2361,16 +2389,15 @@ int service_scan(vector &Targets) { starttime = time(NULL); if (o.verbose) { char targetstr[128]; - struct tm *tm = localtime(&starttime); bool plural = (Targets.size() != 1); if (!plural) { (*(Targets.begin()))->NameIP(targetstr, sizeof(targetstr)); } else snprintf(targetstr, sizeof(targetstr), "%u hosts", (unsigned) Targets.size()); - log_write(LOG_STDOUT, "Initiating service scan against %u %s on %s at %02d:%02d\n", + log_write(LOG_STDOUT, "Scanning %u %s on %s\n", (unsigned) SG->services_remaining.size(), (SG->services_remaining.size() == 1)? "service" : "services", - targetstr, tm->tm_hour, tm->tm_min); + targetstr); } // Lets create a nsock pool for managing all the concurrent probes @@ -2399,18 +2426,16 @@ int service_scan(vector &Targets) { nsp_delete(nsp); if (o.verbose) { - gettimeofday(&now, NULL); + char additional_info[128]; if (SG->num_hosts_timedout == 0) - log_write(LOG_STDOUT, "The service scan took %.2fs to scan %u %s on %u %s.\n", - TIMEVAL_MSEC_SUBTRACT(now, starttv) / 1000.0, + snprintf(additional_info, sizeof(additional_info), "%u %s on %u %s", (unsigned) SG->services_finished.size(), (SG->services_finished.size() == 1)? "service" : "services", (unsigned) Targets.size(), (Targets.size() == 1)? "host" : "hosts"); - else log_write(LOG_STDOUT, - "Finished service scan in %.2fs, but %d %s timed out.\n", - TIMEVAL_MSEC_SUBTRACT(now, starttv) / 1000.0, + else snprintf(additional_info, sizeof(additional_info), "%u %s timed out", SG->num_hosts_timedout, (SG->num_hosts_timedout == 1)? "host" : "hosts"); + SG->SPM->endTask(NULL, additional_info); } // Yeah - done with the service scan. Now I go through the results diff --git a/service_scan.h b/service_scan.h index c4db6d355..a186e143f 100644 --- a/service_scan.h +++ b/service_scan.h @@ -332,6 +332,11 @@ public: int isExcluded(unsigned short port, int proto); struct scan_lists *excludedports; + + static AllProbes *service_scan_init(void); + static void service_scan_free(void); +protected: + static AllProbes *global_AP; }; /********************** PROTOTYPES ***********************************/ diff --git a/targets.cc b/targets.cc index 8a285816f..ede469e91 100644 --- a/targets.cc +++ b/targets.cc @@ -794,7 +794,6 @@ static int get_ping_results(int sd, pcap_t *pd, Target *hostbatch[], return 0; } - static int sendconnecttcpquery(Target *hostbatch[], struct tcpqueryinfo *tqi, Target *target, int probe_port_num, u16 seq, struct timeval *time, struct pingtune *pt, diff --git a/tcpip.cc b/tcpip.cc index 08781d2ae..e68acb171 100644 --- a/tcpip.cc +++ b/tcpip.cc @@ -2118,6 +2118,7 @@ void set_pcap_filter(const char *device, fatal("Error compiling our pcap filter: %s\n", pcap_geterr(pd)); if (pcap_setfilter(pd, &fcode) < 0 ) fatal("Failed to set the pcap filter: %s\n", pcap_geterr(pd)); + pcap_freecode(&fcode); } /* The 'dev' passed in must be at least 32 bytes long */ diff --git a/timing.cc b/timing.cc index 1c42cad91..01779212c 100644 --- a/timing.cc +++ b/timing.cc @@ -243,6 +243,7 @@ ScanProgressMeter::ScanProgressMeter(char *stypestr) { last_print_test = begin; memset(&last_print, 0, sizeof(last_print)); memset(&last_est, 0, sizeof(last_print)); + beginOrEndTask(&begin, NULL, true); } ScanProgressMeter::~ScanProgressMeter() { @@ -382,19 +383,57 @@ bool ScanProgressMeter::printStats(double perc_done, // If we're less than 1% done we probably don't have enough // data for decent timing estimates. Also with perc_done == 0 // these elements will be nonsensical. - if (perc_done < 0.01) + if (perc_done < 0.01) { log_write(LOG_STDOUT, "%s Timing: About %.2f%% done\n", scantypestr, perc_done * 100); - else + log_flush(LOG_STDOUT); + } else { log_write(LOG_STDOUT, "%s Timing: About %.2f%% done; ETC: %02d:%02d (%li:%02li:%02li remaining)\n", scantypestr, perc_done * 100, ltime->tm_hour, ltime->tm_min, sec_left / 3600, (sec_left % 3600) / 60, sec_left % 60); - - log_flush(LOG_STDOUT); + log_write(LOG_XML, "\n", + scantypestr, (unsigned long) now->tv_sec, + perc_done * 100, sec_left, (unsigned long) last_est.tv_sec); + log_flush(LOG_STDOUT|LOG_XML); + } return true; } +/* Indicates that the task is beginning or ending, and that a message should + be generated if appropriate. Returns whether a message was printed. + now may be NULL, if the caller doesn't have the current time handy. + additional_info may be NULL if no additional information is necessary. */ +bool ScanProgressMeter::beginOrEndTask(const struct timeval *now, const char *additional_info, bool beginning) { + struct timeval tvtmp; + struct tm *tm; + time_t tv_sec; + if (!o.verbose) { + return false; + } + if (!now) { + gettimeofday(&tvtmp, NULL); + now = (const struct timeval *) &tvtmp; + } - + tv_sec = now->tv_sec; + tm = localtime(&tv_sec); + if (beginning) { + log_write(LOG_STDOUT, "Initiating %s at %02d:%02d", scantypestr, tm->tm_hour, tm->tm_min); + if (additional_info) { + log_write(LOG_STDOUT, " (%s)", additional_info); + } + log_write(LOG_STDOUT, "\n"); + log_write(LOG_XML, "\n", scantypestr, (unsigned long) now->tv_sec); + } else { + log_write(LOG_STDOUT, "Completed %s at %02d:%02d, %.2fs elapsed", scantypestr, tm->tm_hour, tm->tm_min, TIMEVAL_MSEC_SUBTRACT(*now, begin) / 1000.0); + if (additional_info) { + log_write(LOG_STDOUT, " (%s)", additional_info); + } + log_write(LOG_STDOUT, "\n"); + log_write(LOG_XML, "\n", scantypestr, (unsigned long) now->tv_sec); + } + log_flush(LOG_STDOUT|LOG_XML); + return true; +} diff --git a/timing.h b/timing.h index 58cf0abd2..4ec7421e9 100644 --- a/timing.h +++ b/timing.h @@ -154,12 +154,17 @@ class ScanProgressMeter { /* Prints an estimate of when this scan will complete. */ bool printStats(double perc_done, const struct timeval *now); + /* Prints that this task is complete. */ + bool endTask(const struct timeval *now, const char *additional_info) { return beginOrEndTask(now, additional_info, false); } + struct timeval begin; /* When this ScanProgressMeter was instantiated */ private: struct timeval last_print_test; /* Last time printStatsIfNeccessary was called */ struct timeval last_print; /* The most recent time the ETC was printed */ char *scantypestr; struct timeval last_est; /* The latest PRINTED estimate */ + + bool beginOrEndTask(const struct timeval *now, const char *additional_info, bool beginning); }; #endif /* NMAP_TIMING_H */