logging.c 27.8 KB
Newer Older
1 2 3
/*
 * logging.c: internal logging and debugging
 *
4
 * Copyright (C) 2008, 2010-2011 Red Hat, Inc.
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
 *
 */

#include <config.h>

D
Daniel Veillard 已提交
24 25 26 27 28 29 30 31
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
32
#include <unistd.h>
D
Daniel Veillard 已提交
33
#if HAVE_SYSLOG_H
34
# include <syslog.h>
D
Daniel Veillard 已提交
35 36
#endif

37
#include "ignore-value.h"
38
#include "logging.h"
D
Daniel Veillard 已提交
39 40
#include "memory.h"
#include "util.h"
41
#include "buf.h"
42
#include "threads.h"
43
#include "files.h"
44

D
Daniel Veillard 已提交
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
/*
 * Macro used to format the message as a string in virLogMessage
 * and borrowed from libxml2 (also used in virRaiseError)
 */
#define VIR_GET_VAR_STR(msg, str) {				\
    int       size, prev_size = -1;				\
    int       chars;						\
    char      *larger;						\
    va_list   ap;						\
                                                                \
    str = (char *) malloc(150);					\
    if (str != NULL) {						\
                                                                \
    size = 150;							\
                                                                \
    while (1) {							\
        va_start(ap, msg);					\
        chars = vsnprintf(str, size, msg, ap);			\
        va_end(ap);						\
        if ((chars > -1) && (chars < size)) {			\
            if (prev_size == chars) {				\
                break;						\
            } else {						\
                prev_size = chars;				\
            }							\
        }							\
        if (chars > -1)						\
            size += chars + 1;					\
        else							\
            size += 100;					\
        if ((larger = (char *) realloc(str, size)) == NULL) {	\
            break;						\
        }							\
        str = larger;						\
    }}								\
}

/*
 * A logging buffer to keep some history over logs
 */
#define LOG_BUFFER_SIZE 64000

static char virLogBuffer[LOG_BUFFER_SIZE + 1];
static int virLogLen = 0;
static int virLogStart = 0;
static int virLogEnd = 0;

/*
 * Filters are used to refine the rules on what to keep or drop
 * based on a matching pattern (currently a substring)
 */
struct _virLogFilter {
    const char *match;
    int priority;
};
typedef struct _virLogFilter virLogFilter;
typedef virLogFilter *virLogFilterPtr;

static virLogFilterPtr virLogFilters = NULL;
static int virLogNbFilters = 0;

/*
 * Outputs are used to emit the messages retained
 * after filtering, multiple output can be used simultaneously
 */
struct _virLogOutput {
111
    bool logVersion;
D
Daniel Veillard 已提交
112 113 114 115
    void *data;
    virLogOutputFunc f;
    virLogCloseFunc c;
    int priority;
116 117
    virLogDestination dest;
    const char *name;
D
Daniel Veillard 已提交
118 119 120 121 122 123 124 125 126 127
};
typedef struct _virLogOutput virLogOutput;
typedef virLogOutput *virLogOutputPtr;

static virLogOutputPtr virLogOutputs = NULL;
static int virLogNbOutputs = 0;

/*
 * Default priorities
 */
128
static virLogPriority virLogDefaultPriority = VIR_LOG_DEFAULT;
D
Daniel Veillard 已提交
129 130 131 132 133 134 135

static int virLogResetFilters(void);
static int virLogResetOutputs(void);

/*
 * Logs accesses must be serialized though a mutex
 */
136
virMutex virLogMutex;
D
Daniel Veillard 已提交
137

138
void virLogLock(void)
D
Daniel Veillard 已提交
139
{
140
    virMutexLock(&virLogMutex);
D
Daniel Veillard 已提交
141
}
142
void virLogUnlock(void)
D
Daniel Veillard 已提交
143
{
144
    virMutexUnlock(&virLogMutex);
D
Daniel Veillard 已提交
145 146
}

147 148
static const char *virLogOutputString(virLogDestination ldest) {
    switch (ldest) {
149 150 151 152 153 154
    case VIR_LOG_TO_STDERR:
        return "stderr";
    case VIR_LOG_TO_SYSLOG:
        return "syslog";
    case VIR_LOG_TO_FILE:
        return "file";
155
    }
156
    return "unknown";
157
}
D
Daniel Veillard 已提交
158 159 160

