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

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