1 /*
2  * Copyright 2003-2021 The Music Player Daemon Project
3  * http://www.musicpd.org
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 #include "LogBackend.hxx"
21 #include "Log.hxx"
22 #include "util/Compiler.h"
23 #include "util/Domain.hxx"
24 #include "util/StringStrip.hxx"
25 #include "Version.h"
26 #include "config.h"
27 
28 #include <cassert>
29 
30 #include <stdio.h>
31 #include <string.h>
32 #include <time.h>
33 
34 #ifdef HAVE_SYSLOG
35 #include <syslog.h>
36 #endif
37 
38 #ifdef ANDROID
39 #include <android/log.h>
40 #include "android/LogListener.hxx"
41 #include "Main.hxx"
42 
43 static int
ToAndroidLogLevel(LogLevel log_level)44 ToAndroidLogLevel(LogLevel log_level) noexcept
45 {
46 	switch (log_level) {
47 	case LogLevel::DEBUG:
48 		return ANDROID_LOG_DEBUG;
49 
50 	case LogLevel::INFO:
51 	case LogLevel::NOTICE:
52 		return ANDROID_LOG_INFO;
53 
54 	case LogLevel::WARNING:
55 		return ANDROID_LOG_WARN;
56 
57 	case LogLevel::ERROR:
58 		return ANDROID_LOG_ERROR;
59 	}
60 
61 	assert(false);
62 	gcc_unreachable();
63 }
64 
65 #else
66 
67 static LogLevel log_threshold = LogLevel::NOTICE;
68 
69 static bool enable_timestamp;
70 
71 #ifdef HAVE_SYSLOG
72 static bool enable_syslog;
73 #endif
74 
75 void
SetLogThreshold(LogLevel _threshold)76 SetLogThreshold(LogLevel _threshold) noexcept
77 {
78 	log_threshold = _threshold;
79 }
80 
81 void
EnableLogTimestamp()82 EnableLogTimestamp() noexcept
83 {
84 #ifdef HAVE_SYSLOG
85 	assert(!enable_syslog);
86 #endif
87 	assert(!enable_timestamp);
88 
89 	enable_timestamp = true;
90 }
91 
92 static const char *
log_date()93 log_date() noexcept
94 {
95 	static constexpr size_t LOG_DATE_BUF_SIZE = 16;
96 	static char buf[LOG_DATE_BUF_SIZE];
97 	time_t t = time(nullptr);
98 	strftime(buf, LOG_DATE_BUF_SIZE, "%b %d %H:%M : ", localtime(&t));
99 	return buf;
100 }
101 
102 /**
103  * Determines the length of the string excluding trailing whitespace
104  * characters.
105  */
106 static int
chomp_length(std::string_view p)107 chomp_length(std::string_view p) noexcept
108 {
109 	return StripRight(p.data(), p.size());
110 }
111 
112 #ifdef HAVE_SYSLOG
113 
114 [[gnu::const]]
115 static int
ToSysLogLevel(LogLevel log_level)116 ToSysLogLevel(LogLevel log_level) noexcept
117 {
118 	switch (log_level) {
119 	case LogLevel::DEBUG:
120 		return LOG_DEBUG;
121 
122 	case LogLevel::INFO:
123 		return LOG_INFO;
124 
125 	case LogLevel::NOTICE:
126 		return LOG_NOTICE;
127 
128 	case LogLevel::WARNING:
129 		return LOG_WARNING;
130 
131 	case LogLevel::ERROR:
132 		return LOG_ERR;
133 	}
134 
135 	assert(false);
136 	gcc_unreachable();
137 }
138 
139 static void
SysLog(const Domain & domain,LogLevel log_level,std::string_view message)140 SysLog(const Domain &domain, LogLevel log_level, std::string_view message) noexcept
141 {
142 	syslog(ToSysLogLevel(log_level), "%s: %.*s",
143 	       domain.GetName(),
144 	       chomp_length(message), message.data());
145 }
146 
147 void
LogInitSysLog()148 LogInitSysLog() noexcept
149 {
150 	openlog(PACKAGE, 0, LOG_DAEMON);
151 	enable_syslog = true;
152 }
153 
154 void
LogFinishSysLog()155 LogFinishSysLog() noexcept
156 {
157 	if (enable_syslog)
158 		closelog();
159 }
160 
161 #endif
162 
163 static void
FileLog(const Domain & domain,std::string_view message)164 FileLog(const Domain &domain, std::string_view message) noexcept
165 {
166 	fprintf(stderr, "%s%s: %.*s\n",
167 		enable_timestamp ? log_date() : "",
168 		domain.GetName(),
169 		chomp_length(message), message.data());
170 
171 #ifdef _WIN32
172 	/* force-flush the log file, because setvbuf() does not seem
173 	   to have an effect on WIN32 */
174 	fflush(stderr);
175 #endif
176 }
177 
178 #endif /* !ANDROID */
179 
180 void
Log(LogLevel level,const Domain & domain,std::string_view msg)181 Log(LogLevel level, const Domain &domain, std::string_view msg) noexcept
182 {
183 #ifdef ANDROID
184 	__android_log_print(ToAndroidLogLevel(level), "MPD",
185 			    "%s: %.*s", domain.GetName(),
186 			    (int)msg.size(), msg.data());
187 	if (logListener != nullptr) {
188 		char buffer[1024];
189 		snprintf(buffer, sizeof(buffer), "%s: %.*s",
190 			 domain.GetName(), (int)msg.size(), msg.data());
191 
192 		logListener->OnLog(Java::GetEnv(), ToAndroidLogLevel(level),
193 				   buffer);
194 	}
195 #else
196 
197 	if (level < log_threshold)
198 		return;
199 
200 #ifdef HAVE_SYSLOG
201 	if (enable_syslog) {
202 		SysLog(domain, level, msg);
203 		return;
204 	}
205 #endif
206 
207 	FileLog(domain, msg);
208 #endif /* !ANDROID */
209 }
210