static const char *virLogPriorityString(virLogPriority lvl) {
    switch (lvl) {
161 162 163 164 165 166 167 168
    case VIR_LOG_DEBUG:
        return "debug";
    case VIR_LOG_INFO:
        return "info";
    case VIR_LOG_WARN:
        return "warning";
    case VIR_LOG_ERROR:
        return "error";
D
Daniel Veillard 已提交
169
    }
170
    return "unknown";
D
Daniel Veillard 已提交
171 172 173 174 175 176 177 178 179 180 181 182 183
}

static int virLogInitialized = 0;

/**
 * virLogStartup:
 *
 * Initialize the logging module
 *
 * Returns 0 if successful, and -1 in case or error
 */
int virLogStartup(void) {
    if (virLogInitialized)
184
        return -1;
185 186 187 188

    if (virMutexInit(&virLogMutex) < 0)
        return -1;

D
Daniel Veillard 已提交
189 190 191 192 193
    virLogInitialized = 1;
    virLogLock();
    virLogLen = 0;
    virLogStart = 0;
    virLogEnd = 0;
194
    virLogDefaultPriority = VIR_LOG_DEFAULT;
D
Daniel Veillard 已提交
195
    virLogUnlock();
196
    return 0;
D
Daniel Veillard 已提交
197 198 199 200 201 202 203 204 205 206 207
}

/**
 * virLogReset:
 *
 * Reset the logging module to its default initial state
 *
 * Returns 0 if successful, and -1 in case or error
 */
int virLogReset(void) {
    if (!virLogInitialized)
208
        return virLogStartup();
D
Daniel Veillard 已提交
209 210 211 212 213 214 215

    virLogLock();
    virLogResetFilters();
    virLogResetOutputs();
    virLogLen = 0;
    virLogStart = 0;
    virLogEnd = 0;
216
    virLogDefaultPriority = VIR_LOG_DEFAULT;
D
Daniel Veillard 已提交
217
    virLogUnlock();
218
    return 0;
D
Daniel Veillard 已提交
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
}
/**
 * virLogShutdown:
 *
 * Shutdown the logging module
 */
void virLogShutdown(void) {
    if (!virLogInitialized)
        return;
    virLogLock();
    virLogResetFilters();
    virLogResetOutputs();
    virLogLen = 0;
    virLogStart = 0;
    virLogEnd = 0;
    virLogUnlock();
235
    virMutexDestroy(&virLogMutex);
D
Daniel Veillard 已提交
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
    virLogInitialized = 0;
}

/*
 * Store a string in the ring buffer
 */
static void virLogStr(const char *str, int len) {
    int tmp;

    if (str == NULL)
        return;
    if (len <= 0)
        len = strlen(str);
    if (len > LOG_BUFFER_SIZE)
        return;
    virLogLock();

    /*
     * copy the data and reset the end, we cycle over the end of the buffer
     */
    if (virLogEnd + len >= LOG_BUFFER_SIZE) {
        tmp = LOG_BUFFER_SIZE - virLogEnd;
        memcpy(&virLogBuffer[virLogEnd], str, tmp);
        virLogBuffer[LOG_BUFFER_SIZE] = 0;
260
        memcpy(&virLogBuffer[0], &str[tmp], len - tmp);
D
Daniel Veillard 已提交
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
        virLogEnd = len - tmp;
    } else {
        memcpy(&virLogBuffer[virLogEnd], str, len);
        virLogEnd += len;
    }
    /*
     * Update the log length, and if full move the start index
     */
    virLogLen += len;
    if (virLogLen > LOG_BUFFER_SIZE) {
        tmp = virLogLen - LOG_BUFFER_SIZE;
        virLogLen = LOG_BUFFER_SIZE;
        virLogStart += tmp;
    }
    virLogUnlock();
}

#if 0
/*
 * Output the ring buffer
 */
static int virLogDump(void *data, virLogOutputFunc f) {
    int ret = 0, tmp;

    if ((virLogLen == 0) || (f == NULL))
286
        return 0;
D
Daniel Veillard 已提交
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
    virLogLock();
    if (virLogStart + virLogLen < LOG_BUFFER_SIZE) {
push_end:
        virLogBuffer[virLogStart + virLogLen] = 0;
        tmp = f(data, &virLogBuffer[virLogStart], virLogLen);
        if (tmp < 0) {
            ret = -1;
            goto error;
        }
        ret += tmp;
        virLogStart += tmp;
        virLogLen -= tmp;
    } else {
        tmp = LOG_BUFFER_SIZE - virLogStart;
        ret = f(data, &virLogBuffer[virLogStart], tmp);
        if (ret < 0) {
            ret = -1;
            goto error;
        }
        if (ret < tmp) {
            virLogStart += ret;
            virLogLen -= ret;
        } else {
            virLogStart = 0;
            virLogLen -= tmp;
            /* dump the second part */
            if (virLogLen > 0)
                goto push_end;
        }
    }
error:
    virLogUnlock();
319
    return ret;
D
Daniel Veillard 已提交
320
}
321 322
#endif

