/*- * Copyright (c) 2003-2004 Andrey Simonenko * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #ifndef lint static const char rcsid[] ATTR_UNUSED = "@(#)$Id: ipa_sdb_dump.c,v 1.2 2006/03/21 20:24:45 simon Exp $"; #endif /* !lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ipa_sdb.h" #include "version.h" #define IPA_SDB_DUMP_NAME "ipa_sdb_dump" static int use_stdin; /* Non-zero, if read from data from stdin. */ static const char *file_name = NULL; /* -f */ static FILE *src_fp = NULL; /* FILE. */ static int src_fd; /* File descriptor. */ static const char *envprogname; static int ignore_date_error = 0; /* 1, if one -e; 2 if -ee. */ static int report_date_error; /* 0, if -ee. */ #define NREC_PER_READ 200 /* Number of records to read per one read(2). */ #ifdef WITH_LIMITS # define LIMIT_START_STR "Start :" # define LIMIT_RESTART_STR "Restart :" # define LIMIT_RESTART_EXEC_STR "Restart exec :" # define LIMIT_REACH_STR "Reach :" # define LIMIT_REACH_EXEC_STR "Reach exec :" # define LIMIT_EXPIRE_STR "Expire :" # define LIMIT_EXPIRE_EXEC_STR "Expire exec :" # define LIMIT_UPDATED_STR "Updated :" # define LIMIT_LIMIT_STR "Limit :" # define LIMIT_COUNTER_STR "Counter :" # define LIMIT_STR_LEN 14 #endif /* WITH_LIMITS */ #ifdef WITH_THRESHOLDS # define THRESHOLD_FROM_STR "From :" # define THRESHOLD_UPDATED_STR "Updated :" # define THRESHOLD_THRESHOLD_STR "Threshold :" # define THRESHOLD_COUNTER_STR "Counter :" # define THRESHOLD_STR_LEN 14 #endif /* WITH_THRESHOLDS */ #define PAT_COUNTER "^[[:digit:]]{20}$" /* * For reporting errors from regcomp(3) function. */ #define RE_ERRBUF_SIZ 100 static int re_errcode; static char re_errbuf[RE_ERRBUF_SIZ]; static void output(const char *, ...) ATTR_FORMAT(printf, 1, 2); static void printf_error(const char *, ...) ATTR_FORMAT(printf, 1, 2); static void exit_err(const char *, ...) ATTR_NORETURN ATTR_FORMAT(printf, 1, 2); static void exit_errx(const char *, ...) ATTR_NORETURN ATTR_FORMAT(printf, 1, 2); #define cnt_to_base(cnt, c_high, c_low) do { \ c_high = htonl((uint32_t)(cnt >> 32)); \ c_low = htonl((uint32_t)cnt); \ } while (/* CONSTCOND */ 0) #define cnt_from_base(cnt, c_high, c_low) do { \ cnt = ntohl(c_high); \ cnt <<= 32; \ cnt += ntohl(c_low); \ } while (/* CONSTCOND */ 0) static void show_version(void) { printf(IPA_SDB_DUMP_NAME", version "IPA_SDB_VERSION"\n"); } static void usage(void) { show_version(); printf("\ Usage: %s [options]\n\ Dump ipa_sdb(5) database file to text format and back\n\ Options are:\n\ -f \tRead database data from the given file instead of reading\n\ \t\tit from stdin\n" #ifdef WITH_LIMITS " -l\t\tTreat a database file as a database file for a limit,\n\ \t\tby default it is considered as a database file for a rule\n" #endif #ifdef WITH_THRESHOLDS " -t\t\tTreat a database file as a database file for a threshold,\n\ \t\tby default it is considered as a database file for a rule\n" #endif " -b\t\tConvert text database dump to original database format and\n\ \t\toutput it to stdout\n\ -e\t\tIgnore errors in dates, two switches suppress error messages\n\ -h\t\tOutput this help message and exit\n\ -v\t\tOutput version number and exit\n", envprogname); } /* * Output the program name, a message, an error message and exit. */ static void exit_err(const char *format, ...) { int errno_save = errno; va_list ap; fflush(stdout); fprintf(stderr, "%s: ", envprogname); va_start(ap, format); vfprintf(stderr, format, ap); va_end(ap); if (errno_save != 0) fprintf(stderr, ": %s", strerror(errno_save)); fprintf(stderr, "\n"); exit(1); } /* * Output the program name, a message and exit. */ static void exit_errx(const char *format, ...) { va_list ap; fflush(stdout); fprintf(stderr, "%s: ", envprogname); va_start(ap, format); vfprintf(stderr, format, ap); va_end(ap); fprintf(stderr, "\n"); exit(1); } /* * A wrapper for read(2) syscall. */ static ssize_t readn(int fd, void *vptr, size_t n) { size_t nleft; ssize_t nread; char *ptr; ptr = vptr; nleft = n; for (;;) { if ( (nread = read(fd, ptr, nleft)) < 0) { if (errno == EINTR) nread = 0; else return -1; } else if (nread == 0) break; /* EOF */ nleft -= nread; if (nleft == 0) break; ptr += nread; } return n - nleft; } /* * Output formated string to stdout, check that the number of outputed * characters are correct. */ static void output(const char *format, ...) { va_list ap; va_start(ap, format); if (vprintf(format, ap) < 0) exit_err("output: vprintf failed"); va_end(ap); } /* * Call fflush() for stdout, if it returns nonzero, then output * message about occured error and exit. */ static void output_flush(void) { if (fflush(stdout) != 0) exit_err("output_flush: fflush(stdout)"); } /* * Flush output and output message about error. */ static void printf_error(const char *format, ...) { va_list ap; output_flush(); va_start(ap, format); vfprintf(stderr, format, ap); va_start(ap, format); } /* * Form an error message in re_errbuf according to * re_errcode with the help from the regerror() function. */ static void re_form_errbuf(void) { regerror(re_errcode, (regex_t *)NULL, re_errbuf, sizeof re_errbuf); } /* * Check if date in binary rule's record is correct. */ static int check_date_in_rule(const struct ipa_sdb_rule_record *ptr) { if (ptr->mday > 31 || ptr->mday == 0 || ( (ptr->h1 > 23 || ptr->m1 > 59 || ptr->s1 > 59) && !(ptr->h1 == 24 && ptr->m1 == 0 && ptr->s1 == 0) ) || ( (ptr->h2 > 23 || ptr->m2 > 59 || ptr->s2 > 59) && !(ptr->h2 == 24 && ptr->m2 == 0 && ptr->s2 == 0) ) || (ptr->h2 * 60 * 60 + ptr->m2 * 60 + ptr->s2) < (ptr->h1 * 60 * 60 + ptr->m1 * 60 + ptr->s1) ) { if (report_date_error) printf_error("error: wrong date in the record!\n"); return -1; } return 0; } static void rule_bin_to_txt(void) { #ifdef PRIu64 u_int nrecs; size_t recs_size; ssize_t nread; uint64_t cnt; struct stat statbuf; struct ipa_sdb_rule_record *rec, *recs; if (use_stdin) recs_size = NREC_PER_READ; else { if (fstat(src_fd, &statbuf) < 0) exit_err("file %s: fstat", file_name); recs_size = (size_t)(1 + statbuf.st_size / sizeof *recs); if (recs_size > NREC_PER_READ) recs_size = NREC_PER_READ; } recs_size *= sizeof *recs; if ( (recs = malloc(recs_size)) == NULL) exit_err("malloc, %lu bytes", (u_long)recs_size); nrecs = 0; while ( (nread = readn(src_fd, recs, recs_size)) > 0) { for (rec = recs; nread > 0; ++nrecs, ++rec) { if ( (nread -= sizeof *rec) < 0) exit_errx("file %s: wrong size of file, offset %lu", file_name, (u_long)(nrecs * sizeof *recs)); cnt_from_base(cnt, rec->c_high, rec->c_low); output("%02u/%02u:%02u:%02u-%02u:%02u:%02u %020"PRIu64"\n", rec->mday, rec->h1, rec->m1, rec->s1, rec->h2, rec->m2, rec->s2, cnt); if (check_date_in_rule(rec) < 0) { if (report_date_error) printf_error("wrong record info: record #%u, offset %lu bytes\n", nrecs, (u_long)(nrecs * sizeof *rec)); if (!ignore_date_error) exit(1); } } } if (nread < 0) exit_err("file %s: read", file_name); free(recs); output_flush(); #else exit_errx("this option is not supported, because PRIu64 macro variable is not defined"); #endif /* PRIu64 */ } static void rule_txt_to_bin(void) { #ifdef SCNu64 /* <-- 20 --> <- 20 -> xx/xx:xx:xx-xx:xx:xx xxx...xx\n ^42 */ #define BUF_SIZE 43 #define PAT_RULE_LINE "^[[:digit:]]{2}/[[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}-[[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2} [[:digit:]]{20}\n$" char buf[BUF_SIZE]; u_int mday, h1, m1, s1, h2, m2, s2, lineno; regex_t reg; uint64_t cnt; struct ipa_sdb_rule_record rec; if ( (re_errcode = regcomp(®, PAT_RULE_LINE, REG_EXTENDED|REG_NOSUB)) != 0) { re_form_errbuf(); exit_errx("cannot compile regular expression \"%s\": %s", PAT_RULE_LINE, re_errbuf); } lineno = 0; while (fgets(buf, sizeof buf, src_fp) != NULL) { if (regexec(®, buf, 0, (regmatch_t *)NULL, 0) != 0) exit_err("file %s: wrong format of line #%u", file_name, lineno); if (sscanf(buf, "%02u/%02u:%02u:%02u-%02u:%02u:%02u %020"SCNu64"\n", &mday, &h1, &m1, &s1, &h2, &m2, &s2, &cnt) != 8) exit_errx("file %s: sscanf failed on line #%u", file_name, lineno); rec.mday = (uint8_t)mday; rec.h1 = (uint8_t)h1; rec.m1 = (uint8_t)m1; rec.s1 = (uint8_t)s1; rec.h2 = (uint8_t)h2; rec.m2 = (uint8_t)m2; rec.s2 = (uint8_t)s2; if (check_date_in_rule(&rec) < 0) { if (report_date_error) printf_error("file %s: wrong record info: line #%u\n", file_name, lineno); if (!ignore_date_error) exit(1); } cnt_to_base(cnt, rec.c_high, rec.c_low); if (fwrite(&rec, sizeof rec, 1, stdout) != 1) exit_err("file %s: fwrite", file_name); ++lineno; } if (ferror(src_fp) != 0) exit_err("file %s: fgets failed at line #%u", file_name, lineno); regfree(®); output_flush(); #undef BUF_SIZE #undef PAT_RULE_LINE #else exit_errx("this option is not supported, because SCNu64 macro variable is not defined"); #endif /* SCNu64 */ } #ifdef WITH_ANY_LIMITS static int const last_mday_arr[] = /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */ { 0, 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; /* * Return last month day in the given year/mon. */ static int last_mday(uint16_t year, uint8_t mon) { if (mon == 2) /* February */ return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) ? 29 : 28; return last_mday_arr[mon]; } static int check_db_date(const ipa_sdb_date *ptr, int is_set) { if (!is_set) return 0; if (ptr->mon > 12 || ptr->mday > last_mday(ptr->year, ptr->mon) || ptr->hour > 23 || ptr->min > 59 || ptr->sec > 59) { if (report_date_error) printf_error("error: wrong date in the record!\n"); return -1; } return 0; } static void output_db_date(const char *msg, const ipa_sdb_date *ptr, int is_set) { output("%s ", msg); if (is_set) output("%u.%02u.%02u/%02u:%02u:%02u\n", ntohs(ptr->year), ptr->mon, ptr->mday, ptr->hour, ptr->min, ptr->sec); else output("-\n"); } #endif /* WITH_ANY_LIMITS */ #ifdef WITH_LIMITS static void limit_bin_to_txt(void) { #ifdef PRIu64 int date_error, is_set; u_int nrecs; size_t recs_size; ssize_t nread; uint64_t value; struct stat statbuf; struct ipa_sdb_limit_record *rec, *recs; if (use_stdin) recs_size = NREC_PER_READ; else { if (fstat(src_fd, &statbuf) < 0) exit_err("file %s: fstat", file_name); recs_size = (size_t)(1 + statbuf.st_size / sizeof *recs); if (recs_size > NREC_PER_READ) recs_size = NREC_PER_READ; } recs_size *= sizeof *recs; if ( (recs = malloc(recs_size)) == NULL) exit_err("malloc, %lu bytes", (u_long)recs_size); date_error = 0; nrecs = 0; while ( (nread = readn(src_fd, recs, recs_size)) > 0) { for (rec = recs; nread > 0; ++nrecs, ++rec) { if ( (nread -= sizeof *rec) < 0) exit_errx("file %s: wrong size of file, offset %lu", file_name, (u_long)(nrecs * sizeof *recs)); is_set = rec->set & IPA_SDB_LIMIT_EVENT_START_SET; output_db_date(LIMIT_START_STR, &rec->start, is_set); if (check_db_date(&rec->start, is_set) < 0) date_error = 1; is_set = rec->set & IPA_SDB_LIMIT_EVENT_RESTART_SET; output_db_date(LIMIT_RESTART_STR, &rec->restart, is_set); if (check_db_date(&rec->restart, is_set) < 0) date_error = 1; is_set = rec->set & IPA_SDB_LIMIT_EVENT_RESTART_EXEC_SET; output_db_date(LIMIT_RESTART_EXEC_STR, &rec->restart_exec, is_set); if (check_db_date(&rec->restart_exec, is_set) < 0) date_error = 1; is_set = rec->set & IPA_SDB_LIMIT_EVENT_REACH_SET; output_db_date(LIMIT_REACH_STR, &rec->reach, is_set); if (check_db_date(&rec->reach, is_set) < 0) date_error = 1; is_set = rec->set & IPA_SDB_LIMIT_EVENT_REACH_EXEC_SET; output_db_date(LIMIT_REACH_EXEC_STR, &rec->reach_exec, is_set); if (check_db_date(&rec->reach_exec, is_set) < 0) date_error = 1; is_set = rec->set & IPA_SDB_LIMIT_EVENT_EXPIRE_SET; output_db_date(LIMIT_EXPIRE_STR, &rec->expire, is_set); if (check_db_date(&rec->expire, is_set) < 0) date_error = 1; is_set = rec->set & IPA_SDB_LIMIT_EVENT_EXPIRE_EXEC_SET; output_db_date(LIMIT_EXPIRE_EXEC_STR, &rec->expire_exec, is_set); if (check_db_date(&rec->expire_exec, is_set) < 0) date_error = 1; is_set = rec->set & IPA_SDB_LIMIT_EVENT_UPDATED_SET; output_db_date(LIMIT_UPDATED_STR, &rec->updated, is_set); if (check_db_date(&rec->updated, is_set) < 0) date_error = 1; cnt_from_base(value, rec->l_high, rec->l_low); output(LIMIT_LIMIT_STR" %020"PRIu64"\n", value); cnt_from_base(value, rec->c_high, rec->c_low); output(LIMIT_COUNTER_STR" %020"PRIu64"\n", value); if (date_error) { if (report_date_error) printf_error("wrong record info: record #%u, offset %lu bytes\n", nrecs, (u_long)(nrecs * sizeof *rec)); if (!ignore_date_error) exit(1); date_error = 0; } output("\n"); } } if (nread < 0) exit_err("file %s: read", file_name); free(recs); output_flush(); #else exit_errx("this option is not supported, because PRIu64 macro variable is not defined"); #endif /* PRIu64 */ } struct limit_event { const char *name; ipa_sdb_date *db_date; uint8_t set; }; static struct ipa_sdb_limit_record limit_rec; #define LIMIT_EVENT(X, p) { LIMIT_ ## X ## _STR, p, IPA_SDB_LIMIT_EVENT_ ## X ## _SET } static struct limit_event limit_event_tbl[] = { LIMIT_EVENT(START, &limit_rec.start), LIMIT_EVENT(RESTART, &limit_rec.restart), LIMIT_EVENT(RESTART_EXEC, &limit_rec.restart_exec), LIMIT_EVENT(REACH, &limit_rec.reach), LIMIT_EVENT(REACH_EXEC, &limit_rec.reach_exec), LIMIT_EVENT(EXPIRE, &limit_rec.expire), LIMIT_EVENT(EXPIRE_EXEC, &limit_rec.expire_exec), LIMIT_EVENT(UPDATED, &limit_rec.updated), { NULL, NULL, 0 } }; #undef LIMIT_EVENT static void limit_txt_to_bin(void) { #ifdef SCNu64 #define BUF_SIZE 100 #define PAT_DB_DATE "^([[:digit:]]{4,}\\.[[:digit:]]{2}\\.[[:digit:]]{2}/[[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}|-)$" char buf[BUF_SIZE], *ptr; int limit_str_flag, counter_str_flag; u_int lineno; u_int year, mon, mday, hour, min, sec; size_t len; regex_t reg_db_date, reg_counter; uint64_t value; ipa_sdb_date *db_date; struct limit_event *event; if ( (re_errcode = regcomp(®_db_date, PAT_DB_DATE, REG_EXTENDED|REG_NOSUB)) != 0) { re_form_errbuf(); exit_errx("cannot compile regular expression \"%s\": %s", PAT_DB_DATE, re_errbuf); } if ( (re_errcode = regcomp(®_counter, PAT_COUNTER, REG_EXTENDED|REG_NOSUB)) != 0) { re_form_errbuf(); exit_errx("cannot compile regular expression \"%s\": %s", PAT_COUNTER, re_errbuf); } lineno = 0; limit_str_flag = counter_str_flag = 0; event = limit_event_tbl; limit_rec.set = 0; while (fgets(buf, sizeof buf, src_fp) != NULL) { len = strlen(buf); if (buf[len - 1] != '\n') exit_errx("file %s: too long line or '\\n' is missed in line #%u", file_name, lineno); if (buf[0] == '\n') continue; buf[len - 1] = '\0'; if (len < LIMIT_STR_LEN + 1) exit_errx("file %s: too short line #%u", file_name, lineno); if (event->name != NULL) { if (strncmp(buf, event->name, LIMIT_STR_LEN) != 0) exit_errx("file %s: line started with \"%s\" is expected in line #%u", file_name, event->name, lineno); ptr = buf + LIMIT_STR_LEN + 1; if (regexec(®_db_date, ptr, 0, (regmatch_t *)NULL, 0) != 0) goto wrong_format; db_date = event->db_date; if (*ptr != '-') { if (sscanf(ptr, "%u.%02u.%02u/%02u:%02u:%02u", &year, &mon, &mday, &hour, &min, &sec) != 6) exit_errx("file %s: sscanf failed at line #%u", file_name, lineno); if (year > UINT16_MAX) exit_errx("file %s: too big value for year in date in line #%u", file_name, lineno); db_date->year = htons((uint16_t)year); db_date->mon = (uint8_t)mon; db_date->mday = (uint8_t)mday; db_date->hour = (uint8_t)hour; db_date->min = (uint8_t)min; db_date->sec = (uint8_t)sec; limit_rec.set |= (uint8_t)event->set; if (check_db_date(db_date, 1) < 0) { if (report_date_error) printf_error("file %s: wrong record info: line #%u\n", file_name, lineno); if (!ignore_date_error) exit(1); } } ++event; } else { ptr = buf + LIMIT_STR_LEN + 1; if (regexec(®_counter, ptr, 0, (regmatch_t *)NULL, 0) != 0) goto wrong_format; if (sscanf(ptr, "%020"SCNu64, &value) != 1) exit_errx("file %s: sscanf failed on line #%u", file_name, lineno); if (limit_str_flag == 0) { if (strncmp(buf, LIMIT_LIMIT_STR, LIMIT_STR_LEN) != 0) exit_errx("file %s: line started with \""LIMIT_LIMIT_STR"\" is expected in line #%u", file_name, lineno); limit_str_flag = 1; counter_str_flag = 0; cnt_to_base(value, limit_rec.l_high, limit_rec.l_low); } else { if (strncmp(buf, LIMIT_COUNTER_STR, LIMIT_STR_LEN) != 0) exit_errx("file %s: line started with \""LIMIT_COUNTER_STR"\" is expected, line #%u", file_name, lineno); limit_str_flag = 0; counter_str_flag = 1; cnt_to_base(value, limit_rec.c_high, limit_rec.c_low); if (fwrite(&limit_rec, sizeof limit_rec, 1, stdout) != 1) exit_err("fwrite(stdout)"); limit_rec.set = 0; event = limit_event_tbl; } } ++lineno; } if (ferror(src_fp) != 0) exit_err("file %s: fgets failed at line #%u", file_name, lineno); if (counter_str_flag == 0) exit_errx("file %s: not enough lines for last record", file_name); output_flush(); regfree(®_db_date); regfree(®_counter); return; wrong_format: exit_errx("file %s: wrong format of line #%u", file_name, lineno); #undef BUF_SIZE #undef PAT_DB_DATE #else exit_errx("this option is not supported, because SCNu64 macro variable is not defined"); #endif /* SCNu64 */ } #endif /* WITH_LIMITS */ #ifdef WITH_THRESHOLDS static void threshold_bin_to_txt(void) { #ifdef PRIu64 int date_error; ssize_t nread; uint64_t value; struct ipa_sdb_threshold_record rec; date_error = 0; if ( (nread = readn(src_fd, &rec, sizeof rec)) > 0) { if (nread < sizeof rec) exit_errx("file %s: wrong size of file, offset 0", file_name); output_db_date(THRESHOLD_FROM_STR, &rec.tm_from, 1); if (check_db_date(&rec.tm_from, 1) < 0) date_error = 1; output_db_date(THRESHOLD_UPDATED_STR, &rec.tm_updated, 1); if (check_db_date(&rec.tm_updated, 1) < 0) date_error = 1; cnt_from_base(value, rec.t_high, rec.t_low); output(THRESHOLD_THRESHOLD_STR" %020"PRIu64"\n", value); cnt_from_base(value, rec.c_high, rec.c_low); output(THRESHOLD_COUNTER_STR" %020"PRIu64"\n", value); if (date_error) if (!ignore_date_error) exit(1); output("\n"); } if (nread < 0) exit_err("file %s: read", file_name); /* Try to read one character. */ switch (readn(src_fd, &rec, 1)) { case 0: break; /* EOF */ case 1: exit_errx("file %s: wrong size of file, offset %lu", file_name, (u_long)sizeof(rec)); /* NOTREACHED */ default: /* -1 */ exit_err("file %s: read", file_name); } #else exit_errx("this option is not supported, because PRIu64 macro variable is not defined"); #endif /* PRIu64 */ } static struct ipa_sdb_threshold_record threshold_rec; struct threshold_event { const char *name; ipa_sdb_date *db_date; }; #define THRESHOLD_EVENT(X, p) { THRESHOLD_ ## X ## _STR, p } static struct threshold_event threshold_event_tbl[] = { THRESHOLD_EVENT(FROM, &threshold_rec.tm_from), THRESHOLD_EVENT(UPDATED, &threshold_rec.tm_updated), { NULL, NULL } }; #undef THRESHOLD_EVENT static void threshold_txt_to_bin(void) { #ifdef SCNu64 #define BUF_SIZE 100 #define PAT_DB_DATE "^[[:digit:]]{4,}\\.[[:digit:]]{2}\\.[[:digit:]]{2}/[[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}$" char buf[BUF_SIZE], *ptr; int threshold_str_flag, counter_str_flag; u_int lineno; u_int year, mon, mday, hour, min, sec; size_t len; regex_t reg_db_date, reg_counter; uint64_t value; ipa_sdb_date *db_date; struct threshold_event *event; if ( (re_errcode = regcomp(®_db_date, PAT_DB_DATE, REG_EXTENDED|REG_NOSUB)) != 0) { re_form_errbuf(); exit_errx("cannot compile regular expression \"%s\": %s", PAT_DB_DATE, re_errbuf); } if ( (re_errcode = regcomp(®_counter, PAT_COUNTER, REG_EXTENDED|REG_NOSUB)) != 0) { re_form_errbuf(); exit_errx("cannot compile regular expression \"%s\": %s", PAT_COUNTER, re_errbuf); } lineno = 0; threshold_str_flag = counter_str_flag = 0; event = threshold_event_tbl; while (fgets(buf, sizeof buf, src_fp) != NULL) { len = strlen(buf); if (buf[len - 1] != '\n') exit_errx("file %s: too long line or '\\n' is missed in line #%u", file_name, lineno); if (buf[0] == '\n') continue; buf[len - 1] = '\0'; if (len < THRESHOLD_STR_LEN + 1) exit_errx("file %s: too short line #%u", file_name, lineno); if (event->name != NULL) { if (strncmp(buf, event->name, THRESHOLD_STR_LEN) != 0) exit_errx("file %s: line started with \"%s\" is expected in line #%u", file_name, event->name, lineno); ptr = buf + THRESHOLD_STR_LEN + 1; if (regexec(®_db_date, ptr, 0, (regmatch_t *)NULL, 0) != 0) goto wrong_format; db_date = event->db_date; if (sscanf(ptr, "%u.%02u.%02u/%02u:%02u:%02u", &year, &mon, &mday, &hour, &min, &sec) != 6) exit_errx("file %s: sscanf failed at line #%u", file_name, lineno); if (year > UINT16_MAX) exit_errx("file %s: too big value for year in date in line #%u", file_name, lineno); db_date->year = htons((uint16_t)year); db_date->mon = (uint8_t)mon; db_date->mday = (uint8_t)mday; db_date->hour = (uint8_t)hour; db_date->min = (uint8_t)min; db_date->sec = (uint8_t)sec; if (check_db_date(db_date, 1) < 0) { if (report_date_error) printf_error("file %s: wrong record info: line #%u\n", file_name, lineno); if (!ignore_date_error) exit(1); } ++event; } else { ptr = buf + THRESHOLD_STR_LEN + 1; if (regexec(®_counter, ptr, 0, (regmatch_t *)NULL, 0) != 0) goto wrong_format; if (sscanf(ptr, "%020"SCNu64, &value) != 1) exit_errx("file %s: sscanf failed on line #%u", file_name, lineno); if (threshold_str_flag == 0) { if (strncmp(buf, THRESHOLD_THRESHOLD_STR, THRESHOLD_STR_LEN) != 0) exit_errx("file %s: line started with \""THRESHOLD_THRESHOLD_STR"\" is expected in line #%u", file_name, lineno); threshold_str_flag = 1; cnt_to_base(value, threshold_rec.t_high, threshold_rec.t_low); } else if (counter_str_flag == 0) { if (strncmp(buf, THRESHOLD_COUNTER_STR, THRESHOLD_STR_LEN) != 0) exit_errx("file %s: line started with \""THRESHOLD_COUNTER_STR"\" is expected, line #%u", file_name, lineno); counter_str_flag = 1; cnt_to_base(value, threshold_rec.c_high, threshold_rec.c_low); if (fwrite(&threshold_rec, sizeof threshold_rec, 1, stdout) != 1) exit_err("fwrite(stdout)"); } else exit_errx("file %s: unexpected line #%u", file_name, lineno); } ++lineno; } if (ferror(src_fp) != 0) exit_err("file %s: fgets failed at line #%u", file_name, lineno); if (counter_str_flag == 0) exit_errx("file %s: not enough lines", file_name); output_flush(); regfree(®_db_date); regfree(®_counter); return; wrong_format: exit_errx("file %s: wrong format of line #%u", file_name, lineno); #undef BUF_SIZE #undef PAT_DB_DATE #else exit_errx("this option is not supported, because SCNu64 macro variable is not defined"); #endif /* SCNu64 */ } #endif /* WITH_THRESHOLDS */ #ifdef WITH_LIMITS # define OPTSTRING_LIMIT "l" #else # define OPTSTRING_LIMIT "" #endif #ifdef WITH_THRESHOLDS # define OPTSTRING_THRESHOLD "t" #else # define OPTSTRING_THRESHOLD "" #endif #define OPTSTRING ":bef:h" OPTSTRING_LIMIT OPTSTRING_THRESHOLD "v" int main(int argc, char *argv[]) { int opt; int b_flag = 0; /* -b */ #ifdef WITH_LIMITS int l_flag = 0; /* -l */ #endif #ifdef WITH_THRESHOLDS int t_flag = 0; /* -t */ #endif /* Save the program name. */ if ( (envprogname = strrchr(argv[0], '/')) != NULL) ++envprogname; else envprogname = argv[0]; opterr = 0; while ( (opt = getopt(argc, argv, OPTSTRING)) != -1) switch (opt) { case 'b': b_flag = 1; break; case 'e': ignore_date_error = ignore_date_error == 0 ? 1 : 2; break; case 'f': file_name = optarg; break; case 'h': usage(); return 0; #ifdef WITH_LIMITS case 'l': l_flag = 1; break; #endif #ifdef WITH_THRESHOLDS case 't': t_flag = 1; break; #endif case 'v': show_version(); return 0; case ':': exit_errx("option -%c requires an argument", optopt); /* NOTREACHED */ case '?': exit_errx("invalid switch -%c", optopt); /* NOTREACHED */ default: exit_errx("unexpected option -%c", optopt); } if (optind < argc) exit_errx("non-switch argument \"%s\"", argv[optind]); if (file_name == NULL) { use_stdin = 1; file_name = ""; if (b_flag) src_fp = stdin; else src_fd = STDIN_FILENO; } else { use_stdin = 0; if (b_flag) { if ( (src_fp = fopen(file_name, "r")) == NULL) exit_err("fopen(%s, \"r\")", file_name); } else { if ( (src_fd = open(file_name, O_RDONLY)) < 0) exit_err("open(%s, O_RDONLY)", file_name); } } #if defined(WITH_LIMITS) && defined(WITH_THRESHOLDS) if (l_flag && t_flag) exit_errx("-l and -t flags cannot be used together"); #endif report_date_error = ignore_date_error != 2; #ifdef WITH_LIMITS if (l_flag) { /* -l */ if (b_flag) limit_txt_to_bin(); else limit_bin_to_txt(); } else #endif #ifdef WITH_THRESHOLDS if (t_flag) { /* -t */ if (b_flag) threshold_txt_to_bin(); else threshold_bin_to_txt(); } else #endif { if (b_flag) rule_txt_to_bin(); else rule_bin_to_txt(); } if (!use_stdin) { if (b_flag) { if (fclose(src_fp) != 0) exit_err("file %s: fclose", file_name); } else { if (close(src_fd) < 0) exit_err("file %s: close", file_name); } } return 0; }