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 <stdio.h> 30 #include <unistd.h> 31 #include <assert.h> 32 #include <err.h> 33 #include <time.h> 34 #include <string.h> 35 #include <stdlib.h> 36 #include <zlib.h> 37 #include <sys/endian.h> 38 39 #include "fifolog.h" 40 #include "libfifolog.h" 41 #include "libfifolog_int.h" 42 #include "miniobj.h" 43 44 /*--------------------------------------------------------------------*/ 45 46 struct fifolog_reader { 47 unsigned magic; 48 #define FIFOLOG_READER_MAGIC 0x1036d139 49 struct fifolog_file *ff; 50 unsigned olen; 51 unsigned char *obuf; 52 time_t now; 53 }; 54 55 struct fifolog_reader * 56 fifolog_reader_open(const char *fname) 57 { 58 const char *retval; 59 struct fifolog_reader *fr; 60 int i; 61 62 fr = calloc(sizeof *fr, 1); 63 if (fr == NULL) 64 err(1, "Cannot malloc"); 65 66 retval = fifolog_int_open(&fr->ff, fname, 0); 67 if (retval != NULL) 68 err(1, "%s", retval); 69 70 fr->olen = fr->ff->recsize * 16; 71 fr->obuf = calloc(fr->olen, 1); 72 if (fr->obuf == NULL) 73 err(1, "Cannot malloc"); 74 75 i = inflateInit(fr->ff->zs); 76 assert(i == Z_OK); 77 78 fr->magic = FIFOLOG_READER_MAGIC; 79 return (fr); 80 } 81 82 /* 83 * Find the next SYNC block 84 * 85 * Return: 86 * 0 - empty fifolog 87 * 1 - found sync block 88 * 2 - would have wrapped around 89 * 3 - End of written log. 90 */ 91 92 static int 93 fifolog_reader_findsync(const struct fifolog_file *ff, off_t *o) 94 { 95 int e; 96 unsigned seq, seqs; 97 98 assert(*o < ff->logsize); 99 e = fifolog_int_read(ff, *o); 100 if (e) 101 err(1, "Read error while looking for SYNC"); 102 seq = be32dec(ff->recbuf); 103 if (*o == 0 && seq == 0) 104 return (0); 105 106 if (ff->recbuf[4] & FIFOLOG_FLG_SYNC) 107 return (1); /* That was easy... */ 108 while(1) { 109 assert(*o < ff->logsize); 110 (*o)++; 111 seq++; 112 if (*o == ff->logsize) 113 return (2); /* wraparound */ 114 e = fifolog_int_read(ff, *o); 115 if (e) 116 err(1, "Read error while looking for SYNC"); 117 seqs = be32dec(ff->recbuf); 118 if (seqs != seq) 119 return (3); /* End of log */ 120 if (ff->recbuf[4] & FIFOLOG_FLG_SYNC) 121 return (1); /* Bingo! */ 122 } 123 } 124 125 /* 126 * Seek out a given timestamp 127 */ 128 129 off_t 130 fifolog_reader_seek(const struct fifolog_reader *fr, time_t t0) 131 { 132 off_t o, s, st; 133 time_t t, tt; 134 unsigned seq, seqs; 135 const char *retval; 136 int e; 137 138 CHECK_OBJ_NOTNULL(fr, FIFOLOG_READER_MAGIC); 139 140 /* 141 * First, find the first SYNC block 142 */ 143 o = 0; 144 e = fifolog_reader_findsync(fr->ff, &o); 145 if (e == 0) 146 return (0); /* empty fifolog */ 147 assert(e == 1); 148 149 assert(fr->ff->recbuf[4] & FIFOLOG_FLG_SYNC); 150 seq = be32dec(fr->ff->recbuf); 151 t = be32dec(fr->ff->recbuf + 5); 152 153 if (t > t0) { 154 /* Check if there is a second older part we can use */ 155 retval = fifolog_int_findend(fr->ff, &s); 156 if (retval != NULL) 157 err(1, "%s", retval); 158 e = fifolog_reader_findsync(fr->ff, &s); 159 if (e == 0) 160 return (0); /* empty fifolog */ 161 if (e == 1) { 162 o = s; 163 seq = be32dec(fr->ff->recbuf); 164 t = be32dec(fr->ff->recbuf + 5); 165 } 166 } 167 168 /* Now do a binary search to find the sync block right before t0 */ 169 s = st = (fr->ff->logsize - o) / 2; 170 while (s > 1) { 171 /* We know we shouldn't wrap */ 172 if (o + st > fr->ff->logsize + 1) { 173 s = st = s / 2; 174 continue; 175 } 176 e = fifolog_int_read(fr->ff, o + st); 177 if (e) 178 err(1, "Read error, duing binary search"); 179 /* If not in same part, sequence won't match */ 180 seqs = be32dec(fr->ff->recbuf); 181 if (seqs != seq + st) { 182 s = st = s / 2; 183 continue; 184 } 185 /* If not sync block, try next */ 186 if (!(fr->ff->recbuf[4] & FIFOLOG_FLG_SYNC)) { 187 st++; 188 continue; 189 } 190 /* Check timestamp */ 191 tt = be32dec(fr->ff->recbuf + 5); 192 if (tt >= t0) { 193 s = st = s / 2; 194 continue; 195 } 196 o += st; 197 seq = seqs; 198 } 199 fprintf(stderr, "Read from %jx\n", o * fr->ff->recsize); 200 return (o); 201 } 202 203 static unsigned char * 204 fifolog_reader_chop(struct fifolog_reader *fr, fifolog_reader_render_t *func, void *priv) 205 { 206 u_char *p, *q; 207 uint32_t v, w, u; 208 209 p = fr->obuf; 210 q = fr->obuf + (fr->olen - fr->ff->zs->avail_out); 211 212 while (1) { 213 /* Make sure we have a complete header */ 214 if (p + 5 >= q) 215 return (p); 216 w = 4; 217 u = be32dec(p); 218 if (u & FIFOLOG_TIMESTAMP) { 219 fr->now = be32dec(p + 4); 220 w += 4; 221 } 222 if (u & FIFOLOG_LENGTH) { 223 v = p[w]; 224 w++; 225 } else { 226 for (v = 0; p + v + w < q && p[v + w] != '\0'; v++) 227 continue; 228 if (p + v + w >= q) 229 return (p); 230 v++; 231 } 232 func(priv, fr->now, u, p + w, v); 233 p += w + v; 234 } 235 } 236 237 /* 238 * Process fifolog until end of written log or provided timestamp 239 */ 240 241 void 242 fifolog_reader_process(struct fifolog_reader *fr, off_t from, fifolog_reader_render_t *func, void *priv, time_t end) 243 { 244 uint32_t seq, lseq; 245 off_t o = from; 246 int i, e; 247 time_t t; 248 u_char *p, *q; 249 z_stream *zs; 250 251 CHECK_OBJ_NOTNULL(fr, FIFOLOG_READER_MAGIC); 252 zs = fr->ff->zs; 253 lseq = 0; 254 while (1) { 255 e = fifolog_int_read(fr->ff, o); 256 if (e) 257 err(1, "Read error"); 258 if (++o >= fr->ff->logsize) 259 o = 0; 260 seq = be32dec(fr->ff->recbuf); 261 if (lseq != 0 && seq != lseq + 1) 262 break; 263 lseq = seq; 264 zs->avail_in = fr->ff->recsize - 5; 265 zs->next_in = fr->ff->recbuf + 5; 266 if (fr->ff->recbuf[4] & FIFOLOG_FLG_1BYTE) 267 zs->avail_in -= fr->ff->recbuf[fr->ff->recsize - 1]; 268 if (fr->ff->recbuf[4] & FIFOLOG_FLG_4BYTE) 269 zs->avail_in -= 270 be32dec(fr->ff->recbuf + fr->ff->recsize - 4); 271 if (fr->ff->recbuf[4] & FIFOLOG_FLG_SYNC) { 272 i = inflateReset(zs); 273 assert(i == Z_OK); 274 zs->next_out = fr->obuf; 275 zs->avail_out = fr->olen; 276 t = be32dec(fr->ff->recbuf + 5); 277 if (t > end) 278 break; 279 zs->next_in += 4; 280 zs->avail_in -= 4; 281 } 282 283 while(zs->avail_in > 0) { 284 i = inflate(zs, 0); 285 if (i == Z_BUF_ERROR) { 286 #if 1 287 fprintf(stderr, 288 "Z_BUF_ERROR [%d,%d] [%d,%d,%d]\n", 289 (int)(zs->next_in - fr->ff->recbuf), 290 zs->avail_in, 291 (int)(zs->next_out - fr->obuf), 292 zs->avail_out, fr->olen); 293 exit (250); 294 #else 295 296 i = Z_OK; 297 #endif 298 } 299 if (i == Z_STREAM_END) { 300 i = inflateReset(zs); 301 } 302 if (i != Z_OK) 303 fprintf(stderr, "inflate = %d\n", i); 304 assert(i == Z_OK); 305 if (zs->avail_out != fr->olen) { 306 q = fr->obuf + (fr->olen - zs->avail_out); 307 p = fifolog_reader_chop(fr, func, priv); 308 if (p < q) 309 (void)memmove(fr->obuf, p, q - p); 310 zs->avail_out = fr->olen - (q - p); 311 zs->next_out = fr->obuf + (q - p); 312 } 313 } 314 } 315 } 316