2 * Copyright (C) 2014, 2016, 2018, 2020 J. Ali Harlow <ali@juiblex.co.uk>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include <plover/plover.h>
25 #include <plover/transaction.h>
26 #include <plover-gtk/transactionhelper.h>
27 #include "plover/uri-handler.h"
30 * A PloverTransactionHelper uses a GtkAssistant to help a user run a
34 G_DEFINE_TYPE(PloverTransactionHelper,plover_transaction_helper,G_TYPE_OBJECT)
36 enum plover_transaction_type {
37 TRANSACTION_TYPE_NULL=0,
38 TRANSACTION_TYPE_INSTALL=1UL<<0,
39 TRANSACTION_TYPE_REMOVE=1UL<<1,
40 TRANSACTION_TYPE_UPDATE=TRANSACTION_TYPE_INSTALL|TRANSACTION_TYPE_REMOVE
43 typedef struct _PloverTransactionHelperPrivate {
44 enum plover_transaction_type transaction_type;
45 gchar *default_prefix;
46 } PloverTransactionHelperPrivate;
48 #define PLOVER_TRANSACTION_HELPER_GET_PRIVATE(obj)\
49 G_TYPE_INSTANCE_GET_PRIVATE(obj,\
50 PLOVER_TYPE_TRANSACTION_HELPER,\
51 PloverTransactionHelperPrivate)
58 static guint signals[N_SIGNALS];
60 static PloverTransaction *
61 plover_transaction_helper_new_transaction(PloverTransactionHelper *helper,
64 static void plover_transaction_helper_finalize(PloverTransactionHelper *helper)
66 PloverTransactionHelperPrivate *priv;
67 priv=PLOVER_TRANSACTION_HELPER_GET_PRIVATE(helper);
68 g_free(priv->default_prefix);
69 g_free(helper->error_primary_text);
71 g_free(helper->base_uri);
72 g_free(helper->unsatisfied);
74 plover_comps_free(helper->comps);
75 plover_vector_free(helper->report_adding);
76 plover_vector_free(helper->report_removing);
79 static void plover_transaction_helper_dispose(PloverTransactionHelper *helper)
81 g_clear_error(&helper->error);
82 if (helper->error_dialog)
84 g_signal_handlers_disconnect_by_data(helper->error_dialog,helper);
85 gtk_widget_destroy(helper->error_dialog);
86 helper->error_dialog=NULL;
88 if (helper->assistant)
90 g_signal_handlers_disconnect_by_data(helper->assistant,helper);
91 g_clear_object(&helper->assistant);
93 g_clear_object(&helper->ui);
94 g_slist_foreach(helper->transactions,(GFunc)g_object_unref,NULL);
95 g_slist_free(helper->transactions);
96 helper->transactions=NULL;
97 g_clear_object(&helper->alternate_installed);
98 g_clear_object(&helper->installed);
99 g_clear_object(&helper->upstream);
100 g_clear_object(&helper->relocated_upstream);
104 plover_transaction_helper_class_init(PloverTransactionHelperClass *klass)
106 GObjectClass *gobject_class=G_OBJECT_CLASS(klass);
107 gobject_class->finalize=
108 (void (*)(GObject *))plover_transaction_helper_finalize;
109 gobject_class->dispose=
110 (void (*)(GObject *))plover_transaction_helper_dispose;
111 g_type_class_add_private(klass,sizeof(PloverTransactionHelperPrivate));
112 signals[CLOSE]=g_signal_newv("close",
113 G_TYPE_FROM_CLASS(klass),G_SIGNAL_RUN_LAST,NULL,NULL,NULL,
114 g_cclosure_marshal_VOID__VOID,G_TYPE_NONE,0,NULL);
117 static void plover_transaction_helper_init(PloverTransactionHelper *helper)
119 helper->report_adding=plover_vector_new();
120 helper->report_removing=plover_vector_new();
123 static void plover_transaction_helper_assistant_cancel(GtkAssistant *assistant,
124 PloverTransactionHelper *helper)
126 gtk_widget_hide(GTK_WIDGET(helper->assistant));
127 gtk_assistant_set_current_page(helper->assistant,0);
128 g_signal_emit(helper,signals[CLOSE],0);
131 static void plover_transaction_helper_assistant_close(GtkAssistant *assistant,
132 PloverTransactionHelper *helper)
134 gtk_widget_hide(GTK_WIDGET(helper->assistant));
135 gtk_assistant_set_current_page(helper->assistant,0);
136 g_signal_emit(helper,signals[CLOSE],0);
140 plover_transaction_helper_prepare_confirm(PloverTransactionHelper *helper)
142 gchar *package_list,*add,*remove,*s;
144 struct plover_vector *report;
145 if (helper->report_adding->len)
147 plover_vector_sort(helper->report_adding);
148 if (helper->report_adding_dependencies)
150 report=plover_vector_dup(helper->report_adding);
151 if (helper->report_adding->len==1)
152 plover_vector_append(report,"its dependencies");
154 plover_vector_append(report,"their dependencies");
155 package_list=plover_vector_format_for_display(report);
156 plover_vector_free(report);
160 plover_vector_format_for_display(helper->report_adding);
161 add=g_strconcat("Packages to be installed or updated: ",package_list,
163 g_free(package_list);
167 if (helper->report_removing->len)
169 plover_vector_sort(helper->report_removing);
170 if (helper->report_removing_dependants)
172 report=plover_vector_dup(helper->report_removing);
173 if (helper->report_adding->len==1)
174 plover_vector_append(report,"its dependants");
176 plover_vector_append(report,"their dependants");
177 package_list=plover_vector_format_for_display(report);
178 plover_vector_free(report);
182 plover_vector_format_for_display(helper->report_removing);
183 remove=g_strconcat("Packages to be removed: ",package_list,".",NULL);
184 g_free(package_list);
188 label=GTK_LABEL(gtk_builder_get_object(helper->ui,"SISummaryOfWork"));
190 s=g_strconcat("<b>Installation Summary</b>\n\n",remove,"\n\n",add,NULL);
191 else if (add || remove)
192 s=g_strconcat("<b>Installation Summary</b>\n\n",add?add:remove,NULL);
194 s=g_strdup("<b>Installation Summary</b>\n\nNo changes scheduled");
195 gtk_label_set_markup(label,s);
201 static void plover_transaction_helper_run(PloverTransactionHelper *helper);
203 static void plover_transaction_helper_callback(GObject *source,
204 GAsyncResult *result,gpointer user_data)
207 PloverTransactionHelper *helper=user_data;
208 PloverTransaction *transaction=PLOVER_TRANSACTION(source);
209 if (!plover_transaction_commit_finish(transaction,result,&error))
211 plover_transaction_helper_set_error(helper,error,
212 "Software installation failed");
216 plover_transaction_helper_run(helper);
218 * There may be status updates queued by transaction as idle events.
219 * Process them now before we disconnect so that we don't lose them.
221 while(g_main_context_pending(NULL))
222 g_main_context_iteration(NULL,FALSE);
223 g_signal_handlers_disconnect_by_data(transaction,helper);
224 g_object_unref(transaction);
227 static void plover_transaction_helper_transaction_status_changed(
228 PloverTransaction *transaction,const char *status,
229 PloverTransactionHelper *helper)
232 bar=GTK_PROGRESS_BAR(gtk_builder_get_object(helper->ui,"SIProgressBar"));
233 gtk_progress_bar_set_text(bar,status);
236 static void plover_transaction_helper_run(PloverTransactionHelper *helper)
238 PloverTransaction *transaction;
240 page=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIProgress"));
241 if (helper->transactions)
243 if (helper->assistant)
244 gtk_assistant_set_page_complete(helper->assistant,page,FALSE);
245 transaction=helper->transactions->data;
246 helper->transactions=g_slist_delete_link(helper->transactions,
247 helper->transactions);
248 g_signal_connect(transaction,"status-changed",
249 G_CALLBACK(plover_transaction_helper_transaction_status_changed),
251 plover_transaction_commit_async(transaction,NULL,
252 plover_transaction_helper_callback,helper);
254 else if (helper->assistant)
255 gtk_assistant_set_page_complete(helper->assistant,page,TRUE);
258 static gboolean plover_transaction_helper_pulse(gpointer user_data)
260 PloverTransactionHelper *helper=user_data;
263 if (!helper->assistant)
265 w=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIProgress"));
266 bar=GTK_PROGRESS_BAR(gtk_builder_get_object(helper->ui,"SIProgressBar"));
267 if (gtk_assistant_get_page_complete(helper->assistant,w))
269 gtk_progress_bar_set_fraction(bar,1.0);
270 helper->pulse_handler=0;
275 gtk_progress_bar_pulse(bar);
281 plover_transaction_helper_prepare_progress(PloverTransactionHelper *helper)
284 GtkToggleButton *button;
285 PloverTransaction *transaction;
286 GSList *save_transactions;
287 PloverTransactionHelperPrivate *priv;
288 enum plover_transaction_type save_transaction_type;
289 priv=PLOVER_TRANSACTION_HELPER_GET_PRIVATE(helper);
290 button=GTK_TOGGLE_BUTTON(gtk_builder_get_object(helper->ui,
291 "SIRemoveExisting"));
292 if (gtk_toggle_button_get_active(button))
294 transaction=plover_transaction_helper_new_transaction(helper,&error);
296 * I think we want switch to the alternate installed set in the case of
297 * alternate_database_clashes, but not in the case of
298 * active_database_is_incompatible (see
299 * plover_transaction_helper_update_summary_page).
300 * Whether testing for helper->alternate_installed is sufficient I'm
303 if (helper->alternate_installed)
304 plover_transaction_set_installed(transaction,
305 helper->alternate_installed);
306 if (transaction && !plover_transaction_remove(transaction,NULL,&error))
308 g_object_unref(transaction);
313 save_transactions=helper->transactions;
314 helper->transactions=NULL;
315 save_transaction_type=priv->transaction_type;
316 priv->transaction_type=0;
317 if (!plover_transaction_helper_add_transaction(helper,transaction,
318 NULL,PLOVER_TRANSACTION_HELPER_REPORT_REMOVE,&error))
320 g_object_unref(transaction);
322 helper->transactions=save_transactions;
323 priv->transaction_type=save_transaction_type;
327 g_slist_foreach(save_transactions,(GFunc)g_object_unref,NULL);
328 g_slist_free(save_transactions);
333 if (g_error_matches(error,PLOVER_POSIX_ERROR,ENOENT))
334 g_clear_error(&error);
337 plover_transaction_helper_set_error(helper,error,
338 "Failed to remove existing packages");
345 * Note that PloverTransaction does support cancelling a transaction, but
346 * there are a number of challenges with using it:
347 * - cancellation is only supported during the file phase if razor
348 * has atomic rollback,
349 * - cancellation is not supported during post-transaction scripts at all
350 * (since by the time the first script is started the atomic has already
351 * been committed) and these can take quite some time,
352 * - where a transaction has an embedded COMMIT, any rollback won't
353 * go back beyond this point.
354 * To support user-cancel, then, we would need some mechanism to:
355 * - Comunicate that the operation is being cancelled and this may take
357 * - Not allow cancellation at all after the last post-transaction script
359 * - Report the partially completed transaction where cancellation
360 * occurred after a COMMIT point.
361 * At present, this doesn't appear worth the effort.
363 if (helper->assistant)
364 gtk_assistant_commit(helper->assistant);
365 plover_transaction_helper_run(helper);
366 helper->pulse_handler=g_timeout_add(100,plover_transaction_helper_pulse,
370 static void plover_transaction_helper_assistant_prepare(GtkAssistant *assistant,
371 GtkWidget *page,PloverTransactionHelper *helper)
373 if (page==GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIConfirm")))
374 plover_transaction_helper_prepare_confirm(helper);
375 else if (page==GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIProgress")))
376 plover_transaction_helper_prepare_progress(helper);
380 plover_transaction_helper_remove_existing_toggled(GtkToggleButton *button,
381 PloverTransactionHelper *helper)
384 if (helper->assistant)
386 w=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIConfirm"));
387 gtk_assistant_set_page_complete(helper->assistant,w,
388 gtk_toggle_button_get_active(button));
392 PloverTransactionHelper *plover_transaction_helper_new(GtkBuilder *ui)
395 gchar *s,*directory,*contents;
398 PloverTransactionHelper *helper;
399 g_return_val_if_fail(ui == NULL || GTK_IS_BUILDER(ui),NULL);
400 helper=PLOVER_TRANSACTION_HELPER(
401 g_object_new(PLOVER_TYPE_TRANSACTION_HELPER,NULL));
403 helper->ui=g_object_ref(ui);
405 helper->ui=gtk_builder_new();
407 GTK_ASSISTANT(gtk_builder_get_object(helper->ui,"SoftwareInstallation"));
408 if (!helper->assistant)
410 directory=g_strdup(g_getenv("PLOVER_DATADIR"));
414 s=g_win32_get_package_installation_directory_of_module(NULL);
415 directory=g_build_filename(s,"share","plover",NULL);
418 directory=g_strdup(PLOVER_DATADIR);
421 s=g_build_filename(directory,"software-installation.ui",NULL);
423 (void)g_file_get_contents(s,&contents,&len,&error);
427 (void)gtk_builder_add_from_string(helper->ui,contents,len,&error);
432 g_critical("software-installation.ui: %s",error->message);
433 g_clear_error(&error);
434 g_set_error(&error,PLOVER_GENERAL_ERROR,PLOVER_GENERAL_ERROR_FAILED,
435 "Internal error (no user interface)");
436 plover_transaction_helper_set_error(helper,error,
437 "Can't start installer");
440 helper->assistant=GTK_ASSISTANT(gtk_builder_get_object(helper->ui,
441 "SoftwareInstallation"));
443 if (!helper->assistant)
445 g_critical("\"SoftwareInstallation\" object not found");
446 g_set_error(&error,PLOVER_GENERAL_ERROR,PLOVER_GENERAL_ERROR_FAILED,
447 "Internal error (missing wizard)");
448 plover_transaction_helper_set_error(helper,error,
449 "Can't start installer");
454 g_object_ref(helper->assistant);
455 if (!GTK_IS_ASSISTANT(helper->assistant))
457 g_critical("\"SoftwareInstallation\" is not a GtkAssistant");
458 g_set_error(&error,PLOVER_GENERAL_ERROR,PLOVER_GENERAL_ERROR_FAILED,
459 "Internal error (unexpected wizard type)");
460 plover_transaction_helper_set_error(helper,error,
461 "Can't start installer");
465 g_signal_connect(helper->assistant,"cancel",
466 G_CALLBACK(plover_transaction_helper_assistant_cancel),helper);
467 g_signal_connect(helper->assistant,"close",
468 G_CALLBACK(plover_transaction_helper_assistant_close),helper);
469 g_signal_connect(helper->assistant,"prepare",
470 G_CALLBACK(plover_transaction_helper_assistant_prepare),helper);
471 w=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIRemoveExisting"));
473 g_signal_connect(w,"toggled",
474 G_CALLBACK(plover_transaction_helper_remove_existing_toggled),helper);
475 w=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIIntroduction"));
477 gtk_assistant_set_page_complete(helper->assistant,w,TRUE);
482 plover_transaction_helper_get_installed(PloverTransactionHelper *helper)
484 gchar *s,*saved_database_uri;
485 char *install_root,*local_database;
486 char *active_database=NULL,*alternate_database=NULL;
489 PloverPackageSet *alternate_installed,*installed;
491 struct razor_error *razor_error=NULL;
492 g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL);
493 if (!helper->installed)
495 comps=plover_transaction_helper_get_comps(helper,&error);
498 g_warning("plover_transaction_helper_get_installed: No comps: %s",
503 install_root=getenv("RAZOR_ROOT");
505 install_root="file:/";
506 prefix=plover_transaction_helper_get_prefix(helper,NULL);
509 s=g_strconcat(prefix,"/var/lib/razor",NULL);
510 local_database=razor_path_relative_to_uri(install_root,*s=='/'?s+1:s,
515 g_warning("plover_transaction_helper_get_installed: %s",
516 razor_error_get_msg(razor_error));
517 razor_error_free(razor_error);
523 switch(comps->database)
525 case COMPS_DATABASE_DISTRIBUTION_LOCAL:
526 active_database=local_database;
528 case COMPS_DATABASE_GLOBAL:
529 alternate_database=local_database;
532 saved_database_uri=g_strdup(razor_get_database_uri());
535 razor_set_database_uri(alternate_database);
536 alternate_installed=plover_package_set_new();
537 if (!plover_package_set_open(alternate_installed,install_root,TRUE,
540 g_object_unref(alternate_installed);
541 g_warning("plover_transaction_helper_get_installed: %s",
544 free(local_database);
545 razor_set_database_uri(saved_database_uri);
546 g_free(saved_database_uri);
551 alternate_installed=NULL;
552 razor_set_database_uri(active_database);
553 free(local_database);
554 installed=plover_package_set_new();
555 if (plover_package_set_open(installed,install_root,TRUE,&error))
557 helper->alternate_installed=alternate_installed;
558 helper->installed=installed;
562 g_object_unref(installed);
563 if (alternate_installed)
564 g_object_unref(alternate_installed);
565 g_warning("plover_transaction_helper_get_installed: %s",error->message);
568 razor_set_database_uri(saved_database_uri);
569 g_free(saved_database_uri);
571 return helper->installed;
574 void plover_transaction_helper_set_installed(PloverTransactionHelper *helper,
575 PloverPackageSet *installed)
577 g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper));
578 g_return_if_fail(PLOVER_IS_PACKAGE_SET(installed));
579 g_return_if_fail(helper->installed == NULL);
580 g_clear_object(&helper->alternate_installed);
581 helper->installed=g_object_ref(installed);
585 plover_transaction_helper_get_upstream(PloverTransactionHelper *helper,
588 const char *base_uri;
589 g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL);
590 if (!helper->upstream)
592 base_uri=plover_transaction_helper_get_base_uri(helper);
593 helper->upstream=plover_repository_new_from_yum_uri(base_uri,error);
595 return helper->upstream;
598 void plover_transaction_helper_set_upstream(PloverTransactionHelper *helper,
599 PloverRepository *upstream)
601 g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper));
602 g_return_if_fail(PLOVER_IS_REPOSITORY(upstream));
603 g_return_if_fail(helper->upstream == NULL);
604 helper->upstream=g_object_ref(upstream);
608 plover_transaction_helper_get_base_uri(PloverTransactionHelper *helper)
610 g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL);
611 return helper->base_uri;
614 static gboolean plover_gtk__uri_validate(const char *uri)
617 s=razor_path_relative_to_uri(uri,".",NULL);
622 void plover_transaction_helper_set_base_uri(PloverTransactionHelper *helper,
623 const char *base_uri)
625 g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper));
626 g_return_if_fail(helper->transactions == NULL);
627 g_return_if_fail(plover_gtk__uri_validate(base_uri));
628 g_free(helper->base_uri);
629 helper->base_uri=g_strdup(base_uri);
630 g_free(helper->base);
634 const char *plover_transaction_helper_get_base(PloverTransactionHelper *helper)
636 g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL);
637 if (helper->base_uri && !helper->base)
638 helper->base=razor_path_from_uri(helper->base_uri,NULL);
642 void plover_transaction_helper_set_base(PloverTransactionHelper *helper,
646 g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper));
647 g_return_if_fail(helper->transactions == NULL);
649 base_uri=razor_path_to_uri(base);
652 plover_transaction_helper_set_base_uri(helper,base_uri);
654 helper->base=g_strdup(base);
658 plover_transaction_helper_get_comps(PloverTransactionHelper *helper,
662 GError *tmp_err=NULL;
663 g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL);
664 if (!helper->comps && helper->base_uri)
666 s=razor_path_relative_to_uri(helper->base_uri,"repodata/comps.xml",
668 helper->comps=plover_comps_new_from_uri(s,&tmp_err);
672 "PloverTransactionHelper: Failed to get comps at '%s': %s",
674 g_propagate_error(error,tmp_err);
678 return helper->comps;
682 plover_transaction_helper_get_prefix(PloverTransactionHelper *helper,
687 PloverTransactionHelperPrivate *priv;
688 g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL);
689 g_return_val_if_fail(helper->base_uri != NULL || plover_transaction_helper_get_installed(helper) != NULL,NULL);
690 priv=PLOVER_TRANSACTION_HELPER_GET_PRIVATE(helper);
691 if (helper->base_uri)
693 comps=plover_transaction_helper_get_comps(helper,error);
696 g_free(priv->default_prefix);
697 priv->default_prefix=plover_comps_get_default_prefix(comps);
698 return priv->default_prefix;
700 prefix=plover_package_set_guess_prefix(helper->installed,error);
705 static int plover_transaction_helper_package_count(void)
709 struct razor_set *set;
710 struct razor_package *package;
711 struct razor_package_iterator *pi;
712 install_root=getenv("RAZOR_ROOT");
715 set=razor_root_open_read_only(install_root,NULL);
718 pi=razor_package_iterator_create(set);
719 while (razor_package_iterator_next(pi,&package,RAZOR_DETAIL_LAST))
721 razor_package_iterator_destroy(pi);
722 razor_set_unref(set);
728 static gboolean prefix_clashes(const char *prefix,const char *alt)
730 return g_str_has_prefix(prefix,alt) &&
731 (prefix[strlen(alt)]=='\0' || prefix[strlen(alt)]=='/');
735 plover_transaction_helper_update_summary_page(PloverTransactionHelper *helper,
739 gboolean alternate_database_clashes=FALSE;
740 gboolean active_database_is_incompatible=FALSE;
741 const char *alternate_prefix;
742 gchar *prefix=NULL,*s;
743 struct comps *comps=NULL;
744 GtkWidget *container,*summary,*page;
747 g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper));
748 comps=plover_transaction_helper_get_comps(helper,error);
750 prefix=plover_comps_get_default_prefix(comps);
753 button=GTK_BUTTON(gtk_builder_get_object(helper->ui,"SIRemoveExisting"));
754 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),FALSE);
755 container=GTK_WIDGET(gtk_builder_get_object(helper->ui,
756 "SIIncompatibleInstallation"));
757 summary=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SISummaryOfWork"));
758 page=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIConfirm"));
759 if (helper->check_vendor && prefix && helper->alternate_installed)
762 plover_package_set_guess_prefix(helper->alternate_installed,NULL);
763 if (alternate_prefix && prefix_clashes(prefix,alternate_prefix))
765 alternate_database_clashes=TRUE;
766 remove_count=g_slist_length(
767 plover_package_set_get_packages(helper->alternate_installed));
771 * Rather than try to be too clever, we only deal with one thing
772 * at a time. That means that if the alternate database clashes
773 * there's no point checking if the active database is compatible.
775 if (!alternate_database_clashes)
777 if (helper->check_vendor && prefix &&
778 !plover_package_set_files_match_prefix(helper->installed,prefix))
780 active_database_is_incompatible=TRUE;
782 g_slist_length(plover_package_set_get_packages(helper->installed));
785 if (alternate_database_clashes || active_database_is_incompatible)
787 g_assert(comps!=NULL);
788 label=GTK_LABEL(gtk_builder_get_object(helper->ui,
789 "SIIncompatibleInstallationLabel"));
790 if (alternate_database_clashes)
791 s=g_strdup_printf("<b>Incompatible Installation</b>\n\n"
792 "There is an existing installation under %s\n"
793 "which is not compatible with this distribution. In order\n"
794 "to continue, the existing installation must be uninstalled.",
796 else /* active_database_is_incompatible */
797 s=g_strdup_printf("<b>Incompatible Installation</b>\n\n"
798 "The existing installation is not from %s.\n In order "
799 "to continue, all the existing packages must be removed.",
801 gtk_label_set_markup(label,s);
803 s=g_strdup_printf("Remove %d existing package%s",remove_count,
804 remove_count==1?"":"s");
805 gtk_button_set_label(button,s);
807 gtk_widget_show(container);
808 gtk_widget_hide(summary);
809 if (helper->assistant)
810 gtk_assistant_set_page_complete(helper->assistant,page,FALSE);
814 gtk_widget_hide(container);
815 gtk_widget_show(summary);
816 if (helper->assistant)
817 gtk_assistant_set_page_complete(helper->assistant,page,TRUE);
822 void plover_transaction_helper_set_check_vendor(PloverTransactionHelper *helper,
823 gboolean check_vendor)
825 g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper));
826 if (helper->check_vendor!=check_vendor)
828 helper->check_vendor=check_vendor;
829 if (helper->transactions)
830 plover_transaction_helper_update_summary_page(helper,NULL);
835 * If plover_transaction_helper_add_transaction() fails with an error
836 * of PLOVER_GENERAL_ERROR,PLOVER_GENERAL_ERROR_REQUIREMENTS_NOT_MET
837 * then plover_transaction_helper_get_unsatisfied() can be used to
838 * retrieve a textual description of the problem.
842 plover_transaction_helper_get_unsatisfied(PloverTransactionHelper *helper)
844 g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL);
845 return helper->unsatisfied;
848 #define PLOVER_TRANSACTION_HELPER_IS_VALID_REPORT_ACTION(action) \
849 ((action)==PLOVER_TRANSACTION_HELPER_REPORT_INSTALL || \
850 (action)==PLOVER_TRANSACTION_HELPER_REPORT_REMOVE || \
851 (action)==PLOVER_TRANSACTION_HELPER_REPORT_UPDATE)
854 plover_transaction_helper_add_transaction(PloverTransactionHelper *helper,
855 PloverTransaction *transaction,struct plover_vector *report_packages,
856 PloverTransactionHelperReportAction report_action,GError **error)
859 gboolean other_packages;
861 enum razor_install_action razor_action;
862 PloverTransactionHelperReportAction action;
863 struct razor_install_iterator *ii;
864 struct razor_set *report_set;
865 struct razor_package *package;
866 struct plover_vector *tasked_packages;
867 PloverTransactionHelperPrivate *priv;
869 g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE);
870 g_return_val_if_fail(PLOVER_IS_TRANSACTION(transaction),FALSE);
871 g_return_val_if_fail(PLOVER_TRANSACTION_HELPER_IS_VALID_REPORT_ACTION(report_action),FALSE);
872 g_return_val_if_fail(plover_transaction_get_system_set(transaction)!=NULL,FALSE);
873 priv=PLOVER_TRANSACTION_HELPER_GET_PRIVATE(helper);
874 g_free(helper->unsatisfied);
875 helper->unsatisfied=NULL;
876 if (!plover_transaction_resolve(transaction,error))
878 s=plover_transaction_get_unsatisfied(transaction);
879 helper->unsatisfied=g_strdup(s);
882 ii=plover_transaction_get_install_iterator(transaction,error);
885 if (report_action==PLOVER_TRANSACTION_HELPER_REPORT_REMOVE)
886 report_set=plover_transaction_get_system_set(transaction);
888 report_set=plover_transaction_get_next_set(transaction,error);
891 tasked_packages=plover_vector_new();
892 other_packages=FALSE;
893 while (razor_install_iterator_next(ii,&package,&razor_action,&count))
895 if (razor_action==RAZOR_INSTALL_ACTION_ADD)
896 action=PLOVER_TRANSACTION_HELPER_REPORT_INSTALL;
897 else if (razor_action==RAZOR_INSTALL_ACTION_REMOVE)
898 action=PLOVER_TRANSACTION_HELPER_REPORT_REMOVE;
901 if (action==report_action || razor_action==RAZOR_INSTALL_ACTION_ADD &&
902 report_action==PLOVER_TRANSACTION_HELPER_REPORT_UPDATE)
904 razor_package_get_details(report_set,package,RAZOR_DETAIL_NAME,
905 &name,RAZOR_DETAIL_LAST);
906 if (!report_packages ||
907 plover_vector_contains(report_packages,name))
908 plover_vector_append(tasked_packages,name);
913 if (!tasked_packages->len)
916 * If there are no reportable packages tasked for action there
917 * shouldn't be any packages at all, but let's be paranoid.
919 other_packages=FALSE;
920 razor_install_iterator_rewind(ii);
921 while (razor_install_iterator_next(ii,&package,&razor_action,&count))
923 if (razor_action==RAZOR_INSTALL_ACTION_ADD)
924 action=PLOVER_TRANSACTION_HELPER_REPORT_INSTALL;
925 else if (razor_action==RAZOR_INSTALL_ACTION_REMOVE)
926 action=PLOVER_TRANSACTION_HELPER_REPORT_REMOVE;
929 if (action==report_action ||
930 razor_action==RAZOR_INSTALL_ACTION_ADD &&
931 report_action==PLOVER_TRANSACTION_HELPER_REPORT_UPDATE)
933 razor_package_get_details(report_set,package,RAZOR_DETAIL_NAME,
934 &name,RAZOR_DETAIL_LAST);
935 plover_vector_append(tasked_packages,name);
939 if (!tasked_packages->len)
941 g_set_error(error,PLOVER_GENERAL_ERROR,
942 PLOVER_GENERAL_ERROR_NO_WORK,"Transaction includes no %s actions",
943 report_action==PLOVER_TRANSACTION_HELPER_REPORT_REMOVE?
945 plover_vector_free(tasked_packages);
948 if (!helper->transactions)
949 plover_transaction_helper_update_summary_page(helper,error);
950 g_object_ref(transaction);
951 helper->transactions=g_slist_append(helper->transactions,transaction);
952 if (report_action==PLOVER_TRANSACTION_HELPER_REPORT_REMOVE)
954 priv->transaction_type|=TRANSACTION_TYPE_REMOVE;
955 for(i=0;i<tasked_packages->len;i++)
957 s=tasked_packages->strings[i];
958 if (!plover_vector_contains(helper->report_removing,s))
959 plover_vector_append(helper->report_removing,s);
961 helper->report_removing_dependants|=other_packages;
965 if (report_action==PLOVER_TRANSACTION_HELPER_REPORT_UPDATE)
966 priv->transaction_type|=TRANSACTION_TYPE_REMOVE;
967 priv->transaction_type|=TRANSACTION_TYPE_INSTALL;
968 for(i=0;i<tasked_packages->len;i++)
970 s=tasked_packages->strings[i];
971 if (!plover_vector_contains(helper->report_adding,s))
972 plover_vector_append(helper->report_adding,s);
974 helper->report_adding_dependencies|=other_packages;
976 w=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIProgressLabel"));
977 switch(priv->transaction_type)
979 case TRANSACTION_TYPE_INSTALL:
980 gtk_label_set_markup(GTK_LABEL(w),
981 "<b>Installing the Software</b>\n\n"
982 "Please wait while the Installation Assistant "
983 "installs the software.\n"
984 "This may take several minutes.");
986 case TRANSACTION_TYPE_REMOVE:
987 gtk_label_set_markup(GTK_LABEL(w),
988 "<b>Removing Packages</b>\n\n"
989 "Please wait while the Installation Assistant "
990 "removes packages.\n"
991 "This may take several minutes.");
994 case TRANSACTION_TYPE_UPDATE:
995 gtk_label_set_markup(GTK_LABEL(w),
996 "<b>Updating the Software</b>\n\n"
997 "Please wait while the Installation Assistant "
998 "updates the software.\n"
999 "This may take several minutes.");
1002 plover_vector_free(tasked_packages);
1006 static PloverTransaction *
1007 plover_transaction_helper_new_transaction(PloverTransactionHelper *helper,
1011 const char *base_uri,*prefix;
1012 GError *tmp_error=NULL;
1013 PloverTransaction *transaction;
1014 g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL);
1015 g_return_val_if_fail(plover_transaction_helper_get_installed(helper) != NULL,NULL);
1016 prefix=plover_transaction_helper_get_prefix(helper,&tmp_error);
1019 g_propagate_error(error,tmp_error);
1022 transaction=plover_transaction_new();
1023 plover_transaction_set_prefix(transaction,prefix);
1024 plover_transaction_set_installed(transaction,helper->installed);
1025 if (helper->upstream)
1026 ok=plover_transaction_set_upstream(transaction,helper->upstream,error);
1029 base_uri=plover_transaction_helper_get_base_uri(helper);
1030 ok=plover_transaction_set_upstream_from_yum_uri(transaction,base_uri,
1035 g_object_unref(transaction);
1041 struct plover_vector *plover_transaction_helper_group_get_default_packages(
1042 PloverTransactionHelper *helper,const char *group,GError **error)
1045 struct comps *comps;
1046 struct comps_group *grp;
1047 struct comps_requirement *pkg;
1048 struct plover_vector *default_packages;
1049 g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE);
1050 comps=plover_transaction_helper_get_comps(helper,error);
1053 grp=plover_comps_lookup_group(comps,group);
1056 g_set_error(error,PLOVER_GENERAL_ERROR,
1057 PLOVER_GENERAL_ERROR_FAILED,"%s: group not found",group);
1060 default_packages=plover_vector_new();
1064 for(pkg=grp->packages;pkg;pkg=pkg->next)
1066 if (plover_vector_contains(default_packages,pkg->name))
1068 if (pkg->type==COMPS_REQUIREMENT_DEFAULT ||
1069 pkg->type==COMPS_REQUIREMENT_MANDATORY ||
1070 pkg->type==COMPS_REQUIREMENT_CONDITIONAL && pkg->requires &&
1071 plover_vector_contains(default_packages,pkg->requires))
1074 plover_vector_append(default_packages,pkg->name);
1078 return default_packages;
1082 * Returns TRUE if there is work to be done or FALSE if the packages are
1083 * already installed or on error.
1086 plover_transaction_helper_install_packages(PloverTransactionHelper *helper,
1087 struct plover_vector *packages,GError **error)
1090 PloverTransaction *transaction;
1091 g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE);
1092 g_return_val_if_fail(packages != NULL,FALSE);
1095 g_set_error(error,PLOVER_GENERAL_ERROR,
1096 PLOVER_GENERAL_ERROR_NO_WORK,"No packages listed to be installed");
1099 transaction=plover_transaction_helper_new_transaction(helper,error);
1102 if (!plover_transaction_install(transaction,packages->strings,error))
1104 g_object_unref(transaction);
1107 retval=plover_transaction_helper_add_transaction(helper,transaction,
1108 packages,PLOVER_TRANSACTION_HELPER_REPORT_INSTALL,error);
1109 g_object_unref(transaction);
1114 * Returns TRUE if there is work to be done or FALSE if the group is
1115 * already installed or on error.
1118 plover_transaction_helper_install_group(PloverTransactionHelper *helper,
1119 const char *group,GError **error)
1122 struct plover_vector *selected_packages;
1123 g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE);
1124 selected_packages=plover_transaction_helper_group_get_default_packages(
1125 helper,group,error);
1126 if (!selected_packages)
1128 if (!selected_packages->len)
1130 g_set_error(error,PLOVER_GENERAL_ERROR,
1131 PLOVER_GENERAL_ERROR_FAILED,"%s: no default packages",group);
1132 plover_vector_free(selected_packages);
1135 retval=plover_transaction_helper_install_packages(helper,selected_packages,
1137 plover_vector_free(selected_packages);
1142 * Returns TRUE if there is work to be done or FALSE if the group is
1143 * not installed or on error.
1145 gboolean plover_transaction_helper_remove_group(PloverTransactionHelper *helper,
1146 const char *group,GError **error)
1149 struct plover_vector *selected_packages;
1150 PloverTransaction *transaction;
1151 g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE);
1152 g_return_val_if_fail(plover_transaction_helper_get_installed(helper) != NULL,FALSE);
1153 selected_packages=plover_transaction_helper_group_get_default_packages(
1154 helper,group,error);
1155 if (!selected_packages)
1157 if (!selected_packages->len)
1159 g_set_error(error,PLOVER_GENERAL_ERROR,
1160 PLOVER_GENERAL_ERROR_FAILED,"%s: no default packages",group);
1161 plover_vector_free(selected_packages);
1164 transaction=plover_transaction_new();
1165 plover_transaction_set_installed(transaction,helper->installed);
1166 if (!plover_transaction_remove(transaction,selected_packages->strings,
1169 plover_vector_free(selected_packages);
1170 g_object_unref(transaction);
1173 retval=plover_transaction_helper_add_transaction(helper,transaction,
1174 NULL,PLOVER_TRANSACTION_HELPER_REPORT_REMOVE,error);
1175 g_object_unref(transaction);
1176 plover_vector_free(selected_packages);
1181 * Returns TRUE if there is work to be done or FALSE if the group is
1182 * empty or on error.
1184 * The default action is to:
1185 * - install (and update all) if any of the packages in @group are
1186 * missing or out of date,
1187 * - otherwise update all if any packages are out of date,
1188 * - otherwise remove ALL packages for distribution-local comps,
1189 * - otherwise remove all packages in @group, their dependants and leaves.
1191 gboolean plover_transaction_helper_default_action_on_group(
1192 PloverTransactionHelper *helper,const char *group,GError **error)
1194 gboolean distribution_local=FALSE,retval;
1195 GError *tmp_err=NULL;
1196 struct comps *comps;
1197 struct plover_vector *selected_packages;
1198 PloverTransaction *transaction;
1199 g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE);
1200 selected_packages=plover_transaction_helper_group_get_default_packages(
1201 helper,group,error);
1202 if (!selected_packages)
1204 if (!selected_packages->len)
1206 g_set_error(error,PLOVER_GENERAL_ERROR,
1207 PLOVER_GENERAL_ERROR_FAILED,"%s: no default packages",group);
1208 plover_vector_free(selected_packages);
1211 comps=plover_transaction_helper_get_comps(helper,NULL);
1212 if (comps && comps->database==COMPS_DATABASE_DISTRIBUTION_LOCAL)
1213 distribution_local=TRUE;
1214 transaction=plover_transaction_helper_new_transaction(helper,error);
1217 plover_vector_free(selected_packages);
1220 if (!plover_transaction_install_with_update_all(transaction,
1221 selected_packages->strings,error))
1223 g_object_unref(transaction);
1224 plover_vector_free(selected_packages);
1227 retval=plover_transaction_helper_add_transaction(helper,transaction,
1228 selected_packages,PLOVER_TRANSACTION_HELPER_REPORT_INSTALL,&tmp_err);
1229 g_object_unref(transaction);
1232 if (!g_error_matches(tmp_err,PLOVER_GENERAL_ERROR,
1233 PLOVER_GENERAL_ERROR_NO_WORK))
1235 g_propagate_error(error,tmp_err);
1236 plover_vector_free(selected_packages);
1239 g_clear_error(&tmp_err);
1240 transaction=plover_transaction_helper_new_transaction(helper,error);
1243 plover_vector_free(selected_packages);
1246 if (!plover_transaction_update(transaction,NULL,error))
1248 g_object_unref(transaction);
1249 plover_vector_free(selected_packages);
1252 retval=plover_transaction_helper_add_transaction(helper,transaction,
1253 selected_packages,PLOVER_TRANSACTION_HELPER_REPORT_UPDATE,&tmp_err);
1254 g_object_unref(transaction);
1257 if (!g_error_matches(tmp_err,PLOVER_GENERAL_ERROR,
1258 PLOVER_GENERAL_ERROR_NO_WORK))
1260 g_propagate_error(error,tmp_err);
1261 plover_vector_free(selected_packages);
1264 g_clear_error(&tmp_err);
1265 transaction=plover_transaction_helper_new_transaction(helper,error);
1268 plover_vector_free(selected_packages);
1271 if (!plover_transaction_remove_with_dependants_and_leaves(
1272 transaction,distribution_local?NULL:selected_packages->strings,
1275 g_object_unref(transaction);
1276 plover_vector_free(selected_packages);
1279 retval=plover_transaction_helper_add_transaction(helper,transaction,
1280 NULL,PLOVER_TRANSACTION_HELPER_REPORT_REMOVE,error);
1281 g_object_unref(transaction);
1284 plover_vector_free(selected_packages);
1289 * Returns TRUE if there is work to be done or FALSE if all updates have
1290 * already been applied or on error.
1292 gboolean plover_transaction_helper_update(PloverTransactionHelper *helper,
1296 PloverTransaction *transaction;
1297 g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE);
1298 transaction=plover_transaction_helper_new_transaction(helper,error);
1301 if (!plover_transaction_update(transaction,NULL,error))
1303 g_object_unref(transaction);
1306 retval=plover_transaction_helper_add_transaction(helper,transaction,
1307 NULL,PLOVER_TRANSACTION_HELPER_REPORT_UPDATE,error);
1308 g_object_unref(transaction);
1312 gboolean plover_transaction_helper_get_visible(PloverTransactionHelper *helper)
1314 g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE);
1315 if (helper->error_dialog)
1317 else if (!helper->assistant)
1320 return gtk_widget_get_visible(GTK_WIDGET(helper->assistant));
1323 void plover_transaction_helper_present(PloverTransactionHelper *helper)
1325 g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper));
1326 if (helper->error_dialog)
1327 gtk_window_present(GTK_WINDOW(helper->error_dialog));
1328 else if (helper->assistant)
1329 gtk_window_present(GTK_WINDOW(helper->assistant));
1333 plover_transaction_helper_error_dialog_response(GtkDialog *error_dialog,
1334 int response_id,PloverTransactionHelper *helper)
1336 g_signal_handlers_disconnect_by_data(error_dialog,helper);
1337 if ((GtkWidget *)error_dialog==helper->error_dialog)
1339 gtk_widget_destroy(helper->error_dialog);
1340 helper->error_dialog=NULL;
1341 if (helper->assistant)
1343 gtk_widget_hide(GTK_WIDGET(helper->assistant));
1344 gtk_assistant_set_current_page(helper->assistant,0);
1346 g_signal_emit(helper,signals[CLOSE],0);
1350 const char *plover_transaction_helper_get_error(PloverTransactionHelper *helper,
1351 const GError **error)
1353 g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL);
1354 if (!helper->error_dialog)
1357 *error=helper->error;
1358 return helper->error_primary_text;
1361 void plover_transaction_helper_set_error(PloverTransactionHelper *helper,
1362 const GError *error,const char *primary_text)
1364 GtkMessageType type;
1366 g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper));
1367 g_return_if_fail(error != NULL);
1368 g_return_if_fail(primary_text != NULL);
1369 if (helper->pulse_handler)
1371 g_source_remove(helper->pulse_handler);
1372 helper->pulse_handler=0;
1374 if (helper->error_dialog)
1376 gtk_widget_destroy(helper->error_dialog);
1377 helper->error_dialog=NULL;
1379 g_free(helper->error_primary_text);
1380 helper->error_primary_text=g_strdup(primary_text);
1381 g_clear_error(&helper->error);
1382 helper->error=g_error_copy(error);
1383 if (g_error_matches(error,PLOVER_GENERAL_ERROR,
1384 PLOVER_GENERAL_ERROR_NO_WORK))
1385 type=GTK_MESSAGE_INFO;
1387 type=GTK_MESSAGE_ERROR;
1388 if (helper->assistant)
1389 window=GTK_WINDOW(helper->assistant);
1392 helper->error_dialog=gtk_message_dialog_new(window,
1393 GTK_DIALOG_DESTROY_WITH_PARENT,type,GTK_BUTTONS_CLOSE,primary_text);
1394 gtk_message_dialog_format_secondary_text(
1395 GTK_MESSAGE_DIALOG(helper->error_dialog),error->message);
1396 gtk_widget_show(helper->error_dialog);
1397 g_signal_connect(helper->error_dialog,"response",
1398 G_CALLBACK(plover_transaction_helper_error_dialog_response),helper);