D
Daniel Veillard 已提交
323 324 325 326 327 328 329 330 331 332 333
/**
 * virLogSetDefaultPriority:
 * @priority: the default priority level
 *
 * Set the default priority level, i.e. any logged data of a priority
 * equal or superior to this level will be logged, unless a specific rule
 * was defined for the log category of the message.
 *
 * Returns 0 if successful, -1 in case of error.
 */
int virLogSetDefaultPriority(int priority) {
334
    if ((priority < VIR_LOG_DEBUG) || (priority > VIR_LOG_ERROR)) {
335
        VIR_WARN0("Ignoring invalid log level setting.");
336
        return -1;
337
    }
D
Daniel Veillard 已提交
338 339 340
    if (!virLogInitialized)
        virLogStartup();
    virLogDefaultPriority = priority;
341
    return 0;
D
Daniel Veillard 已提交
342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357
}

/**
 * virLogResetFilters:
 *
 * Removes the set of logging filters defined.
 *
 * Returns the number of filters removed
 */
static int virLogResetFilters(void) {
    int i;

    for (i = 0; i < virLogNbFilters;i++)
        VIR_FREE(virLogFilters[i].match);
    VIR_FREE(virLogFilters);
    virLogNbFilters = 0;
358
    return i;
D
Daniel Veillard 已提交
359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380
}

/**
 * virLogDefineFilter:
 * @match: the pattern to match
 * @priority: the priority to give to messages matching the pattern
 * @flags: extra flag, currently unused
 *
 * Defines a pattern used for log filtering, it allow to select or
 * reject messages independently of the default priority.
 * The filter defines a rules that will apply only to messages matching
 * the pattern (currently if @match is a substring of the message category)
 *
 * Returns -1 in case of failure or the filter number if successful
 */
int virLogDefineFilter(const char *match, int priority,
                       int flags ATTRIBUTE_UNUSED) {
    int i;
    char *mdup = NULL;

    if ((match == NULL) || (priority < VIR_LOG_DEBUG) ||
        (priority > VIR_LOG_ERROR))
381
        return -1;
D
Daniel Veillard 已提交
382 383 384 385 386 387 388 389 390 391

    virLogLock();
    for (i = 0;i < virLogNbFilters;i++) {
        if (STREQ(virLogFilters[i].match, match)) {
            virLogFilters[i].priority = priority;
            goto cleanup;
        }
    }

    mdup = strdup(match);
392
    if (mdup == NULL) {
D
Daniel Veillard 已提交
393 394 395 396 397 398 399 400 401 402 403 404 405 406
        i = -1;
        goto cleanup;
    }
    i = virLogNbFilters;
    if (VIR_REALLOC_N(virLogFilters, virLogNbFilters + 1)) {
        i = -1;
        VIR_FREE(mdup);
        goto cleanup;
    }
    virLogFilters[i].match = mdup;
    virLogFilters[i].priority = priority;
    virLogNbFilters++;
cleanup:
    virLogUnlock();
407
    return i;
D
Daniel Veillard 已提交
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431
}

/**
 * virLogFiltersCheck:
 * @input: the input string
 *
 * Check the input of the message against the existing filters. Currently
 * the match is just a substring check of the category used as the input
 * string, a more subtle approach could be used instead
 *
 * Returns 0 if not matched or the new priority if found.
 */
static int virLogFiltersCheck(const char *input) {
    int ret = 0;
    int i;

    virLogLock();
    for (i = 0;i < virLogNbFilters;i++) {
        if (strstr(input, virLogFilters[i].match)) {
            ret = virLogFilters[i].priority;
            break;
        }
    }
    virLogUnlock();
432
    return ret;
D
Daniel Veillard 已提交
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447
}

/**
 * virLogResetOutputs:
 *
 * Removes the set of logging output defined.
 *
 * Returns the number of output removed
 */
static int virLogResetOutputs(void) {
    int i;

    for (i = 0;i < virLogNbOutputs;i++) {
        if (virLogOutputs[i].c != NULL)
            virLogOutputs[i].c(virLogOutputs[i].data);
448
        VIR_FREE(virLogOutputs[i].name);
D
Daniel Veillard 已提交
449 450 451 452
    }
    VIR_FREE(virLogOutputs);
    i = virLogNbOutputs;
    virLogNbOutputs = 0;
453
    return i;
D
Daniel Veillard 已提交
454 455 456 457 458
}

