1 /** @file
2 
3   A brief file description
4 
5   @section license License
6 
7   Licensed to the Apache Software Foundation (ASF) under one
8   or more contributor license agreements.  See the NOTICE file
9   distributed with this work for additional information
10   regarding copyright ownership.  The ASF licenses this file
11   to you under the Apache License, Version 2.0 (the
12   "License"); you may not use this file except in compliance
13   with the License.  You may obtain a copy of the License at
14 
15       http://www.apache.org/licenses/LICENSE-2.0
16 
17   Unless required by applicable law or agreed to in writing, software
18   distributed under the License is distributed on an "AS IS" BASIS,
19   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20   See the License for the specific language governing permissions and
21   limitations under the License.
22  */
23 
24 /***************************************************************************
25  LogFile.cc
26 
27 
28  ***************************************************************************/
29 
30 #include <vector>
31 #include <string>
32 #include <algorithm>
33 
34 #include "tscore/ink_platform.h"
35 #include "tscore/SimpleTokenizer.h"
36 #include "tscore/ink_file.h"
37 
38 #include <cerrno>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <fcntl.h>
42 #include <libgen.h>
43 
44 #include "P_EventSystem.h"
45 #include "I_Machine.h"
46 
47 #include "tscore/BaseLogFile.h"
48 #include "LogField.h"
49 #include "LogFilter.h"
50 #include "LogFormat.h"
51 #include "LogBuffer.h"
52 #include "LogFile.h"
53 #include "LogObject.h"
54 #include "LogUtils.h"
55 #include "LogConfig.h"
56 #include "Log.h"
57 
58 /*-------------------------------------------------------------------------
59   LogFile::LogFile
60 
61   This constructor builds a LogFile object given the path, filename, header,
62   and logfile format type.  This is the common way to create a new logfile.
63   -------------------------------------------------------------------------*/
64 
LogFile(const char * name,const char * header,LogFileFormat format,uint64_t signature,size_t ascii_buffer_size,size_t max_line_size,int pipe_buffer_size)65 LogFile::LogFile(const char *name, const char *header, LogFileFormat format, uint64_t signature, size_t ascii_buffer_size,
66                  size_t max_line_size, int pipe_buffer_size)
67   : m_file_format(format),
68     m_name(ats_strdup(name)),
69     m_header(ats_strdup(header)),
70     m_signature(signature),
71     m_max_line_size(max_line_size),
72     m_pipe_buffer_size(pipe_buffer_size)
73 {
74   if (m_file_format != LOG_FILE_PIPE) {
75     m_log = new BaseLogFile(name, m_signature);
76     m_log->set_hostname(Machine::instance()->hostname);
77   } else {
78     m_log = nullptr;
79   }
80 
81   m_fd                = -1;
82   m_ascii_buffer_size = (ascii_buffer_size < max_line_size ? max_line_size : ascii_buffer_size);
83 
84   Debug("log-file", "exiting LogFile constructor, m_name=%s, this=%p", m_name, this);
85 }
86 
87 /*-------------------------------------------------------------------------
88   LogFile::LogFile
89 
90   This (copy) constructor builds a LogFile object from another LogFile object.
91   -------------------------------------------------------------------------*/
92 
LogFile(const LogFile & copy)93 LogFile::LogFile(const LogFile &copy)
94   : RefCountObj(copy),
95     m_file_format(copy.m_file_format),
96     m_name(ats_strdup(copy.m_name)),
97     m_header(ats_strdup(copy.m_header)),
98     m_signature(copy.m_signature),
99     m_ascii_buffer_size(copy.m_ascii_buffer_size),
100     m_max_line_size(copy.m_max_line_size),
101     m_pipe_buffer_size(copy.m_pipe_buffer_size),
102     m_fd(copy.m_fd)
103 {
104   ink_release_assert(m_ascii_buffer_size >= m_max_line_size);
105 
106   if (copy.m_log) {
107     m_log = new BaseLogFile(*(copy.m_log));
108   } else {
109     m_log = nullptr;
110   }
111 
112   Debug("log-file", "exiting LogFile copy constructor, m_name=%s, this=%p", m_name, this);
113 }
114 /*-------------------------------------------------------------------------
115   LogFile::~LogFile
116   -------------------------------------------------------------------------*/
117 
~LogFile()118 LogFile::~LogFile()
119 {
120   Debug("log-file", "entering LogFile destructor, this=%p", this);
121 
122   // close_file() checks whether a file is open before attempting to close, so
123   // this is safe to call even if a file had not been opened. Further, calling
124   // close_file() here ensures that we do not leak file descriptors.
125   close_file();
126 
127   delete m_log;
128   ats_free(m_header);
129   ats_free(m_name);
130   Debug("log-file", "exiting LogFile destructor, this=%p", this);
131 }
132 
133 /*-------------------------------------------------------------------------
134   LogFile::change_name
135   -------------------------------------------------------------------------*/
136 
137 void
change_name(const char * new_name)138 LogFile::change_name(const char *new_name)
139 {
140   ats_free(m_name);
141   if (m_log) {
142     m_log->change_name(new_name);
143   }
144   m_name = ats_strdup(new_name);
145 }
146 
147 /*-------------------------------------------------------------------------
148   LogFile::change_header
149   -------------------------------------------------------------------------*/
150 
151 void
change_header(const char * header)152 LogFile::change_header(const char *header)
153 {
154   ats_free(m_header);
155   m_header = ats_strdup(header);
156 }
157 
158 /*-------------------------------------------------------------------------
159   LogFile::open
160 
161   Open the logfile for append access.  This will create a logfile if the
162   file does not already exist.
163   -------------------------------------------------------------------------*/
164 
165 int
open_file()166 LogFile::open_file()
167 {
168   // whatever we want to open should have a name
169   ink_assert(m_name != nullptr);
170 
171   // is_open() takes into account if we're using BaseLogFile or a naked fd
172   if (is_open()) {
173     return LOG_FILE_NO_ERROR;
174   }
175 
176   bool file_exists = LogFile::exists(m_name);
177 
178   if (m_file_format == LOG_FILE_PIPE) {
179     // setup pipe
180     if (mkfifo(m_name, S_IRUSR | S_IWUSR | S_IRGRP) < 0) {
181       if (errno != EEXIST) {
182         Error("Could not create named pipe %s for logging: %s", m_name, strerror(errno));
183         return LOG_FILE_COULD_NOT_CREATE_PIPE;
184       }
185     } else {
186       Debug("log-file", "Created named pipe %s for logging", m_name);
187     }
188 
189     // now open the pipe
190     Debug("log-file", "attempting to open pipe %s", m_name);
191     m_fd = ::open(m_name, O_WRONLY | O_NDELAY, 0);
192     if (m_fd < 0) {
193       Debug("log-file", "no readers for pipe %s", m_name);
194       return LOG_FILE_NO_PIPE_READERS;
195     }
196 
197 #ifdef F_GETPIPE_SZ
198     // adjust pipe size if necessary
199     if (m_pipe_buffer_size) {
200       long pipe_size = static_cast<long>(fcntl(m_fd, F_GETPIPE_SZ));
201       if (pipe_size == -1) {
202         Error("Get pipe size failed for pipe %s: %s", m_name, strerror(errno));
203       } else {
204         Debug("log-file", "Previous buffer size for pipe %s: %ld", m_name, pipe_size);
205       }
206 
207       int ret = fcntl(m_fd, F_SETPIPE_SZ, m_pipe_buffer_size);
208       if (ret == -1) {
209         Error("Set pipe size failed for pipe %s to size %d: %s", m_name, m_pipe_buffer_size, strerror(errno));
210       }
211 
212       pipe_size = static_cast<long>(fcntl(m_fd, F_GETPIPE_SZ));
213       if (pipe_size == -1) {
214         Error("Get pipe size after setting it failed for pipe %s: %s", m_name, strerror(errno));
215       } else {
216         Debug("log-file", "New buffer size for pipe %s: %ld", m_name, pipe_size);
217       }
218     }
219 #endif // F_GETPIPE_SZ
220   } else {
221     if (m_log) {
222       int status = m_log->open_file(Log::config->logfile_perm);
223       if (status == BaseLogFile::LOG_FILE_COULD_NOT_OPEN_FILE) {
224         return LOG_FILE_COULD_NOT_OPEN_FILE;
225       }
226     } else {
227       return LOG_FILE_COULD_NOT_OPEN_FILE;
228     }
229   }
230 
231   //
232   // If we've opened the file and it didn't already exist, then this is a
233   // "new" file and we need to make some initializations.  This is the
234   // time to write any headers and do any one-time initialization of the
235   // file.
236   //
237   if (!file_exists) {
238     if (m_file_format != LOG_FILE_BINARY && m_header && m_log) {
239       Debug("log-file", "writing header to LogFile %s", m_name);
240       writeln(m_header, strlen(m_header), fileno(m_log->m_fp), m_name);
241     }
242   }
243 
244   RecIncrRawStat(log_rsb, this_thread()->mutex->thread_holding, log_stat_log_files_open_stat, 1);
245 
246   Debug("log", "exiting LogFile::open_file(), file=%s presumably open", m_name);
247   return LOG_FILE_NO_ERROR;
248 }
249 
250 /*-------------------------------------------------------------------------
251   LogFile::close
252 
253   Close the current logfile.
254   -------------------------------------------------------------------------*/
255 
256 void
close_file()257 LogFile::close_file()
258 {
259   if (is_open()) {
260     if (m_file_format == LOG_FILE_PIPE) {
261       if (::close(m_fd)) {
262         Error("Error closing LogFile %s: %s.", m_name, strerror(errno));
263       } else {
264         Debug("log-file", "LogFile %s (fd=%d) is closed", m_name, m_fd);
265         RecIncrRawStat(log_rsb, this_thread()->mutex->thread_holding, log_stat_log_files_open_stat, -1);
266       }
267       m_fd = -1;
268     } else if (m_log) {
269       if (m_log->close_file()) {
270         Error("Error closing LogFile %s: %s.", m_log->get_name(), strerror(errno));
271       } else {
272         Debug("log-file", "LogFile %s is closed", m_log->get_name());
273         RecIncrRawStat(log_rsb, this_thread()->mutex->thread_holding, log_stat_log_files_open_stat, -1);
274       }
275     } else {
276       Warning("LogFile %s is open but was not closed", m_name);
277     }
278   }
279 }
280 
281 struct RolledFile {
RolledFileRolledFile282   RolledFile(const std::string &name, time_t mtime) : _name(name), _mtime(mtime) {}
283   std::string _name;
284   time_t _mtime;
285 };
286 
287 /**
288  * @brief trim rolled files to max number of rolled files, older first
289  *
290  * @param rolling_max_count - limit to which rolled files will be trimmed
291  * @return true if success, false if failure
292  */
293 bool
trim_rolled(size_t rolling_max_count)294 LogFile::trim_rolled(size_t rolling_max_count)
295 {
296   /* man: "dirname() may modify the contents of path, so it may be
297    * desirable to pass a copy when calling one of these functions." */
298   char *name = ats_strdup(m_name);
299   std::string logfile_dir(::dirname((name)));
300   ats_free(name);
301 
302   /* Check logging directory access */
303   int err;
304   do {
305     err = access(logfile_dir.c_str(), R_OK | W_OK | X_OK);
306   } while ((err < 0) && (errno == EINTR));
307 
308   if (err < 0) {
309     Error("Error accessing logging directory %s: %s.", logfile_dir.c_str(), strerror(errno));
310     return false;
311   }
312 
313   /* Open logging directory */
314   DIR *ld = ::opendir(logfile_dir.c_str());
315   if (ld == nullptr) {
316     Error("Error opening logging directory %s to collect trim candidates: %s.", logfile_dir.c_str(), strerror(errno));
317     return false;
318   }
319 
320   /* Collect the rolled file names from the logging directory that match the specified log file name */
321   std::vector<RolledFile> rolled;
322   char path[MAXPATHLEN];
323   struct dirent *entry;
324   while ((entry = readdir(ld))) {
325     struct stat sbuf;
326     snprintf(path, MAXPATHLEN, "%s/%s", logfile_dir.c_str(), entry->d_name);
327     int sret = ::stat(path, &sbuf);
328     if (sret != -1 && S_ISREG(sbuf.st_mode)) {
329       int name_len = strlen(m_name);
330       int path_len = strlen(path);
331       if (path_len > name_len && 0 == ::strncmp(m_name, path, name_len) && LogFile::rolled_logfile(entry->d_name)) {
332         rolled.push_back(RolledFile(path, sbuf.st_mtime));
333       }
334     }
335   }
336 
337   ::closedir(ld);
338 
339   bool result = true;
340   std::sort(rolled.begin(), rolled.end(), [](const RolledFile &a, const RolledFile &b) { return a._mtime > b._mtime; });
341   if (rolling_max_count < rolled.size()) {
342     for (auto it = rolled.begin() + rolling_max_count; it != rolled.end(); it++) {
343       const RolledFile &file = *it;
344       if (unlink(file._name.c_str()) < 0) {
345         Error("unable to auto-delete rolled logfile %s: %s.", file._name.c_str(), strerror(errno));
346         result = false;
347       } else {
348         Debug("log-file", "rolled logfile, %s, was auto-deleted", file._name.c_str());
349       }
350     }
351   }
352 
353   rolled.clear();
354   return result;
355 }
356 
357 /*-------------------------------------------------------------------------
358  * LogFile::roll
359  * This function is called by a LogObject to roll its files.
360  *
361  * Return 1 if file rolled, 0 otherwise
362 -------------------------------------------------------------------------*/
363 int
roll(long interval_start,long interval_end,bool reopen_after_rolling)364 LogFile::roll(long interval_start, long interval_end, bool reopen_after_rolling)
365 {
366   if (m_log) {
367     // Due to commit 346b419 the BaseLogFile::close_file() is no longer called within BaseLogFile::roll().
368     // For diagnostic log files, the rolling is implemented by renaming and destroying the BaseLogFile object
369     // and then creating a new BaseLogFile object with the original filename. Due to possible race conditions
370     // the old/new object swap happens within lock/unlock calls within Diags.cc.
371     // For logging log files, the rolling is implemented by renaming the original file and closing it.
372     // Afterwards, the LogFile object will re-open a new file with the original file name using the original object.
373     // There is no need to protect against contention since the open/close/writes are all executed under a
374     // single log flush thread.
375     // Since these two methods of using BaseLogFile are not compatible, we perform the logging log file specific
376     // close file operation here within the containing LogFile object.
377     if (m_log->roll(interval_start, interval_end)) {
378       if (m_log->close_file()) {
379         Error("Error closing LogFile %s: %s.", m_log->get_name(), strerror(errno));
380       }
381 
382       if (reopen_after_rolling) {
383         /* If we re-open now log file will be created even if there is nothing being logged */
384         m_log->open_file();
385       }
386 
387       return 1;
388     }
389   }
390 
391   return 0;
392 }
393 
394 /*-------------------------------------------------------------------------
395   LogFile::reopen_if_moved
396 
397   Check whether the file at the log's filename exists and, if not, close
398   the current file descriptor and reopen it. This function can be used to
399   facilitate external log rotation mechanisms which will move the original
400   file to a rolled filename. Logging will happen to that same file
401   descriptor until this function is called, at which point the non-existent
402   original file will be detected, the file descriptor will be closed, and
403   the log file will be re-opened.
404 
405   Returns True if the file was re-opened, false otherwise.
406   -------------------------------------------------------------------------*/
407 bool
reopen_if_moved()408 LogFile::reopen_if_moved()
409 {
410   if (!m_name) {
411     return false;
412   }
413   if (LogFile::exists(m_name)) {
414     return false;
415   }
416 
417   // Both of the following log if there are problems.
418   close_file();
419   open_file();
420   return true;
421 }
422 
423 /*-------------------------------------------------------------------------
424   LogFile::preproc_and_try_delete
425 
426   preprocess the given buffer data before write to target file
427   and try to delete it when its reference become zero.
428   -------------------------------------------------------------------------*/
429 int
preproc_and_try_delete(LogBuffer * lb)430 LogFile::preproc_and_try_delete(LogBuffer *lb)
431 {
432   int ret = -1;
433   LogBufferHeader *buffer_header;
434 
435   if (lb == nullptr) {
436     Note("Cannot write LogBuffer to LogFile %s; LogBuffer is NULL", m_name);
437     return -1;
438   }
439 
440   ink_atomic_increment(&lb->m_references, 1);
441 
442   if ((buffer_header = lb->header()) == nullptr) {
443     Note("Cannot write LogBuffer to LogFile %s; LogBufferHeader is NULL", m_name);
444     goto done;
445   }
446   if (buffer_header->entry_count == 0) {
447     // no bytes to write
448     Note("LogBuffer with 0 entries for LogFile %s, nothing to write", m_name);
449     goto done;
450   }
451 
452   //
453   // If the start time for this file has yet to be established, then grab
454   // the low_timestamp from the given LogBuffer.  Then, we always set the
455   // end time to the high_timestamp, so it's always up to date.
456   //
457   if (m_log) {
458     if (!m_log->m_start_time) {
459       m_log->m_start_time = buffer_header->low_timestamp;
460     }
461     m_log->m_end_time = buffer_header->high_timestamp;
462   }
463 
464   if (m_file_format == LOG_FILE_BINARY) {
465     //
466     // Ok, now we need to write the binary buffer to the file, and we
467     // can do so in one swift write.  The question is, do we write the
468     // LogBufferHeader with each buffer or not?  The answer is yes.
469     // Even though we'll be puttint down redundant data (things that
470     // don't change between buffers), it's not worth trying to separate
471     // out the buffer-dependent data from the buffer-independent data.
472     //
473     LogFlushData *flush_data = new LogFlushData(this, lb);
474 
475     ProxyMutex *mutex = this_thread()->mutex.get();
476 
477     RecIncrRawStat(log_rsb, mutex->thread_holding, log_stat_num_flush_to_disk_stat, lb->header()->entry_count);
478 
479     RecIncrRawStat(log_rsb, mutex->thread_holding, log_stat_bytes_flush_to_disk_stat, lb->header()->byte_count);
480 
481     ink_atomiclist_push(Log::flush_data_list, flush_data);
482 
483     Log::flush_notify->signal();
484 
485     //
486     // LogBuffer will be deleted in flush thread
487     //
488     return 0;
489   } else if (m_file_format == LOG_FILE_ASCII || m_file_format == LOG_FILE_PIPE) {
490     write_ascii_logbuffer3(buffer_header);
491     ret = 0;
492   } else {
493     Note("Cannot write LogBuffer to LogFile %s; invalid file format: %d", m_name, m_file_format);
494   }
495 
496 done:
497   LogBuffer::destroy(lb);
498   return ret;
499 }
500 
501 /*-------------------------------------------------------------------------
502   LogFile::write_ascii_logbuffer
503 
504   This routine takes the given LogBuffer and writes it (in ASCII) to the
505   given file descriptor.  Written as a stand-alone function, it can be
506   called from either the local LogBuffer::write routine from inside of the
507   proxy, or from an external program (since it is a static function).  The
508   return value is the number of bytes written.
509   -------------------------------------------------------------------------*/
510 
511 int
write_ascii_logbuffer(LogBufferHeader * buffer_header,int fd,const char * path,const char * alt_format)512 LogFile::write_ascii_logbuffer(LogBufferHeader *buffer_header, int fd, const char *path, const char *alt_format)
513 {
514   ink_assert(buffer_header != nullptr);
515   ink_assert(fd >= 0);
516 
517   char fmt_buf[LOG_MAX_FORMATTED_BUFFER];
518   char fmt_line[LOG_MAX_FORMATTED_LINE];
519   LogBufferIterator iter(buffer_header);
520   LogEntryHeader *entry_header;
521   int fmt_buf_bytes  = 0;
522   int fmt_line_bytes = 0;
523   int bytes          = 0;
524 
525   LogFormatType format_type;
526   char *fieldlist_str;
527   char *printf_str;
528 
529   switch (buffer_header->version) {
530   case LOG_SEGMENT_VERSION:
531     format_type = static_cast<LogFormatType>(buffer_header->format_type);
532 
533     fieldlist_str = buffer_header->fmt_fieldlist();
534     printf_str    = buffer_header->fmt_printf();
535     break;
536 
537   default:
538     Note("Invalid LogBuffer version %d in write_ascii_logbuffer; "
539          "current version is %d",
540          buffer_header->version, LOG_SEGMENT_VERSION);
541     return 0;
542   }
543 
544   while ((entry_header = iter.next())) {
545     fmt_line_bytes = LogBuffer::to_ascii(entry_header, format_type, &fmt_line[0], LOG_MAX_FORMATTED_LINE, fieldlist_str, printf_str,
546                                          buffer_header->version, alt_format);
547     ink_assert(fmt_line_bytes > 0);
548 
549     if (fmt_line_bytes > 0) {
550       if ((fmt_line_bytes + fmt_buf_bytes) >= LOG_MAX_FORMATTED_BUFFER) {
551         if (!Log::config->logging_space_exhausted) {
552           bytes += writeln(fmt_buf, fmt_buf_bytes, fd, path);
553         }
554         fmt_buf_bytes = 0;
555       }
556       ink_assert(fmt_buf_bytes < LOG_MAX_FORMATTED_BUFFER);
557       ink_assert(fmt_line_bytes < LOG_MAX_FORMATTED_BUFFER - fmt_buf_bytes);
558       memcpy(&fmt_buf[fmt_buf_bytes], fmt_line, fmt_line_bytes);
559       fmt_buf_bytes += fmt_line_bytes;
560       ink_assert(fmt_buf_bytes < LOG_MAX_FORMATTED_BUFFER);
561       fmt_buf[fmt_buf_bytes] = '\n'; // keep entries separate
562       fmt_buf_bytes += 1;
563     }
564   }
565   if (fmt_buf_bytes > 0) {
566     if (!Log::config->logging_space_exhausted) {
567       ink_assert(fmt_buf_bytes < LOG_MAX_FORMATTED_BUFFER);
568       bytes += writeln(fmt_buf, fmt_buf_bytes, fd, path);
569     }
570   }
571 
572   return bytes;
573 }
574 
575 int
write_ascii_logbuffer3(LogBufferHeader * buffer_header,const char * alt_format)576 LogFile::write_ascii_logbuffer3(LogBufferHeader *buffer_header, const char *alt_format)
577 {
578   Debug("log-file",
579         "entering LogFile::write_ascii_logbuffer3 for %s "
580         "(this=%p)",
581         m_name, this);
582   ink_assert(buffer_header != nullptr);
583 
584   ProxyMutex *mutex = this_thread()->mutex.get();
585   LogBufferIterator iter(buffer_header);
586   LogEntryHeader *entry_header;
587   int fmt_entry_count = 0;
588   int fmt_buf_bytes   = 0;
589   int total_bytes     = 0;
590 
591   LogFormatType format_type;
592   char *fieldlist_str;
593   char *printf_str;
594   char *ascii_buffer;
595 
596   switch (buffer_header->version) {
597   case LOG_SEGMENT_VERSION:
598     format_type   = static_cast<LogFormatType>(buffer_header->format_type);
599     fieldlist_str = buffer_header->fmt_fieldlist();
600     printf_str    = buffer_header->fmt_printf();
601     break;
602 
603   default:
604     Note("Invalid LogBuffer version %d in write_ascii_logbuffer; "
605          "current version is %d",
606          buffer_header->version, LOG_SEGMENT_VERSION);
607     return 0;
608   }
609 
610   while ((entry_header = iter.next())) {
611     fmt_entry_count = 0;
612     fmt_buf_bytes   = 0;
613 
614     if (m_file_format == LOG_FILE_PIPE) {
615       ascii_buffer = static_cast<char *>(ats_malloc(m_max_line_size));
616     } else {
617       ascii_buffer = static_cast<char *>(ats_malloc(m_ascii_buffer_size));
618     }
619 
620     // fill the buffer with as many records as possible
621     //
622     do {
623       if (entry_header->entry_len >= m_max_line_size) {
624         Warning("Log is too long(%" PRIu32 "), it would be truncated. max_len:%zu", entry_header->entry_len, m_max_line_size);
625       }
626 
627       int bytes = LogBuffer::to_ascii(entry_header, format_type, &ascii_buffer[fmt_buf_bytes], m_max_line_size - 1, fieldlist_str,
628                                       printf_str, buffer_header->version, alt_format);
629 
630       if (bytes > 0) {
631         fmt_buf_bytes += bytes;
632         ascii_buffer[fmt_buf_bytes] = '\n';
633         ++fmt_buf_bytes;
634         ++fmt_entry_count;
635       } else {
636         Note("Failed to convert LogBuffer to ascii, have dropped (%" PRIu32 ") bytes.", entry_header->entry_len);
637 
638         RecIncrRawStat(log_rsb, mutex->thread_holding, log_stat_num_lost_before_flush_to_disk_stat, fmt_entry_count);
639 
640         RecIncrRawStat(log_rsb, mutex->thread_holding, log_stat_bytes_lost_before_flush_to_disk_stat, fmt_buf_bytes);
641       }
642       // if writing to a pipe, fill the buffer with a single
643       // record to avoid as much as possible overflowing the
644       // pipe buffer
645       //
646       if (m_file_format == LOG_FILE_PIPE) {
647         break;
648       }
649 
650       if (m_ascii_buffer_size - fmt_buf_bytes < m_max_line_size) {
651         break;
652       }
653     } while ((entry_header = iter.next()));
654 
655     // send the buffer to flush thread
656     //
657     LogFlushData *flush_data = new LogFlushData(this, ascii_buffer, fmt_buf_bytes);
658 
659     RecIncrRawStat(log_rsb, mutex->thread_holding, log_stat_num_flush_to_disk_stat, fmt_entry_count);
660 
661     RecIncrRawStat(log_rsb, mutex->thread_holding, log_stat_bytes_flush_to_disk_stat, fmt_buf_bytes);
662 
663     ink_atomiclist_push(Log::flush_data_list, flush_data);
664 
665     Log::flush_notify->signal();
666 
667     total_bytes += fmt_buf_bytes;
668   }
669 
670   return total_bytes;
671 }
672 
673 bool
rolled_logfile(char * file)674 LogFile::rolled_logfile(char *file)
675 {
676   return BaseLogFile::rolled_logfile(file);
677 }
678 
679 bool
exists(const char * pathname)680 LogFile::exists(const char *pathname)
681 {
682   return BaseLogFile::exists(pathname);
683 }
684 
685 /*-------------------------------------------------------------------------
686   LogFile::writeln
687 
688   This function will make sure the following data is written to the
689   output file (m_fd) with a trailing newline.
690   -------------------------------------------------------------------------*/
691 
692 int
writeln(char * data,int len,int fd,const char * path)693 LogFile::writeln(char *data, int len, int fd, const char *path)
694 {
695   int total_bytes = 0;
696 
697   if (len > 0 && data && fd >= 0) {
698     struct iovec wvec[2];
699     memset(&wvec, 0, sizeof(iovec));
700     memset(&wvec[1], 0, sizeof(iovec));
701     int bytes_this_write, vcnt = 1;
702 
703 #if defined(solaris)
704     wvec[0].iov_base = (caddr_t)data;
705 #else
706     wvec[0].iov_base = (void *)data;
707 #endif
708     wvec[0].iov_len = static_cast<size_t>(len);
709 
710     if (data[len - 1] != '\n') {
711 #if defined(solaris)
712       wvec[1].iov_base = (caddr_t) "\n";
713 #else
714       wvec[1].iov_base = (void *)"\n";
715 #endif
716       wvec[1].iov_len = static_cast<size_t>(1);
717       vcnt++;
718     }
719 
720     if ((bytes_this_write = static_cast<int>(::writev(fd, (const struct iovec *)wvec, vcnt))) < 0) {
721       Warning("An error was encountered in writing to %s: %s.", ((path) ? path : "logfile"), strerror(errno));
722     } else {
723       total_bytes = bytes_this_write;
724     }
725   }
726   return total_bytes;
727 }
728 
729 /*-------------------------------------------------------------------------
730   LogFile::check_fd
731 
732   This routine will occasionally stat the current logfile to make sure that
733   it really does exist.  The easiest way to do this is to close the file
734   and re-open it, which will create the file if it doesn't already exist.
735 
736   Failure to open the logfile will generate a manager alarm and a Warning.
737   -------------------------------------------------------------------------*/
738 
739 void
check_fd()740 LogFile::check_fd()
741 {
742   static bool failure_last_call    = false;
743   static unsigned stat_check_count = 1;
744 
745   if ((stat_check_count % Log::config->file_stat_frequency) == 0) {
746     //
747     // It's time to see if the file really exists.  If we can't see
748     // the file (via access), then we'll close our descriptor and
749     // attempt to re-open it, which will create the file if it's not
750     // there.
751     //
752     if (m_name && !LogFile::exists(m_name)) {
753       close_file();
754     }
755     stat_check_count = 0;
756   }
757   stat_check_count++;
758 
759   int err = open_file();
760   // XXX if open_file() returns, LOG_FILE_FILESYSTEM_CHECKS_FAILED, raise a more informative alarm ...
761   if (err != LOG_FILE_NO_ERROR && err != LOG_FILE_NO_PIPE_READERS) {
762     if (!failure_last_call) {
763       LogUtils::manager_alarm(LogUtils::LOG_ALARM_ERROR, "Traffic Server could not open logfile %s.", m_name);
764       Warning("Traffic Server could not open logfile %s: %s.", m_name, strerror(errno));
765     }
766     failure_last_call = true;
767     return;
768   }
769 
770   failure_last_call = false;
771 }
772 
773 void
display(FILE * fd)774 LogFile::display(FILE *fd)
775 {
776   fprintf(fd, "Logfile: %s, %s\n", get_name(), (is_open()) ? "file is open" : "file is not open");
777 }
778 
779 bool
is_open()780 LogFile::is_open()
781 {
782   if (m_file_format == LOG_FILE_PIPE) {
783     return m_fd >= 0;
784   } else {
785     return m_log && m_log->is_open();
786   }
787 }
788 
789 /*
790  * Returns the fd of the entity (pipe or regular file ) that this object is
791  * representing
792  *
793  * Returns -1 on error, the correct fd otherwise
794  */
795 int
get_fd()796 LogFile::get_fd()
797 {
798   if (m_file_format == LOG_FILE_PIPE) {
799     return m_fd;
800   } else if (m_log && m_log->m_fp) {
801     return fileno(m_log->m_fp);
802   } else {
803     return -1;
804   }
805 }
806