xref: /openbsd/usr.bin/tmux/log.c (revision 097a140d)
1 /* $OpenBSD: log.c,v 1.27 2021/03/31 08:37:48 nicm Exp $ */
2 
3 /*
4  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 
21 #include <errno.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <vis.h>
27 
28 #include "tmux.h"
29 
30 static FILE	*log_file;
31 static int	 log_level;
32 
33 static void	 log_event_cb(int, const char *);
34 static void	 log_vwrite(const char *, va_list);
35 
36 /* Log callback for libevent. */
37 static void
38 log_event_cb(__unused int severity, const char *msg)
39 {
40 	log_debug("%s", msg);
41 }
42 
43 /* Increment log level. */
44 void
45 log_add_level(void)
46 {
47 	log_level++;
48 }
49 
50 /* Get log level. */
51 int
52 log_get_level(void)
53 {
54 	return (log_level);
55 }
56 
57 /* Open logging to file. */
58 void
59 log_open(const char *name)
60 {
61 	char	*path;
62 
63 	if (log_level == 0)
64 		return;
65 	log_close();
66 
67 	xasprintf(&path, "tmux-%s-%ld.log", name, (long)getpid());
68 	log_file = fopen(path, "a");
69 	free(path);
70 	if (log_file == NULL)
71 		return;
72 
73 	setvbuf(log_file, NULL, _IOLBF, 0);
74 	event_set_log_callback(log_event_cb);
75 }
76 
77 /* Toggle logging. */
78 void
79 log_toggle(const char *name)
80 {
81 	if (log_level == 0) {
82 		log_level = 1;
83 		log_open(name);
84 		log_debug("log opened");
85 	} else {
86 		log_debug("log closed");
87 		log_level = 0;
88 		log_close();
89 	}
90 }
91 
92 /* Close logging. */
93 void
94 log_close(void)
95 {
96 	if (log_file != NULL)
97 		fclose(log_file);
98 	log_file = NULL;
99 
100 	event_set_log_callback(NULL);
101 }
102 
103 /* Write a log message. */
104 static void
105 log_vwrite(const char *msg, va_list ap)
106 {
107 	char		*fmt, *out;
108 	struct timeval	 tv;
109 
110 	if (log_file == NULL)
111 		return;
112 
113 	if (vasprintf(&fmt, msg, ap) == -1)
114 		return;
115 	if (stravis(&out, fmt, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL) == -1) {
116 		free(fmt);
117 		return;
118 	}
119 
120 	gettimeofday(&tv, NULL);
121 	if (fprintf(log_file, "%lld.%06d %s\n", (long long)tv.tv_sec,
122 	    (int)tv.tv_usec, out) != -1)
123 		fflush(log_file);
124 
125 	free(out);
126 	free(fmt);
127 }
128 
129 /* Log a debug message. */
130 void
131 log_debug(const char *msg, ...)
132 {
133 	va_list	ap;
134 
135 	if (log_file == NULL)
136 		return;
137 
138 	va_start(ap, msg);
139 	log_vwrite(msg, ap);
140 	va_end(ap);
141 }
142 
143 /* Log a critical error with error string and die. */
144 __dead void
145 fatal(const char *msg, ...)
146 {
147 	char	*fmt;
148 	va_list	 ap;
149 
150 	va_start(ap, msg);
151 	if (asprintf(&fmt, "fatal: %s: %s", msg, strerror(errno)) == -1)
152 		exit(1);
153 	log_vwrite(fmt, ap);
154 	va_end(ap);
155 	exit(1);
156 }
157 
158 /* Log a critical error and die. */
159 __dead void
160 fatalx(const char *msg, ...)
161 {
162 	char	*fmt;
163 	va_list	 ap;
164 
165 	va_start(ap, msg);
166 	if (asprintf(&fmt, "fatal: %s", msg) == -1)
167 		exit(1);
168 	log_vwrite(fmt, ap);
169 	va_end(ap);
170 	exit(1);
171 }
172