1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
|
// SPDX-License-Identifier: ISC
/*
* Copyright (c) 2015-19 David Lamparter, for NetDEF, Inc.
*/
#ifndef _FRR_ZLOG_H
#define _FRR_ZLOG_H
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <sys/uio.h>
#include <assert.h>
#include "atomlist.h"
#include "frrcu.h"
#include "memory.h"
#include "hook.h"
#include "printfrr.h"
#ifdef __cplusplus
extern "C" {
#endif
DECLARE_MGROUP(LOG);
extern char zlog_prefix[];
extern size_t zlog_prefixsz;
extern int zlog_tmpdirfd;
extern int zlog_instance;
extern const char *zlog_progname;
struct xref_logmsg {
struct xref xref;
const char *fmtstring;
uint32_t priority;
uint32_t ec;
const char *args;
};
/* whether flag was added in config mode or enable mode */
#define LOGMSG_FLAG_EPHEMERAL (1 << 0)
#define LOGMSG_FLAG_PERSISTENT (1 << 1)
struct xrefdata_logmsg {
struct xrefdata xrefdata;
uint8_t fl_print_bt;
};
/* These functions are set up to write to stdout/stderr without explicit
* initialization and/or before config load. There is no need to call e.g.
* fprintf(stderr, ...) just because it's "too early" at startup. Depending
* on context, it may still be the right thing to use fprintf though -- try to
* determine whether something is a log message or something else.
*/
extern void vzlogx(const struct xref_logmsg *xref, int prio, const char *fmt,
va_list ap) PRINTFRR(3, 0);
#define vzlog(prio, ...) vzlogx(NULL, prio, __VA_ARGS__)
PRINTFRR(2, 3)
static inline void zlog(int prio, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vzlog(prio, fmt, ap);
va_end(ap);
}
PRINTFRR(2, 3)
static inline void zlog_ref(const struct xref_logmsg *xref,
const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vzlogx(xref, xref->priority, fmt, ap);
va_end(ap);
}
#define _zlog_ecref(ec_, prio, msg, ...) \
do { \
static struct xrefdata_logmsg _xrefdata = { \
.xrefdata = \
{ \
.xref = NULL, \
.uid = {}, \
.hashstr = (msg), \
.hashu32 = {(prio), (ec_)}, \
}, \
}; \
static const struct xref_logmsg _xref __attribute__( \
(used)) = { \
.xref = XREF_INIT(XREFT_LOGMSG, &_xrefdata.xrefdata, \
__func__), \
.fmtstring = (msg), \
.priority = (prio), \
.ec = (ec_), \
.args = (#__VA_ARGS__), \
}; \
XREF_LINK(_xref.xref); \
zlog_ref(&_xref, (msg), ##__VA_ARGS__); \
} while (0)
#define zlog_err(...) _zlog_ecref(0, LOG_ERR, __VA_ARGS__)
#define zlog_warn(...) _zlog_ecref(0, LOG_WARNING, __VA_ARGS__)
#define zlog_info(...) _zlog_ecref(0, LOG_INFO, __VA_ARGS__)
#define zlog_notice(...) _zlog_ecref(0, LOG_NOTICE, __VA_ARGS__)
#define zlog_debug(...) _zlog_ecref(0, LOG_DEBUG, __VA_ARGS__)
#define flog_err(ferr_id, format, ...) \
_zlog_ecref(ferr_id, LOG_ERR, format, ## __VA_ARGS__)
#define flog_warn(ferr_id, format, ...) \
_zlog_ecref(ferr_id, LOG_WARNING, format, ## __VA_ARGS__)
#define flog_err_sys(ferr_id, format, ...) \
_zlog_ecref(ferr_id, LOG_ERR, format, ## __VA_ARGS__)
extern void zlog_sigsafe(const char *text, size_t len);
/* recirculate a log message from zlog_live */
extern void zlog_recirculate_live_msg(uint8_t *data, size_t len);
/* extra priority value to disable a target without deleting it */
#define ZLOG_DISABLED (LOG_EMERG-1)
/* zlog_msg encapsulates a particular logging call from somewhere in the code.
* The same struct is passed around to all zlog_targets.
*
* This is used to defer formatting the log message until it is actually
* requested by one of the targets. If none of the targets needs the message
* formatted, the formatting call is avoided entirely.
*
* This struct is opaque / private to the core zlog code. Logging targets
* should use zlog_msg_* functions to get text / timestamps / ... for a
* message.
*/
struct zlog_msg;
extern int zlog_msg_prio(struct zlog_msg *msg);
extern const struct xref_logmsg *zlog_msg_xref(struct zlog_msg *msg);
/* text is NOT \0 terminated; instead there is a \n after textlen since the
* logging targets would jump extra hoops otherwise for a single byte. (the
* \n is not included in textlen)
*
* calling this with NULL textlen is likely wrong.
* use "%.*s", (int)textlen, text when passing to printf-like functions
*/
extern const char *zlog_msg_text(struct zlog_msg *msg, size_t *textlen);
extern void zlog_msg_args(struct zlog_msg *msg, size_t *hdrlen,
size_t *n_argpos, const struct fmt_outpos **argpos);
/* timestamp formatting control flags */
/* sub-second digit count */
#define ZLOG_TS_PREC 0xfU
/* 8601: 0000-00-00T00:00:00Z (if used with ZLOG_TS_UTC)
* 0000-00-00T00:00:00+00:00 (otherwise)
* Legacy: 0000/00/00 00:00:00 (no TZ indicated!)
*/
#define ZLOG_TS_ISO8601 (1 << 8)
#define ZLOG_TS_LEGACY (1 << 9)
/* default is local time zone */
#define ZLOG_TS_UTC (1 << 10)
struct timespec;
extern size_t zlog_msg_ts(struct zlog_msg *msg, struct fbuf *out,
uint32_t flags);
extern void zlog_msg_tsraw(struct zlog_msg *msg, struct timespec *ts);
/* "mmm dd hh:mm:ss" for RFC3164 syslog. Only ZLOG_TS_UTC for flags. */
extern size_t zlog_msg_ts_3164(struct zlog_msg *msg, struct fbuf *out,
uint32_t flags);
/* currently just returns the current PID/TID since we never write another
* thread's messages
*/
extern void zlog_msg_pid(struct zlog_msg *msg, intmax_t *pid, intmax_t *tid);
/* This list & struct implements the actual logging targets. It is accessed
* lock-free from all threads, and thus MUST only be changed atomically, i.e.
* RCU.
*
* Since there's no atomic replace, the replacement action is an add followed
* by a delete. This means that during logging config changes, log messages
* may be duplicated in the log target that is being changed. The old entry
* being changed MUST also at the very least not crash or do other stupid
* things.
*
* This list and struct are NOT related to config. Logging config is kept
* separately, and results in creating appropriate zlog_target(s) to realize
* the config. Log targets may also be created from varying sources, e.g.
* command line options, or VTY commands ("log monitor").
*
* struct zlog_target is intended to be embedded into a larger structure that
* contains additional field for the specific logging target, e.g. an fd or
* additional options. It MUST be the first field in that larger struct.
*/
PREDECL_ATOMLIST(zlog_targets);
struct zlog_target {
struct zlog_targets_item head;
int prio_min;
void (*logfn)(struct zlog_target *zt, struct zlog_msg *msg[],
size_t nmsgs);
/* for crash handlers, set to NULL if log target can't write crash logs
* without possibly deadlocking (AS-Safe)
*
* text is not \0 terminated & split up into lines (e.g. no \n)
*/
void (*logfn_sigsafe)(struct zlog_target *zt, const char *text,
size_t len);
struct rcu_head rcu_head;
};
/* make a copy for RCUpdating. oldzt may be NULL to allocate a fresh one. */
extern struct zlog_target *zlog_target_clone(struct memtype *mt,
struct zlog_target *oldzt,
size_t size);
/* update the zlog_targets list; both oldzt and newzt may be NULL. You
* still need to zlog_target_free() the old target afterwards if it wasn't
* NULL.
*
* Returns oldzt so you can zlog_target_free(zlog_target_replace(old, new));
* (Some log targets may need extra cleanup inbetween, but remember the old
* target MUST remain functional until the end of the current RCU cycle.)
*/
extern struct zlog_target *zlog_target_replace(struct zlog_target *oldzt,
struct zlog_target *newzt);
/* Mostly for symmetry for zlog_target_clone(), just rcu_free() internally. */
#define zlog_target_free(mt, zt) \
rcu_free(mt, zt, rcu_head)
extern void zlog_init(const char *progname, const char *protoname,
unsigned short instance, uid_t uid, gid_t gid);
DECLARE_HOOK(zlog_init, (const char *progname, const char *protoname,
unsigned short instance, uid_t uid, gid_t gid),
(progname, protoname, instance, uid, gid));
extern void zlog_fini(void);
DECLARE_KOOH(zlog_fini, (), ());
extern void zlog_set_prefix_ec(bool enable);
extern bool zlog_get_prefix_ec(void);
extern void zlog_set_prefix_xid(bool enable);
extern bool zlog_get_prefix_xid(void);
/* for tools & test programs, i.e. anything not a daemon.
* (no cleanup needed at exit)
*/
extern void zlog_aux_init(const char *prefix, int prio_min);
DECLARE_HOOK(zlog_aux_init, (const char *prefix, int prio_min),
(prefix, prio_min));
extern void zlog_startup_end(void);
extern void zlog_tls_buffer_init(void);
extern void zlog_tls_buffer_flush(void);
extern void zlog_tls_buffer_fini(void);
/* Enable or disable 'immediate' output - default is to buffer messages. */
extern void zlog_set_immediate(bool set_p);
bool zlog_get_immediate_mode(void);
extern const char *zlog_priority_str(int priority);
/* Remove temp dirs at shutdown */
void zlog_tmpdir_fini(void);
#ifdef __cplusplus
}
#endif
#endif /* _FRR_ZLOG_H */
|