1176998Sphk/*-
2330449Seadler * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3330449Seadler *
4176998Sphk * Copyright (c) 2005-2008 Poul-Henning Kamp
5176998Sphk * All rights reserved.
6176998Sphk *
7176998Sphk * Redistribution and use in source and binary forms, with or without
8176998Sphk * modification, are permitted provided that the following conditions
9176998Sphk * are met:
10176998Sphk * 1. Redistributions of source code must retain the above copyright
11176998Sphk *    notice, this list of conditions and the following disclaimer.
12176998Sphk * 2. Redistributions in binary form must reproduce the above copyright
13176998Sphk *    notice, this list of conditions and the following disclaimer in the
14176998Sphk *    documentation and/or other materials provided with the distribution.
15176998Sphk *
16176998Sphk * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17176998Sphk * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18176998Sphk * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19176998Sphk * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20176998Sphk * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21176998Sphk * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22176998Sphk * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23176998Sphk * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24176998Sphk * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25176998Sphk * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26176998Sphk * SUCH DAMAGE.
27176998Sphk *
28176998Sphk * $FreeBSD: stable/11/usr.sbin/fifolog/lib/fifolog_write_poll.c 364921 2020-08-28 16:40:38Z gjb $
29176998Sphk */
30176998Sphk
31176998Sphk#include <assert.h>
32176998Sphk#include <stdio.h>
33176998Sphk#include <string.h>
34176998Sphk#include <stdlib.h>
35176998Sphk#include <unistd.h>
36219095Sphk#include <stdint.h>
37176998Sphk#include <time.h>
38176998Sphk#include <sys/endian.h>
39176998Sphk
40176998Sphk#include <zlib.h>
41176998Sphk
42176998Sphk#include "fifolog.h"
43176998Sphk#include "libfifolog_int.h"
44176998Sphk#include "fifolog_write.h"
45176998Sphk#include "miniobj.h"
46176998Sphk
47219095Sphkstatic int fifolog_write_gzip(struct fifolog_writer *f, time_t now);
48219095Sphk
49176998Sphk#define ALLOC(ptr, size) do {                   \
50306909Spfg	(*(ptr)) = calloc(1, size);             \
51176998Sphk	assert(*(ptr) != NULL);                 \
52176998Sphk} while (0)
53176998Sphk
54176998Sphk
55176998Sphkconst char *fifolog_write_statnames[] = {
56219027Sphk	[FIFOLOG_PT_BYTES_PRE] =	"Bytes before compression",
57219027Sphk	[FIFOLOG_PT_BYTES_POST] =	"Bytes after compression",
58219027Sphk	[FIFOLOG_PT_WRITES] =		"Writes",
59219027Sphk	[FIFOLOG_PT_FLUSH] =		"Flushes",
60219027Sphk	[FIFOLOG_PT_SYNC] =		"Syncs",
61219027Sphk	[FIFOLOG_PT_RUNTIME] =		"Runtime"
62176998Sphk};
63176998Sphk
64219095Sphk/**********************************************************************
65176998Sphk * Check that everything is all right
66176998Sphk */
67176998Sphkstatic void
68176998Sphkfifolog_write_assert(const struct fifolog_writer *f)
69176998Sphk{
70176998Sphk
71176998Sphk	CHECK_OBJ_NOTNULL(f, FIFOLOG_WRITER_MAGIC);
72176998Sphk	assert(f->ff->zs->next_out + f->ff->zs->avail_out == \
73216257Sphk	    f->obuf + f->obufsize);
74176998Sphk}
75176998Sphk
76219095Sphk/**********************************************************************
77219095Sphk * Allocate/Destroy a new fifolog writer instance
78219095Sphk */
79219095Sphk
80176998Sphkstruct fifolog_writer *
81176998Sphkfifolog_write_new(void)
82176998Sphk{
83176998Sphk	struct fifolog_writer *f;
84176998Sphk
85216257Sphk	ALLOC_OBJ(f, FIFOLOG_WRITER_MAGIC);
86216257Sphk	assert(f != NULL);
87176998Sphk	return (f);
88176998Sphk}
89176998Sphk
90176998Sphkvoid
91176998Sphkfifolog_write_destroy(struct fifolog_writer *f)
92176998Sphk{
93219095Sphk
94219095Sphk	free(f->obuf);
95219095Sphk	free(f->ibuf);
96219095Sphk	FREE_OBJ(f);
97176998Sphk}
98176998Sphk
99219095Sphk/**********************************************************************
100219095Sphk * Open/Close the fifolog
101219095Sphk */
102219095Sphk
103176998Sphkvoid
104176998Sphkfifolog_write_close(struct fifolog_writer *f)
105176998Sphk{
106219095Sphk	time_t now;
107176998Sphk
108176998Sphk	CHECK_OBJ_NOTNULL(f, FIFOLOG_WRITER_MAGIC);
109219095Sphk	fifolog_write_assert(f);
110219095Sphk
111219095Sphk	f->cleanup = 1;
112219095Sphk	time(&now);
113219095Sphk	fifolog_write_gzip(f, now);
114219095Sphk	fifolog_write_assert(f);
115176998Sphk	fifolog_int_close(&f->ff);
116177381Sphk	free(f->ff);
117176998Sphk}
118176998Sphk
119176998Sphkconst char *
120219095Sphkfifolog_write_open(struct fifolog_writer *f, const char *fn,
121219095Sphk    unsigned writerate, unsigned syncrate, unsigned compression)
122176998Sphk{
123176998Sphk	const char *es;
124176998Sphk	int i;
125176998Sphk	time_t now;
126176998Sphk	off_t o;
127176998Sphk
128176998Sphk	CHECK_OBJ_NOTNULL(f, FIFOLOG_WRITER_MAGIC);
129176998Sphk
130176998Sphk	/* Check for legal compression value */
131219095Sphk	if (compression > Z_BEST_COMPRESSION)
132176998Sphk		return ("Illegal compression value");
133176998Sphk
134176998Sphk	f->writerate = writerate;
135176998Sphk	f->syncrate = syncrate;
136176998Sphk	f->compression = compression;
137176998Sphk
138176998Sphk	/* Reset statistics */
139176998Sphk	memset(f->cnt, 0, sizeof f->cnt);
140176998Sphk
141176998Sphk	es = fifolog_int_open(&f->ff, fn, 1);
142176998Sphk	if (es != NULL)
143176998Sphk		return (es);
144176998Sphk	es = fifolog_int_findend(f->ff, &o);
145176998Sphk	if (es != NULL)
146176998Sphk		return (es);
147188007Sphk	i = fifolog_int_read(f->ff, o);
148188007Sphk	if (i)
149188007Sphk		return ("Read error, looking for seq");
150188007Sphk	f->seq = be32dec(f->ff->recbuf);
151188007Sphk	if (f->seq == 0) {
152188007Sphk		/* Empty fifolog */
153188007Sphk		f->seq = random();
154176998Sphk	} else {
155176998Sphk		f->recno = o + 1;
156188007Sphk		f->seq++;
157176998Sphk	}
158176998Sphk
159216257Sphk	f->obufsize = f->ff->recsize;
160216257Sphk	ALLOC(&f->obuf, f->obufsize);
161216257Sphk
162219095Sphk	f->ibufsize = f->obufsize * 10;
163219095Sphk	ALLOC(&f->ibuf, f->ibufsize);
164219095Sphk	f->ibufptr = 0;
165219095Sphk
166176998Sphk	i = deflateInit(f->ff->zs, (int)f->compression);
167176998Sphk	assert(i == Z_OK);
168176998Sphk
169176998Sphk	f->flag |= FIFOLOG_FLG_RESTART;
170216257Sphk	f->flag |= FIFOLOG_FLG_SYNC;
171216257Sphk	f->ff->zs->next_out = f->obuf + 9;
172216257Sphk	f->ff->zs->avail_out = f->obufsize - 9;
173176998Sphk
174176998Sphk	time(&now);
175176998Sphk	f->starttime = now;
176216257Sphk	f->lastsync = now;
177216257Sphk	f->lastwrite = now;
178176998Sphk
179176998Sphk	fifolog_write_assert(f);
180176998Sphk	return (NULL);
181176998Sphk}
182176998Sphk
183219095Sphk/**********************************************************************
184219095Sphk * Write an output record
185219095Sphk * Returns -1 if there are trouble writing data
186219095Sphk */
187219095Sphk
188216257Sphkstatic int
189216257Sphkfifolog_write_output(struct fifolog_writer *f, int fl, time_t now)
190176998Sphk{
191216257Sphk	long h, l = f->ff->zs->next_out - f->obuf;
192219095Sphk	ssize_t i, w;
193219095Sphk	int retval = 0;
194176998Sphk
195216257Sphk	h = 4;					/* seq */
196216257Sphk	be32enc(f->obuf, f->seq);
197216257Sphk	f->obuf[h] = f->flag;
198216257Sphk	h += 1;					/* flag */
199216257Sphk	if (f->flag & FIFOLOG_FLG_SYNC) {
200216257Sphk		be32enc(f->obuf + h, now);
201216257Sphk		h += 4;				/* timestamp */
202216257Sphk	}
203176998Sphk
204219095Sphk	assert(l <= (long)f->ff->recsize);	/* NB: l includes h */
205216257Sphk	assert(l >= h);
206219095Sphk
207219095Sphk	/* We will never write an entirely empty buffer */
208216257Sphk	if (l == h)
209216257Sphk		return (0);
210216257Sphk
211219095Sphk	if (l < (long)f->ff->recsize && fl == Z_NO_FLUSH)
212216257Sphk		return (0);
213216257Sphk
214216257Sphk	w = f->ff->recsize - l;
215216257Sphk	if (w >  255) {
216216257Sphk		be32enc(f->obuf + f->ff->recsize - 4, w);
217216257Sphk		f->obuf[4] |= FIFOLOG_FLG_4BYTE;
218216257Sphk	} else if (w > 0) {
219219095Sphk		f->obuf[f->ff->recsize - 1] = (uint8_t)w;
220216257Sphk		f->obuf[4] |= FIFOLOG_FLG_1BYTE;
221176998Sphk	}
222216257Sphk
223219095Sphk	f->cnt[FIFOLOG_PT_BYTES_POST] += l - h;
224216257Sphk
225216257Sphk	i = pwrite(f->ff->fd, f->obuf, f->ff->recsize,
226216257Sphk	    (f->recno + 1) * f->ff->recsize);
227219095Sphk	if (i != f->ff->recsize)
228219095Sphk		retval = -1;
229219095Sphk	else
230219095Sphk		retval = 1;
231216257Sphk
232176998Sphk	f->cnt[FIFOLOG_PT_WRITES]++;
233219095Sphk	f->cnt[FIFOLOG_PT_RUNTIME] = now - f->starttime;
234216257Sphk
235216257Sphk	f->lastwrite = now;
236219095Sphk	/*
237219095Sphk	 * We increment these even on error, so as to properly skip bad,
238219095Sphk	 * sectors or other light trouble.
239219095Sphk	 */
240216257Sphk	f->seq++;
241216257Sphk	f->recno++;
242364921Sgjb
243364921Sgjb	/*
244364921Sgjb	 * Ensure we wrap recno once we hit the file size (in records.)
245364921Sgjb	 */
246364921Sgjb	if (f->recno >= f->ff->logsize)
247364921Sgjb		/* recno 0 is header; skip */
248364921Sgjb		f->recno = 1;
249364921Sgjb
250216257Sphk	f->flag = 0;
251216257Sphk
252216257Sphk	memset(f->obuf, 0, f->obufsize);
253216257Sphk	f->ff->zs->next_out = f->obuf + 5;
254216257Sphk	f->ff->zs->avail_out = f->obufsize - 5;
255219095Sphk	return (retval);
256176998Sphk}
257176998Sphk
258219095Sphk/**********************************************************************
259219095Sphk * Run the compression engine
260219095Sphk * Returns -1 if there are trouble writing data
261219095Sphk */
262219095Sphk
263219095Sphkstatic int
264219095Sphkfifolog_write_gzip(struct fifolog_writer *f, time_t now)
265176998Sphk{
266219095Sphk	int i, fl, retval = 0;
267176998Sphk
268219095Sphk	assert(now != 0);
269219095Sphk	if (f->cleanup || now >= (int)(f->lastsync + f->syncrate)) {
270176998Sphk		f->cleanup = 0;
271176998Sphk		fl = Z_FINISH;
272176998Sphk		f->cnt[FIFOLOG_PT_SYNC]++;
273216257Sphk	} else if (now >= (int)(f->lastwrite + f->writerate)) {
274176998Sphk		fl = Z_SYNC_FLUSH;
275176998Sphk		f->cnt[FIFOLOG_PT_FLUSH]++;
276219095Sphk	} else if (f->ibufptr == 0)
277219095Sphk		return (0);
278176998Sphk	else
279176998Sphk		fl = Z_NO_FLUSH;
280176998Sphk
281219095Sphk	f->ff->zs->avail_in = f->ibufptr;
282219095Sphk	f->ff->zs->next_in = f->ibuf;
283176998Sphk
284216257Sphk	while (1) {
285176998Sphk		i = deflate(f->ff->zs, fl);
286216257Sphk		assert(i == Z_OK || i == Z_BUF_ERROR || i == Z_STREAM_END);
287216257Sphk
288219095Sphk		i = fifolog_write_output(f, fl, now);
289219095Sphk		if (i == 0)
290176998Sphk			break;
291219095Sphk		if (i < 0)
292219095Sphk			retval = -1;
293176998Sphk	}
294216257Sphk	assert(f->ff->zs->avail_in == 0);
295219095Sphk	f->ibufptr = 0;
296216257Sphk	if (fl == Z_FINISH) {
297216257Sphk		f->flag |= FIFOLOG_FLG_SYNC;
298216257Sphk		f->ff->zs->next_out = f->obuf + 9;
299216257Sphk		f->ff->zs->avail_out = f->obufsize - 9;
300216257Sphk		f->lastsync = now;
301216257Sphk		assert(Z_OK == deflateReset(f->ff->zs));
302176998Sphk	}
303219095Sphk	return (retval);
304176998Sphk}
305176998Sphk
306219095Sphk/**********************************************************************
307219095Sphk * Poll to see if we need to flush out a record
308219095Sphk * Returns -1 if there are trouble writing data
309219095Sphk */
310219095Sphk
311216257Sphkint
312216257Sphkfifolog_write_poll(struct fifolog_writer *f, time_t now)
313176998Sphk{
314219095Sphk
315216257Sphk	if (now == 0)
316216257Sphk		time(&now);
317219095Sphk	return (fifolog_write_gzip(f, now));
318176998Sphk}
319176998Sphk
320219095Sphk/**********************************************************************
321219095Sphk * Attempt to write an entry into the ibuf.
322176998Sphk * Return zero if there is no space, one otherwise
323176998Sphk */
324176998Sphk
325176998Sphkint
326219095Sphkfifolog_write_record(struct fifolog_writer *f, uint32_t id, time_t now,
327219095Sphk    const void *ptr, ssize_t len)
328176998Sphk{
329176998Sphk	const unsigned char *p;
330219095Sphk	uint8_t buf[9];
331219123Sphk	ssize_t bufl;
332176998Sphk
333176998Sphk	fifolog_write_assert(f);
334176998Sphk	assert(!(id & (FIFOLOG_TIMESTAMP|FIFOLOG_LENGTH)));
335176998Sphk	assert(ptr != NULL);
336176998Sphk
337177381Sphk	p = ptr;
338176998Sphk	if (len == 0) {
339219095Sphk		len = strlen(ptr);
340219095Sphk		len++;
341176998Sphk	} else {
342176998Sphk		assert(len <= 255);
343176998Sphk		id |= FIFOLOG_LENGTH;
344176998Sphk	}
345219095Sphk	assert (len > 0);
346176998Sphk
347219095Sphk	/* Do a timestamp, if needed */
348176998Sphk	if (now == 0)
349176998Sphk		time(&now);
350176998Sphk
351219095Sphk	if (now != f->last)
352176998Sphk		id |= FIFOLOG_TIMESTAMP;
353176998Sphk
354216257Sphk	/* Emit instance+flag */
355216257Sphk	be32enc(buf, id);
356219095Sphk	bufl = 4;
357176998Sphk
358176998Sphk	if (id & FIFOLOG_TIMESTAMP) {
359219095Sphk		be32enc(buf + bufl, (uint32_t)now);
360219095Sphk		bufl += 4;
361176998Sphk	}
362219095Sphk	if (id & FIFOLOG_LENGTH)
363219095Sphk		buf[bufl++] = (u_char)len;
364176998Sphk
365219095Sphk	if (bufl + len + f->ibufptr > f->ibufsize)
366219095Sphk		return (0);
367219095Sphk
368219095Sphk	memcpy(f->ibuf + f->ibufptr, buf, bufl);
369219095Sphk	f->ibufptr += bufl;
370219095Sphk	memcpy(f->ibuf + f->ibufptr, p, len);
371219095Sphk	f->ibufptr += len;
372219095Sphk	f->cnt[FIFOLOG_PT_BYTES_PRE] += bufl + len;
373219095Sphk
374219095Sphk	if (id & FIFOLOG_TIMESTAMP)
375219095Sphk		f->last = now;
376176998Sphk	return (1);
377176998Sphk}
378176998Sphk
379219095Sphk/**********************************************************************
380219095Sphk * Write an entry, polling the gzip/writer until success.
381176998Sphk * Long binary entries are broken into 255 byte chunks.
382219095Sphk * Returns -1 if there are problems writing data
383176998Sphk */
384176998Sphk
385219095Sphkint
386219095Sphkfifolog_write_record_poll(struct fifolog_writer *f, uint32_t id, time_t now,
387219095Sphk    const void *ptr, ssize_t len)
388176998Sphk{
389176998Sphk	u_int l;
390176998Sphk	const unsigned char *p;
391219095Sphk	int retval = 0;
392176998Sphk
393219095Sphk	if (now == 0)
394219095Sphk		time(&now);
395176998Sphk	fifolog_write_assert(f);
396176998Sphk
397176998Sphk	assert(!(id & (FIFOLOG_TIMESTAMP|FIFOLOG_LENGTH)));
398176998Sphk	assert(ptr != NULL);
399176998Sphk
400176998Sphk	if (len == 0) {
401219095Sphk		if (!fifolog_write_record(f, id, now, ptr, len)) {
402219095Sphk			if (fifolog_write_gzip(f, now) < 0)
403219095Sphk				retval = -1;
404219095Sphk			/* The string could be too long for the ibuf, so... */
405219095Sphk			if (!fifolog_write_record(f, id, now, ptr, len))
406219095Sphk				retval = -1;
407176998Sphk		}
408176998Sphk	} else {
409176998Sphk		for (p = ptr; len > 0; len -= l, p += l) {
410176998Sphk			l = len;
411176998Sphk			if (l > 255)
412176998Sphk				l = 255;
413219095Sphk			while (!fifolog_write_record(f, id, now, p, l))
414219095Sphk				if (fifolog_write_gzip(f, now) < 0)
415219095Sphk					retval = -1;
416176998Sphk		}
417176998Sphk	}
418219095Sphk	if (fifolog_write_gzip(f, now) < 0)
419219095Sphk		retval = -1;
420176998Sphk	fifolog_write_assert(f);
421219095Sphk	return (retval);
422176998Sphk}
423