Coverage Report

Created: 2025-07-01 06:50

/src/openvswitch/lib/vlog.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2015, 2016 Nicira, Inc.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at:
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
17
#include <config.h>
18
#include "openvswitch/vlog.h"
19
#include <assert.h>
20
#include <ctype.h>
21
#include <errno.h>
22
#include <fcntl.h>
23
#include <stdarg.h>
24
#include <stdlib.h>
25
#include <string.h>
26
#include <sys/stat.h>
27
#include <sys/types.h>
28
#include <syslog.h>
29
#include <time.h>
30
#include <unistd.h>
31
#include "async-append.h"
32
#include "backtrace.h"
33
#include "coverage.h"
34
#include "dirs.h"
35
#include "openvswitch/dynamic-string.h"
36
#include "openvswitch/ofpbuf.h"
37
#include "ovs-thread.h"
38
#include "sat-math.h"
39
#include "socket-util.h"
40
#include "svec.h"
41
#include "syslog-direct.h"
42
#include "syslog-libc.h"
43
#include "syslog-null.h"
44
#include "syslog-provider.h"
45
#include "timeval.h"
46
#include "unixctl.h"
47
#include "util.h"
48
49
VLOG_DEFINE_THIS_MODULE(vlog);
50
51
/* ovs_assert() logs the assertion message, so using ovs_assert() in this
52
 * source file could cause recursion. */
53
#undef ovs_assert
54
#define ovs_assert use_assert_instead_of_ovs_assert_in_this_module
55
56
/* Name for each logging level. */
57
static const char *const level_names[VLL_N_LEVELS] = {
58
#define VLOG_LEVEL(NAME, SYSLOG_LEVEL, RFC5424) #NAME,
59
    VLOG_LEVELS
60
#undef VLOG_LEVEL
61
};
62
63
/* Syslog value for each logging level. */
64
static const int syslog_levels[VLL_N_LEVELS] = {
65
#define VLOG_LEVEL(NAME, SYSLOG_LEVEL, RFC5424) SYSLOG_LEVEL,
66
    VLOG_LEVELS
67
#undef VLOG_LEVEL
68
};
69
70
/* RFC 5424 defines specific values for each syslog level.  Normally LOG_* use
71
 * the same values.  Verify that in fact they're the same.  If we get assertion
72
 * failures here then we need to define a separate rfc5424_levels[] array. */
73
#define VLOG_LEVEL(NAME, SYSLOG_LEVEL, RFC5424) \
74
    BUILD_ASSERT_DECL(SYSLOG_LEVEL == RFC5424);
75
VLOG_LEVELS
76
#undef VLOG_LEVELS
77
78
/* Similarly, RFC 5424 defines the local0 facility with the value ordinarily
79
 * used for LOG_LOCAL0. */
80
BUILD_ASSERT_DECL(LOG_LOCAL0 == (16 << 3));
81
82
/* Protects the 'pattern' in all "struct destination"s, so that a race between
83
 * changing and reading the pattern does not cause an access to freed
84
 * memory. */
85
static struct ovs_rwlock pattern_rwlock = OVS_RWLOCK_INITIALIZER;
86
87
/* Information about each destination. */
88
struct destination {
89
    const char *name;           /* Name. */
90
    char *pattern OVS_GUARDED_BY(pattern_rwlock); /* Current pattern. */
91
    bool default_pattern;       /* Whether current pattern is the default. */
92
};
93
static struct destination destinations[VLF_N_DESTINATIONS] = {
94
#define VLOG_DESTINATION(NAME, PATTERN) {#NAME, PATTERN, true},
95
    VLOG_DESTINATIONS
96
#undef VLOG_DESTINATION
97
};
98
99
/* Sequence number for the message currently being composed. */
100
DEFINE_STATIC_PER_THREAD_DATA(unsigned int, msg_num, 0);
101
102
/* VLF_FILE configuration.
103
 *
104
 * All of the following is protected by 'log_file_mutex', which nests inside
105
 * pattern_rwlock. */
106
static struct ovs_mutex log_file_mutex OVS_ACQ_AFTER(pattern_rwlock)
107
    = OVS_MUTEX_INITIALIZER;
108
static char *log_file_name OVS_GUARDED_BY(log_file_mutex) = NULL;
109
static int log_fd OVS_GUARDED_BY(log_file_mutex) = -1;
110
static struct async_append *log_writer OVS_GUARDED_BY(log_file_mutex);
111
static bool log_async OVS_GUARDED_BY(log_file_mutex);
112
static struct syslogger *syslogger = NULL;
113
114
/* The log modules. */
115
static struct ovs_list vlog_modules OVS_GUARDED_BY(log_file_mutex)
116
    = OVS_LIST_INITIALIZER(&vlog_modules);
117
118
/* Syslog export configuration. */
119
static int syslog_fd OVS_GUARDED_BY(pattern_rwlock) = -1;
120
121
/* Log facility configuration. */
122
static atomic_int log_facility = 0;
123
124
/* Facility name and its value. */
125
struct vlog_facility {
126
    char *name;           /* Name. */
127
    unsigned int value;   /* Facility associated with 'name'. */
128
};
129
static struct vlog_facility vlog_facilities[] = {
130
    {"kern", LOG_KERN},
131
    {"user", LOG_USER},
132
    {"mail", LOG_MAIL},
133
    {"daemon", LOG_DAEMON},
134
    {"auth", LOG_AUTH},
135
    {"syslog", LOG_SYSLOG},
136
    {"lpr", LOG_LPR},
137
    {"news", LOG_NEWS},
138
    {"uucp", LOG_UUCP},
139
    {"clock", LOG_CRON},
140
    {"ftp", LOG_FTP},
141
    {"ntp", 12<<3},
142
    {"audit", 13<<3},
143
    {"alert", 14<<3},
144
    {"clock2", 15<<3},
145
    {"local0", LOG_LOCAL0},
146
    {"local1", LOG_LOCAL1},
147
    {"local2", LOG_LOCAL2},
148
    {"local3", LOG_LOCAL3},
149
    {"local4", LOG_LOCAL4},
150
    {"local5", LOG_LOCAL5},
151
    {"local6", LOG_LOCAL6},
152
    {"local7", LOG_LOCAL7}
153
};
154
static bool vlog_facility_exists(const char* facility, int *value);
155
156
static void format_log_message(const struct vlog_module *, enum vlog_level,
157
                               const char *pattern,
158
                               const char *message, va_list, struct ds *)
159
    OVS_PRINTF_FORMAT(4, 0);
160
161
/* Searches the 'n_names' in 'names'.  Returns the index of a match for
162
 * 'target', or 'n_names' if no name matches. */
163
static size_t
164
search_name_array(const char *target, const char *const *names, size_t n_names)
165
0
{
166
0
    size_t i;
167
168
0
    for (i = 0; i < n_names; i++) {
169
0
        assert(names[i]);
170
0
        if (!strcasecmp(names[i], target)) {
171
0
            break;
172
0
        }
173
0
    }
174
0
    return i;
175
0
}
176
177
/* Returns the name for logging level 'level'. */
178
const char *
179
vlog_get_level_name(enum vlog_level level)
180
0
{
181
0
    assert(level < VLL_N_LEVELS);
182
0
    return level_names[level];
183
0
}
184
185
/* Returns the logging level with the given 'name', or VLL_N_LEVELS if 'name'
186
 * is not the name of a logging level. */
