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 (%d) while looking for SYNC", e); 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 (%d) while looking for SYNC", e); 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 s++; 159 e = fifolog_reader_findsync(fr->ff, &s); 160 if (e == 0) 161 return (0); /* empty fifolog */ 162 if (e == 1) { 163 o = s; 164 seq = be32dec(fr->ff->recbuf); 165 t = be32dec(fr->ff->recbuf + 5); 166 } 167 } 168 169 /* Now do a binary search to find the sync block right before t0 */ 170 s = st = (fr->ff->logsize - o) / 2; 171 while (s > 1) { 172 /* We know we shouldn't wrap */ 173 if (o + st > fr->ff->logsize + 1) { 174 s = st = s / 2; 175 continue; 176 } 177 e = fifolog_int_read(fr->ff, o + st); 178 if (e) { 179 s = st = s / 2; 180 continue; 181 } 182 /* If not in same part, sequence won't match */ 183 seqs = be32dec(fr->ff->recbuf); 184 if (seqs != seq + st) { 185 s = st = s / 2; 186 continue; 187 } 188 /* If not sync block, try next */ 189 if (!(fr->ff->recbuf[4] & FIFOLOG_FLG_SYNC)) { 190 st++; 191 continue; 192 } 193 /* Check timestamp */ 194 tt = be32dec(fr->ff->recbuf + 5); 195 if (tt >= t0) { 196 s = st = s / 2; 197 continue; 198 } 199 o += st; 200 seq = seqs; 201 } 202 fprintf(stderr, "Read from %jx\n", o * fr->ff->recsize); 203 return (o); 204 } 205 206 static unsigned char * 207 fifolog_reader_chop(struct fifolog_reader *fr, fifolog_reader_render_t *func, void *priv) 208 { 209 u_char *p, *q; 210 uint32_t v, w, u; 211 212 p = fr->obuf; 213 q = fr->obuf + (fr->olen - fr->ff->zs->avail_out); 214 215 while (1) { 216 /* Make sure we have a complete header */ 217 if (p + 5 >= q) 218 return (p); 219 w = 4; 220 u = be32dec(p); 221 if (u & FIFOLOG_TIMESTAMP) { 222 fr->now = be32dec(p + 4); 223 w += 4; 224 } 225 if (u & FIFOLOG_LENGTH) { 226 v = p[w]; 227 w++; 228 } else { 229 for (v = 0; p + v + w < q && p[v + w] != '\0'; v++) 230 continue; 231 if (p + v + w >= q) 232 return (p); 233 v++; 234 } 235 func(priv, fr->now, u, p + w, v); 236 p += w + v; 237 } 238 } 239 240 /* 241 * Process fifolog until end of written log or provided timestamp 242 */ 243 244 void 245 fifolog_reader_process(struct fifolog_reader *fr, off_t from, fifolog_reader_render_t *func, void *priv, time_t end) 246 { 247 uint32_t seq, lseq; 248 off_t o = from; 249 int i, e; 250 time_t t; 251 u_char *p, *q; 252 z_stream *zs; 253 254 CHECK_OBJ_NOTNULL(fr, FIFOLOG_READER_MAGIC); 255 zs = fr->ff->zs; 256 lseq = 0; 257 while (1) { 258 e = fifolog_int_read(fr->ff, o); 259 if (e) 260 err(1, "Read error (%d)", e); 261 if (++o >= fr->ff->logsize) 262 o = 0; 263 seq = be32dec(fr->ff->recbuf); 264 if (lseq != 0 && seq != lseq + 1) 265 break; 266 lseq = seq; 267 zs->avail_in = fr->ff->recsize - 5; 268 zs->next_in = fr->ff->recbuf + 5; 269 if (fr->ff->recbuf[4] & FIFOLOG_FLG_1BYTE) 270 zs->avail_in -= fr->ff->recbuf[fr->ff->recsize - 1]; 271 if (fr->ff->recbuf[4] & FIFOLOG_FLG_4BYTE) 272 zs->avail_in -= 273 be32dec(fr->ff->recbuf + fr->ff->recsize - 4); 274 if (fr->ff->recbuf[4] & FIFOLOG_FLG_SYNC) { 275 i = inflateReset(zs); 276 assert(i == Z_OK); 277 zs->next_out = fr->obuf; 278 zs->avail_out = fr->olen; 279 t = be32dec(fr->ff->recbuf + 5); 280 if (t > end) 281 break; 282 zs->next_in += 4; 283 zs->avail_in -= 4; 284 } 285 286 while(zs->avail_in > 0) { 287 i = inflate(zs, 0); 288 if (i == Z_BUF_ERROR) { 289 #if 1 290 fprintf(stderr, 291 "Z_BUF_ERROR [%d,%d] [%d,%d,%d]\n", 292 (int)(zs->next_in - fr->ff->recbuf), 293 zs->avail_in, 294 (int)(zs->next_out - fr->obuf), 295 zs->avail_out, fr->olen); 296 exit (250); 297 #else 298 299 i = Z_OK; 300 #endif 301 } 302 if (i == Z_STREAM_END) { 303 i = inflateReset(zs); 304 } 305 if (i != Z_OK) 306 fprintf(stderr, "inflate = %d\n", i); 307 assert(i == Z_OK); 308 if (zs->avail_out != fr->olen) { 309 q = fr->obuf + (fr->olen - zs->avail_out); 310 p = fifolog_reader_chop(fr, func, priv); 311 if (p < q) 312 (void)memmove(fr->obuf, p, q - p); 313 zs->avail_out = fr->olen - (q - p); 314 zs->next_out = fr->obuf + (q - p); 315 } 316 } 317 } 318 } 319