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 14KB

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