GQuark  bd_smart_error_quark (void) {
        return g_quark_from_static_string ("g-bd-smart-error-quark");
}

/**
 * BDSmartATAAttribute:
 * @id: Attribute Identifier.
 * @name: A free-form representation of the attribute name, implementation-dependent (e.g. libatasmart internal strings or smartmontools' drivedb.h names).
 * @well_known_name: Translated well-known attribute name (in libatasmart style, e.g. 'raw-read-error-rate') or %NULL in case of unknown, untrusted or vendor-specific value.
 * @value: The normalized value or -1 if unknown.
 * @worst: The worst normalized value of -1 if unknown.
 * @threshold: The threshold of a normalized value or -1 if unknown.
 * @failed_past: Indicates a failure that happened in the past (the normalized worst value is below the threshold).
 * @failing_now: Indicates a failure that is happening now (the normalized value is below the threshold).
 * @value_raw: The raw value of the attribute.
 * @flags: Bitmask of attribute flags. See #BDSmartATAAttributeFlag.
 * @pretty_value: Numerical representation of the parsed raw value, presented in @pretty_value_unit units.
 * @pretty_value_unit: The unit of the parsed raw value.
 * @pretty_value_string: A free-form string representation of the raw value intended for user presentation or %NULL.
 */
/**
 * bd_smart_ata_attribute_free: (skip)
 * @attr: (nullable): %BDSmartATAAttribute to free
 *
 * Frees @attr.
 */
void  bd_smart_ata_attribute_free (BDSmartATAAttribute *attr) {
        if (attr == NULL)
        return;
    g_free (attr->name);
    g_free (attr->well_known_name);
    g_free (attr->pretty_value_string);
    g_free (attr);
}

/**
 * bd_smart_ata_attribute_copy: (skip)
 * @attr: (nullable): %BDSmartATAAttribute to copy
 *
 * Creates a new copy of @attr.
 */
BDSmartATAAttribute * bd_smart_ata_attribute_copy (BDSmartATAAttribute *attr) {
        BDSmartATAAttribute *new_attr;

    if (attr == NULL)
        return NULL;

    new_attr = g_new0 (BDSmartATAAttribute, 1);
    memcpy (new_attr, attr, sizeof (BDSmartATAAttribute));
    new_attr->name = g_strdup (attr->name);
    new_attr->well_known_name = g_strdup (attr->well_known_name);
    new_attr->pretty_value_string = g_strdup (attr->pretty_value_string);

    return new_attr;
}

GType  bd_smart_ata_attribute_get_type () {
        static GType type = 0;

    if (G_UNLIKELY (type == 0)) {
        type = g_boxed_type_register_static ("BDSmartATAAttribute",
                                             (GBoxedCopyFunc) bd_smart_ata_attribute_copy,
                                             (GBoxedFreeFunc) bd_smart_ata_attribute_free);
    }
    return type;
}

/**
 * BDSmartATA:
 * @smart_supported: Indicates that the device has SMART capability.
 * @smart_enabled: Indicates that the SMART support is enabled.
 * @overall_status_passed: %TRUE if the device SMART overall-health self-assessment test result has passed.
 * @offline_data_collection_status: The offline data collection status. See #BDSmartATAOfflineDataCollectionStatus.
 * @auto_offline_data_collection_enabled: %TRUE if Automatic Offline Data Collection is enabled. Only supported with the smartmontools plugin.
 * @offline_data_collection_completion: Total time in seconds to complete Offline data collection.
 * @offline_data_collection_capabilities: Bitmask of offline data collection capabilities, see #BDSmartATAOfflineDataCollectionCapabilities. Only supported with the smartmontools plugin.
 * @self_test_status: Self-test execution status. See #BDSmartATASelfTestStatus.
 * @self_test_percent_remaining: The percentage remaining of a running self-test.
 * @self_test_polling_short: Short self-test routine recommended polling time in minutes or 0 if not supported.
 * @self_test_polling_extended: Extended self-test routine recommended polling time in minutes or 0 if not supported.
 * @self_test_polling_conveyance: Conveyance self-test routine recommended polling time in minutes or 0 if not supported.
 * @smart_capabilities: Bitmask of device misc. SMART capabilities. See #BDSmartATACapabilities. Only supported with the smartmontools plugin.
 * @attributes: (array zero-terminated=1): A list of reported SMART attributes.
 * @power_on_time: The count of minutes in power-on state.
 * @power_cycle_count: The count of full hard disk power on/off cycles.
 * @temperature: The current drive temperature in Kelvin or 0 when temperature is not reported.
 */
