diff options
Diffstat (limited to 'libpoldiff/src/poldiff.c')
-rw-r--r-- | libpoldiff/src/poldiff.c | 814 |
1 files changed, 814 insertions, 0 deletions
diff --git a/libpoldiff/src/poldiff.c b/libpoldiff/src/poldiff.c new file mode 100644 index 0000000..cce4fc4 --- /dev/null +++ b/libpoldiff/src/poldiff.c @@ -0,0 +1,814 @@ +/** + * @file + * Implementation for computing a semantic policy difference. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <config.h> + +#include "poldiff_internal.h" +#include <poldiff/component_record.h> + +#include <apol/util.h> +#include <qpol/policy_extend.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +/** + * All policy items (object classes, types, rules, etc.) must + * implement at least these functions. Next, a record should be + * appended to the array 'component_records' below. + */ +struct poldiff_component_record +{ + const char *item_name; + uint32_t flag_bit; + poldiff_get_item_stats_fn_t get_stats; + poldiff_get_result_items_fn_t get_results; + poldiff_item_get_form_fn_t get_form; + poldiff_item_to_string_fn_t to_string; + poldiff_reset_fn_t reset; + poldiff_get_items_fn_t get_items; + poldiff_item_comp_fn_t comp; + poldiff_new_diff_fn_t new_diff; + poldiff_deep_diff_fn_t deep_diff; +}; + +static const poldiff_component_record_t component_records[] = { + { + "attribute", + POLDIFF_DIFF_ATTRIBS, + poldiff_attrib_get_stats, + poldiff_get_attrib_vector, + poldiff_attrib_get_form, + poldiff_attrib_to_string, + attrib_reset, + attrib_get_items, + attrib_comp, + attrib_new_diff, + attrib_deep_diff, + }, + { + "Allow Rules", + POLDIFF_DIFF_AVALLOW, + poldiff_avrule_get_stats_allow, + poldiff_get_avrule_vector_allow, + poldiff_avrule_get_form, + poldiff_avrule_to_string, + avrule_reset_allow, + avrule_get_items_allow, + avrule_comp, + avrule_new_diff_allow, + avrule_deep_diff_allow, + }, + { + "Audit Allow Rules", + POLDIFF_DIFF_AVAUDITALLOW, + poldiff_avrule_get_stats_auditallow, + poldiff_get_avrule_vector_auditallow, + poldiff_avrule_get_form, + poldiff_avrule_to_string, + avrule_reset_auditallow, + avrule_get_items_auditallow, + avrule_comp, + avrule_new_diff_auditallow, + avrule_deep_diff_auditallow, + }, + { + "Don't Audit Rules", + POLDIFF_DIFF_AVDONTAUDIT, + poldiff_avrule_get_stats_dontaudit, + poldiff_get_avrule_vector_dontaudit, + poldiff_avrule_get_form, + poldiff_avrule_to_string, + avrule_reset_dontaudit, + avrule_get_items_dontaudit, + avrule_comp, + avrule_new_diff_dontaudit, + avrule_deep_diff_dontaudit, + }, + { + "Never Allow Rules", + POLDIFF_DIFF_AVNEVERALLOW, + poldiff_avrule_get_stats_neverallow, + poldiff_get_avrule_vector_neverallow, + poldiff_avrule_get_form, + poldiff_avrule_to_string, + avrule_reset_neverallow, + avrule_get_items_neverallow, + avrule_comp, + avrule_new_diff_neverallow, + avrule_deep_diff_neverallow, + }, + { + "bool", + POLDIFF_DIFF_BOOLS, + poldiff_bool_get_stats, + poldiff_get_bool_vector, + poldiff_bool_get_form, + poldiff_bool_to_string, + bool_reset, + bool_get_items, + bool_comp, + bool_new_diff, + bool_deep_diff, + }, + { + "category", + POLDIFF_DIFF_CATS, + poldiff_cat_get_stats, + poldiff_get_cat_vector, + poldiff_cat_get_form, + poldiff_cat_to_string, + cat_reset, + cat_get_items, + cat_comp, + cat_new_diff, + cat_deep_diff, + }, + { + "class", + POLDIFF_DIFF_CLASSES, + poldiff_class_get_stats, + poldiff_get_class_vector, + poldiff_class_get_form, + poldiff_class_to_string, + class_reset, + class_get_items, + class_comp, + class_new_diff, + class_deep_diff, + }, + { + "common", + POLDIFF_DIFF_COMMONS, + poldiff_common_get_stats, + poldiff_get_common_vector, + poldiff_common_get_form, + poldiff_common_to_string, + common_reset, + common_get_items, + common_comp, + common_new_diff, + common_deep_diff, + }, + { + "level", + POLDIFF_DIFF_LEVELS, + poldiff_level_get_stats, + poldiff_get_level_vector, + poldiff_level_get_form, + poldiff_level_to_string, + level_reset, + level_get_items, + level_comp, + level_new_diff, + level_deep_diff, + }, + { + "range transition", + POLDIFF_DIFF_RANGE_TRANS, + poldiff_range_trans_get_stats, + poldiff_get_range_trans_vector, + poldiff_range_trans_get_form, + poldiff_range_trans_to_string, + range_trans_reset, + range_trans_get_items, + range_trans_comp, + range_trans_new_diff, + range_trans_deep_diff, + }, + { + "role", + POLDIFF_DIFF_ROLES, + poldiff_role_get_stats, + poldiff_get_role_vector, + poldiff_role_get_form, + poldiff_role_to_string, + role_reset, + role_get_items, + role_comp, + role_new_diff, + role_deep_diff, + }, + { + "role_allow", + POLDIFF_DIFF_ROLE_ALLOWS, + poldiff_role_allow_get_stats, + poldiff_get_role_allow_vector, + poldiff_role_allow_get_form, + poldiff_role_allow_to_string, + role_allow_reset, + role_allow_get_items, + role_allow_comp, + role_allow_new_diff, + role_allow_deep_diff, + }, + { + "role_transition", + POLDIFF_DIFF_ROLE_TRANS, + poldiff_role_trans_get_stats, + poldiff_get_role_trans_vector, + poldiff_role_trans_get_form, + poldiff_role_trans_to_string, + role_trans_reset, + role_trans_get_items, + role_trans_comp, + role_trans_new_diff, + role_trans_deep_diff, + }, + { + "Type Change rules", + POLDIFF_DIFF_TECHANGE, + poldiff_terule_get_stats_change, + poldiff_get_terule_vector_change, + poldiff_terule_get_form, + poldiff_terule_to_string, + terule_reset_change, + terule_get_items_change, + terule_comp, + terule_new_diff_change, + terule_deep_diff_change, + }, + { + "Type Member Rules", + POLDIFF_DIFF_TEMEMBER, + poldiff_terule_get_stats_member, + poldiff_get_terule_vector_member, + poldiff_terule_get_form, + poldiff_terule_to_string, + terule_reset_member, + terule_get_items_member, + terule_comp, + terule_new_diff_member, + terule_deep_diff_member, + }, + { + "Type Transition Rules", + POLDIFF_DIFF_TETRANS, + poldiff_terule_get_stats_trans, + poldiff_get_terule_vector_trans, + poldiff_terule_get_form, + poldiff_terule_to_string, + terule_reset_trans, + terule_get_items_trans, + terule_comp, + terule_new_diff_trans, + terule_deep_diff_trans, + }, + { + "type", + POLDIFF_DIFF_TYPES, + poldiff_type_get_stats, + poldiff_get_type_vector, + poldiff_type_get_form, + poldiff_type_to_string, + type_reset, + type_get_items, + type_comp, + type_new_diff, + type_deep_diff, + }, + { + "user", + POLDIFF_DIFF_USERS, + poldiff_user_get_stats, + poldiff_get_user_vector, + poldiff_user_get_form, + poldiff_user_to_string, + user_reset, + user_get_items, + user_comp, + user_new_diff, + user_deep_diff, + }, +}; + +const poldiff_component_record_t *poldiff_get_component_record(uint32_t which) +{ + size_t i = 0; + size_t num_items; + + num_items = sizeof(component_records) / sizeof(poldiff_component_record_t); + for (i = 0; i < num_items; i++) { + if (component_records[i].flag_bit == which) + return &component_records[i]; + } + return NULL; +} + +poldiff_t *poldiff_create(apol_policy_t * orig_policy, apol_policy_t * mod_policy, poldiff_handle_fn_t fn, void *callback_arg) +{ + poldiff_t *diff = NULL; + int error; + + if (!orig_policy || !mod_policy) { + ERR(NULL, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + + if (!(diff = calloc(1, sizeof(poldiff_t)))) { + ERR(NULL, "%s", strerror(ENOMEM)); + errno = ENOMEM; + return NULL; + } + diff->orig_pol = orig_policy; + diff->mod_pol = mod_policy; + diff->orig_qpol = apol_policy_get_qpol(diff->orig_pol); + diff->mod_qpol = apol_policy_get_qpol(diff->mod_pol); + diff->fn = fn; + diff->handle_arg = callback_arg; + if ((diff->type_map = type_map_create()) == NULL) { + ERR(diff, "%s", strerror(ENOMEM)); + poldiff_destroy(&diff); + errno = ENOMEM; + return NULL; + } + if (type_map_infer(diff) < 0) { + error = errno; + poldiff_destroy(&diff); + errno = error; + return NULL; + } + + if ((diff->attrib_diffs = attrib_summary_create()) == NULL || + (diff->avrule_diffs[AVRULE_OFFSET_ALLOW] = avrule_create()) == NULL || + (diff->avrule_diffs[AVRULE_OFFSET_AUDITALLOW] = avrule_create()) == NULL || + (diff->avrule_diffs[AVRULE_OFFSET_DONTAUDIT] = avrule_create()) == NULL || + (diff->avrule_diffs[AVRULE_OFFSET_NEVERALLOW] = avrule_create()) == NULL || + (diff->bool_diffs = bool_create()) == NULL || + (diff->cat_diffs = cat_create()) == NULL || + (diff->class_diffs = class_create()) == NULL || + (diff->common_diffs = common_create()) == NULL || + (diff->level_diffs = level_create()) == NULL || + (diff->range_trans_diffs = range_trans_create()) == NULL || + (diff->role_diffs = role_create()) == NULL || + (diff->role_allow_diffs = role_allow_create()) == NULL || + (diff->role_trans_diffs = role_trans_create()) == NULL || + (diff->terule_diffs[TERULE_OFFSET_CHANGE] = terule_create()) == NULL || + (diff->terule_diffs[TERULE_OFFSET_MEMBER] = terule_create()) == NULL || + (diff->terule_diffs[TERULE_OFFSET_TRANS] = terule_create()) == NULL || + (diff->type_diffs = type_summary_create()) == NULL || (diff->user_diffs = user_create()) == NULL) { + ERR(diff, "%s", strerror(ENOMEM)); + poldiff_destroy(&diff); + errno = ENOMEM; + return NULL; + } + + diff->policy_opts = QPOL_POLICY_OPTION_NO_RULES | QPOL_POLICY_OPTION_NO_NEVERALLOWS; + return diff; +} + +void poldiff_destroy(poldiff_t ** diff) +{ + if (!diff || !(*diff)) + return; + apol_policy_destroy(&(*diff)->orig_pol); + apol_policy_destroy(&(*diff)->mod_pol); + apol_bst_destroy(&(*diff)->class_bst); + apol_bst_destroy(&(*diff)->perm_bst); + apol_bst_destroy(&(*diff)->bool_bst); + + type_map_destroy(&(*diff)->type_map); + attrib_summary_destroy(&(*diff)->attrib_diffs); + avrule_destroy(&(*diff)->avrule_diffs[AVRULE_OFFSET_ALLOW]); + avrule_destroy(&(*diff)->avrule_diffs[AVRULE_OFFSET_AUDITALLOW]); + avrule_destroy(&(*diff)->avrule_diffs[AVRULE_OFFSET_DONTAUDIT]); + avrule_destroy(&(*diff)->avrule_diffs[AVRULE_OFFSET_NEVERALLOW]); + bool_destroy(&(*diff)->bool_diffs); + cat_destroy(&(*diff)->cat_diffs); + class_destroy(&(*diff)->class_diffs); + common_destroy(&(*diff)->common_diffs); + level_destroy(&(*diff)->level_diffs); + range_trans_destroy(&(*diff)->range_trans_diffs); + role_destroy(&(*diff)->role_diffs); + role_allow_destroy(&(*diff)->role_allow_diffs); + role_trans_destroy(&(*diff)->role_trans_diffs); + user_destroy(&(*diff)->user_diffs); + terule_destroy(&(*diff)->terule_diffs[TERULE_OFFSET_CHANGE]); + terule_destroy(&(*diff)->terule_diffs[TERULE_OFFSET_MEMBER]); + terule_destroy(&(*diff)->terule_diffs[TERULE_OFFSET_TRANS]); + type_summary_destroy(&(*diff)->type_diffs); + free(*diff); + *diff = NULL; +} + +/** + * Given a particular policy item record (e.g., one for object + * classes), (re-)perform a diff of them between the two policies + * listed in the poldiff_t structure. Upon success, set the status + * flag within 'diff' to indicate that this diff is done. + * + * @param diff The policy difference structure containing the policies + * to compare and to populate with the item differences. + * @param component_record Item record containg callbacks to perform each + * step of the computation for a particular kind of item. + * + * @return 0 on success and < 0 on error; if the call fails; errno + * will be set and the only defined operation on the policy difference + * structure will be poldiff_destroy(). + */ +static int poldiff_do_item_diff(poldiff_t * diff, const poldiff_component_record_t * component_record) +{ + apol_vector_t *p1_v = NULL, *p2_v = NULL; + int error = 0, retv; + size_t x = 0, y = 0; + void *item_x = NULL, *item_y = NULL; + + if (!diff || !component_record) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + diff->diff_status &= (~component_record->flag_bit); + + INFO(diff, "Getting %s items from original policy.", component_record->item_name); + p1_v = component_record->get_items(diff, diff->orig_pol); + if (!p1_v) { + error = errno; + goto err; + } + + INFO(diff, "Getting %s items from modified policy.", component_record->item_name); + p2_v = component_record->get_items(diff, diff->mod_pol); + if (!p2_v) { + error = errno; + goto err; + } + + INFO(diff, "Finding differences in %s.", component_record->item_name); + for (x = 0, y = 0; x < apol_vector_get_size(p1_v);) { + if (y >= apol_vector_get_size(p2_v)) + break; + item_x = apol_vector_get_element(p1_v, x); + item_y = apol_vector_get_element(p2_v, y); + retv = component_record->comp(item_x, item_y, diff); + if (retv < 0) { + if (component_record->new_diff(diff, POLDIFF_FORM_REMOVED, item_x)) { + error = errno; + goto err; + } + x++; + } else if (retv > 0) { + if (component_record->new_diff(diff, POLDIFF_FORM_ADDED, item_y)) { + error = errno; + goto err; + } + y++; + } else { + if (component_record->deep_diff(diff, item_x, item_y)) { + error = errno; + goto err; + } + x++; + y++; + } + } + for (; x < apol_vector_get_size(p1_v); x++) { + item_x = apol_vector_get_element(p1_v, x); + if (component_record->new_diff(diff, POLDIFF_FORM_REMOVED, item_x)) { + error = errno; + goto err; + } + } + for (; y < apol_vector_get_size(p2_v); y++) { + item_y = apol_vector_get_element(p2_v, y); + if (component_record->new_diff(diff, POLDIFF_FORM_ADDED, item_y)) { + error = errno; + goto err; + } + } + + apol_vector_destroy(&p1_v); + apol_vector_destroy(&p2_v); + diff->diff_status |= component_record->flag_bit; + return 0; + err: + apol_vector_destroy(&p1_v); + apol_vector_destroy(&p2_v); + errno = error; + return -1; +} + +int poldiff_run(poldiff_t * diff, uint32_t flags) +{ + size_t i, num_items; + + if (!flags) + return 0; /* nothing to do */ + + if (!diff) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + + int policy_opts = diff->policy_opts; + if (flags & (POLDIFF_DIFF_AVRULES | POLDIFF_DIFF_TERULES)) { + policy_opts &= ~(QPOL_POLICY_OPTION_NO_RULES); + } + if (flags & POLDIFF_DIFF_AVNEVERALLOW) { + policy_opts &= ~(QPOL_POLICY_OPTION_NO_NEVERALLOWS); + } + if (policy_opts != diff->policy_opts) { + INFO(diff, "%s", "Loading rules from original policy."); + if (qpol_policy_rebuild(diff->orig_qpol, policy_opts)) { + return -1; + } + INFO(diff, "%s", "Loading rules from modified policy."); + if (qpol_policy_rebuild(diff->mod_qpol, policy_opts)) { + return -1; + } + // force flushing of existing pointers into policies + diff->remapped = 1; + diff->policy_opts = policy_opts; + } + + num_items = sizeof(component_records) / sizeof(poldiff_component_record_t); + if (diff->remapped) { + for (i = 0; i < num_items; i++) { + if (component_records[i].flag_bit & POLDIFF_DIFF_REMAPPED) { + INFO(diff, "Resetting %s diff.", component_records[i].item_name); + if (component_records[i].reset(diff)) + return -1; + } + } + diff->diff_status &= ~(POLDIFF_DIFF_REMAPPED); + diff->remapped = 0; + } + + INFO(diff, "%s", "Building type map."); + if (type_map_build(diff)) { + return -1; + } + + diff->line_numbers_enabled = 0; + for (i = 0; i < num_items; i++) { + /* item requested but not yet run */ + if ((flags & component_records[i].flag_bit) && !(component_records[i].flag_bit & diff->diff_status)) { + INFO(diff, "Running %s diff.", component_records[i].item_name); + if (poldiff_do_item_diff(diff, &(component_records[i]))) { + return -1; + } + } + } + + return 0; +} + +int poldiff_is_run(const poldiff_t * diff, uint32_t flags) +{ + if (!flags) + return 1; /* nothing to do */ + + if (!diff) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + if ((diff->diff_status & flags) == flags) { + return 1; + } + return 0; +} + +int poldiff_get_stats(const poldiff_t * diff, uint32_t flags, size_t stats[5]) +{ + size_t i, j, num_items, tmp_stats[5] = { 0, 0, 0, 0, 0 }; + + if (!diff || !flags) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + + stats[0] = stats[1] = stats[2] = stats[3] = stats[4] = 0; + + num_items = sizeof(component_records) / sizeof(poldiff_component_record_t); + for (i = 0; i < num_items; i++) { + if (flags & component_records[i].flag_bit) { + component_records[i].get_stats(diff, tmp_stats); + for (j = 0; j < 5; j++) + stats[j] += tmp_stats[j]; + } + } + + return 0; +} + +int poldiff_enable_line_numbers(poldiff_t * diff) +{ + int retval; + if (diff == NULL) { + errno = EINVAL; + return -1; + } + if (!diff->line_numbers_enabled) { + if (qpol_policy_build_syn_rule_table(diff->orig_qpol)) + return -1; + if (qpol_policy_build_syn_rule_table(diff->mod_qpol)) + return -1; + if ((retval = avrule_enable_line_numbers(diff, AVRULE_OFFSET_ALLOW)) < 0) { + return retval; + } + if ((retval = avrule_enable_line_numbers(diff, AVRULE_OFFSET_AUDITALLOW)) < 0) { + return retval; + } + if ((retval = avrule_enable_line_numbers(diff, AVRULE_OFFSET_DONTAUDIT)) < 0) { + return retval; + } + if ((retval = avrule_enable_line_numbers(diff, AVRULE_OFFSET_NEVERALLOW)) < 0) { + return retval; + } + if ((retval = terule_enable_line_numbers(diff, TERULE_OFFSET_CHANGE)) < 0) { + return retval; + } + if ((retval = terule_enable_line_numbers(diff, TERULE_OFFSET_MEMBER)) < 0) { + return retval; + } + if ((retval = terule_enable_line_numbers(diff, TERULE_OFFSET_TRANS)) < 0) { + return retval; + } + diff->line_numbers_enabled = 1; + } + return 0; +} + +int poldiff_build_bsts(poldiff_t * diff) +{ + apol_vector_t *classes[2] = { NULL, NULL }; + apol_vector_t *perms[2] = { NULL, NULL }; + apol_vector_t *bools[2] = { NULL, NULL }; + size_t i, j; + const qpol_class_t *cls; + qpol_bool_t *qbool; + const char *name; + char *new_name; + int retval = -1, error = 0; + if (diff->class_bst != NULL) { + return 0; + } + if ((diff->class_bst = apol_bst_create(apol_str_strcmp, free)) == NULL || + (diff->perm_bst = apol_bst_create(apol_str_strcmp, free)) == NULL || + (diff->bool_bst = apol_bst_create(apol_str_strcmp, free)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + for (i = 0; i < 2; i++) { + apol_policy_t *p = (i == 0 ? diff->orig_pol : diff->mod_pol); + qpol_policy_t *q = apol_policy_get_qpol(p); + if (apol_class_get_by_query(p, NULL, &classes[i]) < 0 || + apol_perm_get_by_query(p, NULL, &perms[i]) < 0 || apol_bool_get_by_query(p, NULL, &bools[i]) < 0) { + error = errno; + goto cleanup; + } + for (j = 0; j < apol_vector_get_size(classes[i]); j++) { + cls = apol_vector_get_element(classes[i], j); + if (qpol_class_get_name(q, cls, &name) < 0) { + error = errno; + goto cleanup; + } + if ((new_name = strdup(name)) == NULL || + apol_bst_insert_and_get(diff->class_bst, (void **)&new_name, NULL) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + for (j = 0; j < apol_vector_get_size(perms[i]); j++) { + name = (char *)apol_vector_get_element(perms[i], j); + if ((new_name = strdup(name)) == NULL || + apol_bst_insert_and_get(diff->perm_bst, (void **)&new_name, NULL) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + for (j = 0; j < apol_vector_get_size(bools[i]); j++) { + qbool = (qpol_bool_t *) apol_vector_get_element(bools[i], j); + if (qpol_bool_get_name(q, qbool, &name) < 0) { + error = errno; + goto cleanup; + } + if ((new_name = strdup(name)) == NULL || + apol_bst_insert_and_get(diff->bool_bst, (void **)&new_name, NULL) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + } + retval = 0; + cleanup: + apol_vector_destroy(&classes[0]); + apol_vector_destroy(&classes[1]); + apol_vector_destroy(&perms[0]); + apol_vector_destroy(&perms[1]); + apol_vector_destroy(&bools[0]); + apol_vector_destroy(&bools[1]); + errno = error; + return retval; +} + +static void poldiff_handle_default_callback(void *arg __attribute__ ((unused)), + poldiff_t * p __attribute__ ((unused)), int level, const char *fmt, va_list va_args) +{ + switch (level) { + case POLDIFF_MSG_INFO: + { + /* by default do not display these messages */ + return; + } + case POLDIFF_MSG_WARN: + { + fprintf(stderr, "WARNING: "); + break; + } + case POLDIFF_MSG_ERR: + default: + { + fprintf(stderr, "ERROR: "); + break; + } + } + vfprintf(stderr, fmt, va_args); + fprintf(stderr, "\n"); +} + +void poldiff_handle_msg(const poldiff_t * p, int level, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + if (p == NULL || p->fn == NULL) { + poldiff_handle_default_callback(NULL, NULL, level, fmt, ap); + } else { + p->fn(p->handle_arg, p, level, fmt, ap); + } + va_end(ap); +} + +poldiff_item_get_form_fn_t poldiff_component_record_get_form_fn(const poldiff_component_record_t * diff) +{ + if (!diff) { + errno = EINVAL; + return NULL; + } + return diff->get_form; +} + +poldiff_item_to_string_fn_t poldiff_component_record_get_to_string_fn(const poldiff_component_record_t * diff) +{ + if (!diff) { + errno = EINVAL; + return NULL; + } + return diff->to_string; +} + +poldiff_get_item_stats_fn_t poldiff_component_record_get_stats_fn(const poldiff_component_record_t * diff) +{ + if (!diff) { + errno = EINVAL; + return NULL; + } + return diff->get_stats; +} + +poldiff_get_result_items_fn_t poldiff_component_record_get_results_fn(const poldiff_component_record_t * diff) +{ + if (!diff) { + errno = EINVAL; + return NULL; + } + return diff->get_results; +} + +const char *poldiff_component_record_get_label(const poldiff_component_record_t * diff) +{ + if (!diff) { + errno = EINVAL; + return NULL; + } + return diff->item_name; +} |