/* * Copyright (C) 2013-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: Peter Schiffer */ #include "sw-utils.h" const char *provider_name = "software"; const ConfigEntry *provider_config_defaults = NULL; /******************************************************************************* * SwPackage & related functions ******************************************************************************/ void init_sw_package(SwPackage *pkg) { pkg->name = NULL; pkg->epoch = NULL; pkg->version = NULL; pkg->release = NULL; pkg->arch = NULL; pkg->pk_version = NULL; } void free_sw_package(SwPackage *pkg) { free(pkg->name); free(pkg->epoch); free(pkg->version); free(pkg->release); free(pkg->arch); free(pkg->pk_version); init_sw_package(pkg); } short create_sw_package_from_pk_pkg_id(const char *pk_pkg_id, SwPackage *sw_pkg) { short ret = -1; char *delim; const gchar *id, *name, *arch, *ver; gchar **split_id = NULL; init_sw_package(sw_pkg); if (!pk_pkg_id || !*pk_pkg_id) { lmi_warn("Empty package ID!"); goto done; } id = pk_pkg_id; split_id = pk_package_id_split(id); if (!split_id) { lmi_warn("Invalid package ID: %s!", id); goto done; } if (!(name = split_id[PK_PACKAGE_ID_NAME])) { lmi_warn("Package with ID: %s doesn't have name!", id); goto done; } if (!(ver = split_id[PK_PACKAGE_ID_VERSION])) { lmi_warn("Package with ID: %s doesn't have version!", id); goto done; } if (!(arch = split_id[PK_PACKAGE_ID_ARCH])) { lmi_warn("Package with ID: %s doesn't have architecture!", id); goto done; } sw_pkg->name = strdup(name); sw_pkg->arch = strdup(arch); sw_pkg->pk_version = strdup(ver); if ((delim = strchr(ver, ':'))) { sw_pkg->epoch = strndup(ver, delim - ver); ver = delim + 1; } else { sw_pkg->epoch = strdup("0"); } if ((delim = strrchr(ver, '-'))) { sw_pkg->version = strndup(ver, delim - ver); sw_pkg->release = strdup(delim + 1); } else { sw_pkg->version = strdup(ver); sw_pkg->release = strdup("0"); lmi_warn("Package with ID: %s doesn't have release number! Using '0' instead.", id); } if (!sw_pkg->name || !sw_pkg->arch || !sw_pkg->epoch || !sw_pkg->version || !sw_pkg->release || !sw_pkg->pk_version) { lmi_warn("Memory allocation failed."); goto done; } ret = 0; done: if (ret != 0) { free_sw_package(sw_pkg); } return ret; } short create_sw_package_from_pk_pkg(PkPackage *pk_pkg, SwPackage *sw_pkg) { return create_sw_package_from_pk_pkg_id(pk_package_get_id(pk_pkg), sw_pkg); } short create_sw_package_from_elem_name(const char *elem_name, SwPackage *sw_pkg) { short ret = -1; char *en, *delim; init_sw_package(sw_pkg); if (!elem_name || !*elem_name) { lmi_warn("Empty element name."); goto done; } if (!(en = strdup(elem_name))) { lmi_warn("Memory allocation failed."); goto done; } if (!(delim = strrchr(en, '.'))) { lmi_warn("Invalid element name of the package: %s", elem_name); goto done; } sw_pkg->arch = strdup(delim + 1); delim[0] = '\0'; if (!(delim = strrchr(en, '-'))) { lmi_warn("Invalid element name of the package: %s", elem_name); goto done; } sw_pkg->release = strdup(delim + 1); delim[0] = '\0'; if ((delim = strrchr(en, ':'))) { sw_pkg->version = strdup(delim + 1); delim[0] = '\0'; if (!(delim = strrchr(en, '-'))) { lmi_warn("Invalid element name of the package: %s", elem_name); goto done; } sw_pkg->epoch = strdup(delim + 1); delim[0] = '\0'; } else { if (!(delim = strrchr(en, '-'))) { lmi_warn("Invalid element name of the package: %s", elem_name); goto done; } sw_pkg->version = strdup(delim + 1); delim[0] = '\0'; sw_pkg->epoch = strdup("0"); } sw_pkg->name = strdup(en); if (!sw_pkg->name || !sw_pkg->arch || !sw_pkg->epoch || !sw_pkg->version || !sw_pkg->release) { lmi_warn("Memory allocation failed."); goto done; } if (strcmp(sw_pkg->epoch, "0") == 0) { if (asprintf(&sw_pkg->pk_version, "%s-%s", sw_pkg->version, sw_pkg->release) < 0) { lmi_warn("Memory allocation failed."); goto done; } } else { if (asprintf(&sw_pkg->pk_version, "%s:%s-%s", sw_pkg->epoch, sw_pkg->version, sw_pkg->release) < 0) { lmi_warn("Memory allocation failed."); goto done; } } ret = 0; done: free(en); if (ret != 0) { free_sw_package(sw_pkg); } return ret; } void sw_pkg_get_version_str(const SwPackage *pkg, char *ver_str, const unsigned ver_str_len) { snprintf(ver_str, ver_str_len, "%s:%s-%s.%s", pkg->epoch, pkg->version, pkg->release, pkg->arch); } void sw_pkg_get_element_name(const SwPackage *pkg, char *elem_name, const unsigned elem_name_len) { snprintf(elem_name, elem_name_len, "%s-%s:%s-%s.%s", pkg->name, pkg->epoch, pkg->version, pkg->release, pkg->arch); } /******************************************************************************* * Functions related to single PkPackage ******************************************************************************/ void get_pk_pkg_from_sw_pkg(const SwPackage *sw_pkg, PkBitfield filters, PkPackage **pk_pkg) { PkTask *task = NULL; PkPackage *item = NULL; PkResults *results = NULL; GPtrArray *array = NULL; GError *gerror = NULL; gchar **values = NULL; unsigned i; char error_msg[BUFLEN] = ""; task = pk_task_new(); values = g_new0(gchar*, 2); values[0] = g_strdup(sw_pkg->name); results = pk_task_search_names_sync(task, filters, values, NULL, NULL, NULL, &gerror); if (check_and_create_error_msg(results, gerror, "Resolving package failed", error_msg, BUFLEN)) { lmi_warn(error_msg); goto done; } array = pk_results_get_package_array(results); for (i = 0; i < array->len; i++) { item = g_ptr_array_index(array, i); if (strcmp(pk_package_get_name(item), sw_pkg->name) == 0 && strcmp(pk_package_get_version(item), sw_pkg->pk_version) == 0 && strcmp(pk_package_get_arch(item), sw_pkg->arch) == 0) { *pk_pkg = g_object_ref(item); break; } } done: g_strfreev(values); g_clear_error(&gerror); if (array) { g_ptr_array_unref(array); } if (results) { g_object_unref(results); } if (task) { g_object_unref(task); } return; } void get_pk_det_from_pk_pkg(PkPackage *pk_pkg, PkDetails **pk_det, PkTask *task_p) { PkDetails *item = NULL; GPtrArray *array = NULL; gchar **values = NULL; unsigned i; values = g_new0(gchar*, 2); values[0] = g_strdup(pk_package_get_id(pk_pkg)); get_pk_det_from_array(values, &array, task_p); if (!array) { goto done; } for (i = 0; i < array->len; i++) { item = g_ptr_array_index(array, i); if (strcmp(pk_details_get_package_id(item), pk_package_get_id(pk_pkg)) == 0) { *pk_det = g_object_ref(item); break; } } done: g_strfreev(values); if (array) { g_ptr_array_unref(array); } return; } void create_instance_from_pkgkit_data(PkPackage *pk_pkg, PkDetails *pk_det, SwPackage *sw_pkg, const CMPIBroker *cb, const char *ns, LMI_SoftwareIdentity *w) { const gchar *summary, *desc = NULL; char elem_name[BUFLEN] = "", ver_str[BUFLEN] = "", instance_id[BUFLEN] = ""; summary = pk_package_get_summary(pk_pkg); if (pk_det) { desc = pk_details_get_description(pk_det); } sw_pkg_get_element_name(sw_pkg, elem_name, BUFLEN); sw_pkg_get_version_str(sw_pkg, ver_str, BUFLEN); create_instance_id(LMI_SoftwareIdentity_ClassName, elem_name, instance_id, BUFLEN); LMI_SoftwareIdentity_Init(w, cb, ns); LMI_SoftwareIdentity_Set_InstanceID(w, instance_id); LMI_SoftwareIdentity_Init_Classifications(w, 1); LMI_SoftwareIdentity_Set_Classifications(w, 0, 0); LMI_SoftwareIdentity_Init_TargetTypes(w, 2); LMI_SoftwareIdentity_Set_TargetTypes(w, 0, "rpm"); LMI_SoftwareIdentity_Set_TargetTypes(w, 1, "yum"); LMI_SoftwareIdentity_Set_IsEntity(w, 1); LMI_SoftwareIdentity_Set_Architecture(w, sw_pkg->arch); LMI_SoftwareIdentity_Set_ElementName(w, elem_name); LMI_SoftwareIdentity_Set_Epoch(w, atoi(sw_pkg->epoch)); LMI_SoftwareIdentity_Set_Name(w, sw_pkg->name); LMI_SoftwareIdentity_Set_Release(w, sw_pkg->release); LMI_SoftwareIdentity_Set_Version(w, sw_pkg->version); LMI_SoftwareIdentity_Set_VersionString(w, ver_str); if (summary) { LMI_SoftwareIdentity_Set_Caption(w, summary); } if (desc) { LMI_SoftwareIdentity_Set_Description(w, desc); } return; } /******************************************************************************* * Functions related to multiple PkPackages ******************************************************************************/ void get_pk_packages(PkBitfield filters, GPtrArray **garray, char *error_msg, const unsigned error_msg_len) { PkTask *task = NULL; PkResults *results = NULL; GError *gerror = NULL; GPtrArray *array = NULL; task = pk_task_new(); results = pk_task_get_packages_sync(task, filters, NULL, NULL, NULL, &gerror); if (check_and_create_error_msg(results, gerror, "Getting list of packages failed", error_msg, error_msg_len)) { goto done; } array = pk_results_get_package_array(results); g_ptr_array_sort(array, (GCompareFunc) pk_pkg_cmp); *garray = g_ptr_array_ref(array); done: g_clear_error(&gerror); if (array) { g_ptr_array_unref(array); } if (results) { g_object_unref(results); } if (task) { g_object_unref(task); } return; } void get_pk_det_from_array(char **values_p, GPtrArray **garray, PkTask *task_p) { PkTask *task = NULL; PkResults *results = NULL; GPtrArray *array = NULL, *array2 = NULL; GError *gerror = NULL; gchar **values = NULL; unsigned i, j, values_p_count = 0; char error_msg[BUFLEN] = ""; if (!values_p || !*values_p) { lmi_warn("No package IDs given."); goto done; } while (values_p[++values_p_count]); if (task_p) { task = task_p; } else { task = pk_task_new(); } if (values_p_count < PK_DETAILS_LIMIT) { values = g_new0(gchar*, values_p_count + 1); } else { values = g_new0(gchar*, PK_DETAILS_LIMIT + 1); } for (i = 0; i < values_p_count / PK_DETAILS_LIMIT + 1; i++) { j = 0; while (values[j]) { values[j++] = NULL; } for (j = i * PK_DETAILS_LIMIT; j < (i + 1) * PK_DETAILS_LIMIT && j < values_p_count; j++) { values[j - i * PK_DETAILS_LIMIT] = values_p[j]; } results = pk_task_get_details_sync(task, values, NULL, NULL, NULL, &gerror); if (check_and_create_error_msg(results, gerror, "Getting package details failed", error_msg, BUFLEN)) { lmi_warn(error_msg); goto done; } array = pk_results_get_details_array(results); gc_gobject_ptr_array_append(&array2, array); if (results) { g_object_unref(results); results = NULL; } if (array) { g_ptr_array_unref(array); array = NULL; } } g_ptr_array_sort(array2, (GCompareFunc) pk_det_cmp); *garray = g_ptr_array_ref(array2); done: g_clear_error(&gerror); if (values) { g_free(values); } if (array) { g_ptr_array_unref(array); } if (array2) { g_ptr_array_unref(array2); } if (results) { g_object_unref(results); } if (!task_p && task) { g_object_unref(task); } return; } void k_return_sw_identity_op_from_pkg_id(const char *pkg_id, const CMPIBroker *cb, const char *ns, const CMPIResult* cr) { SwPackage sw_pkg; char elem_name[BUFLEN] = "", instance_id[BUFLEN] = ""; init_sw_package(&sw_pkg); if (create_sw_package_from_pk_pkg_id(pkg_id, &sw_pkg) != 0) { goto done; } sw_pkg_get_element_name(&sw_pkg, elem_name, BUFLEN); create_instance_id(LMI_SoftwareIdentity_ClassName, elem_name, instance_id, BUFLEN); LMI_SoftwareIdentityRef w; LMI_SoftwareIdentityRef_Init(&w, cb, ns); LMI_SoftwareIdentityRef_Set_InstanceID(&w, instance_id); KReturnObjectPath(cr, w); done: free_sw_package(&sw_pkg); } void enum_sw_identity_instance_names(PkBitfield filters, const CMPIBroker *cb, const char *ns, const CMPIResult* cr, char *error_msg, const unsigned error_msg_len) { GPtrArray *array = NULL; unsigned i; get_pk_packages(filters, &array, error_msg, error_msg_len); if (!array) { goto done; } for (i = 0; i < array->len; i++) { k_return_sw_identity_op_from_pkg_id( pk_package_get_id(g_ptr_array_index(array, i)), cb, ns, cr); } done: if (array) { g_ptr_array_unref(array); } return; } void enum_sw_identity_instances(PkBitfield filters, const CMPIBroker *cb, const char *ns, const CMPIResult* cr, char *error_msg, const unsigned error_msg_len) { LMI_SoftwareIdentity w; PkPackage *pk_pkg = NULL; PkDetails *pk_det = NULL; GPtrArray *pkg_array = NULL, *det_array = NULL; gchar **values = NULL; SwPackage sw_pkg; int cmpres; unsigned i, det_a_i = 0; init_sw_package(&sw_pkg); get_pk_packages(filters, &pkg_array, error_msg, error_msg_len); if (!pkg_array) { goto done; } values = g_new0(gchar*, pkg_array->len + 1); for (i = 0; i < pkg_array->len; i++) { values[i] = g_strdup(pk_package_get_id(g_ptr_array_index(pkg_array, i))); } get_pk_det_from_array(values, &det_array, NULL); g_strfreev(values); values = NULL; for (i = 0; i < pkg_array->len; i++) { pk_pkg = g_ptr_array_index(pkg_array, i); if (create_sw_package_from_pk_pkg(pk_pkg, &sw_pkg) != 0) { continue; } cmpres = -1; if (det_array) { while (cmpres < 0 && det_a_i < det_array->len) { pk_det = g_ptr_array_index(det_array, det_a_i); cmpres = strcmp(pk_details_get_package_id(pk_det), pk_package_get_id(pk_pkg)); if (cmpres < 0) { /* this should not happen - * we have spare unmatched package details */ det_a_i++; } else if (cmpres > 0) { /* no matching package details for current pk_pkg */ pk_det = NULL; } else { /* found a match */ det_a_i++; } } } create_instance_from_pkgkit_data(pk_pkg, pk_det, &sw_pkg, cb, ns, &w); KReturnInstance(cr, w); free_sw_package(&sw_pkg); } done: if (det_array) { g_ptr_array_unref(det_array); } if (pkg_array) { g_ptr_array_unref(pkg_array); } return; } /******************************************************************************* * Functions related to PkRepos ******************************************************************************/ void get_pk_repo(const char *repo_id_p, PkRepoDetail **repo_p, char *error_msg, const unsigned error_msg_len) { guint i; gchar *repo_id = NULL; PkRepoDetail *r; GPtrArray *array = NULL; get_pk_repos(&array, error_msg, error_msg_len); if (!array) { lmi_warn(error_msg); goto done; } for (i = 0; i < array->len; i++) { r = g_ptr_array_index(array, i); g_object_get(r, "repo-id", &repo_id, NULL); if (!repo_id) { continue; } if (strcmp(repo_id, repo_id_p) == 0) { *repo_p = g_object_ref(r); break; } g_free(repo_id); repo_id = NULL; } done: if (repo_id) { g_free(repo_id); } if (array) { g_ptr_array_unref(array); } return; } void get_pk_repos(GPtrArray **garray, char *error_msg, const unsigned error_msg_len) { PkTask *task = NULL; PkResults *results = NULL; GError *gerror = NULL; GPtrArray *array = NULL; task = pk_task_new(); results = pk_task_get_repo_list_sync(task, 0, NULL, NULL, NULL, &gerror); if (check_and_create_error_msg(results, gerror, "Getting list of repositories failed", error_msg, error_msg_len)) { goto done; } array = pk_results_get_repo_detail_array(results); g_ptr_array_sort(array, (GCompareFunc) pk_repo_cmp); *garray = g_ptr_array_ref(array); done: g_clear_error(&gerror); if (array) { g_ptr_array_unref(array); } if (results) { g_object_unref(results); } if (task) { g_object_unref(task); } return; } void get_repo_id_from_sw_pkg(const SwPackage *sw_pkg, gchar **repo_id_p, char *error_msg, const unsigned error_msg_len) { PkPackage *pk_pkg = NULL; GPtrArray *repos = NULL, *det_array = NULL; gchar *repo_id = NULL, *pkg_id = NULL, **values = NULL, **pkg_id_parts = NULL; const gchar *repo_id_pkg = NULL, *pkg_id_c = NULL; unsigned i, j; get_pk_repos(&repos, error_msg, error_msg_len); if (!repos) { goto done; } get_pk_pkg_from_sw_pkg(sw_pkg, 0, &pk_pkg); if (!pk_pkg) { goto done; } repo_id_pkg = pk_package_get_data(pk_pkg); if (!repo_id_pkg) { lmi_warn("Invalid PackageKit package."); goto done; } for (i = 0; i < repos->len; i++) { g_object_get(g_ptr_array_index(repos, i), "repo_id", &repo_id, NULL); if (strcmp(repo_id, repo_id_pkg) == 0) { *repo_id_p = repo_id; goto done; } g_free(repo_id); repo_id = NULL; } /* We didn't find the match - package is probably installed.. */ j = 0; values = g_new0(gchar*, repos->len + 1); for (i = 0; i < repos->len; i++) { g_object_get(g_ptr_array_index(repos, i), "repo_id", &repo_id, NULL); pkg_id = pk_package_id_build(pk_package_get_name(pk_pkg), pk_package_get_version(pk_pkg), pk_package_get_arch(pk_pkg), repo_id); values[j++] = pkg_id; g_free(repo_id); repo_id = NULL; } get_pk_det_from_array(values, &det_array, NULL); if (det_array && det_array->len > 0) { for (i = 0; i < det_array->len; i++) { pkg_id_c = pk_details_get_package_id(g_ptr_array_index(det_array, i)); pkg_id_parts = pk_package_id_split(pkg_id_c); if (!pkg_id_parts) { continue; } if (!(*repo_id_p = g_strdup(pkg_id_parts[PK_PACKAGE_ID_DATA]))) { lmi_warn("Memory allocation failed."); } goto done; } } done: g_strfreev(values); g_strfreev(pkg_id_parts); if (pk_pkg) { g_object_unref(pk_pkg); } if (repos) { g_ptr_array_unref(repos); } if (det_array) { g_ptr_array_unref(det_array); } return; } /******************************************************************************* * Functions related to PackageKit ******************************************************************************/ short check_and_create_error_msg(PkResults *results, GError *gerror, const char *custom_msg, char *error_msg, const unsigned error_msg_len) { short ret = 0; PkError *error_code = NULL; if (results) { error_code = pk_results_get_error_code(results); if (error_code) { snprintf(error_msg, error_msg_len, "%s: %s, %s", custom_msg, pk_error_enum_to_string(pk_error_get_code(error_code)), pk_error_get_details(error_code)); g_object_unref(error_code); ret = 1; goto done; } } if (gerror) { snprintf(error_msg, error_msg_len, "%s: %s", custom_msg, gerror->message); ret = 1; goto done; } if (!results) { snprintf(error_msg, error_msg_len, "%s: Nothing returned", custom_msg); ret = 1; goto done; } done: return ret; } gint pk_pkg_cmp(gpointer a, gpointer b) { PkPackage *al = *(PkPackage **) a; PkPackage *bl = *(PkPackage **) b; return (gint) strcmp(pk_package_get_id(al), pk_package_get_id(bl)); } gint pk_det_cmp(gpointer a, gpointer b) { PkDetails *al = *(PkDetails **) a; PkDetails *bl = *(PkDetails **) b; return (gint) strcmp(pk_details_get_package_id(al), pk_details_get_package_id(bl)); } gint pk_repo_cmp(gpointer a, gpointer b) { gint res; PkRepoDetail *al = *(PkRepoDetail **) a; PkRepoDetail *bl = *(PkRepoDetail **) b; gchar *repo_id_a = NULL, *repo_id_b = NULL; g_object_get(al, "repo-id", &repo_id_a, NULL); g_object_get(bl, "repo-id", &repo_id_b, NULL); res = (gint) strcmp(repo_id_a, repo_id_b); g_free(repo_id_a); g_free(repo_id_b); return res; } /******************************************************************************* * Functions related to Glib ******************************************************************************/ void gc_gobject_ptr_array_append(GPtrArray **a, const GPtrArray *b) { guint i; if (!a || !b) { lmi_warn("Received empty parameter."); return; } if (!*a) { *a = g_ptr_array_new_full(b->len, g_object_unref); } for (i = 0; i < b->len; i++) { g_ptr_array_add(*a, g_object_ref(g_ptr_array_index(b, i))); } } /******************************************************************************* * Functions related to CMPI ******************************************************************************/ const char *get_elem_name_from_instance_id(const char *instance_id) { if (!instance_id || !*instance_id) { lmi_warn("Empty InstanceID."); return ""; } if (strlen(instance_id) <= SW_IDENTITY_INSTANCE_ID_PREFIX_LEN) { lmi_warn("Invalid InstanceID: %s", instance_id); return ""; } if (strncasecmp(instance_id, SW_IDENTITY_INSTANCE_ID_PREFIX, SW_IDENTITY_INSTANCE_ID_PREFIX_LEN) != 0) { lmi_warn("Invalid InstanceID: %s", instance_id); return ""; } return instance_id + SW_IDENTITY_INSTANCE_ID_PREFIX_LEN; } void create_instance_id(const char *class_name, const char *id, char *instance_id, const unsigned instance_id_len) { if (id) { snprintf(instance_id, instance_id_len, LMI_ORGID ":%s:%s", class_name, id); } else { snprintf(instance_id, instance_id_len, LMI_ORGID ":%s", class_name); } }