1 /* $OpenBSD: log.c,v 1.31 2022/05/30 12:55:25 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
log_event_cb(__unused int severity,const char * msg)35 log_event_cb(__unused int severity, const char *msg)
36 {
37 log_debug("%s", msg);
38 }
39
40 /* Increment log level. */
41 void
log_add_level(void)42 log_add_level(void)
43 {
44 log_level++;
45 }
46
47 /* Get log level. */
48 int
log_get_level(void)49 log_get_level(void)
50 {
51 return (log_level);
52 }
53
54 /* Open logging to file. */
55 void
log_open(const char * name)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
log_toggle(const char * name)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
log_close(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)
log_vwrite(const char * msg,va_list ap,const char * prefix)102 log_vwrite(const char *msg, va_list ap, const char *prefix)
103 {
104 char *s, *out;
105 struct timeval tv;
106
107 if (log_file == NULL)
108 return;
109
110 if (vasprintf(&s, msg, ap) == -1)
111 return;
112 if (stravis(&out, s, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL) == -1) {
113 free(s);
114 return;
115 }
116 free(s);
117
118 gettimeofday(&tv, NULL);
119 if (fprintf(log_file, "%lld.%06d %s%s\n", (long long)tv.tv_sec,
120 (int)tv.tv_usec, prefix, out) != -1)
121 fflush(log_file);
122 free(out);
123 }
124
125 /* Log a debug message. */
126 void
log_debug(const char * msg,...)127 log_debug(const char *msg, ...)
128 {
129 va_list ap;
130
131 if (log_file == NULL)
132 return;
133
134 va_start(ap, msg);
135 log_vwrite(msg, ap, "");
136 va_end(ap);
137 }
138
139 /* Log a critical error with error string and die. */
140 __dead void
fatal(const char * msg,...)141 fatal(const char *msg, ...)
142 {
143 char tmp[256];
144 va_list ap;
145
146 if (snprintf(tmp, sizeof tmp, "fatal: %s: ", strerror(errno)) < 0)
147 exit(1);
148
149 va_start(ap, msg);
150 log_vwrite(msg, ap, tmp);
151 va_end(ap);
152
153 exit(1);
154 }
155
156 /* Log a critical error and die. */
157 __dead void
fatalx(const char * msg,...)158 fatalx(const char *msg, ...)
159 {
160 va_list ap;
161
162 va_start(ap, msg);
163 log_vwrite(msg, ap, "fatal: ");
164 va_end(ap);
165
166 exit(1);
167 }
168