2 +----------------------------------------------------------------------+
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>
24 #include <folly/portability/Fcntl.h>
25 #include <folly/portability/SysStat.h>
27 /* Default permissions for files and directories that are created */
30 #define FILE_MODE ( S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH )
35 ///////////////////////////////////////////////////////////////////////////////
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
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
;
58 start_of_period
= start_of_this_period(time_now
, periodicity
,
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) "
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
);
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();
100 std::string filename
;
101 if (!strncmp(pfilename
, dir
.c_str(), dir
.length())) {
102 filename
= pfilename
+ dir
.length();
104 filename
= pfilename
;
107 create_link(filename
.c_str(), linkname
, linktype
, prevlinkname
);
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
139 if (m_prevFile
) fclose(m_prevFile
);
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(),
153 m_prevLinkName
, m_periodicity
, m_periodMultiple
,
154 m_periodDelay
, m_fileName
, sizeof(m_fileName
),
155 time_now
, &m_nextPeriod
);
161 void Cronolog::changeOwner(const string
&username
, const string
&symlink
) {
165 if (username
.empty() || symlink
.empty()) {
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
)) {
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);
196 fprintf(stderr
, "Unable to chmod %s\n", symlink
.c_str());
201 ///////////////////////////////////////////////////////////////////////////////