/**
 * virLogDefineOutput:
 * @f: the function to call to output a message
459
 * @c: the function to call to close the output (or NULL)
D
Daniel Veillard 已提交
460 461
 * @data: extra data passed as first arg to the function
 * @priority: minimal priority for this filter, use 0 for none
462 463
 * @dest: where to send output of this priority
 * @name: optional name data associated with an output
D
Daniel Veillard 已提交
464 465 466 467 468 469 470 471
 * @flags: extra flag, currently unused
 *
 * Defines an output function for log messages. Each message once
 * gone though filtering is emitted through each registered output.
 *
 * Returns -1 in case of failure or the output number if successful
 */
int virLogDefineOutput(virLogOutputFunc f, virLogCloseFunc c, void *data,
472 473
                       int priority, int dest, const char *name,
                       int flags ATTRIBUTE_UNUSED) {
D
Daniel Veillard 已提交
474
    int ret = -1;
475
    char *ndup = NULL;
D
Daniel Veillard 已提交
476 477

    if (f == NULL)
478
        return -1;
D
Daniel Veillard 已提交
479

480 481
    if (dest == VIR_LOG_TO_SYSLOG || dest == VIR_LOG_TO_FILE) {
        if (name == NULL)
482
            return -1;
483 484
        ndup = strdup(name);
        if (ndup == NULL)
485
            return -1;
486 487
    }

D
Daniel Veillard 已提交
488 489
    virLogLock();
    if (VIR_REALLOC_N(virLogOutputs, virLogNbOutputs + 1)) {
490
        VIR_FREE(ndup);
D
Daniel Veillard 已提交
491 492 493
        goto cleanup;
    }
    ret = virLogNbOutputs++;
494
    virLogOutputs[ret].logVersion = true;
D
Daniel Veillard 已提交
495 496 497 498
    virLogOutputs[ret].f = f;
    virLogOutputs[ret].c = c;
    virLogOutputs[ret].data = data;
    virLogOutputs[ret].priority = priority;
499 500
    virLogOutputs[ret].dest = dest;
    virLogOutputs[ret].name = ndup;
D
Daniel Veillard 已提交
501 502
cleanup:
    virLogUnlock();
503
    return ret;
D
Daniel Veillard 已提交
504 505
}

506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554
static int
virLogFormatString(char **msg,
                   const char *funcname,
                   long long linenr,
                   struct tm *time_info,
                   struct timeval *cur_time,
                   int priority,
                   const char *str)
{
    int ret;
    if ((funcname != NULL)) {
        ret = virAsprintf(msg, "%02d:%02d:%02d.%03d: %d: %s : %s:%lld : %s\n",
                          time_info->tm_hour, time_info->tm_min,
                          time_info->tm_sec, (int) cur_time->tv_usec / 1000,
                          virThreadSelfID(),
                          virLogPriorityString(priority), funcname, linenr, str);
    } else {
        ret = virAsprintf(msg, "%02d:%02d:%02d.%03d: %d: %s : %s\n",
                          time_info->tm_hour, time_info->tm_min,
                          time_info->tm_sec, (int) cur_time->tv_usec / 1000,
                          virThreadSelfID(),
                          virLogPriorityString(priority), str);
    }
    return ret;
}

static int
virLogVersionString(char **msg,
                    struct tm *time_info,
                    struct timeval *cur_time)
{
#ifdef PACKAGER_VERSION
# ifdef PACKAGER
#  define LOG_VERSION_STRING \
    "libvirt version: " VERSION ", package: " PACKAGER_VERSION " (" PACKAGER ")"
# else
#  define LOG_VERSION_STRING \
    "libvirt version: " VERSION ", package: " PACKAGER_VERSION
# endif
#else
# define LOG_VERSION_STRING  \
    "libvirt version: " VERSION
#endif

    return virLogFormatString(msg, NULL, 0,
                              time_info, cur_time,
                              VIR_LOG_INFO, LOG_VERSION_STRING);
}

D
Daniel Veillard 已提交
555 556 557 558
/**
 * virLogMessage:
 * @category: where is that message coming from
 * @priority: the priority level
559 560
 * @funcname: the function emitting the (debug) message
 * @linenr: line where the message was emitted
D
Daniel Veillard 已提交
561 562 563 564 565 566 567
 * @flags: extra flags, 1 if coming from the error handler
 * @fmt: the string format
 * @...: the arguments
 *
 * Call the libvirt logger with some informations. Based on the configuration
 * the message may be stored, sent to output or just discarded
 */
