1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 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 29 #include <assert.h> 30 #include <stdio.h> 31 #include <string.h> 32 #include <stdlib.h> 33 #include <unistd.h> 34 #include <stdint.h> 35 #include <time.h> 36 #include <sys/endian.h> 37 38 #include <zlib.h> 39 40 #include "fifolog.h" 41 #include "libfifolog_int.h" 42 #include "fifolog_write.h" 43 #include "miniobj.h" 44 45 static int fifolog_write_gzip(struct fifolog_writer *f, time_t now); 46 47 #define ALLOC(ptr, size) do { \ 48 (*(ptr)) = calloc(1, size); \ 49 assert(*(ptr) != NULL); \ 50 } while (0) 51 52 53 const char *fifolog_write_statnames[] = { 54 [FIFOLOG_PT_BYTES_PRE] = "Bytes before compression", 55 [FIFOLOG_PT_BYTES_POST] = "Bytes after compression", 56 [FIFOLOG_PT_WRITES] = "Writes", 57 [FIFOLOG_PT_FLUSH] = "Flushes", 58 [FIFOLOG_PT_SYNC] = "Syncs", 59 [FIFOLOG_PT_RUNTIME] = "Runtime" 60 }; 61 62 /********************************************************************** 63 * Check that everything is all right 64 */ 65 static void 66 fifolog_write_assert(const struct fifolog_writer *f) 67 { 68 69 CHECK_OBJ_NOTNULL(f, FIFOLOG_WRITER_MAGIC); 70 assert(f->ff->zs->next_out + f->ff->zs->avail_out == \ 71 f->obuf + f->obufsize); 72 } 73 74 /********************************************************************** 75 * Allocate/Destroy a new fifolog writer instance 76 */ 77 78 struct fifolog_writer * 79 fifolog_write_new(void) 80 { 81 struct fifolog_writer *f; 82 83 ALLOC_OBJ(f, FIFOLOG_WRITER_MAGIC); 84 assert(f != NULL); 85 return (f); 86 } 87 88 void 89 fifolog_write_destroy(struct fifolog_writer *f) 90 { 91 92 free(f->obuf); 93 free(f->ibuf); 94 FREE_OBJ(f); 95 } 96 97 /********************************************************************** 98 * Open/Close the fifolog 99 */ 100 101 void 102 fifolog_write_close(struct fifolog_writer *f) 103 { 104 time_t now; 105 106 CHECK_OBJ_NOTNULL(f, FIFOLOG_WRITER_MAGIC); 107 fifolog_write_assert(f); 108 109 f->cleanup = 1; 110 time(&now); 111 fifolog_write_gzip(f, now); 112 fifolog_write_assert(f); 113 fifolog_int_close(&f->ff); 114 free(f->ff); 115 } 116 117 const char * 118 fifolog_write_open(struct fifolog_writer *f, const char *fn, 119 unsigned writerate, unsigned syncrate, unsigned compression) 120 { 121 const char *es; 122 int i; 123 time_t now; 124 off_t o; 125 126 CHECK_OBJ_NOTNULL(f, FIFOLOG_WRITER_MAGIC); 127 128 /* Check for legal compression value */ 129 if (compression > Z_BEST_COMPRESSION) 130 return ("Illegal compression value"); 131 132 f->writerate = writerate; 133 f->syncrate = syncrate; 134 f->compression = compression; 135 136 /* Reset statistics */ 137 memset(f->cnt, 0, sizeof f->cnt); 138 139 es = fifolog_int_open(&f->ff, fn, 1); 140 if (es != NULL) 141 return (es); 142 es = fifolog_int_findend(f->ff, &o); 143 if (es != NULL) 144 return (es); 145 i = fifolog_int_read(f->ff, o); 146 if (i) 147 return ("Read error, looking for seq"); 148 f->seq = be32dec(f->ff->recbuf); 149 if (f->seq == 0) { 150 /* Empty fifolog */ 151 f->seq = random(); 152 } else { 153 f->recno = o + 1; 154 f->seq++; 155 } 156 157 f->obufsize = f->ff->recsize; 158 ALLOC(&f->obuf, f->obufsize); 159 160 f->ibufsize = f->obufsize * 10; 161 ALLOC(&f->ibuf, f->ibufsize); 162 f->ibufptr = 0; 163 164 i = deflateInit(f->ff->zs, (int)f->compression); 165 assert(i == Z_OK); 166 167 f->flag |= FIFOLOG_FLG_RESTART; 168 f->flag |= FIFOLOG_FLG_SYNC; 169 f->ff->zs->next_out = f->obuf + 9; 170 f->ff->zs->avail_out = f->obufsize - 9; 171 172 time(&now); 173 f->starttime = now; 174 f->lastsync = now; 175 f->lastwrite = now; 176 177 fifolog_write_assert(f); 178 return (NULL); 179 } 180 181 /********************************************************************** 182 * Write an output record 183 * Returns -1 if there are trouble writing data 184 */ 185 186 static int 187 fifolog_write_output(struct fifolog_writer *f, int fl, time_t now) 188 { 189 long h, l = f->ff->zs->next_out - f->obuf; 190 ssize_t i, w; 191 int retval = 0; 192 193 h = 4; /* seq */ 194 be32enc(f->obuf, f->seq); 195 f->obuf[h] = f->flag; 196 h += 1; /* flag */ 197 if (f->flag & FIFOLOG_FLG_SYNC) { 198 be32enc(f->obuf + h, now); 199 h += 4; /* timestamp */ 200 } 201 202 assert(l <= (long)f->ff->recsize); /* NB: l includes h */ 203 assert(l >= h); 204 205 /* We will never write an entirely empty buffer */ 206 if (l == h) 207 return (0); 208 209 if (l < (long)f->ff->recsize && fl == Z_NO_FLUSH) 210 return (0); 211 212 w = f->ff->recsize - l; 213 if (w > 255) { 214 be32enc(f->obuf + f->ff->recsize - 4, w); 215 f->obuf[4] |= FIFOLOG_FLG_4BYTE; 216 } else if (w > 0) { 217 f->obuf[f->ff->recsize - 1] = (uint8_t)w; 218 f->obuf[4] |= FIFOLOG_FLG_1BYTE; 219 } 220 221 f->cnt[FIFOLOG_PT_BYTES_POST] += l - h; 222 223 i = pwrite(f->ff->fd, f->obuf, f->ff->recsize, 224 (f->recno + 1) * f->ff->recsize); 225 if (i != f->ff->recsize) 226 retval = -1; 227 else 228 retval = 1; 229 230 f->cnt[FIFOLOG_PT_WRITES]++; 231 f->cnt[FIFOLOG_PT_RUNTIME] = now - f->starttime; 232 233 f->lastwrite = now; 234 /* 235 * We increment these even on error, so as to properly skip bad, 236 * sectors or other light trouble. 237 */ 238 f->seq++; 239 f->recno++; 240 241 /* 242 * Ensure we wrap recno once we hit the file size (in records.) 243 */ 244 if (f->recno >= f->ff->logsize) 245 /* recno 0 is header; skip */ 246 f->recno = 1; 247 248 f->flag = 0; 249 250 memset(f->obuf, 0, f->obufsize); 251 f->ff->zs->next_out = f->obuf + 5; 252 f->ff->zs->avail_out = f->obufsize - 5; 253 return (retval); 254 } 255 256 /********************************************************************** 257 * Run the compression engine 258 * Returns -1 if there are trouble writing data 259 */ 260 261 static int 262 fifolog_write_gzip(struct fifolog_writer *f, time_t now) 263 { 264 int i, fl, retval = 0; 265 266 assert(now != 0); 267 if (f->cleanup || now >= (int)(f->lastsync + f->syncrate)) { 268 f->cleanup = 0; 269 fl = Z_FINISH; 270 f->cnt[FIFOLOG_PT_SYNC]++; 271 } else if (now >= (int)(f->lastwrite + f->writerate)) { 272 fl = Z_SYNC_FLUSH; 273 f->cnt[FIFOLOG_PT_FLUSH]++; 274 } else if (f->ibufptr == 0) 275 return (0); 276 else 277 fl = Z_NO_FLUSH; 278 279 f->ff->zs->avail_in = f->ibufptr; 280 f->ff->zs->next_in = f->ibuf; 281 282 while (1) { 283 i = deflate(f->ff->zs, fl); 284 assert(i == Z_OK || i == Z_BUF_ERROR || i == Z_STREAM_END); 285 286 i = fifolog_write_output(f, fl, now); 287 if (i == 0) 288 break; 289 if (i < 0) 290 retval = -1; 291 } 292 assert(f->ff->zs->avail_in == 0); 293 f->ibufptr = 0; 294 if (fl == Z_FINISH) { 295 f->flag |= FIFOLOG_FLG_SYNC; 296 f->ff->zs->next_out = f->obuf + 9; 297 f->ff->zs->avail_out = f->obufsize - 9; 298 f->lastsync = now; 299 assert(Z_OK == deflateReset(f->ff->zs)); 300 } 301 return (retval); 302 } 303 304 /********************************************************************** 305 * Poll to see if we need to flush out a record 306 * Returns -1 if there are trouble writing data 307 */ 308 309 int 310 fifolog_write_poll(struct fifolog_writer *f, time_t now) 311 { 312 313 if (now == 0) 314 time(&now); 315 return (fifolog_write_gzip(f, now)); 316 } 317 318 /********************************************************************** 319 * Attempt to write an entry into the ibuf. 320 * Return zero if there is no space, one otherwise 321 */ 322 323 int 324 fifolog_write_record(struct fifolog_writer *f, uint32_t id, time_t now, 325 const void *ptr, ssize_t len) 326 { 327 const unsigned char *p; 328 uint8_t buf[9]; 329 ssize_t bufl; 330 331 fifolog_write_assert(f); 332 assert(!(id & (FIFOLOG_TIMESTAMP|FIFOLOG_LENGTH))); 333 assert(ptr != NULL); 334 335 p = ptr; 336 if (len == 0) { 337 len = strlen(ptr); 338 len++; 339 } else { 340 assert(len <= 255); 341 id |= FIFOLOG_LENGTH; 342 } 343 assert (len > 0); 344 345 /* Do a timestamp, if needed */ 346 if (now == 0) 347 time(&now); 348 349 if (now != f->last) 350 id |= FIFOLOG_TIMESTAMP; 351 352 /* Emit instance+flag */ 353 be32enc(buf, id); 354 bufl = 4; 355 356 if (id & FIFOLOG_TIMESTAMP) { 357 be32enc(buf + bufl, (uint32_t)now); 358 bufl += 4; 359 } 360 if (id & FIFOLOG_LENGTH) 361 buf[bufl++] = (u_char)len; 362 363 if (bufl + len + f->ibufptr > f->ibufsize) 364 return (0); 365 366 memcpy(f->ibuf + f->ibufptr, buf, bufl); 367 f->ibufptr += bufl; 368 memcpy(f->ibuf + f->ibufptr, p, len); 369 f->ibufptr += len; 370 f->cnt[FIFOLOG_PT_BYTES_PRE] += bufl + len; 371 372 if (id & FIFOLOG_TIMESTAMP) 373 f->last = now; 374 return (1); 375 } 376 377 /********************************************************************** 378 * Write an entry, polling the gzip/writer until success. 379 * Long binary entries are broken into 255 byte chunks. 380 * Returns -1 if there are problems writing data 381 */ 382 383 int 384 fifolog_write_record_poll(struct fifolog_writer *f, uint32_t id, time_t now, 385 const void *ptr, ssize_t len) 386 { 387 u_int l; 388 const unsigned char *p; 389 int retval = 0; 390 391 if (now == 0) 392 time(&now); 393 fifolog_write_assert(f); 394 395 assert(!(id & (FIFOLOG_TIMESTAMP|FIFOLOG_LENGTH))); 396 assert(ptr != NULL); 397 398 if (len == 0) { 399 if (!fifolog_write_record(f, id, now, ptr, len)) { 400 if (fifolog_write_gzip(f, now) < 0) 401 retval = -1; 402 /* The string could be too long for the ibuf, so... */ 403 if (!fifolog_write_record(f, id, now, ptr, len)) 404 retval = -1; 405 } 406 } else { 407 for (p = ptr; len > 0; len -= l, p += l) { 408 l = len; 409 if (l > 255) 410 l = 255; 411 while (!fifolog_write_record(f, id, now, p, l)) 412 if (fifolog_write_gzip(f, now) < 0) 413 retval = -1; 414 } 415 } 416 if (fifolog_write_gzip(f, now) < 0) 417 retval = -1; 418 fifolog_write_assert(f); 419 return (retval); 420 } 421