]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/fifolog/lib/fifolog_write_poll.c
Import tzdata 2020a
[FreeBSD/FreeBSD.git] / usr.sbin / fifolog / lib / fifolog_write_poll.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2005-2008 Poul-Henning Kamp
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  * $FreeBSD$
29  */
30
31 #include <assert.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <stdint.h>
37 #include <time.h>
38 #include <sys/endian.h>
39
40 #include <zlib.h>
41
42 #include "fifolog.h"
43 #include "libfifolog_int.h"
44 #include "fifolog_write.h"
45 #include "miniobj.h"
46
47 static int fifolog_write_gzip(struct fifolog_writer *f, time_t now);
48
49 #define ALLOC(ptr, size) do {                   \
50         (*(ptr)) = calloc(1, size);             \
51         assert(*(ptr) != NULL);                 \
52 } while (0)
53
54
55 const char *fifolog_write_statnames[] = {
56         [FIFOLOG_PT_BYTES_PRE] =        "Bytes before compression",
57         [FIFOLOG_PT_BYTES_POST] =       "Bytes after compression",
58         [FIFOLOG_PT_WRITES] =           "Writes",
59         [FIFOLOG_PT_FLUSH] =            "Flushes",
60         [FIFOLOG_PT_SYNC] =             "Syncs",
61         [FIFOLOG_PT_RUNTIME] =          "Runtime"
62 };
63
64 /**********************************************************************
65  * Check that everything is all right
66  */
67 static void
68 fifolog_write_assert(const struct fifolog_writer *f)
69 {
70
71         CHECK_OBJ_NOTNULL(f, FIFOLOG_WRITER_MAGIC);
72         assert(f->ff->zs->next_out + f->ff->zs->avail_out == \
73             f->obuf + f->obufsize);
74 }
75
76 /**********************************************************************
77  * Allocate/Destroy a new fifolog writer instance
78  */
79
80 struct fifolog_writer *
81 fifolog_write_new(void)
82 {
83         struct fifolog_writer *f;
84
85         ALLOC_OBJ(f, FIFOLOG_WRITER_MAGIC);
86         assert(f != NULL);
87         return (f);
88 }
89
90 void
91 fifolog_write_destroy(struct fifolog_writer *f)
92 {
93
94         free(f->obuf);
95         free(f->ibuf);
96         FREE_OBJ(f);
97 }
98
99 /**********************************************************************
100  * Open/Close the fifolog
101  */
102
103 void
104 fifolog_write_close(struct fifolog_writer *f)
105 {
106         time_t now;
107
108         CHECK_OBJ_NOTNULL(f, FIFOLOG_WRITER_MAGIC);
109         fifolog_write_assert(f);
110
111         f->cleanup = 1;
112         time(&now);
113         fifolog_write_gzip(f, now);
114         fifolog_write_assert(f);
115         fifolog_int_close(&f->ff);
116         free(f->ff);
117 }
118
119 const char *
120 fifolog_write_open(struct fifolog_writer *f, const char *fn,
121     unsigned writerate, unsigned syncrate, unsigned compression)
122 {
123         const char *es;
124         int i;
125         time_t now;
126         off_t o;
127
128         CHECK_OBJ_NOTNULL(f, FIFOLOG_WRITER_MAGIC);
129
130         /* Check for legal compression value */
131         if (compression > Z_BEST_COMPRESSION)
132                 return ("Illegal compression value");
133
134         f->writerate = writerate;
135         f->syncrate = syncrate;
136         f->compression = compression;
137
138         /* Reset statistics */
139         memset(f->cnt, 0, sizeof f->cnt);
140
141         es = fifolog_int_open(&f->ff, fn, 1);
142         if (es != NULL)
143                 return (es);
144         es = fifolog_int_findend(f->ff, &o);
145         if (es != NULL)
146                 return (es);
147         i = fifolog_int_read(f->ff, o);
148         if (i)
149                 return ("Read error, looking for seq");
150         f->seq = be32dec(f->ff->recbuf);
151         if (f->seq == 0) {
152                 /* Empty fifolog */
153                 f->seq = random();
154         } else {
155                 f->recno = o + 1;
156                 f->seq++;
157         }
158
159         f->obufsize = f->ff->recsize;
160         ALLOC(&f->obuf, f->obufsize);
161
162         f->ibufsize = f->obufsize * 10;
163         ALLOC(&f->ibuf, f->ibufsize);
164         f->ibufptr = 0;
165
166         i = deflateInit(f->ff->zs, (int)f->compression);
167         assert(i == Z_OK);
168
169         f->flag |= FIFOLOG_FLG_RESTART;
170         f->flag |= FIFOLOG_FLG_SYNC;
171         f->ff->zs->next_out = f->obuf + 9;
172         f->ff->zs->avail_out = f->obufsize - 9;
173
174         time(&now);
175         f->starttime = now;
176         f->lastsync = now;
177         f->lastwrite = now;
178
179         fifolog_write_assert(f);
180         return (NULL);
181 }
182
183 /**********************************************************************
184  * Write an output record
185  * Returns -1 if there are trouble writing data
186  */
187
188 static int
189 fifolog_write_output(struct fifolog_writer *f, int fl, time_t now)
190 {
191         long h, l = f->ff->zs->next_out - f->obuf;
192         ssize_t i, w;
193         int retval = 0;
194
195         h = 4;                                  /* seq */
196         be32enc(f->obuf, f->seq);
197         f->obuf[h] = f->flag;
198         h += 1;                                 /* flag */
199         if (f->flag & FIFOLOG_FLG_SYNC) {
200                 be32enc(f->obuf + h, now);
201                 h += 4;                         /* timestamp */
202         }
203
204         assert(l <= (long)f->ff->recsize);      /* NB: l includes h */
205         assert(l >= h);
206
207         /* We will never write an entirely empty buffer */
208         if (l == h)
209                 return (0);
210
211         if (l < (long)f->ff->recsize && fl == Z_NO_FLUSH)
212                 return (0);
213
214         w = f->ff->recsize - l;
215         if (w >  255) {
216                 be32enc(f->obuf + f->ff->recsize - 4, w);
217                 f->obuf[4] |= FIFOLOG_FLG_4BYTE;
218         } else if (w > 0) {
219                 f->obuf[f->ff->recsize - 1] = (uint8_t)w;
220                 f->obuf[4] |= FIFOLOG_FLG_1BYTE;
221         }
222
223         f->cnt[FIFOLOG_PT_BYTES_POST] += l - h;
224
225         i = pwrite(f->ff->fd, f->obuf, f->ff->recsize,
226             (f->recno + 1) * f->ff->recsize);
227         if (i != f->ff->recsize)
228                 retval = -1;
229         else
230                 retval = 1;
231
232         f->cnt[FIFOLOG_PT_WRITES]++;
233         f->cnt[FIFOLOG_PT_RUNTIME] = now - f->starttime;
234
235         f->lastwrite = now;
236         /*
237          * We increment these even on error, so as to properly skip bad,
238          * sectors or other light trouble.
239          */
240         f->seq++;
241         f->recno++;
242         f->flag = 0;
243
244         memset(f->obuf, 0, f->obufsize);
245         f->ff->zs->next_out = f->obuf + 5;
246         f->ff->zs->avail_out = f->obufsize - 5;
247         return (retval);
248 }
249
250 /**********************************************************************
251  * Run the compression engine
252  * Returns -1 if there are trouble writing data
253  */
254
255 static int
256 fifolog_write_gzip(struct fifolog_writer *f, time_t now)
257 {
258         int i, fl, retval = 0;
259
260         assert(now != 0);
261         if (f->cleanup || now >= (int)(f->lastsync + f->syncrate)) {
262                 f->cleanup = 0;
263                 fl = Z_FINISH;
264                 f->cnt[FIFOLOG_PT_SYNC]++;
265         } else if (now >= (int)(f->lastwrite + f->writerate)) {
266                 fl = Z_SYNC_FLUSH;
267                 f->cnt[FIFOLOG_PT_FLUSH]++;
268         } else if (f->ibufptr == 0)
269                 return (0);
270         else
271                 fl = Z_NO_FLUSH;
272
273         f->ff->zs->avail_in = f->ibufptr;
274         f->ff->zs->next_in = f->ibuf;
275
276         while (1) {
277                 i = deflate(f->ff->zs, fl);
278                 assert(i == Z_OK || i == Z_BUF_ERROR || i == Z_STREAM_END);
279
280                 i = fifolog_write_output(f, fl, now);
281                 if (i == 0)
282                         break;
283                 if (i < 0)
284                         retval = -1;
285         }
286         assert(f->ff->zs->avail_in == 0);
287         f->ibufptr = 0;
288         if (fl == Z_FINISH) {
289                 f->flag |= FIFOLOG_FLG_SYNC;
290                 f->ff->zs->next_out = f->obuf + 9;
291                 f->ff->zs->avail_out = f->obufsize - 9;
292                 f->lastsync = now;
293                 assert(Z_OK == deflateReset(f->ff->zs));
294         }
295         return (retval);
296 }
297
298 /**********************************************************************
299  * Poll to see if we need to flush out a record
300  * Returns -1 if there are trouble writing data
301  */
302
303 int
304 fifolog_write_poll(struct fifolog_writer *f, time_t now)
305 {
306
307         if (now == 0)
308                 time(&now);
309         return (fifolog_write_gzip(f, now));
310 }
311
312 /**********************************************************************
313  * Attempt to write an entry into the ibuf.
314  * Return zero if there is no space, one otherwise
315  */
316
317 int
318 fifolog_write_record(struct fifolog_writer *f, uint32_t id, time_t now,
319     const void *ptr, ssize_t len)
320 {
321         const unsigned char *p;
322         uint8_t buf[9];
323         ssize_t bufl;
324
325         fifolog_write_assert(f);
326         assert(!(id & (FIFOLOG_TIMESTAMP|FIFOLOG_LENGTH)));
327         assert(ptr != NULL);
328
329         p = ptr;
330         if (len == 0) {
331                 len = strlen(ptr);
332                 len++;
333         } else {
334                 assert(len <= 255);
335                 id |= FIFOLOG_LENGTH;
336         }
337         assert (len > 0);
338
339         /* Do a timestamp, if needed */
340         if (now == 0)
341                 time(&now);
342
343         if (now != f->last)
344                 id |= FIFOLOG_TIMESTAMP;
345
346         /* Emit instance+flag */
347         be32enc(buf, id);
348         bufl = 4;
349
350         if (id & FIFOLOG_TIMESTAMP) {
351                 be32enc(buf + bufl, (uint32_t)now);
352                 bufl += 4;
353         }
354         if (id & FIFOLOG_LENGTH)
355                 buf[bufl++] = (u_char)len;
356
357         if (bufl + len + f->ibufptr > f->ibufsize)
358                 return (0);
359
360         memcpy(f->ibuf + f->ibufptr, buf, bufl);
361         f->ibufptr += bufl;
362         memcpy(f->ibuf + f->ibufptr, p, len);
363         f->ibufptr += len;
364         f->cnt[FIFOLOG_PT_BYTES_PRE] += bufl + len;
365
366         if (id & FIFOLOG_TIMESTAMP)
367                 f->last = now;
368         return (1);
369 }
370
371 /**********************************************************************
372  * Write an entry, polling the gzip/writer until success.
373  * Long binary entries are broken into 255 byte chunks.
374  * Returns -1 if there are problems writing data
375  */
376
377 int
378 fifolog_write_record_poll(struct fifolog_writer *f, uint32_t id, time_t now,
379     const void *ptr, ssize_t len)
380 {
381         u_int l;
382         const unsigned char *p;
383         int retval = 0;
384
385         if (now == 0)
386                 time(&now);
387         fifolog_write_assert(f);
388
389         assert(!(id & (FIFOLOG_TIMESTAMP|FIFOLOG_LENGTH)));
390         assert(ptr != NULL);
391
392         if (len == 0) {
393                 if (!fifolog_write_record(f, id, now, ptr, len)) {
394                         if (fifolog_write_gzip(f, now) < 0)
395                                 retval = -1;
396                         /* The string could be too long for the ibuf, so... */
397                         if (!fifolog_write_record(f, id, now, ptr, len))
398                                 retval = -1;
399                 }
400         } else {
401                 for (p = ptr; len > 0; len -= l, p += l) {
402                         l = len;
403                         if (l > 255)
404                                 l = 255;
405                         while (!fifolog_write_record(f, id, now, p, l))
406                                 if (fifolog_write_gzip(f, now) < 0)
407                                         retval = -1;
408                 }
409         }
410         if (fifolog_write_gzip(f, now) < 0)
411                 retval = -1;
412         fifolog_write_assert(f);
413         return (retval);
414 }