• Main Page
  • Related Pages
  • Namespaces
  • Data Structures
  • Files
  • File List
  • Globals

logstats.cc

Go to the documentation of this file.
00001 /** @file
00002 
00003   A brief file description
00004 
00005   @section license License
00006 
00007   Licensed to the Apache Software Foundation (ASF) under one
00008   or more contributor license agreements.  See the NOTICE file
00009   distributed with this work for additional information
00010   regarding copyright ownership.  The ASF licenses this file
00011   to you under the Apache License, Version 2.0 (the
00012   "License"); you may not use this file except in compliance
00013   with the License.  You may obtain a copy of the License at
00014 
00015       http://www.apache.org/licenses/LICENSE-2.0
00016 
00017   Unless required by applicable law or agreed to in writing, software
00018   distributed under the License is distributed on an "AS IS" BASIS,
00019   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00020   See the License for the specific language governing permissions and
00021   limitations under the License.
00022  */
00023 
00024 #include "libts.h"
00025 #undef std  // FIXME: remove dependency on the STL
00026 #include "ink_config.h"
00027 #include "ink_file.h"
00028 #include "I_Layout.h"
00029 #include "I_Version.h"
00030 
00031 // Includes and namespaces etc.
00032 #include "LogStandalone.cc"
00033 
00034 #include "LogObject.h"
00035 #include "hdrs/HTTP.h"
00036 
00037 #include <math.h>
00038 #include <sys/utsname.h>
00039 #if defined(solaris)
00040 #include <sys/types.h>
00041 #include <unistd.h>
00042 #endif
00043 
00044 #include <iostream>
00045 #include <fstream>
00046 #include <sstream>
00047 #include <iomanip>
00048 #include <string>
00049 #include <algorithm>
00050 #include <vector>
00051 #include <list>
00052 #include <functional>
00053 #include <fcntl.h>
00054 
00055 #ifndef _XOPEN_SOURCE
00056 #define _XOPEN_SOURCE 600
00057 #endif
00058 
00059 using namespace std;
00060 
00061 // Constants, please update the VERSION number when you make a new build!!!
00062 #define PROGRAM_NAME            "traffic_logstats"
00063 
00064 const int MAX_LOGBUFFER_SIZE = 65536;
00065 const int DEFAULT_LINE_LEN = 78;
00066 const double LOG10_1024 = 3.0102999566398116;
00067 const int MAX_ORIG_STRING = 4096;
00068 
00069 
00070 // Optimizations for "strcmp()", treat some fixed length (3 or 4 bytes) strings
00071 // as integers.
00072 const int GET_AS_INT = 5522759;
00073 const int PUT_AS_INT = 5526864;
00074 const int HEAD_AS_INT = 1145128264;
00075 const int POST_AS_INT = 1414745936;
00076 
00077 const int TEXT_AS_INT = 1954047348;
00078 
00079 const int JPEG_AS_INT = 1734701162;
00080 const int JPG_AS_INT = 6778986;
00081 const int GIF_AS_INT = 6711655;
00082 const int PNG_AS_INT = 6778480;
00083 const int BMP_AS_INT = 7368034;
00084 const int CSS_AS_INT = 7566179;
00085 const int XML_AS_INT = 7105912;
00086 const int HTML_AS_INT = 1819112552;
00087 const int ZIP_AS_INT = 7367034;
00088 
00089 const int JAVA_AS_INT = 1635148138;     // For "javascript"
00090 const int X_JA_AS_INT = 1634348408;     // For "x-javascript"
00091 const int RSSp_AS_INT = 728986482;      // For "RSS+"
00092 const int PLAI_AS_INT = 1767992432;     // For "plain"
00093 const int IMAG_AS_INT = 1734438249;     // For "image"
00094 const int HTTP_AS_INT = 1886680168;     // For "http" followed by "s://" or "://"
00095 
00096 // Store our "state" (position in log file etc.)
00097 struct LastState
00098 {
00099   off_t offset;
00100   ino_t st_ino;
00101 };
00102 static LastState last_state;
00103 
00104 
00105 // Store the collected counters and stats, per Origin Server, URL or total
00106 struct StatsCounter
00107 {
00108   int64_t count;
00109   int64_t bytes;
00110 };
00111 
00112 struct ElapsedStats
00113 {
00114   int min;
00115   int max;
00116   float avg;
00117   float stddev;
00118 };
00119 
00120 struct OriginStats
00121 {
00122   const char *server;
00123   StatsCounter total;
00124 
00125   struct
00126   {
00127     struct
00128     {
00129       ElapsedStats hit;
00130       ElapsedStats ims;
00131       ElapsedStats refresh;
00132       ElapsedStats other;
00133       ElapsedStats total;
00134     } hits;
00135     struct
00136     {
00137       ElapsedStats miss;
00138       ElapsedStats ims;
00139       ElapsedStats refresh;
00140       ElapsedStats other;
00141       ElapsedStats total;
00142     } misses;
00143   } elapsed;
00144 
00145   struct
00146   {
00147     struct
00148     {
00149       StatsCounter hit;
00150       StatsCounter ims;
00151       StatsCounter refresh;
00152       StatsCounter other;
00153       StatsCounter total;
00154     } hits;
00155     struct
00156     {
00157       StatsCounter miss;
00158       StatsCounter ims;
00159       StatsCounter refresh;
00160       StatsCounter other;
00161       StatsCounter total;
00162     } misses;
00163     struct
00164     {
00165       StatsCounter client_abort;
00166       StatsCounter connect_fail;
00167       StatsCounter invalid_req;
00168       StatsCounter unknown;
00169       StatsCounter other;
00170       StatsCounter total;
00171     } errors;
00172     StatsCounter other;
00173   } results;
00174 
00175   struct
00176   {
00177     StatsCounter c_000;         // Bad
00178     StatsCounter c_100;
00179     StatsCounter c_200;
00180     StatsCounter c_201;
00181     StatsCounter c_202;
00182     StatsCounter c_203;
00183     StatsCounter c_204;
00184     StatsCounter c_205;
00185     StatsCounter c_206;
00186     StatsCounter c_2xx;
00187     StatsCounter c_300;
00188     StatsCounter c_301;
00189     StatsCounter c_302;
00190     StatsCounter c_303;
00191     StatsCounter c_304;
00192     StatsCounter c_305;
00193     StatsCounter c_307;
00194     StatsCounter c_3xx;
00195     StatsCounter c_400;
00196     StatsCounter c_401;
00197     StatsCounter c_402;
00198     StatsCounter c_403;
00199     StatsCounter c_404;
00200     StatsCounter c_405;
00201     StatsCounter c_406;
00202     StatsCounter c_407;
00203     StatsCounter c_408;
00204     StatsCounter c_409;
00205     StatsCounter c_410;
00206     StatsCounter c_411;
00207     StatsCounter c_412;
00208     StatsCounter c_413;
00209     StatsCounter c_414;
00210     StatsCounter c_415;
00211     StatsCounter c_416;
00212     StatsCounter c_417;
00213     StatsCounter c_4xx;
00214     StatsCounter c_500;
00215     StatsCounter c_501;
00216     StatsCounter c_502;
00217     StatsCounter c_503;
00218     StatsCounter c_504;
00219     StatsCounter c_505;
00220     StatsCounter c_5xx;
00221   } codes;
00222 
00223   struct
00224   {
00225     StatsCounter direct;
00226     StatsCounter none;
00227     StatsCounter sibling;
00228     StatsCounter parent;
00229     StatsCounter empty;
00230     StatsCounter invalid;
00231     StatsCounter other;
00232   } hierarchies;
00233 
00234   struct
00235   {
00236     StatsCounter http;
00237     StatsCounter https;
00238     StatsCounter none;
00239     StatsCounter other;
00240   } schemes;
00241 
00242   struct
00243   {
00244     StatsCounter options;
00245     StatsCounter get;
00246     StatsCounter head;
00247     StatsCounter post;
00248     StatsCounter put;
00249     StatsCounter del;
00250     StatsCounter trace;
00251     StatsCounter connect;
00252     StatsCounter purge;
00253     StatsCounter none;
00254     StatsCounter other;
00255   } methods;
00256 
00257   struct
00258   {
00259     struct
00260     {
00261       StatsCounter plain;
00262       StatsCounter xml;
00263       StatsCounter html;
00264       StatsCounter css;
00265       StatsCounter javascript;
00266       StatsCounter other;
00267       StatsCounter total;
00268     } text;
00269     struct
00270     {
00271       StatsCounter jpeg;
00272       StatsCounter gif;
00273       StatsCounter png;
00274       StatsCounter bmp;
00275       StatsCounter other;
00276       StatsCounter total;
00277     } image;
00278     struct
00279     {
00280       StatsCounter shockwave_flash;
00281       StatsCounter quicktime;
00282       StatsCounter javascript;
00283       StatsCounter zip;
00284       StatsCounter other;
00285       StatsCounter rss_xml;
00286       StatsCounter rss_atom;
00287       StatsCounter rss_other;
00288       StatsCounter total;
00289     } application;
00290     struct
00291     {
00292       StatsCounter wav;
00293       StatsCounter mpeg;
00294       StatsCounter other;
00295       StatsCounter total;
00296     } audio;
00297     StatsCounter none;
00298     StatsCounter other;
00299   } content;
00300 };
00301 
00302 struct UrlStats
00303 {
00304   bool operator < (const UrlStats& rhs) const  { return req.count > rhs.req.count;  } // Reverse order
00305 
00306   const char *url;
00307   StatsCounter req;
00308   ElapsedStats time;
00309   int64_t c_000; 
00310   int64_t c_2xx;
00311   int64_t c_3xx;
00312   int64_t c_4xx;
00313   int64_t c_5xx;
00314   int64_t hits;
00315   int64_t misses;
00316   int64_t errors;
00317 };
00318 
00319 ///////////////////////////////////////////////////////////////////////////////
00320 // Equal operator for char* (for the hash_map)
00321 struct eqstr
00322 {
00323   inline bool operator() (const char *s1, const char *s2) const
00324   {
00325     return 0 == strcmp(s1, s2);
00326   }
00327 };
00328 
00329 struct hash_fnv32 {
00330   inline uint32_t operator()(const char* s) const
00331   {
00332     uint32_t hval = (uint32_t)0x811c9dc5; /* FNV1_32_INIT */
00333 
00334     if (s) {
00335       while (*s) {
00336         hval ^= (uint32_t)*s++;
00337         hval *= (uint32_t)0x01000193;  /* FNV_32_PRIME */
00338       }
00339     }
00340 
00341     return hval;
00342   }
00343 };
00344 
00345 typedef std::list<UrlStats> LruStack;
00346 
00347 #if HAVE_CXX_11 && HAVE_UNORDERED_MAP && HAVE_UNORDERED_SET
00348 #include <unordered_map>
00349 #include <unordered_set>
00350 typedef std::unordered_map<const char *, OriginStats *, hash_fnv32, eqstr> OriginStorage;
00351 typedef std::unordered_set<const char *, hash_fnv32, eqstr> OriginSet;
00352 typedef std::unordered_map<const char *, LruStack::iterator, hash_fnv32, eqstr> LruHash;
00353 
00354 // Resize a hash-based container.
00355 template <class T, class N>
00356 void
00357 rehash(T& container, N size) {
00358   container.rehash(size);
00359 }
00360 
00361 #elif HAVE_GNU_CXX_HASH_MAP
00362 #define _BACKWARD_BACKWARD_WARNING_H    // needed for gcc 4.3
00363 #include <ext/hash_map>
00364 #include <ext/hash_set>
00365 typedef __gnu_cxx::hash_map<const char *, OriginStats *, hash_fnv32, eqstr> OriginStorage;
00366 typedef __gnu_cxx::hash_set<const char *, hash_fnv32, eqstr> OriginSet;
00367 typedef __gnu_cxx::hash_map<const char *, LruStack::iterator, hash_fnv32, eqstr> LruHash;
00368 
00369 // Resize a hash-based container.
00370 template <class T, class N>
00371 void
00372 rehash(T& container, N size) {
00373   container.resize(size);
00374 }
00375 
00376 #undef _BACKWARD_BACKWARD_WARNING_H
00377 #else
00378 #error no supported hash container
00379 #endif
00380 
00381 // LRU class for the URL data
00382 void  update_elapsed(ElapsedStats &stat, const int elapsed, const StatsCounter &counter);
00383 
00384 class UrlLru
00385 {
00386 
00387 public:
00388   UrlLru(int size=1000000, int show_urls=0)
00389     : _size(size)
00390   {
00391     _show_urls = size > 0 ? (show_urls >= size ? size-1 : show_urls) : show_urls;
00392     _init();
00393     _reset(false);
00394     _cur = _stack.begin();
00395   }
00396 
00397   void
00398   resize(int size=0)
00399   {
00400     if (0 != size)
00401       _size = size;
00402 
00403     _init();
00404     _reset(true);
00405     _cur = _stack.begin();
00406   }
00407 
00408   void
00409   dump(int as_object=0)
00410   {
00411     int show = _stack.size();
00412 
00413     if (_show_urls > 0 && _show_urls < show)
00414       show = _show_urls;
00415 
00416     _stack.sort();
00417     for (LruStack::iterator u=_stack.begin(); NULL != u->url && --show >= 0; ++u)
00418       _dump_url(u, as_object);
00419     if (as_object)
00420       std::cout << "  \"_timestamp\" : \"" << static_cast<int>(ink_time_wall_seconds()) << "\"" << std::endl;
00421     else 
00422       std::cout << "  { \"_timestamp\" : \"" << static_cast<int>(ink_time_wall_seconds()) << "\" }" << std::endl;
00423   }
00424 
00425   void
00426   add_stat(const char* url, int64_t bytes, int time, int result, int http_code, int as_object=0)
00427   {
00428     LruHash::iterator h = _hash.find(url);
00429 
00430     if (h != _hash.end()) {
00431       LruStack::iterator &l = h->second;
00432 
00433       ++(l->req.count);
00434       l->req.bytes += bytes;
00435 
00436       if ((http_code >= 600) || (http_code < 200))
00437         ++(l->c_000);
00438       else if (http_code >= 500)
00439         ++(l->c_5xx);
00440       else if (http_code >= 400)
00441         ++(l->c_4xx);
00442       else if (http_code >= 300)
00443         ++(l->c_3xx);
00444       else // http_code >= 200
00445         ++(l->c_2xx);
00446 
00447       switch (result) {
00448       case SQUID_LOG_TCP_HIT:
00449       case SQUID_LOG_TCP_IMS_HIT:
00450       case SQUID_LOG_TCP_REFRESH_HIT:
00451       case SQUID_LOG_TCP_DISK_HIT:
00452       case SQUID_LOG_TCP_MEM_HIT:
00453       case SQUID_LOG_TCP_REF_FAIL_HIT:
00454       case SQUID_LOG_UDP_HIT:
00455       case SQUID_LOG_UDP_WEAK_HIT:
00456       case SQUID_LOG_UDP_HIT_OBJ:
00457         ++(l->hits);
00458         break;
00459       case SQUID_LOG_TCP_MISS:
00460       case SQUID_LOG_TCP_IMS_MISS:
00461       case SQUID_LOG_TCP_REFRESH_MISS:
00462       case SQUID_LOG_TCP_EXPIRED_MISS:
00463       case SQUID_LOG_TCP_WEBFETCH_MISS:
00464       case SQUID_LOG_UDP_MISS:
00465         ++(l->misses);
00466         break;
00467       case SQUID_LOG_ERR_CLIENT_ABORT:
00468       case SQUID_LOG_ERR_CONNECT_FAIL:
00469       case SQUID_LOG_ERR_INVALID_REQ:
00470       case SQUID_LOG_ERR_UNKNOWN:
00471       case SQUID_LOG_ERR_READ_TIMEOUT:
00472         ++(l->errors);
00473         break;
00474       }
00475 
00476       update_elapsed(l->time, time, l->req);
00477       // Move this entry to the top of the stack (hence, LRU)
00478       if (_size > 0)
00479         _stack.splice(_stack.begin(), _stack, l);
00480     } else { // "new" URL
00481       const char *u = ats_strdup(url); // We own it.
00482       LruStack::iterator l = _stack.end();
00483 
00484       if (_size > 0) {
00485         if (_cur == l) { // LRU is full, take the last one
00486           --l;
00487           h = _hash.find(l->url);
00488           if (h != _hash.end())
00489             _hash.erase(h);
00490           if (0 == _show_urls)
00491             _dump_url(l, as_object);
00492         } else {
00493           l = _cur++;
00494         }
00495         ats_free(const_cast<char*>(l->url)); // We no longer own this string.
00496       } else {
00497         l = _stack.insert(l, UrlStats()); // This seems faster than having a static "template" ...
00498       }
00499 
00500       // Setup this URL stat
00501       l->url = u;
00502       l->req.bytes = bytes;
00503       l->req.count = 1;
00504 
00505       if ((http_code >= 600) || (http_code < 200))
00506         l->c_000 = 1;
00507       else if (http_code >= 500)
00508         l->c_5xx = 1;
00509       else if (http_code >= 400)
00510         l->c_4xx = 1;
00511       else if (http_code >= 300)
00512         l->c_3xx = 1;
00513       else // http_code >= 200
00514         l->c_2xx = 1;
00515 
00516       switch (result) {
00517       case SQUID_LOG_TCP_HIT:
00518       case SQUID_LOG_TCP_IMS_HIT:
00519       case SQUID_LOG_TCP_REFRESH_HIT:
00520       case SQUID_LOG_TCP_DISK_HIT:
00521       case SQUID_LOG_TCP_MEM_HIT:
00522       case SQUID_LOG_TCP_REF_FAIL_HIT:
00523       case SQUID_LOG_UDP_HIT:
00524       case SQUID_LOG_UDP_WEAK_HIT:
00525       case SQUID_LOG_UDP_HIT_OBJ:
00526         l->hits = 1;
00527         break;
00528       case SQUID_LOG_TCP_MISS:
00529       case SQUID_LOG_TCP_IMS_MISS:
00530       case SQUID_LOG_TCP_REFRESH_MISS:
00531       case SQUID_LOG_TCP_EXPIRED_MISS:
00532       case SQUID_LOG_TCP_WEBFETCH_MISS:
00533       case SQUID_LOG_UDP_MISS:
00534         l->misses = 1;
00535         break;
00536       case SQUID_LOG_ERR_CLIENT_ABORT:
00537       case SQUID_LOG_ERR_CONNECT_FAIL:
00538       case SQUID_LOG_ERR_INVALID_REQ:
00539       case SQUID_LOG_ERR_UNKNOWN:
00540       case SQUID_LOG_ERR_READ_TIMEOUT:
00541         l->errors = 1;
00542         break;
00543       }
00544 
00545       l->time.min = -1;
00546       l->time.max = -1;
00547       update_elapsed(l->time, time, l->req);
00548       _hash[u] = l;
00549 
00550       // We running a real LRU or not?
00551       if (_size > 0)
00552         _stack.splice(_stack.begin(), _stack, l); // Move this to the top of the stack
00553     }
00554   }
00555 
00556 private:
00557   void
00558   _init()
00559   {
00560     if (_size > 0) {
00561       _stack.resize(_size);
00562       rehash(_hash, _size);
00563     }
00564   }
00565 
00566   void
00567   _reset(bool free=false)
00568   {
00569     for (LruStack::iterator l=_stack.begin(); l != _stack.end(); ++l) {
00570       if (free && l->url)
00571         ats_free(const_cast<char*>(l->url));
00572       memset(&(*l), 0, sizeof(UrlStats));
00573     }
00574   }
00575 
00576   void
00577   _dump_url(LruStack::iterator &u, int as_object)
00578   {
00579     if (as_object)
00580       std::cout << "  \"" << u->url << "\" : { ";
00581     else
00582       std::cout << "  { \"" << u->url << "\" : { ";
00583     // Requests
00584     std::cout << "\"req\" : { \"total\" : \"" << u->req.count <<
00585       "\", \"hits\" : \"" <<  u->hits << 
00586       "\", \"misses\" : \"" <<  u->misses << 
00587       "\", \"errors\" : \"" <<  u->errors <<
00588       "\", \"000\" : \"" <<  u->c_000 <<
00589       "\", \"2xx\" : \"" <<  u->c_2xx <<
00590       "\", \"3xx\" : \"" <<  u->c_3xx <<
00591       "\", \"4xx\" : \"" <<  u->c_4xx <<
00592       "\", \"5xx\" : \"" <<  u->c_5xx << "\" }, ";
00593     std:: cout << "\"bytes\" : \"" << u->req.bytes << "\", ";
00594     // Service times
00595     std::cout << "\"svc_t\" : { \"min\" : \"" << u->time.min <<
00596       "\", \"max\" : \"" << u->time.max <<
00597       "\", \"avg\" : \"" << std::setiosflags(ios::fixed) << std::setprecision(2) << u->time.avg <<
00598       "\", \"dev\" : \"" << std::setiosflags(ios::fixed) << std::setprecision(2) << u->time.stddev;
00599 
00600     if (as_object)
00601       std::cout << "\" } }," << std::endl;
00602     else
00603       std::cout << "\" } } }," << std::endl;
00604   }
00605 
00606   LruHash _hash;
00607   LruStack _stack;
00608   int _size, _show_urls;
00609   LruStack::iterator _cur;
00610 };
00611 
00612 
00613 ///////////////////////////////////////////////////////////////////////////////
00614 // Globals, holding the accumulated stats (ok, I'm lazy ...)
00615 static OriginStats totals;
00616 static OriginStorage origins;
00617 static OriginSet *origin_set;
00618 static UrlLru *urls;
00619 static int parse_errors;
00620 
00621 // Command line arguments (parsing)
00622 struct CommandLineArgs
00623 {
00624   char log_file[1024];
00625   char origin_file[1024];
00626   char origin_list[MAX_ORIG_STRING];
00627   int max_origins;
00628   char state_tag[1024];
00629   int64_t min_hits;
00630   int max_age;
00631   int line_len;
00632   int incremental;              // Do an incremental run
00633   int tail;                     // Tail the log file
00634   int summary;                  // Summary only
00635   int json;                     // JSON output
00636   int cgi;                      // CGI output (typically with json)
00637   int urls;                     // Produce JSON output of URL stats, arg is LRU size
00638   int show_urls;                // Max URLs to show
00639   int as_object;                // Show the URL stats as a single JSON object (not array)
00640   int version;
00641   int help;
00642 
00643   CommandLineArgs()
00644     : max_origins(0), min_hits(0), max_age(0), line_len(DEFAULT_LINE_LEN), incremental(0),
00645       tail(0), summary(0), json(0), cgi(0), urls(0), show_urls(0), as_object(0), version(0), help(0)
00646   {
00647     log_file[0] = '\0';
00648     origin_file[0] = '\0';
00649     origin_list[0] = '\0';
00650     state_tag[0] = '\0';
00651   }
00652 
00653   void parse_arguments(char** argv);
00654 };
00655 
00656 static CommandLineArgs cl;
00657 
00658 static ArgumentDescription argument_descriptions[] = {
00659   {"help", 'h', "Give this help", "T", &cl.help, NULL, NULL},
00660   {"log_file", 'f', "Specific logfile to parse", "S1023", cl.log_file, NULL, NULL},
00661   {"origin_list", 'o', "Only show stats for listed Origins", "S4095", cl.origin_list, NULL, NULL},
00662   {"origin_file", 'O', "File listing Origins to show", "S1023", cl.origin_file, NULL, NULL},
00663   {"max_orgins", 'M', "Max number of Origins to show", "I", &cl.max_origins, NULL, NULL},
00664   {"urls", 'u', "Produce JSON stats for URLs, argument is LRU size", "I", &cl.urls, NULL, NULL},
00665   {"show_urls", 'U', "Only show max this number of URLs", "I", &cl.show_urls, NULL, NULL},
00666   {"as_object", 'A', "Produce URL stats as a JSON object instead of array", "T", &cl.as_object, NULL, NULL},
00667   {"incremental", 'i', "Incremental log parsing", "T", &cl.incremental, NULL, NULL},
00668   {"statetag", 'S', "Name of the state file to use", "S1023", cl.state_tag, NULL, NULL},
00669   {"tail", 't', "Parse the last <sec> seconds of log", "I", &cl.tail, NULL, NULL},
00670   {"summary", 's', "Only produce the summary", "T", &cl.summary, NULL, NULL},
00671   {"json", 'j', "Produce JSON formatted output", "T", &cl.json, NULL, NULL},
00672   {"cgi", 'c', "Produce HTTP headers suitable as a CGI", "T", &cl.cgi, NULL, NULL},
00673   {"min_hits", 'm', "Minimum total hits for an Origin", "L", &cl.min_hits, NULL, NULL},
00674   {"max_age", 'a', "Max age for log entries to be considered", "I", &cl.max_age, NULL, NULL},
00675   {"line_len", 'l', "Output line length", "I", &cl.line_len, NULL, NULL},
00676   {"debug_tags", 'T', "Colon-Separated Debug Tags", "S1023", &error_tags, NULL, NULL},
00677   {"version", 'V', "Print Version Id", "T", &cl.version, NULL, NULL},
00678 };
00679 
00680 static const char *USAGE_LINE =
00681   "Usage: " PROGRAM_NAME " [-f logfile] [-o origin[,...]] [-O originfile] [-m minhits] [-inshv]";
00682 
00683 void
00684 CommandLineArgs::parse_arguments(char** argv)
00685 {
00686   // process command-line arguments
00687   process_args(argument_descriptions, countof(argument_descriptions), argv, USAGE_LINE);
00688 
00689   // Process as "CGI" ?
00690   if (strstr(argv[0], ".cgi") || cgi) {
00691     char *query;
00692 
00693     json = 1;
00694     cgi = 1;
00695 
00696     if (NULL != (query = getenv("QUERY_STRING"))) {
00697       char buffer[MAX_ORIG_STRING];
00698       char *tok, *sep_ptr, *val;
00699 
00700       ink_strlcpy(buffer, query, sizeof(buffer));
00701       unescapifyStr(buffer);
00702 
00703       for (tok = strtok_r(buffer, "&", &sep_ptr); tok != NULL;) {
00704         val = strchr(tok, '=');
00705         if (val)
00706           *(val++) = '\0';
00707         if (0 == strncmp(tok, "origin_list", 11)) {
00708           ink_strlcpy(origin_list, val, sizeof(origin_list));
00709         } else if (0 == strncmp(tok, "state_tag", 9)) {
00710           ink_strlcpy(state_tag, val, sizeof(state_tag));
00711         } else if (0 == strncmp(tok, "max_origins", 11)) {
00712           max_origins = strtol(val, NULL, 10);
00713         } else if (0 == strncmp(tok, "urls", 4)) {
00714           urls = strtol(val, NULL, 10);
00715         } else if (0 == strncmp(tok, "show_urls", 9)) {
00716           show_urls = strtol(val, NULL, 10);
00717         } else if (0 == strncmp(tok, "as_object", 9)) {
00718           as_object = strtol(val, NULL, 10);
00719         } else if (0 == strncmp(tok, "min_hits", 8)) {
00720           min_hits = strtol(val, NULL, 10);
00721         } else if (0 == strncmp(tok, "incremental", 11)) {
00722           incremental = strtol(val, NULL, 10);
00723         } else {
00724           // Unknown query arg.
00725         }
00726 
00727         tok = strtok_r(NULL, "&", &sep_ptr);
00728       }
00729     }
00730   }
00731 
00732   // check for the version number request
00733   if (version) {
00734     std::cerr << appVersionInfo.FullVersionInfoStr << std::endl;
00735     _exit(0);
00736   }
00737 
00738   // check for help request
00739   if (help) {
00740     usage(argument_descriptions, countof(argument_descriptions), USAGE_LINE);
00741     _exit(0);
00742   }
00743 }
00744 
00745 
00746 // Enum for return code levels.
00747 enum ExitLevel
00748 {
00749   EXIT_OK = 0,
00750   EXIT_WARNING = 1,
00751   EXIT_CRITICAL = 2,
00752   EXIT_UNKNOWN = 3
00753 };
00754 
00755 struct ExitStatus
00756 {
00757   ExitLevel level;
00758   char notice[1024];
00759 
00760   ExitStatus()
00761     : level(EXIT_OK)
00762   {
00763     memset(notice, 0, sizeof(notice));
00764   }
00765 
00766   void set(ExitLevel l, const char* n=NULL) {
00767     if (l > level)
00768       level = l;
00769     if (n)
00770       ink_strlcat(notice, n, sizeof(notice));
00771   }
00772 
00773   void append(const char *n) {
00774     ink_strlcat(notice, n, sizeof(notice));
00775   }
00776 
00777   void append(const std::string s) {
00778     ink_strlcat(notice, s.c_str(), sizeof(notice));
00779   }
00780 };
00781 
00782 
00783 // Enum for parsing a log line
00784 enum ParseStates
00785 {
00786   P_STATE_ELAPSED,
00787   P_STATE_IP,
00788   P_STATE_RESULT,
00789   P_STATE_CODE,
00790   P_STATE_SIZE,
00791   P_STATE_METHOD,
00792   P_STATE_URL,
00793   P_STATE_RFC931,
00794   P_STATE_HIERARCHY,
00795   P_STATE_PEER,
00796   P_STATE_TYPE,
00797   P_STATE_END
00798 };
00799 
00800 // Enum for HTTP methods
00801 enum HTTPMethod
00802 {
00803   METHOD_OPTIONS,
00804   METHOD_GET,
00805   METHOD_HEAD,
00806   METHOD_POST,
00807   METHOD_PUT,
00808   METHOD_DELETE,
00809   METHOD_TRACE,
00810   METHOD_CONNECT,
00811   METHOD_PURGE,
00812   METHOD_NONE,
00813   METHOD_OTHER
00814 };
00815 
00816 // Enum for URL schemes
00817 enum URLScheme
00818 {
00819   SCHEME_HTTP,
00820   SCHEME_HTTPS,
00821   SCHEME_NONE,
00822   SCHEME_OTHER
00823 };
00824 
00825 
00826 ///////////////////////////////////////////////////////////////////////////////
00827 // Initialize the elapsed field
00828 inline void
00829 init_elapsed(OriginStats *stats)
00830 {
00831   stats->elapsed.hits.hit.min = -1;
00832   stats->elapsed.hits.ims.min = -1;
00833   stats->elapsed.hits.refresh.min = -1;
00834   stats->elapsed.hits.other.min = -1;
00835   stats->elapsed.hits.total.min = -1;
00836   stats->elapsed.misses.miss.min = -1;
00837   stats->elapsed.misses.ims.min = -1;
00838   stats->elapsed.misses.refresh.min = -1;
00839   stats->elapsed.misses.other.min = -1;
00840   stats->elapsed.misses.total.min = -1;
00841 }
00842 
00843 // Update the counters for one StatsCounter
00844 inline void
00845 update_counter(StatsCounter& counter, int size)
00846 {
00847   counter.count++;
00848   counter.bytes += size;
00849 }
00850 
00851 inline void
00852 update_elapsed(ElapsedStats &stat, const int elapsed, const StatsCounter &counter)
00853 {
00854   int newcount, oldcount;
00855   float oldavg, newavg, sum_of_squares;
00856 
00857   // Skip all the "0" values.
00858   if (0 == elapsed)
00859     return;
00860   if (-1 == stat.min)
00861     stat.min = elapsed;
00862   else if (stat.min > elapsed)
00863     stat.min = elapsed;
00864 
00865   if (stat.max < elapsed)
00866     stat.max = elapsed;
00867 
00868   // update_counter should have been called on counter.count before calling
00869   // update_elapsed.
00870   newcount = counter.count;
00871   // New count should never be zero, else there was a programming error.
00872   ink_release_assert(newcount);
00873   oldcount = counter.count - 1;
00874   oldavg = stat.avg;
00875   newavg = (oldavg * oldcount + elapsed) / newcount;
00876   // Now find the new standard deviation from the old one
00877 
00878   if (oldcount != 0)
00879     sum_of_squares = (stat.stddev * stat.stddev * oldcount);
00880   else
00881     sum_of_squares = 0;
00882 
00883   //Find the old sum of squares.
00884   sum_of_squares = sum_of_squares + 2 * oldavg * oldcount * (oldavg - newavg)
00885     + oldcount * (newavg * newavg - oldavg * oldavg);
00886 
00887   //Now, find the new sum of squares.
00888   sum_of_squares = sum_of_squares + (elapsed - newavg) * (elapsed - newavg);
00889 
00890   stat.stddev = sqrt(sum_of_squares / newcount);
00891   stat.avg = newavg;
00892 
00893 }
00894 
00895 ///////////////////////////////////////////////////////////////////////////////
00896 // Update the "result" and "elapsed" stats for a particular record
00897 inline void
00898 update_results_elapsed(OriginStats * stat, int result, int elapsed, int size)
00899 {
00900   switch (result) {
00901   case SQUID_LOG_TCP_HIT:
00902     update_counter(stat->results.hits.hit, size);
00903     update_counter(stat->results.hits.total, size);
00904     update_elapsed(stat->elapsed.hits.hit, elapsed, stat->results.hits.hit);
00905     update_elapsed(stat->elapsed.hits.total, elapsed, stat->results.hits.total);
00906     break;
00907   case SQUID_LOG_TCP_MISS:
00908     update_counter(stat->results.misses.miss, size);
00909     update_counter(stat->results.misses.total, size);
00910     update_elapsed(stat->elapsed.misses.miss, elapsed, stat->results.misses.miss);
00911     update_elapsed(stat->elapsed.misses.total, elapsed, stat->results.misses.total);
00912     break;
00913   case SQUID_LOG_TCP_IMS_HIT:
00914     update_counter(stat->results.hits.ims, size);
00915     update_counter(stat->results.hits.total, size);
00916     update_elapsed(stat->elapsed.hits.ims, elapsed, stat->results.hits.ims);
00917     update_elapsed(stat->elapsed.hits.total, elapsed, stat->results.hits.total);
00918     break;
00919   case SQUID_LOG_TCP_IMS_MISS:
00920     update_counter(stat->results.misses.ims, size);
00921     update_counter(stat->results.misses.total, size);
00922     update_elapsed(stat->elapsed.misses.ims, elapsed, stat->results.misses.ims);
00923     update_elapsed(stat->elapsed.misses.total, elapsed, stat->results.misses.total);
00924     break;
00925   case SQUID_LOG_TCP_REFRESH_HIT:
00926     update_counter(stat->results.hits.refresh, size);
00927     update_counter(stat->results.hits.total, size);
00928     update_elapsed(stat->elapsed.hits.refresh, elapsed, stat->results.hits.refresh);
00929     update_elapsed(stat->elapsed.hits.total, elapsed, stat->results.hits.total);
00930     break;
00931   case SQUID_LOG_TCP_REFRESH_MISS:
00932     update_counter(stat->results.misses.refresh, size);
00933     update_counter(stat->results.misses.total, size);
00934     update_elapsed(stat->elapsed.misses.refresh, elapsed, stat->results.misses.refresh);
00935     update_elapsed(stat->elapsed.misses.total, elapsed, stat->results.misses.total);
00936     break;
00937   case SQUID_LOG_ERR_CLIENT_ABORT:
00938     update_counter(stat->results.errors.client_abort, size);
00939     update_counter(stat->results.errors.total, size);
00940     break;
00941   case SQUID_LOG_ERR_CONNECT_FAIL:
00942     update_counter(stat->results.errors.connect_fail, size);
00943     update_counter(stat->results.errors.total, size);
00944     break;
00945   case SQUID_LOG_ERR_INVALID_REQ:
00946     update_counter(stat->results.errors.invalid_req, size);
00947     update_counter(stat->results.errors.total, size);
00948     break;
00949   case SQUID_LOG_ERR_UNKNOWN:
00950     update_counter(stat->results.errors.unknown, size);
00951     update_counter(stat->results.errors.total, size);
00952     break;
00953   case SQUID_LOG_TCP_DISK_HIT:
00954   case SQUID_LOG_TCP_MEM_HIT:
00955   case SQUID_LOG_TCP_REF_FAIL_HIT:
00956   case SQUID_LOG_UDP_HIT:
00957   case SQUID_LOG_UDP_WEAK_HIT:
00958   case SQUID_LOG_UDP_HIT_OBJ:
00959     update_counter(stat->results.hits.other, size);
00960     update_counter(stat->results.hits.total, size);
00961     update_elapsed(stat->elapsed.hits.other, elapsed, stat->results.hits.other);
00962     update_elapsed(stat->elapsed.hits.total, elapsed, stat->results.hits.total);
00963     break;
00964   case SQUID_LOG_TCP_EXPIRED_MISS:
00965   case SQUID_LOG_TCP_WEBFETCH_MISS:
00966   case SQUID_LOG_UDP_MISS:
00967     update_counter(stat->results.misses.other, size);
00968     update_counter(stat->results.misses.total, size);
00969     update_elapsed(stat->elapsed.misses.other, elapsed, stat->results.misses.other);
00970     update_elapsed(stat->elapsed.misses.total, elapsed, stat->results.misses.total);
00971     break;
00972   default:
00973     if ((result >= SQUID_LOG_ERR_READ_TIMEOUT) && (result <= SQUID_LOG_ERR_UNKNOWN)) {
00974       update_counter(stat->results.errors.other, size);
00975       update_counter(stat->results.errors.total, size);
00976     } else
00977       update_counter(stat->results.other, size);
00978     break;
00979   }
00980 }
00981 
00982 
00983 ///////////////////////////////////////////////////////////////////////////////
00984 // Update the "codes" stats for a particular record
00985 inline void
00986 update_codes(OriginStats * stat, int code, int size)
00987 {
00988   switch (code) {
00989   case 100:
00990     update_counter(stat->codes.c_100, size);
00991     break;
00992 
00993   // 200's
00994   case 200:
00995     update_counter(stat->codes.c_200, size);
00996     break;
00997   case 201:
00998     update_counter(stat->codes.c_201, size);
00999     break;
01000   case 202:
01001     update_counter(stat->codes.c_202, size);
01002     break;
01003   case 203:
01004     update_counter(stat->codes.c_203, size);
01005     break;
01006   case 204:
01007     update_counter(stat->codes.c_204, size);
01008     break;
01009   case 205:
01010     update_counter(stat->codes.c_205, size);
01011     break;
01012   case 206:
01013     update_counter(stat->codes.c_206, size);
01014     break;
01015 
01016   // 300's
01017   case 300:
01018     update_counter(stat->codes.c_300, size);
01019     break;
01020   case 301:
01021     update_counter(stat->codes.c_301, size);
01022     break;
01023   case 302:
01024     update_counter(stat->codes.c_302, size);
01025     break;
01026   case 303:
01027     update_counter(stat->codes.c_303, size);
01028     break;
01029   case 304:
01030     update_counter(stat->codes.c_304, size);
01031     break;
01032   case 305:
01033     update_counter(stat->codes.c_305, size);
01034     break;
01035   case 307:
01036     update_counter(stat->codes.c_307, size);
01037     break;
01038 
01039   // 400's
01040   case 400:
01041     update_counter(stat->codes.c_400, size);
01042     break;
01043   case 401:
01044     update_counter(stat->codes.c_401, size);
01045     break;
01046   case 402:
01047     update_counter(stat->codes.c_402, size);
01048     break;
01049   case 403:
01050     update_counter(stat->codes.c_403, size);
01051     break;
01052   case 404:
01053     update_counter(stat->codes.c_404, size);
01054     break;
01055   case 405:
01056     update_counter(stat->codes.c_405, size);
01057     break;
01058   case 406:
01059     update_counter(stat->codes.c_406, size);
01060     break;
01061   case 407:
01062     update_counter(stat->codes.c_407, size);
01063     break;
01064   case 408:
01065     update_counter(stat->codes.c_408, size);
01066     break;
01067   case 409:
01068     update_counter(stat->codes.c_409, size);
01069     break;
01070   case 410:
01071     update_counter(stat->codes.c_410, size);
01072     break;
01073   case 411:
01074     update_counter(stat->codes.c_411, size);
01075     break;
01076   case 412:
01077     update_counter(stat->codes.c_412, size);
01078     break;
01079   case 413:
01080     update_counter(stat->codes.c_413, size);
01081     break;
01082   case 414:
01083     update_counter(stat->codes.c_414, size);
01084     break;
01085   case 415:
01086     update_counter(stat->codes.c_415, size);
01087     break;
01088   case 416:
01089     update_counter(stat->codes.c_416, size);
01090     break;
01091   case 417:
01092     update_counter(stat->codes.c_417, size);
01093     break;
01094 
01095   // 500's
01096   case 500:
01097     update_counter(stat->codes.c_500, size);
01098     break;
01099   case 501:
01100     update_counter(stat->codes.c_501, size);
01101     break;
01102   case 502:
01103     update_counter(stat->codes.c_502, size);
01104     break;
01105   case 503:
01106     update_counter(stat->codes.c_503, size);
01107     break;
01108   case 504:
01109     update_counter(stat->codes.c_504, size);
01110     break;
01111   case 505:
01112     update_counter(stat->codes.c_505, size);
01113     break;
01114   default:
01115     break;
01116   }
01117 
01118   if ((code >= 600) || (code < 200))
01119     update_counter(stat->codes.c_000, size);
01120   else if (code >= 500)
01121     update_counter(stat->codes.c_5xx, size);
01122   else if (code >= 400)
01123     update_counter(stat->codes.c_4xx, size);
01124   else if (code >= 300)
01125     update_counter(stat->codes.c_3xx, size);
01126   else if (code >= 200)
01127     update_counter(stat->codes.c_2xx, size);
01128 }
01129 
01130 
01131 ///////////////////////////////////////////////////////////////////////////////
01132 // Update the "methods" stats for a particular record
01133 inline void
01134 update_methods(OriginStats * stat, int method, int size)
01135 {
01136   // We're so loppsided on GETs, so makes most sense to test 'out of order'.
01137   switch (method) {
01138   case METHOD_GET:
01139     update_counter(stat->methods.get, size);
01140     break;
01141 
01142   case METHOD_OPTIONS:
01143     update_counter(stat->methods.options, size);
01144     break;
01145 
01146   case METHOD_HEAD:
01147     update_counter(stat->methods.head, size);
01148     break;
01149 
01150   case METHOD_POST:
01151     update_counter(stat->methods.post, size);
01152     break;
01153 
01154   case METHOD_PUT:
01155     update_counter(stat->methods.put, size);
01156     break;
01157 
01158   case METHOD_DELETE:
01159     update_counter(stat->methods.del, size);
01160     break;
01161 
01162   case METHOD_TRACE:
01163     update_counter(stat->methods.trace, size);
01164     break;
01165 
01166   case METHOD_CONNECT:
01167     update_counter(stat->methods.connect, size);
01168     break;
01169 
01170   case METHOD_PURGE:
01171     update_counter(stat->methods.purge, size);
01172     break;
01173 
01174   case METHOD_NONE:
01175     update_counter(stat->methods.none, size);
01176     break;
01177 
01178   default:
01179     update_counter(stat->methods.other, size);
01180     break;
01181   }
01182 }
01183 
01184 
01185 ///////////////////////////////////////////////////////////////////////////////
01186 // Update the "schemes" stats for a particular record
01187 inline void
01188 update_schemes(OriginStats * stat, int scheme, int size)
01189 {
01190   if (SCHEME_HTTP == scheme)
01191     update_counter(stat->schemes.http, size);
01192   else if (SCHEME_HTTPS == scheme)
01193     update_counter(stat->schemes.https, size);
01194   else if (SCHEME_NONE == scheme)
01195     update_counter(stat->schemes.none, size);
01196   else
01197     update_counter(stat->schemes.other, size);
01198 }
01199 
01200 
01201 ///////////////////////////////////////////////////////////////////////////////
01202 // Parse a log buffer
01203 int
01204 parse_log_buff(LogBufferHeader * buf_header, bool summary = false)
01205 {
01206   static LogFieldList *fieldlist = NULL;
01207 
01208   LogEntryHeader *entry;
01209   LogBufferIterator buf_iter(buf_header);
01210   LogField *field;
01211   OriginStorage::iterator o_iter;
01212   ParseStates state;
01213 
01214   char *read_from;
01215   char *tok;
01216   char *ptr;
01217   int tok_len;
01218   int flag = 0;                 // Flag used in state machine to carry "state" forward
01219 
01220   // Parsed results
01221   int http_code = 0, size = 0, result = 0, hier = 0, elapsed = 0;
01222   OriginStats *o_stats;
01223   char *o_server;
01224   HTTPMethod method;
01225   URLScheme scheme;
01226 
01227   if (!fieldlist) {
01228     fieldlist = new LogFieldList;
01229     ink_assert(fieldlist != NULL);
01230     bool agg = false;
01231     LogFormat::parse_symbol_string(buf_header->fmt_fieldlist(), fieldlist, &agg);
01232   }
01233   // Loop over all entries
01234   while ((entry = buf_iter.next())) {
01235     read_from = (char *) entry + sizeof(LogEntryHeader);
01236     // We read and skip over the first field, which is the timestamp.
01237     if ((field = fieldlist->first()))
01238       read_from += INK_MIN_ALIGN;
01239     else                        // This shouldn't happen, buffer must be messed up.
01240       break;
01241 
01242     state = P_STATE_ELAPSED;
01243     o_stats = NULL;
01244     o_server = NULL;
01245     method = METHOD_OTHER;
01246     scheme = SCHEME_OTHER;
01247 
01248     while ((field = fieldlist->next(field))) {
01249       switch (state) {
01250       case P_STATE_ELAPSED:
01251         state = P_STATE_IP;
01252         elapsed = *((int64_t *) (read_from));
01253         read_from += INK_MIN_ALIGN;
01254         break;
01255 
01256       case P_STATE_IP:
01257         state = P_STATE_RESULT;
01258         // Just skip the IP, we no longer assume it's always the same.
01259         {
01260           LogFieldIp* ip = reinterpret_cast<LogFieldIp*>(read_from);
01261           int len = sizeof(LogFieldIp);
01262           if (AF_INET == ip->_family) len = sizeof(LogFieldIp4);
01263           else if (AF_INET6 == ip->_family) len = sizeof(LogFieldIp6);
01264           read_from += INK_ALIGN_DEFAULT(len);
01265         }
01266         break;
01267 
01268       case P_STATE_RESULT:
01269         state = P_STATE_CODE;
01270         result = *((int64_t *) (read_from));
01271         read_from += INK_MIN_ALIGN;
01272         if ((result<32) || (result>255)) {
01273           flag = 1;
01274           state = P_STATE_END;
01275         }
01276         break;
01277 
01278       case P_STATE_CODE:
01279         state = P_STATE_SIZE;
01280         http_code = *((int64_t *) (read_from));
01281         read_from += INK_MIN_ALIGN;
01282         if ((http_code<0) || (http_code>999)) {
01283           flag = 1;
01284           state = P_STATE_END;
01285         }
01286         break;
01287 
01288       case P_STATE_SIZE:
01289         // Warning: This is not 64-bit safe, when converting the log format,
01290         // this needs to be fixed as well.
01291         state = P_STATE_METHOD;
01292         size = *((int64_t *) (read_from));
01293         read_from += INK_MIN_ALIGN;
01294         break;
01295 
01296       case P_STATE_METHOD:
01297         state = P_STATE_URL;
01298         flag = 0;
01299 
01300         // Small optimization for common (3-4 char) cases
01301         switch (*reinterpret_cast <int*>(read_from)) {
01302         case GET_AS_INT:
01303           method = METHOD_GET;
01304           read_from += LogAccess::round_strlen(3 + 1);
01305           break;
01306         case PUT_AS_INT:
01307           method = METHOD_PUT;
01308           read_from += LogAccess::round_strlen(3 + 1);
01309           break;
01310         case HEAD_AS_INT:
01311           method = METHOD_HEAD;
01312           read_from += LogAccess::round_strlen(4 + 1);
01313           break;
01314         case POST_AS_INT:
01315           method = METHOD_POST;
01316           read_from += LogAccess::round_strlen(4 + 1);
01317           break;
01318         default:
01319           tok_len = strlen(read_from);
01320           if ((5 == tok_len) && (0 == strncmp(read_from, "PURGE", 5)))
01321             method = METHOD_PURGE;
01322           else if ((6 == tok_len) && (0 == strncmp(read_from, "DELETE", 6)))
01323             method = METHOD_DELETE;
01324           else if ((7 == tok_len) && (0 == strncmp(read_from, "OPTIONS", 7)))
01325             method = METHOD_OPTIONS;
01326           else if ((1 == tok_len) && ('-' == *read_from)) {
01327             method = METHOD_NONE;
01328             flag = 1;           // No method, so no need to parse the URL
01329           } else {
01330             ptr = read_from;
01331             while (*ptr && isupper(*ptr))
01332               ++ptr;
01333             // Skip URL if it doesn't look like an HTTP method
01334             if (*ptr != '\0')
01335               flag = 1;
01336           }
01337           read_from += LogAccess::round_strlen(tok_len + 1);
01338           break;
01339         }
01340         break;
01341 
01342       case P_STATE_URL:
01343         state = P_STATE_RFC931;
01344         if (urls)
01345           urls->add_stat(read_from, size, elapsed, result, http_code, cl.as_object);
01346 
01347         // TODO check for read_from being empty string
01348         if (0 == flag) {
01349           tok = read_from;
01350           if (HTTP_AS_INT == *reinterpret_cast <int*>(tok)) {
01351             tok += 4;
01352             if (':' == *tok) {
01353               scheme = SCHEME_HTTP;
01354               tok += 3;
01355               tok_len = strlen(tok) + 7;
01356             } else if ('s' == *tok) {
01357               scheme = SCHEME_HTTPS;
01358               tok += 4;
01359               tok_len = strlen(tok) + 8;
01360             } else
01361               tok_len = strlen(tok) + 4;
01362           } else {
01363             if ('/' == *tok)
01364               scheme = SCHEME_NONE;
01365             tok_len = strlen(tok);
01366           }
01367           if ('/' == *tok)      // This is to handle crazy stuff like http:///origin.com
01368             tok++;
01369           ptr = strchr(tok, '/');
01370           if (ptr && !summary) { // Find the origin
01371             *ptr = '\0';
01372 
01373             // TODO: If we save state (struct) for a run, we probably need to always
01374             // update the origin data, no matter what the origin_set is.
01375             if (origin_set->empty() || (origin_set->find(tok) != origin_set->end())) {
01376               o_iter = origins.find(tok);
01377               if (origins.end() == o_iter) {
01378                 o_stats = (OriginStats *)ats_malloc(sizeof(OriginStats));
01379                 memset(o_stats, 0, sizeof(OriginStats));
01380                 init_elapsed(o_stats);
01381                 o_server = ats_strdup(tok);
01382                 if (o_stats && o_server) {
01383                   o_stats->server = o_server;
01384                   origins[o_server] = o_stats;
01385                 }
01386               } else
01387                 o_stats = o_iter->second;
01388             }
01389           }
01390         } else {
01391           // No method given
01392           if ('/' == *read_from)
01393             scheme = SCHEME_NONE;
01394           tok_len = strlen(read_from);
01395         }
01396         read_from += LogAccess::round_strlen(tok_len + 1);
01397 
01398         // Update the stats so far, since now we have the Origin (maybe)
01399         update_results_elapsed(&totals, result, elapsed, size);
01400         update_codes(&totals, http_code, size);
01401         update_methods(&totals, method, size);
01402         update_schemes(&totals, scheme, size);
01403         update_counter(totals.total, size);
01404         if (o_stats != NULL) {
01405           update_results_elapsed(o_stats, result, elapsed, size);
01406           update_codes(o_stats, http_code, size);
01407           update_methods(o_stats, method, size);
01408           update_schemes(o_stats, scheme, size);
01409           update_counter(o_stats->total, size);
01410         }
01411         break;
01412 
01413       case P_STATE_RFC931:
01414         state = P_STATE_HIERARCHY;
01415         if ('-' == *read_from)
01416           read_from += LogAccess::round_strlen(1 + 1);
01417         else
01418           read_from += LogAccess::strlen(read_from);
01419         break;
01420 
01421       case P_STATE_HIERARCHY:
01422         state = P_STATE_PEER;
01423         hier = *((int64_t *) (read_from));
01424         switch (hier) {
01425         case SQUID_HIER_NONE:
01426           update_counter(totals.hierarchies.none, size);
01427           if (o_stats != NULL)
01428             update_counter(o_stats->hierarchies.none, size);
01429           break;
01430         case SQUID_HIER_DIRECT:
01431           update_counter(totals.hierarchies.direct, size);
01432           if (o_stats != NULL)
01433             update_counter(o_stats->hierarchies.direct, size);
01434           break;
01435         case SQUID_HIER_SIBLING_HIT:
01436           update_counter(totals.hierarchies.sibling, size);
01437           if (o_stats != NULL)
01438             update_counter(o_stats->hierarchies.sibling, size);
01439           break;
01440         case SQUID_HIER_PARENT_HIT:
01441           update_counter(totals.hierarchies.parent, size);
01442           if (o_stats != NULL)
01443             update_counter(o_stats->hierarchies.direct, size);
01444           break;
01445         case SQUID_HIER_EMPTY:
01446           update_counter(totals.hierarchies.empty, size);
01447           if (o_stats != NULL)
01448             update_counter(o_stats->hierarchies.empty, size);
01449           break;
01450         default:
01451           if ((hier >= SQUID_HIER_EMPTY) && (hier < SQUID_HIER_INVALID_ASSIGNED_CODE)) {
01452             update_counter(totals.hierarchies.other, size);
01453             if (o_stats != NULL)
01454               update_counter(o_stats->hierarchies.other, size);
01455           } else {
01456             update_counter(totals.hierarchies.invalid, size);
01457             if (o_stats != NULL)
01458               update_counter(o_stats->hierarchies.invalid, size);
01459           }
01460           break;
01461         }
01462         read_from += INK_MIN_ALIGN;
01463         break;
01464 
01465       case P_STATE_PEER:
01466         state = P_STATE_TYPE;
01467         if ('-' == *read_from)
01468           read_from += LogAccess::round_strlen(1 + 1);
01469         else
01470           read_from += LogAccess::strlen(read_from);
01471         break;
01472 
01473       case P_STATE_TYPE:
01474         state = P_STATE_END;
01475         if (IMAG_AS_INT == *reinterpret_cast <int*>(read_from)) {
01476           update_counter(totals.content.image.total, size);
01477           if (o_stats != NULL)
01478             update_counter(o_stats->content.image.total, size);
01479           tok = read_from + 6;
01480           switch (*reinterpret_cast <int*>(tok)) {
01481           case JPEG_AS_INT:
01482             tok_len = 10;
01483             update_counter(totals.content.image.jpeg, size);
01484             if (o_stats != NULL)
01485               update_counter(o_stats->content.image.jpeg, size);
01486             break;
01487           case JPG_AS_INT:
01488             tok_len = 9;
01489             update_counter(totals.content.image.jpeg, size);
01490             if (o_stats != NULL)
01491               update_counter(o_stats->content.image.jpeg, size);
01492             break;
01493           case GIF_AS_INT:
01494             tok_len = 9;
01495             update_counter(totals.content.image.gif, size);
01496             if (o_stats != NULL)
01497               update_counter(o_stats->content.image.gif, size);
01498             break;
01499           case PNG_AS_INT:
01500             tok_len = 9;
01501             update_counter(totals.content.image.png, size);
01502             if (o_stats != NULL)
01503               update_counter(o_stats->content.image.png, size);
01504             break;
01505           case BMP_AS_INT:
01506             tok_len = 9;
01507             update_counter(totals.content.image.bmp, size);
01508             if (o_stats != NULL)
01509               update_counter(o_stats->content.image.bmp, size);
01510             break;
01511           default:
01512             tok_len = 6 + strlen(tok);
01513             update_counter(totals.content.image.other, size);
01514             if (o_stats != NULL)
01515               update_counter(o_stats->content.image.other, size);
01516             break;
01517           }
01518         } else if (TEXT_AS_INT == *reinterpret_cast <int*>(read_from)) {
01519           tok = read_from + 5;
01520           update_counter(totals.content.text.total, size);
01521           if (o_stats != NULL)
01522             update_counter(o_stats->content.text.total, size);
01523           switch (*reinterpret_cast <int*>(tok)) {
01524           case JAVA_AS_INT:
01525             // TODO verify if really "javascript"
01526             tok_len = 15;
01527             update_counter(totals.content.text.javascript, size);
01528             if (o_stats != NULL)
01529               update_counter(o_stats->content.text.javascript, size);
01530             break;
01531           case CSS_AS_INT:
01532             tok_len = 8;
01533             update_counter(totals.content.text.css, size);
01534             if (o_stats != NULL)
01535               update_counter(o_stats->content.text.css, size);
01536             break;
01537           case XML_AS_INT:
01538             tok_len = 8;
01539             update_counter(totals.content.text.xml, size);
01540             if (o_stats != NULL)
01541               update_counter(o_stats->content.text.xml, size);
01542             break;
01543           case HTML_AS_INT:
01544             tok_len = 9;
01545             update_counter(totals.content.text.html, size);
01546             if (o_stats != NULL)
01547               update_counter(o_stats->content.text.html, size);
01548             break;
01549           case PLAI_AS_INT:
01550             tok_len = 10;
01551             update_counter(totals.content.text.plain, size);
01552             if (o_stats != NULL)
01553               update_counter(o_stats->content.text.plain, size);
01554             break;
01555           default:
01556             tok_len = 5 + strlen(tok);;
01557             update_counter(totals.content.text.other, size);
01558             if (o_stats != NULL)
01559               update_counter(o_stats->content.text.other, size);
01560             break;
01561           }
01562         } else if (0 == strncmp(read_from, "application", 11)) {
01563           tok = read_from + 12;
01564           update_counter(totals.content.application.total, size);
01565           if (o_stats != NULL)
01566             update_counter(o_stats->content.application.total, size);
01567           switch (*reinterpret_cast <int*>(tok)) {
01568           case ZIP_AS_INT:
01569             tok_len = 15;
01570             update_counter(totals.content.application.zip, size);
01571             if (o_stats != NULL)
01572               update_counter(o_stats->content.application.zip, size);
01573             break;
01574           case JAVA_AS_INT:
01575             update_counter(totals.content.application.javascript, size);
01576             if (o_stats != NULL)
01577               update_counter(o_stats->content.application.javascript, size);
01578           case X_JA_AS_INT:
01579             tok_len = 24;
01580             update_counter(totals.content.application.javascript, size);
01581             if (o_stats != NULL)
01582               update_counter(o_stats->content.application.javascript, size);
01583             break;
01584           case RSSp_AS_INT:
01585             if (0 == strcmp(tok+4, "xml")) {
01586               tok_len = 19;
01587               update_counter(totals.content.application.rss_xml, size);
01588               if (o_stats != NULL)
01589                 update_counter(o_stats->content.application.rss_xml, size);
01590             } else if (0 == strcmp(tok+4,"atom")) {
01591               tok_len = 20;
01592               update_counter(totals.content.application.rss_atom, size);
01593               if (o_stats != NULL)
01594                 update_counter(o_stats->content.application.rss_atom, size);
01595             } else {
01596               tok_len = 12 + strlen(tok);
01597               update_counter(totals.content.application.rss_other, size);
01598               if (o_stats != NULL)
01599                 update_counter(o_stats->content.application.rss_other, size);
01600             }
01601             break;
01602           default:
01603             if (0 == strcmp(tok, "x-shockwave-flash")) {
01604               tok_len = 29;
01605               update_counter(totals.content.application.shockwave_flash, size);
01606               if (o_stats != NULL)
01607                 update_counter(o_stats->content.application.shockwave_flash, size);
01608             } else if (0 == strcmp(tok, "x-quicktimeplayer")) {
01609               tok_len = 29;
01610               update_counter(totals.content.application.quicktime, size);
01611               if (o_stats != NULL)
01612                 update_counter(o_stats->content.application.quicktime, size);
01613             } else {
01614               tok_len = 12 + strlen(tok);
01615               update_counter(totals.content.application.other, size);
01616               if (o_stats != NULL)
01617                 update_counter(o_stats->content.application.other, size);
01618             }
01619           }
01620         } else if (0 == strncmp(read_from, "audio", 5)) {
01621           tok = read_from + 6;
01622           tok_len = 6 + strlen(tok);
01623           update_counter(totals.content.audio.total, size);
01624           if (o_stats != NULL)
01625             update_counter(o_stats->content.audio.total, size);
01626           if ((0 == strcmp(tok, "x-wav")) || (0 == strcmp(tok, "wav"))) {
01627             update_counter(totals.content.audio.wav, size);
01628             if (o_stats != NULL)
01629               update_counter(o_stats->content.audio.wav, size);
01630           } else if ((0 == strcmp(tok, "x-mpeg")) || (0 == strcmp(tok, "mpeg"))) {
01631             update_counter(totals.content.audio.mpeg, size);
01632             if (o_stats != NULL)
01633               update_counter(o_stats->content.audio.mpeg, size);
01634           } else {
01635             update_counter(totals.content.audio.other, size);
01636             if (o_stats != NULL)
01637               update_counter(o_stats->content.audio.other, size);
01638           }
01639         } else if ('-' == *read_from) {
01640           tok_len = 1;
01641           update_counter(totals.content.none, size);
01642           if (o_stats != NULL)
01643             update_counter(o_stats->content.none, size);
01644         } else {
01645           tok_len = strlen(read_from);
01646           update_counter(totals.content.other, size);
01647           if (o_stats != NULL)
01648             update_counter(o_stats->content.other, size);
01649         }
01650         read_from += LogAccess::round_strlen(tok_len + 1);
01651         flag = 0;               // We exited this state without errors
01652         break;
01653 
01654       case P_STATE_END:
01655         // Nothing to do really
01656         if (flag) {
01657           parse_errors++;
01658         }
01659         break;
01660       }
01661     }
01662   }
01663 
01664   return 0;
01665 }
01666 
01667 
01668 
01669 ///////////////////////////////////////////////////////////////////////////////
01670 // Process a file (FD)
01671 int
01672 process_file(int in_fd, off_t offset, unsigned max_age)
01673 {
01674   char buffer[MAX_LOGBUFFER_SIZE];
01675   int nread, buffer_bytes;
01676 
01677   Debug("logstats", "Processing file [offset=%" PRId64 "].", (int64_t)offset);
01678   while (true) {
01679     Debug("logstats", "Reading initial header.");
01680     buffer[0] = '\0';
01681 
01682     unsigned first_read_size = sizeof(uint32_t) + sizeof(uint32_t);
01683     LogBufferHeader *header = (LogBufferHeader *)&buffer[0];
01684 
01685     // Find the next log header, aligning us properly. This is not
01686     // particularly optimal, but we should only have to do this
01687     // once, and hopefully we'll be aligned immediately.
01688     if (offset > 0) {
01689       Debug("logstats", "Re-aligning file read.");
01690       while (true) {
01691         if (lseek(in_fd, offset, SEEK_SET) < 0) {
01692           Debug("logstats", "Internal seek failed (offset=%"  PRId64 ").", (int64_t)offset);
01693           return 1;
01694         }
01695 
01696         // read the first 8 bytes of the header, which will give us the
01697         // cookie and the version number.
01698         nread = read(in_fd, buffer, first_read_size);
01699         if (!nread || EOF == nread) {
01700           return 0;
01701         }
01702         // ensure that this is a valid logbuffer header
01703         if (header->cookie && (LOG_SEGMENT_COOKIE == header->cookie)) {
01704           offset = 0;
01705           break;
01706         }
01707         offset++;
01708       }
01709       if (!header->cookie) {
01710         return 0;
01711       }
01712     } else {
01713       nread = read(in_fd, buffer, first_read_size);
01714       if (!nread ||  EOF == nread || !header->cookie)
01715         return 0;
01716 
01717       // ensure that this is a valid logbuffer header
01718       if (header->cookie != LOG_SEGMENT_COOKIE) {
01719         Debug("logstats", "Invalid segment cookie (expected %d, got %d)", LOG_SEGMENT_COOKIE, header->cookie);
01720         return 1;
01721       }
01722     }
01723 
01724     Debug("logstats", "LogBuffer version %d, current = %d", header->version, LOG_SEGMENT_VERSION);
01725     if (header->version != LOG_SEGMENT_VERSION)
01726       return 1;
01727 
01728     // read the rest of the header
01729     unsigned second_read_size = sizeof(LogBufferHeader) - first_read_size;
01730     nread = read(in_fd, &buffer[first_read_size], second_read_size);
01731     if (!nread || EOF == nread) {
01732       Debug("logstats", "Second read of header failed (attemped %d bytes at offset %d, got nothing), errno=%d.", second_read_size, first_read_size, errno);
01733       return 1;
01734     }
01735 
01736     // read the rest of the buffer
01737     if (header->byte_count > sizeof(buffer)) {
01738       Debug("logstats", "Header byte count [%d] > expected [%zu]", header->byte_count, sizeof(buffer));
01739       return 1;
01740     }
01741 
01742     buffer_bytes = header->byte_count - sizeof(LogBufferHeader);
01743     if (buffer_bytes <= 0 || (unsigned int) buffer_bytes > (sizeof(buffer) - sizeof(LogBufferHeader))) {
01744       Debug("logstats", "Buffer payload [%d] is wrong.", buffer_bytes);
01745       return 1;
01746     }
01747 
01748     const int MAX_READ_TRIES = 5;
01749     int total_read = 0;
01750     int read_tries_remaining = MAX_READ_TRIES; // since the data will be old anyway, let's only try a few times.
01751     do {
01752       nread = read(in_fd, &buffer[sizeof(LogBufferHeader) + total_read], buffer_bytes - total_read);
01753       if (EOF == nread || !nread) { // just bail on error
01754         Debug("logstats", "Read failed while reading log buffer, wanted %d bytes, nread=%d, errno=%d", buffer_bytes - total_read, nread, errno);
01755         return 1;
01756       } else {
01757         total_read += nread;
01758       }
01759 
01760       if (total_read < buffer_bytes) {
01761         if (--read_tries_remaining <= 0) {
01762           Debug("logstats_failed_retries", "Unable to read after %d tries, total_read=%d, buffer_bytes=%d", MAX_READ_TRIES, total_read, buffer_bytes);
01763           return 1;
01764         }
01765         // let's wait until we get more data on this file descriptor
01766         Debug("logstats_partial_read", "Failed to read buffer payload [%d bytes], total_read=%d, buffer_bytes=%d, tries_remaining=%d",
01767             buffer_bytes - total_read, total_read, buffer_bytes, read_tries_remaining);
01768         usleep(50*1000); // wait 50ms
01769       }
01770     } while (total_read < buffer_bytes);
01771 
01772     // Possibly skip too old entries (the entire buffer is skipped)
01773     if (header->high_timestamp >= max_age) {
01774       if (parse_log_buff(header, cl.summary != 0) != 0) {
01775         Debug("logstats", "Failed to parse log buffer.");
01776         return 1;
01777       }
01778     } else {
01779       Debug("logstats", "Skipping old buffer (age=%d, max=%d)", header->high_timestamp, max_age);
01780     }
01781   }
01782 
01783   return 0;
01784 }
01785 
01786 
01787 ///////////////////////////////////////////////////////////////////////////////
01788 // Determine if this "stat" (Origin Server) is worthwhile to produce a
01789 // report for.
01790 inline int
01791 use_origin(const OriginStats * stat)
01792 {
01793   return ((stat->total.count > cl.min_hits) && (NULL != strchr(stat->server, '.')) && (NULL == strchr(stat->server, '%')));
01794 }
01795 
01796 
01797 ///////////////////////////////////////////////////////////////////////////////
01798 // Produce a nicely formatted output for a stats collection on a stream
01799 inline void
01800 format_center(const char *str)
01801 {
01802   std::cout << std::setfill(' ') << std::setw((cl.line_len - strlen(str)) / 2 + strlen(str)) << str << std::endl << std::endl;
01803 }
01804 
01805 inline void
01806 format_int(int64_t num)
01807 {
01808   if (num > 0) {
01809     int64_t mult = (int64_t) pow((double)10, (int) (log10((double)num) / 3) * 3);
01810     int64_t div;
01811     std::stringstream ss;
01812 
01813     ss.fill('0');
01814     while (mult > 0) {
01815       div = num / mult;
01816       ss << div << std::setw(3);
01817       num -= (div * mult);
01818       if (mult /= 1000)
01819         ss << std::setw(0) << ',' << std::setw(3);
01820     }
01821     std::cout << ss.str();
01822   } else
01823     std::cout << '0';
01824 }
01825 
01826 void
01827 format_elapsed_header()
01828 {
01829   std::cout << std::left << std::setw(24) << "Elapsed time stats";
01830   std::cout << std::right << std::setw(7) << "Min" << std::setw(13) << "Max";
01831   std::cout << std::right << std::setw(17) << "Avg" << std::setw(17) << "Std Deviation" << std::endl;
01832   std::cout << std::setw(cl.line_len) << std::setfill('-') << '-' << std::setfill(' ') << std::endl;
01833 }
01834 
01835 inline void
01836 format_elapsed_line(const char *desc, const ElapsedStats &stat, bool json=false)
01837 {
01838   if (json) {
01839     std::cout << "    " << '"' << desc << "\" : " << "{ ";
01840     std::cout << "\"min\": \"" << stat.min << "\", ";
01841     std::cout << "\"max\": \"" << stat.max << "\", ";
01842     std::cout << "\"avg\": \"" << std::setiosflags(ios::fixed) << std::setprecision(2) << stat.avg << "\", ";
01843     std::cout << "\"dev\": \"" << std::setiosflags(ios::fixed) << std::setprecision(2) << stat.stddev << "\" },";
01844     std::cout << std::endl;
01845   } else {
01846     std::cout << std::left << std::setw(24) << desc;
01847     std::cout << std::right << std::setw(7);
01848     format_int(stat.min);
01849     std::cout << std::right << std::setw(13);
01850     format_int(stat.max);
01851 
01852     std::cout << std::right << std::setw(17) << std::setiosflags(ios::fixed) << std::setprecision(2) << stat.avg;
01853     std::cout << std::right << std::setw(17) << std::setiosflags(ios::fixed) << std::setprecision(2) << stat.stddev;
01854     std::cout << std::endl;
01855   }
01856 }
01857 
01858 
01859 void
01860 format_detail_header(const char *desc)
01861 {
01862   std::cout << std::left << std::setw(29) << desc;
01863   std::cout << std::right << std::setw(15) << "Count" << std::setw(11) << "Percent";
01864   std::cout << std::right << std::setw(12) << "Bytes" << std::setw(11) << "Percent" << std::endl;
01865   std::cout << std::setw(cl.line_len) << std::setfill('-') << '-' << std::setfill(' ') << std::endl;
01866 }
01867 
01868 inline void
01869 format_line(const char *desc, const StatsCounter &stat, const StatsCounter &total, bool json=false)
01870 {
01871   static char metrics[] = "KKMGTP";
01872   static char buf[64];
01873   int ix = (stat.bytes > 1024 ? (int) (log10((double)stat.bytes) / LOG10_1024) : 1);
01874 
01875   if (json) {
01876     std::cout << "    " << '"' << desc << "\" : " << "{ ";
01877     std::cout << "\"req\": \"" << stat.count << "\", ";
01878     std::cout << "\"req_pct\": \"" << std::setiosflags(ios::fixed) << std::setprecision(2) <<
01879       (double)stat.count / total.count * 100 << "\", ";
01880     std::cout << "\"bytes\": \"" << stat.bytes << "\", ";
01881     std::cout << "\"bytes_pct\": \"" << std::setiosflags(ios::fixed) << std::setprecision(2) <<
01882       (double)stat.bytes / total.bytes * 100 << "\" }," << std::endl;
01883   } else {
01884     std::cout << std::left << std::setw(29) << desc;
01885 
01886     std::cout << std::right << std::setw(15);
01887     format_int(stat.count);
01888 
01889     snprintf(buf, sizeof(buf), "%10.2f%%", ((double) stat.count / total.count * 100));
01890     std::cout << std::right << buf;
01891 
01892     snprintf(buf, sizeof(buf), "%10.2f%cB", stat.bytes / pow((double)1024, ix), metrics[ix]);
01893     std::cout << std::right << buf;
01894 
01895     snprintf(buf, sizeof(buf), "%10.2f%%", ((double) stat.bytes / total.bytes * 100));
01896     std::cout << std::right << buf << std::endl;
01897   }
01898 }
01899 
01900 
01901 // Little "helpers" for the vector we use to sort the Origins.
01902 typedef pair<const char *, OriginStats *>OriginPair;
01903 inline bool
01904 operator<(const OriginPair &a, const OriginPair &b)
01905 {
01906   return a.second->total.count > b.second->total.count;
01907 }
01908 
01909 void
01910 print_detail_stats(const OriginStats * stat, bool json=false)
01911 {
01912   // Cache hit/misses etc.
01913   if (!json)
01914     format_detail_header("Request Result");
01915 
01916   format_line(json ? "hit.direct" : "Cache hit", stat->results.hits.hit, stat->total, json);
01917   format_line(json ? "hit.ims" : "Cache hit IMS", stat->results.hits.ims, stat->total,json);
01918   format_line(json ? "hit.refresh" : "Cache hit refresh", stat->results.hits.refresh, stat->total,json);
01919   format_line(json ? "hit.other" : "Cache hit other", stat->results.hits.other, stat->total, json);
01920   format_line(json ? "hit.total" : "Cache hit total", stat->results.hits.total, stat->total, json);
01921 
01922   if (!json)
01923     std::cout << std::endl;
01924 
01925   format_line(json ? "miss.direct" : "Cache miss", stat->results.misses.miss, stat->total, json);
01926   format_line(json ? "miss.ims" : "Cache miss IMS", stat->results.misses.ims, stat->total, json);
01927   format_line(json ? "miss.refresh" : "Cache miss refresh", stat->results.misses.refresh, stat->total, json);
01928   format_line(json ? "miss.other" : "Cache miss other", stat->results.misses.other, stat->total, json);
01929   format_line(json ? "miss.total" : "Cache miss total", stat->results.misses.total, stat->total, json);
01930 
01931   if (!json)
01932     std::cout << std::endl;
01933 
01934   format_line(json ? "error.client_abort" : "Client aborted", stat->results.errors.client_abort, stat->total, json);
01935   format_line(json ? "error.connect_failed" : "Connect failed", stat->results.errors.connect_fail, stat->total, json);
01936   format_line(json ? "error.invalid_request" : "Invalid request", stat->results.errors.invalid_req, stat->total, json);
01937   format_line(json ? "error.unknown" : "Unknown error(99)", stat->results.errors.unknown, stat->total, json);
01938   format_line(json ? "error.other" : "Other errors", stat->results.errors.other, stat->total, json);
01939   format_line(json ? "error.total" : "Errors total", stat->results.errors.total, stat->total, json);
01940 
01941   if (!json) {
01942     std::cout << std::setw(cl.line_len) << std::setfill('.') << '.' << std::setfill(' ') << std::endl;
01943     format_line("Total requests", stat->total, stat->total);
01944     std::cout << std::endl << std::endl;
01945 
01946     // HTTP codes
01947     format_detail_header("HTTP return codes");
01948   }
01949 
01950   format_line(json ? "status.100" : "100 Continue", stat->codes.c_100, stat->total, json);
01951 
01952   format_line(json ? "status.200" : "200 OK", stat->codes.c_200, stat->total, json);
01953   format_line(json ? "status.201" : "201 Created", stat->codes.c_201, stat->total, json);
01954   format_line(json ? "status.202" : "202 Accepted", stat->codes.c_202, stat->total, json);
01955   format_line(json ? "status.203" : "203 Non-Authoritative Info", stat->codes.c_203, stat->total, json);
01956   format_line(json ? "status.204" : "204 No content", stat->codes.c_204, stat->total, json);
01957   format_line(json ? "status.205" : "205 Reset Content", stat->codes.c_205, stat->total, json);
01958   format_line(json ? "status.206" : "206 Partial content", stat->codes.c_206, stat->total, json);
01959   format_line(json ? "status.2xx" : "2xx Total", stat->codes.c_2xx, stat->total, json);
01960 
01961   if (!json)
01962     std::cout << std::endl;
01963 
01964   format_line(json ? "status.300" : "300 Multiple Choices", stat->codes.c_300, stat->total, json);
01965   format_line(json ? "status.301" : "301 Moved permanently", stat->codes.c_301, stat->total, json);
01966   format_line(json ? "status.302" : "302 Found", stat->codes.c_302, stat->total, json);
01967   format_line(json ? "status.303" : "303 See Other", stat->codes.c_303, stat->total, json);
01968   format_line(json ? "status.304" : "304 Not modified", stat->codes.c_304, stat->total, json);
01969   format_line(json ? "status.305" : "305 Use Proxy", stat->codes.c_305, stat->total, json);
01970   format_line(json ? "status.307" : "307 Temporary Redirect", stat->codes.c_307, stat->total, json);
01971   format_line(json ? "status.3xx" : "3xx Total", stat->codes.c_3xx, stat->total, json);
01972 
01973   if (!json)
01974     std::cout << std::endl;
01975 
01976   format_line(json ? "status.400" : "400 Bad request", stat->codes.c_400, stat->total, json);
01977   format_line(json ? "status.401" : "401 Unauthorized", stat->codes.c_401, stat->total, json);
01978   format_line(json ? "status.402" : "402 Payment Required", stat->codes.c_402, stat->total, json);
01979   format_line(json ? "status.403" : "403 Forbidden", stat->codes.c_403, stat->total, json);
01980   format_line(json ? "status.404" : "404 Not found", stat->codes.c_404, stat->total, json);
01981   format_line(json ? "status.405" : "405 Method Not Allowed", stat->codes.c_405, stat->total, json);
01982   format_line(json ? "status.406" : "406 Not Acceptable", stat->codes.c_406, stat->total, json);
01983   format_line(json ? "status.407" : "407 Proxy Auth Required", stat->codes.c_407, stat->total, json);
01984   format_line(json ? "status.408" : "408 Request Timeout", stat->codes.c_408, stat->total, json);
01985   format_line(json ? "status.409" : "409 Conflict", stat->codes.c_409, stat->total, json);
01986   format_line(json ? "status.410" : "410 Gone", stat->codes.c_410, stat->total, json);
01987   format_line(json ? "status.411" : "411 Length Required", stat->codes.c_411, stat->total, json);
01988   format_line(json ? "status.412" : "412 Precondition Failed", stat->codes.c_412, stat->total, json);
01989   format_line(json ? "status.413" : "413 Request Entity Too Large", stat->codes.c_413, stat->total, json);
01990   format_line(json ? "status.414" : "414 Request-URI Too Long", stat->codes.c_414, stat->total, json);
01991   format_line(json ? "status.415" : "415 Unsupported Media Type", stat->codes.c_415, stat->total, json);
01992   format_line(json ? "status.416" : "416 Req Range Not Satisfiable", stat->codes.c_416, stat->total, json);
01993   format_line(json ? "status.417" : "417 Expectation Failed", stat->codes.c_417, stat->total, json);
01994   format_line(json ? "status.4xx" : "4xx Total", stat->codes.c_4xx, stat->total, json);
01995 
01996   if (!json)
01997     std::cout << std::endl;
01998 
01999   format_line(json ? "status.500" : "500 Internal Server Error", stat->codes.c_500, stat->total, json);
02000   format_line(json ? "status.501" : "501 Not implemented", stat->codes.c_501, stat->total, json);
02001   format_line(json ? "status.502" : "502 Bad gateway", stat->codes.c_502, stat->total, json);
02002   format_line(json ? "status.503" : "503 Service unavailable", stat->codes.c_503, stat->total, json);
02003   format_line(json ? "status.504" : "504 Gateway Timeout", stat->codes.c_504, stat->total, json);
02004   format_line(json ? "status.505" : "505 HTTP Ver. Not Supported", stat->codes.c_505, stat->total, json);
02005   format_line(json ? "status.5xx" : "5xx Total", stat->codes.c_5xx, stat->total, json);
02006 
02007   if (!json)
02008     std::cout << std::endl;
02009 
02010   format_line(json ? "status.000" : "000 Unknown", stat->codes.c_000, stat->total, json);
02011 
02012   if (!json) {
02013     std::cout << std::endl << std::endl;
02014 
02015     // Origin hierarchies
02016     format_detail_header("Origin hierarchies");
02017   }
02018 
02019   format_line(json ? "hier.none" : "NONE", stat->hierarchies.none, stat->total, json);
02020   format_line(json ? "hier.direct" : "DIRECT", stat->hierarchies.direct, stat->total, json);
02021   format_line(json ? "hier.sibling" : "SIBLING", stat->hierarchies.sibling, stat->total, json);
02022   format_line(json ? "hier.parent" : "PARENT", stat->hierarchies.parent, stat->total, json);
02023   format_line(json ? "hier.empty" : "EMPTY", stat->hierarchies.empty, stat->total, json);
02024   format_line(json ? "hier.invalid" : "invalid", stat->hierarchies.invalid, stat->total, json);
02025   format_line(json ? "hier.other" : "other", stat->hierarchies.other, stat->total, json);
02026 
02027   if (!json) {
02028     std::cout << std::endl << std::endl;
02029 
02030     // HTTP methods
02031     format_detail_header("HTTP Methods");
02032   }
02033 
02034   format_line(json ? "method.options" : "OPTIONS", stat->methods.options, stat->total, json);
02035   format_line(json ? "method.get" : "GET", stat->methods.get, stat->total, json);
02036   format_line(json ? "method.head" : "HEAD", stat->methods.head, stat->total, json);
02037   format_line(json ? "method.post" : "POST", stat->methods.post, stat->total, json);
02038   format_line(json ? "method.put" : "PUT", stat->methods.put, stat->total, json);
02039   format_line(json ? "method.delete" : "DELETE", stat->methods.del, stat->total, json);
02040   format_line(json ? "method.trace" : "TRACE", stat->methods.trace, stat->total, json);
02041   format_line(json ? "method.connect" : "CONNECT", stat->methods.connect, stat->total, json);
02042   format_line(json ? "method.purge" : "PURGE", stat->methods.purge, stat->total, json);
02043   format_line(json ? "method.none" : "none (-)", stat->methods.none, stat->total, json);
02044   format_line(json ? "method.other" : "other", stat->methods.other, stat->total, json);
02045 
02046   if (!json) {
02047     std::cout << std::endl << std::endl;
02048 
02049     // URL schemes (HTTP/HTTPs)
02050     format_detail_header("URL Schemes");
02051   }
02052 
02053   format_line(json ? "scheme.http" : "HTTP (port 80)", stat->schemes.http, stat->total, json);
02054   format_line(json ? "scheme.https" : "HTTPS (port 443)", stat->schemes.https, stat->total, json);
02055   format_line(json ? "scheme.none" : "none", stat->schemes.none, stat->total, json);
02056   format_line(json ? "scheme.other" : "other", stat->schemes.other, stat->total, json);
02057 
02058   if (!json) {
02059     std::cout << std::endl << std::endl;
02060 
02061     // Content types
02062     format_detail_header("Content Types");
02063   }
02064 
02065   format_line(json ? "content.text.javascript" : "text/javascript", stat->content.text.javascript, stat->total, json);
02066   format_line(json ? "content.text.css" : "text/css", stat->content.text.css, stat->total, json);
02067   format_line(json ? "content.text.html" : "text/html", stat->content.text.html, stat->total, json);
02068   format_line(json ? "content.text.xml" : "text/xml", stat->content.text.xml, stat->total, json);
02069   format_line(json ? "content.text.plain" : "text/plain", stat->content.text.plain, stat->total, json);
02070   format_line(json ? "content.text.other" : "text/ other", stat->content.text.other, stat->total, json);
02071   format_line(json ? "content.text.total" : "text/ total", stat->content.text.total, stat->total, json);
02072 
02073   if (!json)
02074     std::cout << std::endl;
02075 
02076   format_line(json ? "content.image.jpeg" : "image/jpeg", stat->content.image.jpeg, stat->total, json);
02077   format_line(json ? "content.image.gif" : "image/gif", stat->content.image.gif, stat->total, json);
02078   format_line(json ? "content.image.png" : "image/png", stat->content.image.png, stat->total, json);
02079   format_line(json ? "content.image.bmp" : "image/bmp", stat->content.image.bmp, stat->total, json);
02080   format_line(json ? "content.image.other" : "image/ other", stat->content.image.other, stat->total, json);
02081   format_line(json ? "content.image.total" : "image/ total", stat->content.image.total, stat->total, json);
02082 
02083   if (!json)
02084     std::cout << std::endl;
02085 
02086   format_line(json ? "content.audio.x-wav" : "audio/x-wav", stat->content.audio.wav, stat->total, json);
02087   format_line(json ? "content.audio.x-mpeg" : "audio/x-mpeg", stat->content.audio.mpeg, stat->total, json);
02088   format_line(json ? "content.audio.other" : "audio/ other", stat->content.audio.other, stat->total, json);
02089   format_line(json ? "content.audio.total": "audio/ total", stat->content.audio.total, stat->total, json);
02090 
02091   if (!json)
02092     std::cout << std::endl;
02093 
02094   format_line(json ? "content.application.shockwave" : "application/x-shockwave", stat->content.application.shockwave_flash, stat->total, json);
02095   format_line(json ? "content.application.javascript" : "application/[x-]javascript", stat->content.application.javascript, stat->total, json);
02096   format_line(json ? "content.application.quicktime" : "application/x-quicktime", stat->content.application.quicktime, stat->total, json);
02097   format_line(json ? "content.application.zip" : "application/zip", stat->content.application.zip, stat->total, json);
02098   format_line(json ? "content.application.rss_xml" : "application/rss+xml", stat->content.application.rss_xml, stat->total, json);
02099   format_line(json ? "content.application.rss_atom" : "application/rss+atom", stat->content.application.rss_atom, stat->total, json);
02100   format_line(json ? "content.application.other" : "application/ other", stat->content.application.other, stat->total, json);
02101   format_line(json ? "content.application.total": "application/ total", stat->content.application.total, stat->total, json);
02102 
02103   if (!json)
02104     std::cout << std::endl;
02105 
02106   format_line(json ? "content.none" : "none", stat->content.none, stat->total, json);
02107   format_line(json ? "content.other" : "other", stat->content.other, stat->total, json);
02108 
02109   if (!json) {
02110     std::cout << std::endl << std::endl;
02111 
02112   // Elapsed time
02113     format_elapsed_header();
02114   }
02115 
02116   format_elapsed_line(json ? "hit.direct.latency" : "Cache hit", stat->elapsed.hits.hit, json);
02117   format_elapsed_line(json ? "hit.ims.latency" : "Cache hit IMS", stat->elapsed.hits.ims, json);
02118   format_elapsed_line(json ? "hit.refresh.latency" : "Cache hit refresh", stat->elapsed.hits.refresh, json);
02119   format_elapsed_line(json ? "hit.other.latency" : "Cache hit other", stat->elapsed.hits.other, json);
02120   format_elapsed_line(json ? "hit.total.latency" : "Cache hit total", stat->elapsed.hits.total, json);
02121 
02122   format_elapsed_line(json ? "miss.direct.latency" : "Cache miss", stat->elapsed.misses.miss, json);
02123   format_elapsed_line(json ? "miss.ims.latency" : "Cache miss IMS", stat->elapsed.misses.ims, json);
02124   format_elapsed_line(json ? "miss.refresh.latency" : "Cache miss refresh", stat->elapsed.misses.refresh, json);
02125   format_elapsed_line(json ? "miss.other.latency" : "Cache miss other", stat->elapsed.misses.other, json);
02126   format_elapsed_line(json ? "miss.total.latency" : "Cache miss total", stat->elapsed.misses.total, json);
02127 
02128   if (!json) {
02129     std::cout << std::endl;
02130     std::cout << std::setw(cl.line_len) << std::setfill('_') << '_' << std::setfill(' ') << std::endl;
02131   } else {
02132     std::cout << "    \"_timestamp\" : \"" << static_cast<int>(ink_time_wall_seconds()) << '"' << std::endl;
02133   }
02134 }
02135 
02136 
02137 ///////////////////////////////////////////////////////////////////////////////
02138 // Little wrapper around exit, to allow us to exit gracefully
02139 void
02140 my_exit(const ExitStatus& status)
02141 {
02142   vector<OriginPair> vec;
02143   bool first = true;
02144   int max_origins;
02145 
02146   // Special case for URLs output.
02147   if (urls) {
02148     urls->dump(cl.as_object);
02149     if (cl.as_object)
02150       std::cout << "}" << std::endl;
02151     else
02152       std::cout << "]" << std::endl;
02153     _exit(status.level);
02154   }
02155 
02156   if (cl.json) {
02157     // TODO: produce output
02158   } else {
02159     switch (status.level) {
02160     case EXIT_OK:
02161       break;
02162     case EXIT_WARNING:
02163       std::cout << "warning: " << status.notice << std::endl;
02164       break;
02165     case EXIT_CRITICAL:
02166       std::cout << "critical: " << status.notice << std::endl;
02167       _exit(status.level);
02168       break;
02169     case EXIT_UNKNOWN:
02170       std::cout << "unknown: " << status.notice << std::endl;
02171       _exit(status.level);
02172       break;
02173     }
02174   }
02175 
02176   if (!origins.empty()) {
02177     // Sort the Origins by 'traffic'
02178     for (OriginStorage::iterator i = origins.begin(); i != origins.end(); i++)
02179       if (use_origin(i->second))
02180         vec.push_back(*i);
02181     sort(vec.begin(), vec.end());
02182 
02183     if (!cl.json) {
02184       // Produce a nice summary first
02185       format_center("Traffic summary");
02186       std::cout << std::left << std::setw(33) << "Origin Server";
02187       std::cout << std::right << std::setw(15) << "Hits";
02188       std::cout << std::right << std::setw(15) << "Misses";
02189       std::cout << std::right << std::setw(15) << "Errors" << std::endl;
02190       std::cout << std::setw(cl.line_len) << std::setfill('-') << '-' << std::setfill(' ') << std::endl;
02191 
02192       max_origins = cl.max_origins > 0 ? cl.max_origins : INT_MAX;
02193       for (vector<OriginPair>::iterator i = vec.begin(); (i != vec.end()) && (max_origins > 0); ++i, --max_origins) {
02194         std::cout << std::left << std::setw(33) << i->first;
02195         std::cout << std::right << std::setw(15);
02196         format_int(i->second->results.hits.total.count);
02197         std::cout << std::right << std::setw(15);
02198         format_int(i->second->results.misses.total.count);
02199         std::cout << std::right << std::setw(15);
02200         format_int(i->second->results.errors.total.count);
02201         std::cout << std::endl;
02202       }
02203       std::cout << std::setw(cl.line_len) << std::setfill('=') << '=' << std::setfill(' ') << std::endl;
02204       std::cout << std::endl << std::endl << std::endl;
02205     }
02206   }
02207 
02208   // Next the totals for all Origins, unless we specified a list of origins to filter.
02209   if (origin_set->empty()) {
02210     first = false;
02211     if (cl.json) {
02212       std::cout << "{ \"total\": {" << std::endl;
02213       print_detail_stats(&totals, cl.json);
02214       std::cout << "  }";
02215     } else {
02216       format_center("Totals (all Origins combined)");
02217       print_detail_stats(&totals);
02218       std::cout << std::endl << std::endl << std::endl;
02219     }
02220   }
02221 
02222   // And finally the individual Origin Servers.
02223   max_origins = cl.max_origins > 0 ? cl.max_origins : INT_MAX;
02224   for (vector<OriginPair>::iterator i = vec.begin(); (i != vec.end()) && (max_origins > 0); ++i, --max_origins) {
02225     if (cl.json) {
02226       if (first) {
02227         std::cout << "{ ";
02228         first = false;
02229       } else {
02230         std::cout << "," << std::endl << "  ";
02231       }
02232       std::cout << '"' <<  i->first << "\": {" << std::endl;
02233       print_detail_stats(i->second, cl.json);
02234       std::cout << "  }";
02235     } else {
02236       format_center(i->first);
02237       print_detail_stats(i->second);
02238       std::cout << std::endl << std::endl << std::endl;
02239     }
02240   }
02241 
02242   if (cl.json) {
02243     std::cout << std::endl << "}" << std::endl;
02244   }
02245 
02246   _exit(status.level);
02247 }
02248 
02249 ///////////////////////////////////////////////////////////////////////////////
02250 // Open the "default" log file (squid.blog), allow for it to be rotated.
02251 int
02252 open_main_log(ExitStatus& status)
02253 {
02254   std::string logfile(Layout::get()->logdir);
02255   int cnt = 3;
02256   int main_fd;
02257 
02258   logfile.append("/squid.blog");
02259   while (((main_fd = open(logfile.c_str(), O_RDONLY)) < 0) && --cnt) {
02260     switch (errno) {
02261     case ENOENT:
02262     case EACCES:
02263       sleep(5);
02264       break;
02265     default:
02266       status.append(" can't open squid.blog");
02267       return -1;
02268     }
02269   }
02270 
02271   if (main_fd < 0) {
02272     status.append(" squid.blog not enabled");
02273     return -1;
02274   }
02275 #if HAVE_POSIX_FADVISE
02276   posix_fadvise(main_fd, 0, 0, POSIX_FADV_DONTNEED);
02277 #endif
02278   return main_fd;
02279 }
02280 
02281 
02282 
02283 ///////////////////////////////////////////////////////////////////////////////
02284 // main
02285 int
02286 main(int /* argc ATS_UNUSED */, char *argv[])
02287 {
02288   ExitStatus exit_status;
02289   int res, cnt;
02290   int main_fd;
02291   unsigned max_age;
02292   struct flock lck;
02293 
02294   // build the application information structure
02295   appVersionInfo.setup(PACKAGE_NAME,PROGRAM_NAME, PACKAGE_VERSION, __DATE__, __TIME__,
02296                        BUILD_MACHINE, BUILD_PERSON, "");
02297 
02298   // Before accessing file system initialize Layout engine
02299   Layout::create();
02300 
02301   memset(&totals, 0, sizeof(totals));
02302   init_elapsed(&totals);
02303 
02304   origin_set = new OriginSet;
02305   parse_errors = 0;
02306 
02307   // Command line parsing
02308   cl.parse_arguments(argv);
02309 
02310   // Calculate the max age of acceptable log entries, if necessary
02311   if (cl.max_age > 0) {
02312     struct timeval tv;
02313 
02314     gettimeofday(&tv, NULL);
02315     max_age = tv.tv_sec - cl.max_age;
02316   } else {
02317     max_age = 0;
02318   }
02319 
02320   // initialize this application for standalone logging operation
02321   init_log_standalone_basic(PROGRAM_NAME);
02322   Log::init(Log::NO_REMOTE_MANAGEMENT | Log::LOGCAT);
02323 
02324   // Do we have a list of Origins on the command line?
02325   if (cl.origin_list[0] != '\0') {
02326     char *tok;
02327     char *sep_ptr;
02328 
02329     for (tok = strtok_r(cl.origin_list, ",", &sep_ptr); tok != NULL;) {
02330       origin_set->insert(tok);
02331       tok = strtok_r(NULL, ",", &sep_ptr);
02332     }
02333   }
02334   // Load origins from an "external" file (\n separated)
02335   if (cl.origin_file[0] != '\0') {
02336     std::ifstream fs;
02337 
02338     fs.open(cl.origin_file, std::ios::in);
02339     if (!fs.is_open()) {
02340       std::cerr << "can't read " << cl.origin_file << std::endl;
02341       usage(argument_descriptions, countof(argument_descriptions), USAGE_LINE);
02342       _exit(0);
02343     }
02344 
02345     while (!fs.eof()) {
02346       std::string line;
02347       std::string::size_type start, end;
02348 
02349       getline(fs, line);
02350       start = line.find_first_not_of(" \t");
02351       if (start != std::string::npos) {
02352         end = line.find_first_of(" \t#/");
02353         if (std::string::npos == end)
02354           end = line.length();
02355 
02356         if (end > start) {
02357           char *buf;
02358 
02359           buf = ats_strdup(line.substr(start, end).c_str());
02360           if (buf) {
02361             origin_set->insert(buf);
02362           }
02363         }
02364       }
02365     }
02366   }
02367 
02368   // Produce the CGI header first (if applicable)
02369   if (cl.cgi) {
02370     std::cout << "Content-Type: application/javascript\r\n";
02371     std::cout << "Cache-Control: no-cache\r\n\r\n";
02372   }
02373 
02374   // Should we calculate per URL data;
02375   if (cl.urls != 0) {
02376     urls = new UrlLru(cl.urls, cl.show_urls);
02377     if (cl.as_object)
02378       std::cout << "{" << std::endl;
02379     else
02380       std::cout << "[" << std::endl;
02381   }
02382 
02383   // Do the incremental parse of the default squid log.
02384   if (cl.incremental) {
02385     // Change directory to the log dir
02386     if (chdir(Layout::get()->logdir) < 0) {
02387       exit_status.set(EXIT_CRITICAL, " can't chdir to ");
02388       exit_status.append(Layout::get()->logdir);
02389       my_exit(exit_status);
02390     }
02391 
02392     std::string sf_name(Layout::get()->logdir);
02393     struct stat stat_buf;
02394     int state_fd;
02395     sf_name.append("/logstats.state");
02396 
02397     if (cl.state_tag[0] != '\0') {
02398       sf_name.append(".");
02399       sf_name.append(cl.state_tag);
02400     } else {
02401       // Default to the username
02402       struct passwd *pwd = getpwuid(geteuid());
02403 
02404       if (pwd) {
02405         sf_name.append(".");
02406         sf_name.append(pwd->pw_name);
02407       } else {
02408         exit_status.set(EXIT_CRITICAL, " can't get current UID");
02409         my_exit(exit_status);
02410       }
02411     }
02412 
02413     if ((state_fd = open(sf_name.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)) < 0) {
02414       exit_status.set(EXIT_CRITICAL, " can't open state file ");
02415       exit_status.append(sf_name);
02416       my_exit(exit_status);
02417     }
02418     // Get an exclusive lock, if possible. Try for up to 20 seconds.
02419     // Use more portable & standard fcntl() over flock()
02420     lck.l_type = F_WRLCK;
02421     lck.l_whence = 0; /* offset l_start from beginning of file*/
02422     lck.l_start = (off_t)0;
02423     lck.l_len = (off_t)0; /* till end of file*/
02424     cnt = 10;
02425     while (((res = fcntl(state_fd, F_SETLK, &lck)) < 0) && --cnt) {
02426       switch (errno) {
02427       case EWOULDBLOCK:
02428       case EINTR:
02429         sleep(2);
02430         break;
02431       default:
02432         exit_status.set(EXIT_CRITICAL, " locking failure");
02433         my_exit(exit_status);
02434         break;
02435       }
02436     }
02437 
02438     if (res < 0) {
02439       exit_status.set(EXIT_CRITICAL, " can't lock state file");
02440       my_exit(exit_status);
02441     }
02442     // Fetch previous state information, allow for concurrent accesses.
02443     cnt = 10;
02444     while (((res = read(state_fd, &last_state, sizeof(last_state))) < 0) && --cnt) {
02445       switch (errno) {
02446       case EINTR:
02447       case EAGAIN:
02448         sleep(1);
02449         break;
02450       default:
02451         exit_status.set(EXIT_CRITICAL, " can't read state file");
02452         my_exit(exit_status);
02453         break;
02454       }
02455     }
02456 
02457     if (res != sizeof(last_state)) {
02458       // First time / empty file, so reset.
02459       last_state.offset = 0;
02460       last_state.st_ino = 0;
02461     }
02462 
02463     if ((main_fd = open_main_log(exit_status)) < 0) {
02464       exit_status.set(EXIT_CRITICAL);
02465       my_exit(exit_status);
02466     }
02467 
02468     // Get stat's from the main log file.
02469     if (fstat(main_fd, &stat_buf) < 0) {
02470       exit_status.set(EXIT_CRITICAL, " can't stat squid.blog");
02471       my_exit(exit_status);
02472     }
02473     // Make sure the last_state.st_ino is sane.
02474     if (last_state.st_ino <= 0)
02475       last_state.st_ino = stat_buf.st_ino;
02476 
02477     // Check if the main log file was rotated, and if so, locate
02478     // the old file first, and parse the remaining log data.
02479     if (stat_buf.st_ino != last_state.st_ino) {
02480       DIR *dirp = NULL;
02481       struct dirent *dp = NULL;
02482       ino_t old_inode = last_state.st_ino;
02483 
02484       // Save the current log-file's I-Node number.
02485       last_state.st_ino = stat_buf.st_ino;
02486 
02487       // Find the old log file.
02488       dirp = opendir(Layout::get()->logdir);
02489       if (NULL == dirp) {
02490         exit_status.set(EXIT_WARNING, " can't read log directory");
02491       } else {
02492         while ((dp = readdir(dirp)) != NULL) {
02493           if (stat(dp->d_name, &stat_buf) < 0) {
02494             exit_status.set(EXIT_WARNING, " can't stat ");
02495             exit_status.append(dp->d_name);
02496           } else if (stat_buf.st_ino == old_inode) {
02497             int old_fd = open(dp->d_name, O_RDONLY);
02498 
02499             if (old_fd < 0) {
02500               exit_status.set(EXIT_WARNING, " can't open ");
02501               exit_status.append(dp->d_name);
02502               break;            // Don't attempt any more files
02503             }
02504             // Process it
02505             if (process_file(old_fd, last_state.offset, max_age) != 0) {
02506               exit_status.set(EXIT_WARNING, " can't read ");
02507               exit_status.append(dp->d_name);
02508             }
02509             close(old_fd);
02510             break;              // Don't attempt any more files
02511           }
02512         }
02513       }
02514       // Make sure to read from the beginning of the freshly rotated file.
02515       last_state.offset = 0;
02516     } else {
02517       // Make sure the last_state.offset is sane, stat_buf is for the main_fd.
02518       if (last_state.offset > stat_buf.st_size)
02519         last_state.offset = stat_buf.st_size;
02520     }
02521 
02522     // Process the main file (always)
02523     if (process_file(main_fd, last_state.offset, max_age) != 0) {
02524       exit_status.set(EXIT_CRITICAL, " can't parse log");
02525       last_state.offset = 0;
02526       last_state.st_ino = 0;
02527     } else {
02528       // Save the current file offset.
02529       last_state.offset = lseek(main_fd, 0, SEEK_CUR);
02530       if (last_state.offset < 0) {
02531         exit_status.set(EXIT_WARNING, " can't lseek squid.blog");
02532         last_state.offset = 0;
02533       }
02534     }
02535 
02536     // Save the state, release the lock, and close the FDs.
02537     if (lseek(state_fd, 0, SEEK_SET) < 0) {
02538       exit_status.set(EXIT_WARNING, " can't lseek state file");
02539     } else {
02540       if (-1 == write(state_fd, &last_state, sizeof(last_state))) {
02541         exit_status.set(EXIT_WARNING, " can't write state_fd ");
02542       }
02543     }
02544     //flock(state_fd, LOCK_UN);
02545     lck.l_type = F_UNLCK;
02546     fcntl(state_fd, F_SETLK, &lck);
02547     close(main_fd);
02548     close(state_fd);
02549   } else {
02550     if (cl.log_file[0] != '\0') {
02551       main_fd = open(cl.log_file, O_RDONLY);
02552       if (main_fd < 0) {
02553         exit_status.set(EXIT_CRITICAL, " can't open log file ");
02554         exit_status.append(cl.log_file);
02555         my_exit(exit_status);
02556       }
02557     } else {
02558       main_fd = open_main_log(exit_status);
02559     }
02560 
02561     if (cl.tail > 0) {
02562       if (lseek(main_fd, 0, SEEK_END) < 0) {
02563         exit_status.set(EXIT_CRITICAL, " can't lseek squid.blog");
02564         my_exit(exit_status);
02565       }
02566       sleep(cl.tail);
02567     }
02568 
02569     if (process_file(main_fd, 0, max_age) != 0) {
02570       close(main_fd);
02571       exit_status.set(EXIT_CRITICAL, " can't parse log file ");
02572       exit_status.append(cl.log_file);
02573       my_exit(exit_status);
02574     }
02575     close(main_fd);
02576   }
02577 
02578   // All done.
02579   if (EXIT_OK == exit_status.level)
02580     exit_status.append(" OK");
02581   my_exit(exit_status);
02582 }

Generated by  doxygen 1.7.1