Optional Two-phase heap tracing
[hiphop-php.git] / hphp / util / cronolog.cpp
blob9c34702c352aed55e693bdac2d8c64540dee26bf
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present 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 +----------------------------------------------------------------------+
16 #include "hphp/util/cronolog.h"
18 #include <boost/filesystem/path.hpp>
20 #ifndef _MSC_VER
21 #include <pwd.h>
22 #endif
24 #include <folly/portability/Fcntl.h>
25 #include <folly/portability/SysStat.h>
27 /* Default permissions for files and directories that are created */
29 #ifndef FILE_MODE
30 #define FILE_MODE ( S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH )
31 #endif
33 namespace HPHP {
35 ///////////////////////////////////////////////////////////////////////////////
37 using std::string;
38 namespace fs = boost::filesystem;
40 /* Open a new log file: determine the start of the current
41 * period, generate the log file name from the fileTemplate,
42 * determine the end of the period and open the new log file.
44 * Returns the file descriptor of the new log file and also sets the
45 * name of the file and the start time of the next period via pointers
46 * supplied.
48 static FILE *new_log_file(const char *fileTemplate, const char *linkname,
49 mode_t linktype, const char *prevlinkname,
50 PERIODICITY periodicity, int period_multiple,
51 int period_delay, char *pfilename,
52 size_t pfilename_len, time_t time_now,
53 time_t *pnext_period) {
54 time_t start_of_period;
55 struct tm *tm;
56 int log_fd;
58 start_of_period = start_of_this_period(time_now, periodicity,
59 period_multiple);
60 tm = localtime(&start_of_period);
61 strftime(pfilename, pfilename_len, fileTemplate, tm);
62 *pnext_period = start_of_next_period(start_of_period, periodicity,
63 period_multiple) + period_delay;
65 CRONO_DEBUG(("%s (%d): using log file \"%s\" from %s (%d) until %s (%d) "
66 "(for %d secs)\n",
67 timestamp(time_now), time_now, pfilename,
68 timestamp(start_of_period), start_of_period,
69 timestamp(*pnext_period), *pnext_period,
70 *pnext_period - time_now));
72 log_fd = open(pfilename, O_WRONLY|O_CREAT|O_APPEND, FILE_MODE);
74 #ifndef DONT_CREATE_SUBDIRS
75 if ((log_fd < 0) && (errno == ENOENT)) {
76 create_subdirs(pfilename);
77 log_fd = open(pfilename, O_WRONLY|O_CREAT|O_APPEND, FILE_MODE);
79 #endif
81 if (log_fd < 0) {
82 perror(pfilename);
83 return nullptr;
86 #ifndef _MSC_VER
87 if (linkname) {
88 struct stat stat_buf;
89 struct stat stat_buf2;
90 if (fstat(log_fd, &stat_buf) ||
91 stat(linkname, &stat_buf2) ||
92 stat_buf.st_ino != stat_buf2.st_ino ||
93 stat_buf.st_dev != stat_buf2.st_dev) {
95 /* Create a relative symlink to logs under linkname's directory */
96 std::string dir = fs::path(linkname).parent_path().native();
97 if (dir != "/") {
98 dir.append("/");
100 std::string filename;
101 if (!strncmp(pfilename, dir.c_str(), dir.length())) {
102 filename = pfilename + dir.length();
103 } else {
104 filename = pfilename;
107 create_link(filename.c_str(), linkname, linktype, prevlinkname);
110 #endif
111 return fdopen(log_fd, "a");
114 void Cronolog::setPeriodicity() {
115 if (m_periodicity == UNKNOWN) {
116 m_periodicity = determine_periodicity((char *)m_template.c_str());
120 FILE *Cronolog::getOutputFile() {
121 if (m_template.empty()) return m_file;
123 time_t time_now = time(nullptr) + m_timeOffset;
124 /* If the current period has not finished and there is a log file, use it */
125 if ((time_now < m_nextPeriod) && (m_file)) return m_file;
127 /* We need to open a new file under a mutex. */
129 std::lock_guard<std::mutex> lock(m_mutex);
130 if ((time_now >= m_nextPeriod)) {
131 /* the current period has finished */
133 /* We cannot close m_file because there may be other threads still
134 * writing to it. We save m_file in m_prevFile and leave it open for
135 * an entire period. We simply assume that by the end of the delay
136 * no threads should be still referencing m_prevFile and we can safely
137 * close it.
139 if (m_prevFile) fclose(m_prevFile);
140 m_prevFile = m_file;
141 m_file = nullptr;
144 /* If there is no log file open then open a new one. */
145 if (m_file == nullptr) {
146 const char *linkname = m_linkName.empty() ? nullptr : m_linkName.c_str();
147 m_file = new_log_file(m_template.c_str(),
148 #ifdef _MSC_VER
149 "", 0,
150 #else
151 linkname, S_IFLNK,
152 #endif
153 m_prevLinkName, m_periodicity, m_periodMultiple,
154 m_periodDelay, m_fileName, sizeof(m_fileName),
155 time_now, &m_nextPeriod);
158 return m_file;
161 void Cronolog::changeOwner(const string &username, const string &symlink) {
162 #ifdef _MSC_VER
163 return;
164 #else
165 if (username.empty() || symlink.empty()) {
166 return;
169 int username_length = sysconf(_SC_GETPW_R_SIZE_MAX);
170 if (username_length == -1) {
171 username_length = 512;
174 struct passwd user_info, *user_infop;
175 std::vector<char> username_buf(username_length);
177 if (getpwnam_r(username.c_str(), &user_info, &username_buf[0],
178 username_length, &user_infop)) {
179 // invalid user
180 return;
183 if (lchown(symlink.c_str(), user_info.pw_uid, -1) < 0) {
184 fprintf(stderr, "Unable to chmod %s\n", symlink.c_str());
187 // using chown() isn't portable if it is a symlink
188 int fd = open(symlink.c_str(), O_RDONLY | O_NONBLOCK | O_NOCTTY);
189 int success = (fd >= 0 ? fchown(fd, user_info.pw_uid, -1) : -1);
191 if (fd >= 0) {
192 close(fd);
195 if (success < 0) {
196 fprintf(stderr, "Unable to chmod %s\n", symlink.c_str());
198 #endif
201 ///////////////////////////////////////////////////////////////////////////////