187
enum vlog_level
188
vlog_get_level_val(const char *name)
189
0
{
190
0
    return search_name_array(name, level_names, ARRAY_SIZE(level_names));
191
0
}
192
193
/* Returns the name for logging destination 'destination'. */
194
const char *
195
vlog_get_destination_name(enum vlog_destination destination)
196
0
{
197
0
    assert(destination < VLF_N_DESTINATIONS);
198
0
    return destinations[destination].name;
199
0
}
200
201
/* Returns the logging destination named 'name', or VLF_N_DESTINATIONS if
202
 * 'name' is not the name of a logging destination. */
203
enum vlog_destination
204
vlog_get_destination_val(const char *name)
205
0
{
206
0
    size_t i;
207
208
0
    for (i = 0; i < VLF_N_DESTINATIONS; i++) {
209
0
        if (!strcasecmp(destinations[i].name, name)) {
210
0
            break;
211
0
        }
212
0
    }
213
0
    return i;
214
0
}
215
216
void
217
vlog_insert_module(struct ovs_list *vlog)
218
31
{
219
31
    ovs_mutex_lock(&log_file_mutex);
220
31
    ovs_list_insert(&vlog_modules, vlog);
221
31
    ovs_mutex_unlock(&log_file_mutex);
222
31
}
223
224
/* Returns the name for logging module 'module'. */
225
const char *
226
vlog_get_module_name(const struct vlog_module *module)
227
0
{
228
0
    return module->name;
229
0
}
230
231
/* Returns the logging module named 'name', or NULL if 'name' is not the name
232
 * of a logging module. */
233
struct vlog_module *
234
vlog_module_from_name(const char *name)
235
0
{
236
0
    struct vlog_module *mp;
237
238
0
    ovs_mutex_lock(&log_file_mutex);
239
0
    LIST_FOR_EACH (mp, list, &vlog_modules) {
240
0
        if (!strcasecmp(name, mp->name)) {
241
0
            ovs_mutex_unlock(&log_file_mutex);
242
0
            return mp;
243
0
        }
244
0
    }
245
0
    ovs_mutex_unlock(&log_file_mutex);
246
247
0
    return NULL;
248
0
}
249
250
/* Returns the current logging level for the given 'module' and
251
 * 'destination'. */
252
enum vlog_level
253
vlog_get_level(const struct vlog_module *module,
254
               enum vlog_destination destination)
255
0
{
256
0
    assert(destination < VLF_N_DESTINATIONS);
257
0
    return module->levels[destination];
258
0
}
259
260
static void
261
update_min_level(struct vlog_module *module) OVS_REQUIRES(log_file_mutex)
262
0
{
263
0
    enum vlog_destination destination;
264
265
0
    module->min_level = VLL_OFF;
266
0
    for (destination = 0; destination < VLF_N_DESTINATIONS; destination++) {
267
0
        if (log_fd >= 0 || destination != VLF_FILE) {
268
0
            enum vlog_level level = module->levels[destination];
269
0
            if (level > module->min_level) {
270
0
                module->min_level = level;
271
0
            }
272
0
        }
273
0
    }
274
0
}
275
276
static void
277
set_destination_level(enum vlog_destination destination,
278
                      struct vlog_module *module, enum vlog_level level)
279
0
{
280
0
    assert(destination >= 0 && destination < VLF_N_DESTINATIONS);
281
0
    assert(level < VLL_N_LEVELS);
282
283
0
    ovs_mutex_lock(&log_file_mutex);
284
0
    if (!module) {
285
0
        struct vlog_module *mp;
286
0
        LIST_FOR_EACH (mp, list, &vlog_modules) {
287
0
            mp->levels[destination] = level;
288
0
            update_min_level(mp);
289
0
        }
290
0
    } else {
291
0
        module->levels[destination] = level;
292
0
        update_min_level(module);
293
0
    }
294
0
    ovs_mutex_unlock(&log_file_mutex);
295
0
}
296
297
/* Sets the logging level for the given 'module' and 'destination' to 'level'.
298
 * A null 'module' or a 'destination' of VLF_ANY_DESTINATION is treated as a
299
 * wildcard across all modules or destinations, respectively. */
300
void
301
vlog_set_levels(struct vlog_module *module, enum vlog_destination destination,
302
                enum vlog_level level)
303
0
{
304
0
    assert(destination < VLF_N_DESTINATIONS ||
305
0
           destination == VLF_ANY_DESTINATION);
306
0
    if (destination == VLF_ANY_DESTINATION) {
307
0
        for (destination = 0; destination < VLF_N_DESTINATIONS;
308
0
             destination++) {
309
0
            set_destination_level(destination, module, level);
310
0
        }
311
0
    } else {
312
0
        set_destination_level(destination, module, level);
313
0
    }
314
0
}
315
316
static void
317
do_set_pattern(enum vlog_destination destination, const char *pattern)
318
0
{
319
0
    struct destination *f = &destinations[destination];
320
321
0
    ovs_rwlock_wrlock(&pattern_rwlock);
322
0
    if (!f->default_pattern) {
323
0
        free(f->pattern);
324
0
    } else {
325
0
        f->default_pattern = false;
326
0
    }
327
0
    f->pattern = xstrdup(pattern);
328
0
    ovs_rwlock_unlock(&pattern_rwlock);
329
0
}
330
331
/* Sets the pattern for the given 'destination' to 'pattern'. */
332
void
333
vlog_set_pattern(enum vlog_destination destination, const char *pattern)
334
0
{
335
0
    assert(destination < VLF_N_DESTINATIONS ||
336
0
           destination == VLF_ANY_DESTINATION);
337
0
    if (destination == VLF_ANY_DESTINATION) {
338
0
        for (destination = 0; destination < VLF_N_DESTINATIONS;
339
0
             destination++) {
340
0
            do_set_pattern(destination, pattern);
341
0
        }
342
0
    } else {
343
0
        do_set_pattern(destination, pattern);
344
0
    }
345
0
}
346
347
/* Returns a copy of the name of the log file used by VLF_FILE, or NULL if none
348
 * is configured.  The caller must eventually free the returned string. */
349
char *
350
vlog_get_log_file(void)
351
0
{
352
0
    ovs_mutex_lock(&log_file_mutex);
353
0
    char *fn = nullable_xstrdup(log_file_name);
354
0
    ovs_mutex_unlock(&log_file_mutex);
355
356
0
    return fn;
357
0
}
358
359
/* Sets the name of the log file used by VLF_FILE to 'new_log_file_name', or
360
 * closes the current log file if 'new_log_file_name' is NULL.  Takes ownership
361
 * of 'new_log_file_name'.  Returns 0 if successful, otherwise a positive errno
362
 * value. */
