Coverage Report

Created: 2026-05-30 06:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openvswitch/lib/vlog.c
Line
Count
Source
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
182
{
219
182
    ovs_mutex_lock(&log_file_mutex);
220
182
    ovs_list_insert(&vlog_modules, vlog);
221
182
    ovs_mutex_unlock(&log_file_mutex);
222
182
}
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
/* In case a log file exists, change its owner to new 'user' and 'group'.
483
 *
484
 * This is useful for handling cases where the --log-file option is
485
 * specified ahead of the --user option.  */
486
void
487
vlog_change_owner_unix(uid_t user, gid_t group)
488
0
{
489
0
    struct ds err = DS_EMPTY_INITIALIZER;
490
0
    int error;
491
492
0
    ovs_mutex_lock(&log_file_mutex);
493
0
    error = log_file_name ? chown(log_file_name, user, group) : 0;
494
0
    if (error) {
495
        /* Build the error message. We can not call VLOG_FATAL directly
496
         * here because VLOG_FATAL() will try again to to acquire
497
         * 'log_file_mutex' lock, causing deadlock.
498
         */
499
0
        ds_put_format(&err, "Failed to change %s ownership: %s.",
500
0
                      log_file_name, ovs_strerror(errno));
501
0
    }
502
0
    ovs_mutex_unlock(&log_file_mutex);
503
504
0
    if (error) {
505
0
        VLOG_FATAL("%s", ds_steal_cstr(&err));
506
0
    }
507
0
}
508
509
/* Set debugging levels.  Returns null if successful, otherwise an error
510
 * message that the caller must free(). */
511
char *
512
vlog_set_levels_from_string(const char *s_)
513
0
{
514
0
    char *s = xstrdup(s_);
515
0
    char *save_ptr = NULL;
516
0
    char *msg = NULL;
517
0
    char *word;
518
519
0
    word = strtok_r(s, " ,:\t", &save_ptr);
520
0
    if (word && !strcasecmp(word, "PATTERN")) {
521
0
        enum vlog_destination destination;
522
523
0
        word = strtok_r(NULL, " ,:\t", &save_ptr);
524
0
        if (!word) {
525
0
            msg = xstrdup("missing destination");
526
0
            goto exit;
527
0
        }
528
529
0
        destination = (!strcasecmp(word, "ANY")
530
0
                       ? VLF_ANY_DESTINATION
531
0
                       : vlog_get_destination_val(word));
532
0
        if (destination == VLF_N_DESTINATIONS) {
533
0
            msg = xasprintf("unknown destination \"%s\"", word);
534
0
            goto exit;
535
0
        }
536
0
        vlog_set_pattern(destination, save_ptr);
537
0
    } else if (word && !strcasecmp(word, "FACILITY")) {
538
0
        int value;
539
540
0
        if (!vlog_facility_exists(save_ptr, &value)) {
541
0
            msg = xstrdup("invalid facility");
542
0
            goto exit;
543
0
        }
544
0
        atomic_store_explicit(&log_facility, value, memory_order_relaxed);
545
0
    } else {
546
0
        struct vlog_module *module = NULL;
547
0
        enum vlog_level level = VLL_N_LEVELS;
548
0
        enum vlog_destination destination = VLF_N_DESTINATIONS;
549
550
0
        for (; word != NULL; word = strtok_r(NULL, " ,:\t", &save_ptr)) {
551
0
            if (!strcasecmp(word, "ANY")) {
552
0
                continue;
553
0
            } else if (vlog_get_destination_val(word) != VLF_N_DESTINATIONS) {
554
0
                if (destination != VLF_N_DESTINATIONS) {
555
0
                    msg = xstrdup("cannot specify multiple destinations");
556
0
                    goto exit;
557
0
                }
558
0
                destination = vlog_get_destination_val(word);
559
0
            } else if (vlog_get_level_val(word) != VLL_N_LEVELS) {
560
0
                if (level != VLL_N_LEVELS) {
561
0
                    msg = xstrdup("cannot specify multiple levels");
562
0
                    goto exit;
563
0
                }
564
0
                level = vlog_get_level_val(word);
565
0
            } else if (vlog_module_from_name(word)) {
566
0
                if (module) {
567
0
                    msg = xstrdup("cannot specify multiple modules");
568
0
                    goto exit;
569
0
                }
570
0
                module = vlog_module_from_name(word);
571
0
            } else {
572
0
                msg = xasprintf("no destination, level, or module \"%s\"",
573
0
                                word);
574
0
                goto exit;
575
0
            }
576
0
        }
577
578
0
        if (destination == VLF_N_DESTINATIONS) {
579
0
            destination = VLF_ANY_DESTINATION;
580
0
        }
581
0
        if (level == VLL_N_LEVELS) {
582
0
            level = VLL_DBG;
583
0
        }
584
0
        vlog_set_levels(module, destination, level);
585
0
    }
586
587
0
exit:
588
0
    free(s);
589
0
    return msg;
590
0
}
591
592
/* Set debugging levels.  Abort with an error message if 's' is invalid. */
593
void
594
vlog_set_levels_from_string_assert(const char *s)
595
0
{
596
0
    char *error = vlog_set_levels_from_string(s);
597
0
    if (error) {
598
0
        ovs_fatal(0, "%s", error);
599
0
    }
600
0
}
601
602
/* If 'arg' is null, configure maximum verbosity.  Otherwise, sets
603
 * configuration according to 'arg' (see vlog_set_levels_from_string()). */
