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
pa_log_level_meta(pa_log_level_t level,const char * file,int line,const char * func,const char * format,...)593 void pa_log_level_meta(
594 pa_log_level_t level,
595 const char*file,
596 int line,
597 const char *func,
598 const char *format, ...) {
599
600 va_list ap;
601 va_start(ap, format);
602 pa_log_levelv_meta(level, file, line, func, format, ap);
603 va_end(ap);
604 }
605
pa_log_levelv(pa_log_level_t level,const char * format,va_list ap)606 void pa_log_levelv(pa_log_level_t level, const char *format, va_list ap) {
607 pa_log_levelv_meta(level, NULL, 0, NULL, format, ap);
608 }
609
pa_log_level(pa_log_level_t level,const char * format,...)610 void pa_log_level(pa_log_level_t level, const char *format, ...) {
611 va_list ap;
612
613 va_start(ap, format);
614 pa_log_levelv_meta(level, NULL, 0, NULL, format, ap);
615 va_end(ap);
616 }
617
pa_log_ratelimit(pa_log_level_t level)618 bool pa_log_ratelimit(pa_log_level_t level) {
619 /* Not more than 10 messages every 5s */
620 static PA_DEFINE_RATELIMIT(ratelimit, 5 * PA_USEC_PER_SEC, 10);
621
622 init_defaults();
623
624 if (no_rate_limit)
625 return true;
626
627 return pa_ratelimit_test(&ratelimit, level);
628 }
629
pa_log_target_new(pa_log_target_type_t type,const char * file)630 pa_log_target *pa_log_target_new(pa_log_target_type_t type, const char *file) {
631 pa_log_target *t = NULL;
632
633 t = pa_xnew(pa_log_target, 1);
634
635 t->type = type;
636 t->file = pa_xstrdup(file);
637
638 return t;
639 }
640
pa_log_target_free(pa_log_target * t)641 void pa_log_target_free(pa_log_target *t) {
642 pa_assert(t);
643
644 pa_xfree(t->file);
645 pa_xfree(t);
646 }
647
pa_log_parse_target(const char * string)648 pa_log_target *pa_log_parse_target(const char *string) {
649 pa_log_target *t = NULL;
650
651 pa_assert(string);
652
653 if (pa_streq(string, "stderr"))
654 t = pa_log_target_new(PA_LOG_STDERR, NULL);
655 else if (pa_streq(string, "syslog"))
656 t = pa_log_target_new(PA_LOG_SYSLOG, NULL);
657 #ifdef HAVE_SYSTEMD_JOURNAL
658 else if (pa_streq(string, "journal"))
659 t = pa_log_target_new(PA_LOG_JOURNAL, NULL);
660 #endif
661 else if (pa_streq(string, "null"))
662 t = pa_log_target_new(PA_LOG_NULL, NULL);
663 else if (pa_startswith(string, "file:"))
664 t = pa_log_target_new(PA_LOG_FILE, string + 5);
665 else if (pa_startswith(string, "newfile:"))
666 t = pa_log_target_new(PA_LOG_NEWFILE, string + 8);
667 else
668 pa_log(_("Invalid log target."));
669
670 return t;
671 }
672
pa_log_target_to_string(const pa_log_target * t)673 char *pa_log_target_to_string(const pa_log_target *t) {
674 char *string = NULL;
675
676 pa_assert(t);
677
678 switch (t->type) {
679 case PA_LOG_STDERR:
680 string = pa_xstrdup("stderr");
681 break;
682 case PA_LOG_SYSLOG:
683 string = pa_xstrdup("syslog");
684 break;
685 #ifdef HAVE_SYSTEMD_JOURNAL
686 case PA_LOG_JOURNAL:
687 string = pa_xstrdup("journal");
688 break;
689 #endif
690 case PA_LOG_NULL:
691 string = pa_xstrdup("null");
692 break;
693 case PA_LOG_FILE:
694 string = pa_sprintf_malloc("file:%s", t->file);
695 break;
696 case PA_LOG_NEWFILE:
697 string = pa_sprintf_malloc("newfile:%s", t->file);
698 break;
699 }
700
701 return string;
702 }
703