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

LogUtils.cc

Go to the documentation of this file.
00001 /** @file
00002 
00003  This file contains a set of utility routines that are used throughout the
00004  logging implementation.
00005 
00006   @section license License
00007 
00008   Licensed to the Apache Software Foundation (ASF) under one
00009   or more contributor license agreements.  See the NOTICE file
00010   distributed with this work for additional information
00011   regarding copyright ownership.  The ASF licenses this file
00012   to you under the Apache License, Version 2.0 (the
00013   "License"); you may not use this file except in compliance
00014   with the License.  You may obtain a copy of the License at
00015 
00016       http://www.apache.org/licenses/LICENSE-2.0
00017 
00018   Unless required by applicable law or agreed to in writing, software
00019   distributed under the License is distributed on an "AS IS" BASIS,
00020   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00021   See the License for the specific language governing permissions and
00022   limitations under the License.
00023  */
00024 #include "ink_config.h"
00025 
00026 #include <assert.h>
00027 #include <stdio.h>
00028 #include <stdlib.h>
00029 #include <stdarg.h>
00030 #include <string.h>
00031 #include <time.h>
00032 
00033 #include <sys/time.h>
00034 #include <sys/types.h>
00035 #include <sys/stat.h>
00036 #include <unistd.h>
00037 #include <sys/socket.h>
00038 #include <netinet/in.h>
00039 #include <arpa/inet.h>
00040 #include <netdb.h>
00041 
00042 #include "P_RecProcess.h"
00043 // REC_SIGNAL_LOGGING_ERROR    is defined in I_RecSignals.h
00044 // REC_SIGNAL_LOGGING_WARNING  is defined in I_RecSignals.h
00045 
00046 
00047 #include "Compatability.h"
00048 
00049 #include "LogUtils.h"
00050 #include "LogLimits.h"
00051 
00052 
00053 /*-------------------------------------------------------------------------
00054   LogUtils::timestamp_to_str
00055 
00056   This routine will convert a timestamp (seconds) into a short string,
00057   of the format "%Y%m%d.%Hh%Mm%Ss".
00058 
00059   Since the resulting buffer is passed in, this routine is thread-safe.
00060   Return value is the number of characters placed into the array, not
00061   including the NULL.
00062   -------------------------------------------------------------------------*/
00063 
00064 int
00065 LogUtils::timestamp_to_str(long timestamp, char *buf, int size)
00066 {
00067   static const char *format_str = "%Y%m%d.%Hh%Mm%Ss";
00068   struct tm res;
00069   struct tm *tms;
00070   tms = ink_localtime_r((const time_t *) &timestamp, &res);
00071   return strftime(buf, size, format_str, tms);
00072 }
00073 
00074 /*-------------------------------------------------------------------------
00075   LogUtils::timestamp_to_netscape_str
00076 
00077   This routine will convert a timestamp (seconds) into a string compatible
00078   with the Netscape logging formats.
00079 
00080   This routine is intended to be called from the (single) logging thread,
00081   and is therefore NOT MULTITHREADED SAFE.  There is a single, static,
00082   string buffer that the time string is constructed into and returned.
00083   -------------------------------------------------------------------------*/
00084 
00085 char *
00086 LogUtils::timestamp_to_netscape_str(long timestamp)
00087 {
00088   static char timebuf[64];      // NOTE: not MT safe
00089   static char gmtstr[16];
00090   static long last_timestamp = 0;
00091   static char bad_time[] = "Bad timestamp";
00092 
00093   // safety check
00094   if (timestamp < 0) {
00095     return bad_time;
00096   }
00097   //
00098   // since we may have many entries per second, lets only do the
00099   // formatting if we actually have a new timestamp.
00100   //
00101 
00102   if (timestamp != last_timestamp) {
00103     //
00104     // most of this garbage is simply to find out the offset from GMT,
00105     // taking daylight savings into account.
00106     //
00107 #ifdef NEED_ALTZONE_DEFINED
00108     time_t altzone = timezone;
00109 #endif
00110     struct tm res;
00111     struct tm *tms = ink_localtime_r((const time_t *) &timestamp, &res);
00112     // TODO: Not sure this makes sense, can altzone actually be != timezone ??
00113 #ifdef NEED_ALTZONE_DEFINED
00114     long zone = (tms->tm_isdst > 0) ? altzone : timezone;
00115 #else
00116     long zone = -tms->tm_gmtoff;        // double negative!
00117 #endif
00118     int offset;
00119     char sign;
00120 
00121     if (zone >= 0) {
00122       offset = zone / 60;
00123       sign = '-';
00124     } else {
00125       offset = zone / -60;
00126       sign = '+';
00127     }
00128     int glen = snprintf(gmtstr, 16, "%c%.2d%.2d", sign, offset / 60, offset % 60);
00129 
00130     strftime(timebuf, 64 - glen, "%d/%b/%Y:%H:%M:%S ", tms);
00131     ink_strlcat(timebuf, gmtstr, sizeof(timebuf));
00132     last_timestamp = timestamp;
00133   }
00134   return timebuf;
00135 }
00136 
00137 /*-------------------------------------------------------------------------
00138   LogUtils::timestamp_to_date_str
00139 
00140   This routine will convert a timestamp (seconds) into a W3C compatible
00141   date string.
00142   -------------------------------------------------------------------------*/
00143 
00144 char *
00145 LogUtils::timestamp_to_date_str(long timestamp)
00146 {
00147   static char timebuf[64];      // NOTE: not MT safe
00148   static long last_timestamp = 0;
00149   static char bad_time[] = "Bad timestamp";
00150 
00151   // safety check
00152   if (timestamp < 0) {
00153     return bad_time;
00154   }
00155   //
00156   // since we may have many entries per second, lets only do the
00157   // formatting if we actually have a new timestamp.
00158   //
00159 
00160   if (timestamp != last_timestamp) {
00161     struct tm res;
00162     struct tm *tms = ink_localtime_r((const time_t *) &timestamp, &res);
00163     strftime(timebuf, 64, "%Y-%m-%d", tms);
00164     last_timestamp = timestamp;
00165   }
00166   return timebuf;
00167 }
00168 
00169 /*-------------------------------------------------------------------------
00170   LogUtils::timestamp_to_time_str
00171 
00172   This routine will convert a timestamp (seconds) into a W3C compatible
00173   time string.
00174   -------------------------------------------------------------------------*/
00175 
00176 char *
00177 LogUtils::timestamp_to_time_str(long timestamp)
00178 {
00179   static char timebuf[64];      // NOTE: not MT safe
00180   static long last_timestamp = 0;
00181   static char bad_time[] = "Bad timestamp";
00182 
00183   // safety check
00184   if (timestamp < 0) {
00185     return bad_time;
00186   }
00187   //
00188   // since we may have many entries per second, lets only do the
00189   // formatting if we actually have a new timestamp.
00190   //
00191 
00192   if (timestamp != last_timestamp) {
00193     struct tm res;
00194     struct tm *tms = ink_localtime_r((const time_t *) &timestamp, &res);
00195     strftime(timebuf, 64, "%H:%M:%S", tms);
00196     last_timestamp = timestamp;
00197   }
00198   return timebuf;
00199 }
00200 
00201 /*-------------------------------------------------------------------------
00202   LogUtils::manager_alarm
00203 
00204   This routine provides a convenient abstraction for sending the traffic
00205   server manager process an alarm.  The logging system can send
00206   LOG_ALARM_N_TYPES different types of alarms, as defined in LogUtils.h.
00207   Subsequent alarms of the same type will override the previous alarm
00208   entry.
00209   -------------------------------------------------------------------------*/
00210 
00211 void
00212 LogUtils::manager_alarm(LogUtils::AlarmType alarm_type, const char *msg, ...)
00213 {
00214   char msg_buf[LOG_MAX_FORMATTED_LINE];
00215   va_list ap;
00216 
00217   ink_assert(alarm_type >= 0 && alarm_type < LogUtils::LOG_ALARM_N_TYPES);
00218 
00219   if (msg == NULL) {
00220     snprintf(msg_buf, sizeof(msg_buf), "No Message");
00221   } else {
00222     va_start(ap, msg);
00223     vsnprintf(msg_buf, LOG_MAX_FORMATTED_LINE, msg, ap);
00224     va_end(ap);
00225   }
00226 
00227   switch (alarm_type) {
00228   case LogUtils::LOG_ALARM_ERROR:
00229     RecSignalManager(REC_SIGNAL_LOGGING_ERROR, msg_buf);
00230     break;
00231 
00232   case LogUtils::LOG_ALARM_WARNING:
00233     RecSignalManager(REC_SIGNAL_LOGGING_WARNING, msg_buf);
00234     break;
00235 
00236   default:
00237     ink_assert(false);
00238   }
00239 }
00240 
00241 /*-------------------------------------------------------------------------
00242   LogUtils::strip_trailing_newline
00243 
00244   This routine examines the given string buffer to see if the last
00245   character before the trailing NULL is a newline ('\n').  If so, it will
00246   be replaced with a NULL, thus stripping it and reducing the length of
00247   the string by one.
00248   -------------------------------------------------------------------------*/
00249 
00250 void
00251 LogUtils::strip_trailing_newline(char *buf)
00252 {
00253   if (buf != NULL) {
00254     int len =::strlen(buf);
00255     if (len > 0) {
00256       if (buf[len - 1] == '\n') {
00257         buf[len - 1] = '\0';
00258       }
00259     }
00260   }
00261 }
00262 
00263 /*-------------------------------------------------------------------------
00264   LogUtils::escapify_url
00265 
00266   This routine will escapify a URL to remove spaces (and perhaps other ugly
00267   characters) from a URL and replace them with a hex escape sequence.
00268   Since the escapes are larger (multi-byte) than the characters being
00269   replaced, the string returned will be longer than the string passed.
00270   -------------------------------------------------------------------------*/
00271 
00272 char *
00273 LogUtils::escapify_url(Arena *arena, char *url, size_t len_in, int *len_out, char *dst, size_t dst_size, const unsigned char *map)
00274 {
00275   // codes_to_escape is a bitmap encoding the codes that should be escaped.
00276   // These are all the codes defined in section 2.4.3 of RFC 2396
00277   // (control, space, delims, and unwise) plus the tilde. In RFC 2396
00278   // the tilde is an "unreserved" character, but we escape it because
00279   // historically this is what the traffic_server has done.
00280   // Note that we leave codes beyond 127 unmodified.
00281   //
00282   static const unsigned char codes_to_escape[32] = {
00283     0xFF, 0xFF, 0xFF, 0xFF,     // control
00284     0xB4,                       // space " # %
00285     0x00, 0x00,                 //
00286     0x0A,                       // < >
00287     0x00, 0x00, 0x00,           //
00288     0x1E, 0x80,                 // [ \ ] ^ `
00289     0x00, 0x00,                 //
00290     0x1F,                       // { | } ~ DEL
00291     0x00, 0x00, 0x00, 0x00,     // all non-ascii characters unmodified
00292     0x00, 0x00, 0x00, 0x00,     //               .
00293     0x00, 0x00, 0x00, 0x00,     //               .
00294     0x00, 0x00, 0x00, 0x00      //               .
00295   };
00296 
00297   static char hex_digit[16] = {
00298     '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C',
00299     'D', 'E', 'F'
00300   };
00301 
00302   if (!url || (dst && dst_size < len_in)) {
00303     *len_out = 0;
00304     return NULL;
00305   }
00306 
00307   if (!map)
00308     map = codes_to_escape;
00309 
00310   // Count specials in the url, assuming that there won't be any.
00311   //
00312   int count = 0;
00313   char *p = url;
00314   char *in_url_end = url + len_in;
00315 
00316   while (p < in_url_end) {
00317     unsigned char c = *p;
00318     if (map[c / 8] & (1 << (7 - c % 8))) {
00319       ++count;
00320     }
00321     ++p;
00322   }
00323 
00324   if (!count) {
00325     // The common case, no escapes, so just return the source string.
00326     //
00327     *len_out = len_in;
00328     if (dst)
00329       ink_strlcpy(dst, url, dst_size);
00330     return url;
00331   }
00332 
00333   // For each special char found, we'll need an escape string, which is
00334   // three characters long.  Count this and allocate the string required.
00335   //
00336   // make sure we take into account the characters we are substituting
00337   // for when we calculate out_len !!! in other words,
00338   // out_len = len_in + 3*count - count
00339   //
00340   size_t out_len = len_in + 2 * count;
00341 
00342   if (dst && out_len > dst_size) {
00343     *len_out = 0;
00344     return NULL;
00345   }
00346 
00347   // To play it safe, we null terminate the string we return in case
00348   // a module that expects null-terminated strings calls escapify_url,
00349   // so we allocate an extra byte for the EOS
00350   //
00351   char *new_url;
00352 
00353   if (dst)
00354     new_url = dst;
00355   else
00356     new_url = (char *) arena->str_alloc(out_len + 1);
00357 
00358   char *from = url;
00359   char *to = new_url;
00360 
00361   while (from < in_url_end) {
00362     unsigned char c = *from;
00363     if (map[c / 8] & (1 << (7 - c % 8))) {
00364       *to++ = '%';
00365       *to++ = hex_digit[c / 16];
00366       *to++ = hex_digit[c % 16];
00367     } else {
00368       *to++ = *from;
00369     }
00370     from++;
00371   }
00372   *to = '\0';                      // null terminate string
00373 
00374   *len_out = out_len;
00375   return new_url;
00376 }
00377 
00378 /*-------------------------------------------------------------------------
00379   LogUtils::remove_content_type_attributes
00380 
00381   HTTP allows content types to have attributes following the main type and
00382   subtype.  For example, attributes of text/html might be charset=iso-8859.
00383   The content type attributes are not logged, so this function strips them
00384   from the given buffer, if present.
00385   -------------------------------------------------------------------------*/
00386 
00387 void
00388 LogUtils::remove_content_type_attributes(char *type_str, int *type_len)
00389 {
00390   if (!type_str) {
00391     *type_len = 0;
00392     return;
00393   }
00394   // Look for a semicolon and cut out everything after that
00395   //
00396   char *p = (char *) memchr(type_str, ';', *type_len);
00397   if (p) {
00398     *type_len = p - type_str;
00399   }
00400 }
00401 
00402 
00403 /*-------------------------------------------------------------------------
00404   LogUtils::timestamp_to_hex_str
00405 
00406   This routine simply writes the given timestamp integer [time_t] in the equivalent
00407   hexadecimal string format "xxxxxxxxxx" into the provided buffer [buf] of
00408   size [bufLen].
00409 
00410   It returns 1 if the provided buffer is not big enough to hold the
00411   equivalent ip string (and its null terminator), and 0 otherwise.
00412   If the buffer is not big enough, only the ip "segments" that completely
00413   fit into it are written, and the buffer is null terminated.
00414   -------------------------------------------------------------------------*/
00415 
00416 int
00417 LogUtils::timestamp_to_hex_str(unsigned ip, char *buf, size_t bufLen, size_t * numCharsPtr)
00418 {
00419   static const char *table = "0123456789abcdef@";
00420   int retVal = 1;
00421   int shift = 28;
00422   if (buf && bufLen > 0) {
00423     if (bufLen > 8)
00424       bufLen = 8;
00425     for (retVal = 0; retVal < (int) bufLen;) {
00426       buf[retVal++] = (char) table[((ip >> shift) & 0xf)];
00427       shift -= 4;
00428     }
00429 
00430     if (numCharsPtr) {
00431       *numCharsPtr = (size_t) retVal;
00432     }
00433     retVal = (retVal == 8) ? 0 : 1;
00434   }
00435   return retVal;
00436 }
00437 
00438 /*
00439 int
00440 LogUtils::ip_to_str (unsigned ip, char *str, unsigned len)
00441 {
00442     int ret = snprintf (str, len, "%u.%u.%u.%u",
00443                             (ip >> 24) & 0xff,
00444                             (ip >> 16) & 0xff,
00445                             (ip >> 8)  & 0xff,
00446                             ip         & 0xff);
00447 
00448     return ((ret <= (int)len)? ret : (int)len);
00449 }
00450 */
00451 
00452 // return the seconds remaining until the time of the next roll given
00453 // the current time, the rolling offset, and the rolling interval
00454 //
00455 int
00456 LogUtils::seconds_to_next_roll(time_t time_now, int rolling_offset, int rolling_interval)
00457 {
00458   struct tm lt;
00459   ink_localtime_r((const time_t *) &time_now, &lt);
00460   int sidl = lt.tm_sec + lt.tm_min * 60 + lt.tm_hour * 3600;
00461   int tr = rolling_offset * 3600;
00462   return ((tr >= sidl ? (tr - sidl) % rolling_interval : (86400 - (sidl - tr)) % rolling_interval));
00463 }
00464 
00465 
00466 // Checks if the file pointed to by full_filename either is a regular
00467 // file or a pipe and has write permission, or, if the file does not
00468 // exist, if the path prefix of full_filename names a directory that
00469 // has both execute and write permissions, so there will be no problem
00470 // creating the file. If the size_bytes pointer is not NULL, it returns
00471 // the size of the file through it.
00472 // Also checks the current size limit for the file. If there is a
00473 // limit and has_size_limit is not null, *has_size_limit is set to
00474 // true. If there is no limit and has_size_limit is not null,
00475 // *has_size_limit is set to false.  If there is a limit and if the
00476 // current_size_limit_bytes pointer is not null, it returns the limit
00477 // through it.
00478 //
00479 // returns:
00480 //  0 on success
00481 // -1 on system error (no permission, etc.)
00482 //  1 if the file full_filename points to is neither a regular file
00483 //    nor a pipe
00484 //
00485 int
00486 LogUtils::file_is_writeable(const char *full_filename,
00487                             off_t * size_bytes, bool * has_size_limit, uint64_t * current_size_limit_bytes)
00488 {
00489   int ret_val = 0;
00490   int e;
00491   struct stat stat_data;
00492 
00493   e = stat(full_filename, &stat_data);
00494   if (e == 0) {
00495     // stat succeeded, check if full_filename points to a regular
00496     // file/fifo and if so, check if file has write permission
00497     //
00498     if (!(stat_data.st_mode & S_IFREG || stat_data.st_mode & S_IFIFO)) {
00499       ret_val = 1;
00500     } else if (!(stat_data.st_mode & S_IWUSR)) {
00501       errno = EACCES;
00502       ret_val = -1;
00503     }
00504     if (size_bytes)
00505       *size_bytes = stat_data.st_size;
00506   } else {
00507     // stat failed
00508     //
00509     if (errno != ENOENT) {
00510       // can't stat file
00511       //
00512       ret_val = -1;
00513     } else {
00514       // file does not exist, check that the prefix is a directory with
00515       // write and execute permissions
00516 
00517       char *dir;
00518       char *prefix = 0;
00519 
00520       // search for forward or reverse slash in full_filename
00521       // starting from the end
00522       //
00523       const char *slash = strrchr(full_filename, '/');
00524       if (slash) {
00525         size_t prefix_len = slash - full_filename + 1;
00526         prefix = new char[prefix_len + 1];
00527         memcpy(prefix, full_filename, prefix_len);
00528         prefix[prefix_len] = 0;
00529         dir = prefix;
00530       } else {
00531         dir = (char *) ".";     // full_filename has no prefix, use .
00532       }
00533 
00534       // check if directory is executable and writeable
00535       //
00536       e = access(dir, X_OK | W_OK);
00537       if (e < 0) {
00538         ret_val = -1;
00539       } else {
00540         if (size_bytes)
00541           *size_bytes = 0;
00542       }
00543 
00544       if (prefix) {
00545         delete[]prefix;
00546       }
00547     }
00548   }
00549 
00550   // check for the current filesize limit
00551   //
00552   if (ret_val == 0) {
00553     struct rlimit limit_data;
00554     e = getrlimit(RLIMIT_FSIZE, &limit_data);
00555     if (e < 0) {
00556       ret_val = -1;
00557     } else {
00558       if (limit_data.rlim_cur != (rlim_t)RLIM_INFINITY) {
00559         if (has_size_limit)
00560           *has_size_limit = true;
00561         if (current_size_limit_bytes)
00562           *current_size_limit_bytes = limit_data.rlim_cur;
00563       } else {
00564         if (has_size_limit)
00565           *has_size_limit = false;
00566       }
00567     }
00568   }
00569 
00570   return ret_val;
00571 }

Generated by  doxygen 1.7.1