604
void
605
vlog_set_verbosity(const char *arg)
606
0
{
607
0
    if (arg) {
608
0
        char *msg = vlog_set_levels_from_string(arg);
609
0
        if (msg) {
610
0
            ovs_fatal(0, "processing \"%s\": %s", arg, msg);
611
0
        }
612
0
    } else {
613
0
        vlog_set_levels(NULL, VLF_ANY_DESTINATION, VLL_DBG);
614
0
    }
615
0
}
616
617
void
618
vlog_set_syslog_method(const char *method)
619
0
{
620
0
    if (syslogger) {
621
        /* Set syslogger only, if one is not already set.  This effectively
622
         * means that only the first --syslog-method argument is honored. */
623
0
        return;
624
0
    }
625
626
0
    if (!strcmp(method, "null")) {
627
0
        syslogger = syslog_null_create();
628
0
    } else if (!strcmp(method, "libc")) {
629
0
        syslogger = syslog_libc_create();
630
0
    } else if (!strncmp(method, "udp:", 4) || !strncmp(method, "unix:", 5)) {
631
0
        syslogger = syslog_direct_create(method);
632
0
    } else {
633
0
        ovs_fatal(0, "unsupported syslog method '%s'", method);
634
0
    }
635
0
}
636
637
/* Set the vlog udp syslog target. */
638
void
639
vlog_set_syslog_target(const char *target)
640
0
{
641
0
    int new_fd;
642
643
0
    inet_open_active(SOCK_DGRAM, target, -1, NULL, &new_fd, 0);
644
645
0
    ovs_rwlock_wrlock(&pattern_rwlock);
646
0
    if (syslog_fd >= 0) {
647
0
        close(syslog_fd);
648
0
    }
649
0
    syslog_fd = new_fd;
650
0
    ovs_rwlock_unlock(&pattern_rwlock);
651
0
}
652
653
/*
654
 * This function writes directly to log file without using async writer or
655
 * taking a lock.  Caller must hold 'log_file_mutex' or be sure that it's
656
 * not necessary.  Could be used in exceptional cases like dumping of backtrace
657
 * on fatal signals.
658
 */
659
void
660
vlog_direct_write_to_log_file_unsafe(const char *s)
661
    OVS_NO_THREAD_SAFETY_ANALYSIS
662
0
{
663
0
    if (log_fd >= 0) {
664
0
        ovs_ignore(write(log_fd, s, strlen(s)));
665
0
    }
666
0
}
667
668
int
669
vlog_get_log_file_fd_unsafe(void)
670
    OVS_NO_THREAD_SAFETY_ANALYSIS
671
0
{
672
0
    return log_fd;
673
0
}
674
675
/* Returns 'false' if 'facility' is not a valid string. If 'facility'
676
 * is a valid string, sets 'value' with the integer value of 'facility'
677
 * and returns 'true'. */
678
static bool
679
vlog_facility_exists(const char* facility, int *value)
680
0
{
681
0
    size_t i;
682
0
    for (i = 0; i < ARRAY_SIZE(vlog_facilities); i++) {
683
0
        if (!strcasecmp(vlog_facilities[i].name, facility)) {
684
0
            *value = vlog_facilities[i].value;
685
0
            return true;
686
0
        }
687
0
    }
688
0
    return false;
689
0
}
690
691
static void
692
vlog_unixctl_set(struct unixctl_conn *conn, int argc, const char *argv[],
693
                 void *aux OVS_UNUSED)
