timed tail for logfiles. Display loglines given a minimum date and/or a maximum date.
c
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ttail_search_files.c 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059
  1. /*
  2. * Copyright 2017 Yann Weber
  3. *
  4. * This file is part of Ttail.
  5. *
  6. * Ttail is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * any later version.
  10. *
  11. * Ttail is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with Ttail. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include "ttail_search_files.h"
  20. int _ttail_search_closest_files(ttail_t* t)
  21. {
  22. int ret;
  23. size_t i;
  24. struct tm **ftm; /* an array with t->logfile_sz struct tm[2] */
  25. /* Storing min & max of each files in ftm and checking that files are
  26. * sorted well */
  27. ftm = malloc(sizeof(struct tm*) * t->logfile_sz);
  28. if(!ftm)
  29. {
  30. perror("Unable to allocate memory");
  31. goto _ttail_search_closest_files_alloc_err;
  32. }
  33. ret = 0; /* to avoid may be used uninitialized error */
  34. for(i=0; i<t->logfile_sz; i++)
  35. {
  36. if(!t->logfile[i])
  37. {
  38. ftm[i] = NULL;
  39. continue;
  40. }
  41. ftm[i] = malloc(sizeof(struct tm)*2);
  42. if(!ftm[i])
  43. {
  44. perror("Unable to allocate memory for time");
  45. goto _ttail_search_closest_files_alloc_loop_err;
  46. }
  47. ret = _ttail_file_minmax(t, i, ftm[i]);
  48. if(ret < 0)
  49. {
  50. goto _ttail_search_closest_files_loop_err;
  51. }
  52. else if (ret == 1)
  53. {
  54. fprintf(stderr, "Error : unable to find a valid date \
  55. in '%s'\n", t->logfile_name[i]);
  56. free(ftm[i]);
  57. ftm[i] = NULL;
  58. fclose(t->logfile[i]);
  59. t->logfile[i] = NULL;
  60. if(!ttail_permissive(t))
  61. {
  62. ttail_strict_msg();
  63. goto _ttail_search_closest_files_loop_err;
  64. }
  65. }
  66. }
  67. if(_ttail_file_sort(t, ftm) < 0)
  68. {
  69. goto _ttail_search_closest_files_err;
  70. }
  71. if(t->flag & TTAIL_FLAG_DATE_MIN)
  72. {
  73. ret = _ttail_search_files_binary_search(t, &(t->date_min),
  74. (const struct tm**)ftm, 1);
  75. if(ret)
  76. {
  77. if(t->verbose > 2)
  78. {
  79. fprintf(stderr, "Error while looking for \
  80. date-max\n");
  81. }
  82. goto _ttail_search_closest_files_err;
  83. }
  84. }
  85. for(i=0; i<t->logfile_sz; i++)
  86. {
  87. if(!t->logfile[i])
  88. {
  89. continue;
  90. }
  91. if(fseek(t->logfile[i], 0, SEEK_SET) < 0)
  92. {
  93. perror("Error setting position in file");
  94. goto _ttail_search_closest_files_err;
  95. }
  96. }
  97. if(t->flag & TTAIL_FLAG_DATE_MAX)
  98. {
  99. ret = _ttail_search_files_binary_search(t, &(t->date_max),
  100. (const struct tm**)ftm, 0);
  101. if(ret)
  102. {
  103. if(t->verbose > 2)
  104. {
  105. fprintf(stderr, "Error while looking for \
  106. date-max\n");
  107. }
  108. goto _ttail_search_closest_files_err;
  109. }
  110. t->session->file.off_max.off++;
  111. }
  112. for(i=0; i<t->logfile_sz; i++)
  113. {
  114. if(ftm) { free(ftm[i]); }
  115. }
  116. free(ftm);
  117. return ret;
  118. goto _ttail_search_closest_files_err;
  119. _ttail_search_closest_files_err:
  120. i = t->logfile_sz;
  121. do
  122. {
  123. _ttail_search_closest_files_alloc_loop_err:
  124. i--;
  125. _ttail_search_closest_files_loop_err:
  126. if(ftm[i])
  127. {
  128. free(ftm[i]);
  129. }
  130. }while(i);
  131. free(ftm);
  132. _ttail_search_closest_files_alloc_err:
  133. return -1;
  134. }
  135. void _ttail_search_print_files(ttail_t* t, int out)
  136. {
  137. size_t i;
  138. int fd;
  139. char buf[8192];
  140. int r;
  141. off_t cur_off;
  142. /* if no date_min t->session->file.id == 0 */
  143. for(i=t->session->file.off_min.id; i<t->logfile_sz; i++)
  144. {
  145. if(!t->logfile[i])
  146. {
  147. continue;
  148. }
  149. fd = fileno(t->logfile[i]);
  150. if(t->flag & TTAIL_FLAG_DATE_MIN &&
  151. i==t->session->file.off_min.id)
  152. {
  153. if(t->flag & TTAIL_FLAG_DATE_MAX)
  154. {
  155. fseek(t->logfile[i],
  156. t->session->file.off_min.off,
  157. SEEK_SET);
  158. }
  159. else
  160. {
  161. lseek(fd, t->session->file.off_min.off,
  162. SEEK_SET);
  163. }
  164. }
  165. else
  166. {
  167. if(t->flag & TTAIL_FLAG_DATE_MAX)
  168. {
  169. fseek(t->logfile[i], 0, SEEK_SET);
  170. }
  171. else
  172. {
  173. lseek(fd, 0, SEEK_SET);
  174. }
  175. }
  176. cur_off = 0;
  177. while(1)
  178. {
  179. if(t->flag & TTAIL_FLAG_DATE_MAX)
  180. {
  181. if(i >= t->session->file.off_max.id &&
  182. cur_off >= t->session->file.off_max.off)
  183. {
  184. return;
  185. }
  186. if((r = ttail_file_getline(t, i)) < 0)
  187. {
  188. break;
  189. }
  190. cur_off += r;
  191. write(out, ttail_file_getline_buf(t),
  192. strlen(ttail_file_getline_buf(t)));
  193. }
  194. else
  195. {
  196. r = read(fd, buf, sizeof(buf));
  197. if(r == -1)
  198. {
  199. perror("unable to read file");
  200. return;
  201. }
  202. else if(r == 0)
  203. {
  204. break;
  205. }
  206. cur_off += r;
  207. r = write(out, buf, r);
  208. if(r == -1 || r == 0)
  209. {
  210. perror("Unable to write result");
  211. return;
  212. }
  213. }
  214. }
  215. }
  216. }
  217. int _ttail_search_files_binary_search(ttail_t* t, const struct tm* in,
  218. const struct tm** ftm, short min)
  219. {
  220. int cmin, cmax, ret;
  221. size_t valid;
  222. int cmpres;
  223. off_t tmpoff;
  224. ttail_files_off_t *files_off;
  225. size_t *id;
  226. off_t *off;
  227. files_off = min?&(t->session->file.off_min):&(t->session->file.off_max);
  228. id = &(files_off->id),
  229. off = &(files_off->off);
  230. *id = *off = 0;
  231. valid = 0;
  232. while(*id < t->logfile_sz)
  233. {
  234. if(!ftm[*id])
  235. {
  236. (*id)++;
  237. continue;
  238. }
  239. valid++;
  240. cmin = ttail_tm_cmp(&(ftm[*id][0]), in);
  241. cmax = ttail_tm_cmp(&(ftm[*id][1]), in);
  242. if(!cmin)
  243. {
  244. /* found at the begining of the file */
  245. return 0;
  246. }
  247. else if (cmax == 0)
  248. {
  249. /* found at EOF */
  250. if(min)
  251. {
  252. *off = _ttail_file_search_from_end(t, *id, in);
  253. if(*off < 0)
  254. {
  255. *off = 0;
  256. return -1;
  257. }
  258. }
  259. else
  260. {
  261. *off = t->session->file.file_sz[*id];
  262. }
  263. return 0;
  264. }
  265. else if(cmin > 0)
  266. {
  267. /* found at start of file */
  268. off = 0;
  269. return 0;
  270. }
  271. else if(cmax > 0)
  272. {
  273. /* somewhere in current file */
  274. ret = _ttail_search_file_binary_search(t, in, ftm, min);
  275. if(ret)
  276. {
  277. if(t->verbose > 2)
  278. {
  279. fprintf(stderr, "Error while \
  280. running binary search algorithm in '%s'\n", t->logfile_name[*id]);
  281. }
  282. *id = 0;
  283. return ret;
  284. }
  285. /* searching for equivalent */
  286. tmpoff = *off;
  287. while(1)
  288. {
  289. if(fseek(t->logfile[*id], tmpoff, SEEK_SET) < 0)
  290. {
  291. return -1;
  292. }
  293. tmpoff = min?
  294. _ttail_file_start_line(t, *id):
  295. _ttail_file_next_line(t, *id);
  296. if(tmpoff < 0)
  297. {
  298. break;
  299. }
  300. cmpres=0;
  301. ret = _ttail_file_cur_cmp(t, *id, in, &cmpres);
  302. if(ret > 0 && !ttail_permissive(t))
  303. {
  304. ttail_strict_msg();
  305. return -1;
  306. }
  307. if((min && cmpres < 0) || (!min && cmpres > 0))
  308. {
  309. break;
  310. }
  311. *off = tmpoff;
  312. if(min)
  313. {
  314. tmpoff--;
  315. }
  316. }
  317. return 0;
  318. }
  319. else if(*id == t->logfile_sz - 1 && cmax < 0)
  320. {
  321. *off = t->session->file.file_sz[*id];
  322. return 0;
  323. }
  324. (*id)++;
  325. }
  326. if(!valid)
  327. {
  328. fprintf(stderr, "No files to scan\n");
  329. return 0;
  330. }
  331. if(*id == t->logfile_sz)
  332. {
  333. return 0;
  334. }
  335. /* the answer is somewhere in *id file */
  336. *off = _ttail_file_search_from_end(t, *id, in);
  337. return 0;
  338. }
  339. inline int _ttail_search_file_binary_search(ttail_t* t, const struct tm* in,
  340. const struct tm** ftm, short min)
  341. {
  342. off_t cur, sz, d, prev, tmp;
  343. int ret, cmpres;
  344. ttail_files_off_t *files_off;
  345. size_t id;
  346. off_t *off;
  347. files_off = min?&(t->session->file.off_min):&(t->session->file.off_max);
  348. id = files_off->id,
  349. off = &(files_off->off);
  350. sz = t->session->file.file_sz[id];
  351. d = cur = sz / 2;
  352. prev = 0;
  353. cmpres = 0;
  354. while(1)
  355. {
  356. if(fseek(t->logfile[id], cur, SEEK_SET) < 0)
  357. {
  358. perror("Unable to move in the file");
  359. return -1;
  360. }
  361. if(cur > prev)
  362. {
  363. cur = _ttail_file_next_line(t, id);
  364. if(cur < 0)
  365. {
  366. /* not sure errno is really set */
  367. perror("Error searching previous line");
  368. return -1;
  369. }
  370. }
  371. else
  372. {
  373. cur = _ttail_file_start_line(t, id);
  374. if(cur < 0)
  375. {
  376. /* not sure errno is really set */
  377. perror("Error searching previous line");
  378. return -1;
  379. }
  380. }
  381. if(cur == prev)
  382. {
  383. *off = cur;
  384. return 0;
  385. }
  386. prev = cur;
  387. ret = _ttail_file_cur_cmp(t, id, in, &cmpres);
  388. if(ret < 0)
  389. {
  390. if(t->verbose > 2)
  391. {
  392. fprintf(stderr, "Error comparing a logline \
  393. to a date directly from a file\n");
  394. }
  395. return -1;
  396. }
  397. else if(cmpres == 0)
  398. {
  399. *off = cur;
  400. break;
  401. }
  402. else if(cmpres < 0)
  403. {
  404. tmp = _ttail_file_next_line(t,id);
  405. ret = _ttail_file_cur_cmp(t, id, in, &cmpres);
  406. if(cmpres >=0)
  407. {
  408. *off = tmp;
  409. break;
  410. }
  411. d/=2;
  412. cur += d;
  413. cur %= t->session->file.file_sz[id];
  414. }
  415. else
  416. {
  417. d/=2;
  418. cur -= d;
  419. if(cur < 0)
  420. {
  421. cur = 0;
  422. }
  423. }
  424. }
  425. return 0;
  426. }
  427. int ttail_search_files_init(ttail_t* t)
  428. {
  429. struct stat stat;
  430. FILE *fp;
  431. int fd;
  432. size_t i;
  433. off_t *file_sz;
  434. t->session = (ttail_search_t*)malloc(sizeof(ttail_search_file_t));
  435. if(!t->session)
  436. {
  437. perror("Unable to allocate memory for search session");
  438. goto _ttail_search_closest_files_alloc_session_err;
  439. }
  440. memset(t->session, 0, sizeof(ttail_search_file_t));
  441. file_sz = malloc(sizeof(off_t)*t->logfile_sz);
  442. if(!file_sz)
  443. {
  444. perror("Unable to allocate memory for file sizes");
  445. goto _ttail_search_closest_files_alloc_err;
  446. }
  447. t->session->file.file_sz = file_sz;
  448. #ifdef HUGEFILE
  449. t->session->file.sz_div = 0;
  450. #endif
  451. for(i=0;i<t->logfile_sz;i++)
  452. {
  453. fp = t->logfile[i];
  454. if(!fp)
  455. {
  456. file_sz[i] = 0;
  457. continue;
  458. }
  459. if((fd = fileno(fp)) < 0)
  460. {
  461. perror("Unable to get fp");
  462. goto _ttail_search_closest_files_err;
  463. }
  464. if(fstat(fileno(fp), &stat))
  465. {
  466. perror("Unable to get file size");
  467. goto _ttail_search_closest_files_err;
  468. }
  469. file_sz[i] = stat.st_size;
  470. }
  471. /* we got all real size, determining if we need a divisor */
  472. /*
  473. * not implemented
  474. */
  475. if(_ttail_search_closest_files_set_fsizes(t))
  476. {
  477. goto _ttail_search_closest_files_err;
  478. }
  479. t->session->file.buf_sz = 128;
  480. t->session->file.buf = malloc(t->session->file.buf_sz);
  481. if(!t->session->file.buf)
  482. {
  483. goto _ttail_search_closest_files_err;
  484. }
  485. if(_ttail_search_files_fmt_init(t) < 0)
  486. {
  487. goto _ttail_search_closest_files_err;
  488. }
  489. return 0;
  490. _ttail_search_closest_files_err:
  491. free(file_sz);
  492. t->session->file.file_sz = NULL;
  493. _ttail_search_closest_files_alloc_err:
  494. free(t->session);
  495. _ttail_search_closest_files_alloc_session_err:
  496. return -1;
  497. }
  498. int _ttail_search_files_fmt_init(ttail_t* t)
  499. {
  500. int fmt_id;
  501. size_t i,j;
  502. const char *buff;
  503. const char *fmt[] = TTAIL_DEFAULT_FORMATS;
  504. if(t->flag & TTAIL_FLAG_FORMAT)
  505. {
  506. return 1;
  507. }
  508. fmt_id = -1;
  509. for(i=0; i<t->logfile_sz; i++)
  510. {
  511. if(!t->logfile[i]) {
  512. continue;
  513. }
  514. for(j=0; j<10; j++)
  515. {
  516. //try to guess fmt on the 10 first lines
  517. if(ttail_file_getline(t, i) < 0) {
  518. break;
  519. }
  520. buff = ttail_logline_subst(t, ttail_file_getline_buf(t));
  521. if(!buff)
  522. {
  523. if(ttail_permissive(t))
  524. {
  525. continue;
  526. }
  527. fprintf(stderr,
  528. "Unable to find prefix in '%s' logline",
  529. t->logfile_name[i]);
  530. if(t->verbose > 0)
  531. {
  532. fprintf(stderr, " : '%s'",
  533. ttail_file_getline_buf(t));
  534. }
  535. fprintf(stderr, "\n");
  536. return -1;
  537. }
  538. //buff contains the lines starting by the date
  539. fmt_id = ttail_format_guess(buff, NULL);
  540. if(fmt_id >= 0)
  541. {
  542. break;
  543. }
  544. if(!ttail_permissive(t))
  545. {
  546. ttail_strict_msg();
  547. break;
  548. }
  549. }
  550. rewind(t->logfile[i]);
  551. if(fmt_id >= 0)
  552. {
  553. if(t->verbose > 0)
  554. {
  555. fprintf(stderr,
  556. "Detected format %s in file %s\n",
  557. fmt[fmt_id], t->logfile_name[i]);
  558. }
  559. break;
  560. }
  561. }
  562. if(fmt_id >= 0)
  563. {
  564. buff = fmt[fmt_id];
  565. t->fmt = malloc(sizeof(char) * (strlen(buff)+1));
  566. if(!t->fmt)
  567. {
  568. perror("Unable to allocate memory for date format");
  569. return -1;
  570. }
  571. strcpy(t->fmt, buff);
  572. t->flag |= TTAIL_FLAG_FORMAT;
  573. return 0;
  574. }
  575. fprintf(stderr, "Unable to detect date format from logfiles\n");
  576. return -1;
  577. }
  578. int _ttail_search_closest_files_set_fsizes(ttail_t* t)
  579. {
  580. size_t i;
  581. off_t *vfile, *vsz;
  582. vfile = malloc(sizeof(off_t)*t->logfile_sz);
  583. if(!vfile)
  584. {
  585. perror("Unable to allocate memory for file size sum");
  586. return -1;
  587. }
  588. t->session->file.vfile = vfile;
  589. vsz = &(t->session->file.vsz);
  590. *vsz = 0;
  591. for(i=0; i<t->logfile_sz;i++)
  592. {
  593. vfile[i] = *vsz;
  594. #ifdef HUGEFILE
  595. *vsz += t->session->file.file_sz[i] >> t->session->file.sz_div;
  596. #else
  597. *vsz += t->session->file.file_sz[i];
  598. #endif
  599. }
  600. t->session->file.vpos = 0;
  601. return 0;
  602. }
  603. inline int _ttail_file_minmax(ttail_t* t, size_t id, struct tm tm[2])
  604. {
  605. FILE *fp;
  606. long cur;
  607. int ret;
  608. memset(tm, 0, sizeof(struct tm)*2);
  609. fp = t->logfile[id];
  610. if(!fp)
  611. {
  612. fprintf(stderr, "File pointer is null !\n");
  613. return 1;
  614. }
  615. if(fseek(fp, 0, SEEK_SET) < 0)
  616. {
  617. perror("Unable to manipulate fp");
  618. return -1;
  619. }
  620. while(1)
  621. {
  622. if(ttail_file_getline(t, id) < 0)
  623. {
  624. return 1;
  625. }
  626. if(!(ret = ttail_logline2date(t, ttail_file_getline_buf(t), tm)))
  627. {
  628. break;
  629. }
  630. if(!ttail_permissive(t))
  631. {
  632. fprintf(stderr,
  633. "Unable to find %s in logline",
  634. ret == 1?"prefix":"date");
  635. if(t->verbose > 0)
  636. {
  637. fprintf(stderr, " : '%s'\n",
  638. ttail_file_getline_buf(t));
  639. }
  640. else
  641. {
  642. fprintf(stderr,"\n");
  643. }
  644. ttail_strict_msg();
  645. return -1;
  646. }
  647. }
  648. if(fseek(fp, -1, SEEK_END) < 0)
  649. {
  650. perror("Unable to manipulate fp");
  651. return -1;
  652. }
  653. while(1)
  654. {
  655. if((cur = _ttail_file_start_line(t, id)) < 0)
  656. {
  657. fprintf(stderr, "Error will searching line starts in\
  658. %s\n", t->logfile_name[id]);
  659. return -1;
  660. }
  661. if(ttail_file_getline(t, id) < 0)
  662. {
  663. return 1;
  664. }
  665. if(!ttail_logline2date(t, ttail_file_getline_buf(t), tm+1))
  666. {
  667. break;
  668. }
  669. if(!cur)
  670. {
  671. return 1;
  672. }
  673. else if(!ttail_permissive(t))
  674. {
  675. if(t->verbose <= 0)
  676. {
  677. fprintf(stderr,
  678. "Unable to find a date in logline\n");
  679. }
  680. else
  681. {
  682. fprintf(stderr,
  683. "Unable to find a date in '%s'\n",
  684. ttail_file_getline_buf(t));
  685. }
  686. ttail_strict_msg();
  687. return -1;
  688. }
  689. if(fseek(fp, cur-1, SEEK_SET) < 0)
  690. {
  691. perror("Unable to manipulate fp");
  692. return -1;
  693. }
  694. }
  695. return 0;
  696. }
  697. int _ttail_file_reopen(ttail_t* t, size_t id)
  698. {
  699. if(t->logfile[id])
  700. {
  701. fclose(t->logfile[id]);
  702. }
  703. t->logfile[id] = fopen(t->logfile_name[id], "r");
  704. if(!t->logfile[id] && t->verbose > 2)
  705. {
  706. fprintf(stderr, "Unable to reopen '%s'\n",
  707. t->logfile_name[id]);
  708. }
  709. return t->logfile[id]?0:-1;
  710. }
  711. inline int _ttail_file_sort(ttail_t* t, struct tm** ftm)
  712. {
  713. size_t i, j, count;
  714. short sorted, warn;
  715. count = t->logfile_sz;
  716. i=0;
  717. warn = 0;
  718. for(i=0;i<count;i++)
  719. {
  720. while(!ftm[i] && i<count)
  721. {
  722. //move invalid dates to the end
  723. count--;
  724. TTAIL_MVF(t->logfile_name, i, count, char*);
  725. TTAIL_MVF(t->logfile, i, count, FILE*);
  726. TTAIL_MVF(t->session->file.file_sz, i, count,
  727. off_t);
  728. TTAIL_MVF(t->session->file.vfile, i, count,
  729. off_t);
  730. TTAIL_MVF(ftm, i, count, struct tm*);
  731. }
  732. if(!i ||i == count|| ttail_tm_cmp(&(ftm[i-1][1]), &(ftm[i][0])) < 0)
  733. {
  734. continue;
  735. }
  736. sorted=0;
  737. for(j=0; j<i;j++)
  738. {
  739. if(ttail_tm_cmp(&(ftm[i][1]), &(ftm[j][0])) < 0
  740. || (j > 0 && ttail_tm_cmp(&(ftm[i][0]), &(ftm[j-1][1])) > 0))
  741. {
  742. TTAIL_MVBF(t->logfile_name, i, j, char*);
  743. TTAIL_MVBF(t->logfile, i, j, FILE*);
  744. TTAIL_MVBF(t->session->file.file_sz, i, j,
  745. off_t);
  746. TTAIL_MVBF(t->session->file.vfile, i, j,
  747. off_t);
  748. TTAIL_MVBF(ftm, i, j, struct tm*);
  749. sorted=1;
  750. if(!warn && t->verbose > 1)
  751. {
  752. fprintf(stderr, "Warning : Files list \
  753. given as argument has to be sorted.\n");
  754. }
  755. break;
  756. }
  757. if((ttail_tm_cmp(&(ftm[i][0]), &(ftm[j][0])) < 0
  758. && ttail_tm_cmp(&(ftm[i][1]), &(ftm[j][0])) > 0) ||
  759. (j > 0 &&(
  760. ttail_tm_cmp(&(ftm[i][1]), &(ftm[j][1])) > 0 &&
  761. ttail_tm_cmp(&(ftm[i][0]), &ftm[j][1]) < 0)))
  762. {
  763. fprintf(stderr, "Files list not sortable : \
  764. file %s and %s overlaps.\n", t->logfile_name[i], t->logfile_name[j]);
  765. fprintf(stderr,"\
  766. meaning file1 date min < file2 date min < file1 date max \n\
  767. or file1 date min < file2 date max < file1 date max\n");
  768. return -1;
  769. }
  770. }
  771. if(sorted) { continue; }
  772. fprintf(stderr,"Files list not sortable : file %s contains \
  773. or is inside another file\n", t->logfile_name[i]);
  774. fprintf(stderr,"meaning that filex1 date min < file2 date min \
  775. and file1 date max > file2 date max\n");
  776. return -1;
  777. }
  778. return 0;
  779. }
  780. inline long _ttail_file_next_line(ttail_t *t, size_t id)
  781. {
  782. FILE *f;
  783. ssize_t s;
  784. size_t r;
  785. char *buff;
  786. long res;
  787. int c;
  788. f = t->logfile[id];
  789. r=0;
  790. buff = NULL;
  791. s = getline(&buff, &r, f);
  792. if(s < 0)
  793. {
  794. goto _ttail_file_next_line_err;
  795. }
  796. while(1)
  797. {
  798. c = getc(f);
  799. if(c == EOF)
  800. {
  801. return 0;
  802. }
  803. else if(c!='\n')
  804. {
  805. if(fseek(f, -1, SEEK_CUR)<0)
  806. {
  807. perror("Unable to set position in file");
  808. goto _ttail_file_next_line_err;
  809. }
  810. break;
  811. }
  812. }
  813. res = ftell(f);
  814. free(buff);
  815. return res;
  816. _ttail_file_next_line_err:
  817. free(buff);
  818. return -1;
  819. }
  820. inline long _ttail_file_start_line(ttail_t *ttail, size_t id)
  821. {
  822. #define _STARTLN_BUFFLEN 32
  823. FILE *f;
  824. long res; /* function result */
  825. long read_beg, cur, last, start;
  826. int read_sz;
  827. int c;
  828. f = ttail->logfile[id];
  829. if((start = ftell(f)) < 0)
  830. {
  831. perror("Unable to get position in file");
  832. return -1;
  833. }
  834. res = 0;
  835. read_beg = start;
  836. while(!res && start)
  837. {
  838. if(fseek(f, read_beg, SEEK_SET) < 0)
  839. {
  840. perror("Unable to set position in file");
  841. return -1;
  842. }
  843. start = read_beg;
  844. read_sz = start <= _STARTLN_BUFFLEN?start:_STARTLN_BUFFLEN;
  845. read_beg = start - read_sz;
  846. if(fseek(f, read_beg, SEEK_SET) < 0)
  847. {
  848. perror("Unable to set position in file");
  849. return -1;
  850. }
  851. last = -1; /* last pos we saw a '\n' */
  852. cur = read_beg;
  853. while(cur <= start)
  854. {
  855. c = getc(f);
  856. if(c == EOF)
  857. {
  858. if(!res)
  859. {
  860. return 0;
  861. }
  862. break;
  863. }
  864. else if (c =='\n')
  865. {
  866. last = cur;
  867. }
  868. else if(last >= 0)
  869. {
  870. res = cur;
  871. last = -1;
  872. }
  873. cur++;
  874. }
  875. if(!read_beg)
  876. {
  877. break;
  878. }
  879. }
  880. if(fseek(f, res, SEEK_SET) < 0)
  881. {
  882. perror("Unable to set position in file");
  883. return -1;
  884. }
  885. return res;
  886. }
  887. inline off_t _ttail_file_search_from_end(ttail_t* t , size_t id ,
  888. const struct tm* tm)
  889. {
  890. FILE *f;
  891. struct tm curtm;
  892. off_t last;
  893. int ret, cmpret;
  894. off_t result;
  895. result = -1;
  896. f = t->logfile[id];
  897. if(fseek(f, -1, SEEK_END) < 0)
  898. {
  899. return -1;
  900. }
  901. while(1)
  902. {
  903. last = _ttail_file_start_line(t, id);
  904. if(last < 0)
  905. {
  906. break;
  907. }
  908. if(ttail_file_getline(t, id) < 0)
  909. {
  910. break;
  911. }
  912. ret = ttail_logline2date(t, ttail_file_getline_buf(t), &curtm);
  913. if(ret < 0)
  914. {
  915. break;
  916. }
  917. else if(ret > 0 && !ttail_permissive(t))
  918. {
  919. fprintf(stderr, "Unable to find the %s in logline",
  920. ret==1?"prefix":"date");
  921. if(t->verbose > 0)
  922. {
  923. fprintf(stderr, " : '%s'\n",
  924. ttail_file_getline_buf(t));
  925. }
  926. else
  927. {
  928. fprintf(stderr, "\n");
  929. }
  930. ttail_strict_msg();
  931. break;
  932. }
  933. cmpret = ttail_tm_cmp(&curtm, tm);
  934. if(!ret)
  935. {
  936. if(cmpret >= 0)
  937. {
  938. /* found but continue to search the first one*/
  939. result = last;
  940. }
  941. else
  942. {
  943. if(cmpret < 0 && result < 0)
  944. {
  945. result = last;
  946. }
  947. break;
  948. }
  949. }
  950. if(last == 0)
  951. {
  952. /* considere the begining of the file as the answer */
  953. return 0;
  954. }
  955. if(fseek(f, last-1, SEEK_SET))
  956. {
  957. return -1;
  958. }
  959. }
  960. return result;
  961. }
  962. inline int _ttail_file_off_cmp(ttail_t* t, size_t id, off_t off,
  963. const struct tm* tm, int *res)
  964. {
  965. if(fseek(t->logfile[id], off, SEEK_SET))
  966. {
  967. perror("Unable to set position in file");
  968. return -1;
  969. }
  970. if(ttail_file_getline(t, id) < 0)
  971. {
  972. perror("Unable to read a line from file");
  973. return -1;
  974. }
  975. return _ttail_file_cur_cmp(t, id, tm, res);
  976. }
  977. inline int _ttail_file_cur_cmp(ttail_t* t, size_t id, const struct tm* tm ,
  978. int* res)
  979. {
  980. int ret;
  981. struct tm ctm;
  982. if(ttail_file_getline(t, id) < 0)
  983. {
  984. return -1;
  985. }
  986. ret = ttail_logline2date(t, ttail_file_getline_buf(t), &ctm);
  987. if(ret < 0)
  988. {
  989. return -1;
  990. }
  991. else if(ret > 1)
  992. {
  993. return 1;
  994. }
  995. *res = ttail_tm_cmp(&ctm, tm);
  996. return 0;
  997. }
  998. void _ttail_search_file_free(ttail_t* t)
  999. {
  1000. if(!t->session)
  1001. {
  1002. return;
  1003. }
  1004. if(t->session->file.buf)
  1005. {
  1006. free(t->session->file.buf);
  1007. }
  1008. if(t->session->file.file_sz)
  1009. {
  1010. free(t->session->file.file_sz);
  1011. }
  1012. if(t->session->file.vfile)
  1013. {
  1014. free(t->session->file.vfile);
  1015. }
  1016. free(t->session);
  1017. }