/**
 * bd_smart_ata_free: (skip)
 * @data: (nullable): %BDSmartATA to free
 *
 * Frees @data.
 */
void  bd_smart_ata_free (BDSmartATA *data) {
        BDSmartATAAttribute **attr;

    if (data == NULL)
        return;

    for (attr = data->attributes; attr && *attr; attr++)
        bd_smart_ata_attribute_free (*attr);
    g_free (data->attributes);
    g_free (data);
}

/**
 * bd_smart_ata_copy: (skip)
 * @data: (nullable): %BDSmartATA to copy
 *
 * Creates a new copy of @data.
 */
BDSmartATA * bd_smart_ata_copy (BDSmartATA *data) {
        BDSmartATA *new_data;
    BDSmartATAAttribute **attr;
    GPtrArray *ptr_array;

    if (data == NULL)
        return NULL;

    new_data = g_new0 (BDSmartATA, 1);
    memcpy (new_data, data, sizeof (BDSmartATA));

    ptr_array = g_ptr_array_new ();
    for (attr = data->attributes; attr && *attr; attr++)
        g_ptr_array_add (ptr_array, bd_smart_ata_attribute_copy (*attr));
    g_ptr_array_add (ptr_array, NULL);
    new_data->attributes = (BDSmartATAAttribute **) g_ptr_array_free (ptr_array, FALSE);

    return new_data;
}

GType  bd_smart_ata_get_type () {
        static GType type = 0;

    if (G_UNLIKELY (type == 0)) {
        type = g_boxed_type_register_static ("BDSmartATA",
                                             (GBoxedCopyFunc) bd_smart_ata_copy,
                                             (GBoxedFreeFunc) bd_smart_ata_free);
    }
    return type;
}

/**
 * BDSmartSCSI:
 * @smart_supported: Indicates that the device has SMART capability.
 * @smart_enabled: Indicates that the SMART support is enabled.
 * @overall_status_passed: %TRUE if the device SMART overall-health self-assessment test result has passed, %FALSE otherwise with @scsi_ie fields set.
 * @scsi_ie: The reported SCSI Informational Exception in a simplified form. See #BDSmartSCSIInformationalException.
 * @scsi_ie_asc: The reported SCSI Informational Exception ASC (Additional Sense Code) value (only values of 0xb - warnings and 0x5d - impending failures are taken in account).
 * @scsi_ie_ascq: The reported SCSI Informational Exception ASCQ (Additional Sense Code Qualifier) value.
 * @scsi_ie_string: String representation of the current SCSI Informational Exception.
 * @background_scan_status: Background scan status, see #BDSmartSCSIBackgroundScanStatus.
 * @background_scan_progress: Percent of a background scan progress.
 * @background_scan_runs: Number of background scans performed.
 * @background_medium_scan_runs: Number of background medium scans performed.
 * @read_errors_corrected_eccfast: Error counter log - read errors corrected by ECC fast.
 * @read_errors_corrected_eccdelayed: Error counter log - read errors corrected by ECC delayed.
 * @read_errors_corrected_rereads: Error counter log - read errors corrected by rereads.
 * @read_errors_corrected_total: Error counter log - total read errors corrected.
 * @read_errors_uncorrected: Error counter log - total uncorrected read errors.
 * @read_processed_bytes: Error counter log - total bytes processed.
 * @write_errors_corrected_eccfast: Error counter log - write errors corrected by ECC fast.
 * @write_errors_corrected_eccdelayed: Error counter log - write errors corrected by ECC delayed.
 * @write_errors_corrected_rewrites: Error counter log - write errors corrected by rewrites.
 * @write_errors_corrected_total: Error counter log - total write errors corrected.
 * @write_errors_uncorrected: Error counter log - total uncorrected write errors.
 * @write_processed_bytes: Error counter log - total bytes processed.
 * @start_stop_cycle_count: Accumulated start-stop cycles.
 * @start_stop_cycle_lifetime: Specified cycle count over device lifetime.
 * @load_unload_cycle_count: Accumulated load-unload cycles.
 * @load_unload_cycle_lifetime: Specified load-unload count over device lifetime.
 * @scsi_grown_defect_list: Elements in grown defect list.
 * @power_on_time: Accumulated power on time in minutes.
 * @temperature_warning_enabled: Indicates that temperature warning is enabled.
 * @temperature: The current drive temperature in Kelvin or 0 when temperature is not reported.
 * @temperature_drive_trip: The drive trip temperature in Kelvin or 0 when temperature is not reported.
 */
