From bbe21b0f2a32be7e36310803549439eee7df4d53 Mon Sep 17 00:00:00 2001 From: Michal Minar Date: Tue, 24 Jun 2014 12:33:35 +0200 Subject: software-dbus: implemented installation job processing Added initialization and cleanup functions to software utilities needed to initialize job manager. --- src/software-dbus/CMakeLists.txt | 1 + src/software-dbus/lmi_sw_job.c | 869 +++++++++++++++++++++++++++++++++++++++ src/software-dbus/lmi_sw_job.h | 175 ++++++++ src/software-dbus/sw-utils.c | 51 +++ src/software-dbus/sw-utils.h | 10 + 5 files changed, 1106 insertions(+) create mode 100644 src/software-dbus/lmi_sw_job.c create mode 100644 src/software-dbus/lmi_sw_job.h diff --git a/src/software-dbus/CMakeLists.txt b/src/software-dbus/CMakeLists.txt index 8b645e0..839ef70 100644 --- a/src/software-dbus/CMakeLists.txt +++ b/src/software-dbus/CMakeLists.txt @@ -5,6 +5,7 @@ set(CIMPROVAGT_SCRIPT cmpiLMI_${PROVIDER_NAME}-cimprovagt) set(provider_SRCS sw-utils.c + lmi_sw_job.c ) konkretcmpi_generate(${MOF} diff --git a/src/software-dbus/lmi_sw_job.c b/src/software-dbus/lmi_sw_job.c new file mode 100644 index 0000000..a741610 --- /dev/null +++ b/src/software-dbus/lmi_sw_job.c @@ -0,0 +1,869 @@ +/* + * Copyright (C) 2012-2014 Red Hat, Inc. All rights reserved. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Michal Minar + */ + +#include +#include +#include "openlmi.h" +#include "sw-utils.h" +#include "LMI_SystemSoftwareCollection.h" +#include "LMI_SoftwareInstallationJob.h" +#include "lmi_sw_job.h" + +G_DEFINE_TYPE(LmiSwInstallationJob, lmi_sw_installation_job, LMI_TYPE_JOB) + +static void lmi_sw_installation_job_class_init(LmiSwInstallationJobClass *klass) +{ + G_OBJECT_CLASS(klass); +} + +static void lmi_sw_installation_job_init(LmiSwInstallationJob *self) {} + +G_DEFINE_TYPE(LmiSwVerificationJob, lmi_sw_verification_job, LMI_TYPE_JOB) + +static void lmi_sw_verification_job_class_init(LmiSwVerificationJobClass *klass) +{ + G_OBJECT_CLASS(klass); +} + +static void lmi_sw_verification_job_init(LmiSwVerificationJob *self) {} + +static gchar *make_job_description(const LmiJob *job) +{ + guint number = lmi_job_get_number(job); + gchar *jobid = lmi_job_get_jobid(job); + gchar *template = NULL; + gchar *dest = NULL; + gchar *result = NULL; + GVariant *variant = NULL; + guint install_options; + + if (!jobid) + goto memory_err; + if (LMI_IS_SW_INSTALLATION_JOB(job)) { + if (lmi_job_has_in_param(job, IN_PARAM_INSTALL_OPTIONS_NAME)) { + if ((variant = lmi_job_get_in_param(job, + IN_PARAM_INSTALL_OPTIONS_NAME)) == NULL) + goto memory_err; + install_options = g_variant_get_uint32(variant); + if (install_options & INSTALL_OPTION_INSTALL) { + template = "Software package installation job #%u (id=%s) for %s."; + } else if (install_options & INSTALL_OPTION_UPDATE) { + template = "Software package update job #%u (id=%s) for %s."; + } else if (install_options & INSTALL_OPTION_UNINSTALL) { + template = "Software package removal job #%u (id=%s) for %s."; + } else { + lmi_warn("Failed to describe InstallOptions of job \"%s\": %u!", + jobid, install_options); + template = "Software job #%u (id=%s) for %s."; + } + } else { + template = "Newly created job %u (id=%s) for %s."; + } + } else if (LMI_IS_SW_VERIFICATION_JOB(job)) { + template = "Software verification job %u (id=%s) for %s."; + } else { + lmi_error("Can not describe job of type \"%s\".", + G_OBJECT_TYPE_NAME(job)); + goto err; + } + + if (lmi_job_has_in_param(job, IN_PARAM_SOURCE_NAME)) { + if ((variant = lmi_job_get_in_param(job, IN_PARAM_SOURCE_NAME)) == NULL) + goto memory_err; + dest = g_strdup_printf("package %s", g_variant_get_string(variant, NULL)); + } else if (lmi_job_has_in_param(job, IN_PARAM_URI_NAME)) { + if ((variant = lmi_job_get_in_param(job, IN_PARAM_URI_NAME)) == NULL) + goto memory_err; + dest = g_strdup_printf("uri %s", g_variant_get_string(variant, NULL)); + } else { + dest = g_strdup_printf("unknown source"); + } + if (variant) + g_variant_unref(variant); + if (dest == NULL) { + goto memory_err; + } + + g_assert(template); + result = g_strdup_printf(template, number, jobid, dest); + g_free(dest); + g_free(jobid); + jobid = NULL; + if (!result) + goto memory_err; + return result; + +memory_err: + lmi_error("Memory allocation failed"); +err: + g_free(jobid); + return result; +} + +CMPIStatus lmi_sw_job_to_cim_instance(const CMPIBroker *cb, + const CMPIContext *ctx, + const LmiJob *job, + CMPIInstance *instance) +{ + CMPIStatus status = {CMPI_RC_OK, NULL}; + gchar buf[BUFLEN]; + CMPIValue value; + gchar *description = NULL; + + JOB_CRITICAL_BEGIN(job); + + g_snprintf(buf, BUFLEN, "Software installation job #%u", + lmi_job_get_number(job)); + + if ((value.string = CMNewString(cb, buf, &status)) == NULL) + goto err; + if ((status = CMSetProperty(instance, "Caption", + &value, CMPI_string)).rc) + goto string_err; + + value.uint16 = LMI_SoftwareInstallationJob_CommunicationStatus_Communication_OK; + if (lmi_job_get_state(job) == LMI_JOB_STATE_ENUM_EXCEPTION && + lmi_job_has_out_param(job, "PkError")) + { + GVariant *variant = lmi_job_get_out_param(job, "PkError"); + if (variant) { + switch (g_variant_get_uint32(variant)) { + case PK_ERROR_ENUM_NO_NETWORK: + case PK_ERROR_ENUM_PACKAGE_DOWNLOAD_FAILED: + case PK_ERROR_ENUM_NO_MORE_MIRRORS_TO_TRY: + case PK_ERROR_ENUM_CANNOT_FETCH_SOURCES: + value.uint16 = LMI_SoftwareInstallationJob_CommunicationStatus_Lost_Communication; + break; + default: + break; + } + } + g_variant_unref(variant); + } + // This property is already prefilled by jobmanager. Thus no allocation is + // neccessary. + CMSetProperty(instance, "CommunicationStatus", &value, CMPI_uint16); + + if ((description = make_job_description(job)) != NULL) { + if ((value.string = CMNewString(cb, description, &status)) == NULL) + goto description_err; + if ((status = CMSetProperty(instance, "Description", + &value, CMPI_string)).rc) + goto string_err; + g_free(description); + description = NULL; + } + + JOB_CRITICAL_END(job); + return status; + +string_err: + CMRelease(value.string); +description_err: + if (description) + g_free(description); +err: + JOB_CRITICAL_END(job); + lmi_error("Memory allocation failed!"); + return status; +} + +CMPIStatus lmi_sw_job_make_job_parameters(const CMPIBroker *cb, + const CMPIContext *ctx, + const LmiJob *job, + gboolean include_input, + gboolean include_output, + CMPIInstance *instance) +{ + CMPIStatus status = {CMPI_RC_OK, NULL}; + gchar *namespace = NULL; + CMPIValue value, elem; + GVariant *variant; + CMPIObjectPath *csop = NULL, *colop = NULL; + guint32 options, tmp, count = 0; + char instance_id[BUFLEN] = ""; + + if (include_input) { + if (lmi_job_has_in_param(job, "Source")) { + // TODO make it an object path of LMI_SoftwareIdentity + variant = lmi_job_get_in_param(job, "Source"); + value.string = CMNewString(cb, + g_variant_get_string(variant, NULL), &status); + if (value.string == NULL || status.rc) { + g_variant_unref(variant); + goto err; + } + g_variant_unref(variant); + if ((status = CMSetProperty(instance, "Source", + &value, CMPI_string)).rc) + goto string_err; + } + + if (lmi_job_has_in_param(job, "URI")) { + variant = lmi_job_get_in_param(job, "URI"); + value.string = CMNewString(cb, + g_variant_get_string(variant, NULL), &status); + if (!value.string || status.rc) + g_variant_unref(variant); + goto err; + g_variant_unref(variant); + if ((status = CMSetProperty(instance, "URI", &value, CMPI_string)).rc) + goto string_err; + } + + if (lmi_job_has_in_param(job, IN_PARAM_INSTALL_OPTIONS_NAME)) { + variant = lmi_job_get_in_param(job, IN_PARAM_INSTALL_OPTIONS_NAME); + options = g_variant_get_uint32(variant); + g_variant_unref(variant); + tmp = options; + while (tmp) { + count += tmp & 1; + tmp = tmp >> 1; + } + value.array = CMNewArray(cb, count, + CMPI_uint16, &status); + if (value.array == NULL || status.rc) + goto err; + if (options & INSTALLATION_OPERATION_INSTALL) { + elem.uint16 = INSTALL_OPTION_INSTALL; + } else if (options & INSTALLATION_OPERATION_UPDATE) { + elem.uint16 = INSTALL_OPTION_UPDATE; + } else { + elem.uint16 = INSTALL_OPTION_UNINSTALL; + } + CMSetArrayElementAt(value.array, 0, &elem, CMPI_uint16); + tmp = 1; + if (options & INSTALLATION_OPERATION_FORCE) { + elem.uint16 = INSTALL_OPTION_FORCE_INSTALLATION; + CMSetArrayElementAt(value.array, tmp++, &elem, CMPI_uint16); + } + if (options & INSTALLATION_OPERATION_REPAIR) { + elem.uint16 = INSTALL_OPTION_REPAIR; + CMSetArrayElementAt(value.array, tmp++, &elem, CMPI_uint16); + } + if ((status = CMSetProperty(instance, IN_PARAM_INSTALL_OPTIONS_NAME, + &value, CMPI_uint16A)).rc) + goto array_err; + } + + if (lmi_job_has_in_param(job, "Target")) { + variant = lmi_job_get_in_param(job, "Target"); + if (g_variant_get_boolean(variant)) { + g_variant_unref(variant); + if ((csop = lmi_get_computer_system_safe(ctx)) == NULL) + goto err; + if ((status = CMSetProperty(instance, "Target", csop, CMPI_ref)).rc) + goto csop_err; + } else { + g_variant_unref(variant); + } + } + + if (lmi_job_has_in_param(job, "Collection")) { + variant = lmi_job_get_in_param(job, "Collection"); + if (g_variant_get_boolean(variant)) { + g_variant_unref(variant); + if ((namespace = lmi_read_config("CIM", "Namespace")) == NULL) + goto err; + create_instance_id(LMI_SystemSoftwareCollection_ClassName, NULL, + instance_id, BUFLEN); + colop = CMNewObjectPath(cb, namespace, + LMI_SystemSoftwareCollection_ClassName, &status); + if (colop == NULL || status.rc) + goto namespace_err; + if ((status = CMAddKey(colop, "InstanceID", + &value, CMPI_string)).rc) + goto colop_err; + if ((status = CMSetProperty(instance, "Collection", + colop, CMPI_ref)).rc) + goto colop_err; + } else { + g_variant_unref(variant); + } + } + } + + if (include_output) { + // No output parameters that could be filled yet + variant = lmi_job_get_result(job); + if (variant) { + tmp = g_variant_get_uint32(variant); + value.uint32 = tmp; + status = CMSetProperty(instance, "__ReturnValue", &value, CMPI_uint32); + g_variant_unref(variant); + if (status.rc) + goto err; + } + } + + g_free(namespace); + return status; + +colop_err: + CMRelease(colop); + goto err; +namespace_err: + g_free(namespace); +csop_err: + CMRelease(csop); + goto err; +array_err: + CMRelease(value.array); + goto err; +string_err: + CMRelease(value.string); +err: + if (!status.rc) { + lmi_error("Memory allocation failed"); + CMSetStatus(&status, CMPI_RC_ERR_FAILED); + } + return status; +} + +static void finish_partially_completed_installation_job( + LmiJob *job, + GCancellable *cancellable) +{ + gchar *jobid = lmi_job_get_jobid(job); + lmi_warn("TODO: finish partially completed job \"%s\".", jobid); + g_free(jobid); +} + +static const gchar *pk_progress_type_to_string(PkProgressType ptype) +{ + switch (ptype) { + case PK_PROGRESS_TYPE_PACKAGE_ID: + return "package-id"; + case PK_PROGRESS_TYPE_TRANSACTION_ID: + return "transaction-id"; + case PK_PROGRESS_TYPE_PERCENTAGE: + return "percentage"; + case PK_PROGRESS_TYPE_ALLOW_CANCEL: + return "allow-cancel"; + case PK_PROGRESS_TYPE_STATUS: + return "status"; + case PK_PROGRESS_TYPE_ROLE: + return "role"; + case PK_PROGRESS_TYPE_CALLER_ACTIVE: + return "caller-active"; + case PK_PROGRESS_TYPE_ELAPSED_TIME: + return "elapsed-time"; + case PK_PROGRESS_TYPE_REMAINING_TIME: + return "remaining-time"; + case PK_PROGRESS_TYPE_SPEED: + return "speed"; + case PK_PROGRESS_TYPE_DOWNLOAD_SIZE_REMAINING: + return "download-size-remaining"; + case PK_PROGRESS_TYPE_UID: + return "uid"; + case PK_PROGRESS_TYPE_PACKAGE: + return "package"; + case PK_PROGRESS_TYPE_ITEM_PROGRESS: + return "item-progress"; + case PK_PROGRESS_TYPE_TRANSACTION_FLAGS: + return "transaction-flags"; + case PK_PROGRESS_TYPE_INVALID: + return "invalid"; + default: + return "unknown"; + } +} + +static gboolean update_affected_packages(LmiJob *job, const char *package_id) +{ + GVariant *variant; + const gchar * const *affected_orig = NULL; + gchar **affected = NULL; + gboolean found = FALSE; + gsize length; + gsize pkg_id_len; + gboolean missing_version_info = FALSE; + g_assert(LMI_IS_JOB(job)); + + if (!pk_package_id_check(package_id)) { + lmi_warn("Ignoring invalid package_id \"%s\".", package_id); + return FALSE; + } + + pkg_id_len = strlen(package_id); + if (pkg_id_len >= 3 && strncmp(package_id + pkg_id_len - 3, ";;;", 3) == 0) + missing_version_info = TRUE; + + JOB_CRITICAL_BEGIN(job); + if (lmi_job_has_out_param(job, OUT_PARAM_AFFECTED_PACKAGES)) { + variant = lmi_job_get_out_param(job, OUT_PARAM_AFFECTED_PACKAGES); + affected_orig = g_variant_get_strv(variant, &length); + if ((affected = g_new0(gchar *, length + 2)) == NULL) + goto err; + for (gsize i=0; i < length; ++i) { + if ((affected[i] = g_strdup(affected_orig[i])) == NULL) { + g_variant_unref(variant); + goto affected_err; + } + if (!g_strcmp0(affected[i], package_id) || + (missing_version_info && + !strncmp(affected[i], package_id, pkg_id_len - 2))) + { + lmi_debug("Package id \"%s\" is already present in " + OUT_PARAM_AFFECTED_PACKAGES ".", package_id); + found = TRUE; + break; + } + } + g_variant_unref(variant); + if (!found) { + if (missing_version_info) { + lmi_warn("Not appending package id \"%s\" to " + OUT_PARAM_AFFECTED_PACKAGES + " which is missing version information.", package_id); + } else { + affected[length] = g_strdup(package_id); + if ((variant = g_variant_new_strv((gchar const * const *) affected, + length + 1)) == NULL) + goto affected_err; + lmi_job_set_out_param(job, OUT_PARAM_AFFECTED_PACKAGES, variant); + lmi_debug("Appended new pkgid=%s to " + OUT_PARAM_AFFECTED_PACKAGES ".", package_id); + } + } + g_strfreev(affected); + } else { + lmi_debug("Creating new " OUT_PARAM_AFFECTED_PACKAGES " with pkgid=%s.", + package_id); + if ((variant = g_variant_new_strv(&package_id, 1)) == NULL) + goto err; + lmi_job_set_out_param(job, OUT_PARAM_AFFECTED_PACKAGES, variant); + } + JOB_CRITICAL_END(job); + + return TRUE; + +affected_err: + g_strfreev(affected); +err: + lmi_error("Memory allocation failed!"); + JOB_CRITICAL_END(job); + return FALSE; +} + +/** + * Progress callback for packagekit functions. It updates job object. + * + * @param user_data Object of LmiJob. + * */ +static void progress_callback(PkProgress *progress, + PkProgressType type, + gpointer user_data) +{ + LmiJob *job = LMI_JOB(user_data); + gchar *jobid = NULL; + gchar *pkg_id = NULL, *pkg_id_item = NULL; + gchar *tid = NULL; + gint percent, percent_item; + guint status, status_item; + PkItemProgress *itemp = NULL; + GVariant *variant = NULL; + + if (type != PK_PROGRESS_TYPE_PERCENTAGE && + type != PK_PROGRESS_TYPE_TRANSACTION_ID && + type != PK_PROGRESS_TYPE_ITEM_PROGRESS && + type != PK_PROGRESS_TYPE_STATUS && + type != PK_PROGRESS_TYPE_PACKAGE_ID) + return; + + if ((jobid = lmi_job_get_jobid(job)) == NULL) { + lmi_error("Memory allocation failed!"); + goto err; + } + g_object_get(progress, + "status", &status, + "percentage", &percent, + "transaction-id", &tid, + "item-progress", &itemp, + "package-id", &pkg_id, + "status", &status, + NULL); + if (!tid) { + lmi_warn("Missing transaction-id property in progress object" + " for job \"%s\". Ignoring.", jobid); + goto err; + } + + lmi_debug("Processing progress update of \"%s\" for job \"%s\"" + " (%s, %3d%% complete).", + pk_progress_type_to_string(type), tid, + pk_status_enum_to_string(status), percent); + + lmi_job_set_jobid(job, tid); + switch (type) { + case PK_PROGRESS_TYPE_STATUS: + variant = g_variant_new_uint32(status); + lmi_job_set_out_param(job, "Status", variant); + lmi_debug("Status of job \"%s\" changed to \"%s\".", + tid, pk_status_enum_to_string(status)); + break; + + case PK_PROGRESS_TYPE_ITEM_PROGRESS: + if (!itemp) + break; + g_object_get(itemp, + "status", &status_item, + "percentage", &percent_item, + "package-id", &pkg_id_item, + NULL); + lmi_debug("Changed item progress of job \"%s\" regarding package" + " \"%s\" to \"%s\" with %3d%% completion.", + tid, pkg_id_item, + pk_status_enum_to_string(status_item), percent); + if (!update_affected_packages(job, pkg_id_item)) + goto err; + g_free(pkg_id_item); + break; + + case PK_PROGRESS_TYPE_PACKAGE_ID: + lmi_debug("Changed package id of job \"%s\" to %s.", tid, pkg_id); + if (!update_affected_packages(job, pkg_id)) + goto err; + break; + + case PK_PROGRESS_TYPE_PERCENTAGE: + if (!lmi_job_is_finished(job)) + lmi_job_set_percent_complete(job, percent); + break; + + default: + break; + } + g_free(jobid); + return; + +err: + g_free(pkg_id_item); + g_free(tid); + g_free(pkg_id); + g_clear_object(&itemp); + g_free(jobid); + return; +} + +/** + * Process package installation request. First it checks whether the package + * is already installed or not. + * + * @param package_ids Is an array of package ids at least 2 pointers long. + * First pointer shall point to an id of package that shall be installed. + * Second must be `NULL`. + * @param sw_pkg Must be prefilled with information corresponding to the same + * package as in *package_ids*. + * @param force Whether to forcibly reinstall already installed package. Or + * downgrade if a desired one is older than installed. + * @param repair Whether to reinstall already installed package. + * @param status_code Will be set to corresponding code if the installation + * fails. + * @return Results object returned by PackageKit function used to install the + * package. + */ +static PkResults *install_package(PkTask *task, + LmiJob *job, + const gchar * const *package_ids, + const SwPackage *sw_pkg, + gboolean force, + gboolean repair, + GCancellable *cancellable, + LmiJobStatusCodeEnum *status_code, + gchar *err_buf, + guint err_buflen) +{ + PkResults *results = NULL; + GError *gerror = NULL; + PkTask *tmp_task = pk_task_new(); + PkPackage *installed_pkg = NULL; + gchar *values[] = {NULL, NULL}; + GPtrArray *array; + PkBitfield filter = 0; + gchar *jobid = lmi_job_get_jobid(job); + g_assert(PK_IS_TASK(task)); + g_assert(package_ids); + g_assert(package_ids[0] != NULL); + g_assert(package_ids[1] == NULL); + g_assert(sw_pkg); + g_assert(status_code); + g_assert(err_buf); + + values[0] = sw_pkg->name; + pk_bitfield_add(filter, PK_FILTER_ENUM_INSTALLED); + results = pk_task_search_names_sync(tmp_task, filter, + values, cancellable, NULL, NULL, &gerror); + if (check_and_create_error_msg(results, gerror, + "Lookup of installed package failed", err_buf, err_buflen)) + { + if (results != NULL && + pk_results_get_exit_code(results) == PK_EXIT_ENUM_CANCELLED) + { + lmi_warn("Installation job \"%s\" has been terminated.", jobid); + err_buf[0] = 0; + } + goto err; + } + + if (cancellable && g_cancellable_is_cancelled(cancellable)) { + g_clear_object(&results); + goto err; + } + + if ((array = pk_results_get_package_array(results)) == NULL) { + lmi_error("Memory allocation failed"); + *status_code = LMI_JOB_STATUS_CODE_ENUM_FAILED; + goto err; + } + for (gsize i = 0; i < array->len; ++i) { + installed_pkg = g_object_ref(g_ptr_array_index(array, i)); + if (!g_strcmp0(pk_package_get_name(installed_pkg), sw_pkg->name) && + !g_strcmp0(pk_package_get_arch(installed_pkg), sw_pkg->arch)) + break; + installed_pkg = NULL; + } + g_clear_object(&results); + + if (installed_pkg != NULL) { + if (force || (!g_strcmp0(pk_package_get_id(installed_pkg), + package_ids[0]) && repair)) + { + // TODO: reinstall with rpmlib + lmi_debug("Reinstalling installed package \"%s\".", package_ids[0]); + snprintf(err_buf, err_buflen, + "Package reinstallation is currently not supported."); + *status_code = LMI_JOB_STATUS_CODE_ENUM_NOT_SUPPORTED; + } else { + snprintf(err_buf, err_buflen, + "Package \"%s\" is already installed.", package_ids[0]); + *status_code = LMI_JOB_STATUS_CODE_ENUM_ALREADY_EXISTS; + } + goto array_err; + } else { + results = pk_task_install_packages_sync( + task, + (gchar **) package_ids, // no modification is done + cancellable, progress_callback, job, &gerror); + } + g_ptr_array_unref(array); + + if (check_and_create_error_msg(results, gerror, "Installation failed", + err_buf, err_buflen)) + { + if (results != NULL && + pk_results_get_exit_code(results) == PK_EXIT_ENUM_CANCELLED) + { + lmi_warn("Installation job \"%s\" has been terminated.", jobid); + err_buf[0] = 0; + } + goto err; + } + g_free(jobid); + return results; + +array_err: + g_ptr_array_unref(array); +err: + g_clear_object(&results); + g_clear_error(&gerror); + g_free(jobid); + return results; +} + +void lmi_sw_installation_job_process(LmiJob *job, + GCancellable *cancellable) +{ + PkTask *task; + guint32 op; + GVariant *variant; + gchar const *source; + SwPackage sw_pkg; + PkPackage *pk_pkg = NULL; + gchar const *package_ids[] = {NULL, NULL}; + PkResults *results; + GPtrArray *packages; + LmiJobStatusCodeEnum status_code = LMI_JOB_STATUS_CODE_ENUM_FAILED; + gchar *error = NULL; + GError *gerror = NULL; + gchar error_msg[BUFLEN] = ""; + gchar *jobid = NULL; + PkBitfield filter = 0L; + g_assert(LMI_IS_SW_INSTALLATION_JOB(job)); + + lmi_debug("Started processing installation job %u.", + lmi_job_get_number(job)); + + if (cancellable && g_cancellable_is_cancelled(cancellable)) + return; + if (lmi_job_has_own_jobid(job)) { + finish_partially_completed_installation_job(job, cancellable); + } else { + if ((jobid = lmi_job_get_jobid(job)) == NULL) { + error = "Memory allocation failed!"; + goto err; + } + if (!lmi_job_has_in_param(job, IN_PARAM_INSTALL_OPTIONS_NAME) || + !lmi_job_has_in_param(job, IN_PARAM_SOURCE_NAME)) + { + lmi_error("Missing one of { %s, %s } input arguments in job \"%s\"!" + " Terminating job processing.", + IN_PARAM_INSTALL_OPTIONS_NAME, + IN_PARAM_SOURCE_NAME, jobid); + g_snprintf(error_msg, BUFLEN, + "Missing one of { %s, %s } input arguments.", + IN_PARAM_INSTALL_OPTIONS_NAME, IN_PARAM_SOURCE_NAME); + status_code = LMI_JOB_STATUS_CODE_ENUM_INVALID_PARAMETER; + goto err; + } + + task = pk_task_new(); + g_object_set(task, "interactive", FALSE, NULL); + variant = lmi_job_get_in_param(job, IN_PARAM_INSTALL_OPTIONS_NAME); + op = g_variant_get_uint32(variant); + g_variant_unref(variant); + variant = lmi_job_get_in_param(job, IN_PARAM_SOURCE_NAME); + source = g_variant_get_string(variant, NULL); + g_variant_unref(variant); + + init_sw_package(&sw_pkg); + + if (create_sw_package_from_elem_name(source, &sw_pkg) != 0) { + status_code = LMI_JOB_STATUS_CODE_ENUM_INVALID_PARAMETER; + error = "Failed to parse InstanceID."; + goto err; + } + + if (op & INSTALLATION_OPERATION_REMOVE) { + pk_bitfield_add(filter, PK_FILTER_ENUM_INSTALLED); + } + get_pk_pkg_from_sw_pkg(&sw_pkg, filter, &pk_pkg); + if (!pk_pkg) { + free_sw_package(&sw_pkg); + status_code = LMI_JOB_STATUS_CODE_ENUM_NOT_FOUND; + error = "Failed to find matching package."; + goto err; + } + + if (cancellable && g_cancellable_is_cancelled(cancellable)) + goto sw_pkg_err; + + *package_ids = pk_package_get_id(pk_pkg); + if (!update_affected_packages(job, *package_ids)) + { + error = "Memory allocation failed!"; + goto sw_pkg_err; + } + + if (op & INSTALLATION_OPERATION_INSTALL) { + lmi_debug("Starting synchronous package installation task for" + " job \"%s\".", jobid); + results = install_package(task, job, package_ids, &sw_pkg, + op & INSTALLATION_OPERATION_FORCE, + op & INSTALLATION_OPERATION_REPAIR, + cancellable, &status_code, error_msg, BUFLEN); + if (results == NULL) + goto sw_pkg_err; + } else if (op & INSTALLATION_OPERATION_UPDATE) { + lmi_debug("Starting synchronous package update task for" + " job \"%s\".", jobid); + results = pk_task_update_packages_sync( + task, + (gchar **) package_ids, // no modification is done + cancellable, progress_callback, job, &gerror); + error = "Update failed"; + } else { + lmi_debug("Starting synchronous package removal task for" + " job \"%s\".", jobid); + results = pk_task_remove_packages_sync( + task, + (gchar **) package_ids, // no modification is done + op & INSTALLATION_OPERATION_FORCE, // allow dependencies + TRUE, // auto remove + cancellable, progress_callback, job, &gerror); + error = "Uninstallation failed"; + } + free_sw_package(&sw_pkg); + g_clear_object(&pk_pkg); + + if (check_and_create_error_msg(results, gerror, error, + error_msg, BUFLEN)) + { + if (results != NULL && + pk_results_get_exit_code(results) == PK_EXIT_ENUM_CANCELLED) + { + lmi_warn("Installation job \"%s\" has been terminated.", jobid); + error = NULL; + g_clear_error(&gerror); + } + goto sw_pkg_err; + } + + if ((packages = pk_results_get_package_array(results)) == NULL) { + error = "Memory allocation failed!"; + goto results_err; + } + g_clear_object(&results); + for (gsize i=0; i < packages->len; ++i) { + pk_pkg = g_ptr_array_index(packages, i); + if (!update_affected_packages(job, pk_package_get_id(pk_pkg))) + { + error = "Memory allocation failed!"; + goto packages_err; + } + } + g_ptr_array_unref(packages); + lmi_job_finish_ok_with_code(job, + INSTALL_METHOD_RESULT_JOB_COMPLETED_WITH_NO_ERROR); + } + + return; + +packages_err: + g_ptr_array_unref(packages); +results_err: + g_clear_object(&results); + goto err; +sw_pkg_err: + free_sw_package(&sw_pkg); +err: + if (gerror) + error = gerror->message; + if (error_msg[0]) + error = error_msg; + if (error) { + lmi_error("Installation job \"%s\" failed: %s", jobid, error); + lmi_job_finish_exception(job, status_code, error); + } else { + lmi_warn("Terminating installation job \"%s\".", jobid); + lmi_job_finish_terminate(job); + } + g_clear_error(&gerror); + g_free(jobid); +} + +void lmi_sw_verification_job_process(LmiJob *job, + GCancellable *cancellable) +{ + g_assert(LMI_IS_SW_VERIFICATION_JOB(job)); + gchar *jobid = lmi_job_get_jobid(job); + lmi_warn("TODO: process verification job \"%s\".", jobid); + g_free(jobid); +} diff --git a/src/software-dbus/lmi_sw_job.h b/src/software-dbus/lmi_sw_job.h new file mode 100644 index 0000000..84ac3e4 --- /dev/null +++ b/src/software-dbus/lmi_sw_job.h @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2012-2014 Red Hat, Inc. All rights reserved. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Michal Minar + */ + +#ifndef LMI_SW_JOB_H +#define LMI_SW_JOB_H + +#include +#include "lmi_job.h" + +#define INSTALL_OPTION_DEFER_TARGET_SYSTEM_RESET 2 +#define INSTALL_OPTION_FORCE_INSTALLATION 3 +#define INSTALL_OPTION_INSTALL 4 +#define INSTALL_OPTION_UPDATE 5 +#define INSTALL_OPTION_REPAIR 6 +#define INSTALL_OPTION_REBOOT 7 +#define INSTALL_OPTION_PASSWORD 8 +#define INSTALL_OPTION_UNINSTALL 9 +#define INSTALL_OPTION_LOG 10 +#define INSTALL_OPTION_SILENT_MODE 11 +#define INSTALL_OPTION_ADMINISTRATIVE_MODE 12 +#define INSTALL_OPTION_SCHEDULE_INSTALL_AT 13 + +#define INSTALLATION_OPERATION_INSTALL (1 << 0) +#define INSTALLATION_OPERATION_UPDATE (1 << 1) +#define INSTALLATION_OPERATION_REMOVE (1 << 2) +#define INSTALLATION_OPERATION_FORCE (1 << 3) +#define INSTALLATION_OPERATION_REPAIR (1 << 4) +#define INSTALLATION_OPERATION_EXCLUSIVE_GROUP ( \ + INSTALLATION_OPERATION_INSTALL | \ + INSTALLATION_OPERATION_UPDATE | \ + INSTALLATION_OPERATION_REMOVE) + +#define INSTALL_METHOD_RESULT_JOB_COMPLETED_WITH_NO_ERROR 0 +#define INSTALL_METHOD_RESULT_NOT_SUPPORTED 1 +#define INSTALL_METHOD_RESULT_UNSPECIFIED_ERROR 2 +#define INSTALL_METHOD_RESULT_TIMEOUT 3 +#define INSTALL_METHOD_RESULT_FAILED 4 +#define INSTALL_METHOD_RESULT_INVALID_PARAMETER 5 +#define INSTALL_METHOD_RESULT_TARGET_IN_USE 6 +#define INSTALL_METHOD_RESULT_JOB_STARTED 4096 +#define INSTALL_METHOD_RESULT_UNSUPPORTED_TARGET_TYPE 4097 +#define INSTALL_METHOD_RESULT_UNATTENDED_INSTALLATION_NOT_SUPPORTED 4098 +#define INSTALL_METHOD_RESULT_DOWNGRADE_REINSTALL_NOT_SUPPORTED 4099 +#define INSTALL_METHOD_RESULT_NOT_ENOUGH_MEMORY 4100 +#define INSTALL_METHOD_RESULT_NOT_ENOUGH_SWAP_SPACE 4101 +#define INSTALL_METHOD_RESULT_UNSUPPORTED_VERSION_TRANSITION 4102 +#define INSTALL_METHOD_RESULT_NOT_ENOUGH_DISK_SPACE 4103 +#define INSTALL_METHOD_RESULT_SOFTWARE_AND_TARGET_OPERATING_SYSTEM_MISMATCH 4104 +#define INSTALL_METHOD_RESULT_MISSING_DEPENDENCIES 4105 +#define INSTALL_METHOD_RESULT_NOT_APPLICABLE_TO_TARGET 4106 +#define INSTALL_METHOD_RESULT_NO_SUPPORTED_PATH_TO_IMAGE 4107 +#define INSTALL_METHOD_RESULT_CANNOT_ADD_TO_COLLECTION 4108 + +#define IN_PARAM_INSTALL_OPTIONS_NAME "InstallOptions" +#define IN_PARAM_TARGET_NAME "Target" +#define IN_PARAM_COLLECTION_NAME "Collection" +#define IN_PARAM_SOURCE_NAME "Source" +#define IN_PARAM_URI_NAME "URI" +#define OUT_PARAM_AFFECTED_PACKAGES "AffectedPackages" + +/****************************************************************************** + * Software installation job + *****************************************************************************/ +#define LMI_TYPE_SW_INSTALLATION_JOB \ + (lmi_sw_installation_job_get_type ()) +#define LMI_SW_INSTALLATION_JOB(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), LMI_TYPE_SW_INSTALLATION_JOB, \ + LmiSwInstallationJob)) +#define LMI_IS_SW_INSTALLATION_JOB(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LMI_TYPE_SW_INSTALLATION_JOB)) +#define LMI_SW_INSTALLATION_JOB_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), LMI_TYPE_SW_INSTALLATION_JOB, \ + LmiSwInstallationJobClass)) +#define LMI_IS_SW_INSTALLATION_JOB_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), LMI_TYPE_SW_INSTALLATION_JOB)) +#define LMI_SW_INSTALLATION_JOB_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), LMI_TYPE_SW_INSTALLATION_JOB, \ + LmiSwInstallationJobClass)) + +typedef struct _LmiSwInstallationJob LmiSwInstallationJob; +typedef struct _LmiSwInstallationJobClass LmiSwInstallationJobClass; + +struct _LmiSwInstallationJob { + LmiJob parent; +}; + +struct _LmiSwInstallationJobClass { + LmiJobClass parent_class; +}; + +GType lmi_sw_installation_job_get_type(); + +LmiSwInstallationJob *lmi_sw_installation_job_new(); + +void lmi_sw_installation_job_process(LmiJob *job, + GCancellable *cancellable); + +/****************************************************************************** + * Software verification job + *****************************************************************************/ + +#define LMI_TYPE_SW_VERIFICATION_JOB \ + (lmi_sw_verification_job_get_type ()) +#define LMI_SW_VERIFICATION_JOB(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), LMI_TYPE_SW_VERIFICATION_JOB, \ + LmiSwVerificationJob)) +#define LMI_IS_SW_VERIFICATION_JOB(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LMI_TYPE_SW_VERIFICATION_JOB)) +#define LMI_SW_VERIFICATION_JOB_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), LMI_TYPE_SW_VERIFICATION_JOB, \ + LmiSwVerificationJobClass)) +#define LMI_IS_SW_VERIFICATION_JOB_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), LMI_TYPE_SW_VERIFICATION_JOB)) +#define LMI_SW_VERIFICATION_JOB_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), LMI_TYPE_SW_VERIFICATION_JOB, \ + LmiSwVerificationJobClass)) + +typedef struct _LmiSwVerificationJob LmiSwVerificationJob; +typedef struct _LmiSwVerificationJobClass LmiSwVerificationJobClass; + +struct _LmiSwVerificationJob { + LmiJob parent; +}; + +struct _LmiSwVerificationJobClass { + LmiJobClass parent_class; +}; + +GType lmi_sw_verification_job_get_type(); + +LmiSwVerificationJob *lmi_sw_verification_job_new(); + +void lmi_sw_verification_job_process(LmiJob *job, + GCancellable *cancellable); + +/****************************************************************************** + * CIM related stuff + *****************************************************************************/ +CMPIStatus lmi_sw_job_to_cim_instance(const CMPIBroker *cb, + const CMPIContext *ctx, + const LmiJob *job, + CMPIInstance *instance); + +CMPIStatus lmi_sw_job_make_job_parameters(const CMPIBroker *cb, + const CMPIContext *ctx, + const LmiJob *job, + gboolean include_input, + gboolean include_output, + CMPIInstance *instance); + +CMPIStatus lmi_sw_job_get_number_from_instance_id( + const gchar *instance_id, + guint *number); + +CMPIStatus lmi_sw_job_get_number_from_op(const CMPIObjectPath *op, + guint *number); + +#endif /* end of include guard: LMI_SW_JOB_H */ diff --git a/src/software-dbus/sw-utils.c b/src/software-dbus/sw-utils.c index c3105b6..89f843a 100644 --- a/src/software-dbus/sw-utils.c +++ b/src/software-dbus/sw-utils.c @@ -16,12 +16,63 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Authors: Peter Schiffer + * Authors: Michal Minar */ +#include "ind_sender.h" +#include "job_manager.h" +#include "lmi_sw_job.h" #include "sw-utils.h" +#include "LMI_SystemSoftwareCollection.h" const char *provider_name = "software"; const ConfigEntry *provider_config_defaults = NULL; +static JobTypeEntry _sw_job_type_entries[] = \ + { { 0 // needs to be set later with LMI_TYPE_SW_INSTALLATION_JOB + , "LMI_SoftwareInstallationJob" + , lmi_sw_job_to_cim_instance + , lmi_sw_job_make_job_parameters + , METHOD_RESULT_VALUE_TYPE_UINT32 + , lmi_sw_installation_job_process + , TRUE, TRUE } + , { 0 // needs to be set later with LMI_TYPE_SW_VERIFICATION_JOB + , "LMI_SoftwareVerificationJob" + , lmi_sw_job_to_cim_instance + , lmi_sw_job_make_job_parameters + , METHOD_RESULT_VALUE_TYPE_UINT32 + , lmi_sw_verification_job_process + , TRUE, TRUE } + }; +#define JOB_TYPE_ENTRIES_COUNT 2 + +/****************************************************************************** + * Provider init and cleanup + *****************************************************************************/ +void software_init(const char *provider, + const CMPIBroker *cb, + const CMPIContext *ctx, + const ConfigEntry *provider_config_defaults) +{ + CMPIStatus status; + lmi_init(provider, cb, ctx, provider_config_defaults); + _sw_job_type_entries[0].job_type = LMI_TYPE_SW_INSTALLATION_JOB; + _sw_job_type_entries[1].job_type = LMI_TYPE_SW_VERIFICATION_JOB; + if ((status = jobmgr_init(cb, ctx, "Software", false, + _sw_job_type_entries, JOB_TYPE_ENTRIES_COUNT)).rc) + lmi_error("Failed to initialize provider %s: %s", + provider, status.msg ? + CMGetCharsPtr(status.msg, NULL) : "unknown error"); + lmi_debug("Software provider initialized."); +} + +CMPIStatus software_cleanup() +{ + lmi_debug("Software provider cleanup started."); + CMPIStatus status = {CMPI_RC_OK, NULL}; + status = jobmgr_cleanup(); + lmi_debug("Software provider cleanup finished."); + return status; +} /******************************************************************************* * SwPackage & related functions diff --git a/src/software-dbus/sw-utils.h b/src/software-dbus/sw-utils.h index 5f28162..6e09b80 100644 --- a/src/software-dbus/sw-utils.h +++ b/src/software-dbus/sw-utils.h @@ -45,6 +45,16 @@ const char *provider_name; const ConfigEntry *provider_config_defaults; +/****************************************************************************** + * Provider init and cleanup + *****************************************************************************/ +void software_init(const char *provider, + const CMPIBroker *cb, + const CMPIContext *ctx, + const ConfigEntry *provider_config_defaults); + +CMPIStatus software_cleanup(); + /******************************************************************************* * SwPackage & related functions ******************************************************************************/ -- cgit