ali@24: /* ali@103: * Copyright (C) 2014, 2016, 2018, 2020 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@61: #include ali@24: #include ali@24: #include ali@24: #include ali@24: #include ali@24: #include ali@44: #include "plover/uri-handler.h" 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@38: enum plover_transaction_type { ali@38: TRANSACTION_TYPE_NULL=0, ali@38: TRANSACTION_TYPE_INSTALL=1UL<<0, ali@38: TRANSACTION_TYPE_REMOVE=1UL<<1, ali@38: TRANSACTION_TYPE_UPDATE=TRANSACTION_TYPE_INSTALL|TRANSACTION_TYPE_REMOVE ali@38: }; ali@38: ali@38: typedef struct _PloverTransactionHelperPrivate { ali@38: enum plover_transaction_type transaction_type; ali@38: gchar *default_prefix; ali@38: } PloverTransactionHelperPrivate; ali@38: ali@38: #define PLOVER_TRANSACTION_HELPER_GET_PRIVATE(obj)\ ali@38: G_TYPE_INSTANCE_GET_PRIVATE(obj,\ ali@38: PLOVER_TYPE_TRANSACTION_HELPER,\ ali@38: PloverTransactionHelperPrivate) ali@38: 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@86: static PloverTransaction * ali@86: plover_transaction_helper_new_transaction(PloverTransactionHelper *helper, ali@86: GError **error); ali@86: ali@24: static void plover_transaction_helper_finalize(PloverTransactionHelper *helper) ali@24: { ali@38: PloverTransactionHelperPrivate *priv; ali@38: priv=PLOVER_TRANSACTION_HELPER_GET_PRIVATE(helper); ali@38: g_free(priv->default_prefix); ali@24: g_free(helper->error_primary_text); ali@24: g_free(helper->base); ali@98: g_free(helper->base_uri); 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@61: g_clear_object(&helper->alternate_installed); 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@38: g_type_class_add_private(klass,sizeof(PloverTransactionHelperPrivate)); 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@38: /* ali@38: * There may be status updates queued by transaction as idle events. ali@38: * Process them now before we disconnect so that we don't lose them. ali@38: */ ali@38: while(g_main_context_pending(NULL)) ali@38: g_main_context_iteration(NULL,FALSE); 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@38: GSList *save_transactions; ali@38: PloverTransactionHelperPrivate *priv; ali@38: enum plover_transaction_type save_transaction_type; ali@38: priv=PLOVER_TRANSACTION_HELPER_GET_PRIVATE(helper); 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@86: transaction=plover_transaction_helper_new_transaction(helper,&error); ali@86: /* ali@86: * I think we want switch to the alternate installed set in the case of ali@86: * alternate_database_clashes, but not in the case of ali@86: * active_database_is_incompatible (see ali@86: * plover_transaction_helper_update_summary_page). ali@86: * Whether testing for helper->alternate_installed is sufficient I'm ali@86: * far from clear. ali@86: */ ali@86: if (helper->alternate_installed) ali@86: plover_transaction_set_installed(transaction, ali@86: helper->alternate_installed); ali@86: if (transaction && !plover_transaction_remove(transaction,NULL,&error)) ali@86: { ali@86: g_object_unref(transaction); ali@86: transaction=NULL; ali@86: } ali@38: if (transaction) ali@38: { ali@38: save_transactions=helper->transactions; ali@38: helper->transactions=NULL; ali@38: save_transaction_type=priv->transaction_type; ali@38: priv->transaction_type=0; ali@38: if (!plover_transaction_helper_add_transaction(helper,transaction, ali@38: NULL,PLOVER_TRANSACTION_HELPER_REPORT_REMOVE,&error)) ali@38: { ali@38: g_object_unref(transaction); ali@38: transaction=NULL; ali@38: helper->transactions=save_transactions; ali@38: priv->transaction_type=save_transaction_type; ali@38: } ali@38: else ali@38: { ali@38: g_slist_foreach(save_transactions,(GFunc)g_object_unref,NULL); ali@38: g_slist_free(save_transactions); ali@38: } ali@38: } 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@38: "Failed to remove existing packages"); ali@24: g_error_free(error); ali@24: return; ali@24: } ali@24: } 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@61: gchar *s,*saved_database_uri; ali@99: char *install_root,*local_database; ali@99: char *active_database=NULL,*alternate_database=NULL; ali@61: const char *prefix; ali@61: struct comps *comps; ali@61: PloverPackageSet *alternate_installed,*installed; ali@61: GError *error=NULL; ali@61: struct razor_error *razor_error=NULL; ali@24: g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL); ali@61: if (!helper->installed) ali@61: { ali@106: comps=plover_transaction_helper_get_comps(helper,&error); ali@61: if (!comps) ali@61: { ali@106: g_warning("plover_transaction_helper_get_installed: No comps: %s", ali@106: error->message); ali@106: g_error_free(error); ali@61: return NULL; ali@61: } ali@61: install_root=getenv("RAZOR_ROOT"); ali@61: if (!install_root) ali@61: install_root="file:/"; ali@61: prefix=plover_transaction_helper_get_prefix(helper,NULL); ali@61: if (prefix) ali@61: { ali@61: s=g_strconcat(prefix,"/var/lib/razor",NULL); ali@61: local_database=razor_path_relative_to_uri(install_root,*s=='/'?s+1:s, ali@61: &razor_error); ali@61: g_free(s); ali@61: if (!local_database) ali@61: { ali@61: g_warning("plover_transaction_helper_get_installed: %s", ali@61: razor_error_get_msg(razor_error)); ali@61: razor_error_free(razor_error); ali@61: return NULL; ali@61: } ali@61: } ali@61: else ali@61: local_database=NULL; ali@61: switch(comps->database) ali@61: { ali@61: case COMPS_DATABASE_DISTRIBUTION_LOCAL: ali@61: active_database=local_database; ali@61: break; ali@61: case COMPS_DATABASE_GLOBAL: ali@61: alternate_database=local_database; ali@61: break; ali@61: } ali@61: saved_database_uri=g_strdup(razor_get_database_uri()); ali@61: if (prefix) ali@61: { ali@61: razor_set_database_uri(alternate_database); ali@61: alternate_installed=plover_package_set_new(); ali@61: if (!plover_package_set_open(alternate_installed,install_root,TRUE, ali@61: &error)) ali@61: { ali@61: g_object_unref(alternate_installed); ali@61: g_warning("plover_transaction_helper_get_installed: %s", ali@61: error->message); ali@61: g_error_free(error); ali@61: free(local_database); ali@61: razor_set_database_uri(saved_database_uri); ali@61: g_free(saved_database_uri); ali@61: return NULL; ali@61: } ali@61: } ali@61: else ali@61: alternate_installed=NULL; ali@61: razor_set_database_uri(active_database); ali@61: free(local_database); ali@61: installed=plover_package_set_new(); ali@61: if (plover_package_set_open(installed,install_root,TRUE,&error)) ali@61: { ali@61: helper->alternate_installed=alternate_installed; ali@61: helper->installed=installed; ali@61: } ali@61: else ali@61: { ali@61: g_object_unref(installed); ali@61: if (alternate_installed) ali@61: g_object_unref(alternate_installed); ali@61: g_warning("plover_transaction_helper_get_installed: %s",error->message); ali@61: g_error_free(error); ali@61: } ali@61: razor_set_database_uri(saved_database_uri); ali@61: g_free(saved_database_uri); ali@61: } 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@61: g_clear_object(&helper->alternate_installed); 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@98: const char *base_uri; ali@24: g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL); ali@24: if (!helper->upstream) ali@24: { ali@98: base_uri=plover_transaction_helper_get_base_uri(helper); ali@98: helper->upstream=plover_repository_new_from_yum_uri(base_uri,error); ali@24: } ali@24: return helper->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@98: const char * ali@98: plover_transaction_helper_get_base_uri(PloverTransactionHelper *helper) ali@98: { ali@98: g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL); ali@98: return helper->base_uri; ali@98: } ali@98: ali@98: static gboolean plover_gtk__uri_validate(const char *uri) ali@98: { ali@98: char *s; ali@98: s=razor_path_relative_to_uri(uri,".",NULL); ali@98: free(s); ali@98: return !!s; ali@98: } ali@98: ali@98: void plover_transaction_helper_set_base_uri(PloverTransactionHelper *helper, ali@98: const char *base_uri) ali@98: { ali@98: g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper)); ali@98: g_return_if_fail(helper->transactions == NULL); ali@98: g_return_if_fail(plover_gtk__uri_validate(base_uri)); ali@98: g_free(helper->base_uri); ali@98: helper->base_uri=g_strdup(base_uri); ali@98: g_free(helper->base); ali@98: helper->base=NULL; ali@98: } ali@98: 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@98: if (helper->base_uri && !helper->base) ali@98: helper->base=razor_path_from_uri(helper->base_uri,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@98: gchar *base_uri; ali@24: g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper)); ali@24: g_return_if_fail(helper->transactions == NULL); ali@98: if (base) ali@98: base_uri=razor_path_to_uri(base); ali@98: else ali@98: base_uri=NULL; ali@98: plover_transaction_helper_set_base_uri(helper,base_uri); ali@98: g_free(base_uri); 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@106: char *s; ali@106: GError *tmp_err=NULL; ali@24: g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL); ali@98: if (!helper->comps && helper->base_uri) ali@24: { ali@106: s=razor_path_relative_to_uri(helper->base_uri,"repodata/comps.xml", ali@106: NULL); ali@106: helper->comps=plover_comps_new_from_uri(s,&tmp_err); ali@106: if (!helper->comps) ali@106: { ali@106: g_warning( ali@106: "PloverTransactionHelper: Failed to get comps at '%s': %s", ali@106: s,tmp_err->message); ali@106: g_propagate_error(error,tmp_err); ali@106: } ali@106: 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@38: PloverTransactionHelperPrivate *priv; ali@24: g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL); ali@98: g_return_val_if_fail(helper->base_uri != NULL || plover_transaction_helper_get_installed(helper) != NULL,NULL); ali@38: priv=PLOVER_TRANSACTION_HELPER_GET_PRIVATE(helper); ali@98: if (helper->base_uri) ali@24: { ali@24: comps=plover_transaction_helper_get_comps(helper,error); ali@24: if (!comps) ali@24: return NULL; ali@38: g_free(priv->default_prefix); ali@61: priv->default_prefix=plover_comps_get_default_prefix(comps); ali@38: return priv->default_prefix; ali@24: } ali@24: prefix=plover_package_set_guess_prefix(helper->installed,error); ali@24: return prefix; ali@24: } ali@24: ali@61: #if 0 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@61: #endif ali@61: ali@61: static gboolean prefix_clashes(const char *prefix,const char *alt) ali@61: { ali@61: return g_str_has_prefix(prefix,alt) && ali@61: (prefix[strlen(alt)]=='\0' || prefix[strlen(alt)]=='/'); ali@61: } ali@24: ali@76: static void ali@76: plover_transaction_helper_update_summary_page(PloverTransactionHelper *helper, ali@24: GError **error) ali@24: { ali@99: int remove_count=0; ali@61: gboolean alternate_database_clashes=FALSE; ali@61: gboolean active_database_is_incompatible=FALSE; ali@61: const char *alternate_prefix; ali@24: gchar *prefix=NULL,*s; ali@24: struct comps *comps=NULL; ali@38: GtkWidget *container,*summary,*page; ali@24: GtkButton *button; ali@24: GtkLabel *label; ali@76: g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper)); ali@61: comps=plover_transaction_helper_get_comps(helper,error); ali@76: if (comps) ali@76: prefix=plover_comps_get_default_prefix(comps); ali@76: else ali@76: prefix=NULL; 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@38: summary=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SISummaryOfWork")); ali@24: page=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIConfirm")); ali@61: if (helper->check_vendor && prefix && helper->alternate_installed) ali@61: { ali@61: alternate_prefix= ali@61: plover_package_set_guess_prefix(helper->alternate_installed,NULL); ali@61: if (alternate_prefix && prefix_clashes(prefix,alternate_prefix)) ali@61: { ali@61: alternate_database_clashes=TRUE; ali@61: remove_count=g_slist_length( ali@61: plover_package_set_get_packages(helper->alternate_installed)); ali@61: } ali@61: } ali@61: /* ali@61: * Rather than try to be too clever, we only deal with one thing ali@61: * at a time. That means that if the alternate database clashes ali@61: * there's no point checking if the active database is compatible. ali@61: */ ali@61: if (!alternate_database_clashes) ali@61: { ali@61: if (helper->check_vendor && prefix && ali@61: !plover_package_set_files_match_prefix(helper->installed,prefix)) ali@61: { ali@61: active_database_is_incompatible=TRUE; ali@61: remove_count= ali@61: g_slist_length(plover_package_set_get_packages(helper->installed)); ali@61: } ali@61: } ali@61: if (alternate_database_clashes || active_database_is_incompatible) ali@24: { ali@76: g_assert(comps!=NULL); ali@24: label=GTK_LABEL(gtk_builder_get_object(helper->ui, ali@24: "SIIncompatibleInstallationLabel")); ali@61: if (alternate_database_clashes) ali@61: s=g_strdup_printf("Incompatible Installation\n\n" ali@61: "There is an existing installation under %s\n" ali@61: "which is not compatible with this distribution. In order\n" ali@61: "to continue, the existing installation must be uninstalled.", ali@61: comps->vendor); ali@61: else /* active_database_is_incompatible */ ali@61: s=g_strdup_printf("Incompatible Installation\n\n" ali@61: "The existing installation is not from %s.\n In order " ali@61: "to continue, all the existing packages must be removed.", ali@61: comps->vendor); ali@24: gtk_label_set_markup(label,s); ali@24: g_free(s); ali@61: s=g_strdup_printf("Remove %d existing package%s",remove_count, ali@61: remove_count==1?"":"s"); ali@24: gtk_button_set_label(button,s); ali@24: g_free(s); ali@24: gtk_widget_show(container); ali@38: gtk_widget_hide(summary); 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@38: gtk_widget_show(summary); ali@24: if (helper->assistant) ali@24: gtk_assistant_set_page_complete(helper->assistant,page,TRUE); ali@24: } ali@38: g_free(prefix); 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@76: plover_transaction_helper_update_summary_page(helper,NULL); ali@24: } ali@24: } ali@24: ali@24: /* ali@75: * If plover_transaction_helper_add_transaction() fails 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@38: #define PLOVER_TRANSACTION_HELPER_IS_VALID_REPORT_ACTION(action) \ ali@38: ((action)==PLOVER_TRANSACTION_HELPER_REPORT_INSTALL || \ ali@38: (action)==PLOVER_TRANSACTION_HELPER_REPORT_REMOVE || \ ali@38: (action)==PLOVER_TRANSACTION_HELPER_REPORT_UPDATE) ali@38: ali@24: gboolean ali@24: plover_transaction_helper_add_transaction(PloverTransactionHelper *helper, ali@24: PloverTransaction *transaction,struct plover_vector *report_packages, ali@38: PloverTransactionHelperReportAction report_action,GError **error) ali@24: { ali@24: int i,count; ali@24: gboolean other_packages; ali@24: const char *s,*name; ali@99: enum razor_install_action razor_action; ali@99: PloverTransactionHelperReportAction action; ali@24: struct razor_install_iterator *ii; ali@38: struct razor_set *report_set; ali@24: struct razor_package *package; ali@24: struct plover_vector *tasked_packages; ali@38: PloverTransactionHelperPrivate *priv; ali@38: GtkWidget *w; 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@38: g_return_val_if_fail(PLOVER_TRANSACTION_HELPER_IS_VALID_REPORT_ACTION(report_action),FALSE); ali@38: g_return_val_if_fail(plover_transaction_get_system_set(transaction)!=NULL,FALSE); ali@38: priv=PLOVER_TRANSACTION_HELPER_GET_PRIVATE(helper); 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@38: if (report_action==PLOVER_TRANSACTION_HELPER_REPORT_REMOVE) ali@38: report_set=plover_transaction_get_system_set(transaction); ali@38: else ali@38: report_set=plover_transaction_get_next_set(transaction,error); ali@38: if (!report_set) ali@24: return FALSE; ali@24: tasked_packages=plover_vector_new(); ali@24: other_packages=FALSE; ali@99: while (razor_install_iterator_next(ii,&package,&razor_action,&count)) ali@24: { ali@99: if (razor_action==RAZOR_INSTALL_ACTION_ADD) ali@99: action=PLOVER_TRANSACTION_HELPER_REPORT_INSTALL; ali@99: else if (razor_action==RAZOR_INSTALL_ACTION_REMOVE) ali@99: action=PLOVER_TRANSACTION_HELPER_REPORT_REMOVE; ali@99: else ali@99: continue; ali@99: if (action==report_action || razor_action==RAZOR_INSTALL_ACTION_ADD && ali@38: report_action==PLOVER_TRANSACTION_HELPER_REPORT_UPDATE) ali@24: { ali@38: razor_package_get_details(report_set,package,RAZOR_DETAIL_NAME, ali@38: &name,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@103: * shouldn't be any packages at all, but let's be paranoid. ali@24: */ ali@24: other_packages=FALSE; ali@24: razor_install_iterator_rewind(ii); ali@99: while (razor_install_iterator_next(ii,&package,&razor_action,&count)) ali@24: { ali@99: if (razor_action==RAZOR_INSTALL_ACTION_ADD) ali@99: action=PLOVER_TRANSACTION_HELPER_REPORT_INSTALL; ali@99: else if (razor_action==RAZOR_INSTALL_ACTION_REMOVE) ali@99: action=PLOVER_TRANSACTION_HELPER_REPORT_REMOVE; ali@99: else ali@99: continue; ali@103: if (action==report_action || ali@103: razor_action==RAZOR_INSTALL_ACTION_ADD && ali@103: report_action==PLOVER_TRANSACTION_HELPER_REPORT_UPDATE) ali@24: { ali@38: razor_package_get_details(report_set,package,RAZOR_DETAIL_NAME, ali@38: &name,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@38: report_action==PLOVER_TRANSACTION_HELPER_REPORT_REMOVE? ali@38: "remove":"add"); ali@24: plover_vector_free(tasked_packages); ali@24: return FALSE; ali@24: } ali@24: if (!helper->transactions) ali@76: plover_transaction_helper_update_summary_page(helper,error); ali@24: g_object_ref(transaction); ali@24: helper->transactions=g_slist_append(helper->transactions,transaction); ali@38: if (report_action==PLOVER_TRANSACTION_HELPER_REPORT_REMOVE) ali@24: { ali@38: priv->transaction_type|=TRANSACTION_TYPE_REMOVE; ali@38: for(i=0;ilen;i++) ali@38: { ali@38: s=tasked_packages->strings[i]; ali@38: if (!plover_vector_contains(helper->report_removing,s)) ali@38: plover_vector_append(helper->report_removing,s); ali@38: } ali@38: helper->report_removing_dependants|=other_packages; ali@38: } ali@38: else ali@38: { ali@38: if (report_action==PLOVER_TRANSACTION_HELPER_REPORT_UPDATE) ali@38: priv->transaction_type|=TRANSACTION_TYPE_REMOVE; ali@38: priv->transaction_type|=TRANSACTION_TYPE_INSTALL; 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@38: w=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIProgressLabel")); ali@38: switch(priv->transaction_type) ali@24: { ali@38: case TRANSACTION_TYPE_INSTALL: ali@38: gtk_label_set_markup(GTK_LABEL(w), ali@38: "Installing the Software\n\n" ali@38: "Please wait while the Installation Assistant " ali@38: "installs the software.\n" ali@38: "This may take several minutes."); ali@38: break; ali@38: case TRANSACTION_TYPE_REMOVE: ali@38: gtk_label_set_markup(GTK_LABEL(w), ali@38: "Removing Packages\n\n" ali@38: "Please wait while the Installation Assistant " ali@38: "removes packages.\n" ali@38: "This may take several minutes."); ali@38: break; ali@38: default: ali@38: case TRANSACTION_TYPE_UPDATE: ali@38: gtk_label_set_markup(GTK_LABEL(w), ali@38: "Updating the Software\n\n" ali@38: "Please wait while the Installation Assistant " ali@38: "updates the software.\n" ali@38: "This may take several minutes."); ali@38: break; 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@98: const char *base_uri,*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@61: g_return_val_if_fail(plover_transaction_helper_get_installed(helper) != NULL,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@98: base_uri=plover_transaction_helper_get_base_uri(helper); ali@98: ok=plover_transaction_set_upstream_from_yum_uri(transaction,base_uri, ali@98: 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@38: pkg->type==COMPS_REQUIREMENT_CONDITIONAL && pkg->requires && 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@38: packages,PLOVER_TRANSACTION_HELPER_REPORT_INSTALL,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@61: g_return_val_if_fail(plover_transaction_helper_get_installed(helper) != NULL,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@38: NULL,PLOVER_TRANSACTION_HELPER_REPORT_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@103: * Returns TRUE if there is work to be done or FALSE if the group is ali@103: * empty or on error. ali@103: * ali@103: * The default action is to: ali@103: * - install (and update all) if any of the packages in @group are ali@103: * missing or out of date, ali@103: * - otherwise update all if any packages are out of date, ali@103: * - otherwise remove ALL packages for distribution-local comps, ali@103: * - otherwise remove all packages in @group, their dependants and leaves. ali@103: */ ali@103: gboolean plover_transaction_helper_default_action_on_group( ali@103: PloverTransactionHelper *helper,const char *group,GError **error) ali@103: { ali@103: gboolean distribution_local=FALSE,retval; ali@103: GError *tmp_err=NULL; ali@103: struct comps *comps; ali@103: struct plover_vector *selected_packages; ali@103: PloverTransaction *transaction; ali@103: g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE); ali@103: selected_packages=plover_transaction_helper_group_get_default_packages( ali@103: helper,group,error); ali@103: if (!selected_packages) ali@103: return FALSE; ali@103: if (!selected_packages->len) ali@103: { ali@103: g_set_error(error,PLOVER_GENERAL_ERROR, ali@103: PLOVER_GENERAL_ERROR_FAILED,"%s: no default packages",group); ali@103: plover_vector_free(selected_packages); ali@103: return FALSE; ali@103: } ali@103: comps=plover_transaction_helper_get_comps(helper,NULL); ali@103: if (comps && comps->database==COMPS_DATABASE_DISTRIBUTION_LOCAL) ali@103: distribution_local=TRUE; ali@103: transaction=plover_transaction_helper_new_transaction(helper,error); ali@103: if (!transaction) ali@103: { ali@103: plover_vector_free(selected_packages); ali@103: return FALSE; ali@103: } ali@103: if (!plover_transaction_install_with_update_all(transaction, ali@103: selected_packages->strings,error)) ali@103: { ali@103: g_object_unref(transaction); ali@103: plover_vector_free(selected_packages); ali@103: return FALSE; ali@103: } ali@103: retval=plover_transaction_helper_add_transaction(helper,transaction, ali@103: selected_packages,PLOVER_TRANSACTION_HELPER_REPORT_INSTALL,&tmp_err); ali@103: g_object_unref(transaction); ali@103: if (!retval) ali@103: { ali@103: if (!g_error_matches(tmp_err,PLOVER_GENERAL_ERROR, ali@103: PLOVER_GENERAL_ERROR_NO_WORK)) ali@103: { ali@103: g_propagate_error(error,tmp_err); ali@103: plover_vector_free(selected_packages); ali@103: return FALSE; ali@103: } ali@103: g_clear_error(&tmp_err); ali@103: transaction=plover_transaction_helper_new_transaction(helper,error); ali@103: if (!transaction) ali@103: { ali@103: plover_vector_free(selected_packages); ali@103: return FALSE; ali@103: } ali@103: if (!plover_transaction_update(transaction,NULL,error)) ali@103: { ali@103: g_object_unref(transaction); ali@103: plover_vector_free(selected_packages); ali@103: return FALSE; ali@103: } ali@103: retval=plover_transaction_helper_add_transaction(helper,transaction, ali@103: selected_packages,PLOVER_TRANSACTION_HELPER_REPORT_UPDATE,&tmp_err); ali@103: g_object_unref(transaction); ali@103: if (!retval) ali@103: { ali@103: if (!g_error_matches(tmp_err,PLOVER_GENERAL_ERROR, ali@103: PLOVER_GENERAL_ERROR_NO_WORK)) ali@103: { ali@103: g_propagate_error(error,tmp_err); ali@103: plover_vector_free(selected_packages); ali@103: return FALSE; ali@103: } ali@103: g_clear_error(&tmp_err); ali@103: transaction=plover_transaction_helper_new_transaction(helper,error); ali@103: if (!transaction) ali@103: { ali@103: plover_vector_free(selected_packages); ali@103: return FALSE; ali@103: } ali@103: if (!plover_transaction_remove_with_dependants_and_leaves( ali@103: transaction,distribution_local?NULL:selected_packages->strings, ali@103: error)) ali@103: { ali@103: g_object_unref(transaction); ali@103: plover_vector_free(selected_packages); ali@103: return FALSE; ali@103: } ali@103: retval=plover_transaction_helper_add_transaction(helper,transaction, ali@103: NULL,PLOVER_TRANSACTION_HELPER_REPORT_REMOVE,error); ali@103: g_object_unref(transaction); ali@103: } ali@103: } ali@103: plover_vector_free(selected_packages); ali@103: return retval; ali@103: } ali@103: ali@103: /* 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@38: NULL,PLOVER_TRANSACTION_HELPER_REPORT_UPDATE,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@99: g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL); 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: }