/**
 * bd_smart_scsi_free: (skip)
 * @data: (nullable): %BDSmartSCSI to free
 *
 * Frees @data.
 */
void  bd_smart_scsi_free (BDSmartSCSI *data) {
        if (data == NULL)
        return;

    g_free (data->scsi_ie_string);
    g_free (data);
}

/**
 * bd_smart_scsi_copy: (skip)
 * @data: (nullable): %BDSmartSCSI to copy
 *
 * Creates a new copy of @data.
 */
BDSmartSCSI * bd_smart_scsi_copy (BDSmartSCSI *data) {
        BDSmartSCSI *new_data;

    if (data == NULL)
        return NULL;

    new_data = g_new0 (BDSmartSCSI, 1);
    memcpy (new_data, data, sizeof (BDSmartSCSI));
    new_data->scsi_ie_string = g_strdup (data->scsi_ie_string);

    return new_data;
}

GType  bd_smart_scsi_get_type () {
        static GType type = 0;

    if (G_UNLIKELY (type == 0)) {
        type = g_boxed_type_register_static ("BDSmartSCSI",
                                             (GBoxedCopyFunc) bd_smart_scsi_copy,
                                             (GBoxedFreeFunc) bd_smart_scsi_free);
    }
    return type;
}

static gboolean  bd_smart_is_tech_avail_stub (BDSmartTech tech G_GNUC_UNUSED, G_GNUC_UNUSED guint64 mode G_GNUC_UNUSED, GError **error) {
    bd_utils_log_format (BD_UTILS_LOG_CRIT, "The function 'bd_smart_is_tech_avail' called, but not implemented!");
    g_set_error (error, BD_INIT_ERROR, BD_INIT_ERROR_NOT_IMPLEMENTED,
                "The function 'bd_smart_is_tech_avail' called, but not implemented!");
    return FALSE;
}

static gboolean  (*_bd_smart_is_tech_avail) (BDSmartTech tech, G_GNUC_UNUSED guint64 mode, GError **error) = bd_smart_is_tech_avail_stub;

/**
 * bd_smart_is_tech_avail:
 * @tech: the queried tech
 * @mode: a bit mask of queried modes of operation (#BDSmartTechMode) for @tech
 * @error: (out) (nullable): place to store error (details about why the @tech-@mode combination is not available)
 *
 * Returns: whether the @tech-@mode combination is available -- supported by the
 *          plugin implementation and having all the runtime dependencies available
 */
gboolean  bd_smart_is_tech_avail (BDSmartTech tech, G_GNUC_UNUSED guint64 mode, GError **error) {
    return _bd_smart_is_tech_avail (tech, mode, error);
}


