1 /*- 2 * Copyright (c) 2005-2008 Poul-Henning Kamp 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 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 f->flag = 0; 241 242 memset(f->obuf, 0, f->obufsize); 243 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 253 static int 254 fifolog_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 301 int 302 fifolog_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 315 int 316 fifolog_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 375 int 376 fifolog_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