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

Last change on this file since 3185 was 3185, checked in by jrpelegrina, 2 years ago

Update new version: 3.15.02

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