363
static int
364
vlog_set_log_file__(char *new_log_file_name)
365
0
{
366
0
    struct vlog_module *mp;
367
0
    struct stat old_stat;
368
0
    struct stat new_stat;
369
0
    int new_log_fd;
370
0
    bool same_file;
371
0
    bool log_close;
372
373
    /* Open new log file. */
374
0
    if (new_log_file_name) {
375
0
        new_log_fd = open(new_log_file_name, O_WRONLY | O_CREAT | O_APPEND,
376
0
                          0660);
377
0
        if (new_log_fd < 0) {
378
0
            VLOG_WARN("failed to open %s for logging: %s",
379
0
                      new_log_file_name, ovs_strerror(errno));
380
0
            free(new_log_file_name);
381
0
            return errno;
382
0
        }
383
0
    } else {
384
0
        new_log_fd = -1;
385
0
    }
386
387
    /* If the new log file is the same one we already have open, bail out. */
388
0
    ovs_mutex_lock(&log_file_mutex);
389
0
    same_file = ((log_fd < 0
390
0
                  && new_log_fd < 0) ||
391
0
                 (log_fd >= 0
392
0
                  && new_log_fd >= 0
393
0
                  && !fstat(log_fd, &old_stat)
394
0
                  && !fstat(new_log_fd, &new_stat)
395
0
                  && old_stat.st_dev == new_stat.st_dev
396
0
                  && old_stat.st_ino == new_stat.st_ino));
397
0
    ovs_mutex_unlock(&log_file_mutex);
398
0
    if (same_file) {
399
0
        if (new_log_fd >= 0) {
400
0
            close(new_log_fd);
401
0
        }
402
0
        free(new_log_file_name);
403
0
        return 0;
404
0
    }
405
406
    /* Log closing old log file (we can't log while holding log_file_mutex). */
407
0
    ovs_mutex_lock(&log_file_mutex);
408
0
    log_close = log_fd >= 0;
409
0
    ovs_mutex_unlock(&log_file_mutex);
410
0
    if (log_close) {
411
0
        VLOG_INFO("closing log file");
412
0
    }
413
414
    /* Close old log file, if any. */
415
0
    ovs_mutex_lock(&log_file_mutex);
416
0
    async_append_destroy(log_writer);
417
0
    if (log_fd >= 0) {
418
0
        close(log_fd);
419
0
    }
420
0
    free(log_file_name);
421
422
    /* Install new log file. */
423
0
    log_file_name = nullable_xstrdup(new_log_file_name);
424
0
    log_fd = new_log_fd;
425
0
    log_writer = log_async ? async_append_create(new_log_fd) : NULL;
426
427
0
    LIST_FOR_EACH (mp, list, &vlog_modules) {
428
0
        update_min_level(mp);
429
0
    }
430
0
    ovs_mutex_unlock(&log_file_mutex);
431
432
    /* Log opening new log file (we can't log while holding log_file_mutex). */
433
0
    VLOG_INFO("opened log file %s", new_log_file_name);
434
0
    free(new_log_file_name);
435
436
0
    return 0;
437
0
}
438
439
/* Closes the log file, if any.
440
 *
441
 * If the real goal is open a new log file, use vlog_set_log_file() to directly
442
 * do that: there is no need to close the old file first. */
443
void
444
vlog_close_log_file(void)
445
0
{
446
0
    vlog_set_log_file__(NULL);
447
0
}
448
449
/* Sets the name of the log file used by VLF_FILE to 'file_name', or to the
450
 * default file name if 'file_name' is null.  Returns 0 if successful,
451
 * otherwise a positive errno value. */
452
int
453
vlog_set_log_file(const char *file_name)
454
0
{
455
0
    return vlog_set_log_file__(
456
0
        file_name
457
0
        ? xstrdup(file_name)
458
0
        : xasprintf("%s/%s.log", ovs_logdir(), program_name));
459
0
}
460
461
/* Closes and then attempts to re-open the current log file.  (This is useful
462
 * just after log rotation, to ensure that the new log file starts being used.)
463
 * Returns 0 if successful, otherwise a positive errno value. */
464
int
465
vlog_reopen_log_file(void)
466
0
{
467
0
    char *fn;
468
469
0
    ovs_mutex_lock(&log_file_mutex);
470
0
    fn = nullable_xstrdup(log_file_name);
471
0
    ovs_mutex_unlock(&log_file_mutex);
472
473
0
    if (fn) {
474
0
        int error = vlog_set_log_file(fn);
475
0
        free(fn);
476
0
        return error;
477
0
    } else {
478
0
        return 0;
479
0
    }
480
0
}
481
482
#ifndef _WIN32
483
/* In case a log file exists, change its owner to new 'user' and 'group'.
484
 *
485
 * This is useful for handling cases where the --log-file option is
486
 * specified ahead of the --user option.  */
487
void
488
vlog_change_owner_unix(uid_t user, gid_t group)
489
0
{
490
0
    struct ds err = DS_EMPTY_INITIALIZER;
491
0
    int error;
492
493
0
    ovs_mutex_lock(&log_file_mutex);
494
0
    error = log_file_name ? chown(log_file_name, user, group) : 0;
495
0
    if (error) {
496
        /* Build the error message. We can not call VLOG_FATAL directly
497
         * here because VLOG_FATAL() will try again to to acquire
498
         * 'log_file_mutex' lock, causing deadlock.
499
         */
500
0
        ds_put_format(&err, "Failed to change %s ownership: %s.",
501
0
                      log_file_name, ovs_strerror(errno));
502
0
    }
503
0
    ovs_mutex_unlock(&log_file_mutex);
504
505
0
    if (error) {
506
0
        VLOG_FATAL("%s", ds_steal_cstr(&err));
507
0
    }
508
0
}
509
#endif
510
511
/* Set debugging levels.  Returns null if successful, otherwise an error
512
 * message that the caller must free(). */
