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 86static char *ident = NULL; /* in local charset format */ 87static pa_log_target target = { PA_LOG_STDERR, NULL }; 88static pa_log_target_type_t target_override; 89static bool target_override_set = false; 90static pa_log_level_t maximum_level = PA_LOG_ERROR, maximum_level_override = PA_LOG_ERROR; 91static unsigned show_backtrace = 0, show_backtrace_override = 0, skip_backtrace = 0; 92static pa_log_flags_t flags = 0, flags_override = 0; 93static bool no_rate_limit = false; 94static int log_fd = -1; 95static int write_type = 0; 96 97#ifdef HAVE_SYSLOG_H 98static 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 110static 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 119static 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 127void 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. */ 135static void ident_destructor(void) PA_GCC_DESTRUCTOR; 136static void ident_destructor(void) { 137 if (!pa_in_valgrind()) 138 return; 139 140 pa_xfree(ident); 141} 142 143void pa_log_set_level(pa_log_level_t l) { 144 pa_assert(l < PA_LOG_LEVEL_MAX); 145 146 maximum_level = l; 147} 148 149int 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 213void 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 224void pa_log_set_show_backtrace(unsigned nlevels) { 225 show_backtrace = nlevels; 226} 227 228void pa_log_set_skip_backtrace(unsigned nlevels) { 229 skip_backtrace = nlevels; 230} 231 232#ifdef HAVE_EXECINFO_H 233 234static 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 292static 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 358static 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 371void 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 593void 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 617void 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 630void 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 634void 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 642bool 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 654pa_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 665void pa_log_target_free(pa_log_target *t) { 666 pa_assert(t); 667 668 pa_xfree(t->file); 669 pa_xfree(t); 670} 671 672pa_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 697char *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