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