694
0
{
695
0
    int i;
696
697
    /* With no argument, set all destinations and modules to "dbg". */
698
0
    if (argc == 1) {
699
0
        vlog_set_levels(NULL, VLF_ANY_DESTINATION, VLL_DBG);
700
0
    }
701
0
    for (i = 1; i < argc; i++) {
702
0
        char *msg = vlog_set_levels_from_string(argv[i]);
703
0
        if (msg) {
704
0
            unixctl_command_reply_error(conn, msg);
705
0
            free(msg);
706
0
            return;
707
0
        }
708
0
    }
709
0
    unixctl_command_reply(conn, NULL);
710
0
}
711
712
static void
713
vlog_unixctl_list(struct unixctl_conn *conn, int argc OVS_UNUSED,
714
                  const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
715
0
{
716
0
    char *msg = vlog_get_levels();
717
0
    unixctl_command_reply(conn, msg);
718
0
    free(msg);
719
0
}
720
721
static void
722
vlog_unixctl_list_pattern(struct unixctl_conn *conn, int argc OVS_UNUSED,
723
                          const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
724
0
{
725
0
    char *msg;
726
727
0
    msg = vlog_get_patterns();
728
0
    unixctl_command_reply(conn, msg);
729
0
    free(msg);
730
0
}
731
732
static void
733
vlog_unixctl_reopen(struct unixctl_conn *conn, int argc OVS_UNUSED,
734
                    const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
735
0
{
736
0
    bool has_log_file;
737
738
0
    ovs_mutex_lock(&log_file_mutex);
739
0
    has_log_file = log_file_name != NULL;
740
0
    ovs_mutex_unlock(&log_file_mutex);
741
742
0
    if (has_log_file) {
743
0
        int error = vlog_reopen_log_file();
744
0
        if (error) {
745
0
            unixctl_command_reply_error(conn, ovs_strerror(errno));
746
0
        } else {
747
0
            unixctl_command_reply(conn, NULL);
748
0
        }
749
0
    } else {
750
0
        unixctl_command_reply_error(conn, "Logging to file not configured");
751
0
    }
752
0
}
753
754
static void
755
vlog_unixctl_close(struct unixctl_conn *conn, int argc OVS_UNUSED,
756
                   const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
757
0
{
758
0
    ovs_mutex_lock(&log_file_mutex);
759
0
    if (log_fd >= 0) {
760
0
        close(log_fd);
761
0
        log_fd = -1;
762
763
0
        async_append_destroy(log_writer);
764
0
        log_writer = NULL;
765
766
0
        struct vlog_module *mp;
767
0
        LIST_FOR_EACH (mp, list, &vlog_modules) {
768
0
            update_min_level(mp);
769
0
        }
770
0
    }
771
0
    ovs_mutex_unlock(&log_file_mutex);
772
773
0
    unixctl_command_reply(conn, NULL);
774
0
}
775
776
static void
777
set_all_rate_limits(bool enable)
778
0
{
779
0
    struct vlog_module *mp;
780
781
0
    ovs_mutex_lock(&log_file_mutex);
782
0
    LIST_FOR_EACH (mp, list, &vlog_modules) {
783
0
        mp->honor_rate_limits = enable;
784
0
    }
785
0
    ovs_mutex_unlock(&log_file_mutex);
786
0
}
787
788
static void
789
set_rate_limits(struct unixctl_conn *conn, int argc,
790
                const char *argv[], bool enable)
791
0
{
792
0
    if (argc > 1) {
793
0
        int i;
794
795
0
        for (i = 1; i < argc; i++) {
796
0
            if (!strcasecmp(argv[i], "ANY")) {
797
0
                set_all_rate_limits(enable);
798
0
            } else {
799
0
                struct vlog_module *module = vlog_module_from_name(argv[i]);
800
0
                if (!module) {
801
0
                    unixctl_command_reply_error(conn, "unknown module");
802
0
                    return;
803
0
                }
804
0
                module->honor_rate_limits = enable;
805
0
            }
806
0
        }
807
0
    } else {
808
0
        set_all_rate_limits(enable);
809
0
    }
810
0
    unixctl_command_reply(conn, NULL);
811
0
}
812
813
static void
814
vlog_enable_rate_limit(struct unixctl_conn *conn, int argc,
815
                       const char *argv[], void *aux OVS_UNUSED)
816
0
{
817
0
    set_rate_limits(conn, argc, argv, true);
818
0
}
819
820
static void
821
vlog_disable_rate_limit(struct unixctl_conn *conn, int argc,
822
                       const char *argv[], void *aux OVS_UNUSED)
823
0
{
824
0
    set_rate_limits(conn, argc, argv, false);
825
0
}
826
827
static void
828
vlog_inject_info_log(struct unixctl_conn *conn, int argc OVS_UNUSED,
829
                     const char *argv[], void *aux OVS_UNUSED)
830
0
{
831
0
    VLOG_INFO("user-message: %s", argv[1]);
832
0
    unixctl_command_reply(conn, NULL);
833
0
}
834
835
/* Initializes the logging subsystem and registers its unixctl server
836
 * commands. */
837
void
838
vlog_init(void)
839
0
{
840
0
    static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
841
842
0
    if (ovsthread_once_start(&once)) {
843
0
        long long int now;
844
0
        int facility;
845
0
        bool print_syslog_target_deprecation;
846
847
        /* Do initialization work that needs to be done before any logging
848
         * occurs.  We want to keep this really minimal because any attempt to
849
         * log anything before calling ovsthread_once_done() will deadlock. */
850
0
        atomic_read_explicit(&log_facility, &facility, memory_order_relaxed);
851
0
        if (!syslogger) {
852
0
            char *env = getenv("OVS_SYSLOG_METHOD");
853
0
            if (env && env[0]) {
854
0
                vlog_set_syslog_method(env);
855
0
            } else {
856
0
                syslogger = syslog_libc_create();
857
0
            }
858
0
        }
859
0
        syslogger->class->openlog(syslogger, facility ? facility : LOG_DAEMON);
860
0
        ovsthread_once_done(&once);
861
862
        /* Now do anything that we want to happen only once but doesn't have to
863
         * finish before we start logging. */
864
865
0
        now = time_wall_msec();
866
0
        if (now < 0) {
867
0
            char *s = xastrftime_msec("%a, %d %b %Y %H:%M:%S", now, true);
868
0
            VLOG_ERR("current time is negative: %s (%lld)", s, now);
869
0
            free(s);
870
0
        }
871
872
0
        unixctl_command_register(
873
0
            "vlog/set", "{spec | PATTERN:destination:pattern}",
874
0
            0, INT_MAX, vlog_unixctl_set, NULL);
875
0
        unixctl_command_register("vlog/list", "", 0, 0, vlog_unixctl_list,
876
0
                                 NULL);
877
0
        unixctl_command_register("vlog/list-pattern", "", 0, 0,
878
0
                                 vlog_unixctl_list_pattern, NULL);
879
0
        unixctl_command_register("vlog/enable-rate-limit", "[module]...",
880
0
                                 0, INT_MAX, vlog_enable_rate_limit, NULL);
881
0
        unixctl_command_register("vlog/disable-rate-limit", "[module]...",
882
0
                                 0, INT_MAX, vlog_disable_rate_limit, NULL);
883
0
        unixctl_command_register("vlog/reopen", "", 0, 0,
884
0
                                 vlog_unixctl_reopen, NULL);
885
0
        unixctl_command_register("vlog/close", "", 0, 0,
886
0
                                 vlog_unixctl_close, NULL);
887
0
        unixctl_command_register("vlog/message", NULL, 1, 1,
888
0
                                 vlog_inject_info_log, NULL);
889
890
0
        ovs_rwlock_rdlock(&pattern_rwlock);
891
0
        print_syslog_target_deprecation = syslog_fd >= 0;
892
0
        ovs_rwlock_unlock(&pattern_rwlock);
893
894
0
        if (print_syslog_target_deprecation) {
895
0
            VLOG_WARN("--syslog-target flag is deprecated, use "
896
0
                      "--syslog-method instead");
897
0
        }
898
0
    }
899
0
}
900
901
/* Enables VLF_FILE log output to be written asynchronously to disk.
902
 * Asynchronous file writes avoid blocking the process in the case of a busy
903
 * disk, but on the other hand they are less robust: there is a chance that the
904
 * write will not make it to the log file if the process crashes soon after the
905
 * log call. */
906
void
907
vlog_enable_async(void)
908
0
{
909
0
    ovs_mutex_lock(&log_file_mutex);
910
0
    log_async = true;
911
0
    if (log_fd >= 0 && !log_writer) {
912
0
        log_writer = async_append_create(log_fd);
913
0
    }
914
0
    ovs_mutex_unlock(&log_file_mutex);
915
0
}
916
917
void
918
vlog_disable_async(void)
919
0
{
920
0
    ovs_mutex_lock(&log_file_mutex);
921
0
    log_async = false;
922
0
    async_append_destroy(log_writer);
923
0
    log_writer = NULL;
924
0
    ovs_mutex_unlock(&log_file_mutex);
925
0
}
926
927
/* Print the current logging level for each module. */
928
char *
929
vlog_get_levels(void)
930
0
{
931
0
    struct ds s = DS_EMPTY_INITIALIZER;
932
0
    struct vlog_module *mp;
933
0
    struct svec lines = SVEC_EMPTY_INITIALIZER;
934
0
    size_t i;
935
936
0
    ds_put_format(&s, "                 console    syslog    file\n");
937
0
    ds_put_format(&s, "                 -------    ------    ------\n");
938
939
0
    ovs_mutex_lock(&log_file_mutex);
940
0
    LIST_FOR_EACH (mp, list, &vlog_modules) {
941
0
        struct ds line;
942
943
0
        ds_init(&line);
944
0
        ds_put_format(&line, "%-16s  %4s       %4s       %4s",
945
0
                      vlog_get_module_name(mp),
946
0
                      vlog_get_level_name(vlog_get_level(mp, VLF_CONSOLE)),
947
0
                      vlog_get_level_name(vlog_get_level(mp, VLF_SYSLOG)),
948
0
                      vlog_get_level_name(vlog_get_level(mp, VLF_FILE)));
949
0
        if (!mp->honor_rate_limits) {
950
0
            ds_put_cstr(&line, "    (rate limiting disabled)");
951
0
        }
952
0
        ds_put_char(&line, '\n');
953
954
0
        svec_add_nocopy(&lines, ds_steal_cstr(&line));
955
0
    }
956
0
    ovs_mutex_unlock(&log_file_mutex);
957
958
0
    svec_sort(&lines);
959
960
0
    char *line;
961
0
    SVEC_FOR_EACH (i, line, &lines) {
962
0
        ds_put_cstr(&s, line);
963
0
    }
964
0
    svec_destroy(&lines);
965
966
0
    return ds_cstr(&s);
967
0
}
968
969
/* Returns as a string current logging patterns for each destination.
970
 * This string must be released by caller. */
971
char *
972
vlog_get_patterns(void)
973
0
{
974
0
    struct ds ds = DS_EMPTY_INITIALIZER;
975
0
    enum vlog_destination destination;
976
977
0
    ovs_rwlock_rdlock(&pattern_rwlock);
978
0
    ds_put_format(&ds, "         prefix                            format\n");
979
0
    ds_put_format(&ds, "         ------                            ------\n");
980
981
0
    for (destination = 0; destination < VLF_N_DESTINATIONS; destination++) {
982
0
        struct destination *f = &destinations[destination];
983
0
        const char *prefix = "none";
984
985
0
        if (destination == VLF_SYSLOG && syslogger) {
986
0
            prefix = syslog_get_prefix(syslogger);
987
0
        }
988
0
        ds_put_format(&ds, "%-7s  %-32s  %s\n", f->name, prefix, f->pattern);
989
0
    }
990
0
    ovs_rwlock_unlock(&pattern_rwlock);
991
992
0
    return ds_cstr(&ds);
993
0
}
994
995
/* Returns true if a log message emitted for the given 'module' and 'level'
996
 * would cause some log output, false if that module and level are completely
997
 * disabled. */
998
bool
999
vlog_is_enabled(const struct vlog_module *module, enum vlog_level level)
1000
120
{
1001
120
    return module->min_level >= level;
1002
120
}
1003
1004
static const char *
1005
fetch_braces(const char *p, const char *def, char *out, size_t out_size)
1006
0
{
1007
0
    if (*p == '{') {
1008
0
        size_t n = strcspn(p + 1, "}");
1009
0
        size_t n_copy = MIN(n, out_size - 1);
1010
0
        memcpy(out, p + 1, n_copy);
1011
0
        out[n_copy] = '\0';
1012
0
        p += n + 2;
1013
0
    } else {
1014
0
        ovs_strlcpy(out, def, out_size);
1015
0
    }
1016
0
    return p;
1017
0
}
1018
1019
static void
1020
format_log_message(const struct vlog_module *module, enum vlog_level level,
1021
                   const char *pattern, const char *message,
1022
                   va_list args_, struct ds *s)
1023
0
{
1024
0
    char tmp[128];
1025
0
    va_list args;
1026
0
    const char *p;
1027
0
    int facility;
1028
1029
0
    ds_clear(s);
1030
0
    for (p = pattern; *p != '\0'; ) {
1031
0
        const char *subprogram_name;
1032
0
        enum { LEFT, RIGHT } justify = RIGHT;
1033
0
        int pad = ' ';
1034
0
        size_t length, field, used;
1035
1036
0
        if (*p != '%') {
1037
0
            ds_put_char(s, *p++);
1038
0
            continue;
1039
0
        }
1040
1041
0
        p++;
1042
0
        if (*p == '-') {
1043
0
            justify = LEFT;
1044
0
            p++;
1045
0
        }
1046
0
        if (*p == '0') {
1047
0
            pad = '0';
1048
0
            p++;
1049
0
        }
1050
0
        field = 0;
1051
0
        while (isdigit((unsigned char)*p)) {
1052
0
            field = (field * 10) + (*p - '0');
1053
0
            p++;
1054
0
        }
1055
1056
0
        length = s->length;
1057
0
        switch (*p++) {
1058
0
        case 'A':
1059
0
            ds_put_cstr(s, program_name);
1060
0
            break;
1061
0
        case 'B':
1062
0
            atomic_read_explicit(&log_facility, &facility,
1063
0
                                 memory_order_relaxed);
1064
0
            facility = facility ? facility : LOG_LOCAL0;
1065
0
            ds_put_format(s, "%d", facility + syslog_levels[level]);
1066
0
            break;
1067
0
        case 'c':
1068
0
            p = fetch_braces(p, "", tmp, sizeof tmp);
1069
0
            ds_put_cstr(s, vlog_get_module_name(module));
1070
0
            break;
1071
0
        case 'd':
1072
0
            p = fetch_braces(p, "%Y-%m-%d %H:%M:%S.###", tmp, sizeof tmp);
1073
0
            ds_put_strftime_msec(s, tmp, time_wall_msec(), false);
1074
0
            break;
1075
0
        case 'D':
1076
0
            p = fetch_braces(p, "%Y-%m-%d %H:%M:%S.###", tmp, sizeof tmp);
1077
0
            ds_put_strftime_msec(s, tmp, time_wall_msec(), true);
1078
0
            break;
1079
0
        case 'E':
1080
0
            gethostname(tmp, sizeof tmp);
1081
0
            tmp[sizeof tmp - 1] = '\0';
1082
0
            ds_put_cstr(s, tmp);
1083
0
            break;
1084
0
        case 'm':
1085
            /* Format user-supplied log message and trim trailing new-lines. */
1086
0
            length = s->length;
1087
0
            va_copy(args, args_);
1088
0
            ds_put_format_valist(s, message, args);
1089
0
            va_end(args);
1090
0
            while (s->length > length && s->string[s->length - 1] == '\n') {
1091
0
                s->length--;
1092
0
            }
1093
0
            break;
1094
0
        case 'N':
1095
0
            ds_put_format(s, "%u", *msg_num_get_unsafe());
1096
0
            break;
1097
0
        case 'n':
1098
0
            ds_put_char(s, '\n');
1099
0
            break;
1100
0
        case 'p':
1101
0
            ds_put_cstr(s, vlog_get_level_name(level));
1102
0
            break;
1103
0
        case 'P':
1104
0
            ds_put_format(s, "%ld", (long int) getpid());
1105
0
            break;
1106
0
        case 'r':
1107
0
            ds_put_format(s, "%lld", time_msec() - time_boot_msec());
1108
0
            break;
1109
0
        case 't':
1110
0
            subprogram_name = get_subprogram_name();
1111
0
            ds_put_cstr(s, subprogram_name[0] ? subprogram_name : "main");
1112
0
            break;
1113
0
        case 'T':
1114
0
            subprogram_name = get_subprogram_name();
1115
0
            if (subprogram_name[0]) {
1116
0
                ds_put_format(s, "(%s)", subprogram_name);
1117
0
            }
1118
0
            break;
1119
0
        default:
1120
0
            ds_put_char(s, p[-1]);
1121
0
            break;
1122
0
        }
1123
0
        used = s->length - length;
1124
0
        if (used < field) {
1125
0
            size_t n_pad = field - used;
1126
0
            if (justify == RIGHT) {
1127
0
                ds_put_uninit(s, n_pad);
1128
0
                memmove(&s->string[length + n_pad], &s->string[length], used);
1129
0
                memset(&s->string[length], pad, n_pad);
1130
0
            } else {
1131
0
                ds_put_char_multiple(s, pad, n_pad);
1132
0
            }
1133
0
        }
1134
0
    }
1135
0
}
1136
1137
/* Exports the given 'syslog_message' to the configured udp syslog sink. */
1138
static void
1139
send_to_syslog_fd(const char *s, size_t length)
1140
    OVS_REQ_RDLOCK(pattern_rwlock)
1141
0
{
1142
0
    static size_t max_length = SIZE_MAX;
1143
0
    size_t send_len = MIN(length, max_length);
1144
1145
0
    while (write(syslog_fd, s, send_len) < 0 && errno == EMSGSIZE) {
1146
0
        send_len -= send_len / 20;
1147
0
        max_length = send_len;
1148
0
    }
1149
0
}
1150
1151
/* Writes 'message' to the log at the given 'level' and as coming from the
1152
 * given 'module'.
1153
 *
1154
 * Guaranteed to preserve errno. */
1155
void
1156
vlog_valist(const struct vlog_module *module, enum vlog_level level,
1157
            const char *message, va_list args)
1158
0
{
1159
0
    bool log_to_console = module->levels[VLF_CONSOLE] >= level;
1160
0
    bool log_to_syslog = module->levels[VLF_SYSLOG] >= level;
1161
0
    bool log_to_file = module->levels[VLF_FILE]  >= level;
1162
1163
0
    if (!(log_to_console || log_to_syslog || log_to_file)) {
1164
        /* fast path - all logging levels specify no logging, no
1165
         * need to hog the log mutex
1166
         */
1167
0
        return;
1168
0
    }
1169
1170
0
    ovs_mutex_lock(&log_file_mutex);
1171
0
    log_to_file &= (log_fd >= 0);
1172
0
    ovs_mutex_unlock(&log_file_mutex);
1173
0
    if (log_to_console || log_to_syslog || log_to_file) {
1174
0
        int save_errno = errno;
1175
0
        struct ds s;
1176
1177
0
        vlog_init();
1178
1179
0
        ds_init(&s);
1180
0
        ds_reserve(&s, 1024);
1181
0
        ++*msg_num_get();
1182
1183
0
        ovs_rwlock_rdlock(&pattern_rwlock);
1184
0
        if (log_to_console) {
1185
0
            format_log_message(module, level,
1186
0
                               destinations[VLF_CONSOLE].pattern, message,
1187
0
                               args, &s);
1188
0
            ds_put_char(&s, '\n');
1189
0
            fputs(ds_cstr(&s), stderr);
1190
0
        }
1191
1192
0
        if (log_to_syslog) {
1193
0
            int syslog_level = syslog_levels[level];
1194
0
            char *save_ptr = NULL;
1195
0
            char *line;
1196
0
            int facility;
1197
1198
0
            format_log_message(module, level, destinations[VLF_SYSLOG].pattern,
1199
0
                               message, args, &s);
1200
0
            for (line = strtok_r(s.string, "\n", &save_ptr); line;
1201
0
                 line = strtok_r(NULL, "\n", &save_ptr)) {
1202
0
                atomic_read_explicit(&log_facility, &facility,
1203
0
                                     memory_order_relaxed);
1204
0
                syslogger->class->syslog(syslogger, syslog_level|facility, line);
1205
0
            }
1206
1207
0
            if (syslog_fd >= 0) {
1208
0
                format_log_message(module, level,
1209
0
                                   "<%B>1 %D{%Y-%m-%dT%H:%M:%S.###Z} "
1210
0
                                   "%E %A %P %c - \xef\xbb\xbf%m",
1211
0
                                   message, args, &s);
1212
0
                send_to_syslog_fd(ds_cstr(&s), s.length);
1213
0
            }
1214
0
        }
1215
1216
0
        if (log_to_file) {
1217
0
            format_log_message(module, level, destinations[VLF_FILE].pattern,
1218
0
                               message, args, &s);
1219
0
            ds_put_char(&s, '\n');
1220
1221
0
            ovs_mutex_lock(&log_file_mutex);
1222
0
            if (log_fd >= 0) {
1223
0
                if (log_writer) {
1224
0
                    async_append_write(log_writer, s.string, s.length);
1225
0
                    if (level == VLL_EMER) {
1226
0
                        async_append_flush(log_writer);
1227
0
                    }
1228
0
                } else {
1229
0
                    ovs_ignore(write(log_fd, s.string, s.length));
1230
0
                }
1231
0
            }
1232
0
            ovs_mutex_unlock(&log_file_mutex);
1233
0
        }
1234
0
        ovs_rwlock_unlock(&pattern_rwlock);
1235
1236
0
        ds_destroy(&s);
1237
0
        errno = save_errno;
1238
0
    }
1239
0
}
1240
1241
void
1242
vlog(const struct vlog_module *module, enum vlog_level level,
1243
     const char *message, ...)
1244
0
{
1245
0
    va_list args;
1246
1247
0
    va_start(args, message);
1248
0
    vlog_valist(module, level, message, args);
1249
0
    va_end(args);
1250
0
}
1251
1252
/* Logs 'message' to 'module' at maximum verbosity, then exits with a failure
1253
 * exit code.  Always writes the message to stderr, even if the console
1254
 * destination is disabled.
1255
 *
1256
 * Choose this function instead of vlog_abort_valist() if the daemon monitoring
1257
 * facility shouldn't automatically restart the current daemon.  */
1258
void
1259
vlog_fatal_valist(const struct vlog_module *module_,
1260
                  const char *message, va_list args)
1261
0
{
1262
0
    struct vlog_module *module = CONST_CAST(struct vlog_module *, module_);
1263
1264
    /* Don't log this message to the console to avoid redundancy with the
1265
     * message written by the later ovs_fatal_valist(). */
1266
0
    module->levels[VLF_CONSOLE] = VLL_OFF;
1267
1268
0
    vlog_valist(module, VLL_EMER, message, args);
1269
0
    ovs_fatal_valist(0, message, args);
1270
0
}
1271
1272
/* Logs 'message' to 'module' at maximum verbosity, then exits with a failure
1273
 * exit code.  Always writes the message to stderr, even if the console
1274
 * destination is disabled.
1275
 *
1276
 * Choose this function instead of vlog_abort() if the daemon monitoring
1277
 * facility shouldn't automatically restart the current daemon.  */
1278
void
1279
vlog_fatal(const struct vlog_module *module, const char *message, ...)
1280
0
{
1281
0
    va_list args;
1282
1283
0
    va_start(args, message);
1284
0
    vlog_fatal_valist(module, message, args);
1285
0
    va_end(args);
1286
0
}
1287
1288
/* Attempts to log a stack trace, logs 'message' to 'module' at maximum
1289
 * verbosity, then calls abort().  Always writes the message to stderr, even
1290
 * if the console destination is disabled.
1291
 *
1292
 * Choose this function instead of vlog_fatal_valist() if the daemon monitoring
1293
 * facility should automatically restart the current daemon.  */
1294
void
1295
vlog_abort_valist(const struct vlog_module *module_,
1296
                  const char *message, va_list args)
1297
0
{
1298
0
    struct vlog_module *module = (struct vlog_module *) module_;
1299
1300
    /* Don't log this message to the console to avoid redundancy with the
1301
     * message written by the later ovs_abort_valist(). */
1302
0
    module->levels[VLF_CONSOLE] = VLL_OFF;
1303
1304
    /* Printing the stack trace before the 'message', because the 'message'
1305
     * will flush the async log queue (VLL_EMER).  With a different order we
1306
     * would need to flush the queue manually again. */
1307
0
    log_backtrace();
1308
0
    vlog_valist(module, VLL_EMER, message, args);
1309
0
    ovs_abort_valist(0, message, args);
1310
0
}
1311
1312
/* Logs 'message' to 'module' at maximum verbosity, then calls abort().  Always
1313
 * writes the message to stderr, even if the console destination is disabled.
1314
 *
1315
 * Choose this function instead of vlog_fatal() if the daemon monitoring
1316
 * facility should automatically restart the current daemon.  */
1317
void
1318
vlog_abort(const struct vlog_module *module, const char *message, ...)
1319
0
{
1320
0
    va_list args;
1321
1322
0
    va_start(args, message);
1323
0
    vlog_abort_valist(module, message, args);
1324
0
    va_end(args);
1325
0
}
1326
1327
bool
1328
vlog_should_drop(const struct vlog_module *module, enum vlog_level level,
1329
                 struct vlog_rate_limit *rl)
1330
0
{
1331
0
    if (!module->honor_rate_limits) {
1332
0
        return false;
1333
0
    }
1334
1335
0
    if (!vlog_is_enabled(module, level)) {
1336
0
        return true;
1337
0
    }
1338
1339
0
    ovs_mutex_lock(&rl->mutex);
1340
0
    if (!token_bucket_withdraw(&rl->token_bucket, VLOG_MSG_TOKENS)) {
1341
0
        time_t now = time_now();
1342
0
        if (!rl->n_dropped) {
1343
0
            rl->first_dropped = now;
1344
0
        }
1345
0
        rl->last_dropped = now;
1346
0
        rl->n_dropped++;
1347
0
        ovs_mutex_unlock(&rl->mutex);
1348
0
        return true;
1349
0
    }
1350
1351
0
    if (!rl->n_dropped) {
1352
0
        ovs_mutex_unlock(&rl->mutex);
1353
0
    } else {
1354
0
        time_t now = time_now();
1355
0
        unsigned int n_dropped = rl->n_dropped;
1356
0
        unsigned int first_dropped_elapsed = now - rl->first_dropped;
1357
0
        unsigned int last_dropped_elapsed = now - rl->last_dropped;
1358
0
        rl->n_dropped = 0;
1359
0
        ovs_mutex_unlock(&rl->mutex);
1360
1361
0
        vlog(module, level,
1362
0
             "Dropped %u log messages in last %u seconds (most recently, "
1363
0
             "%u seconds ago) due to excessive rate",
1364
0
             n_dropped, first_dropped_elapsed, last_dropped_elapsed);
1365
0
    }
1366
1367
0
    return false;
1368
0
}
1369
1370
void
1371
vlog_rate_limit(const struct vlog_module *module, enum vlog_level level,
1372
                struct vlog_rate_limit *rl, const char *message, ...)
1373
0
{
1374
0
    if (!vlog_should_drop(module, level, rl)) {
1375
0
        va_list args;
1376
1377
0
        va_start(args, message);
1378
0
        vlog_valist(module, level, message, args);
1379
0
        va_end(args);
1380
0
    }
1381
0
}
1382
1383
void
1384
vlog_usage(void)
1385
0
{
1386
0
    printf("\n\
1387
0
Logging options:\n\
1388
0
  -vSPEC, --verbose=SPEC   set logging levels\n\
1389
0
  -v, --verbose            set maximum verbosity level\n\
1390
0
  --log-file[=FILE]        enable logging to specified FILE\n\
1391
0
                           (default: %s/%s.log)\n\
1392
0
  --syslog-method=(libc|unix:file|udp:ip:port)\n\
1393
0
                           specify how to send messages to syslog daemon\n\
1394
0
  --syslog-target=HOST:PORT  also send syslog msgs to HOST:PORT via UDP\n",
1395
0
           ovs_logdir(), program_name);
1396
0
}