static BDSmartATA * bd_smart_ata_get_info_stub (const gchar *device G_GNUC_UNUSED, const BDExtraArg **extra G_GNUC_UNUSED, GError **error) {
    bd_utils_log_format (BD_UTILS_LOG_CRIT, "The function 'bd_smart_ata_get_info' called, but not implemented!");
    g_set_error (error, BD_INIT_ERROR, BD_INIT_ERROR_NOT_IMPLEMENTED,
                "The function 'bd_smart_ata_get_info' called, but not implemented!");
    return NULL;
}

static BDSmartATA * (*_bd_smart_ata_get_info) (const gchar *device, const BDExtraArg **extra, GError **error) = bd_smart_ata_get_info_stub;

/**
 * bd_smart_ata_get_info:
 * @device: device to check.
 * @extra: (nullable) (array zero-terminated=1): extra options to pass through.
 * @error: (out) (optional): place to store error (if any).
 *
 * Retrieve SMART information from the drive.
 *
 * Returns: (transfer full): ATA SMART log or %NULL in case of an error (with @error set).
 *
 * Tech category: %BD_SMART_TECH_ATA-%BD_SMART_TECH_MODE_INFO
 */
BDSmartATA * bd_smart_ata_get_info (const gchar *device, const BDExtraArg **extra, GError **error) {
    return _bd_smart_ata_get_info (device, extra, error);
}


static BDSmartATA * bd_smart_ata_get_info_from_data_stub (const guint8 *data G_GNUC_UNUSED, gsize data_len G_GNUC_UNUSED, GError **error) {
    bd_utils_log_format (BD_UTILS_LOG_CRIT, "The function 'bd_smart_ata_get_info_from_data' called, but not implemented!");
    g_set_error (error, BD_INIT_ERROR, BD_INIT_ERROR_NOT_IMPLEMENTED,
                "The function 'bd_smart_ata_get_info_from_data' called, but not implemented!");
    return NULL;
}

static BDSmartATA * (*_bd_smart_ata_get_info_from_data) (const guint8 *data, gsize data_len, GError **error) = bd_smart_ata_get_info_from_data_stub;

/**
 * bd_smart_ata_get_info_from_data:
 * @data: (array length=data_len): binary data to parse.
 * @data_len: length of the data supplied.
 * @error: (out) (optional): place to store error (if any).
 *
 * Retrieve SMART information from the supplied data.
 *
 * Returns: (transfer full): ATA SMART log or %NULL in case of an error (with @error set).
 *
 * Tech category: %BD_SMART_TECH_ATA-%BD_SMART_TECH_MODE_INFO
 */
BDSmartATA * bd_smart_ata_get_info_from_data (const guint8 *data, gsize data_len, GError **error) {
    return _bd_smart_ata_get_info_from_data (data, data_len, error);
}


static BDSmartSCSI * bd_smart_scsi_get_info_stub (const gchar *device G_GNUC_UNUSED, const BDExtraArg **extra G_GNUC_UNUSED, GError **error) {
    bd_utils_log_format (BD_UTILS_LOG_CRIT, "The function 'bd_smart_scsi_get_info' called, but not implemented!");
    g_set_error (error, BD_INIT_ERROR, BD_INIT_ERROR_NOT_IMPLEMENTED,
                "The function 'bd_smart_scsi_get_info' called, but not implemented!");
    return NULL;
}

static BDSmartSCSI * (*_bd_smart_scsi_get_info) (const gchar *device, const BDExtraArg **extra, GError **error) = bd_smart_scsi_get_info_stub;

/**
 * bd_smart_scsi_get_info:
 * @device: device to check.
 * @extra: (nullable) (array zero-terminated=1): extra options to pass through.
 * @error: (out) (optional): place to store error (if any).
 *
 * Retrieve SMART information from SCSI or SAS-compliant drive.
 *
 * Returns: (transfer full): SCSI SMART log or %NULL in case of an error (with @error set).
 *
 * Tech category: %BD_SMART_TECH_SCSI-%BD_SMART_TECH_MODE_INFO
 */