568 569
void virLogMessage(const char *category, int priority, const char *funcname,
                   long long linenr, int flags, const char *fmt, ...) {
570
    static bool logVersionStderr = true;
D
Daniel Veillard 已提交
571 572 573 574
    char *str = NULL;
    char *msg;
    struct timeval cur_time;
    struct tm time_info;
575
    int len, fprio, i, ret;
576
    int saved_errno = errno;
D
Daniel Veillard 已提交
577 578 579 580 581

    if (!virLogInitialized)
        virLogStartup();

    if (fmt == NULL)
582
        goto cleanup;
D
Daniel Veillard 已提交
583 584 585 586 587 588 589

    /*
     * check against list of specific logging patterns
     */
    fprio = virLogFiltersCheck(category);
    if (fprio == 0) {
        if (priority < virLogDefaultPriority)
590 591 592 593
            goto cleanup;
    } else if (priority < fprio) {
        goto cleanup;
    }
D
Daniel Veillard 已提交
594 595 596 597 598 599

    /*
     * serialize the error message, add level and timestamp
     */
    VIR_GET_VAR_STR(fmt, str);
    if (str == NULL)
600
        goto cleanup;
D
Daniel Veillard 已提交
601 602 603
    gettimeofday(&cur_time, NULL);
    localtime_r(&cur_time.tv_sec, &time_info);

604 605 606
    ret = virLogFormatString(&msg, funcname, linenr,
                             &time_info, &cur_time,
                             priority, str);
607
    VIR_FREE(str);
608 609
    if (ret < 0)
        goto cleanup;
D
Daniel Veillard 已提交
610 611 612 613 614 615 616 617 618 619 620 621 622

    /*
     * Log based on defaults, first store in the history buffer
     * then push the message on the outputs defined, if none
     * use stderr.
     * NOTE: the locking is a single point of contention for multiple
     *       threads, but avoid intermixing. Maybe set up locks per output
     *       to improve paralellism.
     */
    len = strlen(msg);
    virLogStr(msg, len);
    virLogLock();
    for (i = 0; i < virLogNbOutputs;i++) {
623 624 625 626 627 628 629 630 631 632
        if (priority >= virLogOutputs[i].priority) {
            if (virLogOutputs[i].logVersion) {
                char *ver = NULL;
                if (virLogVersionString(&ver, &time_info, &cur_time) >= 0)
                    virLogOutputs[i].f(category, VIR_LOG_INFO, __func__, __LINE__,
                                       ver, strlen(ver),
                                       virLogOutputs[i].data);
                VIR_FREE(ver);
                virLogOutputs[i].logVersion = false;
            }
633 634
            virLogOutputs[i].f(category, priority, funcname, linenr,
                               msg, len, virLogOutputs[i].data);
635
        }
D
Daniel Veillard 已提交
636
    }
637 638 639 640 641 642 643 644 645
    if ((virLogNbOutputs == 0) && (flags != 1)) {
        if (logVersionStderr) {
            char *ver = NULL;
            if (virLogVersionString(&ver, &time_info, &cur_time) >= 0)
                ignore_value (safewrite(STDERR_FILENO,
                                        ver, strlen(ver)));
            VIR_FREE(ver);
            logVersionStderr = false;
        }
646
        ignore_value (safewrite(STDERR_FILENO, msg, len));
647
    }
D
Daniel Veillard 已提交
648 649 650
    virLogUnlock();

    VIR_FREE(msg);
651 652
cleanup:
    errno = saved_errno;
D
Daniel Veillard 已提交
653 654
}

655
static int virLogOutputToFd(const char *category ATTRIBUTE_UNUSED,
D
Daniel Veillard 已提交
656
                            int priority ATTRIBUTE_UNUSED,
657 658 659
                            const char *funcname ATTRIBUTE_UNUSED,
                            long long linenr ATTRIBUTE_UNUSED,
                            const char *str, int len, void *data) {
D
Daniel Veillard 已提交
660 661 662 663
    int fd = (long) data;
    int ret;

    if (fd < 0)
664
        return -1;
D
Daniel Veillard 已提交
665
    ret = safewrite(fd, str, len);
666
    return ret;
D
Daniel Veillard 已提交
667 668 669 670 671
}

static void virLogCloseFd(void *data) {
    int fd = (long) data;

672
    VIR_FORCE_CLOSE(fd);
D
Daniel Veillard 已提交
673 674 675
}

