1 // distribution boxbackup-0.11_trunk_2979 (svn version: 2979)
2 // Box Backup, http://www.boxbackup.org/
3 //
4 // Copyright (c) 2003-2010, Ben Summers and contributors.
5 // All rights reserved.
6 //
7 // Note that this project uses mixed licensing. Any file with this license
8 // attached, or where the code LICENSE-DUAL appears on the first line, falls
9 // under this license. See the file COPYING.txt for more information.
10 //
11 // This file is dual licensed. You may use and distribute it providing that you
12 // comply EITHER with the terms of the BSD license, OR the GPL license. It is
13 // not necessary to comply with both licenses, only one.
14 //
15 // The BSD license option follows:
16 //
17 // Redistribution and use in source and binary forms, with or without
18 // modification, are permitted provided that the following conditions are met:
19 //
20 // 1. Redistributions of source code must retain the above copyright
21 //    notice, this list of conditions and the following disclaimer.
22 //
23 // 2. Redistributions in binary form must reproduce the above copyright
24 //    notice, this list of conditions and the following disclaimer in the
25 //    documentation and/or other materials provided with the distribution.
26 //
27 // 3. Neither the name of the Box Backup nor the names of its contributors may
28 //    be used to endorse or promote products derived from this software without
29 //    specific prior written permission.
30 //
31 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
32 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
33 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
34 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
35 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
36 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
38 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
39 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
40 // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 //
42 // [http://en.wikipedia.org/wiki/BSD_licenses#3-clause_license_.28.22New_BSD_License.22.29]
43 //
44 // The GPL license option follows:
45 //
46 // This program is free software; you can redistribute it and/or
47 // modify it under the terms of the GNU General Public License
48 // as published by the Free Software Foundation; either version 2
49 // of the License, or (at your option) any later version.
50 //
51 // This program is distributed in the hope that it will be useful,
52 // but WITHOUT ANY WARRANTY; without even the implied warranty of
53 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
54 // GNU General Public License for more details.
55 //
56 // You should have received a copy of the GNU General Public License
57 // along with this program; if not, write to the Free Software
58 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
59 //
60 // [http://www.gnu.org/licenses/old-licenses/gpl-2.0.html#SEC4]
61 // --------------------------------------------------------------------------
62 //
63 // File
64 //		Name:    Logging.cpp
65 //		Purpose: Generic logging core routines implementation
66 //		Created: 2006/12/16
67 //
68 // --------------------------------------------------------------------------
69 
70 #include "Box.h"
71 
72 #include <errno.h>
73 #include <time.h>
74 #include <string.h> // for stderror
75 
76 // c.f. http://bugs.debian.org/512510
77 #include <cstdio>
78 
79 #ifdef HAVE_SYSLOG_H
80 	#include <syslog.h>
81 #endif
82 #ifdef HAVE_UNISTD_H
83 	#include <unistd.h>
84 #endif
85 #ifdef WIN32
86 	#include <process.h>
87 #endif
88 
89 #include <cstring>
90 #include <iomanip>
91 
92 #include "BoxTime.h"
93 #include "Logging.h"
94 
95 bool Logging::sLogToSyslog  = false;
96 bool Logging::sLogToConsole = false;
97 bool Logging::sContextSet   = false;
98 
99 bool HideExceptionMessageGuard::sHiddenState = false;
100 
101 std::vector<Logger*> Logging::sLoggers;
102 std::string Logging::sContext;
103 Console*    Logging::spConsole = NULL;
104 Syslog*     Logging::spSyslog  = NULL;
105 Log::Level  Logging::sGlobalLevel = Log::EVERYTHING;
106 Logging     Logging::sGlobalLogging; //automatic initialisation
107 std::string Logging::sProgramName;
108 
Logging()109 Logging::Logging()
110 {
111 	ASSERT(!spConsole);
112 	ASSERT(!spSyslog);
113 	spConsole = new Console();
114 	spSyslog  = new Syslog();
115 	sLogToConsole = true;
116 	sLogToSyslog  = true;
117 }
118 
~Logging()119 Logging::~Logging()
120 {
121 	sLogToConsole = false;
122 	sLogToSyslog  = false;
123 	delete spConsole;
124 	delete spSyslog;
125 	spConsole = NULL;
126 	spSyslog  = NULL;
127 }
128 
ToSyslog(bool enabled)129 void Logging::ToSyslog(bool enabled)
130 {
131 	if (!sLogToSyslog && enabled)
132 	{
133 		Add(spSyslog);
134 	}
135 
136 	if (sLogToSyslog && !enabled)
137 	{
138 		Remove(spSyslog);
139 	}
140 
141 	sLogToSyslog = enabled;
142 }
143 
ToConsole(bool enabled)144 void Logging::ToConsole(bool enabled)
145 {
146 	if (!sLogToConsole && enabled)
147 	{
148 		Add(spConsole);
149 	}
150 
151 	if (sLogToConsole && !enabled)
152 	{
153 		Remove(spConsole);
154 	}
155 
156 	sLogToConsole = enabled;
157 }
158 
FilterConsole(Log::Level level)159 void Logging::FilterConsole(Log::Level level)
160 {
161 	spConsole->Filter(level);
162 }
163 
FilterSyslog(Log::Level level)164 void Logging::FilterSyslog(Log::Level level)
165 {
166 	spSyslog->Filter(level);
167 }
168 
Add(Logger * pNewLogger)169 void Logging::Add(Logger* pNewLogger)
170 {
171 	for (std::vector<Logger*>::iterator i = sLoggers.begin();
172 		i != sLoggers.end(); i++)
173 	{
174 		if (*i == pNewLogger)
175 		{
176 			return;
177 		}
178 	}
179 
180 	sLoggers.insert(sLoggers.begin(), pNewLogger);
181 }
182 
Remove(Logger * pOldLogger)183 void Logging::Remove(Logger* pOldLogger)
184 {
185 	for (std::vector<Logger*>::iterator i = sLoggers.begin();
186 		i != sLoggers.end(); i++)
187 	{
188 		if (*i == pOldLogger)
189 		{
190 			sLoggers.erase(i);
191 			return;
192 		}
193 	}
194 }
195 
Log(Log::Level level,const std::string & rFile,int line,const std::string & rMessage)196 void Logging::Log(Log::Level level, const std::string& rFile,
197 	int line, const std::string& rMessage)
198 {
199 	if (level > sGlobalLevel)
200 	{
201 		return;
202 	}
203 
204 	std::string newMessage;
205 
206 	if (sContextSet)
207 	{
208 		newMessage += "[" + sContext + "] ";
209 	}
210 
211 	newMessage += rMessage;
212 
213 	for (std::vector<Logger*>::iterator i = sLoggers.begin();
214 		i != sLoggers.end(); i++)
215 	{
216 		bool result = (*i)->Log(level, rFile, line, newMessage);
217 		if (!result)
218 		{
219 			return;
220 		}
221 	}
222 }
223 
LogToSyslog(Log::Level level,const std::string & rFile,int line,const std::string & rMessage)224 void Logging::LogToSyslog(Log::Level level, const std::string& rFile,
225 	int line, const std::string& rMessage)
226 {
227 	if (!sLogToSyslog)
228 	{
229 		return;
230 	}
231 
232 	if (level > sGlobalLevel)
233 	{
234 		return;
235 	}
236 
237 	std::string newMessage;
238 
239 	if (sContextSet)
240 	{
241 		newMessage += "[" + sContext + "] ";
242 	}
243 
244 	newMessage += rMessage;
245 
246 	spSyslog->Log(level, rFile, line, newMessage);
247 }
248 
SetContext(std::string context)249 void Logging::SetContext(std::string context)
250 {
251 	sContext = context;
252 	sContextSet = true;
253 }
254 
GetNamedLevel(const std::string & rName)255 Log::Level Logging::GetNamedLevel(const std::string& rName)
256 {
257 	if      (rName == "nothing") { return Log::NOTHING; }
258 	else if (rName == "fatal")   { return Log::FATAL; }
259 	else if (rName == "error")   { return Log::ERROR; }
260 	else if (rName == "warning") { return Log::WARNING; }
261 	else if (rName == "notice")  { return Log::NOTICE; }
262 	else if (rName == "info")    { return Log::INFO; }
263 	else if (rName == "trace")   { return Log::TRACE; }
264 	else if (rName == "everything") { return Log::EVERYTHING; }
265 	else
266 	{
267 		BOX_ERROR("Unknown verbosity level: " << rName);
268 		return Log::INVALID;
269 	}
270 }
271 
ClearContext()272 void Logging::ClearContext()
273 {
274 	sContextSet = false;
275 }
276 
SetProgramName(const std::string & rProgramName)277 void Logging::SetProgramName(const std::string& rProgramName)
278 {
279 	sProgramName = rProgramName;
280 
281 	for (std::vector<Logger*>::iterator i = sLoggers.begin();
282 		i != sLoggers.end(); i++)
283 	{
284 		(*i)->SetProgramName(rProgramName);
285 	}
286 }
287 
SetFacility(int facility)288 void Logging::SetFacility(int facility)
289 {
290 	spSyslog->SetFacility(facility);
291 }
292 
Logger()293 Logger::Logger()
294 : mCurrentLevel(Log::EVERYTHING)
295 {
296 	Logging::Add(this);
297 }
298 
Logger(Log::Level Level)299 Logger::Logger(Log::Level Level)
300 : mCurrentLevel(Level)
301 {
302 	Logging::Add(this);
303 }
304 
~Logger()305 Logger::~Logger()
306 {
307 	Logging::Remove(this);
308 }
309 
310 bool Console::sShowTime = false;
311 bool Console::sShowTimeMicros = false;
312 bool Console::sShowTag = false;
313 bool Console::sShowPID = false;
314 std::string Console::sTag;
315 
SetProgramName(const std::string & rProgramName)316 void Console::SetProgramName(const std::string& rProgramName)
317 {
318 	sTag = rProgramName;
319 }
320 
SetShowTag(bool enabled)321 void Console::SetShowTag(bool enabled)
322 {
323 	sShowTag = enabled;
324 }
325 
SetShowTime(bool enabled)326 void Console::SetShowTime(bool enabled)
327 {
328 	sShowTime = enabled;
329 }
330 
SetShowTimeMicros(bool enabled)331 void Console::SetShowTimeMicros(bool enabled)
332 {
333 	sShowTimeMicros = enabled;
334 }
335 
SetShowPID(bool enabled)336 void Console::SetShowPID(bool enabled)
337 {
338 	sShowPID = enabled;
339 }
340 
Log(Log::Level level,const std::string & rFile,int line,std::string & rMessage)341 bool Console::Log(Log::Level level, const std::string& rFile,
342 	int line, std::string& rMessage)
343 {
344 	if (level > GetLevel())
345 	{
346 		return true;
347 	}
348 
349 	FILE* target = stdout;
350 
351 	if (level <= Log::WARNING)
352 	{
353 		target = stderr;
354 	}
355 
356 	std::ostringstream buf;
357 
358 	if (sShowTime)
359 	{
360 		buf << FormatTime(GetCurrentBoxTime(), false, sShowTimeMicros);
361 		buf << " ";
362 	}
363 
364 	if (sShowTag)
365 	{
366 		if (sShowPID)
367 		{
368 			buf << "[" << sTag << " " << getpid() << "] ";
369 		}
370 		else
371 		{
372 			buf << "[" << sTag << "] ";
373 		}
374 	}
375 	else if (sShowPID)
376 	{
377 		buf << "[" << getpid() << "] ";
378 	}
379 
380 	if (level <= Log::FATAL)
381 	{
382 		buf << "FATAL:   ";
383 	}
384 	else if (level <= Log::ERROR)
385 	{
386 		buf << "ERROR:   ";
387 	}
388 	else if (level <= Log::WARNING)
389 	{
390 		buf << "WARNING: ";
391 	}
392 	else if (level <= Log::NOTICE)
393 	{
394 		buf << "NOTICE:  ";
395 	}
396 	else if (level <= Log::INFO)
397 	{
398 		buf << "INFO:    ";
399 	}
400 	else if (level <= Log::TRACE)
401 	{
402 		buf << "TRACE:   ";
403 	}
404 
405 	buf << rMessage;
406 
407 	#ifdef WIN32
408 		std::string output = buf.str();
409 		ConvertUtf8ToConsole(output.c_str(), output);
410 		fprintf(target, "%s\n", output.c_str());
411 	#else
412 		fprintf(target, "%s\n", buf.str().c_str());
413 	#endif
414 
415 	return true;
416 }
417 
Log(Log::Level level,const std::string & rFile,int line,std::string & rMessage)418 bool Syslog::Log(Log::Level level, const std::string& rFile,
419 	int line, std::string& rMessage)
420 {
421 	if (level > GetLevel())
422 	{
423 		return true;
424 	}
425 
426 	int syslogLevel = LOG_ERR;
427 
428 	switch(level)
429 	{
430 		case Log::NOTHING:    /* fall through */
431 		case Log::INVALID:    /* fall through */
432 		case Log::FATAL:      syslogLevel = LOG_CRIT;    break;
433 		case Log::ERROR:      syslogLevel = LOG_ERR;     break;
434 		case Log::WARNING:    syslogLevel = LOG_WARNING; break;
435 		case Log::NOTICE:     syslogLevel = LOG_NOTICE;  break;
436 		case Log::INFO:       syslogLevel = LOG_INFO;    break;
437 		case Log::TRACE:      /* fall through */
438 		case Log::EVERYTHING: syslogLevel = LOG_DEBUG;   break;
439 	}
440 
441 	std::string msg;
442 
443 	if (level <= Log::FATAL)
444 	{
445 		msg = "FATAL: ";
446 	}
447 	else if (level <= Log::ERROR)
448 	{
449 		msg = "ERROR: ";
450 	}
451 	else if (level <= Log::WARNING)
452 	{
453 		msg = "WARNING: ";
454 	}
455 	else if (level <= Log::NOTICE)
456 	{
457 		msg = "NOTICE: ";
458 	}
459 
460 	msg += rMessage;
461 
462 	syslog(syslogLevel, "%s", msg.c_str());
463 
464 	return true;
465 }
466 
Syslog()467 Syslog::Syslog() : mFacility(LOG_LOCAL6)
468 {
469 	::openlog("Box Backup", LOG_PID, mFacility);
470 }
471 
~Syslog()472 Syslog::~Syslog()
473 {
474 	::closelog();
475 }
476 
SetProgramName(const std::string & rProgramName)477 void Syslog::SetProgramName(const std::string& rProgramName)
478 {
479 	mName = rProgramName;
480 	::closelog();
481 	::openlog(mName.c_str(), LOG_PID, mFacility);
482 }
483 
SetFacility(int facility)484 void Syslog::SetFacility(int facility)
485 {
486 	mFacility = facility;
487 	::closelog();
488 	::openlog(mName.c_str(), LOG_PID, mFacility);
489 }
490 
GetNamedFacility(const std::string & rFacility)491 int Syslog::GetNamedFacility(const std::string& rFacility)
492 {
493 	#define CASE_RETURN(x) if (rFacility == #x) { return LOG_ ## x; }
494 	CASE_RETURN(LOCAL0)
495 	CASE_RETURN(LOCAL1)
496 	CASE_RETURN(LOCAL2)
497 	CASE_RETURN(LOCAL3)
498 	CASE_RETURN(LOCAL4)
499 	CASE_RETURN(LOCAL5)
500 	CASE_RETURN(LOCAL6)
501 	CASE_RETURN(DAEMON)
502 	#undef CASE_RETURN
503 
504 	BOX_ERROR("Unknown log facility '" << rFacility << "', "
505 		"using default LOCAL6");
506 	return LOG_LOCAL6;
507 }
508 
Log(Log::Level Level,const std::string & rFile,int line,std::string & rMessage)509 bool FileLogger::Log(Log::Level Level, const std::string& rFile,
510 	int line, std::string& rMessage)
511 {
512 	if (mLogFile.StreamClosed())
513 	{
514 		/* skip this logger to allow logging failure to open
515 		the log file, without causing an infinite loop */
516 		return true;
517 	}
518 
519 	if (Level > GetLevel())
520 	{
521 		return true;
522 	}
523 
524 	/* avoid infinite loop if this throws an exception */
525 	Logging::Remove(this);
526 
527 	std::ostringstream buf;
528 	buf << FormatTime(GetCurrentBoxTime(), true, false);
529 	buf << " ";
530 
531 	if (Level <= Log::FATAL)
532 	{
533 		buf << "[FATAL]   ";
534 	}
535 	else if (Level <= Log::ERROR)
536 	{
537 		buf << "[ERROR]   ";
538 	}
539 	else if (Level <= Log::WARNING)
540 	{
541 		buf << "[WARNING] ";
542 	}
543 	else if (Level <= Log::NOTICE)
544 	{
545 		buf << "[NOTICE]  ";
546 	}
547 	else if (Level <= Log::INFO)
548 	{
549 		buf << "[INFO]    ";
550 	}
551 	else if (Level <= Log::TRACE)
552 	{
553 		buf << "[TRACE]   ";
554 	}
555 
556 	buf << rMessage << "\n";
557 	std::string output = buf.str();
558 
559 	#ifdef WIN32
560 		ConvertUtf8ToConsole(output.c_str(), output);
561 	#endif
562 
563 	mLogFile.Write(output.c_str(), output.length());
564 
565 	Logging::Add(this);
566 	return true;
567 }
568 
PrintEscapedBinaryData(const std::string & rInput)569 std::string PrintEscapedBinaryData(const std::string& rInput)
570 {
571 	std::ostringstream output;
572 
573 	for (size_t i = 0; i < rInput.length(); i++)
574 	{
575 		if (isprint(rInput[i]))
576 		{
577 			output << rInput[i];
578 		}
579 		else
580 		{
581 			output << "\\x" << std::hex << std::setw(2) <<
582 				std::setfill('0') << (int) rInput[i] <<
583 				std::dec;
584 		}
585 	}
586 
587 	return output.str();
588 }
589