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_init.c 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721
  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_init.h"
  20. void usage()
  21. {
  22. static struct option opts[] = TTAIL_LONG_OPT;
  23. static char *help[][2] = TTAIL_OPT_HELP;
  24. size_t i;
  25. printf("Usage : ttail -d DATE [OPTIONS] [LOGFILES]\n");
  26. printf("\t\n");
  27. printf("Options list\n");
  28. i=0;
  29. while(opts[i].name)
  30. {
  31. printf("\t-%c, --%s", opts[i].val, opts[i].name);
  32. if(opts[i].has_arg == required_argument)
  33. {
  34. printf("=%s\n",
  35. help[i][1]?help[i][1]:"ARG");
  36. }
  37. else if(opts[i].has_arg == optional_argument)
  38. {
  39. printf("[=%s]\n",
  40. help[i][1]?help[i][1]:"ARG");
  41. }
  42. else
  43. {
  44. printf("\n");
  45. }
  46. printf("\t\t%s\n\n", help[i][0]);
  47. i++;
  48. }
  49. printf(TTAIL_HELP_TEXT);
  50. }
  51. ttail_t *ttail_init(int argc, char **argv)
  52. {
  53. ttail_t *res;
  54. int c, opt_i, ret;
  55. size_t i;
  56. char *dates[2] = { NULL, NULL };
  57. char *date;
  58. res = malloc(sizeof(ttail_t));
  59. if(!res)
  60. {
  61. perror("Unable to allocate memory");
  62. goto ttail_init_alloc_err;
  63. }
  64. opterr = 1;
  65. optind = 0;
  66. res->verbose = 0;
  67. res->fmt = NULL;
  68. res->flag = 0;
  69. res->logfile = NULL;
  70. res->logfile_name = NULL;
  71. res->logfile_sz = 0;
  72. res->prefix_sz = 0;
  73. res->session = NULL;
  74. ttail_tm_init(&(res->date_min));
  75. ttail_tm_init(&(res->date_max));
  76. while(1)
  77. {
  78. static struct option long_options[] = TTAIL_LONG_OPT;
  79. c = getopt_long(argc, argv, TTAIL_SHORT_OPT, long_options, &opt_i);
  80. if(c == -1)
  81. break;
  82. switch(c)
  83. {
  84. case 'v':
  85. res->verbose++;
  86. break;
  87. case 'r':
  88. if(ttail_set_prefix(res, optarg))
  89. {
  90. goto ttail_init_err;
  91. }
  92. break;
  93. case 'p':
  94. if(res->flag & TTAIL_FLAG_PREFIX)
  95. {
  96. fprintf(stderr, "Preffix allready \
  97. set\n");
  98. goto ttail_init_err;
  99. }
  100. res->flag |= TTAIL_FLAG_PREFIX;
  101. res->prefix_sz = atoi(optarg);
  102. if(res->prefix_sz <= 0)
  103. {
  104. fprintf(stderr, "Prefix size have \
  105. to be > 0");
  106. goto ttail_init_err;
  107. }
  108. break;
  109. case 'E':
  110. if(ttail_set_flag_re_ex(res) < 0)
  111. {
  112. goto ttail_init_err;
  113. }
  114. break;
  115. case 'I':
  116. if(ttail_set_flag_re_ci(res) < 0)
  117. {
  118. goto ttail_init_err;
  119. }
  120. break;
  121. case 'f':
  122. if(res->flag & TTAIL_FLAG_FORMAT)
  123. {
  124. fprintf(stderr,"Multiple date format \
  125. given\n");
  126. goto ttail_init_err;
  127. }
  128. if(ttail_set_fmt(res, optarg) < 0)
  129. {
  130. goto ttail_init_err;
  131. }
  132. break;
  133. case 'h':
  134. usage();
  135. goto ttail_init_err;
  136. case 'd':
  137. case 'm':
  138. date = malloc(sizeof(char)*(strlen(optarg)+1));
  139. if(!date)
  140. {
  141. goto ttail_init_err;
  142. }
  143. strcpy(date, optarg);
  144. dates[c=='d'?0:1] = date;
  145. break;
  146. default: /* ? */
  147. goto ttail_init_err;
  148. }
  149. }
  150. for(i=optind; i<argc; i++)
  151. {
  152. ret = ttail_add_logfile(res, argv[i]);
  153. if(ret < 0)
  154. {
  155. goto ttail_init_err;
  156. }
  157. }
  158. if(ttail_set_dates(res, dates) < 0)
  159. {
  160. goto ttail_init_err;
  161. }
  162. return res;
  163. ttail_init_err:
  164. if(dates[0]) { free(dates[0]); }
  165. if(dates[1]) { free(dates[1]); }
  166. ttail_free(res);
  167. ttail_init_alloc_err:
  168. return NULL;
  169. }
  170. int ttail_init_check(ttail_t* t)
  171. {
  172. size_t i, ret;
  173. if(t->logfile_sz)
  174. {
  175. ret = 0;
  176. for(i=0; i<t->logfile_sz; i++)
  177. {
  178. if(t->logfile[i])
  179. {
  180. ret = 1;
  181. break;
  182. }
  183. }
  184. if(!ret)
  185. {
  186. fprintf(stderr, "Unable to read from any of the files \
  187. given as argument\n");
  188. return -1;
  189. }
  190. }
  191. if(!(t->flag & TTAIL_FLAG_DATE_MIN) && !(t->flag & TTAIL_FLAG_DATE_MAX))
  192. {
  193. fprintf(stderr, "one of --date-min -d --date-max -m is \
  194. mandatory\n");
  195. return -1;
  196. }
  197. if((t->flag & TTAIL_FLAG_DATE_MIN) && (t->flag & TTAIL_FLAG_DATE_MAX))
  198. {
  199. if(ttail_tm_cmp(&(t->date_min), &(t->date_max)) > 0)
  200. {
  201. fprintf(stderr, "date-min > date-max.\n");
  202. return -1;
  203. }
  204. }
  205. return 0;
  206. }
  207. int ttail_add_logfile(ttail_t* res, const char* filename)
  208. {
  209. void *tmp;
  210. FILE *fp;
  211. char *fname;
  212. int ret, i;
  213. for(i=0; i<res->logfile_sz; i++)
  214. {
  215. if(strcmp(filename, res->logfile_name[i]) == 0)
  216. {
  217. fprintf(stderr, "File '%s' allready added\n",
  218. filename);
  219. return -1;
  220. }
  221. }
  222. res->logfile_sz++;
  223. tmp = res->logfile;
  224. res->logfile = realloc(res->logfile,
  225. sizeof(FILE*)*res->logfile_sz);
  226. ret = 0;
  227. if(!res->logfile)
  228. {
  229. perror("Unable to allocate memory for logfiles");
  230. res->logfile = tmp;
  231. goto ttail_add_logfile_fpalloc_err;
  232. }
  233. fp = fopen(filename, "r");
  234. if(!fp)
  235. {
  236. fprintf(stderr, "Unable to open file : %s\n", filename);
  237. res->logfile[res->logfile_sz-1] = NULL;
  238. ret = 1;
  239. }
  240. res->logfile[res->logfile_sz-1] = fp;
  241. tmp = res->logfile_name;
  242. res->logfile_name = realloc(res->logfile_name,
  243. sizeof(char*)*res->logfile_sz);
  244. if(!res->logfile_name)
  245. {
  246. perror("Unable to allocate memory for logfiles");
  247. res->logfile_name = tmp;
  248. goto ttail_add_logfile_fnalloc_err;
  249. }
  250. fname = malloc(sizeof(char)*(strlen(filename)+1));
  251. if(!fname)
  252. {
  253. perror("Unable to allocate memory for logfiles");
  254. goto ttail_add_logfile_fnalloc_err;
  255. }
  256. strcpy(fname, filename);
  257. res->logfile_name[res->logfile_sz-1] = fname;
  258. return ret;
  259. ttail_add_logfile_fnalloc_err:
  260. fclose(res->logfile[res->logfile_sz-2]);
  261. ttail_add_logfile_fpalloc_err:
  262. res->logfile_sz--;
  263. return -1;
  264. }
  265. int ttail_set_prefix(ttail_t* res, const char* regex)
  266. {
  267. int ret, cflags;
  268. char *re_errbuff;
  269. size_t re_errbuff_sz;
  270. if(res->flag & TTAIL_FLAG_PREFIX)
  271. {
  272. fprintf(stderr, "Regex prefix allready set");
  273. return 1;
  274. }
  275. res->flag |= TTAIL_FLAG_PREFIX;
  276. cflags = 0; /** @todo checks */
  277. if(res->flag & TTAIL_FLAG_EXTENDED_RE)
  278. {
  279. cflags |= REG_EXTENDED;
  280. }
  281. if(res->flag & TTAIL_FLAG_CI_RE)
  282. {
  283. cflags |= REG_ICASE;
  284. }
  285. ret = regcomp(&res->date_prefix, regex, cflags);
  286. if(!ret)
  287. {
  288. res->prefix_sz = -1;
  289. return 0;
  290. }
  291. /*compilation error */
  292. res->flag ^= TTAIL_FLAG_PREFIX;
  293. re_errbuff_sz = regerror(ret,
  294. &res->date_prefix, NULL, 0);
  295. re_errbuff = malloc(
  296. sizeof(char)*re_errbuff_sz);
  297. if(!re_errbuff)
  298. {
  299. perror("Failed to allocate memory for regex compilation \
  300. error message");
  301. goto ttail_set_prefix_err;
  302. }
  303. regerror(ret, &res->date_prefix,
  304. re_errbuff,
  305. sizeof(char)*re_errbuff_sz);
  306. fprintf(stderr, "Regex compilation fails : %s", re_errbuff);
  307. free(re_errbuff);
  308. ttail_set_prefix_err:
  309. return -1;
  310. }
  311. int ttail_set_fmt(ttail_t* t, const char* fmt)
  312. {
  313. size_t len;
  314. if(t->flag & TTAIL_FLAG_FORMAT)
  315. {
  316. return -1;
  317. }
  318. len = strlen(fmt);
  319. t->fmt = malloc(sizeof(char)*(len+1));
  320. if(!t->fmt)
  321. {
  322. return -1;
  323. }
  324. strncpy(t->fmt, fmt, len);
  325. t->fmt[len] = '\0';
  326. t->flag |= TTAIL_FLAG_FORMAT;
  327. return 0;
  328. }
  329. /**@brief This function is used by bot @ref ttail_set_flag_re_ex() and
  330. *ttail_set_flag_re_ci() function
  331. *@param t ttail_t*
  332. *@return -1 on error else 0
  333. */
  334. static int _ttail_common_set_flag_re(ttail_t* ttail)
  335. {
  336. if(ttail->flag & TTAIL_FLAG_PREFIX)
  337. {
  338. fprintf(stderr, "Regex flags has to be set BEFORE the \
  339. prefix\n");
  340. return -1;
  341. }
  342. return 0;
  343. }
  344. int ttail_set_flag_re_ex(ttail_t* ttail)
  345. {
  346. if(_ttail_common_set_flag_re(ttail) < 0)
  347. {
  348. return -1;
  349. }
  350. ttail->flag |= TTAIL_FLAG_EXTENDED_RE;
  351. return 0;
  352. }
  353. int ttail_set_flag_re_ci(ttail_t* ttail)
  354. {
  355. if(_ttail_common_set_flag_re(ttail) < 0)
  356. {
  357. return -1;
  358. }
  359. ttail->flag |= TTAIL_FLAG_CI_RE;
  360. return 0;
  361. }
  362. int ttail_set_dates(ttail_t* res, char* dates[2])
  363. {
  364. int c, ret;
  365. char *date;
  366. for(c=0;c<2;c++)
  367. {
  368. ttail_tm_init(c==0?&(res->date_min):&(res->date_max));
  369. if(!dates[c])
  370. {
  371. continue;
  372. }
  373. date = dates[c];
  374. if(!strncmp(date, "#-", 2))
  375. {
  376. /* relative date */
  377. ret = _ttail_set_date_relative(res, date, c);
  378. }
  379. else
  380. {
  381. /* date from format */
  382. ret = _ttail_set_date_fmt(res, date, c);
  383. }
  384. if(ret < 0)
  385. {
  386. return -1;
  387. }
  388. res->flag |= c?TTAIL_FLAG_DATE_MAX:TTAIL_FLAG_DATE_MIN;
  389. }
  390. if(dates[0]) { free(dates[0]); dates[0] = NULL; }
  391. if(dates[1]) { free(dates[1]); dates[1] = NULL; }
  392. return 0;
  393. }
  394. int _ttail_set_date_fmt(ttail_t* res, char* date, int c)
  395. {
  396. int ret;
  397. char *endp;
  398. char *fmt[] = TTAIL_DEFAULT_FORMATS;
  399. if(!(res->flag & TTAIL_FLAG_FORMAT))
  400. {
  401. /* no format specified */
  402. ret = ttail_format_guess(date,
  403. c==0?&(res->date_min):&(res->date_max));
  404. if(ret < 0)
  405. {
  406. fprintf(stderr, "Unable to guess format for \
  407. date '%s'\n", date);
  408. return -1;
  409. }
  410. res->fmt = malloc(sizeof(char)*(strlen(fmt[ret])+1));
  411. if(!res->fmt)
  412. {
  413. perror("Unable to allocate memory for date format");
  414. res->flag ^= TTAIL_FLAG_FORMAT;
  415. }
  416. strcpy(res->fmt, fmt[ret]);
  417. res->flag |= TTAIL_FLAG_FORMAT;
  418. res->flag |= c?TTAIL_FLAG_DATE_MAX:TTAIL_FLAG_DATE_MIN;
  419. return 0;
  420. }
  421. endp = strptime(date, res->fmt,
  422. c==0?&(res->date_min):&(res->date_max));
  423. if(!endp)
  424. {
  425. fprintf(stderr, "Unable to parse date-%s '%s' with \
  426. format '%s'\n", c==0?"min":"max", date, res->fmt);
  427. return -1;
  428. }
  429. else if(*endp != '\0')
  430. {
  431. fprintf(stderr, "Leading caracters for date-%s : '%s' \
  432. is not matched in '%s'\n",\
  433. c==0?"min":"max", endp, date);
  434. return -1;
  435. }
  436. return 0;
  437. }
  438. int _ttail_set_date_relative(ttail_t* res, char* date, int c)
  439. {
  440. time_t now;
  441. char *unit, str_date[64];
  442. int value;
  443. struct tm *tm, *tm_p;
  444. short mod;
  445. tm = c==0?&(res->date_min):&(res->date_max);
  446. ttail_tm_init(tm);
  447. if(time(&now) < 0)
  448. {
  449. fprintf(stderr, "Unable to retrieve time using time()\n");
  450. return -1;
  451. }
  452. if(!(tm_p = localtime(&now)))
  453. {
  454. fprintf(stderr, "Unable to retrieve localtime using localtime()\n");
  455. return -1;
  456. }
  457. memcpy(tm, tm_p, sizeof(struct tm));
  458. mod = 0;
  459. unit = NULL;
  460. value = (int)strtol(date+2, &unit, 10);
  461. switch(*unit)
  462. {
  463. case 's':
  464. if(*(unit+1) == '\0' || !strcmp(unit, "sec"))
  465. {
  466. tm->tm_sec -= value;
  467. if(tm->tm_sec < 0)
  468. {
  469. mod = 1;
  470. value = abs(tm->tm_sec);
  471. tm->tm_sec = 60 - (value % 60);
  472. value /= 60;
  473. value++;
  474. }
  475. if(!mod)
  476. {
  477. break;
  478. }
  479. }
  480. case 'm':
  481. if(mod || *(unit+1) == '\0' || !strcmp(unit, "min"))
  482. {
  483. mod = 0;
  484. tm->tm_min -= value;
  485. if(tm->tm_min < 0)
  486. {
  487. mod = 1;
  488. value = abs(tm->tm_min);
  489. tm->tm_min = 60 - (value % 60);
  490. value /= 60;
  491. value++;
  492. printf("Value left : %d\n", value);
  493. }
  494. if(!mod)
  495. {
  496. break;
  497. }
  498. }
  499. case 'h':
  500. if(mod || *(unit+1) == '\0' || !strcmp(unit, "hour"))
  501. {
  502. mod = 0;
  503. tm->tm_hour -= value;
  504. if(tm->tm_hour < 0)
  505. {
  506. mod = 1;
  507. value = abs(tm->tm_hour);
  508. tm->tm_hour = 24 - (value % 24);
  509. value /= 24;
  510. value++;
  511. }
  512. if(!mod)
  513. {
  514. break;
  515. }
  516. }
  517. case 'd':
  518. if(mod || *(unit+1) == '\0' || !strcmp(unit, "day"))
  519. {
  520. mod = 0;
  521. tm->tm_mday -= value;
  522. if(tm->tm_mday < 0)
  523. {
  524. mod = 1;
  525. value = abs(tm->tm_mday);
  526. tm->tm_mday = 31 - (value % 31);
  527. value /= 31;
  528. value++;
  529. }
  530. if(!mod)
  531. {
  532. break;
  533. }
  534. }
  535. case 'M':
  536. if(*(unit+1) == '\0' || !strcmp(unit, "Month"))
  537. {
  538. mod = 0;
  539. tm->tm_mon -= value;
  540. if(tm->tm_mon < 0)
  541. {
  542. mod = 1;
  543. value = abs(tm->tm_mon);
  544. tm->tm_mon = 12 - (value % 12);
  545. value /= 12;
  546. value++;
  547. }
  548. if(!mod)
  549. {
  550. break;
  551. }
  552. }
  553. case 'y':
  554. if(mod || *(unit+1) == '\0' || !strcmp(unit, "year"))
  555. {
  556. tm->tm_year -= value;
  557. break;
  558. }
  559. default:
  560. fprintf(stderr,"Invalid relative date '%s'\n", date);
  561. return -1;
  562. }
  563. if(res->verbose > 0)
  564. {
  565. strftime(str_date, 64, "%c", tm);
  566. fprintf(stderr, "%s date set to %s\n",
  567. c?"stop":"start", str_date);
  568. }
  569. return 0;
  570. }
  571. int ttail_norm_dates(ttail_t* ttail)
  572. {
  573. struct tm *tm_p, tm;
  574. /* Huge buffer but no way to make a difference between error and
  575. buffer too small using strftime */
  576. char date[4096], *endp;
  577. size_t date_len;
  578. time_t now;
  579. if(time(&now) < 0)
  580. {
  581. perror("Unable to retrieve time using time()\n");
  582. return -1;
  583. }
  584. if(!(tm_p = localtime(&now)))
  585. {
  586. perror("Unable to retrieve localtime using localtime()\n");
  587. return -1;
  588. }
  589. if(!(date_len = strftime(date, sizeof(date), ttail->fmt, tm_p)))
  590. {
  591. fprintf(stderr, "Unable to print the current date using strftime()\n");
  592. return -1;
  593. }
  594. ttail_tm_init(&tm);
  595. endp = strptime(date, ttail->fmt, &tm);
  596. if(!endp || *endp != '\0')
  597. {
  598. fprintf(stderr, "An error occured, unable to strptime() a date\
  599. we just strftime() !\n");
  600. exit(EXIT_FAILURE);
  601. }
  602. TTAIL_NORMDATE(ttail, &tm, tm_sec);
  603. TTAIL_NORMDATE(ttail, &tm, tm_min);
  604. TTAIL_NORMDATE(ttail, &tm, tm_hour);
  605. TTAIL_NORMDATE(ttail, &tm, tm_mday);
  606. TTAIL_NORMDATE(ttail, &tm, tm_mon);
  607. TTAIL_NORMDATE(ttail, &tm, tm_year);
  608. return 0;
  609. }
  610. int ttail_format_guess(const char* date_str, struct tm* tm)
  611. {
  612. int i, res;
  613. char *res_ret, *ret;
  614. char *fmt[] = TTAIL_DEFAULT_FORMATS;
  615. struct tm dte;
  616. memset(&dte, 0, sizeof(struct tm));
  617. res = -1;
  618. res_ret = NULL;
  619. i=0;
  620. while(fmt[i])
  621. {
  622. ret = strptime(date_str, fmt[i], &dte);
  623. if(ret)
  624. {
  625. if(!res_ret || strlen(res_ret) > strlen(ret))
  626. {
  627. res_ret = ret;
  628. res = i;
  629. if(tm)
  630. {
  631. memcpy(tm, &dte, sizeof(struct tm));
  632. }
  633. }
  634. }
  635. i++;
  636. }
  637. if(!res_ret)
  638. {
  639. if(tm)
  640. {
  641. memset(tm, 0, sizeof(struct tm));
  642. }
  643. return -1;
  644. }
  645. return res;
  646. }
  647. void ttail_free(ttail_t* t)
  648. {
  649. size_t i;
  650. if(t->flag & TTAIL_FLAG_PREFIX && t->prefix_sz < 0)
  651. {
  652. regfree(&(t->date_prefix));
  653. }
  654. for(i=0; i<t->logfile_sz; i++)
  655. {
  656. if(t->logfile[i])
  657. {
  658. fclose(t->logfile[i]);
  659. }
  660. if(t->logfile_name[i])
  661. {
  662. free(t->logfile_name[i]);
  663. }
  664. }
  665. free(t->logfile);
  666. free(t->logfile_name);
  667. t->logfile_sz = 0;
  668. if(t->fmt != NULL)
  669. {
  670. free(t->fmt);
  671. }
  672. free(t);
  673. optind=0;
  674. }