1215166Slstewart/*-
2215166Slstewart * Copyright (c) 2005-2008 Poul-Henning Kamp
3215166Slstewart * All rights reserved.
4215166Slstewart *
5215166Slstewart * Redistribution and use in source and binary forms, with or without
6215166Slstewart * modification, are permitted provided that the following conditions
7215166Slstewart * are met:
8215166Slstewart * 1. Redistributions of source code must retain the above copyright
9215166Slstewart *    notice, this list of conditions and the following disclaimer.
10215166Slstewart * 2. Redistributions in binary form must reproduce the above copyright
11220560Slstewart *    notice, this list of conditions and the following disclaimer in the
12220560Slstewart *    documentation and/or other materials provided with the distribution.
13220560Slstewart *
14215166Slstewart * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15215166Slstewart * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16215166Slstewart * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17215166Slstewart * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18215166Slstewart * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19215166Slstewart * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20215166Slstewart * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21215166Slstewart * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22215166Slstewart * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23215166Slstewart * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24215166Slstewart * SUCH DAMAGE.
25215166Slstewart *
26215166Slstewart * $FreeBSD$
27215166Slstewart */
28215166Slstewart
29215166Slstewart#include <assert.h>
30215166Slstewart#include <stdio.h>
31215166Slstewart#include <string.h>
32215166Slstewart#include <stdlib.h>
33215166Slstewart#include <unistd.h>
34215166Slstewart#include <stdint.h>
35215166Slstewart#include <time.h>
36215166Slstewart#include <sys/endian.h>
37215166Slstewart
38215166Slstewart#include <zlib.h>
39215166Slstewart
40215166Slstewart#include "fifolog.h"
41215166Slstewart#include "libfifolog_int.h"
42215166Slstewart#include "fifolog_write.h"
43220560Slstewart#include "miniobj.h"
44220560Slstewart
45220560Slstewartstatic int fifolog_write_gzip(struct fifolog_writer *f, time_t now);
46220560Slstewart
47220560Slstewart#define ALLOC(ptr, size) do {                   \
48215166Slstewart	(*(ptr)) = calloc(size, 1);             \
49215166Slstewart	assert(*(ptr) != NULL);                 \
50215166Slstewart} while (0)
51215166Slstewart
52215166Slstewart
53215166Slstewartconst char *fifolog_write_statnames[] = {
54215166Slstewart	[FIFOLOG_PT_BYTES_PRE] =	"Bytes before compression",
55215166Slstewart	[FIFOLOG_PT_BYTES_POST] =	"Bytes after compression",
56216107Slstewart	[FIFOLOG_PT_WRITES] =		"Writes",
57215166Slstewart	[FIFOLOG_PT_FLUSH] =		"Flushes",
58215166Slstewart	[FIFOLOG_PT_SYNC] =		"Syncs",
59215166Slstewart	[FIFOLOG_PT_RUNTIME] =		"Runtime"
60215166Slstewart};
61216107Slstewart
62215166Slstewart/**********************************************************************
63216107Slstewart * Check that everything is all right
64215166Slstewart */
65215166Slstewartstatic void
66215166Slstewartfifolog_write_assert(const struct fifolog_writer *f)
67215166Slstewart{
68215166Slstewart
69215166Slstewart	CHECK_OBJ_NOTNULL(f, FIFOLOG_WRITER_MAGIC);
70215166Slstewart	assert(f->ff->zs->next_out + f->ff->zs->avail_out == \
71216107Slstewart	    f->obuf + f->obufsize);
72216107Slstewart}
73216107Slstewart
74216107Slstewart/**********************************************************************
75215166Slstewart * Allocate/Destroy a new fifolog writer instance
76215166Slstewart */
77215166Slstewart
78215166Slstewartstruct fifolog_writer *
79216107Slstewartfifolog_write_new(void)
80215166Slstewart{
81215166Slstewart	struct fifolog_writer *f;
82215166Slstewart
83215166Slstewart	ALLOC_OBJ(f, FIFOLOG_WRITER_MAGIC);
84216107Slstewart	assert(f != NULL);
85215166Slstewart	return (f);
86215166Slstewart}
87215166Slstewart
88215166Slstewartvoid
89215166Slstewartfifolog_write_destroy(struct fifolog_writer *f)
90215166Slstewart{
91215166Slstewart
92215166Slstewart	free(f->obuf);
93215166Slstewart	free(f->ibuf);
94215166Slstewart	FREE_OBJ(f);
95215166Slstewart}
96215166Slstewart
97215166Slstewart/**********************************************************************
98215166Slstewart * Open/Close the fifolog
99215166Slstewart */
100215166Slstewart
101215166Slstewartvoid
102215166Slstewartfifolog_write_close(struct fifolog_writer *f)
103215166Slstewart{
104215166Slstewart	time_t now;
105215166Slstewart
106215166Slstewart	CHECK_OBJ_NOTNULL(f, FIFOLOG_WRITER_MAGIC);
107215166Slstewart	fifolog_write_assert(f);
108215166Slstewart
109215166Slstewart	f->cleanup = 1;
110215166Slstewart	time(&now);
111215166Slstewart	fifolog_write_gzip(f, now);
112215166Slstewart	fifolog_write_assert(f);
113215166Slstewart	fifolog_int_close(&f->ff);
114215166Slstewart	free(f->ff);
115215166Slstewart}
116215166Slstewart
117215166Slstewartconst char *
118215166Slstewartfifolog_write_open(struct fifolog_writer *f, const char *fn,
119215166Slstewart    unsigned writerate, unsigned syncrate, unsigned compression)
120215166Slstewart{
121215166Slstewart	const char *es;
122215166Slstewart	int i;
123215166Slstewart	time_t now;
124215166Slstewart	off_t o;
125215166Slstewart
126215166Slstewart	CHECK_OBJ_NOTNULL(f, FIFOLOG_WRITER_MAGIC);
127215166Slstewart
128215166Slstewart	/* Check for legal compression value */
129215166Slstewart	if (compression > Z_BEST_COMPRESSION)
130215166Slstewart		return ("Illegal compression value");
131215166Slstewart
132215166Slstewart	f->writerate = writerate;
133215166Slstewart	f->syncrate = syncrate;
134215166Slstewart	f->compression = compression;
135215166Slstewart
136215166Slstewart	/* Reset statistics */
137215166Slstewart	memset(f->cnt, 0, sizeof f->cnt);
138215166Slstewart
139215166Slstewart	es = fifolog_int_open(&f->ff, fn, 1);
140215166Slstewart	if (es != NULL)
141215166Slstewart		return (es);
142215166Slstewart	es = fifolog_int_findend(f->ff, &o);
143215166Slstewart	if (es != NULL)
144215166Slstewart		return (es);
145215166Slstewart	i = fifolog_int_read(f->ff, o);
146215166Slstewart	if (i)
147215166Slstewart		return ("Read error, looking for seq");
148215166Slstewart	f->seq = be32dec(f->ff->recbuf);
149215166Slstewart	if (f->seq == 0) {
150215166Slstewart		/* Empty fifolog */
151216107Slstewart		f->seq = random();
152216107Slstewart	} else {
153216107Slstewart		f->recno = o + 1;
154216107Slstewart		f->seq++;
155216107Slstewart	}
156216107Slstewart
157216107Slstewart	f->obufsize = f->ff->recsize;
158216107Slstewart	ALLOC(&f->obuf, f->obufsize);
159216107Slstewart
160216107Slstewart	f->ibufsize = f->obufsize * 10;
161216107Slstewart	ALLOC(&f->ibuf, f->ibufsize);
162216107Slstewart	f->ibufptr = 0;
163216107Slstewart
164216107Slstewart	i = deflateInit(f->ff->zs, (int)f->compression);
165216107Slstewart	assert(i == Z_OK);
166216107Slstewart
167216107Slstewart	f->flag |= FIFOLOG_FLG_RESTART;
168216107Slstewart	f->flag |= FIFOLOG_FLG_SYNC;
169216107Slstewart	f->ff->zs->next_out = f->obuf + 9;
170216107Slstewart	f->ff->zs->avail_out = f->obufsize - 9;
171216107Slstewart
172216107Slstewart	time(&now);
173216107Slstewart	f->starttime = now;
174216107Slstewart	f->lastsync = now;
175216107Slstewart	f->lastwrite = now;
176216107Slstewart
177216107Slstewart	fifolog_write_assert(f);
178215166Slstewart	return (NULL);
179216107Slstewart}
180215166Slstewart
181216107Slstewart/**********************************************************************
182215166Slstewart * Write an output record
183215166Slstewart * Returns -1 if there are trouble writing data
184215166Slstewart */
185215166Slstewart
186218167Slstewartstatic int
187218167Slstewartfifolog_write_output(struct fifolog_writer *f, int fl, time_t now)
188218167Slstewart{
189218167Slstewart	long h, l = f->ff->zs->next_out - f->obuf;
190215166Slstewart	ssize_t i, w;
191215166Slstewart	int retval = 0;
192215166Slstewart
193215166Slstewart	h = 4;					/* seq */
194215166Slstewart	be32enc(f->obuf, f->seq);
195215166Slstewart	f->obuf[h] = f->flag;
196215166Slstewart	h += 1;					/* flag */
197215166Slstewart	if (f->flag & FIFOLOG_FLG_SYNC) {
198215166Slstewart		be32enc(f->obuf + h, now);
199215166Slstewart		h += 4;				/* timestamp */
200215166Slstewart	}
201215166Slstewart
202215166Slstewart	assert(l <= (long)f->ff->recsize);	/* NB: l includes h */
203215166Slstewart	assert(l >= h);
204215166Slstewart
205215166Slstewart	/* We will never write an entirely empty buffer */
206215166Slstewart	if (l == h)
207215166Slstewart		return (0);
208215166Slstewart
209215166Slstewart	if (l < (long)f->ff->recsize && fl == Z_NO_FLUSH)
210215166Slstewart		return (0);
211215166Slstewart
212216107Slstewart	w = f->ff->recsize - l;
213215166Slstewart	if (w >  255) {
214216107Slstewart		be32enc(f->obuf + f->ff->recsize - 4, w);
215215166Slstewart		f->obuf[4] |= FIFOLOG_FLG_4BYTE;
216215166Slstewart	} else if (w > 0) {
217293711Shiren		f->obuf[f->ff->recsize - 1] = (uint8_t)w;
218293711Shiren		f->obuf[4] |= FIFOLOG_FLG_1BYTE;
219293711Shiren	}
220215166Slstewart
221215166Slstewart	f->cnt[FIFOLOG_PT_BYTES_POST] += l - h;
222215166Slstewart
223215166Slstewart	i = pwrite(f->ff->fd, f->obuf, f->ff->recsize,
224215166Slstewart	    (f->recno + 1) * f->ff->recsize);
225215166Slstewart	if (i != f->ff->recsize)
226215166Slstewart		retval = -1;
227215166Slstewart	else
228215166Slstewart		retval = 1;
229215166Slstewart
230293711Shiren	f->cnt[FIFOLOG_PT_WRITES]++;
231293711Shiren	f->cnt[FIFOLOG_PT_RUNTIME] = now - f->starttime;
232215166Slstewart
233293711Shiren	f->lastwrite = now;
234293711Shiren	/*
235293711Shiren	 * We increment these even on error, so as to properly skip bad,
236293711Shiren	 * sectors or other light trouble.
237293711Shiren	 */
238215166Slstewart	f->seq++;
239215166Slstewart	f->recno++;
240215166Slstewart	f->flag = 0;
241215166Slstewart
242216105Slstewart	memset(f->obuf, 0, f->obufsize);
243215166Slstewart	f->ff->zs->next_out = f->obuf + 5;
244	f->ff->zs->avail_out = f->obufsize - 5;
245	return (retval);
246}
247
248/**********************************************************************
249 * Run the compression engine
250 * Returns -1 if there are trouble writing data
251 */
252
253static int
254fifolog_write_gzip(struct fifolog_writer *f, time_t now)
255{
256	int i, fl, retval = 0;
257
258	assert(now != 0);
259	if (f->cleanup || now >= (int)(f->lastsync + f->syncrate)) {
260		f->cleanup = 0;
261		fl = Z_FINISH;
262		f->cnt[FIFOLOG_PT_SYNC]++;
263	} else if (now >= (int)(f->lastwrite + f->writerate)) {
264		fl = Z_SYNC_FLUSH;
265		f->cnt[FIFOLOG_PT_FLUSH]++;
266	} else if (f->ibufptr == 0)
267		return (0);
268	else
269		fl = Z_NO_FLUSH;
270
271	f->ff->zs->avail_in = f->ibufptr;
272	f->ff->zs->next_in = f->ibuf;
273
274	while (1) {
275		i = deflate(f->ff->zs, fl);
276		assert(i == Z_OK || i == Z_BUF_ERROR || i == Z_STREAM_END);
277
278		i = fifolog_write_output(f, fl, now);
279		if (i == 0)
280			break;
281		if (i < 0)
282			retval = -1;
283	}
284	assert(f->ff->zs->avail_in == 0);
285	f->ibufptr = 0;
286	if (fl == Z_FINISH) {
287		f->flag |= FIFOLOG_FLG_SYNC;
288		f->ff->zs->next_out = f->obuf + 9;
289		f->ff->zs->avail_out = f->obufsize - 9;
290		f->lastsync = now;
291		assert(Z_OK == deflateReset(f->ff->zs));
292	}
293	return (retval);
294}
295
296/**********************************************************************
297 * Poll to see if we need to flush out a record
298 * Returns -1 if there are trouble writing data
299 */
300
301int
302fifolog_write_poll(struct fifolog_writer *f, time_t now)
303{
304
305	if (now == 0)
306		time(&now);
307	return (fifolog_write_gzip(f, now));
308}
309
310/**********************************************************************
311 * Attempt to write an entry into the ibuf.
312 * Return zero if there is no space, one otherwise
313 */
314
315int
316fifolog_write_record(struct fifolog_writer *f, uint32_t id, time_t now,
317    const void *ptr, ssize_t len)
318{
319	const unsigned char *p;
320	uint8_t buf[9];
321	ssize_t bufl;
322
323	fifolog_write_assert(f);
324	assert(!(id & (FIFOLOG_TIMESTAMP|FIFOLOG_LENGTH)));
325	assert(ptr != NULL);
326
327	p = ptr;
328	if (len == 0) {
329		len = strlen(ptr);
330		len++;
331	} else {
332		assert(len <= 255);
333		id |= FIFOLOG_LENGTH;
334	}
335	assert (len > 0);
336
337	/* Do a timestamp, if needed */
338	if (now == 0)
339		time(&now);
340
341	if (now != f->last)
342		id |= FIFOLOG_TIMESTAMP;
343
344	/* Emit instance+flag */
345	be32enc(buf, id);
346	bufl = 4;
347
348	if (id & FIFOLOG_TIMESTAMP) {
349		be32enc(buf + bufl, (uint32_t)now);
350		bufl += 4;
351	}
352	if (id & FIFOLOG_LENGTH)
353		buf[bufl++] = (u_char)len;
354
355	if (bufl + len + f->ibufptr > f->ibufsize)
356		return (0);
357
358	memcpy(f->ibuf + f->ibufptr, buf, bufl);
359	f->ibufptr += bufl;
360	memcpy(f->ibuf + f->ibufptr, p, len);
361	f->ibufptr += len;
362	f->cnt[FIFOLOG_PT_BYTES_PRE] += bufl + len;
363
364	if (id & FIFOLOG_TIMESTAMP)
365		f->last = now;
366	return (1);
367}
368
369/**********************************************************************
370 * Write an entry, polling the gzip/writer until success.
371 * Long binary entries are broken into 255 byte chunks.
372 * Returns -1 if there are problems writing data
373 */
374
375int
376fifolog_write_record_poll(struct fifolog_writer *f, uint32_t id, time_t now,
377    const void *ptr, ssize_t len)
378{
379	u_int l;
380	const unsigned char *p;
381	int retval = 0;
382
383	if (now == 0)
384		time(&now);
385	fifolog_write_assert(f);
386
387	assert(!(id & (FIFOLOG_TIMESTAMP|FIFOLOG_LENGTH)));
388	assert(ptr != NULL);
389
390	if (len == 0) {
391		if (!fifolog_write_record(f, id, now, ptr, len)) {
392			if (fifolog_write_gzip(f, now) < 0)
393				retval = -1;
394			/* The string could be too long for the ibuf, so... */
395			if (!fifolog_write_record(f, id, now, ptr, len))
396				retval = -1;
397		}
398	} else {
399		for (p = ptr; len > 0; len -= l, p += l) {
400			l = len;
401			if (l > 255)
402				l = 255;
403			while (!fifolog_write_record(f, id, now, p, l))
404				if (fifolog_write_gzip(f, now) < 0)
405					retval = -1;
406		}
407	}
408	if (fifolog_write_gzip(f, now) < 0)
409		retval = -1;
410	fifolog_write_assert(f);
411	return (retval);
412}
413