1 /*
2 * Logging for unix service.
3 *
4 * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <usual/logging.h>
20
21 #include <sys/stat.h>
22
23 #include <usual/ctype.h>
24 #include <usual/string.h>
25 #include <usual/time.h>
26 #include <usual/err.h>
27
28 #ifdef HAVE_SYSLOG_H
29 #include <syslog.h>
30 #endif
31
32 #ifdef USE_SYSTEMD
33 #define SD_JOURNAL_SUPPRESS_LOCATION
34 #include <systemd/sd-journal.h>
35 #endif
36
37 #ifdef WIN32
38 #define LOG_EMERG 0
39 #define LOG_ALERT 1
40 #define LOG_CRIT 2
41 #define LOG_ERR 3
42 #define LOG_WARNING 4
43 #define LOG_NOTICE 5
44 #define LOG_INFO 6
45 #define LOG_DEBUG 7
46
47 #define LOG_PID 0
48 #define LOG_DAEMON 0
49
openlog(const char * ident,int opt,int fac)50 static inline void openlog(const char *ident, int opt, int fac) {}
51 #define syslog win32_eventlog
52 #define closelog()
53 static void win32_eventlog(int level, const char *fmt, ...) _PRINTF(2, 3);
54 #endif
55
56 int cf_quiet = 0;
57 int cf_verbose = 0;
58 const char *cf_logfile = NULL;
59
60 int cf_syslog = 0;
61 const char *cf_syslog_ident = NULL;
62 const char *cf_syslog_facility = NULL;
63
64 enum LogLevel cf_syslog_level = LG_INFO;
65 enum LogLevel cf_logfile_level = LG_NOISE;
66 enum LogLevel cf_stderr_level = LG_NOISE;
67
68 /* optional function to fill prefix */
69 logging_prefix_fn_t logging_prefix_cb;
70
71 static FILE *log_file = NULL;
72 static bool syslog_started = false;
73
74 struct LevelInfo {
75 const char *tag;
76 int syslog_prio;
77 };
78
79 static const struct LevelInfo log_level_list[] = {
80 { "FATAL", LOG_CRIT }, /* LG_FATAL */
81 { "ERROR", LOG_ERR }, /* LG_ERROR */
82 { "WARNING", LOG_WARNING },/* LG_WARNING */
83 { "LOG", LOG_INFO }, /* LG_STATS*/
84 { "LOG", LOG_INFO }, /* LG_INFO */
85 { "DEBUG", LOG_DEBUG }, /* LG_DEBUG */
86 { "NOISE", LOG_DEBUG }, /* LG_NOISE */
87 };
88
89 struct FacName { const char *name; int code; };
90 static const struct FacName facility_names [] = {
91 #ifndef WIN32
92 { "auth", LOG_AUTH },
93 #ifdef LOG_AUTHPRIV
94 { "authpriv", LOG_AUTHPRIV },
95 #endif
96 { "daemon", LOG_DAEMON },
97 { "user", LOG_USER },
98 { "local0", LOG_LOCAL0 },
99 { "local1", LOG_LOCAL1 },
100 { "local2", LOG_LOCAL2 },
101 { "local3", LOG_LOCAL3 },
102 { "local4", LOG_LOCAL4 },
103 { "local5", LOG_LOCAL5 },
104 { "local6", LOG_LOCAL6 },
105 { "local7", LOG_LOCAL7 },
106 #endif
107 { NULL },
108 };
109
reset_logging(void)110 void reset_logging(void)
111 {
112 if (log_file) {
113 fclose(log_file);
114 log_file = NULL;
115 }
116 if (syslog_started) {
117 closelog();
118 syslog_started = 0;
119 }
120 }
121
122
start_syslog(void)123 static void start_syslog(void)
124 {
125 const struct FacName *f;
126 int fac = LOG_DAEMON;
127 const char *ident = cf_syslog_ident;
128
129 if (!cf_syslog)
130 return;
131
132 if (cf_syslog_facility) {
133 for (f = facility_names; f->name; f++) {
134 if (strcmp(f->name, cf_syslog_facility) == 0) {
135 fac = f->code;
136 break;
137 }
138 }
139 }
140
141 if (!ident) {
142 ident = getprogname();
143 if (!ident)
144 ident = "unnamed";
145 }
146
147 openlog(ident, LOG_PID, fac);
148 syslog_started = 1;
149 }
150
151
log_generic(enum LogLevel level,void * ctx,const char * fmt,...)152 void log_generic(enum LogLevel level, void *ctx, const char *fmt, ...)
153 {
154 char buf[2048], buf2[2048];
155 char ebuf[256];
156 char timebuf[64];
157 const struct LevelInfo *lev = &log_level_list[level];
158 unsigned pid = getpid();
159 va_list ap;
160 int pfxlen = 0;
161 int old_errno = errno;
162 char *msg = buf;
163
164 if (logging_prefix_cb) {
165 pfxlen = logging_prefix_cb(level, ctx, buf, sizeof(buf));
166 if (pfxlen < 0)
167 goto done;
168 if (pfxlen >= (int)sizeof(buf))
169 pfxlen = sizeof(buf) - 1;
170 }
171 va_start(ap, fmt);
172 vsnprintf(buf + pfxlen, sizeof(buf) - pfxlen, fmt, ap);
173 va_end(ap);
174
175 /* replace '\n' in message with '\n\t', strip trailing whitespace */
176 if (strchr(msg, '\n')) {
177 char *dst = buf2;
178 for (; *msg && dst - buf2 < (int)sizeof(buf2) - 2; msg++) {
179 *dst++ = *msg;
180 if (*msg == '\n')
181 *dst++ = '\t';
182 }
183 while (dst > buf2 && isspace(dst[-1]))
184 dst--;
185 *dst = 0;
186 msg = buf2;
187 }
188
189 format_time_ms(0, timebuf, sizeof(timebuf));
190
191 if (!log_file && cf_logfile && cf_logfile[0]) {
192 log_file = fopen(cf_logfile, "a");
193 if (log_file) {
194 /* Got the file, disable buffering */
195 setvbuf(log_file, NULL, _IONBF, 0);
196 } else {
197 /* Unable to open, complain and fail */
198 fprintf(stderr, "%s %u %s Cannot open logfile: '%s': %s\n",
199 timebuf, pid, log_level_list[0].tag,
200 cf_logfile,
201 strerror_r(errno, ebuf, sizeof(ebuf)));
202 exit(1);
203 }
204 }
205
206 if (!cf_quiet && level <= cf_stderr_level) {
207 #ifdef USE_SYSTEMD
208 static bool journal_stream_checked = false;
209 static bool use_systemd_journal = false;
210
211 if (!journal_stream_checked) {
212 if (getenv("JOURNAL_STREAM")) {
213 long long unsigned int f1, f2;
214 if (sscanf(getenv("JOURNAL_STREAM"), "%llu:%llu", &f1, &f2) == 2) {
215 struct stat st;
216 dev_t js_dev = f1;
217 ino_t js_ino = f2;
218 if (fstat(fileno(stderr), &st) >= 0)
219 if (js_dev == st.st_dev && js_ino == st.st_ino)
220 use_systemd_journal = true;
221 }
222
223 }
224 journal_stream_checked = true;
225 }
226 if (use_systemd_journal)
227 sd_journal_print(lev->syslog_prio, "%s", msg);
228 else
229 #endif
230 fprintf(stderr, "%s [%u] %s %s\n", timebuf, pid, lev->tag, msg);
231 }
232
233 if (log_file && level <= cf_logfile_level)
234 fprintf(log_file, "%s [%u] %s %s\n", timebuf, pid, lev->tag, msg);
235
236 if (cf_syslog && level <= cf_syslog_level) {
237 if (!syslog_started)
238 start_syslog();
239 syslog(lev->syslog_prio, "%s", msg);
240 }
241 done:
242 if (old_errno != errno)
243 errno = old_errno;
244 }
245
246
log_fatal(const char * file,int line,const char * func,bool show_perror,void * ctx,const char * fmt,...)247 void log_fatal(const char *file, int line, const char *func, bool show_perror, void *ctx, const char *fmt, ...)
248 {
249 char buf[2048], ebuf[256];
250 const char *estr = NULL;
251 int old_errno = 0;
252 va_list ap;
253
254 if (show_perror) {
255 old_errno = errno;
256 estr = strerror_r(errno, ebuf, sizeof(ebuf));
257 }
258
259 va_start(ap, fmt);
260 vsnprintf(buf, sizeof(buf), fmt, ap);
261 va_end(ap);
262
263 if (show_perror) {
264 log_generic(LG_FATAL, ctx, "@%s:%d in function %s(): %s: %s [%d]",
265 file, line, func, buf, estr, old_errno);
266 } else {
267 log_generic(LG_FATAL, ctx, "@%s:%d in function %s(): %s",
268 file, line, func, buf);
269 }
270 }
271
272 #ifdef WIN32
273
win32_eventlog(int level,const char * fmt,...)274 static void win32_eventlog(int level, const char *fmt, ...)
275 {
276 static HANDLE evtHandle = INVALID_HANDLE_VALUE;
277 int elevel;
278 char buf[1024];
279 const char *strlist[1] = { buf };
280 va_list ap;
281
282 va_start(ap, fmt);
283 vsnprintf(buf, sizeof(buf), fmt, ap);
284 va_end(ap);
285
286 switch (level) {
287 case LOG_CRIT:
288 case LOG_ERR:
289 elevel = EVENTLOG_ERROR_TYPE;
290 break;
291 case LOG_WARNING:
292 elevel = EVENTLOG_WARNING_TYPE;
293 break;
294 default:
295 elevel = EVENTLOG_INFORMATION_TYPE;
296 }
297
298 if (evtHandle == INVALID_HANDLE_VALUE) {
299 evtHandle = RegisterEventSource(NULL, cf_syslog_ident);
300 if (evtHandle == NULL || evtHandle == INVALID_HANDLE_VALUE) {
301 evtHandle = INVALID_HANDLE_VALUE;
302 return;
303 }
304 }
305 ReportEvent(evtHandle, elevel, 0, 0, NULL, 1, 0, strlist, NULL);
306 }
307
308 #endif
309