BDSmartSCSI * bd_smart_scsi_get_info (const gchar *device, const BDExtraArg **extra, GError **error) {
    return _bd_smart_scsi_get_info (device, extra, error);
}


static gboolean  bd_smart_set_enabled_stub (const gchar *device G_GNUC_UNUSED, gboolean enabled G_GNUC_UNUSED, const BDExtraArg **extra G_GNUC_UNUSED, GError **error) {
    bd_utils_log_format (BD_UTILS_LOG_CRIT, "The function 'bd_smart_set_enabled' called, but not implemented!");
    g_set_error (error, BD_INIT_ERROR, BD_INIT_ERROR_NOT_IMPLEMENTED,
                "The function 'bd_smart_set_enabled' called, but not implemented!");
    return FALSE;
}

static gboolean  (*_bd_smart_set_enabled) (const gchar *device, gboolean enabled, const BDExtraArg **extra, GError **error) = bd_smart_set_enabled_stub;

/**
 * bd_smart_set_enabled:
 * @device: SMART-capable device.
 * @enabled: whether to enable or disable the SMART functionality
 * @extra: (nullable) (array zero-terminated=1): extra options to pass through.
 * @error: (out) (optional): place to store error (if any).
 *
 * Enables or disables SMART functionality on device.
 *
 * Returns: %TRUE when the functionality was set successfully or %FALSE in case of an error (with @error set).
 *
 * Tech category: %BD_SMART_TECH_ATA-%BD_SMART_TECH_MODE_INFO
 */
gboolean  bd_smart_set_enabled (const gchar *device, gboolean enabled, const BDExtraArg **extra, GError **error) {
    return _bd_smart_set_enabled (device, enabled, extra, error);
}


static gboolean  bd_smart_device_self_test_stub (const gchar *device G_GNUC_UNUSED, BDSmartSelfTestOp operation G_GNUC_UNUSED, const BDExtraArg **extra G_GNUC_UNUSED, GError **error) {
    bd_utils_log_format (BD_UTILS_LOG_CRIT, "The function 'bd_smart_device_self_test' called, but not implemented!");
    g_set_error (error, BD_INIT_ERROR, BD_INIT_ERROR_NOT_IMPLEMENTED,
                "The function 'bd_smart_device_self_test' called, but not implemented!");
    return FALSE;
}

static gboolean  (*_bd_smart_device_self_test) (const gchar *device, BDSmartSelfTestOp operation, const BDExtraArg **extra, GError **error) = bd_smart_device_self_test_stub;

/**
 * bd_smart_device_self_test:
 * @device: device to trigger the test on.
 * @operation: #BDSmartSelfTestOp self-test operation.
 * @extra: (nullable) (array zero-terminated=1): extra options to pass through.
 * @error: (out) (optional): place to store error (if any).
 *
 * Executes or aborts device self-test.
 *
 * Returns: %TRUE when the self-test was triggered successfully or %FALSE in case of an error (with @error set).
 *
 * Tech category: %BD_SMART_TECH_ATA-%BD_SMART_TECH_MODE_SELFTEST
 */
gboolean  bd_smart_device_self_test (const gchar *device, BDSmartSelfTestOp operation, const BDExtraArg **extra, GError **error) {
    return _bd_smart_device_self_test (device, operation, extra, error);
}


