1 /*
2  * Copyright (c) 2008, XenSource Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *     * Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     * Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the distribution.
12  *     * Neither the name of XenSource Inc. nor the names of its contributors
13  *       may be used to endorse or promote products derived from this software
14  *       without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
20  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28 
29 #include <stdio.h>
30 #include <fcntl.h>
31 #include <unistd.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <stdarg.h>
35 #include <syslog.h>
36 #include <inttypes.h>
37 #include <sys/time.h>
38 
39 #include "tapdisk-log.h"
40 #include "tapdisk-utils.h"
41 
42 #define MAX_ENTRY_LEN      512
43 #define MAX_ERROR_MESSAGES 16
44 
45 struct error {
46 	int            cnt;
47 	int            err;
48 	char          *func;
49 	char           msg[MAX_ENTRY_LEN];
50 };
51 
52 struct ehandle {
53 	int            cnt;
54 	int            dropped;
55 	struct error   errors[MAX_ERROR_MESSAGES];
56 };
57 
58 struct tlog {
59 	char          *p;
60 	int            size;
61 	uint64_t       cnt;
62 	char          *buf;
63 	int            level;
64 	char          *file;
65 	int            append;
66 };
67 
68 static struct ehandle tapdisk_err;
69 static struct tlog tapdisk_log;
70 
71 void
open_tlog(char * file,size_t bytes,int level,int append)72 open_tlog(char *file, size_t bytes, int level, int append)
73 {
74 	tapdisk_log.size = ((bytes + 511) & (~511));
75 
76 	if (asprintf(&tapdisk_log.file, "%s.%d", file, getpid()) == -1)
77 		return;
78 
79 	if (posix_memalign((void **)&tapdisk_log.buf, 512, tapdisk_log.size)) {
80 		free(tapdisk_log.file);
81 		tapdisk_log.buf = NULL;
82 		return;
83 	}
84 
85 	memset(tapdisk_log.buf, 0, tapdisk_log.size);
86 
87 	tapdisk_log.p      = tapdisk_log.buf;
88 	tapdisk_log.level  = level;
89 	tapdisk_log.append = append;
90 }
91 
92 void
close_tlog(void)93 close_tlog(void)
94 {
95 	if (!tapdisk_log.buf)
96 		return;
97 
98 	if (tapdisk_log.append)
99 		tlog_flush();
100 
101 	free(tapdisk_log.buf);
102 	free(tapdisk_log.file);
103 
104 	memset(&tapdisk_log, 0, sizeof(struct tlog));
105 }
106 
107 void
__tlog_write(int level,const char * func,const char * fmt,...)108 __tlog_write(int level, const char *func, const char *fmt, ...)
109 {
110 	char *buf;
111 	va_list ap;
112 	struct timeval t;
113 	int ret, len, avail;
114 
115 	if (!tapdisk_log.buf)
116 		return;
117 
118 	if (level > tapdisk_log.level)
119 		return;
120 
121 	avail = tapdisk_log.size - (tapdisk_log.p - tapdisk_log.buf);
122 	if (avail < MAX_ENTRY_LEN) {
123 		if (tapdisk_log.append)
124 			tlog_flush();
125 		tapdisk_log.p = tapdisk_log.buf;
126 	}
127 
128 	buf = tapdisk_log.p;
129 	gettimeofday(&t, NULL);
130 	len = snprintf(buf, MAX_ENTRY_LEN - 1, "%08"PRIu64":%010ld.%06lld:"
131 		       "%s ", tapdisk_log.cnt,
132 			t.tv_sec, (unsigned long long)t.tv_usec, func);
133 
134 	va_start(ap, fmt);
135 	ret = vsnprintf(buf + len, MAX_ENTRY_LEN - (len + 1), fmt, ap);
136 	va_end(ap);
137 
138 	len = (ret < MAX_ENTRY_LEN - (len + 1) ?
139 	       len + ret : MAX_ENTRY_LEN - 1);
140 	buf[len] = '\0';
141 
142 	tapdisk_log.cnt++;
143 	tapdisk_log.p += len;
144 }
145 
146 void
__tlog_error(int err,const char * func,const char * fmt,...)147 __tlog_error(int err, const char *func, const char *fmt, ...)
148 {
149 	va_list ap;
150 	int i, len, ret;
151 	struct error *e;
152 	struct timeval t;
153 
154 	err = (err > 0 ? err : -err);
155 
156 	for (i = 0; i < tapdisk_err.cnt; i++) {
157 		e = &tapdisk_err.errors[i];
158 		if (e->err == err && e->func == func) {
159 			e->cnt++;
160 			return;
161 		}
162 	}
163 
164 	if (tapdisk_err.cnt >= MAX_ERROR_MESSAGES) {
165 		tapdisk_err.dropped++;
166 		return;
167 	}
168 
169 	gettimeofday(&t, NULL);
170 	e = &tapdisk_err.errors[tapdisk_err.cnt];
171 
172 	len = snprintf(e->msg, MAX_ENTRY_LEN - 1, "%010ld.%06lld:%s ",
173 		       t.tv_sec, (unsigned long long)t.tv_usec, func);
174 
175 	va_start(ap, fmt);
176 	ret = vsnprintf(e->msg + len, MAX_ENTRY_LEN - (len + 1), fmt, ap);
177 	va_end(ap);
178 
179 	len = (ret < MAX_ENTRY_LEN - (len + 1) ?
180 	       len + ret : MAX_ENTRY_LEN - 1);
181 	e->msg[len] = '\0';
182 
183 	e->cnt++;
184 	e->err  = err;
185 	e->func = (char *)func;
186 	tapdisk_err.cnt++;
187 }
188 
189 void
tlog_print_errors(void)190 tlog_print_errors(void)
191 {
192 	int i;
193 	struct error *e;
194 
195 	for (i = 0; i < tapdisk_err.cnt; i++) {
196 		e = &tapdisk_err.errors[i];
197 		syslog(LOG_INFO, "TAPDISK ERROR: errno %d at %s (cnt = %d): "
198 		       "%s\n", e->err, e->func, e->cnt, e->msg);
199 	}
200 
201 	if (tapdisk_err.dropped)
202 		syslog(LOG_INFO, "TAPDISK ERROR: %d other error messages "
203 		       "dropped\n", tapdisk_err.dropped);
204 }
205 
206 void
tlog_flush_errors(void)207 tlog_flush_errors(void)
208 {
209 	int i;
210 	struct error *e;
211 
212 	for (i = 0; i < tapdisk_err.cnt; i++) {
213 		e = &tapdisk_err.errors[i];
214 		tlog_write(TLOG_WARN, "TAPDISK ERROR: errno %d at %s "
215 			   "(cnt = %d): %s\n", e->err, e->func, e->cnt,
216 			   e->msg);
217 	}
218 
219 	if (tapdisk_err.dropped)
220 		tlog_write(TLOG_WARN, "TAPDISK ERROR: %d other error messages "
221 		       "dropped\n", tapdisk_err.dropped);
222 }
223 
224 void
tlog_flush(void)225 tlog_flush(void)
226 {
227 	int fd, flags;
228 	size_t size, wsize;
229 
230 	if (!tapdisk_log.buf)
231 		return;
232 
233 	flags = O_CREAT | O_WRONLY | O_DIRECT | O_NONBLOCK;
234 	if (!tapdisk_log.append)
235 		flags |= O_TRUNC;
236 
237 	fd = open(tapdisk_log.file, flags, 0644);
238 	if (fd == -1)
239 		return;
240 
241 	if (tapdisk_log.append)
242 		if (lseek(fd, 0, SEEK_END) == (off_t)-1)
243 			goto out;
244 
245 	tlog_flush_errors();
246 
247 	size  = tapdisk_log.p - tapdisk_log.buf;
248 	wsize = ((size + 511) & (~511));
249 
250 	memset(tapdisk_log.buf + size, '\n', wsize - size);
251 	write_exact(fd, tapdisk_log.buf, wsize);
252 
253 	tapdisk_log.p = tapdisk_log.buf;
254 
255 out:
256 	close(fd);
257 }
258