static int virLogAddOutputToStderr(int priority) {
676 677
    if (virLogDefineOutput(virLogOutputToFd, NULL, (void *)2L, priority,
                           VIR_LOG_TO_STDERR, NULL, 0) < 0)
678 679
        return -1;
    return 0;
D
Daniel Veillard 已提交
680 681 682 683 684
}

static int virLogAddOutputToFile(int priority, const char *file) {
    int fd;

685
    fd = open(file, O_CREAT | O_APPEND | O_WRONLY, S_IRUSR | S_IWUSR);
D
Daniel Veillard 已提交
686
    if (fd < 0)
687
        return -1;
D
Daniel Veillard 已提交
688
    if (virLogDefineOutput(virLogOutputToFd, virLogCloseFd, (void *)(long)fd,
689
                           priority, VIR_LOG_TO_FILE, file, 0) < 0) {
690
        VIR_FORCE_CLOSE(fd);
691
        return -1;
D
Daniel Veillard 已提交
692
    }
693
    return 0;
D
Daniel Veillard 已提交
694 695 696
}

#if HAVE_SYSLOG_H
697 698 699 700 701 702
static int virLogOutputToSyslog(const char *category ATTRIBUTE_UNUSED,
                                int priority,
                                const char *funcname ATTRIBUTE_UNUSED,
                                long long linenr ATTRIBUTE_UNUSED,
                                const char *str, int len ATTRIBUTE_UNUSED,
                                void *data ATTRIBUTE_UNUSED) {
D
Daniel Veillard 已提交
703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721
    int prio;

    switch (priority) {
        case VIR_LOG_DEBUG:
            prio = LOG_DEBUG;
            break;
        case VIR_LOG_INFO:
            prio = LOG_INFO;
            break;
        case VIR_LOG_WARN:
            prio = LOG_WARNING;
            break;
        case VIR_LOG_ERROR:
            prio = LOG_ERR;
            break;
        default:
            prio = LOG_ERR;
    }
    syslog(prio, "%s", str);
722
    return len;
D
Daniel Veillard 已提交
723 724
}

725 726
static char *current_ident = NULL;

D
Daniel Veillard 已提交
727 728
static void virLogCloseSyslog(void *data ATTRIBUTE_UNUSED) {
    closelog();
729
    VIR_FREE(current_ident);
D
Daniel Veillard 已提交
730 731 732
}

static int virLogAddOutputToSyslog(int priority, const char *ident) {
733 734 735 736 737 738
    /*
     * ident needs to be kept around on Solaris
     */
    VIR_FREE(current_ident);
    current_ident = strdup(ident);
    if (current_ident == NULL)
739
        return -1;
740 741

    openlog(current_ident, 0, 0);
D
Daniel Veillard 已提交
742
    if (virLogDefineOutput(virLogOutputToSyslog, virLogCloseSyslog, NULL,
743
                           priority, VIR_LOG_TO_SYSLOG, ident, 0) < 0) {
D
Daniel Veillard 已提交
744
        closelog();
745
        VIR_FREE(current_ident);
746
        return -1;
D
Daniel Veillard 已提交
747
    }
748
    return 0;
D
Daniel Veillard 已提交
749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781
}
#endif /* HAVE_SYSLOG_H */

#define IS_SPACE(cur)                                                   \
    ((*cur == ' ') || (*cur == '\t') || (*cur == '\n') ||               \
     (*cur == '\r') || (*cur == '\\'))

/**
 * virLogParseOutputs:
 * @outputs: string defining a (set of) output(s)
 *
 * The format for an output can be:
 *    x:stderr
 *       output goes to stderr
 *    x:syslog:name
 *       use syslog for the output and use the given name as the ident
 *    x:file:file_path
 *       output to a file, with the given filepath
 * In all case the x prefix is the minimal level, acting as a filter
 *    0: everything
 *    1: DEBUG
 *    2: INFO
 *    3: WARNING
 *    4: ERROR
 *
 * Multiple output can be defined in a single @output, they just need to be
 * separated by spaces.
 *
 * Returns the number of output parsed and installed or -1 in case of error
 */
