Create dedicate crate for php_escaping.rs
[hiphop-php.git] / hphp / runtime / server / log-writer.cpp
blob4158d03a14706c14073e11a0c4572fdd8a3c3c53
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2014 Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/runtime/server/log-writer.h"
19 #include <cctype>
20 #include <sstream>
21 #include <map>
23 #include "hphp/util/logger.h"
24 #include "hphp/runtime/base/init-fini-node.h"
27 namespace HPHP {
28 ///////////////////////////////////////////////////////////////////////////////
29 std::string FieldGenerator::escapeData(const char* s, int len) {
30 static const char digits[] = "0123456789abcdef";
31 std::ostringstream out;
32 for (int i = 0; i < len; i++) {
33 unsigned char uc = *s++;
34 switch (uc) {
35 case '"': out << "\\\""; break;
36 case '\\': out << "\\\\"; break;
37 case '\b': out << "\\b"; break;
38 case '\f': out << "\\f"; break;
39 case '\n': out << "\\n"; break;
40 case '\r': out << "\\r"; break;
41 case '\t': out << "\\t"; break;
42 default:
43 if (uc >= ' ' && (uc & 127) == uc) {
44 out << (char)uc;
45 } else {
46 out << "\\x" << digits[(uc >> 4) & 15] << digits[(uc >> 0) & 15];
48 break;
51 return out.str();
54 ClassicWriter::~ClassicWriter() {
55 if (m_channel == LogChannel::REGULAR) {
56 if (m_logdata.file[0] == '|') {
57 pclose(m_filelog);
58 } else {
59 fclose(m_filelog);
64 void ClassicWriter::init(const std::string& username,
65 AccessLog::GetThreadDataFunc fn) {
66 m_threadDataFn = fn;
67 if (m_channel == LogChannel::CRONOLOG) {
68 assertx(!m_logdata.file.empty());
69 m_cronolog = std::make_unique<Cronolog>();
70 if (m_logdata.file.find('%') != std::string::npos) {
71 m_cronolog->m_template = m_logdata.file;
72 m_cronolog->setPeriodicity();
73 if (m_logdata.periodMultiplier) {
74 m_cronolog->m_periodMultiple = m_logdata.periodMultiplier;
77 m_cronolog->m_linkName = m_logdata.symLink;
78 Cronolog::changeOwner(username, m_logdata.symLink);
79 } else {
80 m_cronolog->m_file = fopen(m_logdata.file.c_str(), "a");
82 } else if (m_channel == LogChannel::REGULAR) {
83 assertx(!m_logdata.file.empty());
84 if (m_logdata.file[0] == '|') {
85 std::string plog = m_logdata.file.substr(1);
86 m_filelog = popen(plog.c_str(), "w");
87 } else {
88 m_filelog = fopen(m_logdata.file.c_str(), "a");
90 if (!m_filelog) {
91 Logger::Error("Couldn't open access log file %s", m_logdata.file.c_str());
96 void ClassicWriter::write(Transport* transport, const VirtualHost* vhost) {
97 auto outfile = getOutputFile();
98 if (!outfile) return;
99 char c;
100 std::ostringstream out;
101 const auto* format = m_logdata.format.c_str();
102 FieldGenerator fieldGen{
103 transport,
104 vhost,
105 (m_threadDataFn ? m_threadDataFn() : nullptr)
107 while ((c = *format++)) {
108 if (c != '%') {
109 out << c;
110 continue;
112 if (parseConditions(format, transport->getResponseCode())) {
113 std::string field;
114 std::string arg = parseArgument(format);
115 while (!std::isalpha(*format)) format++;
116 if (fieldGen.gen<std::string>(*format++, arg, field)) {
117 out << field;
118 } else {
119 out << '-';
121 } else {
122 skipField(format);
123 out << '-';
126 out << std::endl;
128 auto str = out.str();
129 int nbytes = fwrite(str.data(), 1, str.size(), outfile);
130 fflush(outfile);
132 if (m_channel != LogChannel::REGULAR || m_logdata.file[0] != '|') {
133 recordWriteAndMaybeDropCaches(outfile, nbytes);
137 const std::string ClassicWriter::handle = "__classic";
139 bool ClassicWriter::parseConditions(const char*& format, int code) {
140 bool wantMatch = true;
141 if (*format == '!') {
142 wantMatch = false;
143 format++;
144 } else if (!isdigit(*format)) {
145 // No conditions
146 return true;
148 char buf[4];
149 buf[3] = '\0';
151 bool matched = false;
152 while (isdigit(*format)) {
153 buf[0] = format[0];
154 buf[1] = format[1];
155 buf[2] = format[2];
156 if (folly::to<int>(buf) == code) {
157 matched = true;
158 break;
160 format+=4;
162 while (!(*format == '{' || isalpha(*format))) {
163 format++;
165 return wantMatch == matched;
168 std::string ClassicWriter::parseArgument(const char*& format) {
169 if (*format != '{') return std::string();
170 format++;
171 const char *start = format;
172 while (*format != '}') { format++; }
173 std::string res(start, format - start);
174 format++;
175 return res;
178 void ClassicWriter::skipField(const char*& format) {
179 // Skip argument
180 if (*format == '{') {
181 while (*format != '}') { format++; }
182 format++;
184 // Find control letter
185 while (!isalpha(*format)) { format++; }
186 // Skip it
187 format++;
190 static InitFiniNode registerClassicWriter(
191 []() { AccessLogFileData::registerWriter(
192 ClassicWriter::handle,
193 [](const AccessLogFileData& alfd, LogChannel chan) {
194 return std::make_unique<ClassicWriter>(alfd, chan);
196 );},
197 InitFiniNode::When::ServerPreInit
199 ///////////////////////////////////////////////////////////////////////////////