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