1 /*
2  * Copyright (c) 2015-19  David Lamparter, for NetDEF, Inc.
3  *
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.
7  *
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.
15  */
16 
17 #include "zebra.h"
18 
19 #include <sys/un.h>
20 #include <syslog.h>
21 
22 #include "memory.h"
23 #include "frrcu.h"
24 #include "frr_pthread.h"
25 #include "printfrr.h"
26 #include "zlog.h"
27 #include "zlog_targets.h"
28 
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
31  * absolute end.
32  */
33 
34 DECLARE_MGROUP(LOG)
35 DEFINE_MGROUP_ACTIVEATEXIT(LOG, "logging subsystem")
36 
37 DEFINE_MTYPE_STATIC(LOG, LOG_FD,        "log file target")
38 DEFINE_MTYPE_STATIC(LOG, LOG_FD_NAME,   "log file name")
39 DEFINE_MTYPE_STATIC(LOG, LOG_FD_ROTATE, "log file rotate helper")
40 DEFINE_MTYPE_STATIC(LOG, LOG_SYSL,      "syslog target")
41 
42 struct zlt_fd {
43 	struct zlog_target zt;
44 
45 	atomic_uint_fast32_t fd;
46 
47 	char ts_subsec;
48 	bool record_priority;
49 
50 	struct rcu_head_close head_close;
51 };
52 
53 static const char * const prionames[] = {
54 	[LOG_EMERG] =	"emergencies: ",
55 	[LOG_ALERT] =	"alerts: ",
56 	[LOG_CRIT] =	"critical: ",
57 	[LOG_ERR] =	"errors: ",
58 	[LOG_WARNING] =	"warnings: ",
59 	[LOG_NOTICE] =	"notifications: ",
60 	[LOG_INFO] =	"informational: ",
61 	[LOG_DEBUG] =	"debugging: ",
62 };
63 
zlog_fd(struct zlog_target * zt,struct zlog_msg * msgs[],size_t nmsgs)64 void zlog_fd(struct zlog_target *zt, struct zlog_msg *msgs[], size_t nmsgs)
65 {
66 	struct zlt_fd *zte = container_of(zt, struct zlt_fd, zt);
67 	int fd;
68 	size_t i, textlen, iovpos = 0;
69 	size_t niov = MIN(4 * nmsgs + 1, IOV_MAX);
70 	struct iovec iov[niov];
71 	/* "\nYYYY-MM-DD HH:MM:SS.NNNNNNNNN+ZZ:ZZ " = 37 chars */
72 #define TS_LEN 40
73 	char ts_buf[TS_LEN * nmsgs], *ts_pos = ts_buf;
74 
75 	fd = atomic_load_explicit(&zte->fd, memory_order_relaxed);
76 
77 	for (i = 0; i < nmsgs; i++) {
78 		struct zlog_msg *msg = msgs[i];
79 		int prio = zlog_msg_prio(msg);
80 
81 		if (prio > zt->prio_min)
82 			continue;
83 
84 		iov[iovpos].iov_base = ts_pos;
85 		if (iovpos > 0)
86 			*ts_pos++ = '\n';
87 		ts_pos += zlog_msg_ts(msg, ts_pos, sizeof(ts_buf) - 1
88 				      - (ts_pos - ts_buf),
89 				      ZLOG_TS_LEGACY | zte->ts_subsec);
90 		*ts_pos++ = ' ';
91 		iov[iovpos].iov_len = ts_pos - (char *)iov[iovpos].iov_base;
92 
93 		iovpos++;
94 
95 		if (zte->record_priority) {
96 			iov[iovpos].iov_base = (char *)prionames[prio];
97 			iov[iovpos].iov_len = strlen(iov[iovpos].iov_base);
98 
99 			iovpos++;
100 		}
101 
102 		iov[iovpos].iov_base = zlog_prefix;
103 		iov[iovpos].iov_len = zlog_prefixsz;
104 
105 		iovpos++;
106 
107 		iov[iovpos].iov_base = (char *)zlog_msg_text(msg, &textlen);
108 		iov[iovpos].iov_len = textlen;
109 
110 		iovpos++;
111 
112 		if (ts_buf + sizeof(ts_buf) - ts_pos < TS_LEN
113 		    || i + 1 == nmsgs
114 		    || array_size(iov) - iovpos < 5) {
115 			iov[iovpos].iov_base = (char *)"\n";
116 			iov[iovpos].iov_len = 1;
117 
118 			iovpos++;
119 
120 			writev(fd, iov, iovpos);
121 
122 			iovpos = 0;
123 			ts_pos = ts_buf;
124 		}
125 	}
126 
127 	assert(iovpos == 0);
128 }
129 
zlog_fd_sigsafe(struct zlog_target * zt,const char * text,size_t len)130 static void zlog_fd_sigsafe(struct zlog_target *zt, const char *text,
131 			    size_t len)
132 {
133 	struct zlt_fd *zte = container_of(zt, struct zlt_fd, zt);
134 	struct iovec iov[4];
135 	int fd;
136 
137 	iov[0].iov_base = (char *)prionames[LOG_CRIT];
138 	iov[0].iov_len = zte->record_priority ? strlen(iov[0].iov_base) : 0;
139 
140 	iov[1].iov_base = zlog_prefix;
141 	iov[1].iov_len = zlog_prefixsz;
142 
143 	iov[2].iov_base = (char *)text;
144 	iov[2].iov_len = len;
145 
146 	iov[3].iov_base = (char *)"\n";
147 	iov[3].iov_len = 1;
148 
149 	fd = atomic_load_explicit(&zte->fd, memory_order_relaxed);
150 
151 	writev(fd, iov, array_size(iov));
152 }
153 
154 /*
155  * (re-)configuration
156  */
157 
zlog_file_init(struct zlog_cfg_file * zcf)158 void zlog_file_init(struct zlog_cfg_file *zcf)
159 {
160 	memset(zcf, 0, sizeof(*zcf));
161 	zcf->prio_min = ZLOG_DISABLED;
162 	zcf->fd = -1;
163 	pthread_mutex_init(&zcf->cfg_mtx, NULL);
164 }
165 
zlog_file_target_free(struct zlt_fd * zlt)166 static void zlog_file_target_free(struct zlt_fd *zlt)
167 {
168 	if (!zlt)
169 		return;
170 
171 	rcu_close(&zlt->head_close, zlt->fd);
172 	rcu_free(MTYPE_LOG_FD, zlt, zt.rcu_head);
173 }
174 
zlog_file_fini(struct zlog_cfg_file * zcf)175 void zlog_file_fini(struct zlog_cfg_file *zcf)
176 {
177 	if (zcf->active) {
178 		struct zlt_fd *ztf;
179 		struct zlog_target *zt;
180 
181 		zt = zlog_target_replace(&zcf->active->zt, NULL);
182 		ztf = container_of(zt, struct zlt_fd, zt);
183 		zlog_file_target_free(ztf);
184 	}
185 	XFREE(MTYPE_LOG_FD_NAME, zcf->filename);
186 	pthread_mutex_destroy(&zcf->cfg_mtx);
187 }
188 
zlog_file_cycle(struct zlog_cfg_file * zcf)189 static bool zlog_file_cycle(struct zlog_cfg_file *zcf)
190 {
191 	struct zlog_target *zt, *old;
192 	struct zlt_fd *zlt = NULL;
193 	int fd;
194 	bool rv = true;
195 
196 	do {
197 		if (zcf->prio_min == ZLOG_DISABLED)
198 			break;
199 
200 		if (zcf->fd != -1)
201 			fd = dup(zcf->fd);
202 		else if (zcf->filename)
203 			fd = open(zcf->filename,
204 				  O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC
205 					| O_NOCTTY,
206 				  LOGFILE_MASK);
207 		else
208 			fd = -1;
209 
210 		if (fd < 0) {
211 			rv = false;
212 			break;
213 		}
214 
215 		zt = zlog_target_clone(MTYPE_LOG_FD, &zcf->active->zt,
216 				       sizeof(*zlt));
217 		zlt = container_of(zt, struct zlt_fd, zt);
218 
219 		zlt->fd = fd;
220 		zlt->record_priority = zcf->record_priority;
221 		zlt->ts_subsec = zcf->ts_subsec;
222 
223 		zlt->zt.prio_min = zcf->prio_min;
224 		zlt->zt.logfn = zcf->zlog_wrap ? zcf->zlog_wrap : zlog_fd;
225 		zlt->zt.logfn_sigsafe = zlog_fd_sigsafe;
226 	} while (0);
227 
228 	old = zlog_target_replace(zcf->active ? &zcf->active->zt : NULL,
229 				  zlt ? &zlt->zt : NULL);
230 	zcf->active = zlt;
231 
232 	zlog_file_target_free(container_of_null(old, struct zlt_fd, zt));
233 
234 	return rv;
235 }
236 
zlog_file_set_other(struct zlog_cfg_file * zcf)237 void zlog_file_set_other(struct zlog_cfg_file *zcf)
238 {
239 	frr_with_mutex(&zcf->cfg_mtx) {
240 		zlog_file_cycle(zcf);
241 	}
242 }
243 
zlog_file_set_filename(struct zlog_cfg_file * zcf,const char * filename)244 bool zlog_file_set_filename(struct zlog_cfg_file *zcf, const char *filename)
245 {
246 	frr_with_mutex(&zcf->cfg_mtx) {
247 		XFREE(MTYPE_LOG_FD_NAME, zcf->filename);
248 		zcf->filename = XSTRDUP(MTYPE_LOG_FD_NAME, filename);
249 		zcf->fd = -1;
250 
251 		return zlog_file_cycle(zcf);
252 	}
253 	assert(0);
254 }
255 
zlog_file_set_fd(struct zlog_cfg_file * zcf,int fd)256 bool zlog_file_set_fd(struct zlog_cfg_file *zcf, int fd)
257 {
258 	frr_with_mutex(&zcf->cfg_mtx) {
259 		if (zcf->fd == fd)
260 			return true;
261 
262 		XFREE(MTYPE_LOG_FD_NAME, zcf->filename);
263 		zcf->fd = fd;
264 
265 		return zlog_file_cycle(zcf);
266 	}
267 	assert(0);
268 }
269 
270 struct rcu_close_rotate {
271 	struct rcu_head_close head_close;
272 	struct rcu_head head_self;
273 };
274 
zlog_file_rotate(struct zlog_cfg_file * zcf)275 bool zlog_file_rotate(struct zlog_cfg_file *zcf)
276 {
277 	struct rcu_close_rotate *rcr;
278 	int fd;
279 
280 	frr_with_mutex(&zcf->cfg_mtx) {
281 		if (!zcf->active || !zcf->filename)
282 			return true;
283 
284 		fd = open(zcf->filename,
285 			  O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC | O_NOCTTY,
286 			  LOGFILE_MASK);
287 		if (fd < 0)
288 			return false;
289 
290 		fd = atomic_exchange_explicit(&zcf->active->fd,
291 					      (uint_fast32_t)fd,
292 					      memory_order_relaxed);
293 	}
294 
295 	rcr = XCALLOC(MTYPE_LOG_FD_ROTATE, sizeof(*rcr));
296 	rcu_close(&rcr->head_close, fd);
297 	rcu_free(MTYPE_LOG_FD_ROTATE, rcr, head_self);
298 
299 	return true;
300 }
301 
302 /* fixed crash logging */
303 
304 static struct zlt_fd zlog_crashlog;
305 
zlog_crashlog_sigsafe(struct zlog_target * zt,const char * text,size_t len)306 static void zlog_crashlog_sigsafe(struct zlog_target *zt, const char *text,
307 				  size_t len)
308 {
309 	static int crashlog_fd = -1;
310 
311 	if (crashlog_fd == -1) {
312 #ifdef HAVE_OPENAT
313 		crashlog_fd = openat(zlog_tmpdirfd, "crashlog",
314 				     O_WRONLY | O_APPEND | O_CREAT,
315 				     LOGFILE_MASK);
316 #endif
317 		if (crashlog_fd < 0)
318 			crashlog_fd = -2;
319 	}
320 
321 	if (crashlog_fd == -2)
322 		return;
323 
324 	zlog_crashlog.fd = crashlog_fd;
325 	zlog_fd_sigsafe(&zlog_crashlog.zt, text, len);
326 }
327 
328 /* this is used for assert failures (they don't need AS-Safe logging) */
zlog_crashlog_plain(struct zlog_target * zt,struct zlog_msg * msgs[],size_t nmsgs)329 static void zlog_crashlog_plain(struct zlog_target *zt, struct zlog_msg *msgs[],
330 				size_t nmsgs)
331 {
332 	size_t i, len;
333 	const char *text;
334 
335 	for (i = 0; i < nmsgs; i++) {
336 		if (zlog_msg_prio(msgs[i]) > zt->prio_min)
337 			continue;
338 
339 		text = zlog_msg_text(msgs[i], &len);
340 		zlog_crashlog_sigsafe(zt, text, len);
341 	}
342 }
343 
zlog_crashlog_init(void)344 static void zlog_crashlog_init(void)
345 {
346 	zlog_crashlog.zt.prio_min = LOG_CRIT;
347 	zlog_crashlog.zt.logfn = zlog_crashlog_plain;
348 	zlog_crashlog.zt.logfn_sigsafe = zlog_crashlog_sigsafe;
349 	zlog_crashlog.fd = -1;
350 
351 	zlog_target_replace(NULL, &zlog_crashlog.zt);
352 }
353 
354 /* fixed logging for test/auxiliary programs */
355 
356 static struct zlt_fd zlog_aux_stdout;
357 static bool zlog_is_aux;
358 
zlt_aux_init(const char * prefix,int prio_min)359 static int zlt_aux_init(const char *prefix, int prio_min)
360 {
361 	zlog_is_aux = true;
362 
363 	zlog_aux_stdout.zt.prio_min = prio_min;
364 	zlog_aux_stdout.zt.logfn = zlog_fd;
365 	zlog_aux_stdout.zt.logfn_sigsafe = zlog_fd_sigsafe;
366 	zlog_aux_stdout.fd = STDOUT_FILENO;
367 
368 	zlog_target_replace(NULL, &zlog_aux_stdout.zt);
369 	zlog_startup_end();
370 	return 0;
371 }
372 
zlt_init(const char * progname,const char * protoname,unsigned short instance,uid_t uid,gid_t gid)373 static int zlt_init(const char *progname, const char *protoname,
374 		     unsigned short instance, uid_t uid, gid_t gid)
375 {
376 	openlog(progname, LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON);
377 	return 0;
378 }
379 
zlt_fini(void)380 static int zlt_fini(void)
381 {
382 	closelog();
383 	return 0;
384 }
385 
386 /* fixed startup logging to stderr */
387 
388 static struct zlt_fd zlog_startup_stderr;
389 
zlog_startup_init(void)390 __attribute__((_CONSTRUCTOR(450))) static void zlog_startup_init(void)
391 {
392 	zlog_startup_stderr.zt.prio_min = LOG_WARNING;
393 	zlog_startup_stderr.zt.logfn = zlog_fd;
394 	zlog_startup_stderr.zt.logfn_sigsafe = zlog_fd_sigsafe;
395 	zlog_startup_stderr.fd = STDERR_FILENO;
396 
397 	zlog_target_replace(NULL, &zlog_startup_stderr.zt);
398 
399 	hook_register(zlog_aux_init, zlt_aux_init);
400 	hook_register(zlog_init, zlt_init);
401 	hook_register(zlog_fini, zlt_fini);
402 }
403 
zlog_startup_end(void)404 void zlog_startup_end(void)
405 {
406 	static bool startup_ended = false;
407 
408 	if (startup_ended)
409 		return;
410 	startup_ended = true;
411 
412 	zlog_target_replace(&zlog_startup_stderr.zt, NULL);
413 
414 	if (zlog_is_aux)
415 		return;
416 
417 	/* until here, crashlogs go to stderr */
418 	zlog_crashlog_init();
419 }
420 
421 /* syslog */
422 
423 struct zlt_syslog {
424 	struct zlog_target zt;
425 
426 	int syslog_facility;
427 };
428 
zlog_syslog(struct zlog_target * zt,struct zlog_msg * msgs[],size_t nmsgs)429 static void zlog_syslog(struct zlog_target *zt, struct zlog_msg *msgs[],
430 			size_t nmsgs)
431 {
432 	size_t i;
433 	struct zlt_syslog *zte = container_of(zt, struct zlt_syslog, zt);
434 
435 	for (i = 0; i < nmsgs; i++) {
436 		if (zlog_msg_prio(msgs[i]) > zt->prio_min)
437 			continue;
438 
439 		syslog(zlog_msg_prio(msgs[i]) | zte->syslog_facility, "%s",
440 		       zlog_msg_text(msgs[i], NULL));
441 	}
442 }
443 
444 #ifndef _PATH_LOG
445 #define _PATH_LOG "/dev/log"
446 #endif
447 
zlog_syslog_sigsafe(struct zlog_target * zt,const char * text,size_t len)448 static void zlog_syslog_sigsafe(struct zlog_target *zt, const char *text,
449 				size_t len)
450 {
451 	static int syslog_fd = -1;
452 
453 	char hdr[192];
454 	size_t hdrlen;
455 	struct iovec iov[2];
456 
457 	if (syslog_fd == -1) {
458 		syslog_fd = socket(AF_UNIX, SOCK_DGRAM, 0);
459 		if (syslog_fd >= 0) {
460 			struct sockaddr_un sa;
461 			socklen_t salen = sizeof(sa);
462 
463 			sa.sun_family = AF_UNIX;
464 			strlcpy(sa.sun_path, _PATH_LOG, sizeof(sa.sun_path));
465 #ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
466 			salen = sa.sun_len = SUN_LEN(&sa);
467 #endif
468 			if (connect(syslog_fd, (struct sockaddr *)&sa, salen)) {
469 				close(syslog_fd);
470 				syslog_fd = -1;
471 			}
472 		}
473 
474 		/* /dev/log could be a fifo instead of a socket */
475 		if (syslog_fd == -1) {
476 			syslog_fd = open(_PATH_LOG, O_WRONLY | O_NOCTTY);
477 			if (syslog_fd < 0)
478 				/* give up ... */
479 				syslog_fd = -2;
480 		}
481 	}
482 
483 	if (syslog_fd == -2)
484 		return;
485 
486 	/* note zlog_prefix includes trailing ": ", need to cut off 2 chars */
487 	hdrlen = snprintfrr(hdr, sizeof(hdr), "<%d>%.*s[%ld]: ", LOG_CRIT,
488 			    zlog_prefixsz > 2 ? (int)(zlog_prefixsz - 2) : 0,
489 			    zlog_prefix, (long)getpid());
490 
491 	iov[0].iov_base = hdr;
492 	iov[0].iov_len = hdrlen;
493 
494 	iov[1].iov_base = (char *)text;
495 	iov[1].iov_len = len;
496 
497 	writev(syslog_fd, iov, array_size(iov));
498 }
499 
500 
501 static pthread_mutex_t syslog_cfg_mutex = PTHREAD_MUTEX_INITIALIZER;
502 static struct zlt_syslog *zlt_syslog;
503 static int syslog_facility = LOG_DAEMON;
504 static int syslog_prio_min = ZLOG_DISABLED;
505 
zlog_syslog_set_facility(int facility)506 void zlog_syslog_set_facility(int facility)
507 {
508 	struct zlog_target *newztc;
509 	struct zlt_syslog *newzt;
510 
511 	frr_with_mutex(&syslog_cfg_mutex) {
512 		if (facility == syslog_facility)
513 			return;
514 		syslog_facility = facility;
515 
516 		if (syslog_prio_min == ZLOG_DISABLED)
517 			return;
518 
519 		newztc = zlog_target_clone(MTYPE_LOG_SYSL, &zlt_syslog->zt,
520 					   sizeof(*newzt));
521 		newzt = container_of(newztc, struct zlt_syslog, zt);
522 		newzt->syslog_facility = syslog_facility;
523 
524 		zlog_target_free(MTYPE_LOG_SYSL,
525 				 zlog_target_replace(&zlt_syslog->zt,
526 						     &newzt->zt));
527 
528 		zlt_syslog = newzt;
529 	}
530 }
531 
zlog_syslog_get_facility(void)532 int zlog_syslog_get_facility(void)
533 {
534 	frr_with_mutex(&syslog_cfg_mutex) {
535 		return syslog_facility;
536 	}
537 	assert(0);
538 }
539 
zlog_syslog_set_prio_min(int prio_min)540 void zlog_syslog_set_prio_min(int prio_min)
541 {
542 	struct zlog_target *newztc;
543 	struct zlt_syslog *newzt = NULL;
544 
545 	frr_with_mutex(&syslog_cfg_mutex) {
546 		if (prio_min == syslog_prio_min)
547 			return;
548 		syslog_prio_min = prio_min;
549 
550 		if (syslog_prio_min != ZLOG_DISABLED) {
551 			newztc = zlog_target_clone(MTYPE_LOG_SYSL,
552 						   &zlt_syslog->zt,
553 						   sizeof(*newzt));
554 			newzt = container_of(newztc, struct zlt_syslog, zt);
555 			newzt->zt.prio_min = prio_min;
556 			newzt->zt.logfn = zlog_syslog;
557 			newzt->zt.logfn_sigsafe = zlog_syslog_sigsafe;
558 			newzt->syslog_facility = syslog_facility;
559 		}
560 
561 		zlog_target_free(MTYPE_LOG_SYSL,
562 				 zlog_target_replace(&zlt_syslog->zt,
563 						     &newzt->zt));
564 
565 		zlt_syslog = newzt;
566 	}
567 }
568 
zlog_syslog_get_prio_min(void)569 int zlog_syslog_get_prio_min(void)
570 {
571 	frr_with_mutex(&syslog_cfg_mutex) {
572 		return syslog_prio_min;
573 	}
574 	assert(0);
575 }
576