int virLogParseOutputs(const char *outputs) {
    const char *cur = outputs, *str;
    char *name;
782
    char *abspath;
D
Daniel Veillard 已提交
783
    int prio;
784 785
    int ret = -1;
    int count = 0;
D
Daniel Veillard 已提交
786 787

    if (cur == NULL)
788
        return -1;
D
Daniel Veillard 已提交
789 790 791 792

    virSkipSpaces(&cur);
    while (*cur != 0) {
        prio= virParseNumber(&cur);
793
        if ((prio < VIR_LOG_DEBUG) || (prio > VIR_LOG_ERROR))
794
            goto cleanup;
D
Daniel Veillard 已提交
795
        if (*cur != ':')
796
            goto cleanup;
D
Daniel Veillard 已提交
797 798 799 800
        cur++;
        if (STREQLEN(cur, "stderr", 6)) {
            cur += 6;
            if (virLogAddOutputToStderr(prio) == 0)
801
                count++;
D
Daniel Veillard 已提交
802 803 804
        } else if (STREQLEN(cur, "syslog", 6)) {
            cur += 6;
            if (*cur != ':')
805
                goto cleanup;
D
Daniel Veillard 已提交
806 807 808 809 810
            cur++;
            str = cur;
            while ((*cur != 0) && (!IS_SPACE(cur)))
                cur++;
            if (str == cur)
811
                goto cleanup;
D
Daniel Veillard 已提交
812 813 814
#if HAVE_SYSLOG_H
            name = strndup(str, cur - str);
            if (name == NULL)
815
                goto cleanup;
D
Daniel Veillard 已提交
816
            if (virLogAddOutputToSyslog(prio, name) == 0)
817
                count++;
D
Daniel Veillard 已提交
818 819 820 821 822
            VIR_FREE(name);
#endif /* HAVE_SYSLOG_H */
        } else if (STREQLEN(cur, "file", 4)) {
            cur += 4;
            if (*cur != ':')
823
                goto cleanup;
D
Daniel Veillard 已提交
824 825 826 827 828
            cur++;
            str = cur;
            while ((*cur != 0) && (!IS_SPACE(cur)))
                cur++;
            if (str == cur)
829
                goto cleanup;
D
Daniel Veillard 已提交
830 831
            name = strndup(str, cur - str);
            if (name == NULL)
832
                goto cleanup;
833 834 835 836 837
            if (virFileAbsPath(name, &abspath) < 0) {
                VIR_FREE(name);
                return -1; /* skip warning here because setting was fine */
            }
            if (virLogAddOutputToFile(prio, abspath) == 0)
838
                count++;
D
Daniel Veillard 已提交
839
            VIR_FREE(name);
840
            VIR_FREE(abspath);
D
Daniel Veillard 已提交
841
        } else {
842
            goto cleanup;
D
Daniel Veillard 已提交
843 844 845
        }
        virSkipSpaces(&cur);
    }
846 847 848
    ret = count;
cleanup:
    if (ret == -1)
849
        VIR_WARN0("Ignoring invalid log output setting.");
850
    return ret;
D
Daniel Veillard 已提交
851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874
}

/**
 * virLogParseFilters:
 * @filters: string defining a (set of) filter(s)
 *
 * The format for a filter is:
 *    x:name
 *       where name is a match string
 * the x prefix is the minimal level where the messages should be logged
 *    1: DEBUG
 *    2: INFO
 *    3: WARNING
 *    4: ERROR
 *
 * Multiple filter can be defined in a single @filters, they just need to be
 * separated by spaces.
 *
 * Returns the number of filter parsed and installed or -1 in case of error
 */
int virLogParseFilters(const char *filters) {
    const char *cur = filters, *str;
    char *name;
    int prio;
875 876
    int ret = -1;
    int count = 0;
D
Daniel Veillard 已提交
877 878

    if (cur == NULL)
879
        return -1;
D
Daniel Veillard 已提交
880 881 882 883

    virSkipSpaces(&cur);
    while (*cur != 0) {
        prio= virParseNumber(&cur);
884
        if ((prio < VIR_LOG_DEBUG) || (prio > VIR_LOG_ERROR))
885
            goto cleanup;
D
Daniel Veillard 已提交
886
        if (*cur != ':')
887
            goto cleanup;
D
Daniel Veillard 已提交
888 889 890 891 892
        cur++;
        str = cur;
        while ((*cur != 0) && (!IS_SPACE(cur)))
            cur++;
        if (str == cur)
893
            goto cleanup;
D
Daniel Veillard 已提交
894 895
        name = strndup(str, cur - str);
        if (name == NULL)
896
            goto cleanup;
D
Daniel Veillard 已提交
897
        if (virLogDefineFilter(name, prio, 0) >= 0)
898
            count++;
D
Daniel Veillard 已提交
899 900 901
        VIR_FREE(name);
        virSkipSpaces(&cur);
    }
902 903 904
    ret = count;
cleanup:
    if (ret == -1)
905
        VIR_WARN0("Ignoring invalid log filter setting.");
906
    return ret;
D
Daniel Veillard 已提交
907
}
908 909 910 911 912 913 914

