/* * Copyright (C) 2014, 2016, 2018 J. Ali Harlow * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" #include #include #include #include #include #include #include #include "plover/uri-handler.h" /* * A PloverTransactionHelper uses a GtkAssistant to help a user run a * transaction. */ G_DEFINE_TYPE(PloverTransactionHelper,plover_transaction_helper,G_TYPE_OBJECT) enum plover_transaction_type { TRANSACTION_TYPE_NULL=0, TRANSACTION_TYPE_INSTALL=1UL<<0, TRANSACTION_TYPE_REMOVE=1UL<<1, TRANSACTION_TYPE_UPDATE=TRANSACTION_TYPE_INSTALL|TRANSACTION_TYPE_REMOVE }; typedef struct _PloverTransactionHelperPrivate { enum plover_transaction_type transaction_type; gchar *default_prefix; } PloverTransactionHelperPrivate; #define PLOVER_TRANSACTION_HELPER_GET_PRIVATE(obj)\ G_TYPE_INSTANCE_GET_PRIVATE(obj,\ PLOVER_TYPE_TRANSACTION_HELPER,\ PloverTransactionHelperPrivate) enum { CLOSE=0, N_SIGNALS }; static guint signals[N_SIGNALS]; static void plover_transaction_helper_finalize(PloverTransactionHelper *helper) { PloverTransactionHelperPrivate *priv; priv=PLOVER_TRANSACTION_HELPER_GET_PRIVATE(helper); g_free(priv->default_prefix); g_free(helper->error_primary_text); g_free(helper->base); g_free(helper->unsatisfied); if (helper->comps) plover_comps_free(helper->comps); plover_vector_free(helper->report_adding); plover_vector_free(helper->report_removing); } static void plover_transaction_helper_dispose(PloverTransactionHelper *helper) { g_clear_error(&helper->error); if (helper->error_dialog) { g_signal_handlers_disconnect_by_data(helper->error_dialog,helper); gtk_widget_destroy(helper->error_dialog); helper->error_dialog=NULL; } if (helper->assistant) { g_signal_handlers_disconnect_by_data(helper->assistant,helper); g_clear_object(&helper->assistant); } g_clear_object(&helper->ui); g_slist_foreach(helper->transactions,(GFunc)g_object_unref,NULL); g_slist_free(helper->transactions); helper->transactions=NULL; g_clear_object(&helper->alternate_installed); g_clear_object(&helper->installed); g_clear_object(&helper->upstream); g_clear_object(&helper->relocated_upstream); } static void plover_transaction_helper_class_init(PloverTransactionHelperClass *klass) { GObjectClass *gobject_class=G_OBJECT_CLASS(klass); gobject_class->finalize= (void (*)(GObject *))plover_transaction_helper_finalize; gobject_class->dispose= (void (*)(GObject *))plover_transaction_helper_dispose; g_type_class_add_private(klass,sizeof(PloverTransactionHelperPrivate)); signals[CLOSE]=g_signal_newv("close", G_TYPE_FROM_CLASS(klass),G_SIGNAL_RUN_LAST,NULL,NULL,NULL, g_cclosure_marshal_VOID__VOID,G_TYPE_NONE,0,NULL); } static void plover_transaction_helper_init(PloverTransactionHelper *helper) { helper->report_adding=plover_vector_new(); helper->report_removing=plover_vector_new(); } static void plover_transaction_helper_assistant_cancel(GtkAssistant *assistant, PloverTransactionHelper *helper) { gtk_widget_hide(GTK_WIDGET(helper->assistant)); gtk_assistant_set_current_page(helper->assistant,0); g_signal_emit(helper,signals[CLOSE],0); } static void plover_transaction_helper_assistant_close(GtkAssistant *assistant, PloverTransactionHelper *helper) { gtk_widget_hide(GTK_WIDGET(helper->assistant)); gtk_assistant_set_current_page(helper->assistant,0); g_signal_emit(helper,signals[CLOSE],0); } static void plover_transaction_helper_prepare_confirm(PloverTransactionHelper *helper) { gchar *package_list,*add,*remove,*s; GtkLabel *label; struct plover_vector *report; if (helper->report_adding->len) { plover_vector_sort(helper->report_adding); if (helper->report_adding_dependencies) { report=plover_vector_dup(helper->report_adding); if (helper->report_adding->len==1) plover_vector_append(report,"its dependencies"); else plover_vector_append(report,"their dependencies"); package_list=plover_vector_format_for_display(report); plover_vector_free(report); } else package_list= plover_vector_format_for_display(helper->report_adding); add=g_strconcat("Packages to be installed or updated: ",package_list, ".",NULL); g_free(package_list); } else add=NULL; if (helper->report_removing->len) { plover_vector_sort(helper->report_removing); if (helper->report_removing_dependants) { report=plover_vector_dup(helper->report_removing); if (helper->report_adding->len==1) plover_vector_append(report,"its dependants"); else plover_vector_append(report,"their dependants"); package_list=plover_vector_format_for_display(report); plover_vector_free(report); } else package_list= plover_vector_format_for_display(helper->report_removing); remove=g_strconcat("Packages to be removed: ",package_list,".",NULL); g_free(package_list); } else remove=NULL; label=GTK_LABEL(gtk_builder_get_object(helper->ui,"SISummaryOfWork")); if (add && remove) s=g_strconcat("Installation Summary\n\n",remove,"\n\n",add,NULL); else if (add || remove) s=g_strconcat("Installation Summary\n\n",add?add:remove,NULL); else s=g_strdup("Installation Summary\n\nNo changes scheduled"); gtk_label_set_markup(label,s); g_free(s); g_free(add); g_free(remove); } static void plover_transaction_helper_run(PloverTransactionHelper *helper); static void plover_transaction_helper_callback(GObject *source, GAsyncResult *result,gpointer user_data) { GError *error=NULL; PloverTransactionHelper *helper=user_data; PloverTransaction *transaction=PLOVER_TRANSACTION(source); if (!plover_transaction_commit_finish(transaction,result,&error)) { plover_transaction_helper_set_error(helper,error, "Software installation failed"); g_error_free(error); } else plover_transaction_helper_run(helper); /* * There may be status updates queued by transaction as idle events. * Process them now before we disconnect so that we don't lose them. */ while(g_main_context_pending(NULL)) g_main_context_iteration(NULL,FALSE); g_signal_handlers_disconnect_by_data(transaction,helper); g_object_unref(transaction); } static void plover_transaction_helper_transaction_status_changed( PloverTransaction *transaction,const char *status, PloverTransactionHelper *helper) { GtkProgressBar *bar; bar=GTK_PROGRESS_BAR(gtk_builder_get_object(helper->ui,"SIProgressBar")); gtk_progress_bar_set_text(bar,status); } static void plover_transaction_helper_run(PloverTransactionHelper *helper) { PloverTransaction *transaction; GtkWidget *page; page=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIProgress")); if (helper->transactions) { if (helper->assistant) gtk_assistant_set_page_complete(helper->assistant,page,FALSE); transaction=helper->transactions->data; helper->transactions=g_slist_delete_link(helper->transactions, helper->transactions); g_signal_connect(transaction,"status-changed", G_CALLBACK(plover_transaction_helper_transaction_status_changed), helper); plover_transaction_commit_async(transaction,NULL, plover_transaction_helper_callback,helper); } else if (helper->assistant) gtk_assistant_set_page_complete(helper->assistant,page,TRUE); } static gboolean plover_transaction_helper_pulse(gpointer user_data) { PloverTransactionHelper *helper=user_data; GtkWidget *w; GtkProgressBar *bar; if (!helper->assistant) return FALSE; w=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIProgress")); bar=GTK_PROGRESS_BAR(gtk_builder_get_object(helper->ui,"SIProgressBar")); if (gtk_assistant_get_page_complete(helper->assistant,w)) { gtk_progress_bar_set_fraction(bar,1.0); helper->pulse_handler=0; return FALSE; } else { gtk_progress_bar_pulse(bar); return TRUE; } } static void plover_transaction_helper_prepare_progress(PloverTransactionHelper *helper) { GError *error=NULL; GtkToggleButton *button; PloverTransaction *transaction; GSList *save_transactions; PloverTransactionHelperPrivate *priv; enum plover_transaction_type save_transaction_type; priv=PLOVER_TRANSACTION_HELPER_GET_PRIVATE(helper); button=GTK_TOGGLE_BUTTON(gtk_builder_get_object(helper->ui, "SIRemoveExisting")); if (gtk_toggle_button_get_active(button)) { transaction=plover_transaction_new_remove(NULL,&error); if (transaction) { save_transactions=helper->transactions; helper->transactions=NULL; save_transaction_type=priv->transaction_type; priv->transaction_type=0; if (!plover_transaction_helper_add_transaction(helper,transaction, NULL,PLOVER_TRANSACTION_HELPER_REPORT_REMOVE,&error)) { g_object_unref(transaction); transaction=NULL; helper->transactions=save_transactions; priv->transaction_type=save_transaction_type; } else { g_slist_foreach(save_transactions,(GFunc)g_object_unref,NULL); g_slist_free(save_transactions); } } if (!transaction) { if (g_error_matches(error,PLOVER_POSIX_ERROR,ENOENT)) g_clear_error(&error); if (error) { plover_transaction_helper_set_error(helper,error, "Failed to remove existing packages"); g_error_free(error); return; } } } /* * Note that PloverTransaction does support cancelling a transaction, but * there are a number of challenges with using it: * - cancellation is only supported during the file phase if razor * has atomic rollback, * - cancellation is not supported during post-transaction scripts at all * (since by the time the first script is started the atomic has already * been committed) and these can take quite some time, * - where a transaction has an embedded COMMIT, any rollback won't * go back beyond this point. * To support user-cancel, then, we would need some mechanism to: * - Comunicate that the operation is being cancelled and this may take * some time, * - Not allow cancellation at all after the last post-transaction script * phase is started, * - Report the partially completed transaction where cancellation * occurred after a COMMIT point. * At present, this doesn't appear worth the effort. */ if (helper->assistant) gtk_assistant_commit(helper->assistant); plover_transaction_helper_run(helper); helper->pulse_handler=g_timeout_add(100,plover_transaction_helper_pulse, helper); } static void plover_transaction_helper_assistant_prepare(GtkAssistant *assistant, GtkWidget *page,PloverTransactionHelper *helper) { if (page==GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIConfirm"))) plover_transaction_helper_prepare_confirm(helper); else if (page==GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIProgress"))) plover_transaction_helper_prepare_progress(helper); } static void plover_transaction_helper_remove_existing_toggled(GtkToggleButton *button, PloverTransactionHelper *helper) { GtkWidget *w; if (helper->assistant) { w=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIConfirm")); gtk_assistant_set_page_complete(helper->assistant,w, gtk_toggle_button_get_active(button)); } } PloverTransactionHelper *plover_transaction_helper_new(GtkBuilder *ui) { gsize len; gchar *s,*directory,*contents; GError *error=NULL; GtkWidget *w; PloverTransactionHelper *helper; g_return_val_if_fail(ui == NULL || GTK_IS_BUILDER(ui),NULL); helper=PLOVER_TRANSACTION_HELPER( g_object_new(PLOVER_TYPE_TRANSACTION_HELPER,NULL)); if (ui) helper->ui=g_object_ref(ui); else helper->ui=gtk_builder_new(); helper->assistant= GTK_ASSISTANT(gtk_builder_get_object(helper->ui,"SoftwareInstallation")); if (!helper->assistant) { directory=g_strdup(g_getenv("PLOVER_DATADIR")); if (!directory) { #ifdef WIN32 s=g_win32_get_package_installation_directory_of_module(NULL); directory=g_build_filename(s,"share","plover",NULL); g_free(s); #else directory=g_strdup(PLOVER_DATADIR); #endif } s=g_build_filename(directory,"software-installation.ui",NULL); g_free(directory); (void)g_file_get_contents(s,&contents,&len,&error); g_free(s); if (!error) { (void)gtk_builder_add_from_string(helper->ui,contents,len,&error); g_free(contents); } if (error) { g_critical("software-installation.ui: %s",error->message); g_clear_error(&error); g_set_error(&error,PLOVER_GENERAL_ERROR,PLOVER_GENERAL_ERROR_FAILED, "Internal error (no user interface)"); plover_transaction_helper_set_error(helper,error, "Can't start installer"); return helper; } helper->assistant=GTK_ASSISTANT(gtk_builder_get_object(helper->ui, "SoftwareInstallation")); } if (!helper->assistant) { g_critical("\"SoftwareInstallation\" object not found"); g_set_error(&error,PLOVER_GENERAL_ERROR,PLOVER_GENERAL_ERROR_FAILED, "Internal error (missing wizard)"); plover_transaction_helper_set_error(helper,error, "Can't start installer"); g_error_free(error); return helper; } else g_object_ref(helper->assistant); if (!GTK_IS_ASSISTANT(helper->assistant)) { g_critical("\"SoftwareInstallation\" is not a GtkAssistant"); g_set_error(&error,PLOVER_GENERAL_ERROR,PLOVER_GENERAL_ERROR_FAILED, "Internal error (unexpected wizard type)"); plover_transaction_helper_set_error(helper,error, "Can't start installer"); g_error_free(error); return helper; } g_signal_connect(helper->assistant,"cancel", G_CALLBACK(plover_transaction_helper_assistant_cancel),helper); g_signal_connect(helper->assistant,"close", G_CALLBACK(plover_transaction_helper_assistant_close),helper); g_signal_connect(helper->assistant,"prepare", G_CALLBACK(plover_transaction_helper_assistant_prepare),helper); w=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIRemoveExisting")); if (w) g_signal_connect(w,"toggled", G_CALLBACK(plover_transaction_helper_remove_existing_toggled),helper); w=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIIntroduction")); if (w) gtk_assistant_set_page_complete(helper->assistant,w,TRUE); return helper; } PloverPackageSet * plover_transaction_helper_get_installed(PloverTransactionHelper *helper) { gchar *s,*saved_database_uri; char *install_root,*local_database,*active_database,*alternate_database; const char *prefix; struct comps *comps; PloverPackageSet *alternate_installed,*installed; GError *error=NULL; struct razor_error *razor_error=NULL; g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL); if (!helper->installed) { comps=plover_transaction_helper_get_comps(helper,NULL); if (!comps) { g_warning("plover_transaction_helper_get_installed: No comps"); return NULL; } install_root=getenv("RAZOR_ROOT"); if (!install_root) install_root="file:/"; prefix=plover_transaction_helper_get_prefix(helper,NULL); if (prefix) { s=g_strconcat(prefix,"/var/lib/razor",NULL); local_database=razor_path_relative_to_uri(install_root,*s=='/'?s+1:s, &razor_error); g_free(s); if (!local_database) { g_warning("plover_transaction_helper_get_installed: %s", razor_error_get_msg(razor_error)); razor_error_free(razor_error); return NULL; } } else local_database=NULL; switch(comps->database) { case COMPS_DATABASE_DISTRIBUTION_LOCAL: active_database=local_database; alternate_database=NULL; break; case COMPS_DATABASE_GLOBAL: active_database=NULL; alternate_database=local_database; break; } saved_database_uri=g_strdup(razor_get_database_uri()); if (prefix) { razor_set_database_uri(alternate_database); alternate_installed=plover_package_set_new(); if (!plover_package_set_open(alternate_installed,install_root,TRUE, &error)) { g_object_unref(alternate_installed); g_warning("plover_transaction_helper_get_installed: %s", error->message); g_error_free(error); free(local_database); razor_set_database_uri(saved_database_uri); g_free(saved_database_uri); return NULL; } } else alternate_installed=NULL; razor_set_database_uri(active_database); free(local_database); installed=plover_package_set_new(); if (plover_package_set_open(installed,install_root,TRUE,&error)) { helper->alternate_installed=alternate_installed; helper->installed=installed; } else { g_object_unref(installed); if (alternate_installed) g_object_unref(alternate_installed); g_warning("plover_transaction_helper_get_installed: %s",error->message); g_error_free(error); } razor_set_database_uri(saved_database_uri); g_free(saved_database_uri); } return helper->installed; } void plover_transaction_helper_set_installed(PloverTransactionHelper *helper, PloverPackageSet *installed) { g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper)); g_return_if_fail(PLOVER_IS_PACKAGE_SET(installed)); g_return_if_fail(helper->installed == NULL); g_clear_object(&helper->alternate_installed); helper->installed=g_object_ref(installed); } PloverRepository * plover_transaction_helper_get_upstream(PloverTransactionHelper *helper, GError **error) { const char *base; g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL); if (!helper->upstream) { base=plover_transaction_helper_get_base(helper); helper->upstream=plover_repository_new_from_yum(base,error); } return helper->upstream; } void plover_transaction_helper_set_upstream(PloverTransactionHelper *helper, PloverRepository *upstream) { g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper)); g_return_if_fail(PLOVER_IS_REPOSITORY(upstream)); g_return_if_fail(helper->upstream == NULL); helper->upstream=g_object_ref(upstream); } const char *plover_transaction_helper_get_base(PloverTransactionHelper *helper) { g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL); return helper->base; } void plover_transaction_helper_set_base(PloverTransactionHelper *helper, const char *base) { g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper)); g_return_if_fail(helper->transactions == NULL); g_free(helper->base); helper->base=g_strdup(base); } struct comps * plover_transaction_helper_get_comps(PloverTransactionHelper *helper, GError **error) { gchar *s; g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL); if (!helper->comps && helper->base) { s=g_strconcat(helper->base,"/repodata/comps.xml",NULL); helper->comps=plover_comps_new_from_file(s); if (!helper->comps) g_set_error(error,PLOVER_GENERAL_ERROR, PLOVER_GENERAL_ERROR_FAILED,"%s: %s",s,g_strerror(errno)); g_free(s); } return helper->comps; } const char * plover_transaction_helper_get_prefix(PloverTransactionHelper *helper, GError **error) { const char *prefix; struct comps *comps; PloverTransactionHelperPrivate *priv; g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL); g_return_val_if_fail(helper->base != NULL || plover_transaction_helper_get_installed(helper) != NULL,NULL); priv=PLOVER_TRANSACTION_HELPER_GET_PRIVATE(helper); if (helper->base) { comps=plover_transaction_helper_get_comps(helper,error); if (!comps) return NULL; g_free(priv->default_prefix); priv->default_prefix=plover_comps_get_default_prefix(comps); return priv->default_prefix; } prefix=plover_package_set_guess_prefix(helper->installed,error); return prefix; } #if 0 static int plover_transaction_helper_package_count(void) { int count=0; char *install_root; struct razor_set *set; struct razor_package *package; struct razor_package_iterator *pi; install_root=getenv("RAZOR_ROOT"); if (!install_root) install_root=""; set=razor_root_open_read_only(install_root,NULL); if (set) { pi=razor_package_iterator_create(set); while (razor_package_iterator_next(pi,&package,RAZOR_DETAIL_LAST)) count++; razor_package_iterator_destroy(pi); razor_set_unref(set); } return count; } #endif static gboolean prefix_clashes(const char *prefix,const char *alt) { return g_str_has_prefix(prefix,alt) && (prefix[strlen(alt)]=='\0' || prefix[strlen(alt)]=='/'); } static void plover_transaction_helper_update_summary_page(PloverTransactionHelper *helper, GError **error) { int i,remove_count=0; gboolean alternate_database_clashes=FALSE; gboolean active_database_is_incompatible=FALSE; char *local_database,*active_database,*alternate_database; const char *alternate_prefix; gchar *prefix=NULL,*s; struct comps *comps=NULL; GtkWidget *container,*summary,*page; GtkButton *button; GtkLabel *label; g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper)); comps=plover_transaction_helper_get_comps(helper,error); if (comps) prefix=plover_comps_get_default_prefix(comps); else prefix=NULL; button=GTK_BUTTON(gtk_builder_get_object(helper->ui,"SIRemoveExisting")); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),FALSE); container=GTK_WIDGET(gtk_builder_get_object(helper->ui, "SIIncompatibleInstallation")); summary=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SISummaryOfWork")); page=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIConfirm")); if (helper->check_vendor && prefix && helper->alternate_installed) { alternate_prefix= plover_package_set_guess_prefix(helper->alternate_installed,NULL); if (alternate_prefix && prefix_clashes(prefix,alternate_prefix)) { alternate_database_clashes=TRUE; remove_count=g_slist_length( plover_package_set_get_packages(helper->alternate_installed)); } } /* * Rather than try to be too clever, we only deal with one thing * at a time. That means that if the alternate database clashes * there's no point checking if the active database is compatible. */ if (!alternate_database_clashes) { if (helper->check_vendor && prefix && !plover_package_set_files_match_prefix(helper->installed,prefix)) { active_database_is_incompatible=TRUE; remove_count= g_slist_length(plover_package_set_get_packages(helper->installed)); } } if (alternate_database_clashes || active_database_is_incompatible) { g_assert(comps!=NULL); label=GTK_LABEL(gtk_builder_get_object(helper->ui, "SIIncompatibleInstallationLabel")); if (alternate_database_clashes) s=g_strdup_printf("Incompatible Installation\n\n" "There is an existing installation under %s\n" "which is not compatible with this distribution. In order\n" "to continue, the existing installation must be uninstalled.", comps->vendor); else /* active_database_is_incompatible */ s=g_strdup_printf("Incompatible Installation\n\n" "The existing installation is not from %s.\n In order " "to continue, all the existing packages must be removed.", comps->vendor); gtk_label_set_markup(label,s); g_free(s); s=g_strdup_printf("Remove %d existing package%s",remove_count, remove_count==1?"":"s"); gtk_button_set_label(button,s); g_free(s); gtk_widget_show(container); gtk_widget_hide(summary); if (helper->assistant) gtk_assistant_set_page_complete(helper->assistant,page,FALSE); } else { gtk_widget_hide(container); gtk_widget_show(summary); if (helper->assistant) gtk_assistant_set_page_complete(helper->assistant,page,TRUE); } g_free(prefix); } void plover_transaction_helper_set_check_vendor(PloverTransactionHelper *helper, gboolean check_vendor) { g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper)); if (helper->check_vendor!=check_vendor) { helper->check_vendor=check_vendor; if (helper->transactions) plover_transaction_helper_update_summary_page(helper,NULL); } } /* * If plover_transaction_helper_add_transaction() fails with an error * of PLOVER_GENERAL_ERROR,PLOVER_GENERAL_ERROR_REQUIREMENTS_NOT_MET * then plover_transaction_helper_get_unsatisfied() can be used to * retrieve a textual description of the problem. */ const char * plover_transaction_helper_get_unsatisfied(PloverTransactionHelper *helper) { g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL); return helper->unsatisfied; } #define PLOVER_TRANSACTION_HELPER_IS_VALID_REPORT_ACTION(action) \ ((action)==PLOVER_TRANSACTION_HELPER_REPORT_INSTALL || \ (action)==PLOVER_TRANSACTION_HELPER_REPORT_REMOVE || \ (action)==PLOVER_TRANSACTION_HELPER_REPORT_UPDATE) gboolean plover_transaction_helper_add_transaction(PloverTransactionHelper *helper, PloverTransaction *transaction,struct plover_vector *report_packages, PloverTransactionHelperReportAction report_action,GError **error) { int i,count; gboolean other_packages; const char *s,*name; enum razor_install_action action; struct razor_install_iterator *ii; struct razor_set *report_set; struct razor_package *package; struct plover_vector *tasked_packages; PloverTransactionHelperPrivate *priv; GtkWidget *w; g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE); g_return_val_if_fail(PLOVER_IS_TRANSACTION(transaction),FALSE); g_return_val_if_fail(PLOVER_TRANSACTION_HELPER_IS_VALID_REPORT_ACTION(report_action),FALSE); g_return_val_if_fail(plover_transaction_get_system_set(transaction)!=NULL,FALSE); priv=PLOVER_TRANSACTION_HELPER_GET_PRIVATE(helper); g_free(helper->unsatisfied); helper->unsatisfied=NULL; if (!plover_transaction_resolve(transaction,error)) { s=plover_transaction_get_unsatisfied(transaction); helper->unsatisfied=g_strdup(s); return FALSE; } ii=plover_transaction_get_install_iterator(transaction,error); if (!ii) return FALSE; if (report_action==PLOVER_TRANSACTION_HELPER_REPORT_REMOVE) report_set=plover_transaction_get_system_set(transaction); else report_set=plover_transaction_get_next_set(transaction,error); if (!report_set) return FALSE; tasked_packages=plover_vector_new(); other_packages=FALSE; while (razor_install_iterator_next(ii,&package,&action,&count)) { if (action==report_action || action==RAZOR_INSTALL_ACTION_ADD && report_action==PLOVER_TRANSACTION_HELPER_REPORT_UPDATE) { razor_package_get_details(report_set,package,RAZOR_DETAIL_NAME, &name,RAZOR_DETAIL_LAST); if (!report_packages || plover_vector_contains(report_packages,name)) plover_vector_append(tasked_packages,name); else other_packages=TRUE; } } if (!tasked_packages->len) { /* * If there are no reportable packages tasked for action there * shouldn't by any packages at all, but let's be paranoid. */ other_packages=FALSE; razor_install_iterator_rewind(ii); while (razor_install_iterator_next(ii,&package,&action,&count)) { if (action==report_action) { razor_package_get_details(report_set,package,RAZOR_DETAIL_NAME, &name,RAZOR_DETAIL_LAST); plover_vector_append(tasked_packages,name); } } } if (!tasked_packages->len) { g_set_error(error,PLOVER_GENERAL_ERROR, PLOVER_GENERAL_ERROR_NO_WORK,"Transaction includes no %s actions", report_action==PLOVER_TRANSACTION_HELPER_REPORT_REMOVE? "remove":"add"); plover_vector_free(tasked_packages); return FALSE; } if (!helper->transactions) plover_transaction_helper_update_summary_page(helper,error); g_object_ref(transaction); helper->transactions=g_slist_append(helper->transactions,transaction); if (report_action==PLOVER_TRANSACTION_HELPER_REPORT_REMOVE) { priv->transaction_type|=TRANSACTION_TYPE_REMOVE; for(i=0;ilen;i++) { s=tasked_packages->strings[i]; if (!plover_vector_contains(helper->report_removing,s)) plover_vector_append(helper->report_removing,s); } helper->report_removing_dependants|=other_packages; } else { if (report_action==PLOVER_TRANSACTION_HELPER_REPORT_UPDATE) priv->transaction_type|=TRANSACTION_TYPE_REMOVE; priv->transaction_type|=TRANSACTION_TYPE_INSTALL; for(i=0;ilen;i++) { s=tasked_packages->strings[i]; if (!plover_vector_contains(helper->report_adding,s)) plover_vector_append(helper->report_adding,s); } helper->report_adding_dependencies|=other_packages; } w=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIProgressLabel")); switch(priv->transaction_type) { case TRANSACTION_TYPE_INSTALL: gtk_label_set_markup(GTK_LABEL(w), "Installing the Software\n\n" "Please wait while the Installation Assistant " "installs the software.\n" "This may take several minutes."); break; case TRANSACTION_TYPE_REMOVE: gtk_label_set_markup(GTK_LABEL(w), "Removing Packages\n\n" "Please wait while the Installation Assistant " "removes packages.\n" "This may take several minutes."); break; default: case TRANSACTION_TYPE_UPDATE: gtk_label_set_markup(GTK_LABEL(w), "Updating the Software\n\n" "Please wait while the Installation Assistant " "updates the software.\n" "This may take several minutes."); break; } plover_vector_free(tasked_packages); return TRUE; } static PloverTransaction * plover_transaction_helper_new_transaction(PloverTransactionHelper *helper, GError **error) { gboolean ok; const char *base,*prefix; GError *tmp_error=NULL; PloverTransaction *transaction; g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL); g_return_val_if_fail(plover_transaction_helper_get_installed(helper) != NULL,NULL); prefix=plover_transaction_helper_get_prefix(helper,&tmp_error); if (tmp_error) { g_propagate_error(error,tmp_error); return NULL; } transaction=plover_transaction_new(); plover_transaction_set_prefix(transaction,prefix); plover_transaction_set_installed(transaction,helper->installed); if (helper->upstream) ok=plover_transaction_set_upstream(transaction,helper->upstream,error); else { base=plover_transaction_helper_get_base(helper); ok=plover_transaction_set_upstream_from_yum(transaction,base,error); } if (!ok) { g_object_unref(transaction); transaction=NULL; } return transaction; } struct plover_vector *plover_transaction_helper_group_get_default_packages( PloverTransactionHelper *helper,const char *group,GError **error) { gboolean changed; struct comps *comps; struct comps_group *grp; struct comps_requirement *pkg; struct plover_vector *default_packages; g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE); comps=plover_transaction_helper_get_comps(helper,error); if (!comps) return NULL; grp=plover_comps_lookup_group(comps,group); if (!grp) { g_set_error(error,PLOVER_GENERAL_ERROR, PLOVER_GENERAL_ERROR_FAILED,"%s: group not found",group); return NULL; } default_packages=plover_vector_new(); do { changed=FALSE; for(pkg=grp->packages;pkg;pkg=pkg->next) { if (plover_vector_contains(default_packages,pkg->name)) continue; if (pkg->type==COMPS_REQUIREMENT_DEFAULT || pkg->type==COMPS_REQUIREMENT_MANDATORY || pkg->type==COMPS_REQUIREMENT_CONDITIONAL && pkg->requires && plover_vector_contains(default_packages,pkg->requires)) { changed=TRUE; plover_vector_append(default_packages,pkg->name); } } } while(changed); return default_packages; } /* * Returns TRUE if there is work to be done or FALSE if the packages are * already installed or on error. */ gboolean plover_transaction_helper_install_packages(PloverTransactionHelper *helper, struct plover_vector *packages,GError **error) { gboolean retval; PloverTransaction *transaction; g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE); g_return_val_if_fail(packages != NULL,FALSE); if (!packages->len) { g_set_error(error,PLOVER_GENERAL_ERROR, PLOVER_GENERAL_ERROR_NO_WORK,"No packages listed to be installed"); return FALSE; } transaction=plover_transaction_helper_new_transaction(helper,error); if (!transaction) return FALSE; if (!plover_transaction_install(transaction,packages->strings,error)) { g_object_unref(transaction); return FALSE; } retval=plover_transaction_helper_add_transaction(helper,transaction, packages,PLOVER_TRANSACTION_HELPER_REPORT_INSTALL,error); g_object_unref(transaction); return retval; } /* * Returns TRUE if there is work to be done or FALSE if the group is * already installed or on error. */ gboolean plover_transaction_helper_install_group(PloverTransactionHelper *helper, const char *group,GError **error) { gboolean retval; struct plover_vector *selected_packages; g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE); selected_packages=plover_transaction_helper_group_get_default_packages( helper,group,error); if (!selected_packages) return FALSE; if (!selected_packages->len) { g_set_error(error,PLOVER_GENERAL_ERROR, PLOVER_GENERAL_ERROR_FAILED,"%s: no default packages",group); plover_vector_free(selected_packages); return FALSE; } retval=plover_transaction_helper_install_packages(helper,selected_packages, error); plover_vector_free(selected_packages); return retval; } /* * Returns TRUE if there is work to be done or FALSE if the group is * not installed or on error. */ gboolean plover_transaction_helper_remove_group(PloverTransactionHelper *helper, const char *group,GError **error) { gboolean retval; struct plover_vector *selected_packages; PloverTransaction *transaction; g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE); g_return_val_if_fail(plover_transaction_helper_get_installed(helper) != NULL,FALSE); selected_packages=plover_transaction_helper_group_get_default_packages( helper,group,error); if (!selected_packages) return FALSE; if (!selected_packages->len) { g_set_error(error,PLOVER_GENERAL_ERROR, PLOVER_GENERAL_ERROR_FAILED,"%s: no default packages",group); plover_vector_free(selected_packages); return FALSE; } transaction=plover_transaction_new(); plover_transaction_set_installed(transaction,helper->installed); if (!plover_transaction_remove(transaction,selected_packages->strings, error)) { plover_vector_free(selected_packages); g_object_unref(transaction); return FALSE; } retval=plover_transaction_helper_add_transaction(helper,transaction, NULL,PLOVER_TRANSACTION_HELPER_REPORT_REMOVE,error); g_object_unref(transaction); plover_vector_free(selected_packages); return retval; } /* * Returns TRUE if there is work to be done or FALSE if all updates have * already been applied or on error. */ gboolean plover_transaction_helper_update(PloverTransactionHelper *helper, GError **error) { gboolean retval; PloverTransaction *transaction; g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE); transaction=plover_transaction_helper_new_transaction(helper,error); if (!transaction) return FALSE; if (!plover_transaction_update(transaction,NULL,error)) { g_object_unref(transaction); return FALSE; } retval=plover_transaction_helper_add_transaction(helper,transaction, NULL,PLOVER_TRANSACTION_HELPER_REPORT_UPDATE,error); g_object_unref(transaction); return retval; } gboolean plover_transaction_helper_get_visible(PloverTransactionHelper *helper) { g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE); if (helper->error_dialog) return TRUE; else if (!helper->assistant) return FALSE; else return gtk_widget_get_visible(GTK_WIDGET(helper->assistant)); } void plover_transaction_helper_present(PloverTransactionHelper *helper) { g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper)); if (helper->error_dialog) gtk_window_present(GTK_WINDOW(helper->error_dialog)); else if (helper->assistant) gtk_window_present(GTK_WINDOW(helper->assistant)); } static void plover_transaction_helper_error_dialog_response(GtkDialog *error_dialog, int response_id,PloverTransactionHelper *helper) { g_signal_handlers_disconnect_by_data(error_dialog,helper); if ((GtkWidget *)error_dialog==helper->error_dialog) { gtk_widget_destroy(helper->error_dialog); helper->error_dialog=NULL; if (helper->assistant) { gtk_widget_hide(GTK_WIDGET(helper->assistant)); gtk_assistant_set_current_page(helper->assistant,0); } g_signal_emit(helper,signals[CLOSE],0); } } const char *plover_transaction_helper_get_error(PloverTransactionHelper *helper, const GError **error) { g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper)); if (!helper->error_dialog) return NULL; if (error) *error=helper->error; return helper->error_primary_text; } void plover_transaction_helper_set_error(PloverTransactionHelper *helper, const GError *error,const char *primary_text) { GtkMessageType type; GtkWindow *window; g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper)); g_return_if_fail(error != NULL); g_return_if_fail(primary_text != NULL); if (helper->pulse_handler) { g_source_remove(helper->pulse_handler); helper->pulse_handler=0; } if (helper->error_dialog) { gtk_widget_destroy(helper->error_dialog); helper->error_dialog=NULL; } g_free(helper->error_primary_text); helper->error_primary_text=g_strdup(primary_text); g_clear_error(&helper->error); helper->error=g_error_copy(error); if (g_error_matches(error,PLOVER_GENERAL_ERROR, PLOVER_GENERAL_ERROR_NO_WORK)) type=GTK_MESSAGE_INFO; else type=GTK_MESSAGE_ERROR; if (helper->assistant) window=GTK_WINDOW(helper->assistant); else window=NULL; helper->error_dialog=gtk_message_dialog_new(window, GTK_DIALOG_DESTROY_WITH_PARENT,type,GTK_BUTTONS_CLOSE,primary_text); gtk_message_dialog_format_secondary_text( GTK_MESSAGE_DIALOG(helper->error_dialog),error->message); gtk_widget_show(helper->error_dialog); g_signal_connect(helper->error_dialog,"response", G_CALLBACK(plover_transaction_helper_error_dialog_response),helper); }