2 * Copyright (c) 2015-19 David Lamparter, for NetDEF, Inc.
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24 #include "frr_pthread.h"
27 #include "zlog_targets.h"
29 /* these allocations are intentionally left active even when doing full exit
30 * cleanup, in order to keep the logging subsystem fully functional until the
34 DEFINE_MGROUP_ACTIVEATEXIT(LOG
, "logging subsystem");
36 DEFINE_MTYPE_STATIC(LOG
, LOG_FD
, "log file target");
37 DEFINE_MTYPE_STATIC(LOG
, LOG_FD_NAME
, "log file name");
38 DEFINE_MTYPE_STATIC(LOG
, LOG_FD_ROTATE
, "log file rotate helper");
39 DEFINE_MTYPE_STATIC(LOG
, LOG_SYSL
, "syslog target");
42 struct zlog_target zt
;
44 atomic_uint_fast32_t fd
;
49 struct rcu_head_close head_close
;
52 static const char * const prionames
[] = {
53 [LOG_EMERG
] = "emergencies: ",
54 [LOG_ALERT
] = "alerts: ",
55 [LOG_CRIT
] = "critical: ",
56 [LOG_ERR
] = "errors: ",
57 [LOG_WARNING
] = "warnings: ",
58 [LOG_NOTICE
] = "notifications: ",
59 [LOG_INFO
] = "informational: ",
60 [LOG_DEBUG
] = "debugging: ",
63 void zlog_fd(struct zlog_target
*zt
, struct zlog_msg
*msgs
[], size_t nmsgs
)
65 struct zlt_fd
*zte
= container_of(zt
, struct zlt_fd
, zt
);
67 size_t i
, textlen
, iovpos
= 0;
68 size_t niov
= MIN(4 * nmsgs
+ 1, IOV_MAX
);
69 struct iovec iov
[niov
];
70 /* "\nYYYY-MM-DD HH:MM:SS.NNNNNNNNN+ZZ:ZZ " = 37 chars */
72 char ts_buf
[TS_LEN
* nmsgs
], *ts_pos
= ts_buf
;
74 fd
= atomic_load_explicit(&zte
->fd
, memory_order_relaxed
);
76 for (i
= 0; i
< nmsgs
; i
++) {
77 struct zlog_msg
*msg
= msgs
[i
];
78 int prio
= zlog_msg_prio(msg
);
80 if (prio
<= zt
->prio_min
) {
84 .len
= sizeof(ts_buf
),
87 iov
[iovpos
].iov_base
= ts_pos
;
88 zlog_msg_ts(msg
, &fbuf
,
89 ZLOG_TS_LEGACY
| zte
->ts_subsec
);
94 ts_pos
- (char *)iov
[iovpos
].iov_base
;
98 if (zte
->record_priority
) {
99 iov
[iovpos
].iov_base
= (char *)prionames
[prio
];
100 iov
[iovpos
].iov_len
=
101 strlen(iov
[iovpos
].iov_base
);
106 iov
[iovpos
].iov_base
= zlog_prefix
;
107 iov
[iovpos
].iov_len
= zlog_prefixsz
;
111 iov
[iovpos
].iov_base
=
112 (char *)zlog_msg_text(msg
, &textlen
);
113 iov
[iovpos
].iov_len
= textlen
+ 1;
118 /* conditions that trigger writing:
119 * - out of space for more timestamps/headers
120 * - this being the last message in the batch
121 * - not enough remaining iov entries
123 if (iovpos
> 0 && (ts_buf
+ sizeof(ts_buf
) - ts_pos
< TS_LEN
125 || array_size(iov
) - iovpos
< 5)) {
126 writev(fd
, iov
, iovpos
);
136 static void zlog_fd_sigsafe(struct zlog_target
*zt
, const char *text
,
139 struct zlt_fd
*zte
= container_of(zt
, struct zlt_fd
, zt
);
143 iov
[0].iov_base
= (char *)prionames
[LOG_CRIT
];
144 iov
[0].iov_len
= zte
->record_priority
? strlen(iov
[0].iov_base
) : 0;
146 iov
[1].iov_base
= zlog_prefix
;
147 iov
[1].iov_len
= zlog_prefixsz
;
149 iov
[2].iov_base
= (char *)text
;
150 iov
[2].iov_len
= len
;
152 iov
[3].iov_base
= (char *)"\n";
155 fd
= atomic_load_explicit(&zte
->fd
, memory_order_relaxed
);
157 writev(fd
, iov
, array_size(iov
));
164 void zlog_file_init(struct zlog_cfg_file
*zcf
)
166 memset(zcf
, 0, sizeof(*zcf
));
167 zcf
->prio_min
= ZLOG_DISABLED
;
169 pthread_mutex_init(&zcf
->cfg_mtx
, NULL
);
172 static void zlog_file_target_free(struct zlt_fd
*zlt
)
177 rcu_close(&zlt
->head_close
, zlt
->fd
);
178 rcu_free(MTYPE_LOG_FD
, zlt
, zt
.rcu_head
);
181 void zlog_file_fini(struct zlog_cfg_file
*zcf
)
185 struct zlog_target
*zt
;
187 zt
= zlog_target_replace(&zcf
->active
->zt
, NULL
);
188 ztf
= container_of(zt
, struct zlt_fd
, zt
);
189 zlog_file_target_free(ztf
);
191 XFREE(MTYPE_LOG_FD_NAME
, zcf
->filename
);
192 pthread_mutex_destroy(&zcf
->cfg_mtx
);
195 static bool zlog_file_cycle(struct zlog_cfg_file
*zcf
)
197 struct zlog_target
*zt
, *old
;
198 struct zlt_fd
*zlt
= NULL
;
203 if (zcf
->prio_min
== ZLOG_DISABLED
)
208 else if (zcf
->filename
)
209 fd
= open(zcf
->filename
,
210 O_WRONLY
| O_APPEND
| O_CREAT
| O_CLOEXEC
221 zt
= zlog_target_clone(MTYPE_LOG_FD
, &zcf
->active
->zt
,
223 zlt
= container_of(zt
, struct zlt_fd
, zt
);
226 zlt
->record_priority
= zcf
->record_priority
;
227 zlt
->ts_subsec
= zcf
->ts_subsec
;
229 zlt
->zt
.prio_min
= zcf
->prio_min
;
230 zlt
->zt
.logfn
= zcf
->zlog_wrap
? zcf
->zlog_wrap
: zlog_fd
;
231 zlt
->zt
.logfn_sigsafe
= zlog_fd_sigsafe
;
234 old
= zlog_target_replace(zcf
->active
? &zcf
->active
->zt
: NULL
,
235 zlt
? &zlt
->zt
: NULL
);
238 zlog_file_target_free(container_of_null(old
, struct zlt_fd
, zt
));
243 void zlog_file_set_other(struct zlog_cfg_file
*zcf
)
245 frr_with_mutex (&zcf
->cfg_mtx
) {
246 zlog_file_cycle(zcf
);
250 bool zlog_file_set_filename(struct zlog_cfg_file
*zcf
, const char *filename
)
252 frr_with_mutex (&zcf
->cfg_mtx
) {
253 XFREE(MTYPE_LOG_FD_NAME
, zcf
->filename
);
254 zcf
->filename
= XSTRDUP(MTYPE_LOG_FD_NAME
, filename
);
257 return zlog_file_cycle(zcf
);
263 bool zlog_file_set_fd(struct zlog_cfg_file
*zcf
, int fd
)
265 frr_with_mutex (&zcf
->cfg_mtx
) {
269 XFREE(MTYPE_LOG_FD_NAME
, zcf
->filename
);
272 return zlog_file_cycle(zcf
);
278 struct rcu_close_rotate
{
279 struct rcu_head_close head_close
;
280 struct rcu_head head_self
;
283 bool zlog_file_rotate(struct zlog_cfg_file
*zcf
)
285 struct rcu_close_rotate
*rcr
;
288 frr_with_mutex (&zcf
->cfg_mtx
) {
289 if (!zcf
->active
|| !zcf
->filename
)
292 fd
= open(zcf
->filename
,
293 O_WRONLY
| O_APPEND
| O_CREAT
| O_CLOEXEC
| O_NOCTTY
,
298 fd
= atomic_exchange_explicit(&zcf
->active
->fd
,
300 memory_order_relaxed
);
303 rcr
= XCALLOC(MTYPE_LOG_FD_ROTATE
, sizeof(*rcr
));
304 rcu_close(&rcr
->head_close
, fd
);
305 rcu_free(MTYPE_LOG_FD_ROTATE
, rcr
, head_self
);
310 /* fixed crash logging */
312 static struct zlt_fd zlog_crashlog
;
314 static void zlog_crashlog_sigsafe(struct zlog_target
*zt
, const char *text
,
317 static int crashlog_fd
= -1;
319 if (crashlog_fd
== -1) {
321 crashlog_fd
= openat(zlog_tmpdirfd
, "crashlog",
322 O_WRONLY
| O_APPEND
| O_CREAT
,
329 if (crashlog_fd
== -2)
332 zlog_crashlog
.fd
= crashlog_fd
;
333 zlog_fd_sigsafe(&zlog_crashlog
.zt
, text
, len
);
336 /* this is used for assert failures (they don't need AS-Safe logging) */
337 static void zlog_crashlog_plain(struct zlog_target
*zt
, struct zlog_msg
*msgs
[],
343 for (i
= 0; i
< nmsgs
; i
++) {
344 if (zlog_msg_prio(msgs
[i
]) > zt
->prio_min
)
347 text
= zlog_msg_text(msgs
[i
], &len
);
348 zlog_crashlog_sigsafe(zt
, text
, len
);
352 static void zlog_crashlog_init(void)
354 zlog_crashlog
.zt
.prio_min
= LOG_CRIT
;
355 zlog_crashlog
.zt
.logfn
= zlog_crashlog_plain
;
356 zlog_crashlog
.zt
.logfn_sigsafe
= zlog_crashlog_sigsafe
;
357 zlog_crashlog
.fd
= -1;
359 zlog_target_replace(NULL
, &zlog_crashlog
.zt
);
362 /* fixed logging for test/auxiliary programs */
364 static struct zlt_fd zlog_aux_stdout
;
365 static bool zlog_is_aux
;
367 static int zlt_aux_init(const char *prefix
, int prio_min
)
371 zlog_aux_stdout
.zt
.prio_min
= prio_min
;
372 zlog_aux_stdout
.zt
.logfn
= zlog_fd
;
373 zlog_aux_stdout
.zt
.logfn_sigsafe
= zlog_fd_sigsafe
;
374 zlog_aux_stdout
.fd
= STDOUT_FILENO
;
376 zlog_target_replace(NULL
, &zlog_aux_stdout
.zt
);
381 static int zlt_init(const char *progname
, const char *protoname
,
382 unsigned short instance
, uid_t uid
, gid_t gid
)
384 openlog(progname
, LOG_CONS
| LOG_NDELAY
| LOG_PID
, LOG_DAEMON
);
388 static int zlt_fini(void)
394 /* fixed startup logging to stderr */
396 static struct zlt_fd zlog_startup_stderr
;
398 __attribute__((_CONSTRUCTOR(450))) static void zlog_startup_init(void)
400 zlog_startup_stderr
.zt
.prio_min
= LOG_WARNING
;
401 zlog_startup_stderr
.zt
.logfn
= zlog_fd
;
402 zlog_startup_stderr
.zt
.logfn_sigsafe
= zlog_fd_sigsafe
;
403 zlog_startup_stderr
.fd
= STDERR_FILENO
;
405 zlog_target_replace(NULL
, &zlog_startup_stderr
.zt
);
407 hook_register(zlog_aux_init
, zlt_aux_init
);
408 hook_register(zlog_init
, zlt_init
);
409 hook_register(zlog_fini
, zlt_fini
);
412 void zlog_startup_end(void)
414 static bool startup_ended
= false;
418 startup_ended
= true;
420 zlog_target_replace(&zlog_startup_stderr
.zt
, NULL
);
425 /* until here, crashlogs go to stderr */
426 zlog_crashlog_init();
432 struct zlog_target zt
;
437 static void zlog_syslog(struct zlog_target
*zt
, struct zlog_msg
*msgs
[],
441 struct zlt_syslog
*zte
= container_of(zt
, struct zlt_syslog
, zt
);
445 for (i
= 0; i
< nmsgs
; i
++) {
446 if (zlog_msg_prio(msgs
[i
]) > zt
->prio_min
)
449 text
= zlog_msg_text(msgs
[i
], &text_len
);
450 syslog(zlog_msg_prio(msgs
[i
]) | zte
->syslog_facility
, "%.*s",
451 (int)text_len
, text
);
456 #define _PATH_LOG "/dev/log"
459 static void zlog_syslog_sigsafe(struct zlog_target
*zt
, const char *text
,
462 static int syslog_fd
= -1;
468 if (syslog_fd
== -1) {
469 syslog_fd
= socket(AF_UNIX
, SOCK_DGRAM
, 0);
470 if (syslog_fd
>= 0) {
471 struct sockaddr_un sa
;
472 socklen_t salen
= sizeof(sa
);
474 sa
.sun_family
= AF_UNIX
;
475 strlcpy(sa
.sun_path
, _PATH_LOG
, sizeof(sa
.sun_path
));
476 #ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
477 salen
= sa
.sun_len
= SUN_LEN(&sa
);
479 if (connect(syslog_fd
, (struct sockaddr
*)&sa
, salen
)) {
485 /* /dev/log could be a fifo instead of a socket */
486 if (syslog_fd
== -1) {
487 syslog_fd
= open(_PATH_LOG
, O_WRONLY
| O_NOCTTY
);
497 /* note zlog_prefix includes trailing ": ", need to cut off 2 chars */
498 hdrlen
= snprintfrr(hdr
, sizeof(hdr
), "<%d>%.*s[%ld]: ", LOG_CRIT
,
499 zlog_prefixsz
> 2 ? (int)(zlog_prefixsz
- 2) : 0,
500 zlog_prefix
, (long)getpid());
502 iov
[0].iov_base
= hdr
;
503 iov
[0].iov_len
= hdrlen
;
505 iov
[1].iov_base
= (char *)text
;
506 iov
[1].iov_len
= len
;
508 writev(syslog_fd
, iov
, array_size(iov
));
512 static pthread_mutex_t syslog_cfg_mutex
= PTHREAD_MUTEX_INITIALIZER
;
513 static struct zlt_syslog
*zlt_syslog
;
514 static int syslog_facility
= LOG_DAEMON
;
515 static int syslog_prio_min
= ZLOG_DISABLED
;
517 void zlog_syslog_set_facility(int facility
)
519 struct zlog_target
*newztc
;
520 struct zlt_syslog
*newzt
;
522 frr_with_mutex (&syslog_cfg_mutex
) {
523 if (facility
== syslog_facility
)
525 syslog_facility
= facility
;
527 if (syslog_prio_min
== ZLOG_DISABLED
)
530 newztc
= zlog_target_clone(MTYPE_LOG_SYSL
, &zlt_syslog
->zt
,
532 newzt
= container_of(newztc
, struct zlt_syslog
, zt
);
533 newzt
->syslog_facility
= syslog_facility
;
535 zlog_target_free(MTYPE_LOG_SYSL
,
536 zlog_target_replace(&zlt_syslog
->zt
,
543 int zlog_syslog_get_facility(void)
545 frr_with_mutex (&syslog_cfg_mutex
) {
546 return syslog_facility
;
552 void zlog_syslog_set_prio_min(int prio_min
)
554 struct zlog_target
*newztc
;
555 struct zlt_syslog
*newzt
= NULL
;
557 frr_with_mutex (&syslog_cfg_mutex
) {
558 if (prio_min
== syslog_prio_min
)
560 syslog_prio_min
= prio_min
;
562 if (syslog_prio_min
!= ZLOG_DISABLED
) {
563 newztc
= zlog_target_clone(MTYPE_LOG_SYSL
,
566 newzt
= container_of(newztc
, struct zlt_syslog
, zt
);
567 newzt
->zt
.prio_min
= prio_min
;
568 newzt
->zt
.logfn
= zlog_syslog
;
569 newzt
->zt
.logfn_sigsafe
= zlog_syslog_sigsafe
;
570 newzt
->syslog_facility
= syslog_facility
;
573 zlog_target_free(MTYPE_LOG_SYSL
,
574 zlog_target_replace(&zlt_syslog
->zt
,
581 int zlog_syslog_get_prio_min(void)
583 frr_with_mutex (&syslog_cfg_mutex
) {
584 return syslog_prio_min
;