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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740
  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. if(t->flag & TTAIL_FLAG_FORMAT)
  322. {
  323. return -1;
  324. }
  325. t->fmt = strdup(fmt);
  326. if(!t->fmt)
  327. {
  328. return -1;
  329. }
  330. t->flag |= TTAIL_FLAG_FORMAT;
  331. return 0;
  332. }
  333. /**@brief This function is used by bot @ref ttail_set_flag_re_ex() and
  334. *ttail_set_flag_re_ci() function
  335. *@param t ttail_t*
  336. *@return -1 on error else 0
  337. */
  338. static int _ttail_common_set_flag_re(ttail_t* ttail)
  339. {
  340. if(ttail->flag & TTAIL_FLAG_PREFIX)
  341. {
  342. fprintf(stderr, "Regex flags has to be set BEFORE the \
  343. prefix\n");
  344. return -1;
  345. }
  346. return 0;
  347. }
  348. int ttail_set_flag_re_ex(ttail_t* ttail)
  349. {
  350. if(_ttail_common_set_flag_re(ttail) < 0)
  351. {
  352. return -1;
  353. }
  354. ttail->flag |= TTAIL_FLAG_EXTENDED_RE;
  355. return 0;
  356. }
  357. int ttail_set_flag_re_ci(ttail_t* ttail)
  358. {
  359. if(_ttail_common_set_flag_re(ttail) < 0)
  360. {
  361. return -1;
  362. }
  363. ttail->flag |= TTAIL_FLAG_CI_RE;
  364. return 0;
  365. }
  366. int ttail_set_dates(ttail_t* res, char* dates[2])
  367. {
  368. int c, ret;
  369. char *date;
  370. for(c=0;c<2;c++)
  371. {
  372. ttail_tm_init(c==0?&(res->date_min):&(res->date_max));
  373. if(!dates[c])
  374. {
  375. continue;
  376. }
  377. date = dates[c];
  378. if(!strncmp(date, "#-", 2))
  379. {
  380. /* relative date */
  381. ret = _ttail_set_date_relative(res, date, c);
  382. }
  383. else
  384. {
  385. /* date from format */
  386. ret = _ttail_set_date_fmt(res, date, c);
  387. }
  388. if(ret < 0)
  389. {
  390. return -1;
  391. }
  392. res->flag |= c?TTAIL_FLAG_DATE_MAX:TTAIL_FLAG_DATE_MIN;
  393. }
  394. if(dates[0]) { free(dates[0]); dates[0] = NULL; }
  395. if(dates[1]) { free(dates[1]); dates[1] = NULL; }
  396. return 0;
  397. }
  398. int _ttail_set_date_fmt(ttail_t* res, char* date, int c)
  399. {
  400. int ret;
  401. char *endp;
  402. char *fmt[] = TTAIL_DEFAULT_FORMATS;
  403. if(!(res->flag & TTAIL_FLAG_FORMAT))
  404. {
  405. /* no format specified */
  406. ret = ttail_format_guess(date,
  407. c==0?&(res->date_min):&(res->date_max));
  408. if(ret < 0)
  409. {
  410. fprintf(stderr, "Unable to guess format for \
  411. date '%s'\n", date);
  412. return -1;
  413. }
  414. res->fmt = malloc(sizeof(char)*(strlen(fmt[ret])+1));
  415. if(!res->fmt)
  416. {
  417. perror("Unable to allocate memory for date format");
  418. res->flag ^= TTAIL_FLAG_FORMAT;
  419. }
  420. strcpy(res->fmt, fmt[ret]);
  421. res->flag |= TTAIL_FLAG_FORMAT;
  422. res->flag |= c?TTAIL_FLAG_DATE_MAX:TTAIL_FLAG_DATE_MIN;
  423. return 0;
  424. }
  425. endp = strptime(date, res->fmt,
  426. c==0?&(res->date_min):&(res->date_max));
  427. if(!endp)
  428. {
  429. fprintf(stderr, "Unable to parse date-%s '%s' with \
  430. format '%s'\n", c==0?"min":"max", date, res->fmt);
  431. return -1;
  432. }
  433. else if(*endp != '\0')
  434. {
  435. fprintf(stderr, "Leading caracters for date-%s : '%s' \
  436. is not matched in '%s'\n",\
  437. c==0?"min":"max", endp, date);
  438. return -1;
  439. }
  440. return 0;
  441. }
  442. int _ttail_set_date_relative(ttail_t* res, char* date, int c)
  443. {
  444. time_t now;
  445. char *unit, str_date[64];
  446. int value;
  447. struct tm *tm, *tm_p;
  448. short mod;
  449. if(strncmp(date, "#-", 2))
  450. {
  451. fprintf(stderr, "'%s' is not a relative date\n", date);
  452. return -1;
  453. }
  454. tm = c==0?&(res->date_min):&(res->date_max);
  455. ttail_tm_init(tm);
  456. if(time(&now) < 0)
  457. {
  458. fprintf(stderr, "Unable to retrieve time using time()\n");
  459. return -1;
  460. }
  461. if(!(tm_p = localtime(&now)))
  462. {
  463. fprintf(stderr, "Unable to retrieve localtime using localtime()\n");
  464. return -1;
  465. }
  466. memcpy(tm, tm_p, sizeof(struct tm));
  467. mod = 0;
  468. unit = NULL;
  469. value = (int)strtol(date+2, &unit, 10);
  470. if(unit == date+2)
  471. {
  472. fprintf(stderr, "No value found in format '%s'\n", date);
  473. return -1;
  474. }
  475. if(value < 0)
  476. {
  477. fprintf(stderr, "Bad relative format '%s'\n", date);
  478. return -1;
  479. }
  480. switch(*unit)
  481. {
  482. case 's':
  483. if(*(unit+1) == '\0' || !strcmp(unit, "sec"))
  484. {
  485. tm->tm_sec -= value;
  486. if(tm->tm_sec < 0)
  487. {
  488. mod = 1;
  489. value = abs(tm->tm_sec);
  490. tm->tm_sec = 60 - (value % 60);
  491. value /= 60;
  492. value++;
  493. }
  494. if(!mod)
  495. {
  496. break;
  497. }
  498. }
  499. case 'm':
  500. if(mod || *(unit+1) == '\0' || !strcmp(unit, "min"))
  501. {
  502. mod = 0;
  503. tm->tm_min -= value;
  504. if(tm->tm_min < 0)
  505. {
  506. mod = 1;
  507. value = abs(tm->tm_min);
  508. tm->tm_min = 60 - (value % 60);
  509. value /= 60;
  510. value++;
  511. }
  512. if(!mod)
  513. {
  514. break;
  515. }
  516. }
  517. case 'h':
  518. if(mod || *(unit+1) == '\0' || !strcmp(unit, "hour"))
  519. {
  520. mod = 0;
  521. tm->tm_hour -= value;
  522. if(tm->tm_hour < 0)
  523. {
  524. mod = 1;
  525. value = abs(tm->tm_hour);
  526. tm->tm_hour = 24 - (value % 24);
  527. value /= 24;
  528. value++;
  529. }
  530. if(!mod)
  531. {
  532. break;
  533. }
  534. }
  535. case 'd':
  536. if(mod || *(unit+1) == '\0' || !strcmp(unit, "day"))
  537. {
  538. mod = 0;
  539. tm->tm_mday -= value;
  540. if(tm->tm_mday <= 0)
  541. {
  542. mod = 1;
  543. value = abs(tm->tm_mday);
  544. tm->tm_mday = 31 - (value % 31);
  545. value /= 31;
  546. value++;
  547. }
  548. if(!mod)
  549. {
  550. break;
  551. }
  552. }
  553. case 'M':
  554. if(mod || *(unit+1) == '\0' || !strcmp(unit, "Month"))
  555. {
  556. mod = 0;
  557. tm->tm_mon -= value;
  558. if(tm->tm_mon < 0)
  559. {
  560. mod = 1;
  561. value = abs(tm->tm_mon);
  562. tm->tm_mon = 12 - (value % 12);
  563. value /= 12;
  564. value++;
  565. }
  566. if(!mod)
  567. {
  568. break;
  569. }
  570. }
  571. case 'y':
  572. if(mod || *(unit+1) == '\0' || !strcmp(unit, "year"))
  573. {
  574. tm->tm_year -= value;
  575. break;
  576. }
  577. default:
  578. fprintf(stderr,"Invalid relative date '%s'\n", date);
  579. return -1;
  580. }
  581. if(res->verbose > 0)
  582. {
  583. strftime(str_date, 64, "%c", tm);
  584. fprintf(stderr, "%s date set to %s\n",
  585. c?"stop":"start", str_date);
  586. }
  587. return 0;
  588. }
  589. int ttail_norm_dates(ttail_t* ttail)
  590. {
  591. struct tm *tm_p, tm;
  592. /* Huge buffer but no way to make a difference between error and
  593. buffer too small using strftime */
  594. char date[4096], *endp;
  595. size_t date_len;
  596. time_t now;
  597. if(time(&now) < 0)
  598. {
  599. perror("Unable to retrieve time using time()\n");
  600. return -1;
  601. }
  602. if(!(tm_p = localtime(&now)))
  603. {
  604. perror("Unable to retrieve localtime using localtime()\n");
  605. return -1;
  606. }
  607. if(!(date_len = strftime(date, sizeof(date), ttail->fmt, tm_p)))
  608. {
  609. fprintf(stderr, "Unable to print the current date using strftime()\n");
  610. return -1;
  611. }
  612. ttail_tm_init(&tm);
  613. endp = strptime(date, ttail->fmt, &tm);
  614. if(!endp || *endp != '\0')
  615. {
  616. fprintf(stderr, "An error occured, unable to strptime() a date\
  617. we just strftime() !\n");
  618. exit(EXIT_FAILURE);
  619. }
  620. TTAIL_NORMDATE(ttail, &tm, tm_sec);
  621. TTAIL_NORMDATE(ttail, &tm, tm_min);
  622. TTAIL_NORMDATE(ttail, &tm, tm_hour);
  623. TTAIL_NORMDATE(ttail, &tm, tm_mday);
  624. TTAIL_NORMDATE(ttail, &tm, tm_mon);
  625. TTAIL_NORMDATE(ttail, &tm, tm_year);
  626. return 0;
  627. }
  628. int ttail_format_guess(const char* date_str, struct tm* tm)
  629. {
  630. int i, res;
  631. char *res_ret, *ret;
  632. char *fmt[] = TTAIL_DEFAULT_FORMATS;
  633. struct tm dte;
  634. memset(&dte, 0, sizeof(struct tm));
  635. res = -1;
  636. res_ret = NULL;
  637. i=0;
  638. while(fmt[i])
  639. {
  640. ret = strptime(date_str, fmt[i], &dte);
  641. if(ret)
  642. {
  643. if(!res_ret || strlen(res_ret) > strlen(ret))
  644. {
  645. res_ret = ret;
  646. res = i;
  647. if(tm)
  648. {
  649. memcpy(tm, &dte, sizeof(struct tm));
  650. }
  651. }
  652. }
  653. i++;
  654. }
  655. if(!res_ret)
  656. {
  657. if(tm)
  658. {
  659. memset(tm, 0, sizeof(struct tm));
  660. }
  661. return -1;
  662. }
  663. return res;
  664. }
  665. void ttail_free(ttail_t* t)
  666. {
  667. size_t i;
  668. if(t->flag & TTAIL_FLAG_PREFIX && t->prefix_sz < 0)
  669. {
  670. regfree(&(t->date_prefix));
  671. }
  672. for(i=0; i<t->logfile_sz; i++)
  673. {
  674. if(t->logfile[i])
  675. {
  676. fclose(t->logfile[i]);
  677. }
  678. if(t->logfile_name[i])
  679. {
  680. free(t->logfile_name[i]);
  681. }
  682. }
  683. free(t->logfile);
  684. free(t->logfile_name);
  685. t->logfile_sz = 0;
  686. if(t->fmt != NULL)
  687. {
  688. free(t->fmt);
  689. }
  690. free(t);
  691. optind=0;
  692. }