static gpointer load_smart_from_plugin(const gchar *so_name) {
    void *handle = NULL;
    char *error = NULL;
    gboolean (*init_fn) (void) = NULL;

    handle = dlopen(so_name, RTLD_LAZY | RTLD_NODELETE);
    if (!handle) {
        bd_utils_log_format (BD_UTILS_LOG_WARNING, "failed to load module smart: %s", dlerror());
        return NULL;
    }

    dlerror();
    * (void**) (&init_fn) = dlsym(handle, "bd_smart_init");
    if ((error = dlerror()) != NULL)
        bd_utils_log_format (BD_UTILS_LOG_DEBUG, "failed to load the init() function for smart: %s", error);
    /* coverity[dead_error_condition] */
    if (init_fn && !init_fn()) {
        dlclose(handle);
        return NULL;
    }
    init_fn = NULL;

    dlerror();
    * (void**) (&_bd_smart_is_tech_avail) = dlsym(handle, "bd_smart_is_tech_avail");
    if ((error = dlerror()) != NULL) {
        bd_utils_log_format (BD_UTILS_LOG_WARNING, "failed to load bd_smart_is_tech_avail: %s", error);
        _bd_smart_is_tech_avail = bd_smart_is_tech_avail_stub;
    }

    dlerror();
    * (void**) (&_bd_smart_ata_get_info) = dlsym(handle, "bd_smart_ata_get_info");
    if ((error = dlerror()) != NULL) {
        bd_utils_log_format (BD_UTILS_LOG_WARNING, "failed to load bd_smart_ata_get_info: %s", error);
        _bd_smart_ata_get_info = bd_smart_ata_get_info_stub;
    }

    dlerror();
    * (void**) (&_bd_smart_ata_get_info_from_data) = dlsym(handle, "bd_smart_ata_get_info_from_data");
    if ((error = dlerror()) != NULL) {
        bd_utils_log_format (BD_UTILS_LOG_WARNING, "failed to load bd_smart_ata_get_info_from_data: %s", error);
        _bd_smart_ata_get_info_from_data = bd_smart_ata_get_info_from_data_stub;
    }

    dlerror();
    * (void**) (&_bd_smart_scsi_get_info) = dlsym(handle, "bd_smart_scsi_get_info");
    if ((error = dlerror()) != NULL) {
        bd_utils_log_format (BD_UTILS_LOG_WARNING, "failed to load bd_smart_scsi_get_info: %s", error);
        _bd_smart_scsi_get_info = bd_smart_scsi_get_info_stub;
    }

    dlerror();
    * (void**) (&_bd_smart_set_enabled) = dlsym(handle, "bd_smart_set_enabled");
    if ((error = dlerror()) != NULL) {
        bd_utils_log_format (BD_UTILS_LOG_WARNING, "failed to load bd_smart_set_enabled: %s", error);
        _bd_smart_set_enabled = bd_smart_set_enabled_stub;
    }

    dlerror();
    * (void**) (&_bd_smart_device_self_test) = dlsym(handle, "bd_smart_device_self_test");
    if ((error = dlerror()) != NULL) {
        bd_utils_log_format (BD_UTILS_LOG_WARNING, "failed to load bd_smart_device_self_test: %s", error);
        _bd_smart_device_self_test = bd_smart_device_self_test_stub;
    }

    return handle;
}

static gboolean unload_smart (gpointer handle) {
    char *error = NULL;
    gboolean (*close_fn) (void) = NULL;

    _bd_smart_is_tech_avail = bd_smart_is_tech_avail_stub;
    _bd_smart_ata_get_info = bd_smart_ata_get_info_stub;
    _bd_smart_ata_get_info_from_data = bd_smart_ata_get_info_from_data_stub;
    _bd_smart_scsi_get_info = bd_smart_scsi_get_info_stub;
    _bd_smart_set_enabled = bd_smart_set_enabled_stub;
    _bd_smart_device_self_test = bd_smart_device_self_test_stub;

    dlerror();
    * (void**) (&close_fn) = dlsym(handle, "bd_smart_close");
    if (((error = dlerror()) != NULL) || !close_fn)
        bd_utils_log_format (BD_UTILS_LOG_DEBUG, "failed to load the close_plugin() function for smart: %s", error);
    /* coverity[dead_error_condition] */
    if (close_fn) {
        close_fn();
    }

    return dlclose(handle) == 0;
}

