/*- * Copyright (c) 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: sdb_st_mod.c,v 1.4 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" #if IPA_ST_MOD_API_VERSION != 1 # error This module supports only ipa_st_mod API version 1 #endif #define ST_MOD_API_VERSION 1 /* Supported ipa_st_mod API. */ #if IPA_MEMFUNC_API_VERSION != 1 # error This module supports only ipa_memfunc API version 1 #endif #define MEMFUNC_API_VERSION 1 /* Supported memfunc API. */ #ifdef WITH_PTHREAD # define MOD_FLAGS IPA_MOD_FLAG_PTHREAD_SAFE #else # define MOD_FLAGS 0 #endif #define MEMFLAG_OPTIMIZE IPA_MEMFUNC_FLAG_OPTIMIZE #define MOD_NAME "ipa_st_sdb" #define MOD_ST_NAME "sdb" #define MOD_CONF_PREFIX "sdb" #define MARRAY_NAME(x) MOD_NAME ":" #x #define MZONE_NAME(x) MOD_NAME ":" #x #define MTYPE_NAME(x) MOD_NAME ":m_" #x extern struct ipa_st_mod IPA_ST_MOD_ENTRY(ipa_st_sdb); /* Is defined at the end of this file. */ static const ipa_suppfunc *suppfunc; /* Exported support functions. */ static const ipa_memfunc *memfunc; /* Exported memory manipulation functions. */ static ipa_mem_type *m_parser; /* Memory from parser. */ static ipa_mem_type *m_anon; /* Anonymous memory allocations. */ static ipa_mem_type *m_tmp; /* Temporary memory allocations. */ static void *(*mem_malloc)(size_t, ipa_mem_type *); static void *(*mem_realloc)(void *, size_t, ipa_mem_type *); static char *(*mem_strdup)(const char *, ipa_mem_type *); static void (*mem_free)(void *, ipa_mem_type *); static ipa_marray *(*marray_init)(const char *, const char *, u_int, void **, size_t, u_int, u_int); static void (*marray_deinit)(ipa_marray *); static int (*marray_alloc)(ipa_marray *, u_int *, int); static void (*marray_free)(ipa_marray *, u_int); static void (*marray_minimize)(ipa_marray *); static int (*marray_check_index)(ipa_marray *, u_int); static ipa_mzone *(*mzone_init)(const char *, const char *, u_int, size_t, u_int, u_int); static void (*mzone_deinit)(ipa_mzone *); static void *(*mzone_alloc)(ipa_mzone *); static void (*mzone_free)(ipa_mzone *, void *); static u_int (*mzone_nused)(ipa_mzone *); static int config_section = 0; /* 1, if there was sdb: {} section. */ static int quiet; /* sdb: { quiet } */ static int allow_symlinks; /* sdb: { allow_symlinks } */ static int check_version; /* sdb: { check_version } */ static char *global_db_dir; /* global { sdb:db_dir } */ static char *db_dir_default = IPA_SDB_DB_DIR; /* Default database directory. */ static int global_db_dir_checked; /* Nonzero, if global_db_dir was checked. */ #define DESC_LIST_NALLOC 100 #define RULE_STAT_NALLOC 200 #define NREC_PER_READ 50 #define RULE_NSIZE 30 #define RULE_NALLOC 20 #ifdef WITH_LIMITS /* * Module's data for limit{}. */ struct limit { const char *limit_name; /* Limit's name. */ u_int limitno; /* Limit order number. */ char *limit_dir; /* Directory for limit. */ }; #define LIMIT_STAT_NALLOC 50 #define LIMIT(r, i) ((r)->limits[i]) /* Pointer to limit by number. */ #define LIMIT_NSIZE RULE_NSIZE #define LIMIT_NALLOC RULE_NALLOC static ipa_mzone *limit_mzone; /* Mzone for all limits. */ #endif /* WITH_LIMITS*/ #ifdef WITH_THRESHOLDS /* * Module's data for threshold{}. */ struct threshold { const char *threshold_name;/* Threshold's name. */ u_int thresholdno; /* Threshold order number. */ char *threshold_dir; /* Directory for threshold. */ }; #define THRESHOLD(r, i) ((r)->thresholds[i]) /* Pointer to threshold by number. */ #define THRESHOLD_NSIZE RULE_NSIZE #define THRESHOLD_NALLOC RULE_NALLOC static ipa_mzone *threshold_mzone; /* Mzone for all thresholds. */ #endif /* WITH_THRESHOLDS */ #define RULE_INITED 0x01 /* Rule was inited in init_rule(). */ /* * Module's data for rule{}. */ struct rule { const char *rule_name; /* Rule's name. */ u_int ruleno; /* Number of rule. */ char *db_dir; /* rule { sdb:db_dir } */ #ifdef WITH_LIMITS struct limit **limits; /* Array of pointers to rule's limits. */ u_int nlimits; /* Number of limits. */ char *limits_dir; /* Directory for limits. */ #endif #ifdef WITH_THRESHOLDS struct threshold **thresholds; /* Array of pointers to rule's thresholds. */ u_int nthresholds; /* Number of thresholds. */ char *thresholds_dir;/* Directory for thresholds. */ #endif char *rule_dir; /* Directory for rule. */ u_int ref_count; /* Reference counter. */ u_int flags; /* RULE_xxx flags. */ TAILQ_ENTRY(rule) link; /* For list of all rules. */ }; static struct rule **rules_ptr; /* Array of pointers to rules. */ static u_int nstatrules; /* Number of static rules. */ static ipa_mzone *rule_mzone; /* Mzone for all rules. */ static ipa_marray *rules_ptr_marray; /* Marray for array of pointers to rules. */ #define RULE(x) rules_ptr[x] /* Pointer to rule by number. */ static TAILQ_HEAD(, rule) rules_list; /* List of all rules. */ static struct rule *currule; /* Current rule. */ static const char *currulename; /* Name of current rule. */ static int in_rule; /* 1, if we are in rule{} section. */ static int (*stat_func)(const char *, struct stat *); /* stat() or lstat(). */ static const char *stat_func_name; /* "stat" or "lstat". */ #define cnt_from_base(cnt, c_high, c_low) do { \ (cnt) = ntohl(c_high); \ (cnt) <<= 32; \ (cnt) += ntohl(c_low); \ } while (/* CONSTCOND */ 0) #define MONTHES_IN_YEAR 12 #define HOURS_IN_DAY 24 #define SECONDS_IN_MINUTE (60) #define MINUTES_IN_HOUR (60) #define SECONDS_IN_HOUR (MINUTES_IN_HOUR * SECONDS_IN_MINUTE) static void logmsg(int, const char *, ...) ATTR_FORMAT(printf, 2, 3); static void logmsgx(int, const char *, ...) ATTR_FORMAT(printf, 2, 3); static void logconferrx(const char *, ...) ATTR_FORMAT(printf, 1, 2); static int mem_asprintf(ipa_mem_type *, char **, const char *, ...) ATTR_FORMAT(printf, 3, 4); /* * A wrapper for imported logmsg() function. */ static void logmsg(int priority, const char *format, ...) { va_list ap; va_start(ap, format); suppfunc->logmsg(MOD_NAME, priority, errno, format, ap); va_end(ap); } /* * A wrapper for imported logmsg() function, * but error code is always zero here. */ static void logmsgx(int priority, const char *format, ...) { va_list ap; va_start(ap, format); suppfunc->logmsg(MOD_NAME, priority, 0, format, ap); va_end(ap); } /* * A wrapper for imported logconferr() function, * but error code is always zero here. */ static void logconferrx(const char *format, ...) { va_list ap; va_start(ap, format); suppfunc->logconferr(MOD_NAME, 0, format, ap); va_end(ap); } /* * A wrapper for imported print_param_name() function. */ static void print_param_name(const char *name) { suppfunc->print_param_name(MOD_CONF_PREFIX, name); } /* * A wrapper for imported print_param_end() function. */ static void print_param_end(void) { suppfunc->print_param_end(); } static int mem_asprintf(ipa_mem_type *mem_type, char **bufp, const char *format, ...) { int ret; va_list ap; va_start(ap, format); ret = memfunc->mem_vasprintf(mem_type, bufp, format, ap); va_end(ap); return ret; } /* * A wrapper for read(2) syscall. */ static ssize_t readn(int fd, const char *fname, 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) { logmsg(IPA_LOG_WARNING, "readn: read(%s)", fname); nread = 0; } else { logmsg(IPA_LOG_ERR, "readn: read(%s)", fname); return -1; } } else if (nread == 0) break; /* EOF */ nleft -= nread; if (nleft == 0) break; ptr += nread; logmsgx(IPA_LOG_WARNING, "readn: read: short read from %s", fname); } return n - nleft; } static struct rule * alloc_rule(u_int ruleno) { struct rule *rule; if ( (rule = mzone_alloc(rule_mzone)) == NULL) { logconferrx("alloc_rule: mzone_alloc fauiled"); return NULL; } rule->rule_name = currulename; rule->ruleno = ruleno; #ifdef WITH_LIMITS rule->limits = NULL; rule->nlimits = 0; rule->limits_dir = NULL; #endif #ifdef WITH_THRESHOLDS rule->thresholds = NULL; rule->nthresholds = 0; rule->thresholds_dir = NULL; #endif rule->db_dir = rule->rule_dir = NULL; rule->ref_count = 1; rule->flags = 0; if (marray_alloc(rules_ptr_marray, &ruleno, 1) < 0) { logconferrx("alloc_rule: marray_alloc failed"); mzone_free(rule_mzone, rule); return NULL; } RULE(ruleno) = rule; TAILQ_INSERT_TAIL(&rules_list, rule, link); return rule; } static struct rule * alloc_currule(void) { return currule = alloc_rule(nstatrules - 1); } /* * Parse "sdb:" section. */ /* ARGSUSED */ static int parse_config_section(void *arg ATTR_UNUSED) { config_section = 1; return 0; } /* * Parse "db_dir" parameter. */ static int parse_db_dir(void *arg) { char *str = *(char **)arg, **dstp; if (*str == '\0') { logconferrx("argument should be a non-empty string"); return -1; } if (*str != '/') { logconferrx("directory should be given with absolute path"); return -1; } if (in_rule) { if (currule == NULL) if (alloc_currule() == NULL) return -1; dstp = &currule->db_dir; } else dstp = &global_db_dir; mem_free(*dstp, m_parser); *dstp = str; return 0; } /* * Parse "quiet" parameter. */ static int parse_quiet(void *arg) { quiet = *(int *)arg; return 0; } /* * Parse "allow_symlinks" parameter. */ static int parse_allow_symlinks(void *arg) { allow_symlinks = *(int *)arg; return 0; } /* * Parse "check_version" parameter. */ static int parse_check_version(void *arg) { check_version = *(int *)arg; return 0; } static int conf_event(u_int event, u_int no, const void *arg) { switch (event) { case IPA_CONF_EVENT_RULE_BEGIN: nstatrules = no + 1; in_rule = 1; currulename = arg; break; case IPA_CONF_EVENT_RULE_END: currule = NULL; in_rule = 0; break; } return 0; } static void conf_real(void) { if (global_db_dir == NULL) global_db_dir = db_dir_default; if (quiet < 0) quiet = 1; if (allow_symlinks < 0) allow_symlinks = 0; if (check_version < 0) check_version = 1; } static int conf_mimic_real(void) { struct rule *rule; conf_real(); config_section = 1; TAILQ_FOREACH(rule, &rules_list, link) if (rule->db_dir == NULL) rule->db_dir = global_db_dir; return 0; } static int conf_init(void) { suppfunc = IPA_ST_MOD_ENTRY(ipa_st_sdb).suppfunc; memfunc = IPA_ST_MOD_ENTRY(ipa_st_sdb).memfunc; if (memfunc->api_ver != MEMFUNC_API_VERSION) { logconferrx("conf_init: module understands memfunc API version %u, exported memfunc API version is %u", MEMFUNC_API_VERSION, memfunc->api_ver); return -1; } m_parser = memfunc->m_parser; if ((m_anon = memfunc->mem_type_new(MTYPE_NAME(anon), "Anonymous memory", 0)) == NULL || (m_tmp = memfunc->mem_type_new(MTYPE_NAME(tmp), "Temporal memory", 0)) == NULL) { logconferrx("conf_init: mem_type_new failed"); return -1; } mem_malloc = memfunc->mem_malloc; mem_realloc = memfunc->mem_realloc; mem_strdup = memfunc->mem_strdup; mem_free = memfunc->mem_free; marray_init = memfunc->marray_init; marray_deinit = memfunc->marray_deinit; marray_alloc = memfunc->marray_alloc; marray_free = memfunc->marray_free; marray_minimize = memfunc->marray_minimize; marray_check_index = memfunc->marray_check_index; mzone_init = memfunc->mzone_init; mzone_deinit = memfunc->mzone_deinit; mzone_alloc = memfunc->mzone_alloc; mzone_free = memfunc->mzone_free; mzone_nused = memfunc->mzone_nused; if ( (rules_ptr_marray = marray_init(MARRAY_NAME(rules_ptr), "Pointers to rules", 0, (void *)&rules_ptr, sizeof(struct rule *), RULE_NSIZE, RULE_NALLOC)) == NULL) { logconferrx("conf_init: marray_init failed"); return -1; } if ( (rule_mzone = mzone_init(MZONE_NAME(rule), "Rules", MEMFLAG_OPTIMIZE, sizeof(struct rule), RULE_NSIZE, RULE_NALLOC)) == NULL) { logconferrx("conf_init: mzone_init failed"); return -1; } nstatrules = 0; in_rule = 0; currule = NULL; global_db_dir = NULL; quiet = allow_symlinks = check_version = -1; TAILQ_INIT(&rules_list); return 0; } static int conf_deinit(void) { /* Just to prevent of using incorrect link in alloc_rule(). */ currulename = NULL; return 0; } static void show_db_dir(const char *db_dir) { if (db_dir != NULL) { print_param_name("db_dir"); suppfunc->print_string(db_dir); print_param_end(); } } static void show_boolean(const char *param_name, int boolean) { if (boolean >= 0) { suppfunc->print_param_name((char *)NULL, param_name); suppfunc->print_boolean(boolean); print_param_end(); } } static void conf_show(u_int sect_id, u_int no) { const struct rule *rule; switch (sect_id) { case IPA_CONF_SECT_ROOT: if (config_section) { suppfunc->print_sect_name(MOD_ST_NAME, ""); suppfunc->print_sect_begin(); suppfunc->set_indent(1); show_boolean("quiet", quiet); show_boolean("allow_symlinks", allow_symlinks); show_boolean("check_version", check_version); suppfunc->set_indent(0); suppfunc->print_sect_end(); } break; case IPA_CONF_SECT_GLOBAL: show_db_dir(global_db_dir); break; case IPA_CONF_SECT_RULE: if (marray_check_index(rules_ptr_marray, no)) { rule = RULE(no); show_db_dir(rule->db_dir); } break; } } static int st_pre_init(void) { if (!quiet) logmsgx(IPA_LOG_INFO, "initing SDB statistics, version "IPA_SDB_VERSION); conf_real(); if (allow_symlinks > 0) { stat_func = stat; stat_func_name = "stat"; } else { stat_func = lstat; stat_func_name = "lstat"; } /* Try to minimize rules_ptr_marray. */ if (nstatrules != 0) marray_minimize(rules_ptr_marray); global_db_dir_checked = 0; #ifdef WITH_LIMITS limit_mzone = NULL; #endif #ifdef WITH_THRESHOLDS threshold_mzone = NULL; #endif return 0; } static int st_init(void) { return 0; } static int check_file(const char *path, const struct stat *statbuf) { if (!S_ISREG(statbuf->st_mode)) { logmsgx(IPA_LOG_ERR, "check_file: %s: is expected to be a regular file", path); return -1; } return 0; } static int check_dir(const char *path, const struct stat *statbuf) { if (!S_ISDIR(statbuf->st_mode)) { logmsgx(IPA_LOG_ERR, "check_dir: %s: is expected to be a directory", path); return -1; } return 0; } static int check_db_dir(const char *db_dir) { u_int version; char *version_file; FILE *fp; struct stat statbuf; /* Check that main database directory exists. */ if (db_dir == global_db_dir && global_db_dir_checked) return 0; if (stat_func(db_dir, &statbuf) < 0) { logmsg(IPA_LOG_ERR, "check_db_dir: %s(%s)", stat_func_name, db_dir); return -1; } if (check_dir(db_dir, &statbuf) < 0) { logmsgx(IPA_LOG_ERR, "check_db_dir: check_dir failed"); return -1; } if (check_version) { /* Check database format version file. */ if (mem_asprintf(m_tmp, &version_file, "%s/"IPA_SDB_VERSION_FILE, db_dir) < 0) { logmsgx(IPA_LOG_ERR, "check_db_dir: mem_asprintf failed"); return -1; } fp = NULL; if (stat_func(version_file, &statbuf) < 0) { if (errno != ENOENT) { logmsg(IPA_LOG_ERR, "check_db_dir: %s(%s)", stat_func_name, version_file); goto failed; } logmsgx(IPA_LOG_WARNING, "check_db_dir: file %s does not exist, cannot check version of format of %s database", version_file, db_dir); } else { if ( (fp = fopen(version_file, "r")) == NULL) { logmsg(IPA_LOG_ERR, "check_db_dir: fopen(%s, \"r\")", version_file); goto failed; } if (fscanf(fp, "%u", &version) != 1) { logmsg(IPA_LOG_ERR, "check_db_dir: fscanf(%s, \"%%u\") failed", version_file); goto failed; } if (version != IPA_SDB_FORMAT_VERSION) { logmsgx(IPA_LOG_ERR, "check_db_dir: version of format of %s database is %u, current version of format is %u", db_dir, version, IPA_SDB_FORMAT_VERSION); goto failed; } if (fclose(fp) != 0) { logmsg(IPA_LOG_ERR, "check_db_dir: fclose(%s)", version_file); fp = NULL; goto failed; } } mem_free(version_file, m_tmp); } if (db_dir == global_db_dir) global_db_dir_checked = 1; return 0; failed: if (fp != NULL) if (fclose(fp) != 0) logmsg(IPA_LOG_ERR, "check_db_dir: fclose(%s)", version_file); mem_free(version_file, m_tmp); return -1; } static void free_rule(struct rule *rule) { rule->ref_count--; if (rule->ref_count == 0) { TAILQ_REMOVE(&rules_list, rule, link); if (rule->db_dir != global_db_dir) mem_free(rule->db_dir, m_parser); #ifdef WITH_LIMITS mem_free(rule->limits, m_anon); mem_free(rule->limits_dir, m_anon); #endif #ifdef WITH_THRESHOLDS mem_free(rule->thresholds, m_anon); mem_free(rule->thresholds_dir, m_anon); #endif mem_free(rule->rule_dir, m_anon); marray_free(rules_ptr_marray, rule->ruleno); mzone_free(rule_mzone, rule); } } /* * The same as free_rule(), but free only those memory, which * could be allocated during configuration phase. */ static void free_rule_fast(struct rule *rule) { rule->ref_count--; if (rule->ref_count == 0) { TAILQ_REMOVE(&rules_list, rule, link); if (rule->db_dir != global_db_dir) mem_free(rule->db_dir, m_parser); marray_free(rules_ptr_marray, rule->ruleno); mzone_free(rule_mzone, rule); } } static int st_init_rule(u_int ruleno, const char *rule_name) { struct rule *rule; struct stat statbuf; if (!marray_check_index(rules_ptr_marray, ruleno)) { if ( (rule = alloc_rule(ruleno)) == NULL) { logmsgx(IPA_LOG_ERR, "st_init_rule: alloc_rule failed"); return -1; } } else rule = RULE(ruleno); /* Inherit settings from global{}. */ if (rule->db_dir == NULL) rule->db_dir = global_db_dir; rule->rule_name = rule_name; if (check_db_dir(rule->db_dir) < 0) { logmsgx(IPA_LOG_ERR, "st_init_rule: check_db_dir failed"); goto failed; } if (mem_asprintf(m_anon, &rule->rule_dir, "%s/%s", rule->db_dir, rule_name) < 0) { logmsgx(IPA_LOG_ERR, "st_init_rule: mem_asprintf failed"); goto failed; } if (stat_func(rule->rule_dir, &statbuf) < 0) { if (errno == ENOENT) logmsgx(IPA_LOG_ERR, "st_init_rule: unknown rule %s: cannot find rule directory %s", rule_name, rule->rule_dir); else logmsg(IPA_LOG_ERR, "st_init_rule: %s(%s)", stat_func_name, rule->rule_dir); goto failed; } if (check_dir(rule->rule_dir, &statbuf) < 0) { logmsgx(IPA_LOG_ERR, "st_init_rule: check_dir failed"); goto failed; } rule->flags |= RULE_INITED; return 0; failed: free_rule(rule); return -1; } #ifdef WITH_LIMITS static void free_limit(struct rule *rule, struct limit *limit) { mem_free(limit->limit_dir, m_anon); LIMIT(rule, limit->limitno) = NULL; mzone_free(limit_mzone, limit); free_rule(rule); } static int st_init_limit(u_int ruleno, const char *rule_name, u_int limitno, const char *limit_name) { struct rule *rule; struct limit *limit, **limits; struct stat statbuf; if (!marray_check_index(rules_ptr_marray, ruleno)) { if (st_init_rule(ruleno, rule_name) < 0) { logmsgx(IPA_LOG_ERR, "st_init_limit: st_init_rule failed"); return -1; } rule = RULE(ruleno); } else { rule = RULE(ruleno); rule->ref_count++; } if (rule->limits_dir == NULL) if (mem_asprintf(m_anon, &rule->limits_dir, "%s/"IPA_SDB_LIMITS_DIR, rule->rule_dir) < 0) { logmsgx(IPA_LOG_ERR, "st_init_limit: mem_asprintf failed"); goto failed; } if (limit_mzone == NULL) if ( (limit_mzone = mzone_init(MZONE_NAME(limit), "Limits", 0, sizeof(struct limit), LIMIT_NSIZE, LIMIT_NALLOC)) == NULL) { logmsgx(IPA_LOG_ERR, "st_init_limit: mzone_init failed"); goto failed; } if (rule->nlimits < limitno + 1) { if ( (limits = mem_realloc(rule->limits, (limitno + 1) * sizeof *rule->limits, m_anon)) == NULL) { logmsgx(IPA_LOG_ERR, "st_init_limit: mem_realloc failed"); goto failed; } rule->limits = limits; rule->nlimits = limitno + 1; LIMIT(rule, limitno) = NULL; } else { logmsgx(IPA_LOG_ERR, "st_init_limit: unexpected order number %u of limit", limitno); goto failed; } if ( (limit = mzone_alloc(limit_mzone)) == NULL) { logmsgx(IPA_LOG_ERR, "st_init_limit: mzone_alloc failed"); goto failed; } LIMIT(rule, limitno) = limit; limit->limit_name = limit_name; limit->limitno = limitno; if (mem_asprintf(m_anon, &limit->limit_dir, "%s/%s", rule->limits_dir, limit_name) < 0) { logmsgx(IPA_LOG_ERR, "st_init_limit: mem_asprintf faield"); goto limit_failed; } if (stat_func(limit->limit_dir, &statbuf) < 0) { if (errno == ENOENT) logmsgx(IPA_LOG_ERR, "st_init_limit: unknown rule %s, limit %s: cannot find limit directory %s", rule->rule_name, limit_name, limit->limit_dir); else logmsg(IPA_LOG_ERR, "st_init_limit: %s(%s)", stat_func_name, limit->limit_dir); goto limit_failed; } if (check_dir(limit->limit_dir, &statbuf) < 0) { logmsgx(IPA_LOG_ERR, "st_init_limit: check_dir failed"); goto limit_failed; } return 0; limit_failed: free_limit(rule, limit); return -1; failed: free_rule(rule); return -1; } static int st_deinit_limit(u_int ruleno, u_int limitno) { struct rule *rule; struct limit *limit; if (marray_check_index(rules_ptr_marray, ruleno)) { /* Such rule was registered. */ rule = RULE(ruleno); if (limitno < rule->nlimits) { /* * Such limit was registered, but probably * free_limit() was already called for it. */ limit = LIMIT(rule, limitno); if (limit != NULL) free_limit(rule, limit); } } return 0; } #else # define st_init_limit NULL # define st_deinit_limit NULL #endif /* WITH_LIMITS */ #ifdef WITH_THRESHOLDS static void free_threshold(struct rule *rule, struct threshold *threshold) { mem_free(threshold->threshold_dir, m_anon); THRESHOLD(rule, threshold->thresholdno) = NULL; mzone_free(threshold_mzone, threshold); free_rule(rule); } static int st_init_threshold(u_int ruleno, const char *rule_name, u_int thresholdno, const char *threshold_name) { struct rule *rule; struct threshold *threshold, **thresholds; struct stat statbuf; if (!marray_check_index(rules_ptr_marray, ruleno)) { if (st_init_rule(ruleno, rule_name) < 0) { logmsgx(IPA_LOG_ERR, "st_init_threshold: st_init_rule failed"); return -1; } rule = RULE(ruleno); } else { rule = RULE(ruleno); rule->ref_count++; } if (rule->thresholds_dir == NULL) if (mem_asprintf(m_anon, &rule->thresholds_dir, "%s/"IPA_SDB_THRESHOLDS_DIR, rule->rule_dir) < 0) { logmsgx(IPA_LOG_ERR, "st_init_threshold: mem_asprintf failed"); goto failed; } if (threshold_mzone == NULL) if ( (threshold_mzone = mzone_init(MZONE_NAME(threshold), "Thresholds", 0, sizeof(struct threshold), THRESHOLD_NSIZE, THRESHOLD_NALLOC)) == NULL) { logmsgx(IPA_LOG_ERR, "st_init_threshold: mzone_init failed"); goto failed; } if (rule->nthresholds < thresholdno + 1) { if ( (thresholds = mem_realloc(rule->thresholds, (thresholdno + 1) * sizeof *rule->thresholds, m_anon)) == NULL) { logmsgx(IPA_LOG_ERR, "st_init_threshold: mem_realloc failed"); goto failed; } rule->thresholds = thresholds; rule->nthresholds = thresholdno + 1; THRESHOLD(rule, thresholdno) = NULL; } else { logmsgx(IPA_LOG_ERR, "st_init_threshold: unexpected order number %u of threshold", thresholdno); goto failed; } if ( (threshold = mzone_alloc(threshold_mzone)) == NULL) { logmsgx(IPA_LOG_ERR, "st_init_threshold: mzone_alloc failed"); goto failed; } THRESHOLD(rule, thresholdno) = threshold; threshold->threshold_name = threshold_name; threshold->thresholdno = thresholdno; if (mem_asprintf(m_anon, &threshold->threshold_dir, "%s/%s", rule->thresholds_dir, threshold_name) < 0) { logmsgx(IPA_LOG_ERR, "st_init_threshold: mem_asprintf faield"); goto threshold_failed; } if (stat_func(threshold->threshold_dir, &statbuf) < 0) { if (errno == ENOENT) logmsgx(IPA_LOG_ERR, "st_init_threshold: unknown rule %s, threshold %s: cannot find threshold directory %s", rule->rule_name, threshold_name, threshold->threshold_dir); else logmsg(IPA_LOG_ERR, "st_init_threshold: %s(%s)", stat_func_name, threshold->threshold_dir); goto threshold_failed; } if (check_dir(threshold->threshold_dir, &statbuf) < 0) { logmsgx(IPA_LOG_ERR, "st_init_threshold: check_dir failed"); goto threshold_failed; } return 0; threshold_failed: free_threshold(rule, threshold); return -1; failed: free_rule(rule); return -1; } static int st_deinit_threshold(u_int ruleno, u_int thresholdno) { struct rule *rule; struct threshold *threshold; if (marray_check_index(rules_ptr_marray, ruleno)) { /* Such rule was registered. */ rule = RULE(ruleno); if (thresholdno < rule->nthresholds) { /* * Such threshold was registered, but probably * free_threshold() was already called for it, */ threshold = THRESHOLD(rule, thresholdno); if (threshold != NULL) free_threshold(rule, threshold); } } return 0; } #else # define st_init_threshold NULL # define st_deinit_threshold NULL #endif /* WITH_THRSHOLDS */ static int st_deinit_rule(u_int ruleno) { if (marray_check_index(rules_ptr_marray, ruleno)) { /* Such rule was registered. */ free_rule(RULE(ruleno)); } return 0; } static int st_deinit(void) { u_int n; struct rule *rule; if (!quiet) logmsgx(IPA_LOG_INFO, "deiniting SDB statistics"); if (global_db_dir != db_dir_default) mem_free(global_db_dir, m_parser); while ( (rule = TAILQ_FIRST(&rules_list)) != NULL) { if (rule->flags & RULE_INITED) { logmsgx(IPA_LOG_ERR, "internal error: st_deinit: rule %s: rule was inited, but was not deinited (ref_count %u)", rule->rule_name, rule->ref_count); return -1; } free_rule_fast(rule); } if ( (n = memfunc->marray_nused(rules_ptr_marray)) != 0) { logmsgx(IPA_LOG_ERR, "internal error: st_deinit: rules_ptr_marray is not empty: %u", n); return -1; } marray_deinit(rules_ptr_marray); if ( (n = mzone_nused(rule_mzone)) != 0) { logmsgx(IPA_LOG_ERR, "internal error: st_deinit: rule_mzone is not empty: %u", n); return -1; } mzone_deinit(rule_mzone); #ifdef WITH_LIMITS if (limit_mzone != NULL) { if ( (n = mzone_nused(limit_mzone)) != 0) { logmsgx(IPA_LOG_ERR, "internal error: st_deinit: limit_mzone is not empty: %u", n); return -1; } mzone_deinit(limit_mzone); } #endif #ifdef WITH_THRESHOLDS if (threshold_mzone != NULL) { if ( (n = mzone_nused(threshold_mzone)) != 0) { logmsgx(IPA_LOG_ERR, "internal error: st_deinit: threshold_mzone is not empty: %u", n); return -1; } mzone_deinit(threshold_mzone); } #endif return 0; } static int read_info(ipa_mem_type *mem_type, const char *path, char **infop) { int fd; ssize_t size; char *info; struct stat statbuf; if (stat_func(path, &statbuf) < 0) { if (errno == ENOENT) goto no_info; logmsg(IPA_LOG_ERR, "read_info: %s(%s)", stat_func_name, path); return -1; } if ( (fd = open(path, O_RDONLY)) < 0) { if (errno == ENOENT) goto no_info; logmsg(IPA_LOG_ERR, "read_info: open(%s, O_RDONLY)", path); return -1; } info = NULL; size = statbuf.st_size; if ( (info = mem_malloc(size + 1, mem_type)) == NULL) { logmsgx(IPA_LOG_ERR, "read_info: mem_malloc failed"); goto failed; } if ( (size = readn(fd, path, info, size)) < 0) { logmsgx(IPA_LOG_ERR, "read_info: readn failed"); goto failed; } info[size] = '\0'; if (close(fd) < 0) { fd = -1; logmsg(IPA_LOG_ERR, "read_info: close(%s)", path); goto failed; } *infop = info; return 0; failed: if (fd >= 0) if (close(fd) < 0) logmsg(IPA_LOG_ERR, "read_info: close(%s)", path); mem_free(info, mem_type); return -1; no_info: *infop = NULL; return 0; } static int st_get_rule_info(u_int ruleno, ipa_mem_type *mem_type, char **infop) { char *path; const struct rule *rule; rule = RULE(ruleno); if (mem_asprintf(m_tmp, &path, "%s/"IPA_SDB_INFO_FILE, rule->rule_dir) < 0) { logmsgx(IPA_LOG_ERR, "st_get_rule_info: mem_asprintf failed"); return -1; } if (read_info(mem_type, path, infop) < 0) { logmsgx(IPA_LOG_ERR, "st_get_rule_info: read_info failed"); goto failed; } mem_free(path, m_tmp); return 0; failed: mem_free(path, m_tmp); return -1; } /* * Generic function for geting list of rule, limits and thresholds. */ static int get_desc_list(const char *dir_path, const regex_t *regexp, const char *ignore_name, ipa_mem_type *mem_type, u_int *n, struct ipa_entity_desc **listp) { u_int i, nalloc; char *info_path, *name, *info; DIR *dirp; struct dirent *dp; struct ipa_entity_desc *desc, *desc_list; if ( (dirp = opendir(dir_path)) == NULL) { logmsg(IPA_LOG_ERR, "get_desc_list: opendir(%s)", dir_path); return -1; } i = nalloc = 0; desc_list = NULL; while ( (dp = readdir(dirp)) != NULL) { /* Optimize and don't use strcmp(3) here for "." and "..". */ if (*dp->d_name == '.') switch (*(dp->d_name + 1)) { case '\0': continue; case '.': if (*(dp->d_name + 2) == '\0') continue; } if (ignore_name != NULL) if (strcmp(dp->d_name, ignore_name) == 0) continue; if (regexp != NULL) if (regexec(regexp, dp->d_name, 0, (regmatch_t *)NULL, 0) != 0) continue; info = info_path = NULL; if ( (name = mem_strdup(dp->d_name, mem_type)) == NULL) { logmsgx(IPA_LOG_ERR, "get_desc_list: mem_strdup failed"); goto failed; } if (mem_asprintf(m_tmp, &info_path, "%s/%s/"IPA_SDB_INFO_FILE, dir_path, name) < 0) { logmsgx(IPA_LOG_ERR, "get_desc_list: mem_asprintf failed"); goto failed; } if (read_info(mem_type, info_path, &info) < 0) { logmsgx(IPA_LOG_ERR, "get_desc_list: read_info failed"); goto failed; } mem_free(info_path, m_tmp); info_path = NULL; if (nalloc == 0) { if ( (desc = mem_realloc(desc_list, (i + DESC_LIST_NALLOC) * sizeof *desc_list, mem_type)) == NULL) { logmsgx(IPA_LOG_ERR, "get_desc_list: mem_realloc failed"); goto failed; } desc_list = desc; nalloc = DESC_LIST_NALLOC; } desc = desc_list + i; desc->name = name; desc->info = info; ++i; --nalloc; } /* Minimized used memory. */ if (desc_list != NULL) if ( (desc = mem_realloc(desc_list, i * sizeof *desc_list, mem_type)) != NULL) desc_list = desc; *n = i; *listp = desc_list; return 0; failed: mem_free(name, mem_type); mem_free(info, mem_type); mem_free(info_path, m_tmp); if (desc_list != NULL) { for (desc = desc_list; desc < desc_list + i; ++desc) { mem_free(desc->name, mem_type); mem_free(desc->info, mem_type); } mem_free(desc_list, mem_type); } return -1; } static int st_get_rules_list(const char *pat ATTR_UNUSED, const regex_t *regexp, ipa_mem_type *mem_type, u_int *n, struct ipa_entity_desc **listp) { if (check_db_dir(global_db_dir) < 0) { logmsgx(IPA_LOG_ERR, "st_get_rules_list: check_db_dir failed"); return -1; } if (get_desc_list(global_db_dir, regexp, IPA_SDB_VERSION_FILE, mem_type, n, listp) < 0) { logmsgx(IPA_LOG_ERR, "st_get_rules_list: get_desc_list failed"); return -1; } return 0; } static int st_get_rule_stat(u_int ruleno, const ipa_tm *tm1, const ipa_tm *tm2, int exact, ipa_mem_type *mem_type, u_int *n, struct ipa_rule_stat **bufp) { int fd; int year, year1, year2, mon, mon1, mon2, mday, mday1, mday2; int sec1, sec2, rsec1, rsec2; char *path; u_int i, nalloc, nrecs; size_t src_size, recs_size; ssize_t nread; struct stat statbuf; const struct rule *rule; struct ipa_rule_stat *rule_stat_arr, *rule_stat; struct ipa_sdb_rule_record *recs, *rec; rule = RULE(ruleno); fd = -1; i = nalloc = 0; rule_stat_arr = NULL; recs_size = NREC_PER_READ * sizeof *recs; if ( (recs = mem_malloc(recs_size, m_tmp)) == NULL) { logmsgx(IPA_LOG_ERR, "st_get_rule_stat: mem_malloc failed"); return -1; } year1 = tm1->tm_year; year2 = tm2->tm_year; for (year = year1; year <= year2; ++year) { mon1 = year == year1 ? tm1->tm_mon : 1; mon2 = year == year2 ? tm2->tm_mon : MONTHES_IN_YEAR; for (mon = mon1; mon <= mon2; ++mon) { if (mem_asprintf(m_tmp, &path, "%s/%d%02d", rule->rule_dir, year, mon) < 0) { logmsgx(IPA_LOG_ERR, "st_get_rule_stat: mem_asprintf failed"); goto failed; } if (stat_func(path, &statbuf) < 0) { if (errno == ENOENT) { mem_free(path, m_tmp); continue; } logmsg(IPA_LOG_ERR, "st_get_rule_stat: %s(%s)", stat_func_name, path); goto failed; } if (check_file(path, &statbuf) < 0) { logmsgx(IPA_LOG_ERR, "st_get_rule_stat: check_file failed"); goto failed; } src_size = statbuf.st_size; if (src_size == 0) { mem_free(path, m_tmp); continue; } if ( (fd = open(path, O_RDONLY)) < 0) { logmsg(IPA_LOG_ERR, "st_get_rule_stat: open(%s, O_RDONLY)", path); goto failed; } if (year == year1 && mon == mon1) { mday1 = tm1->tm_mday; sec1 = tm1->tm_hour * SECONDS_IN_HOUR + tm1->tm_min * SECONDS_IN_MINUTE + tm1->tm_sec; } else { mday1 = 1; sec1 = 0; } if (year == year2 && mon == mon2) { mday2 = tm2->tm_mday; sec2 = tm2->tm_hour * SECONDS_IN_HOUR + tm2->tm_min * SECONDS_IN_MINUTE + tm2->tm_sec; } else { mday2 = 31; sec2 = HOURS_IN_DAY * SECONDS_IN_HOUR; } for (;;) { nread = src_size < recs_size ? src_size : recs_size; if ( (nread = readn(fd, path, recs, nread)) <= 0) break; if (nread % sizeof(*recs) != 0) { logmsgx(IPA_LOG_ERR, "st_get_rule_stat: readn from %s: short read %ld", path, (long)nread); goto failed; } for (rec = recs, nrecs = nread / sizeof *recs; nrecs > 0; ++rec, --nrecs) { mday = rec->mday; if (mday < mday1 || mday > mday2) continue; /* In days range. */ if (mday == mday1 || mday == mday2) { /* Check boundaries. */ rsec1 = rec->h1 * SECONDS_IN_HOUR + rec->m1 * SECONDS_IN_MINUTE + rec->s1; rsec2 = rec->h2 * SECONDS_IN_HOUR + rec->m2 * SECONDS_IN_MINUTE + rec->s2; if (exact) { if (mday == mday1 && rsec1 < sec1) continue; if (mday == mday2 && rsec2 > sec2) continue; } else { if (mday == mday1 && rsec1 < sec1 && rsec2 < sec1) continue; if (mday == mday2 && rsec1 > sec2 && rsec2 > sec2) continue; } } if (nalloc == 0) { if ( (rule_stat = mem_realloc(rule_stat_arr, (i + RULE_STAT_NALLOC) * sizeof *rule_stat_arr, mem_type)) == NULL) { logmsgx(IPA_LOG_ERR, "st_get_rule_stat: mem_realloc failed"); goto failed; } rule_stat_arr = rule_stat; nalloc = RULE_STAT_NALLOC; } rule_stat = rule_stat_arr + i; rule_stat->year = year; rule_stat->mon = mon; rule_stat->mday = mday; rule_stat->h1 = rec->h1; rule_stat->m1 = rec->m1; rule_stat->s1 = rec->s1; rule_stat->h2 = rec->h2; rule_stat->m2 = rec->m2; rule_stat->s2 = rec->s2; cnt_from_base(rule_stat->cnt, rec->c_high, rec->c_low); ++i; --nalloc; } if ( (src_size -= nread) == 0) break; } switch (nread) { case 0: logmsgx(IPA_LOG_WARNING, "st_get_rule_stat: readn from %s returned 0", path); break; case -1: logmsgx(IPA_LOG_ERR, "st_get_rule_stat: readn from %s failed", path); goto failed; } if (close(fd) < 0) { logmsg(IPA_LOG_ERR, "st_get_rule_stat: close(%s)", path); fd = -1; goto failed; } fd = -1; mem_free(path, m_tmp); } } mem_free(recs, m_tmp); /* Minimized used memory. */ if (rule_stat_arr != NULL) if ( (rule_stat = mem_realloc(rule_stat_arr, i * sizeof *rule_stat_arr, mem_type)) != NULL) rule_stat_arr = rule_stat; *n = i; *bufp = rule_stat_arr; return 0; failed: if (fd >= 0) if (close(fd) < 0) logmsg(IPA_LOG_ERR, "st_get_rule_stat: close(%s)", path); mem_free(path, m_tmp); mem_free(recs, m_tmp); mem_free(rule_stat_arr, mem_type); return -1; } #ifdef WITH_ANY_LIMITS /* * Convert machine independent ipa_sdb_date{} values to ipa_tm{}. */ static void copy_db_date_to_tm(const ipa_sdb_date *date, ipa_tm *tm) { tm->tm_year = ntohs(date->year); tm->tm_mon = date->mon; tm->tm_mday = date->mday; tm->tm_hour = date->hour; tm->tm_min = date->min; tm->tm_sec = date->sec; } #endif /* WITH_ANY_LIMITS */ #ifdef WITH_LIMITS static int st_get_limit_info(u_int ruleno, u_int limitno, ipa_mem_type *mem_type, char **infop) { char *path; const struct rule *rule; const struct limit *limit; rule = RULE(ruleno); limit = LIMIT(rule, limitno); if (mem_asprintf(m_tmp, &path, "%s/"IPA_SDB_INFO_FILE, limit->limit_dir) < 0) { logmsgx(IPA_LOG_ERR, "st_get_limit_info: mem_asprintf failed"); return -1; } if (read_info(mem_type, path, infop) < 0) { logmsgx(IPA_LOG_ERR, "st_get_limit_info: read_info failed"); goto failed; } mem_free(path, m_tmp); return 0; failed: mem_free(path, m_tmp); return -1; } static int st_get_limits_list(u_int ruleno, const char *pat ATTR_UNUSED, const regex_t *regexp, ipa_mem_type *mem_type, u_int *n, struct ipa_entity_desc **listp) { struct rule *rule; struct stat statbuf; rule = RULE(ruleno); if (rule->limits_dir == NULL) if (mem_asprintf(m_anon, &rule->limits_dir, "%s/"IPA_SDB_LIMITS_DIR, rule->rule_dir) < 0) { logmsgx(IPA_LOG_ERR, "st_get_limits_list: mem_asprintf failed"); return -1; } if (stat_func(rule->limits_dir, &statbuf) < 0) { if (errno == ENOENT) { *n = 0; *listp = NULL; return 0; } logmsgx(IPA_LOG_ERR, "st_get_limits_list: %s(%s)", stat_func_name, rule->limits_dir); return -1; } if (check_dir(rule->limits_dir, &statbuf) < 0) { logmsgx(IPA_LOG_ERR, "st_get_limits_list: check_dir failed"); return -1; } if (get_desc_list(rule->limits_dir, regexp, (const char *)NULL, mem_type, n, listp) < 0) { logmsgx(IPA_LOG_ERR, "st_get_limits_list: get_desc_list failed"); return -1; } return 0; } static void copy_sdb_limit_record(const struct ipa_sdb_limit_record *rec, struct ipa_limit_state *state) { state->event_date_set = 0; if (rec->set & IPA_SDB_LIMIT_EVENT_START_SET) { state->event_date_set = IPA_LIMIT_EVENT_START_SET; copy_db_date_to_tm(&rec->start, &state->event_date[IPA_LIMIT_EVENT_START]); } if (rec->set & IPA_SDB_LIMIT_EVENT_RESTART_SET) { state->event_date_set |= IPA_LIMIT_EVENT_RESTART_SET; copy_db_date_to_tm(&rec->restart, &state->event_date[IPA_LIMIT_EVENT_RESTART]); } if (rec->set & IPA_SDB_LIMIT_EVENT_RESTART_EXEC_SET) { state->event_date_set |= IPA_LIMIT_EVENT_RESTART_EXEC_SET; copy_db_date_to_tm(&rec->restart_exec, &state->event_date[IPA_LIMIT_EVENT_RESTART_EXEC]); } if (rec->set & IPA_SDB_LIMIT_EVENT_REACH_SET) { state->event_date_set |= IPA_LIMIT_EVENT_REACH_SET; copy_db_date_to_tm(&rec->reach, &state->event_date[IPA_LIMIT_EVENT_REACH]); } if (rec->set & IPA_SDB_LIMIT_EVENT_REACH_EXEC_SET) { state->event_date_set |= IPA_LIMIT_EVENT_REACH_EXEC_SET; copy_db_date_to_tm(&rec->reach_exec, &state->event_date[IPA_LIMIT_EVENT_REACH_EXEC]); } if (rec->set & IPA_SDB_LIMIT_EVENT_EXPIRE_SET) { state->event_date_set |= IPA_LIMIT_EVENT_EXPIRE_SET; copy_db_date_to_tm(&rec->expire, &state->event_date[IPA_LIMIT_EVENT_EXPIRE]); } if (rec->set & IPA_SDB_LIMIT_EVENT_EXPIRE_EXEC_SET) { state->event_date_set |= IPA_LIMIT_EVENT_EXPIRE_EXEC_SET; copy_db_date_to_tm(&rec->expire_exec, &state->event_date[IPA_LIMIT_EVENT_EXPIRE_EXEC]); } if (rec->set & IPA_SDB_LIMIT_EVENT_UPDATED_SET) { state->event_date_set |= IPA_LIMIT_EVENT_UPDATED_SET; copy_db_date_to_tm(&rec->updated, &state->event_date[IPA_LIMIT_EVENT_UPDATED]); } cnt_from_base(state->lim, rec->l_high, rec->l_low); cnt_from_base(state->cnt, rec->c_high, rec->c_low); } static int st_get_limit_stat(u_int ruleno, u_int limitno, const ipa_tm *tm1, const ipa_tm *tm2, ipa_mem_type *mem_type, u_int *n, struct ipa_limit_state **bufp) { int fd; int year, year1, year2, mon, mon1, mon2, mday, mday1, mday2; int sec1, sec2, rsec; char *path; u_int i, nalloc, nrecs; size_t src_size, recs_size; ssize_t nread; struct stat statbuf; struct ipa_sdb_limit_record *recs, rec1; struct ipa_limit_state *limit_state_arr, *limit_state; const struct ipa_sdb_limit_record *rec; const struct rule *rule; const struct limit *limit; const ipa_sdb_date *db_date; rule = RULE(ruleno); limit = LIMIT(rule, limitno); fd = -1; recs = NULL; limit_state_arr = NULL; if (tm1 == NULL) { if (mem_asprintf(m_tmp, &path, "%s/"IPA_SDB_LIMITS_DIR"/%s/"IPA_SDB_LIMIT_CURRENT, rule->rule_dir, limit->limit_name) < 0) { logmsgx(IPA_LOG_ERR, "st_get_limit_stat: mem_asprintf failed"); return -1; } if (stat_func(path, &statbuf) < 0) { if (errno != ENOENT) { logmsg(IPA_LOG_ERR, "st_get_limit_stat: %s(%s)", stat_func_name, path); goto failed; } src_size = 0; } else { if (check_file(path, &statbuf) < 0) { logmsgx(IPA_LOG_ERR, "st_get_limit_stat: check_file failed"); goto failed; } src_size = statbuf.st_size; if (src_size % sizeof *recs != 0) { logmsgx(IPA_LOG_ERR, "st_get_limit_stat: wrong size of file %s", path); goto failed; } } if ( (limit_state_arr = mem_malloc(sizeof *limit_state_arr, mem_type)) == NULL) { logmsgx(IPA_LOG_ERR, "st_get_limit_stat: mem_malloc failed"); goto failed; } i = 1; if (src_size == 0) limit_state_arr->lim = UINT64_C(0); else { if ( (fd = open(path, O_RDONLY)) < 0) { logmsg(IPA_LOG_ERR, "st_get_limit_stat: open(%s, O_RDONLY)", path); goto failed; } if (lseek(fd, -(off_t)sizeof(rec1), SEEK_END) == (off_t)-1) { logmsg(IPA_LOG_ERR, "st_get_limit_stat: lseek(%s, -%lu, SEEK_END", path, (u_long)sizeof(rec1)); goto failed; } if (readn(fd, path, &rec1, sizeof rec1) != sizeof rec1) { logmsgx(IPA_LOG_ERR, "st_get_limit_stat: readn failed"); goto failed; } if (close(fd) < 0) { logmsg(IPA_LOG_ERR, "st_get_limit_stat: close(%s)", path); fd = -1; goto failed; } copy_sdb_limit_record(&rec1, limit_state_arr); } mem_free(path, m_tmp); } else { recs_size = NREC_PER_READ * sizeof *recs; if ( (recs = mem_malloc(recs_size, m_tmp)) == NULL) { logmsgx(IPA_LOG_ERR, "st_get_limit_stat: mem_malloc failed"); return -1; } i = nalloc = 0; year1 = tm1->tm_year; year2 = tm2->tm_year; for (year = year1; year <= year2; ++year) { mon1 = year == year1 ? tm1->tm_mon : 1; mon2 = year == year2 ? tm2->tm_mon : MONTHES_IN_YEAR; for (mon = mon1; mon <= mon2; ++mon) { if (mem_asprintf(m_tmp, &path, "%s/"IPA_SDB_LIMITS_DIR"/%s/%d%02d", rule->rule_dir, limit->limit_name, year, mon) < 0) { logmsgx(IPA_LOG_ERR, "st_get_limit_stat: mem_asprintf failed"); goto failed; } if (stat_func(path, &statbuf) < 0) { if (errno == ENOENT) { mem_free(path, m_tmp); continue; } logmsgx(IPA_LOG_ERR, "st_get_limit_stat: %s(%s)", stat_func_name, path); goto failed; } if (check_file(path, &statbuf) < 0) { logmsgx(IPA_LOG_ERR, "st_get_limit_stat: check_file failed"); goto failed; } src_size = statbuf.st_size; if (src_size == 0) { mem_free(path, m_tmp); continue; } if ( (fd = open(path, O_RDONLY)) < 0) { logmsgx(IPA_LOG_ERR, "st_get_limit_stat: open(%s, O_RDONLY)", path); goto failed; } nread = src_size < recs_size ? src_size : recs_size; if (year == year1 && mon == mon1) { mday1 = tm1->tm_mday; sec1 = tm1->tm_hour * SECONDS_IN_HOUR + tm1->tm_min * SECONDS_IN_MINUTE + tm1->tm_sec; } else { mday1 = 1; sec1 = 0; } if (year == year2 && mon == mon2) { mday2 = tm2->tm_mday; sec2 = tm2->tm_hour * SECONDS_IN_HOUR + tm2->tm_min * SECONDS_IN_MINUTE + tm2->tm_sec; } else { mday2 = 31; sec2 = HOURS_IN_DAY * SECONDS_IN_HOUR; } for (;;) { nread = src_size < recs_size ? src_size : recs_size; if ( (nread = readn(fd, path, recs, nread)) <= 0) break; if (nread % sizeof(*recs) != 0) { logmsgx(IPA_LOG_ERR, "st_get_rule_stat: readn from %s: short read %ld", path, (long)nread); goto failed; } for (rec = recs, nrecs = nread / sizeof *recs; nrecs > 0; ++rec, --nrecs) { db_date = &rec->start; mday = db_date->mday; if (mday < mday1 || mday > mday2) continue; /* In days range. */ if (mday == mday1 || mday == mday2) { /* Check boundaries. */ rsec = db_date->hour * SECONDS_IN_HOUR + db_date->min * SECONDS_IN_MINUTE + db_date->sec; if (mday == mday1 && rsec < sec1) continue; if (mday == mday2 && rsec > sec2) continue; } if (nalloc == 0) { if ( (limit_state = mem_realloc(limit_state_arr, (i + LIMIT_STAT_NALLOC) * sizeof *limit_state_arr, mem_type)) == NULL) { logmsgx(IPA_LOG_ERR, "st_get_limit_stat: mem_realloc failed"); goto failed; } limit_state_arr = limit_state; nalloc = LIMIT_STAT_NALLOC; } limit_state = limit_state_arr + i; copy_sdb_limit_record(rec, limit_state); ++i; --nalloc; } if ( (src_size -= nread) == 0) break; } switch (nread) { case 0: logmsgx(IPA_LOG_WARNING, "st_get_limit_stat: readn from %s returned 0", path); break; case -1: logmsgx(IPA_LOG_ERR, "st_get_limit_stat: readn from %s failed", path); goto failed; } if (close(fd) < 0) { logmsgx(IPA_LOG_ERR, "st_get_limit_stat: close(%s)", path); fd = -1; goto failed; } fd = -1; mem_free(path, m_tmp); } } } mem_free(recs, m_tmp); /* Minimize used memory. */ if (limit_state_arr != NULL) if ( (limit_state = mem_realloc(limit_state_arr, i * sizeof *limit_state_arr, mem_type)) != NULL) limit_state_arr = limit_state; *n = i; *bufp = limit_state_arr; return 0; failed: if (fd >= 0) if (close(fd) < 0) logmsg(IPA_LOG_ERR, "st_get_limit_stat: close(%s)", path); mem_free(path, m_tmp); mem_free(recs, m_tmp); mem_free(limit_state_arr, mem_type); return -1; } #else # define st_get_limit_info NULL # define st_get_limits_list NULL # define st_get_limit_stat NULL #endif /* WITH_LIMITS */ #ifdef WITH_THRESHOLDS static int st_get_threshold_info(u_int ruleno, u_int thresholdno, ipa_mem_type *mem_type, char **infop) { char *path; const struct rule *rule; const struct threshold *threshold; rule = RULE(ruleno); threshold = THRESHOLD(rule, thresholdno); if (mem_asprintf(m_tmp, &path, "%s/"IPA_SDB_INFO_FILE, threshold->threshold_dir) < 0) { logmsgx(IPA_LOG_ERR, "st_get_threshold_info: mem_asprintf failed"); return -1; } if (read_info(mem_type, path, infop) < 0) { logmsgx(IPA_LOG_ERR, "st_get_threshold_info: read_info failed"); goto failed; } mem_free(path, m_tmp); return 0; failed: mem_free(path, m_tmp); return -1; } static int st_get_thresholds_list(u_int ruleno, const char *pat ATTR_UNUSED, const regex_t *regexp, ipa_mem_type *mem_type, u_int *n, struct ipa_entity_desc **listp) { struct rule *rule; struct stat statbuf; rule = RULE(ruleno); if (rule->thresholds_dir == NULL) if (mem_asprintf(m_anon, &rule->thresholds_dir, "%s/"IPA_SDB_THRESHOLDS_DIR, rule->rule_dir) < 0) { logmsgx(IPA_LOG_ERR, "st_get_tresholds_list: mem_asprintf failed"); return -1; } if (stat_func(rule->thresholds_dir, &statbuf) < 0) { if (errno == ENOENT) { *n = 0; *listp = NULL; return 0; } logmsgx(IPA_LOG_ERR, "st_get_tresholds_list: %s(%s)", stat_func_name, rule->thresholds_dir); return -1; } if (check_dir(rule->thresholds_dir, &statbuf) < 0) { logmsgx(IPA_LOG_ERR, "st_get_thresholds_list: check_dir failed"); return -1; } if (get_desc_list(rule->thresholds_dir, regexp, (const char *)NULL, mem_type, n, listp) < 0) { logmsgx(IPA_LOG_ERR, "st_get_thresholds_list: get_desc_list failed"); return -1; } return 0; } static int st_get_threshold_stat(u_int ruleno, u_int thresholdno, struct ipa_threshold_state *buf) { int fd; char *path; struct stat statbuf; const struct rule *rule; const struct threshold *threshold; struct ipa_sdb_threshold_record rec; rule = RULE(ruleno); threshold = THRESHOLD(rule, thresholdno); if (mem_asprintf(m_tmp, &path, "%s/"IPA_SDB_THRESHOLD_STATE, threshold->threshold_dir) < 0) { logmsgx(IPA_LOG_ERR, "st_get_threshold_stat: mem_asprintf failed"); return -1; } fd = -1; if (stat_func(path, &statbuf) < 0) { if (errno == ENOENT) { buf->thr = UINT64_C(0); goto done; } logmsg(IPA_LOG_ERR, "st_get_threshold_stat: %s(%s)", stat_func_name, path); goto failed; } if (check_file(path, &statbuf) < 0) { logmsgx(IPA_LOG_ERR, "st_get_threshold: check_file failed"); goto failed; } if (statbuf.st_size == 0) { buf->thr = UINT64_C(0); goto done; } if (statbuf.st_size != sizeof rec) { logmsgx(IPA_LOG_ERR, "st_get_threshold_stat: wrong size of database the file %s", path); goto failed; } if ( (fd = open(path, O_RDONLY)) < 0) { if (errno == ENOENT) { buf->thr = UINT64_C(0); goto done; } logmsg(IPA_LOG_ERR, "st_get_threshold_stat: open(%s, O_RDONLY)", path); goto failed; } if (readn(fd, path, &rec, sizeof rec) != sizeof rec) { logmsgx(IPA_LOG_ERR, "st_get_threshold_stat: readn failed"); goto failed; } cnt_from_base(buf->thr, rec.t_high, rec.t_low); cnt_from_base(buf->cnt, rec.c_high, rec.c_low); copy_db_date_to_tm(&rec.tm_from, &buf->tm_from); copy_db_date_to_tm(&rec.tm_updated, &buf->tm_updated); if (close(fd) < 0) { logmsg(IPA_LOG_ERR, "st_get_threshold_stat: close(%s)", path); fd = -1; goto failed; } done: mem_free(path, m_tmp); return 0; failed: if (fd >= 0) if (close(fd) < 0) logmsg(IPA_LOG_ERR, "st_get_threshold_stat: close(%s)", path); mem_free(path, m_tmp); return -1; } #else # define st_get_threshold_info NULL # define st_get_thresholds_list NULL # define st_get_threshold_stat NULL #endif /* WITH_THRESHOLDS */ #define SDB_CONF_ROOT_SECT (IPA_CONF_SECT_CUSTOM_OFFSET + 1) static const u_int sect_root[] = { IPA_CONF_SECT_ROOT, 0 }; static const u_int sect_cust_root[] = { SDB_CONF_ROOT_SECT, 0 }; static const u_int sect_any_rule[] = { IPA_CONF_SECT_GLOBAL, IPA_CONF_SECT_RULE, 0 }; static ipa_conf_sect conf_sect_tbl[] = { { "", SDB_CONF_ROOT_SECT, 0, NULL, NULL, IPA_CONF_TYPE_MISC, sect_root, parse_config_section }, { NULL, 0, 0, NULL, NULL, IPA_CONF_TYPE_MISC, NULL, NULL } }; static ipa_conf_param conf_param_tbl[] = { { "db_dir", 1, NULL, NULL, IPA_CONF_TYPE_STRING, sect_any_rule, parse_db_dir }, { "quiet", 1, NULL, NULL, IPA_CONF_TYPE_BOOLEAN, sect_cust_root, parse_quiet }, { "allow_symlinks", 1, NULL, NULL, IPA_CONF_TYPE_BOOLEAN, sect_cust_root, parse_allow_symlinks }, { "check_version", 1, NULL, NULL, IPA_CONF_TYPE_BOOLEAN, sect_cust_root, parse_check_version }, { NULL, 0, NULL, NULL, IPA_CONF_TYPE_MISC, NULL, NULL } }; struct ipa_st_mod IPA_ST_MOD_ENTRY(ipa_st_sdb) = { ST_MOD_API_VERSION, /* api_ver */ MOD_FLAGS, /* mod_flags */ MOD_ST_NAME, /* st_name */ NULL, /* suppfunc */ NULL, /* memfunc */ MOD_CONF_PREFIX, /* conf_prefix */ conf_sect_tbl, /* conf_sect_tbl */ conf_param_tbl, /* conf_param_tbl */ conf_init, /* conf_init */ conf_deinit, /* conf_deinit */ conf_event, /* conf_event */ conf_mimic_real, /* conf_mimic_real */ NULL, /* conf_inherit */ conf_show, /* conf_show */ st_pre_init, /* st_pre_init */ st_init_rule, /* st_init_rule */ st_init_limit, /* st_init_limit */ st_init_threshold, /* st_init_threshold */ st_init, /* st_init */ st_deinit_threshold, /* st_deinit_threshold */ st_deinit_limit, /* st_deinit_limit */ st_deinit_rule, /* st_deinit_rule */ st_deinit, /* st_deinit */ st_get_rule_info, /* st_get_rule_info */ st_get_limit_info, /* st_get_limit_info */ st_get_threshold_info, /* st_get_threshold_info */ st_get_rules_list, /* st_get_rules_list */ st_get_limits_list, /* st_get_limits_list */ st_get_thresholds_list, /* st_get_thresholds_list */ st_get_rule_stat, /* st_get_rule_stat */ st_get_limit_stat, /* st_get_limit_stat */ st_get_threshold_stat /* st_get_threshold_stat */ };