513
char *
514
vlog_set_levels_from_string(const char *s_)
515
0
{
516
0
    char *s = xstrdup(s_);
517
0
    char *save_ptr = NULL;
518
0
    char *msg = NULL;
519
0
    char *word;
520
521
0
    word = strtok_r(s, " ,:\t", &save_ptr);
522
0
    if (word && !strcasecmp(word, "PATTERN")) {
523
0
        enum vlog_destination destination;
524
525
0
        word = strtok_r(NULL, " ,:\t", &save_ptr);
526
0
        if (!word) {
527
0
            msg = xstrdup("missing destination");
528
0
            goto exit;
529
0
        }
530
531
0
        destination = (!strcasecmp(word, "ANY")
532
0
                       ? VLF_ANY_DESTINATION
533
0
                       : vlog_get_destination_val(word));
534
0
        if (destination == VLF_N_DESTINATIONS) {
535
0
            msg = xasprintf("unknown destination \"%s\"", word);
536
0
            goto exit;
537
0
        }
538
0
        vlog_set_pattern(destination, save_ptr);
539
0
    } else if (word && !strcasecmp(word, "FACILITY")) {
540
0
        int value;
541
542
0
        if (!vlog_facility_exists(save_ptr, &value)) {
543
0
            msg = xstrdup("invalid facility");
544
0
            goto exit;
545
0
        }
546
0
        atomic_store_explicit(&log_facility, value, memory_order_relaxed);
547
0
    } else {
548
0
        struct vlog_module *module = NULL;
549
0
        enum vlog_level level = VLL_N_LEVELS;
550
0
        enum vlog_destination destination = VLF_N_DESTINATIONS;
551
552
0
        for (; word != NULL; word = strtok_r(NULL, " ,:\t", &save_ptr)) {
553
0
            if (!strcasecmp(word, "ANY")) {
554
0
                continue;
555
0
            } else if (vlog_get_destination_val(word) != VLF_N_DESTINATIONS) {
556
0
                if (destination != VLF_N_DESTINATIONS) {
557
0
                    msg = xstrdup("cannot specify multiple destinations");
558
0
                    goto exit;
559
0
                }
560
0
                destination = vlog_get_destination_val(word);
561
0
            } else if (vlog_get_level_val(word) != VLL_N_LEVELS) {
562
0
                if (level != VLL_N_LEVELS) {
563
0
                    msg = xstrdup("cannot specify multiple levels");
564
0
                    goto exit;
565
0
                }
566
0
                level = vlog_get_level_val(word);
567
0
            } else if (vlog_module_from_name(word)) {
568
0
                if (module) {
569
0
                    msg = xstrdup("cannot specify multiple modules");
570
0
                    goto exit;
571
0
                }
572
0
                module = vlog_module_from_name(word);
573
0
            } else {
574
0
                msg = xasprintf("no destination, level, or module \"%s\"",
575
0
                                word);
576
0
                goto exit;
577
0
            }
578
0
        }
579
580
0
        if (destination == VLF_N_DESTINATIONS) {
581
0
            destination = VLF_ANY_DESTINATION;
582
0
        }
583
0
        if (level == VLL_N_LEVELS) {
584
0
            level = VLL_DBG;
585
0
        }
586
0
        vlog_set_levels(module, destination, level);
587
0
    }
588
589
0
exit:
590
0
    free(s);
591
0
    return msg;
592
0
}
593
594
/* Set debugging levels.  Abort with an error message if 's' is invalid. */
595
void
596
vlog_set_levels_from_string_assert(const char *s)
597
0
{
598
0
    char *error = vlog_set_levels_from_string(s);
599
0
    if (error) {
600
0
        ovs_fatal(0, "%s", error);
601
0
    }
602
0
}
603
604
/* If 'arg' is null, configure maximum verbosity.  Otherwise, sets
605
 * configuration according to 'arg' (see vlog_set_levels_from_string()). */
606
void
607
vlog_set_verbosity(const char *arg)
608
0
{
609
0
    if (arg) {
610
0
        char *msg = vlog_set_levels_from_string(arg);
611
0
        if (msg) {
612
0
            ovs_fatal(0, "processing \"%s\": %s", arg, msg);
613
0
        }
614
0
    } else {
615
0
        vlog_set_levels(NULL, VLF_ANY_DESTINATION, VLL_DBG);
616
0
    }
617
0
}
618
619
void
620
vlog_set_syslog_method(const char *method)
621
0
{
622
0
    if (syslogger) {
623
        /* Set syslogger only, if one is not already set.  This effectively
624
         * means that only the first --syslog-method argument is honored. */
625
0
        return;
626
0
    }
627
628
0
    if (!strcmp(method, "null")) {
629
0
        syslogger = syslog_null_create();
630
0
    } else if (!strcmp(method, "libc")) {
631
0
        syslogger = syslog_libc_create();
632
0
    } else if (!strncmp(method, "udp:", 4) || !strncmp(method, "unix:", 5)) {
633
0
        syslogger = syslog_direct_create(method);
634
0
    } else {
635
0
        ovs_fatal(0, "unsupported syslog method '%s'", method);
636
0
    }
637
0
}
638
639
/* Set the vlog udp syslog target. */
640
void
641
vlog_set_syslog_target(const char *target)
642
0
{
643
0
    int new_fd;
644
645
0
    inet_open_active(SOCK_DGRAM, target, -1, NULL, &new_fd, 0);
646
647
0
    ovs_rwlock_wrlock(&pattern_rwlock);
648
0
    if (syslog_fd >= 0) {
649
0
        close(syslog_fd);
650
0
    }
651
0
    syslog_fd = new_fd;
652
0
    ovs_rwlock_unlock(&pattern_rwlock);
653
0
}
654
655
/*
656
 * This function writes directly to log file without using async writer or
657
 * taking a lock.  Caller must hold 'log_file_mutex' or be sure that it's
658
 * not necessary.  Could be used in exceptional cases like dumping of backtrace
659
 * on fatal signals.
660
 */
661
void
662
vlog_direct_write_to_log_file_unsafe(const char *s)
663
    OVS_NO_THREAD_SAFETY_ANALYSIS
664
0
{
665
0
    if (log_fd >= 0) {
666
0
        ignore(write(log_fd, s, strlen(s)));
667
0
    }
668
0
}
669
670
int
671
vlog_get_log_file_fd_unsafe(void)
672
    OVS_NO_THREAD_SAFETY_ANALYSIS
673
0
{
674
0
    return log_fd;
675
0
}
676
677
/* Returns 'false' if 'facility' is not a valid string. If 'facility'
678
 * is a valid string, sets 'value' with the integer value of 'facility'
679
 * and returns 'true'. */
680
static bool
681
vlog_facility_exists(const char* facility, int *value)
682
0
{
683
0
    size_t i;
684
0
    for (i = 0; i < ARRAY_SIZE(vlog_facilities); i++) {
685
0
        if (!strcasecmp(vlog_facilities[i].name, facility)) {
686
0
            *value = vlog_facilities[i].value;
687
0
            return true;
688
0
        }
689
0
    }
690
0
    return false;
691
0
}
692
693
static void
694
vlog_unixctl_set(struct unixctl_conn *conn, int argc, const char *argv[],
695
                 void *aux OVS_UNUSED)
