source: filezilla/trunk/fuentes/src/engine/logging.cpp @ 130

Last change on this file since 130 was 130, checked in by jrpelegrina, 4 years ago

First release to xenial

File size: 7.9 KB
Line 
1#include <filezilla.h>
2
3#include "logging_private.h"
4
5#ifdef __WXMSW__
6#include <wx/filename.h>
7#endif
8#include <wx/log.h>
9
10#include <errno.h>
11
12bool CLogging::m_logfile_initialized = false;
13#ifdef __WXMSW__
14HANDLE CLogging::m_log_fd = INVALID_HANDLE_VALUE;
15#else
16int CLogging::m_log_fd = -1;
17#endif
18wxString CLogging::m_prefixes[static_cast<int>(MessageType::count)];
19unsigned int CLogging::m_pid;
20int CLogging::m_max_size;
21wxString CLogging::m_file;
22
23int CLogging::m_refcount = 0;
24mutex CLogging::mutex_(false);
25
26thread_local int CLogging::debug_level_{0};
27thread_local int CLogging::raw_listing_{0};
28
29CLogging::CLogging(CFileZillaEnginePrivate & engine)
30        : engine_(engine)
31{
32        scoped_lock l(mutex_);
33        m_refcount++;
34}
35
36CLogging::~CLogging()
37{
38        scoped_lock l(mutex_);
39        m_refcount--;
40
41        if (!m_refcount) {
42#ifdef __WXMSW__
43                if (m_log_fd != INVALID_HANDLE_VALUE) {
44                        CloseHandle(m_log_fd);
45                        m_log_fd = INVALID_HANDLE_VALUE;
46                }
47#else
48                if (m_log_fd != -1) {
49                        close(m_log_fd);
50                        m_log_fd = -1;
51                }
52#endif
53                m_logfile_initialized = false;
54        }
55}
56
57bool CLogging::ShouldLog(MessageType nMessageType) const
58{
59        switch (nMessageType) {
60        case MessageType::Debug_Warning:
61                if (!debug_level_)
62                        return false;
63                break;
64        case MessageType::Debug_Info:
65                if (debug_level_ < 2)
66                        return false;
67                break;
68        case MessageType::Debug_Verbose:
69                if (debug_level_ < 3)
70                        return false;
71                break;
72        case MessageType::Debug_Debug:
73                if (debug_level_ != 4)
74                        return false;
75                break;
76        case MessageType::RawList:
77                if (!raw_listing_)
78                        return false;
79                break;
80        default:
81                break;
82        }
83        return true;
84}
85
86void CLogging::InitLogFile() const
87{
88        if (m_logfile_initialized)
89                return;
90
91        m_logfile_initialized = true;
92
93        m_file = engine_.GetOptions().GetOption(OPTION_LOGGING_FILE);
94        if (m_file.empty())
95                return;
96
97#ifdef __WXMSW__
98        m_log_fd = CreateFile(m_file, FILE_APPEND_DATA, FILE_SHARE_DELETE | FILE_SHARE_WRITE | FILE_SHARE_READ, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
99        if (m_log_fd == INVALID_HANDLE_VALUE)
100#else
101        m_log_fd = open(m_file.fn_str(), O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, 0644);
102        if (m_log_fd == -1)
103#endif
104        {
105                LogMessage(MessageType::Error, _("Could not open log file: %s"), wxSysErrorMsg());
106                return;
107        }
108
109        m_prefixes[static_cast<int>(MessageType::Status)] = _("Status:");
110        m_prefixes[static_cast<int>(MessageType::Error)] = _("Error:");
111        m_prefixes[static_cast<int>(MessageType::Command)] = _("Command:");
112        m_prefixes[static_cast<int>(MessageType::Response)] = _("Response:");
113        m_prefixes[static_cast<int>(MessageType::Debug_Warning)] = _("Trace:");
114        m_prefixes[static_cast<int>(MessageType::Debug_Info)] = m_prefixes[static_cast<int>(MessageType::Debug_Warning)];
115        m_prefixes[static_cast<int>(MessageType::Debug_Verbose)] = m_prefixes[static_cast<int>(MessageType::Debug_Warning)];
116        m_prefixes[static_cast<int>(MessageType::Debug_Debug)] = m_prefixes[static_cast<int>(MessageType::Debug_Warning)];
117        m_prefixes[static_cast<int>(MessageType::RawList)] = _("Listing:");
118
119        m_pid = wxGetProcessId();
120
121        m_max_size = engine_.GetOptions().GetOptionVal(OPTION_LOGGING_FILE_SIZELIMIT);
122        if (m_max_size < 0)
123                m_max_size = 0;
124        else if (m_max_size > 2000)
125                m_max_size = 2000;
126        m_max_size *= 1024 * 1024;
127}
128
129void CLogging::LogToFile(MessageType nMessageType, const wxString& msg) const
130{
131        scoped_lock l(mutex_);
132
133        InitLogFile();
134#ifdef __WXMSW__
135        if (m_log_fd == INVALID_HANDLE_VALUE)
136                return;
137#else
138        if (m_log_fd == -1)
139                return;
140#endif
141
142        CDateTime now = CDateTime::Now();
143        wxString out(wxString::Format(_T("%s %u %d %s %s")
144#ifdef __WXMSW__
145                _T("\r\n"),
146#else
147                _T("\n"),
148#endif
149                now.Format(_T("%Y-%m-%d %H:%M:%S"), CDateTime::local), m_pid, engine_.GetEngineId(), m_prefixes[static_cast<int>(nMessageType)], msg));
150
151        const wxWX2MBbuf utf8 = out.mb_str(wxConvUTF8);
152        if (utf8) {
153#ifdef __WXMSW__
154                if (m_max_size) {
155                        LARGE_INTEGER size;
156                        if (!GetFileSizeEx(m_log_fd, &size) || size.QuadPart > m_max_size) {
157                                CloseHandle(m_log_fd);
158                                m_log_fd = INVALID_HANDLE_VALUE;
159
160                                // m_log_fd might no longer be the original file.
161                                // Recheck on a new handle. Proteced with a mutex against other processes
162                                HANDLE hMutex = ::CreateMutex(0, true, _T("FileZilla 3 Logrotate Mutex"));
163                                if (!hMutex) {
164                                        wxString error = wxSysErrorMsg();
165                                        LogMessage(MessageType::Error, _("Could not create logging mutex: %s"), error);
166                                        return;
167                                }
168
169                                HANDLE hFile = CreateFile(m_file, FILE_APPEND_DATA, FILE_SHARE_DELETE | FILE_SHARE_WRITE | FILE_SHARE_READ, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
170                                if (hFile == INVALID_HANDLE_VALUE) {
171                                        wxString error = wxSysErrorMsg();
172
173                                        // Oh dear..
174                                        ReleaseMutex(hMutex);
175                                        CloseHandle(hMutex);
176
177                                        LogMessage(MessageType::Error, _("Could not open log file: %s"), error);
178                                        return;
179                                }
180
181                                wxString error;
182                                if (GetFileSizeEx(hFile, &size) && size.QuadPart > m_max_size) {
183                                        CloseHandle(hFile);
184
185                                        // MoveFileEx can fail if trying to access a deleted file for which another process still has
186                                        // a handle. Move it far away first.
187                                        // Todo: Handle the case in which logdir and tmpdir are on different volumes.
188                                        // (Why is everthing so needlessly complex on MSW?)
189                                        wxString tmp = wxFileName::CreateTempFileName(_T("fz3"));
190                                        MoveFileEx(m_file + _T(".1"), tmp, MOVEFILE_REPLACE_EXISTING);
191                                        DeleteFile(tmp);
192                                        MoveFileEx(m_file, m_file + _T(".1"), MOVEFILE_REPLACE_EXISTING);
193                                        m_log_fd = CreateFile(m_file, FILE_APPEND_DATA, FILE_SHARE_DELETE | FILE_SHARE_WRITE | FILE_SHARE_READ, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
194                                        if (m_log_fd == INVALID_HANDLE_VALUE) {
195                                                // If this function would return bool, I'd return FILE_NOT_FOUND here.
196                                                error = wxSysErrorMsg();
197                                        }
198                                }
199                                else
200                                        m_log_fd = hFile;
201
202                                if (hMutex) {
203                                        ReleaseMutex(hMutex);
204                                        CloseHandle(hMutex);
205                                }
206
207                                if (!error.empty())
208                                        LogMessage(MessageType::Error, _("Could not open log file: %s"), error);
209                        }
210                }
211                DWORD len = (DWORD)strlen((const char*)utf8);
212                DWORD written;
213                BOOL res = WriteFile(m_log_fd, (const char*)utf8, len, &written, 0);
214                if (!res || written != len) {
215                        LogMessage(MessageType::Error, _("Could not write to log file: %s"), wxSysErrorMsg());
216                        CloseHandle(m_log_fd);
217                        m_log_fd = INVALID_HANDLE_VALUE;
218                }
219#else
220                if (m_max_size) {
221                        struct stat buf;
222                        int rc = fstat(m_log_fd, &buf);
223                        while (!rc && buf.st_size > m_max_size) {
224                                struct flock lock = {0};
225                                lock.l_type = F_WRLCK;
226                                lock.l_whence = SEEK_SET;
227                                lock.l_start = 0;
228                                lock.l_len = 1;
229
230                                int rc;
231
232                                // Retry through signals
233                                while ((rc = fcntl(m_log_fd, F_SETLKW, &lock)) == -1 && errno == EINTR);
234
235                                // Ignore any other failures
236                                int fd = open(m_file.fn_str(), O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, 0644);
237                                if (fd == -1) {
238                                        wxString error = wxSysErrorMsg();
239
240                                        close(m_log_fd);
241                                        m_log_fd = -1;
242
243                                        LogMessage(MessageType::Error, error);
244                                        return;
245                                }
246                                struct stat buf2;
247                                rc = fstat(fd, &buf2);
248
249                                // Different files
250                                if (!rc && buf.st_ino != buf2.st_ino) {
251                                        close(m_log_fd); // Releases the lock
252                                        m_log_fd = fd;
253                                        buf = buf2;
254                                        continue;
255                                }
256
257                                // The file is indeed the log file and we are holding a lock on it.
258
259                                // Rename it
260                                rc = rename(m_file.fn_str(), (m_file + _T(".1")).fn_str());
261                                close(m_log_fd);
262                                close(fd);
263
264                                // Get the new file
265                                m_log_fd = open(m_file.fn_str(), O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, 0644);
266                                if (m_log_fd == -1) {
267                                        LogMessage(MessageType::Error, wxSysErrorMsg());
268                                        return;
269                                }
270
271                                if (!rc) // Rename didn't fail
272                                        rc = fstat(m_log_fd, &buf);
273                        }
274                }
275                size_t len = strlen((const char*)utf8);
276                size_t written = write(m_log_fd, (const char*)utf8, len);
277                if (written != len) {
278                        close(m_log_fd);
279                        m_log_fd = -1;
280                        LogMessage(MessageType::Error, _("Could not write to log file: %s"), wxSysErrorMsg());
281                }
282#endif
283        }
284}
285
286void CLogging::UpdateLogLevel(COptionsBase & options)
287{
288        debug_level_ = options.GetOptionVal(OPTION_LOGGING_DEBUGLEVEL);
289        raw_listing_ = options.GetOptionVal(OPTION_LOGGING_RAWLISTING);
290}
Note: See TracBrowser for help on using the repository browser.