/* * Copyright 2017 Yann Weber * * This file is part of Ttail. * * Ttail is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * any later version. * * Ttail is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Ttail. If not, see . */ #include "ttail_init.h" void usage() { static struct option opts[] = TTAIL_LONG_OPT; static char *help[][2] = TTAIL_OPT_HELP; size_t i; printf("Usage : ttail -d DATE [OPTIONS] [LOGFILES]\n"); printf("\t\n"); printf("Options list\n"); i=0; while(opts[i].name) { printf("\t-%c, --%s", opts[i].val, opts[i].name); if(opts[i].has_arg == required_argument) { printf("=%s\n", help[i][1]?help[i][1]:"ARG"); } else if(opts[i].has_arg == optional_argument) { printf("[=%s]\n", help[i][1]?help[i][1]:"ARG"); } else { printf("\n"); } printf("\t\t%s\n\n", help[i][0]); i++; } printf(TTAIL_HELP_TEXT); } ttail_t *ttail_init(int argc, char **argv) { ttail_t *res; int c, opt_i, ret; size_t i; char *dates[2] = { NULL, NULL }; char *date; res = malloc(sizeof(ttail_t)); if(!res) { perror("Unable to allocate memory"); goto ttail_init_alloc_err; } opterr = 1; optind = 0; res->verbose = 0; res->fmt = NULL; res->flag = 0; res->logfile = NULL; res->logfile_name = NULL; res->logfile_sz = 0; res->prefix_sz = 0; res->session = NULL; ttail_tm_init(&(res->date_min)); ttail_tm_init(&(res->date_max)); while(1) { static struct option long_options[] = TTAIL_LONG_OPT; c = getopt_long(argc, argv, TTAIL_SHORT_OPT, long_options, &opt_i); if(c == -1) break; switch(c) { case 'v': res->verbose++; break; case 'r': if(ttail_set_prefix(res, optarg)) { goto ttail_init_err; } break; case 'p': if(res->flag & TTAIL_FLAG_PREFIX) { fprintf(stderr, "Preffix allready \ set\n"); goto ttail_init_err; } res->flag |= TTAIL_FLAG_PREFIX; res->prefix_sz = atoi(optarg); if(res->prefix_sz <= 0) { fprintf(stderr, "Prefix size have \ to be > 0"); goto ttail_init_err; } break; case 'E': if(ttail_set_flag_re_ex(res) < 0) { goto ttail_init_err; } break; case 'I': if(ttail_set_flag_re_ci(res) < 0) { goto ttail_init_err; } break; case 'f': if(res->flag & TTAIL_FLAG_FORMAT) { fprintf(stderr,"Multiple date format \ given\n"); goto ttail_init_err; } if(ttail_set_fmt(res, optarg) < 0) { goto ttail_init_err; } break; case 'h': usage(); goto ttail_init_err; case 'd': case 'm': date = malloc(sizeof(char)*(strlen(optarg)+1)); if(!date) { goto ttail_init_err; } strcpy(date, optarg); dates[c=='d'?0:1] = date; break; case 'P': if(res->flag & TTAIL_FLAG_PERMISSIVE) { fprintf(stderr, "Warning : looks like \ -P --permissive flag was set more than once\n"); } res->flag |= TTAIL_FLAG_PERMISSIVE; break; default: /* ? */ goto ttail_init_err; } } for(i=optind; ilogfile_sz) { ret = 0; for(i=0; ilogfile_sz; i++) { if(t->logfile[i]) { ret = 1; break; } } if(!ret) { fprintf(stderr, "Unable to read from any of the files \ given as argument\n"); return -1; } } if(!(t->flag & TTAIL_FLAG_DATE_MIN) && !(t->flag & TTAIL_FLAG_DATE_MAX)) { fprintf(stderr, "one of --date-min -d --date-max -m is \ mandatory\n"); return -1; } if((t->flag & TTAIL_FLAG_DATE_MIN) && (t->flag & TTAIL_FLAG_DATE_MAX)) { if(ttail_tm_cmp(&(t->date_min), &(t->date_max)) > 0) { fprintf(stderr, "date-min > date-max.\n"); return -1; } } return 0; } int ttail_add_logfile(ttail_t* res, const char* filename) { void *tmp; FILE *fp; char *fname; int ret, i; for(i=0; ilogfile_sz; i++) { if(strcmp(filename, res->logfile_name[i]) == 0) { fprintf(stderr, "File '%s' allready added\n", filename); return -1; } } res->logfile_sz++; tmp = res->logfile; res->logfile = realloc(res->logfile, sizeof(FILE*)*res->logfile_sz); ret = 0; if(!res->logfile) { perror("Unable to allocate memory for logfiles"); res->logfile = tmp; goto ttail_add_logfile_fpalloc_err; } fp = fopen(filename, "r"); if(!fp) { fprintf(stderr, "Unable to open file : %s\n", filename); res->logfile[res->logfile_sz-1] = NULL; ret = 1; } res->logfile[res->logfile_sz-1] = fp; tmp = res->logfile_name; res->logfile_name = realloc(res->logfile_name, sizeof(char*)*res->logfile_sz); if(!res->logfile_name) { perror("Unable to allocate memory for logfiles"); res->logfile_name = tmp; goto ttail_add_logfile_fnalloc_err; } fname = malloc(sizeof(char)*(strlen(filename)+1)); if(!fname) { perror("Unable to allocate memory for logfiles"); goto ttail_add_logfile_fnalloc_err; } strcpy(fname, filename); res->logfile_name[res->logfile_sz-1] = fname; return ret; ttail_add_logfile_fnalloc_err: fclose(res->logfile[res->logfile_sz-2]); ttail_add_logfile_fpalloc_err: res->logfile_sz--; return -1; } int ttail_set_prefix(ttail_t* res, const char* regex) { int ret, cflags; char *re_errbuff; size_t re_errbuff_sz; if(res->flag & TTAIL_FLAG_PREFIX) { fprintf(stderr, "Regex prefix allready set"); return 1; } res->flag |= TTAIL_FLAG_PREFIX; cflags = 0; if(res->flag & TTAIL_FLAG_EXTENDED_RE) { cflags |= REG_EXTENDED; } if(res->flag & TTAIL_FLAG_CI_RE) { cflags |= REG_ICASE; } ret = regcomp(&res->date_prefix, regex, cflags); if(!ret) { res->prefix_sz = -1; return 0; } /*compilation error */ res->flag ^= TTAIL_FLAG_PREFIX; re_errbuff_sz = regerror(ret, &res->date_prefix, NULL, 0); re_errbuff = malloc( sizeof(char)*re_errbuff_sz); if(!re_errbuff) { perror("Failed to allocate memory for regex compilation \ error message"); goto ttail_set_prefix_err; } regerror(ret, &res->date_prefix, re_errbuff, sizeof(char)*re_errbuff_sz); fprintf(stderr, "Regex compilation fails : %s", re_errbuff); free(re_errbuff); ttail_set_prefix_err: return -1; } int ttail_set_fmt(ttail_t* t, const char* fmt) { if(t->flag & TTAIL_FLAG_FORMAT) { return -1; } t->fmt = strdup(fmt); if(!t->fmt) { return -1; } t->flag |= TTAIL_FLAG_FORMAT; return 0; } /**@brief This function is used by bot @ref ttail_set_flag_re_ex() and *ttail_set_flag_re_ci() function *@param t ttail_t* *@return -1 on error else 0 */ static int _ttail_common_set_flag_re(ttail_t* ttail) { if(ttail->flag & TTAIL_FLAG_PREFIX) { fprintf(stderr, "Regex flags has to be set BEFORE the \ prefix\n"); return -1; } return 0; } int ttail_set_flag_re_ex(ttail_t* ttail) { if(_ttail_common_set_flag_re(ttail) < 0) { return -1; } ttail->flag |= TTAIL_FLAG_EXTENDED_RE; return 0; } int ttail_set_flag_re_ci(ttail_t* ttail) { if(_ttail_common_set_flag_re(ttail) < 0) { return -1; } ttail->flag |= TTAIL_FLAG_CI_RE; return 0; } int ttail_set_dates(ttail_t* res, char* dates[2]) { int c, ret; char *date; for(c=0;c<2;c++) { ttail_tm_init(c==0?&(res->date_min):&(res->date_max)); if(!dates[c]) { continue; } date = dates[c]; if(!strncmp(date, "#-", 2)) { /* relative date */ ret = _ttail_set_date_relative(res, date, c); } else { /* date from format */ ret = _ttail_set_date_fmt(res, date, c); } if(ret < 0) { return -1; } res->flag |= c?TTAIL_FLAG_DATE_MAX:TTAIL_FLAG_DATE_MIN; } if(dates[0]) { free(dates[0]); dates[0] = NULL; } if(dates[1]) { free(dates[1]); dates[1] = NULL; } return 0; } int _ttail_set_date_fmt(ttail_t* res, char* date, int c) { int ret; char *endp; char *fmt[] = TTAIL_DEFAULT_FORMATS; if(!(res->flag & TTAIL_FLAG_FORMAT)) { /* no format specified */ ret = ttail_format_guess(date, c==0?&(res->date_min):&(res->date_max)); if(ret < 0) { fprintf(stderr, "Unable to guess format for \ date '%s'\n", date); return -1; } res->fmt = malloc(sizeof(char)*(strlen(fmt[ret])+1)); if(!res->fmt) { perror("Unable to allocate memory for date format"); res->flag ^= TTAIL_FLAG_FORMAT; } strcpy(res->fmt, fmt[ret]); res->flag |= TTAIL_FLAG_FORMAT; res->flag |= c?TTAIL_FLAG_DATE_MAX:TTAIL_FLAG_DATE_MIN; return 0; } endp = strptime(date, res->fmt, c==0?&(res->date_min):&(res->date_max)); if(!endp) { fprintf(stderr, "Unable to parse date-%s '%s' with \ format '%s'\n", c==0?"min":"max", date, res->fmt); return -1; } else if(*endp != '\0') { fprintf(stderr, "Leading caracters for date-%s : '%s' \ is not matched in '%s'\n",\ c==0?"min":"max", endp, date); return -1; } return 0; } int _ttail_set_date_relative(ttail_t* res, char* date, int c) { time_t now; char *unit, str_date[64]; int value; struct tm *tm, *tm_p; short mod; if(strncmp(date, "#-", 2)) { fprintf(stderr, "'%s' is not a relative date\n", date); return -1; } tm = c==0?&(res->date_min):&(res->date_max); ttail_tm_init(tm); if(time(&now) < 0) { fprintf(stderr, "Unable to retrieve time using time()\n"); return -1; } if(!(tm_p = localtime(&now))) { fprintf(stderr, "Unable to retrieve localtime using localtime()\n"); return -1; } memcpy(tm, tm_p, sizeof(struct tm)); mod = 0; unit = NULL; value = (int)strtol(date+2, &unit, 10); if(unit == date+2) { fprintf(stderr, "No value found in format '%s'\n", date); return -1; } if(value < 0) { fprintf(stderr, "Bad relative format '%s'\n", date); return -1; } switch(*unit) { case 's': if(*(unit+1) == '\0' || !strcmp(unit, "sec")) { tm->tm_sec -= value; if(tm->tm_sec < 0) { mod = 1; value = abs(tm->tm_sec); tm->tm_sec = 60 - (value % 60); value /= 60; value++; } if(!mod) { break; } } case 'm': if(mod || *(unit+1) == '\0' || !strcmp(unit, "min")) { mod = 0; tm->tm_min -= value; if(tm->tm_min < 0) { mod = 1; value = abs(tm->tm_min); tm->tm_min = 60 - (value % 60); value /= 60; value++; } if(!mod) { break; } } case 'h': if(mod || *(unit+1) == '\0' || !strcmp(unit, "hour")) { mod = 0; tm->tm_hour -= value; if(tm->tm_hour < 0) { mod = 1; value = abs(tm->tm_hour); tm->tm_hour = 24 - (value % 24); value /= 24; value++; } if(!mod) { break; } } case 'd': if(mod || *(unit+1) == '\0' || !strcmp(unit, "day")) { mod = 0; tm->tm_mday -= value; if(tm->tm_mday <= 0) { mod = 1; value = abs(tm->tm_mday); tm->tm_mday = 31 - (value % 31); value /= 31; value++; } if(!mod) { break; } } case 'M': if(mod || *(unit+1) == '\0' || !strcmp(unit, "Month")) { mod = 0; tm->tm_mon -= value; if(tm->tm_mon < 0) { mod = 1; value = abs(tm->tm_mon); tm->tm_mon = 12 - (value % 12); value /= 12; value++; } if(!mod) { break; } } case 'y': if(mod || *(unit+1) == '\0' || !strcmp(unit, "year")) { tm->tm_year -= value; break; } default: fprintf(stderr,"Invalid relative date '%s'\n", date); return -1; } if(res->verbose > 0) { strftime(str_date, 64, "%c", tm); fprintf(stderr, "%s date set to %s\n", c?"stop":"start", str_date); } return 0; } int ttail_norm_dates(ttail_t* ttail) { struct tm *tm_p, tm; /* Huge buffer but no way to make a difference between error and buffer too small using strftime */ char date[4096], *endp; size_t date_len; time_t now; if(time(&now) < 0) { perror("Unable to retrieve time using time()\n"); return -1; } if(!(tm_p = localtime(&now))) { perror("Unable to retrieve localtime using localtime()\n"); return -1; } if(!(date_len = strftime(date, sizeof(date), ttail->fmt, tm_p))) { fprintf(stderr, "Unable to print the current date using strftime()\n"); return -1; } ttail_tm_init(&tm); endp = strptime(date, ttail->fmt, &tm); if(!endp || *endp != '\0') { fprintf(stderr, "An error occured, unable to strptime() a date\ we just strftime() !\n"); exit(EXIT_FAILURE); } TTAIL_NORMDATE(ttail, &tm, tm_sec); TTAIL_NORMDATE(ttail, &tm, tm_min); TTAIL_NORMDATE(ttail, &tm, tm_hour); TTAIL_NORMDATE(ttail, &tm, tm_mday); TTAIL_NORMDATE(ttail, &tm, tm_mon); TTAIL_NORMDATE(ttail, &tm, tm_year); return 0; } int ttail_format_guess(const char* date_str, struct tm* tm) { int i, res; char *res_ret, *ret; char *fmt[] = TTAIL_DEFAULT_FORMATS; struct tm dte; memset(&dte, 0, sizeof(struct tm)); res = -1; res_ret = NULL; i=0; while(fmt[i]) { ret = strptime(date_str, fmt[i], &dte); if(ret) { if(!res_ret || strlen(res_ret) > strlen(ret)) { res_ret = ret; res = i; if(tm) { memcpy(tm, &dte, sizeof(struct tm)); } } } i++; } if(!res_ret) { if(tm) { memset(tm, 0, sizeof(struct tm)); } return -1; } return res; } void ttail_free(ttail_t* t) { size_t i; if(t->flag & TTAIL_FLAG_PREFIX && t->prefix_sz < 0) { regfree(&(t->date_prefix)); } for(i=0; ilogfile_sz; i++) { if(t->logfile[i]) { fclose(t->logfile[i]); } if(t->logfile_name[i]) { free(t->logfile_name[i]); } } free(t->logfile); free(t->logfile_name); t->logfile_sz = 0; if(t->fmt != NULL) { free(t->fmt); } free(t); optind=0; }