696
0
{
697
0
    int i;
698
699
    /* With no argument, set all destinations and modules to "dbg". */
700
0
    if (argc == 1) {
701
0
        vlog_set_levels(NULL, VLF_ANY_DESTINATION, VLL_DBG);
702
0
    }
703
0
    for (i = 1; i < argc; i++) {
704
0
        char *msg = vlog_set_levels_from_string(argv[i]);
705
0
        if (msg) {
706
0
            unixctl_command_reply_error(conn, msg);
707
0
            free(msg);
708
0
            return;
709
0
        }
710
0
    }
711
0
    unixctl_command_reply(conn, NULL);
712
0
}
713
714
static void
715
vlog_unixctl_list(struct unixctl_conn *conn, int argc OVS_UNUSED,
716
                  const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
717
0
{
718
0
    char *msg = vlog_get_levels();
719
0
    unixctl_command_reply(conn, msg);
720
0
    free(msg);
721
0
}
722
723
static void
724
vlog_unixctl_list_pattern(struct unixctl_conn *conn, int argc OVS_UNUSED,
725
                          const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
726
0
{
727
0
    char *msg;
728
729
0
    msg = vlog_get_patterns();
730
0
    unixctl_command_reply(conn, msg);
731
0
    free(msg);
732
0
}
733
734
static void
735
vlog_unixctl_reopen(struct unixctl_conn *conn, int argc OVS_UNUSED,
736
                    const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
737
0
{
738
0
    bool has_log_file;
739
740
0
    ovs_mutex_lock(&log_file_mutex);
741
0
    has_log_file = log_file_name != NULL;
742
0
    ovs_mutex_unlock(&log_file_mutex);
743
744
0
    if (has_log_file) {
745
0
        int error = vlog_reopen_log_file();
746
0
        if (error) {
747
0
            unixctl_command_reply_error(conn, ovs_strerror(errno));
748
0
        } else {
749
0
            unixctl_command_reply(conn, NULL);
750
0
        }
751
0
    } else {
752
0
        unixctl_command_reply_error(conn, "Logging to file not configured");
753
0
    }
754
0
}
755
756
static void
757
vlog_unixctl_close(struct unixctl_conn *conn, int argc OVS_UNUSED,
758
                   const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
759
0
{
760
0
    ovs_mutex_lock(&log_file_mutex);
761
0
    if (log_fd >= 0) {
762
0
        close(log_fd);
763
0
        log_fd = -1;
764
765
0
        async_append_destroy(log_writer);
766
0
        log_writer = NULL;
767
768
0
        struct vlog_module *mp;
769
0
        LIST_FOR_EACH (mp, list, &vlog_modules) {
770
0
            update_min_level(mp);
771
0
        }
772
0
    }
773
0
    ovs_mutex_unlock(&log_file_mutex);
774
775
0
    unixctl_command_reply(conn, NULL);
776
0
}
777
778
static void
779
set_all_rate_limits(bool enable)
780
0
{
781
0
    struct vlog_module *mp;
782
783
0
    ovs_mutex_lock(&log_file_mutex);
784
0
    LIST_FOR_EACH (mp, list, &vlog_modules) {
785
0
        mp->honor_rate_limits = enable;
786
0
    }
787
0
    ovs_mutex_unlock(&log_file_mutex);
788
0
}
789
790
static void
791
set_rate_limits(struct unixctl_conn *conn, int argc,
792
                const char *argv[], bool enable)
793
0
{
794
0
    if (argc > 1) {
795
0
        int i;
796
797
0
        for (i = 1; i < argc; i++) {
798
0
            if (!strcasecmp(argv[i], "ANY")) {
799
0
                set_all_rate_limits(enable);
800
0
            } else {
801
0
                struct vlog_module *module = vlog_module_from_name(argv[i]);
802
0
                if (!module) {
803
0
                    unixctl_command_reply_error(conn, "unknown module");
804
0
                    return;
805
0
                }
806
0
                module->honor_rate_limits = enable;
807
0
            }
808
0
        }
809
0
    } else {
810
0
        set_all_rate_limits(enable);
811
0
    }
812
0
    unixctl_command_reply(conn, NULL);
813
0
}
814
815
static void
816
vlog_enable_rate_limit(struct unixctl_conn *conn, int argc,
817
                       const char *argv[], void *aux OVS_UNUSED)
818
0
{
819
0
    set_rate_limits(conn, argc, argv, true);
820
0
}
821
822
static void
823
vlog_disable_rate_limit(struct unixctl_conn *conn, int argc,
824
                       const char *argv[], void *aux OVS_UNUSED)
825
0
{
826
0
    set_rate_limits(conn, argc, argv, false);
827
0
}
828
829
static void
830
vlog_inject_info_log(struct unixctl_conn *conn, int argc OVS_UNUSED,
831
                     const char *argv[], void *aux OVS_UNUSED)
832
0
{
833
0
    VLOG_INFO("user-message: %s", argv[1]);
834
0
    unixctl_command_reply(conn, NULL);
835
0
}
836
837
/* Initializes the logging subsystem and registers its unixctl server
838
 * commands. */
839
void
840
vlog_init(void)
841
0
{
842
0
    static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
843
844
0
    if (ovsthread_once_start(&once)) {
845
0
        long long int now;
846
0
        int facility;
847
0
        bool print_syslog_target_deprecation;
848
849
        /* Do initialization work that needs to be done before any logging
850
         * occurs.  We want to keep this really minimal because any attempt to
851
         * log anything before calling ovsthread_once_done() will deadlock. */
852
0
        atomic_read_explicit(&log_facility, &facility, memory_order_relaxed);
853
0
        if (!syslogger) {
854
0
            char *env = getenv("OVS_SYSLOG_METHOD");
855
0
            if (env && env[0]) {
856
0
                vlog_set_syslog_method(env);
857
0
            } else {
858
0
                syslogger = syslog_libc_create();
859
0
            }
860
0
        }
861
0
        syslogger->class->openlog(syslogger, facility ? facility : LOG_DAEMON);
862
0
        ovsthread_once_done(&once);
863
864
        /* Now do anything that we want to happen only once but doesn't have to
865
         * finish before we start logging. */
866
867
0
        now = time_wall_msec();
868
0
        if (now < 0) {
869
0
            char *s = xastrftime_msec("%a, %d %b %Y %H:%M:%S", now, true);
870
0
            VLOG_ERR("current time is negative: %s (%lld)", s, now);
871
0
            free(s);
872
0
        }
873
874
0
        unixctl_command_register(
875
0
            "vlog/set", "{spec | PATTERN:destination:pattern}",
876
0
            0, INT_MAX, vlog_unixctl_set, NULL);
877
0
        unixctl_command_register("vlog/list", "", 0, 0, vlog_unixctl_list,
878
0
                                 NULL);
879
0
        unixctl_command_register("vlog/list-pattern", "", 0, 0,
880
0
                                 vlog_unixctl_list_pattern, NULL);
881
0
        unixctl_command_register("vlog/enable-rate-limit", "[module]...",
882
0
                                 0, INT_MAX, vlog_enable_rate_limit, NULL);
883
0
        unixctl_command_register("vlog/disable-rate-limit", "[module]...",
884
0
                                 0, INT_MAX, vlog_disable_rate_limit, NULL);
885
0
        unixctl_command_register("vlog/reopen", "", 0, 0,
886
0
                                 vlog_unixctl_reopen, NULL);
887
0
        unixctl_command_register("vlog/close", "", 0, 0,
888
0
                                 vlog_unixctl_close, NULL);
889
0
        unixctl_command_register("vlog/message", NULL, 1, 1,
890
0
                                 vlog_inject_info_log, NULL);
891
892
0
        ovs_rwlock_rdlock(&pattern_rwlock);
893
0
        print_syslog_target_deprecation = syslog_fd >= 0;
894
0
        ovs_rwlock_unlock(&pattern_rwlock);
895
896
0
        if (print_syslog_target_deprecation) {
897
0
            VLOG_WARN("--syslog-target flag is deprecated, use "
898
0
                      "--syslog-method instead");
899
0
        }
900
0
    }
901
0
}
902
903
/* Enables VLF_FILE log output to be written asynchronously to disk.
904
 * Asynchronous file writes avoid blocking the process in the case of a busy
905
 * disk, but on the other hand they are less robust: there is a chance that the
906
 * write will not make it to the log file if the process crashes soon after the
907
 * log call. */
908
void
909
vlog_enable_async(void)
910
0
{
911
0
    ovs_mutex_lock(&log_file_mutex);
912
0
    log_async = true;
913
0
    if (log_fd >= 0 && !log_writer) {
914
0
        log_writer = async_append_create(log_fd);
915
0
    }
916
0
    ovs_mutex_unlock(&log_file_mutex);
917
0
}
918
919
void
920
vlog_disable_async(void)
921
0
{
922
0
    ovs_mutex_lock(&log_file_mutex);
923
0
    log_async = false;
924
0
    async_append_destroy(log_writer);
925
0
    log_writer = NULL;
926
0
    ovs_mutex_unlock(&log_file_mutex);
927
0
}
928
929
/* Print the current logging level for each module. */
930
char *
931
vlog_get_levels(void)
932
0
{
933
0
    struct ds s = DS_EMPTY_INITIALIZER;
934
0
    struct vlog_module *mp;
935
0
    struct svec lines = SVEC_EMPTY_INITIALIZER;
936
0
    size_t i;
937
938
0
    ds_put_format(&s, "                 console    syslog    file\n");
939
0
    ds_put_format(&s, "                 -------    ------    ------\n");
940
941
0
    ovs_mutex_lock(&log_file_mutex);
942
0
    LIST_FOR_EACH (mp, list, &vlog_modules) {
943
0
        struct ds line;
944
945
0
        ds_init(&line);
946
0
        ds_put_format(&line, "%-16s  %4s       %4s       %4s",
947
0
                      vlog_get_module_name(mp),
948
0
                      vlog_get_level_name(vlog_get_level(mp, VLF_CONSOLE)),
949
0
                      vlog_get_level_name(vlog_get_level(mp, VLF_SYSLOG)),
950
0
                      vlog_get_level_name(vlog_get_level(mp, VLF_FILE)));
951
0
        if (!mp->honor_rate_limits) {
952
0
            ds_put_cstr(&line, "    (rate limiting disabled)");
953
0
        }
954
0
        ds_put_char(&line, '\n');
955
956
0
        svec_add_nocopy(&lines, ds_steal_cstr(&line));
957
0
    }
958
0
    ovs_mutex_unlock(&log_file_mutex);
959
960
0
    svec_sort(&lines);
961
962
0
    char *line;
963
0
    SVEC_FOR_EACH (i, line, &lines) {
964
0
        ds_put_cstr(&s, line);
965
0
    }
966
0
    svec_destroy(&lines);
967
968
0
    return ds_cstr(&s);
969
0
}
970
971
/* Returns as a string current logging patterns for each destination.
972
 * This string must be released by caller. */
973
char *
974
vlog_get_patterns(void)
975
0
{
976
0
    struct ds ds = DS_EMPTY_INITIALIZER;
977
0
    enum vlog_destination destination;
978
979
0
    ovs_rwlock_rdlock(&pattern_rwlock);
980
0
    ds_put_format(&ds, "         prefix                            format\n");
981
0
    ds_put_format(&ds, "         ------                            ------\n");
982
983
0
    for (destination = 0; destination < VLF_N_DESTINATIONS; destination++) {
984
0
        struct destination *f = &destinations[destination];
985
0
        const char *prefix = "none";
986
987
0
        if (destination == VLF_SYSLOG && syslogger) {
988
0
            prefix = syslog_get_prefix(syslogger);
989
0
        }
990
0
        ds_put_format(&ds, "%-7s  %-32s  %s\n", f->name, prefix, f->pattern);
991
0
    }
992
0
    ovs_rwlock_unlock(&pattern_rwlock);
993
994
0
    return ds_cstr(&ds);
995
0
}
996
997
/* Returns true if a log message emitted for the given 'module' and 'level'
998
 * would cause some log output, false if that module and level are completely
999
 * disabled. */
1000
bool
1001
vlog_is_enabled(const struct vlog_module *module, enum vlog_level level)
1002
0
{
1003
0
    return module->min_level >= level;
1004
0
}
1005
1006
static const char *
1007
fetch_braces(const char *p, const char *def, char *out, size_t out_size)
1008
0
{
1009
0
    if (*p == '{') {
1010
0
        size_t n = strcspn(p + 1, "}");
1011
0
        size_t n_copy = MIN(n, out_size - 1);
1012
0
        memcpy(out, p + 1, n_copy);
1013
0
        out[n_copy] = '\0';
1014
0
        p += n + 2;
1015
0
    } else {
1016
0
        ovs_strlcpy(out, def, out_size);
1017
0
    }
1018
0
    return p;
1019
0
}
1020
1021
static void
1022
format_log_message(const struct vlog_module *module, enum vlog_level level,
1023
                   const char *pattern, const char *message,
1024
                   va_list args_, struct ds *s)
1025
0
{
1026
0
    char tmp[128];
1027
0
    va_list args;
1028
0
    const char *p;
1029
0
    int facility;
1030
1031
0
    ds_clear(s);
1032
0
    for (p = pattern; *p != '\0'; ) {
1033
0
        const char *subprogram_name;
1034
0
        enum { LEFT, RIGHT } justify = RIGHT;
1035
0
        int pad = ' ';
1036
0
        size_t length, field, used;
1037
1038
0
        if (*p != '%') {
1039
0
            ds_put_char(s, *p++);
1040
0
            continue;
1041
0
        }
1042
1043
0
        p++;
1044
0
        if (*p == '-') {
1045
0
            justify = LEFT;
1046
0
            p++;
1047
0
        }
1048
0
        if (*p == '0') {
1049
0
            pad = '0';
1050
0
            p++;
1051
0
        }
1052
0
        field = 0;
1053
0
        while (isdigit((unsigned char)*p)) {
1054
0
            field = (field * 10) + (*p - '0');
1055
0
            p++;
1056
0
        }
1057
1058
0
        length = s->length;
1059
0
        switch (*p++) {
1060
0
        case 'A':
1061
0
            ds_put_cstr(s, program_name);
1062
0
            break;
1063
0
        case 'B':
1064
0
            atomic_read_explicit(&log_facility, &facility,
1065
0
                                 memory_order_relaxed);
1066
0
            facility = facility ? facility : LOG_LOCAL0;
1067
0
            ds_put_format(s, "%d", facility + syslog_levels[level]);
1068
0
            break;
1069
0
        case 'c':
1070
0
            p = fetch_braces(p, "", tmp, sizeof tmp);
1071
0
            ds_put_cstr(s, vlog_get_module_name(module));
1072
0
            break;
1073
0
        case 'd':
1074
0
            p = fetch_braces(p, "%Y-%m-%d %H:%M:%S.###", tmp, sizeof tmp);
1075
0
            ds_put_strftime_msec(s, tmp, time_wall_msec(), false);
1076
0
            break;
1077
0
        case 'D':
1078
0
            p = fetch_braces(p, "%Y-%m-%d %H:%M:%S.###", tmp, sizeof tmp);
1079
0
            ds_put_strftime_msec(s, tmp, time_wall_msec(), true);
1080
0
            break;
1081
0
        case 'E':
1082
0
            gethostname(tmp, sizeof tmp);
1083
0
            tmp[sizeof tmp - 1] = '\0';
1084
0
            ds_put_cstr(s, tmp);
1085
0
            break;
1086
0
        case 'm':
1087
            /* Format user-supplied log message and trim trailing new-lines. */
1088
0
            length = s->length;
1089
0
            va_copy(args, args_);
1090
0
            ds_put_format_valist(s, message, args);
1091
0
            va_end(args);
1092
0
            while (s->length > length && s->string[s->length - 1] == '\n') {
1093
0
                s->length--;
1094
0
            }
1095
0
            break;
1096
0
        case 'N':
1097
0
            ds_put_format(s, "%u", *msg_num_get_unsafe());
1098
0
            break;
1099
0
        case 'n':
1100
0
            ds_put_char(s, '\n');
1101
0
            break;
1102
0
        case 'p':
1103
0
            ds_put_cstr(s, vlog_get_level_name(level));
1104
0
            break;
1105
0
        case 'P':
1106
0
            ds_put_format(s, "%ld", (long int) getpid());
1107
0
            break;
1108
0
        case 'r':
1109
0
            ds_put_format(s, "%lld", time_msec() - time_boot_msec());
1110
0
            break;
1111
0
        case 't':
1112
0
            subprogram_name = get_subprogram_name();
1113
0
            ds_put_cstr(s, subprogram_name[0] ? subprogram_name : "main");
1114
0
            break;
1115
0
        case 'T':
1116
0
            subprogram_name = get_subprogram_name();
1117
0
            if (subprogram_name[0]) {
1118
0
                ds_put_format(s, "(%s)", subprogram_name);
1119
0
            }
1120
0
            break;
1121
0
        default:
1122
0
            ds_put_char(s, p[-1]);
1123
0
            break;
1124
0
        }
1125
0
        used = s->length - length;
1126
0
        if (used < field) {
1127
0
            size_t n_pad = field - used;
1128
0
            if (justify == RIGHT) {
1129
0
                ds_put_uninit(s, n_pad);
1130
0
                memmove(&s->string[length + n_pad], &s->string[length], used);
1131
0
                memset(&s->string[length], pad, n_pad);
1132
0
            } else {
1133
0
                ds_put_char_multiple(s, pad, n_pad);
1134
0
            }
1135
0
        }
1136
0
    }
1137
0
}
1138
1139
/* Exports the given 'syslog_message' to the configured udp syslog sink. */
1140
static void
1141
send_to_syslog_fd(const char *s, size_t length)
1142
    OVS_REQ_RDLOCK(pattern_rwlock)
1143
0
{
1144
0
    static size_t max_length = SIZE_MAX;
1145
0
    size_t send_len = MIN(length, max_length);
1146
1147
0
    while (write(syslog_fd, s, send_len) < 0 && errno == EMSGSIZE) {
1148
0
        send_len -= send_len / 20;
1149
0
        max_length = send_len;
1150
0
    }
1151
0
}
1152
1153
/* Writes 'message' to the log at the given 'level' and as coming from the
1154
 * given 'module'.
1155
 *
1156
 * Guaranteed to preserve errno. */
1157
void
1158
vlog_valist(const struct vlog_module *module, enum vlog_level level,
1159
            const char *message, va_list args)
1160
0
{
1161
0
    bool log_to_console = module->levels[VLF_CONSOLE] >= level;
1162
0
    bool log_to_syslog = module->levels[VLF_SYSLOG] >= level;
1163
0
    bool log_to_file = module->levels[VLF_FILE]  >= level;
1164
1165
0
    if (!(log_to_console || log_to_syslog || log_to_file)) {
1166
        /* fast path - all logging levels specify no logging, no
1167
         * need to hog the log mutex
1168
         */
1169
0
        return;
1170
0
    }
1171
1172
0
    ovs_mutex_lock(&log_file_mutex);
1173
0
    log_to_file &= (log_fd >= 0);
1174
0
    ovs_mutex_unlock(&log_file_mutex);
1175
0
    if (log_to_console || log_to_syslog || log_to_file) {
1176
0
        int save_errno = errno;
1177
0
        struct ds s;
1178
1179
0
        vlog_init();
1180
1181
0
        ds_init(&s);
1182
0
        ds_reserve(&s, 1024);
1183
0
        ++*msg_num_get();
1184
1185
0
        ovs_rwlock_rdlock(&pattern_rwlock);
1186
0
        if (log_to_console) {
1187
0
            format_log_message(module, level,
1188
0
                               destinations[VLF_CONSOLE].pattern, message,
1189
0
                               args, &s);
1190
0
            ds_put_char(&s, '\n');
1191
0
            fputs(ds_cstr(&s), stderr);
1192
0
        }
1193
1194
0
        if (log_to_syslog) {
1195
0
            int syslog_level = syslog_levels[level];
1196
0
            char *save_ptr = NULL;
1197
0
            char *line;
1198
0
            int facility;
1199
1200
0
            format_log_message(module, level, destinations[VLF_SYSLOG].pattern,
1201
0
                               message, args, &s);
1202
0
            for (line = strtok_r(s.string, "\n", &save_ptr); line;
1203
0
                 line = strtok_r(NULL, "\n", &save_ptr)) {
1204
0
                atomic_read_explicit(&log_facility, &facility,
1205
0
                                     memory_order_relaxed);
1206
0
                syslogger->class->syslog(syslogger, syslog_level|facility, line);
1207
0
            }
1208
1209
0
            if (syslog_fd >= 0) {
1210
0
                format_log_message(module, level,
1211
0
                                   "<%B>1 %D{%Y-%m-%dT%H:%M:%S.###Z} "
1212
0
                                   "%E %A %P %c - \xef\xbb\xbf%m",
1213
0
                                   message, args, &s);
1214
0
                send_to_syslog_fd(ds_cstr(&s), s.length);
1215
0
            }
1216
0
        }
1217
1218
0
        if (log_to_file) {
1219
0
            format_log_message(module, level, destinations[VLF_FILE].pattern,
1220
0
                               message, args, &s);
1221
0
            ds_put_char(&s, '\n');
1222
1223
0
            ovs_mutex_lock(&log_file_mutex);
1224
0
            if (log_fd >= 0) {
1225
0
                if (log_writer) {
1226
0
                    async_append_write(log_writer, s.string, s.length);
1227
0
                    if (level == VLL_EMER) {
1228
0
                        async_append_flush(log_writer);
1229
0
                    }
1230
0
                } else {
1231
0
                    ignore(write(log_fd, s.string, s.length));
1232
0
                }
1233
0
            }
1234
0
            ovs_mutex_unlock(&log_file_mutex);
1235
0
        }
1236
0
        ovs_rwlock_unlock(&pattern_rwlock);
1237
1238
0
        ds_destroy(&s);
1239
0
        errno = save_errno;
1240
0
    }
1241
0
}
1242
1243
void
1244
vlog(const struct vlog_module *module, enum vlog_level level,
1245
     const char *message, ...)
1246
0
{
1247
0
    va_list args;
1248
1249
0
    va_start(args, message);
1250
0
    vlog_valist(module, level, message, args);
1251
0
    va_end(args);
1252
0
}
1253
1254
/* Logs 'message' to 'module' at maximum verbosity, then exits with a failure
1255
 * exit code.  Always writes the message to stderr, even if the console
1256
 * destination is disabled.
1257
 *
1258
 * Choose this function instead of vlog_abort_valist() if the daemon monitoring
1259
 * facility shouldn't automatically restart the current daemon.  */
1260
void
1261
vlog_fatal_valist(const struct vlog_module *module_,
1262
                  const char *message, va_list args)
1263
0
{
1264
0
    struct vlog_module *module = CONST_CAST(struct vlog_module *, module_);
1265
1266
    /* Don't log this message to the console to avoid redundancy with the
1267
     * message written by the later ovs_fatal_valist(). */
1268
0
    module->levels[VLF_CONSOLE] = VLL_OFF;
1269
1270
0
    vlog_valist(module, VLL_EMER, message, args);
1271
0
    ovs_fatal_valist(0, message, args);
1272
0
}
1273
1274
/* Logs 'message' to 'module' at maximum verbosity, then exits with a failure
1275
 * exit code.  Always writes the message to stderr, even if the console
1276
 * destination is disabled.
1277
 *
1278
 * Choose this function instead of vlog_abort() if the daemon monitoring
1279
 * facility shouldn't automatically restart the current daemon.  */
1280
void
1281
vlog_fatal(const struct vlog_module *module, const char *message, ...)
1282
0
{
1283
0
    va_list args;
1284
1285
0
    va_start(args, message);
1286
0
    vlog_fatal_valist(module, message, args);
1287
0
    va_end(args);
1288
0
}
1289
1290
/* Attempts to log a stack trace, logs 'message' to 'module' at maximum
1291
 * verbosity, then calls abort().  Always writes the message to stderr, even
1292
 * if the console destination is disabled.
1293
 *
1294
 * Choose this function instead of vlog_fatal_valist() if the daemon monitoring
1295
 * facility should automatically restart the current daemon.  */
1296
void
1297
vlog_abort_valist(const struct vlog_module *module_,
1298
                  const char *message, va_list args)
1299
0
{
1300
0
    struct vlog_module *module = (struct vlog_module *) module_;
1301
1302
    /* Don't log this message to the console to avoid redundancy with the
1303
     * message written by the later ovs_abort_valist(). */
1304
0
    module->levels[VLF_CONSOLE] = VLL_OFF;
1305
1306
    /* Printing the stack trace before the 'message', because the 'message'
1307
     * will flush the async log queue (VLL_EMER).  With a different order we
1308
     * would need to flush the queue manually again. */
1309
0
    log_backtrace();
1310
0
    vlog_valist(module, VLL_EMER, message, args);
1311
0
    ovs_abort_valist(0, message, args);
1312
0
}
1313
1314
/* Logs 'message' to 'module' at maximum verbosity, then calls abort().  Always
1315
 * writes the message to stderr, even if the console destination is disabled.
1316
 *
1317
 * Choose this function instead of vlog_fatal() if the daemon monitoring
1318
 * facility should automatically restart the current daemon.  */
1319
void
1320
vlog_abort(const struct vlog_module *module, const char *message, ...)
1321
0
{
1322
0
    va_list args;
1323
1324
0
    va_start(args, message);
1325
0
    vlog_abort_valist(module, message, args);
1326
0
    va_end(args);
1327
0
}
1328
1329
bool
1330
vlog_should_drop(const struct vlog_module *module, enum vlog_level level,
1331
                 struct vlog_rate_limit *rl)
1332
0
{
1333
0
    if (!module->honor_rate_limits) {
1334
0
        return false;
1335
0
    }
1336
1337
0
    if (!vlog_is_enabled(module, level)) {
1338
0
        return true;
1339
0
    }
1340
1341
0
    ovs_mutex_lock(&rl->mutex);
1342
0
    if (!token_bucket_withdraw(&rl->token_bucket, VLOG_MSG_TOKENS)) {
1343
0
        time_t now = time_now();
1344
0
        if (!rl->n_dropped) {
1345
0
            rl->first_dropped = now;
1346
0
        }
1347
0
        rl->last_dropped = now;
1348
0
        rl->n_dropped++;
1349
0
        ovs_mutex_unlock(&rl->mutex);
1350
0
        return true;
1351
0
    }
1352
1353
0
    if (!rl->n_dropped) {
1354
0
        ovs_mutex_unlock(&rl->mutex);
1355
0
    } else {
1356
0
        time_t now = time_now();
1357
0
        unsigned int n_dropped = rl->n_dropped;
1358
0
        unsigned int first_dropped_elapsed = now - rl->first_dropped;
1359
0
        unsigned int last_dropped_elapsed = now - rl->last_dropped;
1360
0
        rl->n_dropped = 0;
1361
0
        ovs_mutex_unlock(&rl->mutex);
1362
1363
0
        vlog(module, level,
1364
0
             "Dropped %u log messages in last %u seconds (most recently, "
1365
0
             "%u seconds ago) due to excessive rate",
1366
0
             n_dropped, first_dropped_elapsed, last_dropped_elapsed);
1367
0
    }
1368
1369
0
    return false;
1370
0
}
1371
1372
void
1373
vlog_rate_limit(const struct vlog_module *module, enum vlog_level level,
1374
                struct vlog_rate_limit *rl, const char *message, ...)
1375
0
{
1376
0
    if (!vlog_should_drop(module, level, rl)) {
1377
0
        va_list args;
1378
1379
0
        va_start(args, message);
1380
0
        vlog_valist(module, level, message, args);
1381
0
        va_end(args);
1382
0
    }
1383
0
}
1384
1385
void
1386
vlog_usage(void)
1387
0
{
1388
0
    printf("\n\
1389
0
Logging options:\n\
1390
0
  -vSPEC, --verbose=SPEC   set logging levels\n\
1391
0
  -v, --verbose            set maximum verbosity level\n\
1392
0
  --log-file[=FILE]        enable logging to specified FILE\n\
1393
0
                           (default: %s/%s.log)\n\
1394
0
  --syslog-method=(libc|unix:file|udp:ip:port)\n\
1395
0
                           specify how to send messages to syslog daemon\n\
1396
0
  --syslog-target=HOST:PORT  also send syslog msgs to HOST:PORT via UDP\n",
1397
0
           ovs_logdir(), program_name);
1398
0
}