timed tail for logfiles. Display loglines given a minimum date and/or a maximum date.
c
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

ttail_search_files.c 9.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588
  1. #include "ttail_search_files.h"
  2. int _ttail_search_closest_files(ttail_t* t, const struct tm *tm)
  3. {
  4. int ret;
  5. size_t i, prev;
  6. struct tm **ftm; /* t->logfile_sz len of struct tm[2] */
  7. ret = _ttail_search_closest_files_init(t);
  8. if(ret)
  9. {
  10. return ret;
  11. }
  12. /* Storing min & max of each files in ftm and checking that files are
  13. * sorted well */
  14. ftm = malloc(sizeof(struct tm*) * t->logfile_sz);
  15. if(!ftm)
  16. {
  17. perror("Unable to allocate memory");
  18. goto _ttail_search_closest_files_alloc_err;
  19. }
  20. for(i=0; i<t->logfile_sz; i++)
  21. {
  22. ftm[i] = malloc(sizeof(struct tm)*2);
  23. if(!ftm[i])
  24. {
  25. perror("Unable to allocate memory for time");
  26. goto _ttail_search_closest_files_alloc_loop_err;
  27. }
  28. if(!t->logfile[i])
  29. {
  30. continue;
  31. }
  32. ret = _ttail_file_minmax(t, 0, ftm[i]);
  33. if(ret < 0)
  34. {
  35. fprintf(stderr, "Minmax error\n");
  36. goto _ttail_search_closest_files_loop_err;
  37. }
  38. else if (ret == 1)
  39. {
  40. if(t->verbose)
  41. {
  42. fprintf(stderr, "Warning : unable to find \
  43. a valid date in '%s'\n", t->logfile_name[i]);
  44. }
  45. free(ftm[i]);
  46. ftm[i] = NULL;
  47. fclose(t->logfile[i]);
  48. t->logfile[i] = NULL;
  49. }
  50. prev = i;
  51. if(i && prev &&
  52. ttail_tm_cmp(&(ftm[prev][1]), &(ftm[i][0])) > 0)
  53. {
  54. /* files are not sorted */
  55. fprintf(stderr, "Files do not seems to be sorted. \
  56. File sorting not implemented yet\n");
  57. goto _ttail_search_closest_files_loop_err;
  58. }
  59. }
  60. /* TODO begining binary search of date_min */
  61. ret = _ttail_search_files_binary_search(t, tm, (const struct tm**)ftm);
  62. return 0;
  63. goto _ttail_search_closest_files_err;
  64. _ttail_search_closest_files_err:
  65. i = t->logfile_sz;
  66. do
  67. {
  68. _ttail_search_closest_files_alloc_loop_err:
  69. i--;
  70. _ttail_search_closest_files_loop_err:
  71. if(ftm[i])
  72. {
  73. free(ftm[i]);
  74. }
  75. }while(i);
  76. free(ftm);
  77. _ttail_search_closest_files_alloc_err:
  78. return -1;
  79. }
  80. int _ttail_search_files_binary_search(ttail_t* t, const struct tm* in,
  81. const struct tm** ftm)
  82. {
  83. int cmin, cmax;
  84. size_t valid;
  85. off_t *off;
  86. size_t *id;
  87. off = &(t->session->file.off);
  88. id = &(t->session->file.id);
  89. *id = *off = 0;
  90. valid = 0;
  91. if(ttail_tm_cmp(&(ftm[0][0]), in) > 0)
  92. {
  93. return 1;
  94. }
  95. while(1)
  96. {
  97. if(!ftm[*id])
  98. {
  99. (*id)++;
  100. continue;
  101. }
  102. valid++;
  103. cmin = ttail_tm_cmp(&(ftm[*id][0]), in);
  104. cmax = ttail_tm_cmp(&(ftm[*id][1]), in);
  105. if(!cmin)
  106. {
  107. /* found at the begining of the file */
  108. return 0;
  109. }
  110. else if (!cmax)
  111. {
  112. /* found at EOF */
  113. *off = _ttail_from_search_from_end(t, *id, in);
  114. if(*off < 0)
  115. {
  116. *off = 0;
  117. return -1;
  118. }
  119. return 0;
  120. }
  121. else if(cmin > 0)
  122. {
  123. /* not found */
  124. *id=0;
  125. return 1;
  126. }
  127. else if(*id == t->logfile_sz - 1 ||
  128. cmax > 0)
  129. {
  130. /* somewhere in current file */
  131. break;
  132. }
  133. (*id)++;
  134. }
  135. if(!valid)
  136. {
  137. fprintf(stderr, "No files to scan");
  138. }
  139. /* the answer is somewhere in *id file */
  140. *off = _ttail_from_search_from_end(t, *id, in);
  141. return 0;
  142. }
  143. inline int _ttail_search_file_binary_search(ttail_t* t, const struct tm* in,
  144. const struct tm** ftm)
  145. {
  146. off_t cur, sz, d, prev;
  147. int ret, cmpres;
  148. off_t *off;
  149. size_t id;
  150. off = &(t->session->file.off);
  151. id = t->session->file.id;
  152. sz = t->session->file.file_sz[id];
  153. d = cur = sz / 2;
  154. prev = 0;
  155. cmpres = 0;
  156. while(1)
  157. {
  158. cur = _ttail_file_next_line(t->logfile[id]);
  159. if(cur < -1)
  160. {
  161. cur = _ttail_file_start_line(t->logfile[id]);
  162. if(cur < 0)
  163. {
  164. return -1;
  165. }
  166. }
  167. if(cur == prev)
  168. {
  169. *off = cur;
  170. return 0;
  171. }
  172. prev = cur;
  173. ret = _ttail_file_cur_cmp(t, id, in, &cmpres);
  174. if(ret < 0)
  175. {
  176. return -1;
  177. }
  178. else if(cmpres == 0)
  179. {
  180. *off = cur;
  181. break;
  182. }
  183. else if(cmpres < 0)
  184. {
  185. ret = _ttail_file_cur_cmp(t, id, in, &cmpres);
  186. if(cmpres >=0)
  187. {
  188. *off = cur;
  189. break;
  190. }
  191. d/=2;
  192. cur += d;
  193. cur %= t->session->file.file_sz[id];
  194. }
  195. else
  196. {
  197. d/=2;
  198. cur -= d;
  199. }
  200. }
  201. return 0;
  202. }
  203. int _ttail_search_closest_files_init(ttail_t* t)
  204. {
  205. struct stat stat;
  206. FILE *fp;
  207. int fd;
  208. size_t i;
  209. off_t *file_sz;
  210. t->session = (ttail_search_t*)malloc(sizeof(ttail_search_file_t));
  211. if(!t->session)
  212. {
  213. perror("Unable to allocate memory for search session");
  214. goto _ttail_search_closest_files_alloc_session_err;
  215. }
  216. memset(t->session, 0, sizeof(ttail_search_file_t));
  217. file_sz = malloc(sizeof(off_t)*t->logfile_sz);
  218. if(!file_sz)
  219. {
  220. perror("Unable to allocate memory for file sizes");
  221. goto _ttail_search_closest_files_alloc_err;
  222. }
  223. t->session->file.file_sz = file_sz;
  224. #ifdef HUGEFILE
  225. t->session->file.sz_div = 0;
  226. #endif
  227. for(i=0;i<t->logfile_sz;i++)
  228. {
  229. fp = t->logfile[i];
  230. if(!fp)
  231. {
  232. file_sz[i] = 0;
  233. continue;
  234. }
  235. if((fd = fileno(fp)) < 0)
  236. {
  237. perror("Unable to get fp");
  238. goto _ttail_search_closest_files_err;
  239. }
  240. if(fstat(fileno(fp), &stat))
  241. {
  242. perror("Unable to get file size");
  243. goto _ttail_search_closest_files_err;
  244. }
  245. file_sz[i] = stat.st_size;
  246. }
  247. /* we got all real size, determining if we need a divisor */
  248. /*
  249. * not implemented
  250. */
  251. if(_ttail_search_closest_files_set_fsizes(t))
  252. {
  253. goto _ttail_search_closest_files_err;
  254. }
  255. t->session->file.buf_sz = 128;
  256. t->session->file.buf = malloc(t->session->file.buf_sz);
  257. if(!t->session->file.buf)
  258. {
  259. goto _ttail_search_closest_files_err;
  260. }
  261. return 0;
  262. _ttail_search_closest_files_err:
  263. free(file_sz);
  264. t->session->file.file_sz = NULL;
  265. _ttail_search_closest_files_alloc_err:
  266. free(t->session);
  267. _ttail_search_closest_files_alloc_session_err:
  268. return -1;
  269. }
  270. int _ttail_search_closest_files_set_fsizes(ttail_t* t)
  271. {
  272. size_t i;
  273. off_t *vfile, *vsz;
  274. vfile = malloc(sizeof(off_t)*t->logfile_sz);
  275. if(!vfile)
  276. {
  277. perror("Unable to allocate memory for file size sum");
  278. return -1;
  279. }
  280. t->session->file.vfile = vfile;
  281. vsz = &(t->session->file.vsz);
  282. *vsz = 0;
  283. for(i=0; i<t->logfile_sz;i++)
  284. {
  285. vfile[i] = *vsz;
  286. #ifdef HUGEFILE
  287. *vsz += t->session->file.file_sz[i] >> t->session->file.sz_div;
  288. #else
  289. *vsz += t->session->file.file_sz[i];
  290. #endif
  291. }
  292. t->session->file.vpos = 0;
  293. return 0;
  294. }
  295. inline int _ttail_file_minmax(ttail_t* t, size_t id, struct tm tm[2])
  296. {
  297. FILE *fp;
  298. long cur;
  299. memset(tm, 0, sizeof(struct tm)*2);
  300. fp = t->logfile[id];
  301. if(!fp)
  302. {
  303. return 1;
  304. }
  305. if(fseek(fp, 0, SEEK_SET) < 0)
  306. {
  307. perror("Unable to manipulate fp");
  308. return -1;
  309. }
  310. while(1)
  311. {
  312. if(ttail_getline(t, id) < 0)
  313. {
  314. return 1;
  315. }
  316. if(!ttail_logline2date(t, ttail_getline_buf(t), tm))
  317. {
  318. break;
  319. }
  320. }
  321. if(fseek(fp, -1, SEEK_END) < 0)
  322. {
  323. perror("Unable to manipulate fp");
  324. return -1;
  325. }
  326. while(1)
  327. {
  328. if((cur = _ttail_file_start_line(fp)) < 0)
  329. {
  330. return -1;
  331. }
  332. if(ttail_getline(t, id) < 0)
  333. {
  334. return 1;
  335. }
  336. if(!ttail_logline2date(t, ttail_getline_buf(t), tm+1))
  337. {
  338. break;
  339. }
  340. if(!cur)
  341. {
  342. return 1;
  343. }
  344. if(fseek(fp, cur-1, SEEK_SET) < 0)
  345. {
  346. perror("Unable to manipulate fp");
  347. return -1;
  348. }
  349. }
  350. return 0;
  351. }
  352. int _ttail_file_reopen(ttail_t* t, size_t id)
  353. {
  354. if(t->logfile[id])
  355. {
  356. fclose(t->logfile[id]);
  357. }
  358. t->logfile[id] = fopen(t->logfile_name[id], "r");
  359. if(!t->logfile[id] && t->verbose > 2)
  360. {
  361. fprintf(stderr, "Unable to reopen '%s'\n",
  362. t->logfile_name[id]);
  363. }
  364. return t->logfile[id]?0:-1;
  365. }
  366. inline long _ttail_file_next_line(FILE* f)
  367. {
  368. ssize_t s;
  369. size_t r;
  370. char *buff;
  371. long res;
  372. int c;
  373. r=0;
  374. buff = NULL;
  375. s = getline(&buff, &r, f);
  376. if(s == -1)
  377. {
  378. goto _ttail_file_next_line_err;
  379. }
  380. while(1)
  381. {
  382. c = getc(f);
  383. if(c == EOF)
  384. {
  385. return 0;
  386. }
  387. else if(c!='\n')
  388. {
  389. if(fseek(f, -1, SEEK_CUR)<0)
  390. {
  391. goto _ttail_file_next_line_err;
  392. }
  393. break;
  394. }
  395. }
  396. res = ftell(f);
  397. free(buff);
  398. return res;
  399. _ttail_file_next_line_err:
  400. free(buff);
  401. return -1;
  402. }
  403. inline long _ttail_file_start_line(FILE* f)
  404. {
  405. #define _STARTLN_BUFFLEN 32
  406. long res; /* function result */
  407. long read_beg, cur, last, start;
  408. int read_sz;
  409. int c;
  410. if((start = ftell(f)) < 0)
  411. {
  412. return -1;
  413. }
  414. res = 0;
  415. read_beg = start;
  416. while(!res && start)
  417. {
  418. if(fseek(f, read_beg, SEEK_SET) < 0)
  419. {
  420. return -1;
  421. }
  422. start = read_beg;
  423. read_sz = start <= _STARTLN_BUFFLEN?start:_STARTLN_BUFFLEN;
  424. read_beg = start - read_sz;
  425. if(fseek(f, read_beg, SEEK_SET) < 0)
  426. {
  427. return -1;
  428. }
  429. last = -1; /* last pos we saw a '\n' */
  430. cur = read_beg;
  431. while(cur <= start)
  432. {
  433. c = getc(f);
  434. if(c == EOF)
  435. {
  436. if(!res)
  437. {
  438. return 0;
  439. }
  440. break;
  441. }
  442. else if (c =='\n')
  443. {
  444. last = cur;
  445. }
  446. else if(last >= 0)
  447. {
  448. res = cur;
  449. last = -1;
  450. }
  451. cur++;
  452. }
  453. if(!read_beg)
  454. {
  455. break;
  456. }
  457. }
  458. if(fseek(f, res, SEEK_SET) < 0)
  459. {
  460. return -1;
  461. }
  462. return res;
  463. }
  464. inline off_t _ttail_from_search_from_end(ttail_t* t , size_t id ,
  465. const struct tm* tm)
  466. {
  467. FILE *f;
  468. struct tm curtm;
  469. off_t last;
  470. int ret;
  471. f = t->logfile[id];
  472. if(fseek(f, -1, SEEK_END) < 0)
  473. {
  474. return -1;
  475. }
  476. while(1)
  477. {
  478. last = _ttail_file_start_line(f);
  479. if(last < 0)
  480. {
  481. goto _ttail_from_search_from_end_err;
  482. }
  483. if(ttail_getline(t, id) < 0)
  484. {
  485. goto _ttail_from_search_from_end_err;
  486. }
  487. ret = ttail_logline2date(t, ttail_getline_buf(t), &curtm);
  488. if(ret < 0)
  489. {
  490. goto _ttail_from_search_from_end_err;
  491. }
  492. if(!ret && !ttail_tm_cmp(&curtm, tm))
  493. {
  494. /* found */
  495. break;
  496. }
  497. if(last == 0)
  498. {
  499. /* considere the begining of the file as the answer */
  500. return 0;
  501. }
  502. if(fseek(f, last-1, SEEK_CUR))
  503. {
  504. goto _ttail_from_search_from_end_err;
  505. }
  506. }
  507. return last;
  508. _ttail_from_search_from_end_err:
  509. return -1;
  510. }
  511. inline int _ttail_file_off_cmp(ttail_t* t, size_t id, off_t off,
  512. const struct tm* tm, int *res)
  513. {
  514. if(fseek(t->logfile[id], off, SEEK_CUR))
  515. {
  516. return -1;
  517. }
  518. if(ttail_getline(t, id) < 0)
  519. {
  520. return -1;
  521. }
  522. return _ttail_file_cur_cmp(t, id, tm, res);
  523. }
  524. inline int _ttail_file_cur_cmp(ttail_t* t, size_t id, const struct tm* tm ,
  525. int* res)
  526. {
  527. int ret;
  528. struct tm ctm;
  529. ret = ttail_logline2date(t, ttail_getline_buf(t), &ctm);
  530. if(ret < 0)
  531. {
  532. return -1;
  533. }
  534. else if(ret == 1)
  535. {
  536. return 1;
  537. }
  538. *res = ttail_tm_cmp(&ctm, tm);
  539. return 0;
  540. }
  541. void _ttail_search_file_free(ttail_t* t)
  542. {
  543. if(!t->session)
  544. {
  545. return;
  546. }
  547. if(t->session->file.buf)
  548. {
  549. free(t->session->file.buf);
  550. }
  551. if(t->session->file.file_sz)
  552. {
  553. free(t->session->file.file_sz);
  554. }
  555. if(t->session->file.vfile)
  556. {
  557. free(t->session->file.vfile);
  558. }
  559. free(t->session);
  560. }