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

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064
  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. r = write(out, ttail_file_getline_buf(t),
  192. strlen(ttail_file_getline_buf(t)));
  193. if(r == -1)
  194. {
  195. perror("unable to write");
  196. return;
  197. }
  198. }
  199. else
  200. {
  201. r = read(fd, buf, sizeof(buf));
  202. if(r == -1)
  203. {
  204. perror("unable to read file");
  205. return;
  206. }
  207. else if(r == 0)
  208. {
  209. break;
  210. }
  211. cur_off += r;
  212. r = write(out, buf, r);
  213. if(r == -1 || r == 0)
  214. {
  215. perror("Unable to write result");
  216. return;
  217. }
  218. }
  219. }
  220. }
  221. }
  222. int _ttail_search_files_binary_search(ttail_t* t, const struct tm* in,
  223. const struct tm** ftm, short min)
  224. {
  225. int cmin, cmax, ret;
  226. size_t valid;
  227. int cmpres;
  228. off_t tmpoff;
  229. ttail_files_off_t *files_off;
  230. size_t *id;
  231. off_t *off;
  232. files_off = min?&(t->session->file.off_min):&(t->session->file.off_max);
  233. id = &(files_off->id),
  234. off = &(files_off->off);
  235. *id = *off = 0;
  236. valid = 0;
  237. while(*id < t->logfile_sz)
  238. {
  239. if(!ftm[*id])
  240. {
  241. (*id)++;
  242. continue;
  243. }
  244. valid++;
  245. cmin = ttail_tm_cmp(&(ftm[*id][0]), in);
  246. cmax = ttail_tm_cmp(&(ftm[*id][1]), in);
  247. if(!cmin)
  248. {
  249. /* found at the begining of the file */
  250. return 0;
  251. }
  252. else if (cmax == 0)
  253. {
  254. /* found at EOF */
  255. if(min)
  256. {
  257. *off = _ttail_file_search_from_end(t, *id, in);
  258. if(*off < 0)
  259. {
  260. *off = 0;
  261. return -1;
  262. }
  263. }
  264. else
  265. {
  266. *off = t->session->file.file_sz[*id];
  267. }
  268. return 0;
  269. }
  270. else if(cmin > 0)
  271. {
  272. /* found at start of file */
  273. off = 0;
  274. return 0;
  275. }
  276. else if(cmax > 0)
  277. {
  278. /* somewhere in current file */
  279. ret = _ttail_search_file_binary_search(t, in, ftm, min);
  280. if(ret)
  281. {
  282. if(t->verbose > 2)
  283. {
  284. fprintf(stderr, "Error while \
  285. running binary search algorithm in '%s'\n", t->logfile_name[*id]);
  286. }
  287. *id = 0;
  288. return ret;
  289. }
  290. /* searching for equivalent */
  291. tmpoff = *off;
  292. while(1)
  293. {
  294. if(fseek(t->logfile[*id], tmpoff, SEEK_SET) < 0)
  295. {
  296. return -1;
  297. }
  298. tmpoff = min?
  299. _ttail_file_start_line(t, *id):
  300. _ttail_file_next_line(t, *id);
  301. if(tmpoff < 0)
  302. {
  303. break;
  304. }
  305. cmpres=0;
  306. ret = _ttail_file_cur_cmp(t, *id, in, &cmpres);
  307. if(ret > 0 && !ttail_permissive(t))
  308. {
  309. ttail_strict_msg();
  310. return -1;
  311. }
  312. if((min && cmpres < 0) || (!min && cmpres > 0))
  313. {
  314. break;
  315. }
  316. *off = tmpoff;
  317. if(min)
  318. {
  319. tmpoff--;
  320. }
  321. }
  322. return 0;
  323. }
  324. else if(*id == t->logfile_sz - 1 && cmax < 0)
  325. {
  326. *off = t->session->file.file_sz[*id];
  327. return 0;
  328. }
  329. (*id)++;
  330. }
  331. if(!valid)
  332. {
  333. fprintf(stderr, "No files to scan\n");
  334. return 0;
  335. }
  336. if(*id == t->logfile_sz)
  337. {
  338. return 0;
  339. }
  340. /* the answer is somewhere in *id file */
  341. *off = _ttail_file_search_from_end(t, *id, in);
  342. return 0;
  343. }
  344. inline int _ttail_search_file_binary_search(ttail_t* t, const struct tm* in,
  345. const struct tm** ftm, short min)
  346. {
  347. off_t cur, sz, d, prev, tmp;
  348. int ret, cmpres;
  349. ttail_files_off_t *files_off;
  350. size_t id;
  351. off_t *off;
  352. files_off = min?&(t->session->file.off_min):&(t->session->file.off_max);
  353. id = files_off->id,
  354. off = &(files_off->off);
  355. sz = t->session->file.file_sz[id];
  356. d = cur = sz / 2;
  357. prev = 0;
  358. cmpres = 0;
  359. while(1)
  360. {
  361. if(fseek(t->logfile[id], cur, SEEK_SET) < 0)
  362. {
  363. perror("Unable to move in the file");
  364. return -1;
  365. }
  366. if(cur > prev)
  367. {
  368. cur = _ttail_file_next_line(t, id);
  369. if(cur < 0)
  370. {
  371. /* not sure errno is really set */
  372. perror("Error searching previous line");
  373. return -1;
  374. }
  375. }
  376. else
  377. {
  378. cur = _ttail_file_start_line(t, id);
  379. if(cur < 0)
  380. {
  381. /* not sure errno is really set */
  382. perror("Error searching previous line");
  383. return -1;
  384. }
  385. }
  386. if(cur == prev)
  387. {
  388. *off = cur;
  389. return 0;
  390. }
  391. prev = cur;
  392. ret = _ttail_file_cur_cmp(t, id, in, &cmpres);
  393. if(ret < 0)
  394. {
  395. if(t->verbose > 2)
  396. {
  397. fprintf(stderr, "Error comparing a logline \
  398. to a date directly from a file\n");
  399. }
  400. return -1;
  401. }
  402. else if(cmpres == 0)
  403. {
  404. *off = cur;
  405. break;
  406. }
  407. else if(cmpres < 0)
  408. {
  409. tmp = _ttail_file_next_line(t,id);
  410. ret = _ttail_file_cur_cmp(t, id, in, &cmpres);
  411. if(cmpres >=0)
  412. {
  413. *off = tmp;
  414. break;
  415. }
  416. d/=2;
  417. cur += d;
  418. cur %= t->session->file.file_sz[id];
  419. }
  420. else
  421. {
  422. d/=2;
  423. cur -= d;
  424. if(cur < 0)
  425. {
  426. cur = 0;
  427. }
  428. }
  429. }
  430. return 0;
  431. }
  432. int ttail_search_files_init(ttail_t* t)
  433. {
  434. struct stat stat;
  435. FILE *fp;
  436. int fd;
  437. size_t i;
  438. off_t *file_sz;
  439. t->session = (ttail_search_t*)malloc(sizeof(ttail_search_file_t));
  440. if(!t->session)
  441. {
  442. perror("Unable to allocate memory for search session");
  443. goto _ttail_search_closest_files_alloc_session_err;
  444. }
  445. memset(t->session, 0, sizeof(ttail_search_file_t));
  446. file_sz = malloc(sizeof(off_t)*t->logfile_sz);
  447. if(!file_sz)
  448. {
  449. perror("Unable to allocate memory for file sizes");
  450. goto _ttail_search_closest_files_alloc_err;
  451. }
  452. t->session->file.file_sz = file_sz;
  453. #ifdef HUGEFILE
  454. t->session->file.sz_div = 0;
  455. #endif
  456. for(i=0;i<t->logfile_sz;i++)
  457. {
  458. fp = t->logfile[i];
  459. if(!fp)
  460. {
  461. file_sz[i] = 0;
  462. continue;
  463. }
  464. if((fd = fileno(fp)) < 0)
  465. {
  466. perror("Unable to get fp");
  467. goto _ttail_search_closest_files_err;
  468. }
  469. if(fstat(fileno(fp), &stat))
  470. {
  471. perror("Unable to get file size");
  472. goto _ttail_search_closest_files_err;
  473. }
  474. file_sz[i] = stat.st_size;
  475. }
  476. /* we got all real size, determining if we need a divisor */
  477. /*
  478. * not implemented
  479. */
  480. if(_ttail_search_closest_files_set_fsizes(t))
  481. {
  482. goto _ttail_search_closest_files_err;
  483. }
  484. t->session->file.buf_sz = 128;
  485. t->session->file.buf = malloc(t->session->file.buf_sz);
  486. if(!t->session->file.buf)
  487. {
  488. goto _ttail_search_closest_files_err;
  489. }
  490. if(_ttail_search_files_fmt_init(t) < 0)
  491. {
  492. goto _ttail_search_closest_files_err;
  493. }
  494. return 0;
  495. _ttail_search_closest_files_err:
  496. free(file_sz);
  497. t->session->file.file_sz = NULL;
  498. _ttail_search_closest_files_alloc_err:
  499. free(t->session);
  500. _ttail_search_closest_files_alloc_session_err:
  501. return -1;
  502. }
  503. int _ttail_search_files_fmt_init(ttail_t* t)
  504. {
  505. int fmt_id;
  506. size_t i,j;
  507. const char *buff;
  508. const char *fmt[] = TTAIL_DEFAULT_FORMATS;
  509. if(t->flag & TTAIL_FLAG_FORMAT)
  510. {
  511. return 1;
  512. }
  513. fmt_id = -1;
  514. for(i=0; i<t->logfile_sz; i++)
  515. {
  516. if(!t->logfile[i]) {
  517. continue;
  518. }
  519. for(j=0; j<10; j++)
  520. {
  521. //try to guess fmt on the 10 first lines
  522. if(ttail_file_getline(t, i) < 0) {
  523. break;
  524. }
  525. buff = ttail_logline_subst(t, ttail_file_getline_buf(t));
  526. if(!buff)
  527. {
  528. if(ttail_permissive(t))
  529. {
  530. continue;
  531. }
  532. fprintf(stderr,
  533. "Unable to find prefix in '%s' logline",
  534. t->logfile_name[i]);
  535. if(t->verbose > 0)
  536. {
  537. fprintf(stderr, " : '%s'",
  538. ttail_file_getline_buf(t));
  539. }
  540. fprintf(stderr, "\n");
  541. return -1;
  542. }
  543. //buff contains the lines starting by the date
  544. fmt_id = ttail_format_guess(buff, NULL);
  545. if(fmt_id >= 0)
  546. {
  547. break;
  548. }
  549. if(!ttail_permissive(t))
  550. {
  551. ttail_strict_msg();
  552. break;
  553. }
  554. }
  555. rewind(t->logfile[i]);
  556. if(fmt_id >= 0)
  557. {
  558. if(t->verbose > 0)
  559. {
  560. fprintf(stderr,
  561. "Detected format %s in file %s\n",
  562. fmt[fmt_id], t->logfile_name[i]);
  563. }
  564. break;
  565. }
  566. }
  567. if(fmt_id >= 0)
  568. {
  569. buff = fmt[fmt_id];
  570. t->fmt = malloc(sizeof(char) * (strlen(buff)+1));
  571. if(!t->fmt)
  572. {
  573. perror("Unable to allocate memory for date format");
  574. return -1;
  575. }
  576. strcpy(t->fmt, buff);
  577. t->flag |= TTAIL_FLAG_FORMAT;
  578. return 0;
  579. }
  580. fprintf(stderr, "Unable to detect date format from logfiles\n");
  581. return -1;
  582. }
  583. int _ttail_search_closest_files_set_fsizes(ttail_t* t)
  584. {
  585. size_t i;
  586. off_t *vfile, *vsz;
  587. vfile = malloc(sizeof(off_t)*t->logfile_sz);
  588. if(!vfile)
  589. {
  590. perror("Unable to allocate memory for file size sum");
  591. return -1;
  592. }
  593. t->session->file.vfile = vfile;
  594. vsz = &(t->session->file.vsz);
  595. *vsz = 0;
  596. for(i=0; i<t->logfile_sz;i++)
  597. {
  598. vfile[i] = *vsz;
  599. #ifdef HUGEFILE
  600. *vsz += t->session->file.file_sz[i] >> t->session->file.sz_div;
  601. #else
  602. *vsz += t->session->file.file_sz[i];
  603. #endif
  604. }
  605. t->session->file.vpos = 0;
  606. return 0;
  607. }
  608. inline int _ttail_file_minmax(ttail_t* t, size_t id, struct tm tm[2])
  609. {
  610. FILE *fp;
  611. long cur;
  612. int ret;
  613. memset(tm, 0, sizeof(struct tm)*2);
  614. fp = t->logfile[id];
  615. if(!fp)
  616. {
  617. fprintf(stderr, "File pointer is null !\n");
  618. return 1;
  619. }
  620. if(fseek(fp, 0, SEEK_SET) < 0)
  621. {
  622. perror("Unable to manipulate fp");
  623. return -1;
  624. }
  625. while(1)
  626. {
  627. if(ttail_file_getline(t, id) < 0)
  628. {
  629. return 1;
  630. }
  631. if(!(ret = ttail_logline2date(t, ttail_file_getline_buf(t), tm)))
  632. {
  633. break;
  634. }
  635. if(!ttail_permissive(t))
  636. {
  637. fprintf(stderr,
  638. "Unable to find %s in logline",
  639. ret == 1?"prefix":"date");
  640. if(t->verbose > 0)
  641. {
  642. fprintf(stderr, " : '%s'\n",
  643. ttail_file_getline_buf(t));
  644. }
  645. else
  646. {
  647. fprintf(stderr,"\n");
  648. }
  649. ttail_strict_msg();
  650. return -1;
  651. }
  652. }
  653. if(fseek(fp, -1, SEEK_END) < 0)
  654. {
  655. perror("Unable to manipulate fp");
  656. return -1;
  657. }
  658. while(1)
  659. {
  660. if((cur = _ttail_file_start_line(t, id)) < 0)
  661. {
  662. fprintf(stderr, "Error will searching line starts in\
  663. %s\n", t->logfile_name[id]);
  664. return -1;
  665. }
  666. if(ttail_file_getline(t, id) < 0)
  667. {
  668. return 1;
  669. }
  670. if(!ttail_logline2date(t, ttail_file_getline_buf(t), tm+1))
  671. {
  672. break;
  673. }
  674. if(!cur)
  675. {
  676. return 1;
  677. }
  678. else if(!ttail_permissive(t))
  679. {
  680. if(t->verbose <= 0)
  681. {
  682. fprintf(stderr,
  683. "Unable to find a date in logline\n");
  684. }
  685. else
  686. {
  687. fprintf(stderr,
  688. "Unable to find a date in '%s'\n",
  689. ttail_file_getline_buf(t));
  690. }
  691. ttail_strict_msg();
  692. return -1;
  693. }
  694. if(fseek(fp, cur-1, SEEK_SET) < 0)
  695. {
  696. perror("Unable to manipulate fp");
  697. return -1;
  698. }
  699. }
  700. return 0;
  701. }
  702. int _ttail_file_reopen(ttail_t* t, size_t id)
  703. {
  704. if(t->logfile[id])
  705. {
  706. fclose(t->logfile[id]);
  707. }
  708. t->logfile[id] = fopen(t->logfile_name[id], "r");
  709. if(!t->logfile[id] && t->verbose > 2)
  710. {
  711. fprintf(stderr, "Unable to reopen '%s'\n",
  712. t->logfile_name[id]);
  713. }
  714. return t->logfile[id]?0:-1;
  715. }
  716. inline int _ttail_file_sort(ttail_t* t, struct tm** ftm)
  717. {
  718. size_t i, j, count;
  719. short sorted, warn;
  720. count = t->logfile_sz;
  721. i=0;
  722. warn = 0;
  723. for(i=0;i<count;i++)
  724. {
  725. while(!ftm[i] && i<count)
  726. {
  727. //move invalid dates to the end
  728. count--;
  729. TTAIL_MVF(t->logfile_name, i, count, char*);
  730. TTAIL_MVF(t->logfile, i, count, FILE*);
  731. TTAIL_MVF(t->session->file.file_sz, i, count,
  732. off_t);
  733. TTAIL_MVF(t->session->file.vfile, i, count,
  734. off_t);
  735. TTAIL_MVF(ftm, i, count, struct tm*);
  736. }
  737. if(!i ||i == count|| ttail_tm_cmp(&(ftm[i-1][1]), &(ftm[i][0])) < 0)
  738. {
  739. continue;
  740. }
  741. sorted=0;
  742. for(j=0; j<i;j++)
  743. {
  744. if(ttail_tm_cmp(&(ftm[i][1]), &(ftm[j][0])) < 0
  745. || (j > 0 && ttail_tm_cmp(&(ftm[i][0]), &(ftm[j-1][1])) > 0))
  746. {
  747. TTAIL_MVBF(t->logfile_name, i, j, char*);
  748. TTAIL_MVBF(t->logfile, i, j, FILE*);
  749. TTAIL_MVBF(t->session->file.file_sz, i, j,
  750. off_t);
  751. TTAIL_MVBF(t->session->file.vfile, i, j,
  752. off_t);
  753. TTAIL_MVBF(ftm, i, j, struct tm*);
  754. sorted=1;
  755. if(!warn && t->verbose > 1)
  756. {
  757. fprintf(stderr, "Warning : Files list \
  758. given as argument has to be sorted.\n");
  759. }
  760. break;
  761. }
  762. if((ttail_tm_cmp(&(ftm[i][0]), &(ftm[j][0])) < 0
  763. && ttail_tm_cmp(&(ftm[i][1]), &(ftm[j][0])) > 0) ||
  764. (j > 0 &&(
  765. ttail_tm_cmp(&(ftm[i][1]), &(ftm[j][1])) > 0 &&
  766. ttail_tm_cmp(&(ftm[i][0]), &ftm[j][1]) < 0)))
  767. {
  768. fprintf(stderr, "Files list not sortable : \
  769. file %s and %s overlaps.\n", t->logfile_name[i], t->logfile_name[j]);
  770. fprintf(stderr,"\
  771. meaning file1 date min < file2 date min < file1 date max \n\
  772. or file1 date min < file2 date max < file1 date max\n");
  773. return -1;
  774. }
  775. }
  776. if(sorted) { continue; }
  777. fprintf(stderr,"Files list not sortable : file %s contains \
  778. or is inside another file\n", t->logfile_name[i]);
  779. fprintf(stderr,"meaning that filex1 date min < file2 date min \
  780. and file1 date max > file2 date max\n");
  781. return -1;
  782. }
  783. return 0;
  784. }
  785. inline long _ttail_file_next_line(ttail_t *t, size_t id)
  786. {
  787. FILE *f;
  788. ssize_t s;
  789. size_t r;
  790. char *buff;
  791. long res;
  792. int c;
  793. f = t->logfile[id];
  794. r=0;
  795. buff = NULL;
  796. s = getline(&buff, &r, f);
  797. if(s < 0)
  798. {
  799. goto _ttail_file_next_line_err;
  800. }
  801. while(1)
  802. {
  803. c = getc(f);
  804. if(c == EOF)
  805. {
  806. return 0;
  807. }
  808. else if(c!='\n')
  809. {
  810. if(fseek(f, -1, SEEK_CUR)<0)
  811. {
  812. perror("Unable to set position in file");
  813. goto _ttail_file_next_line_err;
  814. }
  815. break;
  816. }
  817. }
  818. res = ftell(f);
  819. free(buff);
  820. return res;
  821. _ttail_file_next_line_err:
  822. free(buff);
  823. return -1;
  824. }
  825. inline long _ttail_file_start_line(ttail_t *ttail, size_t id)
  826. {
  827. #define _STARTLN_BUFFLEN 32
  828. FILE *f;
  829. long res; /* function result */
  830. long read_beg, cur, last, start;
  831. int read_sz;
  832. int c;
  833. f = ttail->logfile[id];
  834. if((start = ftell(f)) < 0)
  835. {
  836. perror("Unable to get position in file");
  837. return -1;
  838. }
  839. res = 0;
  840. read_beg = start;
  841. while(!res && start)
  842. {
  843. if(fseek(f, read_beg, SEEK_SET) < 0)
  844. {
  845. perror("Unable to set position in file");
  846. return -1;
  847. }
  848. start = read_beg;
  849. read_sz = start <= _STARTLN_BUFFLEN?start:_STARTLN_BUFFLEN;
  850. read_beg = start - read_sz;
  851. if(fseek(f, read_beg, SEEK_SET) < 0)
  852. {
  853. perror("Unable to set position in file");
  854. return -1;
  855. }
  856. last = -1; /* last pos we saw a '\n' */
  857. cur = read_beg;
  858. while(cur <= start)
  859. {
  860. c = getc(f);
  861. if(c == EOF)
  862. {
  863. if(!res)
  864. {
  865. return 0;
  866. }
  867. break;
  868. }
  869. else if (c =='\n')
  870. {
  871. last = cur;
  872. }
  873. else if(last >= 0)
  874. {
  875. res = cur;
  876. last = -1;
  877. }
  878. cur++;
  879. }
  880. if(!read_beg)
  881. {
  882. break;
  883. }
  884. }
  885. if(fseek(f, res, SEEK_SET) < 0)
  886. {
  887. perror("Unable to set position in file");
  888. return -1;
  889. }
  890. return res;
  891. }
  892. inline off_t _ttail_file_search_from_end(ttail_t* t , size_t id ,
  893. const struct tm* tm)
  894. {
  895. FILE *f;
  896. struct tm curtm;
  897. off_t last;
  898. int ret, cmpret;
  899. off_t result;
  900. result = -1;
  901. f = t->logfile[id];
  902. if(fseek(f, -1, SEEK_END) < 0)
  903. {
  904. return -1;
  905. }
  906. while(1)
  907. {
  908. last = _ttail_file_start_line(t, id);
  909. if(last < 0)
  910. {
  911. break;
  912. }
  913. if(ttail_file_getline(t, id) < 0)
  914. {
  915. break;
  916. }
  917. ret = ttail_logline2date(t, ttail_file_getline_buf(t), &curtm);
  918. if(ret < 0)
  919. {
  920. break;
  921. }
  922. else if(ret > 0 && !ttail_permissive(t))
  923. {
  924. fprintf(stderr, "Unable to find the %s in logline",
  925. ret==1?"prefix":"date");
  926. if(t->verbose > 0)
  927. {
  928. fprintf(stderr, " : '%s'\n",
  929. ttail_file_getline_buf(t));
  930. }
  931. else
  932. {
  933. fprintf(stderr, "\n");
  934. }
  935. ttail_strict_msg();
  936. break;
  937. }
  938. cmpret = ttail_tm_cmp(&curtm, tm);
  939. if(!ret)
  940. {
  941. if(cmpret >= 0)
  942. {
  943. /* found but continue to search the first one*/
  944. result = last;
  945. }
  946. else
  947. {
  948. if(cmpret < 0 && result < 0)
  949. {
  950. result = last;
  951. }
  952. break;
  953. }
  954. }
  955. if(last == 0)
  956. {
  957. /* considere the begining of the file as the answer */
  958. return 0;
  959. }
  960. if(fseek(f, last-1, SEEK_SET))
  961. {
  962. return -1;
  963. }
  964. }
  965. return result;
  966. }
  967. inline int _ttail_file_off_cmp(ttail_t* t, size_t id, off_t off,
  968. const struct tm* tm, int *res)
  969. {
  970. if(fseek(t->logfile[id], off, SEEK_SET))
  971. {
  972. perror("Unable to set position in file");
  973. return -1;
  974. }
  975. if(ttail_file_getline(t, id) < 0)
  976. {
  977. perror("Unable to read a line from file");
  978. return -1;
  979. }
  980. return _ttail_file_cur_cmp(t, id, tm, res);
  981. }
  982. inline int _ttail_file_cur_cmp(ttail_t* t, size_t id, const struct tm* tm ,
  983. int* res)
  984. {
  985. int ret;
  986. struct tm ctm;
  987. if(ttail_file_getline(t, id) < 0)
  988. {
  989. return -1;
  990. }
  991. ret = ttail_logline2date(t, ttail_file_getline_buf(t), &ctm);
  992. if(ret < 0)
  993. {
  994. return -1;
  995. }
  996. else if(ret > 1)
  997. {
  998. return 1;
  999. }
  1000. *res = ttail_tm_cmp(&ctm, tm);
  1001. return 0;
  1002. }
  1003. void _ttail_search_file_free(ttail_t* t)
  1004. {
  1005. if(!t->session)
  1006. {
  1007. return;
  1008. }
  1009. if(t->session->file.buf)
  1010. {
  1011. free(t->session->file.buf);
  1012. }
  1013. if(t->session->file.file_sz)
  1014. {
  1015. free(t->session->file.file_sz);
  1016. }
  1017. if(t->session->file.vfile)
  1018. {
  1019. free(t->session->file.vfile);
  1020. }
  1021. free(t->session);
  1022. }