ali@24: /* ali@24: * Copyright (C) 2014 J. Ali Harlow ali@24: * ali@24: * This program is free software; you can redistribute it and/or modify ali@24: * it under the terms of the GNU General Public License as published by ali@24: * the Free Software Foundation; either version 2 of the License, or ali@24: * (at your option) any later version. ali@24: * ali@24: * This program is distributed in the hope that it will be useful, ali@24: * but WITHOUT ANY WARRANTY; without even the implied warranty of ali@24: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ali@24: * GNU General Public License for more details. ali@24: * ali@24: * You should have received a copy of the GNU General Public License along ali@24: * with this program; if not, write to the Free Software Foundation, Inc., ali@24: * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ali@24: */ ali@24: ali@24: #include "config.h" ali@24: #include ali@24: #include ali@24: #include ali@24: #include ali@24: #include ali@24: #include ali@24: ali@24: /* ali@24: * A PloverTransactionHelper uses a GtkAssistant to help a user run a ali@24: * transaction. ali@24: */ ali@24: ali@24: G_DEFINE_TYPE(PloverTransactionHelper,plover_transaction_helper,G_TYPE_OBJECT) ali@24: ali@24: enum { ali@24: CLOSE=0, ali@24: N_SIGNALS ali@24: }; ali@24: ali@24: static guint signals[N_SIGNALS]; ali@24: ali@24: static void plover_transaction_helper_finalize(PloverTransactionHelper *helper) ali@24: { ali@24: g_free(helper->error_primary_text); ali@24: g_free(helper->base); ali@24: g_free(helper->unsatisfied); ali@24: if (helper->comps) ali@24: plover_comps_free(helper->comps); ali@24: plover_vector_free(helper->report_adding); ali@24: plover_vector_free(helper->report_removing); ali@24: } ali@24: ali@24: static void plover_transaction_helper_dispose(PloverTransactionHelper *helper) ali@24: { ali@24: g_clear_error(&helper->error); ali@24: if (helper->error_dialog) ali@24: { ali@24: g_signal_handlers_disconnect_by_data(helper->error_dialog,helper); ali@24: gtk_widget_destroy(helper->error_dialog); ali@24: helper->error_dialog=NULL; ali@24: } ali@24: if (helper->assistant) ali@24: { ali@24: g_signal_handlers_disconnect_by_data(helper->assistant,helper); ali@24: g_clear_object(&helper->assistant); ali@24: } ali@24: g_clear_object(&helper->ui); ali@24: g_slist_foreach(helper->transactions,(GFunc)g_object_unref,NULL); ali@24: g_slist_free(helper->transactions); ali@24: helper->transactions=NULL; ali@24: g_clear_object(&helper->installed); ali@24: g_clear_object(&helper->upstream); ali@24: g_clear_object(&helper->relocated_upstream); ali@24: } ali@24: ali@24: static void ali@24: plover_transaction_helper_class_init(PloverTransactionHelperClass *klass) ali@24: { ali@24: GObjectClass *gobject_class=G_OBJECT_CLASS(klass); ali@24: gobject_class->finalize= ali@24: (void (*)(GObject *))plover_transaction_helper_finalize; ali@24: gobject_class->dispose= ali@24: (void (*)(GObject *))plover_transaction_helper_dispose; ali@24: signals[CLOSE]=g_signal_newv("close", ali@24: G_TYPE_FROM_CLASS(klass),G_SIGNAL_RUN_LAST,NULL,NULL,NULL, ali@24: g_cclosure_marshal_VOID__VOID,G_TYPE_NONE,0,NULL); ali@24: } ali@24: ali@24: static void plover_transaction_helper_init(PloverTransactionHelper *helper) ali@24: { ali@24: helper->report_adding=plover_vector_new(); ali@24: helper->report_removing=plover_vector_new(); ali@24: } ali@24: ali@24: static void plover_transaction_helper_assistant_cancel(GtkAssistant *assistant, ali@24: PloverTransactionHelper *helper) ali@24: { ali@24: gtk_widget_hide(GTK_WIDGET(helper->assistant)); ali@24: gtk_assistant_set_current_page(helper->assistant,0); ali@24: g_signal_emit(helper,signals[CLOSE],0); ali@24: } ali@24: ali@24: static void plover_transaction_helper_assistant_close(GtkAssistant *assistant, ali@24: PloverTransactionHelper *helper) ali@24: { ali@24: gtk_widget_hide(GTK_WIDGET(helper->assistant)); ali@24: gtk_assistant_set_current_page(helper->assistant,0); ali@24: g_signal_emit(helper,signals[CLOSE],0); ali@24: } ali@24: ali@24: static void ali@24: plover_transaction_helper_prepare_confirm(PloverTransactionHelper *helper) ali@24: { ali@24: gchar *package_list,*add,*remove,*s; ali@24: GtkLabel *label; ali@24: struct plover_vector *report; ali@24: if (helper->report_adding->len) ali@24: { ali@24: plover_vector_sort(helper->report_adding); ali@24: if (helper->report_adding_dependencies) ali@24: { ali@24: report=plover_vector_dup(helper->report_adding); ali@24: if (helper->report_adding->len==1) ali@24: plover_vector_append(report,"its dependencies"); ali@24: else ali@24: plover_vector_append(report,"their dependencies"); ali@24: package_list=plover_vector_format_for_display(report); ali@24: plover_vector_free(report); ali@24: } ali@24: else ali@24: package_list= ali@24: plover_vector_format_for_display(helper->report_adding); ali@24: add=g_strconcat("Packages to be installed or updated: ",package_list, ali@24: ".",NULL); ali@24: g_free(package_list); ali@24: } ali@24: else ali@24: add=NULL; ali@24: if (helper->report_removing->len) ali@24: { ali@24: plover_vector_sort(helper->report_removing); ali@24: if (helper->report_removing_dependants) ali@24: { ali@24: report=plover_vector_dup(helper->report_removing); ali@24: if (helper->report_adding->len==1) ali@24: plover_vector_append(report,"its dependants"); ali@24: else ali@24: plover_vector_append(report,"their dependants"); ali@24: package_list=plover_vector_format_for_display(report); ali@24: plover_vector_free(report); ali@24: } ali@24: else ali@24: package_list= ali@24: plover_vector_format_for_display(helper->report_removing); ali@24: remove=g_strconcat("Packages to be removed: ",package_list,".",NULL); ali@24: g_free(package_list); ali@24: } ali@24: else ali@24: remove=NULL; ali@24: label=GTK_LABEL(gtk_builder_get_object(helper->ui,"SISummaryOfWork")); ali@24: if (add && remove) ali@24: s=g_strconcat("Installation Summary\n\n",remove,"\n\n",add,NULL); ali@24: else if (add || remove) ali@24: s=g_strconcat("Installation Summary\n\n",add?add:remove,NULL); ali@24: else ali@24: s=g_strdup("Installation Summary\n\nNo changes scheduled"); ali@24: gtk_label_set_markup(label,s); ali@24: g_free(s); ali@24: g_free(add); ali@24: g_free(remove); ali@24: } ali@24: ali@24: static void plover_transaction_helper_run(PloverTransactionHelper *helper); ali@24: ali@24: static void plover_transaction_helper_callback(GObject *source, ali@24: GAsyncResult *result,gpointer user_data) ali@24: { ali@24: GError *error=NULL; ali@24: PloverTransactionHelper *helper=user_data; ali@24: PloverTransaction *transaction=PLOVER_TRANSACTION(source); ali@24: if (!plover_transaction_commit_finish(transaction,result,&error)) ali@24: { ali@24: plover_transaction_helper_set_error(helper,error, ali@24: "Software installation failed"); ali@24: g_error_free(error); ali@24: } ali@24: else ali@24: plover_transaction_helper_run(helper); ali@24: g_signal_handlers_disconnect_by_data(transaction,helper); ali@24: g_object_unref(transaction); ali@24: } ali@24: ali@24: static void plover_transaction_helper_transaction_status_changed( ali@24: PloverTransaction *transaction,const char *status, ali@24: PloverTransactionHelper *helper) ali@24: { ali@24: GtkProgressBar *bar; ali@24: bar=GTK_PROGRESS_BAR(gtk_builder_get_object(helper->ui,"SIProgressBar")); ali@24: gtk_progress_bar_set_text(bar,status); ali@24: } ali@24: ali@24: static void plover_transaction_helper_run(PloverTransactionHelper *helper) ali@24: { ali@24: PloverTransaction *transaction; ali@24: GtkWidget *page; ali@24: page=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIProgress")); ali@24: if (helper->transactions) ali@24: { ali@24: if (helper->assistant) ali@24: gtk_assistant_set_page_complete(helper->assistant,page,FALSE); ali@24: transaction=helper->transactions->data; ali@24: helper->transactions=g_slist_delete_link(helper->transactions, ali@24: helper->transactions); ali@24: g_signal_connect(transaction,"status-changed", ali@24: G_CALLBACK(plover_transaction_helper_transaction_status_changed), ali@24: helper); ali@24: plover_transaction_commit_async(transaction,NULL, ali@24: plover_transaction_helper_callback,helper); ali@24: } ali@24: else if (helper->assistant) ali@24: gtk_assistant_set_page_complete(helper->assistant,page,TRUE); ali@24: } ali@24: ali@24: static gboolean plover_transaction_helper_pulse(gpointer user_data) ali@24: { ali@24: PloverTransactionHelper *helper=user_data; ali@24: GtkWidget *w; ali@24: GtkProgressBar *bar; ali@24: if (!helper->assistant) ali@24: return FALSE; ali@24: w=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIProgress")); ali@24: bar=GTK_PROGRESS_BAR(gtk_builder_get_object(helper->ui,"SIProgressBar")); ali@24: if (gtk_assistant_get_page_complete(helper->assistant,w)) ali@24: { ali@24: gtk_progress_bar_set_fraction(bar,1.0); ali@24: helper->pulse_handler=0; ali@24: return FALSE; ali@24: } ali@24: else ali@24: { ali@24: gtk_progress_bar_pulse(bar); ali@24: return TRUE; ali@24: } ali@24: } ali@24: ali@24: static void ali@24: plover_transaction_helper_prepare_progress(PloverTransactionHelper *helper) ali@24: { ali@24: GError *error=NULL; ali@24: GtkToggleButton *button; ali@24: PloverTransaction *transaction; ali@24: button=GTK_TOGGLE_BUTTON(gtk_builder_get_object(helper->ui, ali@24: "SIRemoveExisting")); ali@24: if (gtk_toggle_button_get_active(button)) ali@24: { ali@24: transaction=plover_transaction_new_remove(NULL,&error); ali@24: if (!transaction) ali@24: { ali@24: if (g_error_matches(error,PLOVER_POSIX_ERROR,ENOENT)) ali@24: g_clear_error(&error); ali@24: if (error) ali@24: { ali@24: plover_transaction_helper_set_error(helper,error, ali@24: "Software installation failed"); ali@24: g_error_free(error); ali@24: return; ali@24: } ali@24: } ali@24: else ali@24: helper->transactions= ali@24: g_slist_prepend(helper->transactions,transaction); ali@24: } ali@24: /* ali@24: * Note that PloverTransaction does support cancelling a transaction, but ali@24: * there are a number of challenges with using it: ali@24: * - cancellation is only supported during the file phase if razor ali@24: * has atomic rollback, ali@24: * - cancellation is not supported during post-transaction scripts at all ali@24: * (since by the time the first script is started the atomic has already ali@24: * been committed) and these can take quite some time, ali@24: * - where a transaction has an embedded COMMIT, any rollback won't ali@24: * go back beyond this point. ali@24: * To support user-cancel, then, we would need some mechanism to: ali@24: * - Comunicate that the operation is being cancelled and this may take ali@24: * some time, ali@24: * - Not allow cancellation at all after the last post-transaction script ali@24: * phase is started, ali@24: * - Report the partially completed transaction where cancellation ali@24: * occurred after a COMMIT point. ali@24: * At present, this doesn't appear worth the effort. ali@24: */ ali@24: if (helper->assistant) ali@24: gtk_assistant_commit(helper->assistant); ali@24: plover_transaction_helper_run(helper); ali@24: helper->pulse_handler=g_timeout_add(100,plover_transaction_helper_pulse, ali@24: helper); ali@24: } ali@24: ali@24: static void plover_transaction_helper_assistant_prepare(GtkAssistant *assistant, ali@24: GtkWidget *page,PloverTransactionHelper *helper) ali@24: { ali@24: if (page==GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIConfirm"))) ali@24: plover_transaction_helper_prepare_confirm(helper); ali@24: else if (page==GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIProgress"))) ali@24: plover_transaction_helper_prepare_progress(helper); ali@24: } ali@24: ali@24: static void ali@24: plover_transaction_helper_remove_existing_toggled(GtkToggleButton *button, ali@24: PloverTransactionHelper *helper) ali@24: { ali@24: GtkWidget *w; ali@24: if (helper->assistant) ali@24: { ali@24: w=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIConfirm")); ali@24: gtk_assistant_set_page_complete(helper->assistant,w, ali@24: gtk_toggle_button_get_active(button)); ali@24: } ali@24: } ali@24: ali@24: PloverTransactionHelper *plover_transaction_helper_new(GtkBuilder *ui) ali@24: { ali@24: gsize len; ali@24: gchar *s,*directory,*contents; ali@24: GError *error=NULL; ali@24: GtkWidget *w; ali@24: PloverTransactionHelper *helper; ali@24: g_return_val_if_fail(ui == NULL || GTK_IS_BUILDER(ui),NULL); ali@24: helper=PLOVER_TRANSACTION_HELPER( ali@24: g_object_new(PLOVER_TYPE_TRANSACTION_HELPER,NULL)); ali@24: if (ui) ali@24: helper->ui=g_object_ref(ui); ali@24: else ali@24: helper->ui=gtk_builder_new(); ali@24: helper->assistant= ali@24: GTK_ASSISTANT(gtk_builder_get_object(helper->ui,"SoftwareInstallation")); ali@24: if (!helper->assistant) ali@24: { ali@24: directory=g_strdup(g_getenv("PLOVER_DATADIR")); ali@24: if (!directory) ali@24: { ali@24: #ifdef WIN32 ali@24: s=g_win32_get_package_installation_directory_of_module(NULL); ali@24: directory=g_build_filename(s,"share","plover",NULL); ali@24: g_free(s); ali@24: #else ali@24: directory=g_strdup(PLOVER_DATADIR); ali@24: #endif ali@24: } ali@24: s=g_build_filename(directory,"software-installation.ui",NULL); ali@24: g_free(directory); ali@24: (void)g_file_get_contents(s,&contents,&len,&error); ali@24: g_free(s); ali@24: if (!error) ali@24: { ali@24: (void)gtk_builder_add_from_string(helper->ui,contents,len,&error); ali@24: g_free(contents); ali@24: } ali@24: if (error) ali@24: { ali@24: g_critical("software-installation.ui: %s",error->message); ali@24: g_clear_error(&error); ali@24: g_set_error(&error,PLOVER_GENERAL_ERROR,PLOVER_GENERAL_ERROR_FAILED, ali@24: "Internal error (no user interface)"); ali@24: plover_transaction_helper_set_error(helper,error, ali@24: "Can't start installer"); ali@24: return helper; ali@24: } ali@24: helper->assistant=GTK_ASSISTANT(gtk_builder_get_object(helper->ui, ali@24: "SoftwareInstallation")); ali@24: } ali@24: if (!helper->assistant) ali@24: { ali@24: g_critical("\"SoftwareInstallation\" object not found"); ali@24: g_set_error(&error,PLOVER_GENERAL_ERROR,PLOVER_GENERAL_ERROR_FAILED, ali@24: "Internal error (missing wizard)"); ali@24: plover_transaction_helper_set_error(helper,error, ali@24: "Can't start installer"); ali@24: g_error_free(error); ali@24: return helper; ali@24: } ali@24: else ali@24: g_object_ref(helper->assistant); ali@24: if (!GTK_IS_ASSISTANT(helper->assistant)) ali@24: { ali@24: g_critical("\"SoftwareInstallation\" is not a GtkAssistant"); ali@24: g_set_error(&error,PLOVER_GENERAL_ERROR,PLOVER_GENERAL_ERROR_FAILED, ali@24: "Internal error (unexpected wizard type)"); ali@24: plover_transaction_helper_set_error(helper,error, ali@24: "Can't start installer"); ali@24: g_error_free(error); ali@24: return helper; ali@24: } ali@24: g_signal_connect(helper->assistant,"cancel", ali@24: G_CALLBACK(plover_transaction_helper_assistant_cancel),helper); ali@24: g_signal_connect(helper->assistant,"close", ali@24: G_CALLBACK(plover_transaction_helper_assistant_close),helper); ali@24: g_signal_connect(helper->assistant,"prepare", ali@24: G_CALLBACK(plover_transaction_helper_assistant_prepare),helper); ali@24: w=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIRemoveExisting")); ali@24: if (w) ali@24: g_signal_connect(w,"toggled", ali@24: G_CALLBACK(plover_transaction_helper_remove_existing_toggled),helper); ali@24: w=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIIntroduction")); ali@24: if (w) ali@24: gtk_assistant_set_page_complete(helper->assistant,w,TRUE); ali@24: return helper; ali@24: } ali@24: ali@24: PloverPackageSet * ali@24: plover_transaction_helper_get_installed(PloverTransactionHelper *helper) ali@24: { ali@24: g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL); ali@24: return helper->installed; ali@24: } ali@24: ali@24: void plover_transaction_helper_set_installed(PloverTransactionHelper *helper, ali@24: PloverPackageSet *installed) ali@24: { ali@24: g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper)); ali@24: g_return_if_fail(PLOVER_IS_PACKAGE_SET(installed)); ali@24: g_return_if_fail(helper->installed == NULL); ali@24: helper->installed=g_object_ref(installed); ali@24: } ali@24: ali@24: PloverRepository * ali@24: plover_transaction_helper_get_upstream(PloverTransactionHelper *helper, ali@24: GError **error) ali@24: { ali@24: const char *base; ali@24: #if 0 ali@24: const char *prefix; ali@24: struct razor_relocations *relocations=NULL; ali@24: #endif ali@24: g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL); ali@24: if (!helper->upstream) ali@24: { ali@24: #if 0 ali@24: prefix=plover_transaction_helper_get_prefix(helper,error); ali@24: if (!prefix) ali@24: return NULL; ali@24: #endif ali@24: base=plover_transaction_helper_get_base(helper); ali@24: #if 0 ali@24: if (prefix) ali@24: { ali@24: relocations=razor_relocations_create(); ali@24: razor_relocations_add(relocations,"/usr",prefix); ali@24: } ali@24: #endif ali@24: helper->upstream=plover_repository_new_from_yum(base,error); ali@24: #if 0 ali@24: if (relocations) ali@24: razor_relocations_destroy(relocations); ali@24: #endif ali@24: } ali@24: return helper->upstream; ali@24: } ali@24: ali@24: static PloverPackageSet *plover_transaction_helper_get_relocated_upstream( ali@24: PloverTransactionHelper *helper,GError **error) ali@24: { ali@24: const char *prefix; ali@24: struct razor_relocations *relocations=NULL; ali@24: GError *tmp_error=NULL; ali@24: PloverRepository *upstream; ali@24: PloverPackageSet *set; ali@24: if (!helper->relocated_upstream) ali@24: { ali@24: upstream=plover_transaction_helper_get_upstream(helper,error); ali@24: if (!upstream) ali@24: return NULL; ali@24: prefix=plover_transaction_helper_get_prefix(helper,&tmp_error); ali@24: if (tmp_error) ali@24: { ali@24: g_propagate_error(error,tmp_error); ali@24: return NULL; ali@24: } ali@24: set=plover_repository_get_package_set(upstream); ali@24: if (prefix) ali@24: { ali@24: relocations=razor_relocations_create(); ali@24: razor_relocations_add(relocations,"/usr",prefix); ali@24: helper->relocated_upstream= ali@24: plover_package_set_new_from_repository(upstream,relocations, ali@24: error); ali@24: if (relocations) ali@24: razor_relocations_destroy(relocations); ali@24: } ali@24: else ali@24: helper->relocated_upstream=g_object_ref(set); ali@24: } ali@24: return helper->relocated_upstream; ali@24: } ali@24: ali@24: void plover_transaction_helper_set_upstream(PloverTransactionHelper *helper, ali@24: PloverRepository *upstream) ali@24: { ali@24: g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper)); ali@24: g_return_if_fail(PLOVER_IS_REPOSITORY(upstream)); ali@24: g_return_if_fail(helper->upstream == NULL); ali@24: helper->upstream=g_object_ref(upstream); ali@24: } ali@24: ali@24: const char *plover_transaction_helper_get_base(PloverTransactionHelper *helper) ali@24: { ali@24: g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL); ali@24: return helper->base; ali@24: } ali@24: ali@24: void plover_transaction_helper_set_base(PloverTransactionHelper *helper, ali@24: const char *base) ali@24: { ali@24: g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper)); ali@24: g_return_if_fail(helper->transactions == NULL); ali@24: g_free(helper->base); ali@24: helper->base=g_strdup(base); ali@24: } ali@24: ali@24: struct comps * ali@24: plover_transaction_helper_get_comps(PloverTransactionHelper *helper, ali@24: GError **error) ali@24: { ali@24: gchar *s; ali@24: g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL); ali@24: g_return_val_if_fail(helper->base != NULL,NULL); ali@24: if (!helper->comps) ali@24: { ali@24: s=g_strconcat(helper->base,"/repodata/comps.xml",NULL); ali@24: helper->comps=plover_comps_new_from_file(s); ali@24: if (!helper->comps) ali@24: g_set_error(error,PLOVER_GENERAL_ERROR, ali@24: PLOVER_GENERAL_ERROR_FAILED,"%s: %s",s,g_strerror(errno)); ali@24: g_free(s); ali@24: } ali@24: return helper->comps; ali@24: } ali@24: ali@24: const char * ali@24: plover_transaction_helper_get_prefix(PloverTransactionHelper *helper, ali@24: GError **error) ali@24: { ali@24: const char *prefix; ali@24: struct comps *comps; ali@24: g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL); ali@24: g_return_val_if_fail(helper->base != NULL || helper->installed != NULL,NULL); ali@24: if (helper->base) ali@24: { ali@24: comps=plover_transaction_helper_get_comps(helper,error); ali@24: if (!comps) ali@24: return NULL; ali@24: return plover_default_prefix_for_vendor(comps->vendor); ali@24: } ali@24: prefix=plover_package_set_guess_prefix(helper->installed,error); ali@24: return prefix; ali@24: } ali@24: ali@24: static int plover_transaction_helper_package_count(void) ali@24: { ali@24: int count=0; ali@24: char *install_root; ali@24: struct razor_set *set; ali@24: struct razor_package *package; ali@24: struct razor_package_iterator *pi; ali@24: install_root=getenv("RAZOR_ROOT"); ali@24: if (!install_root) ali@24: install_root=""; ali@24: set=razor_root_open_read_only(install_root,NULL); ali@24: if (set) ali@24: { ali@24: pi=razor_package_iterator_create(set); ali@24: while (razor_package_iterator_next(pi,&package,RAZOR_DETAIL_LAST)) ali@24: count++; ali@24: razor_package_iterator_destroy(pi); ali@24: razor_set_unref(set); ali@24: } ali@24: return count; ali@24: } ali@24: ali@24: static gboolean ali@24: plover_transaction_helper_check_vendor(PloverTransactionHelper *helper, ali@24: GError **error) ali@24: { ali@24: int i; ali@24: gchar *prefix=NULL,*s; ali@24: struct comps *comps=NULL; ali@24: GtkWidget *container,*page; ali@24: GtkButton *button; ali@24: GtkLabel *label; ali@24: g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE); ali@24: if (helper->check_vendor) ali@24: { ali@24: comps=plover_transaction_helper_get_comps(helper,error); ali@24: if (!comps) ali@24: return FALSE; ali@24: prefix=plover_default_prefix_for_vendor(comps->vendor); ali@24: } ali@24: button=GTK_BUTTON(gtk_builder_get_object(helper->ui,"SIRemoveExisting")); ali@24: gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),FALSE); ali@24: container=GTK_WIDGET(gtk_builder_get_object(helper->ui, ali@24: "SIIncompatibleInstallation")); ali@24: page=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIConfirm")); ali@24: if (helper->check_vendor && prefix && ali@24: !plover_installed_files_match_prefix(prefix)) ali@24: { ali@24: label=GTK_LABEL(gtk_builder_get_object(helper->ui, ali@24: "SIIncompatibleInstallationLabel")); ali@24: s=g_strdup_printf("Incompatible Installation\n\n" ali@24: "The existing installation is not from %s.\n" ali@24: "In order to continue, all the existing packages must be removed.", ali@24: comps->vendor); ali@24: gtk_label_set_markup(label,s); ali@24: g_free(s); ali@24: i=plover_transaction_helper_package_count(); ali@24: s=g_strdup_printf("Remove %d existing package%s",i,i==1?"":"s"); ali@24: gtk_button_set_label(button,s); ali@24: g_free(s); ali@24: gtk_widget_show(container); ali@24: if (helper->assistant) ali@24: gtk_assistant_set_page_complete(helper->assistant,page,FALSE); ali@24: } ali@24: else ali@24: { ali@24: gtk_widget_hide(container); ali@24: if (helper->assistant) ali@24: gtk_assistant_set_page_complete(helper->assistant,page,TRUE); ali@24: } ali@24: return TRUE; ali@24: } ali@24: ali@24: void plover_transaction_helper_set_check_vendor(PloverTransactionHelper *helper, ali@24: gboolean check_vendor) ali@24: { ali@24: g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper)); ali@24: if (helper->check_vendor!=check_vendor) ali@24: { ali@24: helper->check_vendor=check_vendor; ali@24: if (helper->transactions) ali@24: plover_transaction_helper_check_vendor(helper,NULL); ali@24: } ali@24: } ali@24: ali@24: /* ali@24: * If plover_transaction_helper_add_transaction() failes with an error ali@24: * of PLOVER_GENERAL_ERROR,PLOVER_GENERAL_ERROR_REQUIREMENTS_NOT_MET ali@24: * then plover_transaction_helper_get_unsatisfied() can be used to ali@24: * retrieve a textual description of the problem. ali@24: */ ali@24: ali@24: const char * ali@24: plover_transaction_helper_get_unsatisfied(PloverTransactionHelper *helper) ali@24: { ali@24: g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL); ali@24: return helper->unsatisfied; ali@24: } ali@24: ali@24: gboolean ali@24: plover_transaction_helper_add_transaction(PloverTransactionHelper *helper, ali@24: PloverTransaction *transaction,struct plover_vector *report_packages, ali@24: enum razor_install_action report_action,GError **error) ali@24: { ali@24: int i,count; ali@24: gboolean other_packages; ali@24: const char *s,*name; ali@24: enum razor_install_action action; ali@24: struct razor_install_iterator *ii; ali@24: struct razor_set *next; ali@24: struct razor_package *package; ali@24: struct plover_vector *tasked_packages; ali@24: g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE); ali@24: g_return_val_if_fail(PLOVER_IS_TRANSACTION(transaction),FALSE); ali@24: g_return_val_if_fail(report_action==RAZOR_INSTALL_ACTION_ADD || report_action==RAZOR_INSTALL_ACTION_REMOVE,FALSE); ali@24: g_free(helper->unsatisfied); ali@24: helper->unsatisfied=NULL; ali@24: if (!plover_transaction_resolve(transaction,error)) ali@24: { ali@24: s=plover_transaction_get_unsatisfied(transaction); ali@24: helper->unsatisfied=g_strdup(s); ali@24: return FALSE; ali@24: } ali@24: ii=plover_transaction_get_install_iterator(transaction,error); ali@24: if (!ii) ali@24: return FALSE; ali@24: next=plover_transaction_get_next_set(transaction,error); ali@24: if (!next) ali@24: return FALSE; ali@24: tasked_packages=plover_vector_new(); ali@24: other_packages=FALSE; ali@24: while (razor_install_iterator_next(ii,&package,&action,&count)) ali@24: { ali@24: if (action==report_action) ali@24: { ali@24: razor_package_get_details(next,package,RAZOR_DETAIL_NAME,&name, ali@24: RAZOR_DETAIL_LAST); ali@24: if (!report_packages || ali@24: plover_vector_contains(report_packages,name)) ali@24: plover_vector_append(tasked_packages,name); ali@24: else ali@24: other_packages=TRUE; ali@24: } ali@24: } ali@24: if (!tasked_packages->len) ali@24: { ali@24: /* ali@24: * If there are no reportable packages tasked for action there ali@24: * shouldn't by any packages at all, but let's be paranoid. ali@24: */ ali@24: other_packages=FALSE; ali@24: razor_install_iterator_rewind(ii); ali@24: while (razor_install_iterator_next(ii,&package,&action,&count)) ali@24: { ali@24: if (action==report_action) ali@24: { ali@24: razor_package_get_details(next,package,RAZOR_DETAIL_NAME,&name, ali@24: RAZOR_DETAIL_LAST); ali@24: plover_vector_append(tasked_packages,name); ali@24: } ali@24: } ali@24: } ali@24: if (!tasked_packages->len) ali@24: { ali@24: g_set_error(error,PLOVER_GENERAL_ERROR, ali@24: PLOVER_GENERAL_ERROR_NO_WORK,"Transaction includes no %s actions", ali@24: report_action==RAZOR_INSTALL_ACTION_ADD?"add":"remove"); ali@24: plover_vector_free(tasked_packages); ali@24: return FALSE; ali@24: } ali@24: if (!helper->transactions) ali@24: plover_transaction_helper_check_vendor(helper,error); ali@24: g_object_ref(transaction); ali@24: helper->transactions=g_slist_append(helper->transactions,transaction); ali@24: if (report_action==RAZOR_INSTALL_ACTION_ADD) ali@24: { ali@24: for(i=0;ilen;i++) ali@24: { ali@24: s=tasked_packages->strings[i]; ali@24: if (!plover_vector_contains(helper->report_adding,s)) ali@24: plover_vector_append(helper->report_adding,s); ali@24: } ali@24: helper->report_adding_dependencies|=other_packages; ali@24: } ali@24: else ali@24: { ali@24: for(i=0;ilen;i++) ali@24: { ali@24: s=tasked_packages->strings[i]; ali@24: if (!plover_vector_contains(helper->report_removing,s)) ali@24: plover_vector_append(helper->report_removing,s); ali@24: } ali@24: helper->report_removing_dependants|=other_packages; ali@24: } ali@24: plover_vector_free(tasked_packages); ali@24: return TRUE; ali@24: } ali@24: ali@24: static PloverTransaction * ali@24: plover_transaction_helper_new_transaction(PloverTransactionHelper *helper, ali@24: GError **error) ali@24: { ali@24: gboolean ok; ali@24: const char *base,*prefix; ali@24: GError *tmp_error=NULL; ali@24: PloverTransaction *transaction; ali@24: g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL); ali@24: prefix=plover_transaction_helper_get_prefix(helper,&tmp_error); ali@24: if (tmp_error) ali@24: { ali@24: g_propagate_error(error,tmp_error); ali@24: return NULL; ali@24: } ali@24: transaction=plover_transaction_new(); ali@24: plover_transaction_set_prefix(transaction,prefix); ali@24: plover_transaction_set_installed(transaction,helper->installed); ali@24: if (helper->upstream) ali@24: ok=plover_transaction_set_upstream(transaction,helper->upstream,error); ali@24: else ali@24: { ali@24: base=plover_transaction_helper_get_base(helper); ali@24: ok=plover_transaction_set_upstream_from_yum(transaction,base,error); ali@24: } ali@24: if (!ok) ali@24: { ali@24: g_object_unref(transaction); ali@24: transaction=NULL; ali@24: } ali@24: return transaction; ali@24: } ali@24: ali@24: struct plover_vector *plover_transaction_helper_group_get_default_packages( ali@24: PloverTransactionHelper *helper,const char *group,GError **error) ali@24: { ali@24: gboolean changed; ali@24: struct comps *comps; ali@24: struct comps_group *grp; ali@24: struct comps_requirement *pkg; ali@24: struct plover_vector *default_packages; ali@24: g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE); ali@24: comps=plover_transaction_helper_get_comps(helper,error); ali@24: if (!comps) ali@24: return NULL; ali@24: grp=plover_comps_lookup_group(comps,group); ali@24: if (!grp) ali@24: { ali@24: g_set_error(error,PLOVER_GENERAL_ERROR, ali@24: PLOVER_GENERAL_ERROR_FAILED,"%s: group not found",group); ali@24: return NULL; ali@24: } ali@24: default_packages=plover_vector_new(); ali@24: do ali@24: { ali@24: changed=FALSE; ali@24: for(pkg=grp->packages;pkg;pkg=pkg->next) ali@24: { ali@24: if (plover_vector_contains(default_packages,pkg->name)) ali@24: continue; ali@24: if (pkg->type==COMPS_REQUIREMENT_DEFAULT || ali@24: pkg->type==COMPS_REQUIREMENT_MANDATORY || ali@24: pkg->type==COMPS_REQUIREMENT_CONDITIONAL && ali@24: plover_vector_contains(default_packages,pkg->requires)) ali@24: { ali@24: changed=TRUE; ali@24: plover_vector_append(default_packages,pkg->name); ali@24: } ali@24: } ali@24: } while(changed); ali@24: return default_packages; ali@24: } ali@24: ali@24: /* ali@24: * Returns TRUE if there is work to be done or FALSE if the packages are ali@24: * already installed or on error. ali@24: */ ali@24: gboolean ali@24: plover_transaction_helper_install_packages(PloverTransactionHelper *helper, ali@24: struct plover_vector *packages,GError **error) ali@24: { ali@24: gboolean retval; ali@24: PloverTransaction *transaction; ali@24: g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE); ali@24: g_return_val_if_fail(packages != NULL,FALSE); ali@24: if (!packages->len) ali@24: { ali@24: g_set_error(error,PLOVER_GENERAL_ERROR, ali@24: PLOVER_GENERAL_ERROR_NO_WORK,"No packages listed to be installed"); ali@24: return FALSE; ali@24: } ali@24: transaction=plover_transaction_helper_new_transaction(helper,error); ali@24: if (!transaction) ali@24: return FALSE; ali@24: if (!plover_transaction_install(transaction,packages->strings,error)) ali@24: { ali@24: g_object_unref(transaction); ali@24: return FALSE; ali@24: } ali@24: retval=plover_transaction_helper_add_transaction(helper,transaction, ali@24: packages,RAZOR_INSTALL_ACTION_ADD,error); ali@24: g_object_unref(transaction); ali@24: return retval; ali@24: } ali@24: ali@24: /* ali@24: * Returns TRUE if there is work to be done or FALSE if the group is ali@24: * already installed or on error. ali@24: */ ali@24: gboolean ali@24: plover_transaction_helper_install_group(PloverTransactionHelper *helper, ali@24: const char *group,GError **error) ali@24: { ali@24: gboolean retval; ali@24: struct plover_vector *selected_packages; ali@24: g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE); ali@24: selected_packages=plover_transaction_helper_group_get_default_packages( ali@24: helper,group,error); ali@24: if (!selected_packages) ali@24: return FALSE; ali@24: if (!selected_packages->len) ali@24: { ali@24: g_set_error(error,PLOVER_GENERAL_ERROR, ali@24: PLOVER_GENERAL_ERROR_FAILED,"%s: no default packages",group); ali@24: plover_vector_free(selected_packages); ali@24: return FALSE; ali@24: } ali@24: retval=plover_transaction_helper_install_packages(helper,selected_packages, ali@24: error); ali@24: plover_vector_free(selected_packages); ali@24: return retval; ali@24: } ali@24: ali@24: /* ali@24: * Returns TRUE if there is work to be done or FALSE if the group is ali@24: * not installed or on error. ali@24: */ ali@24: gboolean plover_transaction_helper_remove_group(PloverTransactionHelper *helper, ali@24: const char *group,GError **error) ali@24: { ali@24: gboolean retval; ali@24: struct plover_vector *selected_packages; ali@24: PloverTransaction *transaction; ali@24: g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE); ali@24: selected_packages=plover_transaction_helper_group_get_default_packages( ali@24: helper,group,error); ali@24: if (!selected_packages) ali@24: return FALSE; ali@24: if (!selected_packages->len) ali@24: { ali@24: g_set_error(error,PLOVER_GENERAL_ERROR, ali@24: PLOVER_GENERAL_ERROR_FAILED,"%s: no default packages",group); ali@24: plover_vector_free(selected_packages); ali@24: return FALSE; ali@24: } ali@24: transaction=plover_transaction_new(); ali@24: plover_transaction_set_installed(transaction,helper->installed); ali@24: if (!plover_transaction_remove(transaction,selected_packages->strings, ali@24: error)) ali@24: { ali@24: plover_vector_free(selected_packages); ali@24: g_object_unref(transaction); ali@24: return FALSE; ali@24: } ali@24: retval=plover_transaction_helper_add_transaction(helper,transaction, ali@24: NULL,RAZOR_INSTALL_ACTION_REMOVE,error); ali@24: g_object_unref(transaction); ali@24: plover_vector_free(selected_packages); ali@24: return retval; ali@24: } ali@24: ali@24: /* ali@24: * Returns TRUE if there is work to be done or FALSE if all updates have ali@24: * already been applied or on error. ali@24: */ ali@24: gboolean plover_transaction_helper_update(PloverTransactionHelper *helper, ali@24: GError **error) ali@24: { ali@24: gboolean retval; ali@24: PloverTransaction *transaction; ali@24: g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE); ali@24: transaction=plover_transaction_helper_new_transaction(helper,error); ali@24: if (!transaction) ali@24: return FALSE; ali@24: if (!plover_transaction_update(transaction,NULL,error)) ali@24: { ali@24: g_object_unref(transaction); ali@24: return FALSE; ali@24: } ali@24: retval=plover_transaction_helper_add_transaction(helper,transaction, ali@24: NULL,RAZOR_INSTALL_ACTION_ADD,error); ali@24: g_object_unref(transaction); ali@24: return retval; ali@24: } ali@24: ali@24: gboolean plover_transaction_helper_get_visible(PloverTransactionHelper *helper) ali@24: { ali@24: g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE); ali@24: if (helper->error_dialog) ali@24: return TRUE; ali@24: else if (!helper->assistant) ali@24: return FALSE; ali@24: else ali@24: return gtk_widget_get_visible(GTK_WIDGET(helper->assistant)); ali@24: } ali@24: ali@24: void plover_transaction_helper_present(PloverTransactionHelper *helper) ali@24: { ali@24: g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper)); ali@24: if (helper->error_dialog) ali@24: gtk_window_present(GTK_WINDOW(helper->error_dialog)); ali@24: else if (helper->assistant) ali@24: gtk_window_present(GTK_WINDOW(helper->assistant)); ali@24: } ali@24: ali@24: static void ali@24: plover_transaction_helper_error_dialog_response(GtkDialog *error_dialog, ali@24: int response_id,PloverTransactionHelper *helper) ali@24: { ali@24: g_signal_handlers_disconnect_by_data(error_dialog,helper); ali@24: if ((GtkWidget *)error_dialog==helper->error_dialog) ali@24: { ali@24: gtk_widget_destroy(helper->error_dialog); ali@24: helper->error_dialog=NULL; ali@24: if (helper->assistant) ali@24: { ali@24: gtk_widget_hide(GTK_WIDGET(helper->assistant)); ali@24: gtk_assistant_set_current_page(helper->assistant,0); ali@24: } ali@24: g_signal_emit(helper,signals[CLOSE],0); ali@24: } ali@24: } ali@24: ali@24: const char *plover_transaction_helper_get_error(PloverTransactionHelper *helper, ali@24: const GError **error) ali@24: { ali@24: g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper)); ali@24: if (!helper->error_dialog) ali@24: return NULL; ali@24: if (error) ali@24: *error=helper->error; ali@24: return helper->error_primary_text; ali@24: } ali@24: ali@24: void plover_transaction_helper_set_error(PloverTransactionHelper *helper, ali@24: const GError *error,const char *primary_text) ali@24: { ali@24: GtkMessageType type; ali@24: GtkWindow *window; ali@24: g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper)); ali@24: g_return_if_fail(error != NULL); ali@24: g_return_if_fail(primary_text != NULL); ali@24: if (helper->pulse_handler) ali@24: { ali@24: g_source_remove(helper->pulse_handler); ali@24: helper->pulse_handler=0; ali@24: } ali@24: if (helper->error_dialog) ali@24: { ali@24: gtk_widget_destroy(helper->error_dialog); ali@24: helper->error_dialog=NULL; ali@24: } ali@24: g_free(helper->error_primary_text); ali@24: helper->error_primary_text=g_strdup(primary_text); ali@24: g_clear_error(&helper->error); ali@24: helper->error=g_error_copy(error); ali@24: if (g_error_matches(error,PLOVER_GENERAL_ERROR, ali@24: PLOVER_GENERAL_ERROR_NO_WORK)) ali@24: type=GTK_MESSAGE_INFO; ali@24: else ali@24: type=GTK_MESSAGE_ERROR; ali@24: if (helper->assistant) ali@24: window=GTK_WINDOW(helper->assistant); ali@24: else ali@24: window=NULL; ali@24: helper->error_dialog=gtk_message_dialog_new(window, ali@24: GTK_DIALOG_DESTROY_WITH_PARENT,type,GTK_BUTTONS_CLOSE,primary_text); ali@24: gtk_message_dialog_format_secondary_text( ali@24: GTK_MESSAGE_DIALOG(helper->error_dialog),error->message); ali@24: gtk_widget_show(helper->error_dialog); ali@24: g_signal_connect(helper->error_dialog,"response", ali@24: G_CALLBACK(plover_transaction_helper_error_dialog_response),helper); ali@24: }