/**
 * virLogGetDefaultPriority:
 *
 * Returns the current logging priority level.
 */
int virLogGetDefaultPriority(void) {
915
    return virLogDefaultPriority;
916 917
}

918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935
/**
 * virLogGetFilters:
 *
 * Returns a string listing the current filters, in the format originally
 * specified in the config file or environment. Caller must free the
 * result.
 */
char *virLogGetFilters(void) {
    int i;
    virBuffer filterbuf = VIR_BUFFER_INITIALIZER;

    virLogLock();
    for (i = 0; i < virLogNbFilters; i++) {
        virBufferVSprintf(&filterbuf, "%d:%s ", virLogFilters[i].priority,
                          virLogFilters[i].match);
    }
    virLogUnlock();

936 937
    if (virBufferError(&filterbuf)) {
        virBufferFreeAndReset(&filterbuf);
938
        return NULL;
939
    }
940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975

    return virBufferContentAndReset(&filterbuf);
}

/**
 * virLogGetOutputs:
 *
 * Returns a string listing the current outputs, in the format originally
 * specified in the config file or environment. Caller must free the
 * result.
 */
char *virLogGetOutputs(void) {
    int i;
    virBuffer outputbuf = VIR_BUFFER_INITIALIZER;

    virLogLock();
    for (i = 0; i < virLogNbOutputs; i++) {
        int dest = virLogOutputs[i].dest;
        if (i)
            virBufferVSprintf(&outputbuf, " ");
        switch (dest) {
            case VIR_LOG_TO_SYSLOG:
            case VIR_LOG_TO_FILE:
                virBufferVSprintf(&outputbuf, "%d:%s:%s",
                                  virLogOutputs[i].priority,
                                  virLogOutputString(dest),
                                  virLogOutputs[i].name);
                break;
            default:
                virBufferVSprintf(&outputbuf, "%d:%s",
                                  virLogOutputs[i].priority,
                                  virLogOutputString(dest));
        }
    }
    virLogUnlock();

976 977
    if (virBufferError(&outputbuf)) {
        virBufferFreeAndReset(&outputbuf);
978
        return NULL;
979
    }
980 981 982 983

    return virBufferContentAndReset(&outputbuf);
}

984 985 986 987 988 989
/**
 * virLogGetNbFilters:
 *
 * Returns the current number of defined log filters.
 */
int virLogGetNbFilters(void) {
990
    return virLogNbFilters;
991 992 993 994 995 996 997 998
}

/**
 * virLogGetNbOutputs:
 *
 * Returns the current number of defined log outputs.
 */
int virLogGetNbOutputs(void) {
999
    return virLogNbOutputs;
1000
}
1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026

/**
 * virLogParseDefaultPriority:
 * @priority: string defining the desired logging level
 *
 * Parses and sets the default log priority level. It can take a string or
 * number corresponding to the following levels:
 *    1: DEBUG
 *    2: INFO
 *    3: WARNING
 *    4: ERROR
 *
 * Returns the parsed log level or -1 on error.
 */
int virLogParseDefaultPriority(const char *priority) {
    int ret = -1;

    if (STREQ(priority, "1") || STREQ(priority, "debug"))
        ret = virLogSetDefaultPriority(VIR_LOG_DEBUG);
    else if (STREQ(priority, "2") || STREQ(priority, "info"))
        ret = virLogSetDefaultPriority(VIR_LOG_INFO);
    else if (STREQ(priority, "3") || STREQ(priority, "warning"))
        ret = virLogSetDefaultPriority(VIR_LOG_WARN);
    else if (STREQ(priority, "4") || STREQ(priority, "error"))
        ret = virLogSetDefaultPriority(VIR_LOG_ERROR);
    else
1027
        VIR_WARN0("Ignoring invalid log level setting");
1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045

    return ret;
}

/**
 * virLogSetFromEnv:
 *
 * Sets virLogDefaultPriority, virLogFilters and virLogOutputs based on
 * environment variables.
 */
void virLogSetFromEnv(void) {
    char *debugEnv;

    debugEnv = getenv("LIBVIRT_DEBUG");
    if (debugEnv && *debugEnv)
        virLogParseDefaultPriority(debugEnv);
    debugEnv = getenv("LIBVIRT_LOG_FILTERS");
    if (debugEnv && *debugEnv)
1046
        virLogParseFilters(debugEnv);
1047 1048
    debugEnv = getenv("LIBVIRT_LOG_OUTPUTS");
    if (debugEnv && *debugEnv)
1049
        virLogParseOutputs(debugEnv);
1050
}