1 /***
2   This file is part of PulseAudio.
3 
4   Copyright 2004-2006 Lennart Poettering
5   Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
6 
7   PulseAudio is free software; you can redistribute it and/or modify
8   it under the terms of the GNU Lesser General Public License as
9   published by the Free Software Foundation; either version 2.1 of the
10   License, or (at your option) any later version.
11 
12   PulseAudio is distributed in the hope that it will be useful, but
13   WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   General Public License for more details.
16 
17   You should have received a copy of the GNU Lesser General Public
18   License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
19 ***/
20 
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 
25 #include <stdarg.h>
26 #include <stdio.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <sys/stat.h>
32 
33 #ifdef HAVE_EXECINFO_H
34 #include <execinfo.h>
35 #endif
36 
37 #ifdef HAVE_SYSLOG_H
38 #include <syslog.h>
39 #endif
40 
41 #ifdef HAVE_SYSTEMD_JOURNAL
42 
43 /* sd_journal_send() implicitly add fields for the source file,
44  * function name and code line from where it's invoked. As the
45  * correct code location fields CODE_FILE, CODE_LINE and
46  * CODE_FUNC are already handled by this module, we do not want
47  * the automatic values supplied by the systemd journal API.
48  *
49  * Without suppressing these, both the actual log event source
50  * and the call to sd_journal_send() will be logged. */
51 #define SD_JOURNAL_SUPPRESS_LOCATION
52 
53 #include <systemd/sd-journal.h>
54 #endif
55 
56 #include <pulse/gccmacro.h>
57 #include <pulse/rtclock.h>
58 #include <pulse/utf8.h>
59 #include <pulse/xmalloc.h>
60 #include <pulse/util.h>
61 #include <pulse/timeval.h>
62 
63 #include <pulsecore/macro.h>
64 #include <pulsecore/core-util.h>
65 #include <pulsecore/core-error.h>
66 #include <pulsecore/once.h>
67 #include <pulsecore/ratelimit.h>
68 #include <pulsecore/thread.h>
69 #include <pulsecore/i18n.h>
70 
71 #include "log.h"
72 
73 #define ENV_LOG_SYSLOG "PULSE_LOG_SYSLOG"
74 #define ENV_LOG_JOURNAL "PULSE_LOG_JOURNAL"
75 #define ENV_LOG_LEVEL "PULSE_LOG"
76 #define ENV_LOG_COLORS "PULSE_LOG_COLORS"
77 #define ENV_LOG_PRINT_TIME "PULSE_LOG_TIME"
78 #define ENV_LOG_PRINT_FILE "PULSE_LOG_FILE"
79 #define ENV_LOG_PRINT_META "PULSE_LOG_META"
80 #define ENV_LOG_PRINT_LEVEL "PULSE_LOG_LEVEL"
81 #define ENV_LOG_BACKTRACE "PULSE_LOG_BACKTRACE"
82 #define ENV_LOG_BACKTRACE_SKIP "PULSE_LOG_BACKTRACE_SKIP"
83 #define ENV_LOG_NO_RATELIMIT "PULSE_LOG_NO_RATE_LIMIT"
84 #define LOG_MAX_SUFFIX_NUMBER 99
85 
86 static char *ident = NULL; /* in local charset format */
87 static pa_log_target target = { PA_LOG_STDERR, NULL };
88 static pa_log_target_type_t target_override;
89 static bool target_override_set = false;
90 static pa_log_level_t maximum_level = PA_LOG_ERROR, maximum_level_override = PA_LOG_ERROR;
91 static unsigned show_backtrace = 0, show_backtrace_override = 0, skip_backtrace = 0;
92 static pa_log_flags_t flags = 0, flags_override = 0;
93 static bool no_rate_limit = false;
94 static int log_fd = -1;
95 static int write_type = 0;
96 
97 #ifdef HAVE_SYSLOG_H
98 static const int level_to_syslog[] = {
99     [PA_LOG_ERROR] = LOG_ERR,
100     [PA_LOG_WARN] = LOG_WARNING,
101     [PA_LOG_NOTICE] = LOG_NOTICE,
102     [PA_LOG_INFO] = LOG_INFO,
103     [PA_LOG_DEBUG] = LOG_DEBUG
104 };
105 #endif
106 
107 /* These are actually equivalent to the syslog ones
108  * but we don't want to depend on syslog.h */
109 #ifdef HAVE_SYSTEMD_JOURNAL
110 static const int level_to_journal[] = {
111     [PA_LOG_ERROR]  = 3,
112     [PA_LOG_WARN]   = 4,
113     [PA_LOG_NOTICE] = 5,
114     [PA_LOG_INFO]   = 6,
115     [PA_LOG_DEBUG]  = 7
116 };
117 #endif
118 
119 static const char level_to_char[] = {
120     [PA_LOG_ERROR] = 'E',
121     [PA_LOG_WARN] = 'W',
122     [PA_LOG_NOTICE] = 'N',
123     [PA_LOG_INFO] = 'I',
124     [PA_LOG_DEBUG] = 'D'
125 };
126 
pa_log_set_ident(const char *p)127 void pa_log_set_ident(const char *p) {
128     pa_xfree(ident);
129 
130     if (!(ident = pa_utf8_to_locale(p)))
131         ident = pa_ascii_filter(p);
132 }
133 
134 /* To make valgrind shut up. */
135 static void ident_destructor(void) PA_GCC_DESTRUCTOR;
ident_destructor(void)136 static void ident_destructor(void) {
137     if (!pa_in_valgrind())
138         return;
139 
140     pa_xfree(ident);
141 }
142 
pa_log_set_level(pa_log_level_t l)143 void pa_log_set_level(pa_log_level_t l) {
144     pa_assert(l < PA_LOG_LEVEL_MAX);
145 
146     maximum_level = l;
147 }
148 
pa_log_set_target(pa_log_target *t)149 int pa_log_set_target(pa_log_target *t) {
150     int fd = -1;
151     int old_fd;
152 
153     pa_assert(t);
154 
155     switch (t->type) {
156         case PA_LOG_STDERR:
157         case PA_LOG_SYSLOG:
158 #ifdef HAVE_SYSTEMD_JOURNAL
159         case PA_LOG_JOURNAL:
160 #endif
161         case PA_LOG_NULL:
162             break;
163         case PA_LOG_FILE:
164             if ((fd = pa_open_cloexec(t->file, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR)) < 0) {
165                 pa_log(_("Failed to open target file '%s'."), t->file);
166                 return -1;
167             }
168             break;
169         case PA_LOG_NEWFILE: {
170             char *file_path;
171             char *p;
172             unsigned version;
173 
174             file_path = pa_sprintf_malloc("%s.xx", t->file);
175             p = file_path + strlen(t->file);
176 
177             for (version = 0; version <= LOG_MAX_SUFFIX_NUMBER; version++) {
178                 memset(p, 0, 3); /* Overwrite the ".xx" part in file_path with zero bytes. */
179 
180                 if (version > 0)
181                     pa_snprintf(p, 4, ".%u", version); /* Why 4? ".xx" + termitating zero byte. */
182 
183                 if ((fd = pa_open_cloexec(file_path, O_WRONLY | O_TRUNC | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) >= 0)
184                     break;
185             }
186 
187             if (version > LOG_MAX_SUFFIX_NUMBER) {
188                 pa_log(_("Tried to open target file '%s', '%s.1', '%s.2' ... '%s.%d', but all failed."),
189                         t->file, t->file, t->file, t->file, LOG_MAX_SUFFIX_NUMBER);
190                 pa_xfree(file_path);
191                 return -1;
192             } else
193                 pa_log_debug("Opened target file %s\n", file_path);
194 
195             pa_xfree(file_path);
196             break;
197         }
198     }
199 
200     target.type = t->type;
201     pa_xfree(target.file);
202     target.file = pa_xstrdup(t->file);
203 
204     old_fd = log_fd;
205     log_fd = fd;
206 
207     if (old_fd >= 0)
208         pa_close(old_fd);
209 
210     return 0;
211 }
212 
pa_log_set_flags(pa_log_flags_t _flags, pa_log_merge_t merge)213 void pa_log_set_flags(pa_log_flags_t _flags, pa_log_merge_t merge) {
214     pa_assert(!(_flags & ~(PA_LOG_COLORS|PA_LOG_PRINT_TIME|PA_LOG_PRINT_FILE|PA_LOG_PRINT_META|PA_LOG_PRINT_LEVEL)));
215 
216     if (merge == PA_LOG_SET)
217         flags |= _flags;
218     else if (merge == PA_LOG_UNSET)
219         flags &= ~_flags;
220     else
221         flags = _flags;
222 }
223 
pa_log_set_show_backtrace(unsigned nlevels)224 void pa_log_set_show_backtrace(unsigned nlevels) {
225     show_backtrace = nlevels;
226 }
227 
pa_log_set_skip_backtrace(unsigned nlevels)228 void pa_log_set_skip_backtrace(unsigned nlevels) {
229     skip_backtrace = nlevels;
230 }
231 
232 #ifdef HAVE_EXECINFO_H
233 
get_backtrace(unsigned show_nframes)234 static char* get_backtrace(unsigned show_nframes) {
235     void* trace[32];
236     int n_frames;
237     char **symbols, *e, *r;
238     unsigned j, n, s;
239     size_t a;
240 
241     pa_assert(show_nframes > 0);
242 
243     n_frames = backtrace(trace, PA_ELEMENTSOF(trace));
244 
245     if (n_frames <= 0)
246         return NULL;
247 
248     symbols = backtrace_symbols(trace, n_frames);
249 
250     if (!symbols)
251         return NULL;
252 
253     s = skip_backtrace;
254     n = PA_MIN((unsigned) n_frames, s + show_nframes);
255 
256     a = 4;
257 
258     for (j = s; j < n; j++) {
259         if (j > s)
260             a += 2;
261         a += strlen(pa_path_get_filename(symbols[j]));
262     }
263 
264     r = pa_xnew(char, a);
265 
266     strcpy(r, " (");
267     e = r + 2;
268 
269     for (j = s; j < n; j++) {
270         const char *sym;
271 
272         if (j > s) {
273             strcpy(e, "<<");
274             e += 2;
275         }
276 
277         sym = pa_path_get_filename(symbols[j]);
278 
279         strcpy(e, sym);
280         e += strlen(sym);
281     }
282 
283     strcpy(e, ")");
284 
285     free(symbols);
286 
287     return r;
288 }
289 
290 #endif
291 
init_defaults(void)292 static void init_defaults(void) {
293     PA_ONCE_BEGIN {
294 
295         const char *e;
296 
297         if (!ident) {
298             char binary[256];
299             if (pa_get_binary_name(binary, sizeof(binary)))
300                 pa_log_set_ident(binary);
301         }
302 
303         if (getenv(ENV_LOG_SYSLOG)) {
304             target_override = PA_LOG_SYSLOG;
305             target_override_set = true;
306         }
307 
308 #ifdef HAVE_SYSTEMD_JOURNAL
309         if (getenv(ENV_LOG_JOURNAL)) {
310             target_override = PA_LOG_JOURNAL;
311             target_override_set = true;
312         }
313 #endif
314 
315         if ((e = getenv(ENV_LOG_LEVEL))) {
316             maximum_level_override = (pa_log_level_t) atoi(e);
317 
318             if (maximum_level_override >= PA_LOG_LEVEL_MAX)
319                 maximum_level_override = PA_LOG_LEVEL_MAX-1;
320         }
321 
322         if (getenv(ENV_LOG_COLORS))
323             flags_override |= PA_LOG_COLORS;
324 
325         if (getenv(ENV_LOG_PRINT_TIME))
326             flags_override |= PA_LOG_PRINT_TIME;
327 
328         if (getenv(ENV_LOG_PRINT_FILE))
329             flags_override |= PA_LOG_PRINT_FILE;
330 
331         if (getenv(ENV_LOG_PRINT_META))
332             flags_override |= PA_LOG_PRINT_META;
333 
334         if (getenv(ENV_LOG_PRINT_LEVEL))
335             flags_override |= PA_LOG_PRINT_LEVEL;
336 
337         if ((e = getenv(ENV_LOG_BACKTRACE))) {
338             show_backtrace_override = (unsigned) atoi(e);
339 
340             if (show_backtrace_override <= 0)
341                 show_backtrace_override = 0;
342         }
343 
344         if ((e = getenv(ENV_LOG_BACKTRACE_SKIP))) {
345             skip_backtrace = (unsigned) atoi(e);
346 
347             if (skip_backtrace <= 0)
348                 skip_backtrace = 0;
349         }
350 
351         if (getenv(ENV_LOG_NO_RATELIMIT))
352             no_rate_limit = true;
353 
354     } PA_ONCE_END;
355 }
356 
357 #ifdef HAVE_SYSLOG_H
log_syslog(pa_log_level_t level, char *t, char *timestamp, char *location, char *bt)358 static void log_syslog(pa_log_level_t level, char *t, char *timestamp, char *location, char *bt) {
359     char *local_t;
360 
361     openlog(ident, LOG_PID, LOG_USER);
362 
363     if ((local_t = pa_utf8_to_locale(t)))
364         t = local_t;
365 
366     syslog(level_to_syslog[level], "%s%s%s%s", timestamp, location, t, pa_strempty(bt));
367     pa_xfree(local_t);
368 }
369 #endif
370 
pa_log_levelv_meta( pa_log_level_t level, const char*file, int line, const char *func, const char *format, va_list ap)371 void pa_log_levelv_meta(
372         pa_log_level_t level,
373         const char*file,
374         int line,
375         const char *func,
376         const char *format,
377         va_list ap) {
378 
379     char *t, *n;
380     int saved_errno = errno;
381     char *bt = NULL;
382     pa_log_target_type_t _target;
383     pa_log_level_t _maximum_level;
384     unsigned _show_backtrace;
385     pa_log_flags_t _flags;
386 
387     /* We don't use dynamic memory allocation here to minimize the hit
388      * in RT threads */
389     char text[256], location[128], timestamp[32];
390 
391     pa_assert(level < PA_LOG_LEVEL_MAX);
392     pa_assert(format);
393 
394     init_defaults();
395 
396     _target = target_override_set ? target_override : target.type;
397     _maximum_level = PA_MAX(maximum_level, maximum_level_override);
398     _show_backtrace = PA_MAX(show_backtrace, show_backtrace_override);
399     _flags = flags | flags_override;
400 
401     if (PA_LIKELY(level > _maximum_level)) {
402         errno = saved_errno;
403         return;
404     }
405 
406     pa_vsnprintf(text, sizeof(text), format, ap);
407 
408     const char *filename = strrchr(file, '/');
409     if (filename) {
410         filename++;
411     } else {
412         filename = file;
413     }
414 
415     if (level == PA_LOG_ERROR) {
416         AUDIO_ERR_LOG("%{public}s:%{public}u (%{public}s) %{public}s", filename, line, func, text);
417     } else if (level == PA_LOG_WARN) {
418         AUDIO_WARNING_LOG("%{public}s:%{public}u (%{public}s) %{public}s", filename, line, func, text);
419     } else if (level == PA_LOG_NOTICE || level == PA_LOG_INFO) {
420         AUDIO_INFO_LOG("%{public}s:%{public}u (%{public}s) %{public}s", filename, line, func, text);
421     } else if (level == PA_LOG_DEBUG) {
422         AUDIO_DEBUG_LOG("%{public}s:%{public}u (%{public}s) %{public}s", filename, line, func, text);
423     }
424 
425     if ((_flags & PA_LOG_PRINT_META) && file && line > 0 && func)
426         pa_snprintf(location, sizeof(location), "[%s][%s:%i %s()] ",
427                     pa_strnull(pa_thread_get_name(pa_thread_self())), file, line, func);
428     else if ((_flags & (PA_LOG_PRINT_META|PA_LOG_PRINT_FILE)) && file)
429         pa_snprintf(location, sizeof(location), "[%s] %s: ",
430                     pa_strnull(pa_thread_get_name(pa_thread_self())), pa_path_get_filename(file));
431     else
432         location[0] = 0;
433 
434     if (_flags & PA_LOG_PRINT_TIME) {
435         static pa_usec_t start, last;
436         pa_usec_t u, a, r;
437 
438         u = pa_rtclock_now();
439 
440         PA_ONCE_BEGIN {
441             start = u;
442             last = u;
443         } PA_ONCE_END;
444 
445         r = u - last;
446         a = u - start;
447 
448         /* This is not thread safe, but this is a debugging tool only
449          * anyway. */
450         last = u;
451 
452         pa_snprintf(timestamp, sizeof(timestamp), "(%4llu.%03llu|%4llu.%03llu) ",
453                     (unsigned long long) (a / PA_USEC_PER_SEC),
454                     (unsigned long long) (((a / PA_USEC_PER_MSEC)) % 1000),
455                     (unsigned long long) (r / PA_USEC_PER_SEC),
456                     (unsigned long long) (((r / PA_USEC_PER_MSEC)) % 1000));
457 
458     } else
459         timestamp[0] = 0;
460 
461 #ifdef HAVE_EXECINFO_H
462     if (_show_backtrace > 0)
463         bt = get_backtrace(_show_backtrace);
464 #endif
465 
466     if (!pa_utf8_valid(text))
467         pa_logl(level, "Invalid UTF-8 string following below:");
468 
469     for (t = text; t; t = n) {
470         if ((n = strchr(t, '\n'))) {
471             *n = 0;
472             n++;
473         }
474 
475         /* We ignore strings only made out of whitespace */
476         if (t[strspn(t, "\t ")] == 0)
477             continue;
478 
479         switch (_target) {
480 
481             case PA_LOG_STDERR: {
482                 const char *prefix = "", *suffix = "", *grey = "";
483                 char *local_t;
484 
485 #ifndef OS_IS_WIN32
486                 /* Yes indeed. Useless, but fun! */
487                 if ((_flags & PA_LOG_COLORS) && isatty(STDERR_FILENO)) {
488                     if (level <= PA_LOG_ERROR)
489                         prefix = "\x1B[1;31m";
490                     else if (level <= PA_LOG_WARN)
491                         prefix = "\x1B[1m";
492 
493                     if (bt)
494                         grey = "\x1B[2m";
495 
496                     if (grey[0] || prefix[0])
497                         suffix = "\x1B[0m";
498                 }
499 #endif
500 
501                 /* We shouldn't be using dynamic allocation here to
502                  * minimize the hit in RT threads */
503                 if ((local_t = pa_utf8_to_locale(t)))
504                     t = local_t;
505 
506                 if (_flags & PA_LOG_PRINT_LEVEL)
507                     fprintf(stderr, "%s%c: %s%s%s%s%s%s\n", timestamp, level_to_char[level], location, prefix, t, grey, pa_strempty(bt), suffix);
508                 else
509                     fprintf(stderr, "%s%s%s%s%s%s%s\n", timestamp, location, prefix, t, grey, pa_strempty(bt), suffix);
510 #ifdef OS_IS_WIN32
511                 fflush(stderr);
512 #endif
513 
514                 pa_xfree(local_t);
515 
516                 break;
517             }
518 
519 #ifdef HAVE_SYSLOG_H
520             case PA_LOG_SYSLOG:
521                 log_syslog(level, t, timestamp, location, bt);
522                 break;
523 #endif
524 
525 #ifdef HAVE_SYSTEMD_JOURNAL
526             case PA_LOG_JOURNAL:
527                 if (sd_journal_send("MESSAGE=%s", t,
528                                 "PRIORITY=%i", level_to_journal[level],
529                                 "CODE_FILE=%s", file,
530                                 "CODE_FUNC=%s", func,
531                                 "CODE_LINE=%d", line,
532                                 "PULSE_BACKTRACE=%s", pa_strempty(bt),
533                                 NULL) < 0) {
534 #ifdef HAVE_SYSLOG_H
535                     pa_log_target new_target = { .type = PA_LOG_SYSLOG, .file = NULL };
536 
537                     syslog(level_to_syslog[PA_LOG_ERROR], "%s%s%s", timestamp, __FILE__,
538                            "Error writing logs to the journal. Redirect log messages to syslog.");
539                     log_syslog(level, t, timestamp, location, bt);
540 #else
541                     pa_log_target new_target = { .type = PA_LOG_STDERR, .file = NULL };
542 
543                     saved_errno = errno;
544                     fprintf(stderr, "%s\n", "Error writing logs to the journal. Redirect log messages to console.");
545                     fprintf(stderr, "%s\n", t);
546 #endif
547                     pa_log_set_target(&new_target);
548                 }
549                 break;
550 #endif
551 
552             case PA_LOG_FILE:
553             case PA_LOG_NEWFILE: {
554                 char *local_t;
555 
556                 if ((local_t = pa_utf8_to_locale(t)))
557                     t = local_t;
558 
559                 if (log_fd >= 0) {
560                     char metadata[256];
561 
562                     if (_flags & PA_LOG_PRINT_LEVEL)
563                         pa_snprintf(metadata, sizeof(metadata), "%s%c: %s", timestamp, level_to_char[level], location);
564                     else
565                         pa_snprintf(metadata, sizeof(metadata), "%s%s", timestamp, location);
566 
567                     if ((pa_write(log_fd, metadata, strlen(metadata), &write_type) < 0)
568                             || (pa_write(log_fd, t, strlen(t), &write_type) < 0)
569                             || (bt && pa_write(log_fd, bt, strlen(bt), &write_type) < 0)
570                             || (pa_write(log_fd, "\n", 1, &write_type) < 0)) {
571                         pa_log_target new_target = { .type = PA_LOG_STDERR, .file = NULL };
572                         saved_errno = errno;
573                         fprintf(stderr, "%s\n", "Error writing logs to a file descriptor. Redirect log messages to console.");
574                         fprintf(stderr, "%s %s\n", metadata, t);
575                         pa_log_set_target(&new_target);
576                     }
577                 }
578 
579                 pa_xfree(local_t);
580 
581                 break;
582             }
583             case PA_LOG_NULL:
584             default:
585                 break;
586         }
587     }
588 
589     pa_xfree(bt);
590     errno = saved_errno;
591 }
592 
PrintCallStackInfonull593 void PrintCallStackInfo()
594 {
595     const int32_t maxDepth = 20;
596     void *stacks[maxDepth];
597 
598     int stackNum = backtrace(stacks, maxDepth);
599     AUDIO_ERR_LOG("backtrace() returned %{public}d addresses\n", stackNum);
600 
601     char **symbols = backtrace_symbols(stacks, stackNum);
602     if (symbols == NULL) {
603         AUDIO_ERR_LOG("backtrace_symbols faile.");
604         for (int i = 0; i < stackNum; i++) {
605             AUDIO_ERR_LOG("  [%{public}02d] addr: %{public}p\n", i, stacks[i]);
606         }
607         return;
608     }
609 
610     for (int i = 0; i < stackNum; i++) {
611         AUDIO_ERR_LOG("  [%{public}02d] %{public}s\n", i, symbols[i]);
612     }
613 
614     free(symbols);
615 }
616 
pa_log_level_meta( pa_log_level_t level, const char*file, int line, const char *func, const char *format, ...)617 void pa_log_level_meta(
618         pa_log_level_t level,
619         const char*file,
620         int line,
621         const char *func,
622         const char *format, ...) {
623 
624     va_list ap;
625     va_start(ap, format);
626     pa_log_levelv_meta(level, file, line, func, format, ap);
627     va_end(ap);
628 }
629 
pa_log_levelv(pa_log_level_t level, const char *format, va_list ap)630 void pa_log_levelv(pa_log_level_t level, const char *format, va_list ap) {
631     pa_log_levelv_meta(level, NULL, 0, NULL, format, ap);
632 }
633 
pa_log_level(pa_log_level_t level, const char *format, ...)634 void pa_log_level(pa_log_level_t level, const char *format, ...) {
635     va_list ap;
636 
637     va_start(ap, format);
638     pa_log_levelv_meta(level, NULL, 0, NULL, format, ap);
639     va_end(ap);
640 }
641 
pa_log_ratelimit(pa_log_level_t level)642 bool pa_log_ratelimit(pa_log_level_t level) {
643     /* Not more than 10 messages every 5s */
644     static PA_DEFINE_RATELIMIT(ratelimit, 5 * PA_USEC_PER_SEC, 10);
645 
646     init_defaults();
647 
648     if (no_rate_limit)
649         return true;
650 
651     return pa_ratelimit_test(&ratelimit, level);
652 }
653 
pa_log_target_new(pa_log_target_type_t type, const char *file)654 pa_log_target *pa_log_target_new(pa_log_target_type_t type, const char *file) {
655     pa_log_target *t = NULL;
656 
657     t = pa_xnew(pa_log_target, 1);
658 
659     t->type = type;
660     t->file = pa_xstrdup(file);
661 
662     return t;
663 }
664 
pa_log_target_free(pa_log_target *t)665 void pa_log_target_free(pa_log_target *t) {
666     pa_assert(t);
667 
668     pa_xfree(t->file);
669     pa_xfree(t);
670 }
671 
pa_log_parse_target(const char *string)672 pa_log_target *pa_log_parse_target(const char *string) {
673     pa_log_target *t = NULL;
674 
675     pa_assert(string);
676 
677     if (pa_streq(string, "stderr"))
678         t = pa_log_target_new(PA_LOG_STDERR, NULL);
679     else if (pa_streq(string, "syslog"))
680         t = pa_log_target_new(PA_LOG_SYSLOG, NULL);
681 #ifdef HAVE_SYSTEMD_JOURNAL
682     else if (pa_streq(string, "journal"))
683         t = pa_log_target_new(PA_LOG_JOURNAL, NULL);
684 #endif
685     else if (pa_streq(string, "null"))
686         t = pa_log_target_new(PA_LOG_NULL, NULL);
687     else if (pa_startswith(string, "file:"))
688         t = pa_log_target_new(PA_LOG_FILE, string + 5);
689     else if (pa_startswith(string, "newfile:"))
690         t = pa_log_target_new(PA_LOG_NEWFILE, string + 8);
691     else
692         pa_log(_("Invalid log target."));
693 
694     return t;
695 }
696 
pa_log_target_to_string(const pa_log_target *t)697 char *pa_log_target_to_string(const pa_log_target *t) {
698     char *string = NULL;
699 
700     pa_assert(t);
701 
702     switch (t->type) {
703         case PA_LOG_STDERR:
704             string = pa_xstrdup("stderr");
705             break;
706         case PA_LOG_SYSLOG:
707             string = pa_xstrdup("syslog");
708             break;
709 #ifdef HAVE_SYSTEMD_JOURNAL
710         case PA_LOG_JOURNAL:
711             string = pa_xstrdup("journal");
712             break;
713 #endif
714         case PA_LOG_NULL:
715             string = pa_xstrdup("null");
716             break;
717         case PA_LOG_FILE:
718             string = pa_sprintf_malloc("file:%s", t->file);
719             break;
720         case PA_LOG_NEWFILE:
721             string = pa_sprintf_malloc("newfile:%s", t->file);
722             break;
723     }
724 
725     return string;
726 }
727