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