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

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