1329dcbaphk/*-
27551d83pfg * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
37551d83pfg *
4329dcbaphk * Copyright (c) 2005-2008 Poul-Henning Kamp
5329dcbaphk * All rights reserved.
6329dcbaphk *
7329dcbaphk * Redistribution and use in source and binary forms, with or without
8329dcbaphk * modification, are permitted provided that the following conditions
9329dcbaphk * are met:
10329dcbaphk * 1. Redistributions of source code must retain the above copyright
11329dcbaphk *    notice, this list of conditions and the following disclaimer.
12329dcbaphk * 2. Redistributions in binary form must reproduce the above copyright
13329dcbaphk *    notice, this list of conditions and the following disclaimer in the
14329dcbaphk *    documentation and/or other materials provided with the distribution.
15329dcbaphk *
16329dcbaphk * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17329dcbaphk * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18329dcbaphk * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19329dcbaphk * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20329dcbaphk * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21329dcbaphk * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22329dcbaphk * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23329dcbaphk * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24329dcbaphk * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25329dcbaphk * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26329dcbaphk * SUCH DAMAGE.
27329dcbaphk *
28329dcbaphk * $FreeBSD$
29329dcbaphk */
30329dcbaphk
31329dcbaphk#include <assert.h>
32329dcbaphk#include <stdio.h>
33329dcbaphk#include <string.h>
34329dcbaphk#include <stdlib.h>
35329dcbaphk#include <unistd.h>
36851c06bphk#include <stdint.h>
37329dcbaphk#include <time.h>
38329dcbaphk#include <sys/endian.h>
39329dcbaphk
40329dcbaphk#include <zlib.h>
41329dcbaphk
42329dcbaphk#include "fifolog.h"
43329dcbaphk#include "libfifolog_int.h"
44329dcbaphk#include "fifolog_write.h"
45329dcbaphk#include "miniobj.h"
46329dcbaphk
47851c06bphkstatic int fifolog_write_gzip(struct fifolog_writer *f, time_t now);
48851c06bphk
49329dcbaphk#define ALLOC(ptr, size) do {                   \
5096b13f0pfg	(*(ptr)) = calloc(1, size);             \
51329dcbaphk	assert(*(ptr) != NULL);                 \
52329dcbaphk} while (0)
53329dcbaphk
54329dcbaphk
55329dcbaphkconst char *fifolog_write_statnames[] = {
568642393phk	[FIFOLOG_PT_BYTES_PRE] =	"Bytes before compression",
578642393phk	[FIFOLOG_PT_BYTES_POST] =	"Bytes after compression",
588642393phk	[FIFOLOG_PT_WRITES] =		"Writes",
598642393phk	[FIFOLOG_PT_FLUSH] =		"Flushes",
608642393phk	[FIFOLOG_PT_SYNC] =		"Syncs",
618642393phk	[FIFOLOG_PT_RUNTIME] =		"Runtime"
62329dcbaphk};
63329dcbaphk
64851c06bphk/**********************************************************************
65329dcbaphk * Check that everything is all right
66329dcbaphk */
67329dcbaphkstatic void
68329dcbaphkfifolog_write_assert(const struct fifolog_writer *f)
69329dcbaphk{
70329dcbaphk
71329dcbaphk	CHECK_OBJ_NOTNULL(f, FIFOLOG_WRITER_MAGIC);
72329dcbaphk	assert(f->ff->zs->next_out + f->ff->zs->avail_out == \
73bda3b88phk	    f->obuf + f->obufsize);
74329dcbaphk}
75329dcbaphk
76851c06bphk/**********************************************************************
77851c06bphk * Allocate/Destroy a new fifolog writer instance
78851c06bphk */
79851c06bphk
80329dcbaphkstruct fifolog_writer *
81329dcbaphkfifolog_write_new(void)
82329dcbaphk{
83329dcbaphk	struct fifolog_writer *f;
84329dcbaphk
85bda3b88phk	ALLOC_OBJ(f, FIFOLOG_WRITER_MAGIC);
86bda3b88phk	assert(f != NULL);
87329dcbaphk	return (f);
88329dcbaphk}
89329dcbaphk
90329dcbaphkvoid
91329dcbaphkfifolog_write_destroy(struct fifolog_writer *f)
92329dcbaphk{
93851c06bphk
94851c06bphk	free(f->obuf);
95851c06bphk	free(f->ibuf);
96851c06bphk	FREE_OBJ(f);
97329dcbaphk}
98329dcbaphk
99851c06bphk/**********************************************************************
100851c06bphk * Open/Close the fifolog
101851c06bphk */
102851c06bphk
103329dcbaphkvoid
104329dcbaphkfifolog_write_close(struct fifolog_writer *f)
105329dcbaphk{
106851c06bphk	time_t now;
107329dcbaphk
108329dcbaphk	CHECK_OBJ_NOTNULL(f, FIFOLOG_WRITER_MAGIC);
109851c06bphk	fifolog_write_assert(f);
110851c06bphk
111851c06bphk	f->cleanup = 1;
112851c06bphk	time(&now);
113851c06bphk	fifolog_write_gzip(f, now);
114851c06bphk	fifolog_write_assert(f);
115329dcbaphk	fifolog_int_close(&f->ff);
116394021fphk	free(f->ff);
117329dcbaphk}
118329dcbaphk
119329dcbaphkconst char *
120851c06bphkfifolog_write_open(struct fifolog_writer *f, const char *fn,
121851c06bphk    unsigned writerate, unsigned syncrate, unsigned compression)
122329dcbaphk{
123329dcbaphk	const char *es;
124329dcbaphk	int i;
125329dcbaphk	time_t now;
126329dcbaphk	off_t o;
127329dcbaphk
128329dcbaphk	CHECK_OBJ_NOTNULL(f, FIFOLOG_WRITER_MAGIC);
129329dcbaphk
130329dcbaphk	/* Check for legal compression value */
131851c06bphk	if (compression > Z_BEST_COMPRESSION)
132329dcbaphk		return ("Illegal compression value");
133329dcbaphk
134329dcbaphk	f->writerate = writerate;
135329dcbaphk	f->syncrate = syncrate;
136329dcbaphk	f->compression = compression;
137329dcbaphk
138329dcbaphk	/* Reset statistics */
139329dcbaphk	memset(f->cnt, 0, sizeof f->cnt);
140329dcbaphk
141329dcbaphk	es = fifolog_int_open(&f->ff, fn, 1);
142329dcbaphk	if (es != NULL)
143329dcbaphk		return (es);
144329dcbaphk	es = fifolog_int_findend(f->ff, &o);
145329dcbaphk	if (es != NULL)
146329dcbaphk		return (es);
1478b7a456phk	i = fifolog_int_read(f->ff, o);
1488b7a456phk	if (i)
1498b7a456phk		return ("Read error, looking for seq");
1508b7a456phk	f->seq = be32dec(f->ff->recbuf);
1518b7a456phk	if (f->seq == 0) {
1528b7a456phk		/* Empty fifolog */
1538b7a456phk		f->seq = random();
154329dcbaphk	} else {
155329dcbaphk		f->recno = o + 1;
1568b7a456phk		f->seq++;
157329dcbaphk	}
158329dcbaphk
159bda3b88phk	f->obufsize = f->ff->recsize;
160bda3b88phk	ALLOC(&f->obuf, f->obufsize);
161bda3b88phk
162851c06bphk	f->ibufsize = f->obufsize * 10;
163851c06bphk	ALLOC(&f->ibuf, f->ibufsize);
164851c06bphk	f->ibufptr = 0;
165851c06bphk
166329dcbaphk	i = deflateInit(f->ff->zs, (int)f->compression);
167329dcbaphk	assert(i == Z_OK);
168329dcbaphk
169329dcbaphk	f->flag |= FIFOLOG_FLG_RESTART;
170bda3b88phk	f->flag |= FIFOLOG_FLG_SYNC;
171bda3b88phk	f->ff->zs->next_out = f->obuf + 9;
172bda3b88phk	f->ff->zs->avail_out = f->obufsize - 9;
173329dcbaphk
174329dcbaphk	time(&now);
175329dcbaphk	f->starttime = now;
176bda3b88phk	f->lastsync = now;
177bda3b88phk	f->lastwrite = now;
178329dcbaphk
179329dcbaphk	fifolog_write_assert(f);
180329dcbaphk	return (NULL);
181329dcbaphk}
182329dcbaphk
183851c06bphk/**********************************************************************
184851c06bphk * Write an output record
185851c06bphk * Returns -1 if there are trouble writing data
186851c06bphk */
187851c06bphk
188bda3b88phkstatic int
189bda3b88phkfifolog_write_output(struct fifolog_writer *f, int fl, time_t now)
190329dcbaphk{
191bda3b88phk	long h, l = f->ff->zs->next_out - f->obuf;
192851c06bphk	ssize_t i, w;
193851c06bphk	int retval = 0;
194bda3b88phk
195bda3b88phk	h = 4;					/* seq */
196bda3b88phk	be32enc(f->obuf, f->seq);
197bda3b88phk	f->obuf[h] = f->flag;
198bda3b88phk	h += 1;					/* flag */
199bda3b88phk	if (f->flag & FIFOLOG_FLG_SYNC) {
200bda3b88phk		be32enc(f->obuf + h, now);
201bda3b88phk		h += 4;				/* timestamp */
202bda3b88phk	}
203329dcbaphk
204851c06bphk	assert(l <= (long)f->ff->recsize);	/* NB: l includes h */
205bda3b88phk	assert(l >= h);
206851c06bphk
207851c06bphk	/* We will never write an entirely empty buffer */
208bda3b88phk	if (l == h)
209bda3b88phk		return (0);
210bda3b88phk
211851c06bphk	if (l < (long)f->ff->recsize && fl == Z_NO_FLUSH)
212bda3b88phk		return (0);
213bda3b88phk
214bda3b88phk	w = f->ff->recsize - l;
215bda3b88phk	if (w >  255) {
216bda3b88phk		be32enc(f->obuf + f->ff->recsize - 4, w);
217bda3b88phk		f->obuf[4] |= FIFOLOG_FLG_4BYTE;
218bda3b88phk	} else if (w > 0) {
219851c06bphk		f->obuf[f->ff->recsize - 1] = (uint8_t)w;
220bda3b88phk		f->obuf[4] |= FIFOLOG_FLG_1BYTE;
221329dcbaphk	}
222bda3b88phk
223851c06bphk	f->cnt[FIFOLOG_PT_BYTES_POST] += l - h;
224bda3b88phk
225bda3b88phk	i = pwrite(f->ff->fd, f->obuf, f->ff->recsize,
226bda3b88phk	    (f->recno + 1) * f->ff->recsize);
227851c06bphk	if (i != f->ff->recsize)
228851c06bphk		retval = -1;
229851c06bphk	else
230851c06bphk		retval = 1;
231bda3b88phk
232329dcbaphk	f->cnt[FIFOLOG_PT_WRITES]++;
233851c06bphk	f->cnt[FIFOLOG_PT_RUNTIME] = now - f->starttime;
234bda3b88phk
235bda3b88phk	f->lastwrite = now;
236851c06bphk	/*
237851c06bphk	 * We increment these even on error, so as to properly skip bad,
238851c06bphk	 * sectors or other light trouble.
239851c06bphk	 */
240bda3b88phk	f->seq++;
241bda3b88phk	f->recno++;
242ab99701adrian
243ab99701adrian	/*
244ab99701adrian	 * Ensure we wrap recno once we hit the file size (in records.)
245ab99701adrian	 */
246ab99701adrian	if (f->recno >= f->ff->logsize)
247ab99701adrian		/* recno 0 is header; skip */
248ab99701adrian		f->recno = 1;
249ab99701adrian
250bda3b88phk	f->flag = 0;
251bda3b88phk
252bda3b88phk	memset(f->obuf, 0, f->obufsize);
253bda3b88phk	f->ff->zs->next_out = f->obuf + 5;
254bda3b88phk	f->ff->zs->avail_out = f->obufsize - 5;
255851c06bphk	return (retval);
256329dcbaphk}
257329dcbaphk
258851c06bphk/**********************************************************************
259851c06bphk * Run the compression engine
260851c06bphk * Returns -1 if there are trouble writing data
261851c06bphk */
262329dcbaphk
263851c06bphkstatic int
264851c06bphkfifolog_write_gzip(struct fifolog_writer *f, time_t now)
265851c06bphk{
266851c06bphk	int i, fl, retval = 0;
267329dcbaphk
268851c06bphk	assert(now != 0);
269851c06bphk	if (f->cleanup || now >= (int)(f->lastsync + f->syncrate)) {
270329dcbaphk		f->cleanup = 0;
271329dcbaphk		fl = Z_FINISH;
272329dcbaphk		f->cnt[FIFOLOG_PT_SYNC]++;
273bda3b88phk	} else if (now >= (int)(f->lastwrite + f->writerate)) {
274329dcbaphk		fl = Z_SYNC_FLUSH;
275329dcbaphk		f->cnt[FIFOLOG_PT_FLUSH]++;
276851c06bphk	} else if (f->ibufptr == 0)
277851c06bphk		return (0);
278329dcbaphk	else
279329dcbaphk		fl = Z_NO_FLUSH;
280329dcbaphk
281851c06bphk	f->ff->zs->avail_in = f->ibufptr;
282851c06bphk	f->ff->zs->next_in = f->ibuf;
283bda3b88phk
284bda3b88phk	while (1) {
285329dcbaphk		i = deflate(f->ff->zs, fl);
286bda3b88phk		assert(i == Z_OK || i == Z_BUF_ERROR || i == Z_STREAM_END);
287329dcbaphk
288851c06bphk		i = fifolog_write_output(f, fl, now);
289851c06bphk		if (i == 0)
290bda3b88phk			break;
291851c06bphk		if (i < 0)
292851c06bphk			retval = -1;
293329dcbaphk	}
294bda3b88phk	assert(f->ff->zs->avail_in == 0);
295851c06bphk	f->ibufptr = 0;
296bda3b88phk	if (fl == Z_FINISH) {
297bda3b88phk		f->flag |= FIFOLOG_FLG_SYNC;
298bda3b88phk		f->ff->zs->next_out = f->obuf + 9;
299bda3b88phk		f->ff->zs->avail_out = f->obufsize - 9;
300bda3b88phk		f->lastsync = now;
301bda3b88phk		assert(Z_OK == deflateReset(f->ff->zs));
302329dcbaphk	}
303851c06bphk	return (retval);
304329dcbaphk}
305329dcbaphk
306851c06bphk/**********************************************************************
307851c06bphk * Poll to see if we need to flush out a record
308851c06bphk * Returns -1 if there are trouble writing data
309851c06bphk */
310851c06bphk
311bda3b88phkint
312bda3b88phkfifolog_write_poll(struct fifolog_writer *f, time_t now)
313329dcbaphk{
314851c06bphk
315bda3b88phk	if (now == 0)
316bda3b88phk		time(&now);
317851c06bphk	return (fifolog_write_gzip(f, now));
318329dcbaphk}
319329dcbaphk
320851c06bphk/**********************************************************************
321851c06bphk * Attempt to write an entry into the ibuf.
322329dcbaphk * Return zero if there is no space, one otherwise
323329dcbaphk */
324329dcbaphk
325329dcbaphkint
326851c06bphkfifolog_write_record(struct fifolog_writer *f, uint32_t id, time_t now,
327851c06bphk    const void *ptr, ssize_t len)
328329dcbaphk{
329329dcbaphk	const unsigned char *p;
330851c06bphk	uint8_t buf[9];
331a3f6bc2phk	ssize_t bufl;
332329dcbaphk
333329dcbaphk	fifolog_write_assert(f);
334329dcbaphk	assert(!(id & (FIFOLOG_TIMESTAMP|FIFOLOG_LENGTH)));
335329dcbaphk	assert(ptr != NULL);
336329dcbaphk
337394021fphk	p = ptr;
338329dcbaphk	if (len == 0) {
339851c06bphk		len = strlen(ptr);
340851c06bphk		len++;
341329dcbaphk	} else {
342329dcbaphk		assert(len <= 255);
343329dcbaphk		id |= FIFOLOG_LENGTH;
344329dcbaphk	}
345851c06bphk	assert (len > 0);
346329dcbaphk
347851c06bphk	/* Do a timestamp, if needed */
348329dcbaphk	if (now == 0)
349329dcbaphk		time(&now);
350329dcbaphk
351851c06bphk	if (now != f->last)
352329dcbaphk		id |= FIFOLOG_TIMESTAMP;
353329dcbaphk
354bda3b88phk	/* Emit instance+flag */
355bda3b88phk	be32enc(buf, id);
356851c06bphk	bufl = 4;
357329dcbaphk
358329dcbaphk	if (id & FIFOLOG_TIMESTAMP) {
359851c06bphk		be32enc(buf + bufl, (uint32_t)now);
360851c06bphk		bufl += 4;
361329dcbaphk	}
362851c06bphk	if (id & FIFOLOG_LENGTH)
363851c06bphk		buf[bufl++] = (u_char)len;
364329dcbaphk
365851c06bphk	if (bufl + len + f->ibufptr > f->ibufsize)
366851c06bphk		return (0);
367851c06bphk
368851c06bphk	memcpy(f->ibuf + f->ibufptr, buf, bufl);
369851c06bphk	f->ibufptr += bufl;
370851c06bphk	memcpy(f->ibuf + f->ibufptr, p, len);
371851c06bphk	f->ibufptr += len;
372851c06bphk	f->cnt[FIFOLOG_PT_BYTES_PRE] += bufl + len;
373851c06bphk
374851c06bphk	if (id & FIFOLOG_TIMESTAMP)
375851c06bphk		f->last = now;
376329dcbaphk	return (1);
377329dcbaphk}
378329dcbaphk
379851c06bphk/**********************************************************************
380851c06bphk * Write an entry, polling the gzip/writer until success.
381329dcbaphk * Long binary entries are broken into 255 byte chunks.
382851c06bphk * Returns -1 if there are problems writing data
383329dcbaphk */
384329dcbaphk
385851c06bphkint
386851c06bphkfifolog_write_record_poll(struct fifolog_writer *f, uint32_t id, time_t now,
387851c06bphk    const void *ptr, ssize_t len)
388329dcbaphk{
389329dcbaphk	u_int l;
390329dcbaphk	const unsigned char *p;
391851c06bphk	int retval = 0;
392329dcbaphk
393851c06bphk	if (now == 0)
394851c06bphk		time(&now);
395329dcbaphk	fifolog_write_assert(f);
396329dcbaphk
397329dcbaphk	assert(!(id & (FIFOLOG_TIMESTAMP|FIFOLOG_LENGTH)));
398329dcbaphk	assert(ptr != NULL);
399329dcbaphk
400329dcbaphk	if (len == 0) {
401851c06bphk		if (!fifolog_write_record(f, id, now, ptr, len)) {
402851c06bphk			if (fifolog_write_gzip(f, now) < 0)
403851c06bphk				retval = -1;
404851c06bphk			/* The string could be too long for the ibuf, so... */
405851c06bphk			if (!fifolog_write_record(f, id, now, ptr, len))
406851c06bphk				retval = -1;
407329dcbaphk		}
408329dcbaphk	} else {
409329dcbaphk		for (p = ptr; len > 0; len -= l, p += l) {
410329dcbaphk			l = len;
411329dcbaphk			if (l > 255)
412329dcbaphk				l = 255;
413851c06bphk			while (!fifolog_write_record(f, id, now, p, l))
414851c06bphk				if (fifolog_write_gzip(f, now) < 0)
415851c06bphk					retval = -1;
416329dcbaphk		}
417329dcbaphk	}
418851c06bphk	if (fifolog_write_gzip(f, now) < 0)
419851c06bphk		retval = -1;
420329dcbaphk	fifolog_write_assert(f);
421851c06bphk	return (retval);
422329dcbaphk}
423