xref: /openbsd-src/usr.bin/tmux/log.c (revision 92d7bafb12acb58c9a19680a161425388f8f888f)
1 /* $OpenBSD: log.c,v 1.28 2021/08/25 07:09:30 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 /* Log callback for libevent. */
34 static void
35 log_event_cb(__unused int severity, const char *msg)
36 {
37 	log_debug("%s", msg);
38 }
39 
40 /* Increment log level. */
41 void
42 log_add_level(void)
43 {
44 	log_level++;
45 }
46 
47 /* Get log level. */
48 int
49 log_get_level(void)
50 {
51 	return (log_level);
52 }
53 
54 /* Open logging to file. */
55 void
56 log_open(const char *name)
57 {
58 	char	*path;
59 
60 	if (log_level == 0)
61 		return;
62 	log_close();
63 
64 	xasprintf(&path, "tmux-%s-%ld.log", name, (long)getpid());
65 	log_file = fopen(path, "a");
66 	free(path);
67 	if (log_file == NULL)
68 		return;
69 
70 	setvbuf(log_file, NULL, _IOLBF, 0);
71 	event_set_log_callback(log_event_cb);
72 }
73 
74 /* Toggle logging. */
75 void
76 log_toggle(const char *name)
77 {
78 	if (log_level == 0) {
79 		log_level = 1;
80 		log_open(name);
81 		log_debug("log opened");
82 	} else {
83 		log_debug("log closed");
84 		log_level = 0;
85 		log_close();
86 	}
87 }
88 
89 /* Close logging. */
90 void
91 log_close(void)
92 {
93 	if (log_file != NULL)
94 		fclose(log_file);
95 	log_file = NULL;
96 
97 	event_set_log_callback(NULL);
98 }
99 
100 /* Write a log message. */
101 static void printflike(1, 0)
102 log_vwrite(const char *msg, va_list ap)
103 {
104 	char		*fmt, *out;
105 	struct timeval	 tv;
106 
107 	if (log_file == NULL)
108 		return;
109 
110 	if (vasprintf(&fmt, msg, ap) == -1)
111 		return;
112 	if (stravis(&out, fmt, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL) == -1) {
113 		free(fmt);
114 		return;
115 	}
116 
117 	gettimeofday(&tv, NULL);
118 	if (fprintf(log_file, "%lld.%06d %s\n", (long long)tv.tv_sec,
119 	    (int)tv.tv_usec, out) != -1)
120 		fflush(log_file);
121 
122 	free(out);
123 	free(fmt);
124 }
125 
126 /* Log a debug message. */
127 void
128 log_debug(const char *msg, ...)
129 {
130 	va_list	ap;
131 
132 	if (log_file == NULL)
133 		return;
134 
135 	va_start(ap, msg);
136 	log_vwrite(msg, ap);
137 	va_end(ap);
138 }
139 
140 /* Log a critical error with error string and die. */
141 __dead void
142 fatal(const char *msg, ...)
143 {
144 	char	*fmt;
145 	va_list	 ap;
146 
147 	va_start(ap, msg);
148 	if (asprintf(&fmt, "fatal: %s: %s", msg, strerror(errno)) == -1)
149 		exit(1);
150 	log_vwrite(fmt, ap);
151 	va_end(ap);
152 	exit(1);
153 }
154 
155 /* Log a critical error and die. */
156 __dead void
157 fatalx(const char *msg, ...)
158 {
159 	char	*fmt;
160 	va_list	 ap;
161 
162 	va_start(ap, msg);
163 	if (asprintf(&fmt, "fatal: %s", msg) == -1)
164 		exit(1);
165 	log_vwrite(fmt, ap);
166 	va_end(ap);
167 	exit(1);
168 }
169