Fix bug causing a transaction without a base to be treated as a programming error
2 * Copyright (C) 2014, 2016, 2018 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 void plover_transaction_helper_finalize(PloverTransactionHelper *helper)
62 PloverTransactionHelperPrivate *priv;
63 priv=PLOVER_TRANSACTION_HELPER_GET_PRIVATE(helper);
64 g_free(priv->default_prefix);
65 g_free(helper->error_primary_text);
67 g_free(helper->unsatisfied);
69 plover_comps_free(helper->comps);
70 plover_vector_free(helper->report_adding);
71 plover_vector_free(helper->report_removing);
74 static void plover_transaction_helper_dispose(PloverTransactionHelper *helper)
76 g_clear_error(&helper->error);
77 if (helper->error_dialog)
79 g_signal_handlers_disconnect_by_data(helper->error_dialog,helper);
80 gtk_widget_destroy(helper->error_dialog);
81 helper->error_dialog=NULL;
83 if (helper->assistant)
85 g_signal_handlers_disconnect_by_data(helper->assistant,helper);
86 g_clear_object(&helper->assistant);
88 g_clear_object(&helper->ui);
89 g_slist_foreach(helper->transactions,(GFunc)g_object_unref,NULL);
90 g_slist_free(helper->transactions);
91 helper->transactions=NULL;
92 g_clear_object(&helper->alternate_installed);
93 g_clear_object(&helper->installed);
94 g_clear_object(&helper->upstream);
95 g_clear_object(&helper->relocated_upstream);
99 plover_transaction_helper_class_init(PloverTransactionHelperClass *klass)
101 GObjectClass *gobject_class=G_OBJECT_CLASS(klass);
102 gobject_class->finalize=
103 (void (*)(GObject *))plover_transaction_helper_finalize;
104 gobject_class->dispose=
105 (void (*)(GObject *))plover_transaction_helper_dispose;
106 g_type_class_add_private(klass,sizeof(PloverTransactionHelperPrivate));
107 signals[CLOSE]=g_signal_newv("close",
108 G_TYPE_FROM_CLASS(klass),G_SIGNAL_RUN_LAST,NULL,NULL,NULL,
109 g_cclosure_marshal_VOID__VOID,G_TYPE_NONE,0,NULL);
112 static void plover_transaction_helper_init(PloverTransactionHelper *helper)
114 helper->report_adding=plover_vector_new();
115 helper->report_removing=plover_vector_new();
118 static void plover_transaction_helper_assistant_cancel(GtkAssistant *assistant,
119 PloverTransactionHelper *helper)
121 gtk_widget_hide(GTK_WIDGET(helper->assistant));
122 gtk_assistant_set_current_page(helper->assistant,0);
123 g_signal_emit(helper,signals[CLOSE],0);
126 static void plover_transaction_helper_assistant_close(GtkAssistant *assistant,
127 PloverTransactionHelper *helper)
129 gtk_widget_hide(GTK_WIDGET(helper->assistant));
130 gtk_assistant_set_current_page(helper->assistant,0);
131 g_signal_emit(helper,signals[CLOSE],0);
135 plover_transaction_helper_prepare_confirm(PloverTransactionHelper *helper)
137 gchar *package_list,*add,*remove,*s;
139 struct plover_vector *report;
140 if (helper->report_adding->len)
142 plover_vector_sort(helper->report_adding);
143 if (helper->report_adding_dependencies)
145 report=plover_vector_dup(helper->report_adding);
146 if (helper->report_adding->len==1)
147 plover_vector_append(report,"its dependencies");
149 plover_vector_append(report,"their dependencies");
150 package_list=plover_vector_format_for_display(report);
151 plover_vector_free(report);
155 plover_vector_format_for_display(helper->report_adding);
156 add=g_strconcat("Packages to be installed or updated: ",package_list,
158 g_free(package_list);
162 if (helper->report_removing->len)
164 plover_vector_sort(helper->report_removing);
165 if (helper->report_removing_dependants)
167 report=plover_vector_dup(helper->report_removing);
168 if (helper->report_adding->len==1)
169 plover_vector_append(report,"its dependants");
171 plover_vector_append(report,"their dependants");
172 package_list=plover_vector_format_for_display(report);
173 plover_vector_free(report);
177 plover_vector_format_for_display(helper->report_removing);
178 remove=g_strconcat("Packages to be removed: ",package_list,".",NULL);
179 g_free(package_list);
183 label=GTK_LABEL(gtk_builder_get_object(helper->ui,"SISummaryOfWork"));
185 s=g_strconcat("<b>Installation Summary</b>\n\n",remove,"\n\n",add,NULL);
186 else if (add || remove)
187 s=g_strconcat("<b>Installation Summary</b>\n\n",add?add:remove,NULL);
189 s=g_strdup("<b>Installation Summary</b>\n\nNo changes scheduled");
190 gtk_label_set_markup(label,s);
196 static void plover_transaction_helper_run(PloverTransactionHelper *helper);
198 static void plover_transaction_helper_callback(GObject *source,
199 GAsyncResult *result,gpointer user_data)
202 PloverTransactionHelper *helper=user_data;
203 PloverTransaction *transaction=PLOVER_TRANSACTION(source);
204 if (!plover_transaction_commit_finish(transaction,result,&error))
206 plover_transaction_helper_set_error(helper,error,
207 "Software installation failed");
211 plover_transaction_helper_run(helper);
213 * There may be status updates queued by transaction as idle events.
214 * Process them now before we disconnect so that we don't lose them.
216 while(g_main_context_pending(NULL))
217 g_main_context_iteration(NULL,FALSE);
218 g_signal_handlers_disconnect_by_data(transaction,helper);
219 g_object_unref(transaction);
222 static void plover_transaction_helper_transaction_status_changed(
223 PloverTransaction *transaction,const char *status,
224 PloverTransactionHelper *helper)
227 bar=GTK_PROGRESS_BAR(gtk_builder_get_object(helper->ui,"SIProgressBar"));
228 gtk_progress_bar_set_text(bar,status);
231 static void plover_transaction_helper_run(PloverTransactionHelper *helper)
233 PloverTransaction *transaction;
235 page=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIProgress"));
236 if (helper->transactions)
238 if (helper->assistant)
239 gtk_assistant_set_page_complete(helper->assistant,page,FALSE);
240 transaction=helper->transactions->data;
241 helper->transactions=g_slist_delete_link(helper->transactions,
242 helper->transactions);
243 g_signal_connect(transaction,"status-changed",
244 G_CALLBACK(plover_transaction_helper_transaction_status_changed),
246 plover_transaction_commit_async(transaction,NULL,
247 plover_transaction_helper_callback,helper);
249 else if (helper->assistant)
250 gtk_assistant_set_page_complete(helper->assistant,page,TRUE);
253 static gboolean plover_transaction_helper_pulse(gpointer user_data)
255 PloverTransactionHelper *helper=user_data;
258 if (!helper->assistant)
260 w=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIProgress"));
261 bar=GTK_PROGRESS_BAR(gtk_builder_get_object(helper->ui,"SIProgressBar"));
262 if (gtk_assistant_get_page_complete(helper->assistant,w))
264 gtk_progress_bar_set_fraction(bar,1.0);
265 helper->pulse_handler=0;
270 gtk_progress_bar_pulse(bar);
276 plover_transaction_helper_prepare_progress(PloverTransactionHelper *helper)
279 GtkToggleButton *button;
280 PloverTransaction *transaction;
281 GSList *save_transactions;
282 PloverTransactionHelperPrivate *priv;
283 enum plover_transaction_type save_transaction_type;
284 priv=PLOVER_TRANSACTION_HELPER_GET_PRIVATE(helper);
285 button=GTK_TOGGLE_BUTTON(gtk_builder_get_object(helper->ui,
286 "SIRemoveExisting"));
287 if (gtk_toggle_button_get_active(button))
289 transaction=plover_transaction_new_remove(NULL,&error);
292 save_transactions=helper->transactions;
293 helper->transactions=NULL;
294 save_transaction_type=priv->transaction_type;
295 priv->transaction_type=0;
296 if (!plover_transaction_helper_add_transaction(helper,transaction,
297 NULL,PLOVER_TRANSACTION_HELPER_REPORT_REMOVE,&error))
299 g_object_unref(transaction);
301 helper->transactions=save_transactions;
302 priv->transaction_type=save_transaction_type;
306 g_slist_foreach(save_transactions,(GFunc)g_object_unref,NULL);
307 g_slist_free(save_transactions);
312 if (g_error_matches(error,PLOVER_POSIX_ERROR,ENOENT))
313 g_clear_error(&error);
316 plover_transaction_helper_set_error(helper,error,
317 "Failed to remove existing packages");
324 * Note that PloverTransaction does support cancelling a transaction, but
325 * there are a number of challenges with using it:
326 * - cancellation is only supported during the file phase if razor
327 * has atomic rollback,
328 * - cancellation is not supported during post-transaction scripts at all
329 * (since by the time the first script is started the atomic has already
330 * been committed) and these can take quite some time,
331 * - where a transaction has an embedded COMMIT, any rollback won't
332 * go back beyond this point.
333 * To support user-cancel, then, we would need some mechanism to:
334 * - Comunicate that the operation is being cancelled and this may take
336 * - Not allow cancellation at all after the last post-transaction script
338 * - Report the partially completed transaction where cancellation
339 * occurred after a COMMIT point.
340 * At present, this doesn't appear worth the effort.
342 if (helper->assistant)
343 gtk_assistant_commit(helper->assistant);
344 plover_transaction_helper_run(helper);
345 helper->pulse_handler=g_timeout_add(100,plover_transaction_helper_pulse,
349 static void plover_transaction_helper_assistant_prepare(GtkAssistant *assistant,
350 GtkWidget *page,PloverTransactionHelper *helper)
352 if (page==GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIConfirm")))
353 plover_transaction_helper_prepare_confirm(helper);
354 else if (page==GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIProgress")))
355 plover_transaction_helper_prepare_progress(helper);
359 plover_transaction_helper_remove_existing_toggled(GtkToggleButton *button,
360 PloverTransactionHelper *helper)
363 if (helper->assistant)
365 w=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIConfirm"));
366 gtk_assistant_set_page_complete(helper->assistant,w,
367 gtk_toggle_button_get_active(button));
371 PloverTransactionHelper *plover_transaction_helper_new(GtkBuilder *ui)
374 gchar *s,*directory,*contents;
377 PloverTransactionHelper *helper;
378 g_return_val_if_fail(ui == NULL || GTK_IS_BUILDER(ui),NULL);
379 helper=PLOVER_TRANSACTION_HELPER(
380 g_object_new(PLOVER_TYPE_TRANSACTION_HELPER,NULL));
382 helper->ui=g_object_ref(ui);
384 helper->ui=gtk_builder_new();
386 GTK_ASSISTANT(gtk_builder_get_object(helper->ui,"SoftwareInstallation"));
387 if (!helper->assistant)
389 directory=g_strdup(g_getenv("PLOVER_DATADIR"));
393 s=g_win32_get_package_installation_directory_of_module(NULL);
394 directory=g_build_filename(s,"share","plover",NULL);
397 directory=g_strdup(PLOVER_DATADIR);
400 s=g_build_filename(directory,"software-installation.ui",NULL);
402 (void)g_file_get_contents(s,&contents,&len,&error);
406 (void)gtk_builder_add_from_string(helper->ui,contents,len,&error);
411 g_critical("software-installation.ui: %s",error->message);
412 g_clear_error(&error);
413 g_set_error(&error,PLOVER_GENERAL_ERROR,PLOVER_GENERAL_ERROR_FAILED,
414 "Internal error (no user interface)");
415 plover_transaction_helper_set_error(helper,error,
416 "Can't start installer");
419 helper->assistant=GTK_ASSISTANT(gtk_builder_get_object(helper->ui,
420 "SoftwareInstallation"));
422 if (!helper->assistant)
424 g_critical("\"SoftwareInstallation\" object not found");
425 g_set_error(&error,PLOVER_GENERAL_ERROR,PLOVER_GENERAL_ERROR_FAILED,
426 "Internal error (missing wizard)");
427 plover_transaction_helper_set_error(helper,error,
428 "Can't start installer");
433 g_object_ref(helper->assistant);
434 if (!GTK_IS_ASSISTANT(helper->assistant))
436 g_critical("\"SoftwareInstallation\" is not a GtkAssistant");
437 g_set_error(&error,PLOVER_GENERAL_ERROR,PLOVER_GENERAL_ERROR_FAILED,
438 "Internal error (unexpected wizard type)");
439 plover_transaction_helper_set_error(helper,error,
440 "Can't start installer");
444 g_signal_connect(helper->assistant,"cancel",
445 G_CALLBACK(plover_transaction_helper_assistant_cancel),helper);
446 g_signal_connect(helper->assistant,"close",
447 G_CALLBACK(plover_transaction_helper_assistant_close),helper);
448 g_signal_connect(helper->assistant,"prepare",
449 G_CALLBACK(plover_transaction_helper_assistant_prepare),helper);
450 w=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIRemoveExisting"));
452 g_signal_connect(w,"toggled",
453 G_CALLBACK(plover_transaction_helper_remove_existing_toggled),helper);
454 w=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIIntroduction"));
456 gtk_assistant_set_page_complete(helper->assistant,w,TRUE);
461 plover_transaction_helper_get_installed(PloverTransactionHelper *helper)
463 gchar *s,*saved_database_uri;
464 char *install_root,*local_database,*active_database,*alternate_database;
467 PloverPackageSet *alternate_installed,*installed;
469 struct razor_error *razor_error=NULL;
470 g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL);
471 if (!helper->installed)
473 comps=plover_transaction_helper_get_comps(helper,NULL);
476 g_warning("plover_transaction_helper_get_installed: No comps");
479 install_root=getenv("RAZOR_ROOT");
481 install_root="file:/";
482 prefix=plover_transaction_helper_get_prefix(helper,NULL);
485 s=g_strconcat(prefix,"/var/lib/razor",NULL);
486 local_database=razor_path_relative_to_uri(install_root,*s=='/'?s+1:s,
491 g_warning("plover_transaction_helper_get_installed: %s",
492 razor_error_get_msg(razor_error));
493 razor_error_free(razor_error);
499 switch(comps->database)
501 case COMPS_DATABASE_DISTRIBUTION_LOCAL:
502 active_database=local_database;
503 alternate_database=NULL;
505 case COMPS_DATABASE_GLOBAL:
506 active_database=NULL;
507 alternate_database=local_database;
510 saved_database_uri=g_strdup(razor_get_database_uri());
513 razor_set_database_uri(alternate_database);
514 alternate_installed=plover_package_set_new();
515 if (!plover_package_set_open(alternate_installed,install_root,TRUE,
518 g_object_unref(alternate_installed);
519 g_warning("plover_transaction_helper_get_installed: %s",
522 free(local_database);
523 razor_set_database_uri(saved_database_uri);
524 g_free(saved_database_uri);
529 alternate_installed=NULL;
530 razor_set_database_uri(active_database);
531 free(local_database);
532 installed=plover_package_set_new();
533 if (plover_package_set_open(installed,install_root,TRUE,&error))
535 helper->alternate_installed=alternate_installed;
536 helper->installed=installed;
540 g_object_unref(installed);
541 if (alternate_installed)
542 g_object_unref(alternate_installed);
543 g_warning("plover_transaction_helper_get_installed: %s",error->message);
546 razor_set_database_uri(saved_database_uri);
547 g_free(saved_database_uri);
549 return helper->installed;
552 void plover_transaction_helper_set_installed(PloverTransactionHelper *helper,
553 PloverPackageSet *installed)
555 g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper));
556 g_return_if_fail(PLOVER_IS_PACKAGE_SET(installed));
557 g_return_if_fail(helper->installed == NULL);
558 g_clear_object(&helper->alternate_installed);
559 helper->installed=g_object_ref(installed);
563 plover_transaction_helper_get_upstream(PloverTransactionHelper *helper,
567 g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL);
568 if (!helper->upstream)
570 base=plover_transaction_helper_get_base(helper);
571 helper->upstream=plover_repository_new_from_yum(base,error);
573 return helper->upstream;
576 void plover_transaction_helper_set_upstream(PloverTransactionHelper *helper,
577 PloverRepository *upstream)
579 g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper));
580 g_return_if_fail(PLOVER_IS_REPOSITORY(upstream));
581 g_return_if_fail(helper->upstream == NULL);
582 helper->upstream=g_object_ref(upstream);
585 const char *plover_transaction_helper_get_base(PloverTransactionHelper *helper)
587 g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL);
591 void plover_transaction_helper_set_base(PloverTransactionHelper *helper,
594 g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper));
595 g_return_if_fail(helper->transactions == NULL);
596 g_free(helper->base);
597 helper->base=g_strdup(base);
601 plover_transaction_helper_get_comps(PloverTransactionHelper *helper,
605 g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL);
606 if (!helper->comps && helper->base)
608 s=g_strconcat(helper->base,"/repodata/comps.xml",NULL);
609 helper->comps=plover_comps_new_from_file(s);
611 g_set_error(error,PLOVER_GENERAL_ERROR,
612 PLOVER_GENERAL_ERROR_FAILED,"%s: %s",s,g_strerror(errno));
615 return helper->comps;
619 plover_transaction_helper_get_prefix(PloverTransactionHelper *helper,
624 PloverTransactionHelperPrivate *priv;
625 g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL);
626 g_return_val_if_fail(helper->base != NULL || plover_transaction_helper_get_installed(helper) != NULL,NULL);
627 priv=PLOVER_TRANSACTION_HELPER_GET_PRIVATE(helper);
630 comps=plover_transaction_helper_get_comps(helper,error);
633 g_free(priv->default_prefix);
634 priv->default_prefix=plover_comps_get_default_prefix(comps);
635 return priv->default_prefix;
637 prefix=plover_package_set_guess_prefix(helper->installed,error);
642 static int plover_transaction_helper_package_count(void)
646 struct razor_set *set;
647 struct razor_package *package;
648 struct razor_package_iterator *pi;
649 install_root=getenv("RAZOR_ROOT");
652 set=razor_root_open_read_only(install_root,NULL);
655 pi=razor_package_iterator_create(set);
656 while (razor_package_iterator_next(pi,&package,RAZOR_DETAIL_LAST))
658 razor_package_iterator_destroy(pi);
659 razor_set_unref(set);
665 static gboolean prefix_clashes(const char *prefix,const char *alt)
667 return g_str_has_prefix(prefix,alt) &&
668 (prefix[strlen(alt)]=='\0' || prefix[strlen(alt)]=='/');
672 plover_transaction_helper_check_vendor(PloverTransactionHelper *helper,
675 int i,remove_count=0;
676 gboolean alternate_database_clashes=FALSE;
677 gboolean active_database_is_incompatible=FALSE;
678 char *local_database,*active_database,*alternate_database;
679 const char *alternate_prefix;
680 gchar *prefix=NULL,*s;
681 struct comps *comps=NULL;
682 GtkWidget *container,*summary,*page;
685 g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE);
686 comps=plover_transaction_helper_get_comps(helper,error);
689 prefix=plover_comps_get_default_prefix(comps);
690 button=GTK_BUTTON(gtk_builder_get_object(helper->ui,"SIRemoveExisting"));
691 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),FALSE);
692 container=GTK_WIDGET(gtk_builder_get_object(helper->ui,
693 "SIIncompatibleInstallation"));
694 summary=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SISummaryOfWork"));
695 page=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIConfirm"));
696 if (helper->check_vendor && prefix && helper->alternate_installed)
699 plover_package_set_guess_prefix(helper->alternate_installed,NULL);
700 if (alternate_prefix && prefix_clashes(prefix,alternate_prefix))
702 alternate_database_clashes=TRUE;
703 remove_count=g_slist_length(
704 plover_package_set_get_packages(helper->alternate_installed));
708 * Rather than try to be too clever, we only deal with one thing
709 * at a time. That means that if the alternate database clashes
710 * there's no point checking if the active database is compatible.
712 if (!alternate_database_clashes)
714 if (helper->check_vendor && prefix &&
715 !plover_package_set_files_match_prefix(helper->installed,prefix))
717 active_database_is_incompatible=TRUE;
719 g_slist_length(plover_package_set_get_packages(helper->installed));
722 if (alternate_database_clashes || active_database_is_incompatible)
724 label=GTK_LABEL(gtk_builder_get_object(helper->ui,
725 "SIIncompatibleInstallationLabel"));
726 if (alternate_database_clashes)
727 s=g_strdup_printf("<b>Incompatible Installation</b>\n\n"
728 "There is an existing installation under %s\n"
729 "which is not compatible with this distribution. In order\n"
730 "to continue, the existing installation must be uninstalled.",
732 else /* active_database_is_incompatible */
733 s=g_strdup_printf("<b>Incompatible Installation</b>\n\n"
734 "The existing installation is not from %s.\n In order "
735 "to continue, all the existing packages must be removed.",
737 gtk_label_set_markup(label,s);
739 s=g_strdup_printf("Remove %d existing package%s",remove_count,
740 remove_count==1?"":"s");
741 gtk_button_set_label(button,s);
743 gtk_widget_show(container);
744 gtk_widget_hide(summary);
745 if (helper->assistant)
746 gtk_assistant_set_page_complete(helper->assistant,page,FALSE);
750 gtk_widget_hide(container);
751 gtk_widget_show(summary);
752 if (helper->assistant)
753 gtk_assistant_set_page_complete(helper->assistant,page,TRUE);
759 void plover_transaction_helper_set_check_vendor(PloverTransactionHelper *helper,
760 gboolean check_vendor)
762 g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper));
763 if (helper->check_vendor!=check_vendor)
765 helper->check_vendor=check_vendor;
766 if (helper->transactions)
767 plover_transaction_helper_check_vendor(helper,NULL);
772 * If plover_transaction_helper_add_transaction() fails with an error
773 * of PLOVER_GENERAL_ERROR,PLOVER_GENERAL_ERROR_REQUIREMENTS_NOT_MET
774 * then plover_transaction_helper_get_unsatisfied() can be used to
775 * retrieve a textual description of the problem.
779 plover_transaction_helper_get_unsatisfied(PloverTransactionHelper *helper)
781 g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL);
782 return helper->unsatisfied;
785 #define PLOVER_TRANSACTION_HELPER_IS_VALID_REPORT_ACTION(action) \
786 ((action)==PLOVER_TRANSACTION_HELPER_REPORT_INSTALL || \
787 (action)==PLOVER_TRANSACTION_HELPER_REPORT_REMOVE || \
788 (action)==PLOVER_TRANSACTION_HELPER_REPORT_UPDATE)
791 plover_transaction_helper_add_transaction(PloverTransactionHelper *helper,
792 PloverTransaction *transaction,struct plover_vector *report_packages,
793 PloverTransactionHelperReportAction report_action,GError **error)
796 gboolean other_packages;
798 enum razor_install_action action;
799 struct razor_install_iterator *ii;
800 struct razor_set *report_set;
801 struct razor_package *package;
802 struct plover_vector *tasked_packages;
803 PloverTransactionHelperPrivate *priv;
805 g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE);
806 g_return_val_if_fail(PLOVER_IS_TRANSACTION(transaction),FALSE);
807 g_return_val_if_fail(PLOVER_TRANSACTION_HELPER_IS_VALID_REPORT_ACTION(report_action),FALSE);
808 g_return_val_if_fail(plover_transaction_get_system_set(transaction)!=NULL,FALSE);
809 priv=PLOVER_TRANSACTION_HELPER_GET_PRIVATE(helper);
810 g_free(helper->unsatisfied);
811 helper->unsatisfied=NULL;
812 if (!plover_transaction_resolve(transaction,error))
814 s=plover_transaction_get_unsatisfied(transaction);
815 helper->unsatisfied=g_strdup(s);
818 ii=plover_transaction_get_install_iterator(transaction,error);
821 if (report_action==PLOVER_TRANSACTION_HELPER_REPORT_REMOVE)
822 report_set=plover_transaction_get_system_set(transaction);
824 report_set=plover_transaction_get_next_set(transaction,error);
827 tasked_packages=plover_vector_new();
828 other_packages=FALSE;
829 while (razor_install_iterator_next(ii,&package,&action,&count))
831 if (action==report_action || action==RAZOR_INSTALL_ACTION_ADD &&
832 report_action==PLOVER_TRANSACTION_HELPER_REPORT_UPDATE)
834 razor_package_get_details(report_set,package,RAZOR_DETAIL_NAME,
835 &name,RAZOR_DETAIL_LAST);
836 if (!report_packages ||
837 plover_vector_contains(report_packages,name))
838 plover_vector_append(tasked_packages,name);
843 if (!tasked_packages->len)
846 * If there are no reportable packages tasked for action there
847 * shouldn't by any packages at all, but let's be paranoid.
849 other_packages=FALSE;
850 razor_install_iterator_rewind(ii);
851 while (razor_install_iterator_next(ii,&package,&action,&count))
853 if (action==report_action)
855 razor_package_get_details(report_set,package,RAZOR_DETAIL_NAME,
856 &name,RAZOR_DETAIL_LAST);
857 plover_vector_append(tasked_packages,name);
861 if (!tasked_packages->len)
863 g_set_error(error,PLOVER_GENERAL_ERROR,
864 PLOVER_GENERAL_ERROR_NO_WORK,"Transaction includes no %s actions",
865 report_action==PLOVER_TRANSACTION_HELPER_REPORT_REMOVE?
867 plover_vector_free(tasked_packages);
870 if (!helper->transactions)
871 plover_transaction_helper_check_vendor(helper,error);
872 g_object_ref(transaction);
873 helper->transactions=g_slist_append(helper->transactions,transaction);
874 if (report_action==PLOVER_TRANSACTION_HELPER_REPORT_REMOVE)
876 priv->transaction_type|=TRANSACTION_TYPE_REMOVE;
877 for(i=0;i<tasked_packages->len;i++)
879 s=tasked_packages->strings[i];
880 if (!plover_vector_contains(helper->report_removing,s))
881 plover_vector_append(helper->report_removing,s);
883 helper->report_removing_dependants|=other_packages;
887 if (report_action==PLOVER_TRANSACTION_HELPER_REPORT_UPDATE)
888 priv->transaction_type|=TRANSACTION_TYPE_REMOVE;
889 priv->transaction_type|=TRANSACTION_TYPE_INSTALL;
890 for(i=0;i<tasked_packages->len;i++)
892 s=tasked_packages->strings[i];
893 if (!plover_vector_contains(helper->report_adding,s))
894 plover_vector_append(helper->report_adding,s);
896 helper->report_adding_dependencies|=other_packages;
898 w=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIProgressLabel"));
899 switch(priv->transaction_type)
901 case TRANSACTION_TYPE_INSTALL:
902 gtk_label_set_markup(GTK_LABEL(w),
903 "<b>Installing the Software</b>\n\n"
904 "Please wait while the Installation Assistant "
905 "installs the software.\n"
906 "This may take several minutes.");
908 case TRANSACTION_TYPE_REMOVE:
909 gtk_label_set_markup(GTK_LABEL(w),
910 "<b>Removing Packages</b>\n\n"
911 "Please wait while the Installation Assistant "
912 "removes packages.\n"
913 "This may take several minutes.");
916 case TRANSACTION_TYPE_UPDATE:
917 gtk_label_set_markup(GTK_LABEL(w),
918 "<b>Updating the Software</b>\n\n"
919 "Please wait while the Installation Assistant "
920 "updates the software.\n"
921 "This may take several minutes.");
924 plover_vector_free(tasked_packages);
928 static PloverTransaction *
929 plover_transaction_helper_new_transaction(PloverTransactionHelper *helper,
933 const char *base,*prefix;
934 GError *tmp_error=NULL;
935 PloverTransaction *transaction;
936 g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL);
937 g_return_val_if_fail(plover_transaction_helper_get_installed(helper) != NULL,NULL);
938 prefix=plover_transaction_helper_get_prefix(helper,&tmp_error);
941 g_propagate_error(error,tmp_error);
944 transaction=plover_transaction_new();
945 plover_transaction_set_prefix(transaction,prefix);
946 plover_transaction_set_installed(transaction,helper->installed);
947 if (helper->upstream)
948 ok=plover_transaction_set_upstream(transaction,helper->upstream,error);
951 base=plover_transaction_helper_get_base(helper);
952 ok=plover_transaction_set_upstream_from_yum(transaction,base,error);
956 g_object_unref(transaction);
962 struct plover_vector *plover_transaction_helper_group_get_default_packages(
963 PloverTransactionHelper *helper,const char *group,GError **error)
967 struct comps_group *grp;
968 struct comps_requirement *pkg;
969 struct plover_vector *default_packages;
970 g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE);
971 comps=plover_transaction_helper_get_comps(helper,error);
974 grp=plover_comps_lookup_group(comps,group);
977 g_set_error(error,PLOVER_GENERAL_ERROR,
978 PLOVER_GENERAL_ERROR_FAILED,"%s: group not found",group);
981 default_packages=plover_vector_new();
985 for(pkg=grp->packages;pkg;pkg=pkg->next)
987 if (plover_vector_contains(default_packages,pkg->name))
989 if (pkg->type==COMPS_REQUIREMENT_DEFAULT ||
990 pkg->type==COMPS_REQUIREMENT_MANDATORY ||
991 pkg->type==COMPS_REQUIREMENT_CONDITIONAL && pkg->requires &&
992 plover_vector_contains(default_packages,pkg->requires))
995 plover_vector_append(default_packages,pkg->name);
999 return default_packages;
1003 * Returns TRUE if there is work to be done or FALSE if the packages are
1004 * already installed or on error.
1007 plover_transaction_helper_install_packages(PloverTransactionHelper *helper,
1008 struct plover_vector *packages,GError **error)
1011 PloverTransaction *transaction;
1012 g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE);
1013 g_return_val_if_fail(packages != NULL,FALSE);
1016 g_set_error(error,PLOVER_GENERAL_ERROR,
1017 PLOVER_GENERAL_ERROR_NO_WORK,"No packages listed to be installed");
1020 transaction=plover_transaction_helper_new_transaction(helper,error);
1023 if (!plover_transaction_install(transaction,packages->strings,error))
1025 g_object_unref(transaction);
1028 retval=plover_transaction_helper_add_transaction(helper,transaction,
1029 packages,PLOVER_TRANSACTION_HELPER_REPORT_INSTALL,error);
1030 g_object_unref(transaction);
1035 * Returns TRUE if there is work to be done or FALSE if the group is
1036 * already installed or on error.
1039 plover_transaction_helper_install_group(PloverTransactionHelper *helper,
1040 const char *group,GError **error)
1043 struct plover_vector *selected_packages;
1044 g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE);
1045 selected_packages=plover_transaction_helper_group_get_default_packages(
1046 helper,group,error);
1047 if (!selected_packages)
1049 if (!selected_packages->len)
1051 g_set_error(error,PLOVER_GENERAL_ERROR,
1052 PLOVER_GENERAL_ERROR_FAILED,"%s: no default packages",group);
1053 plover_vector_free(selected_packages);
1056 retval=plover_transaction_helper_install_packages(helper,selected_packages,
1058 plover_vector_free(selected_packages);
1063 * Returns TRUE if there is work to be done or FALSE if the group is
1064 * not installed or on error.
1066 gboolean plover_transaction_helper_remove_group(PloverTransactionHelper *helper,
1067 const char *group,GError **error)
1070 struct plover_vector *selected_packages;
1071 PloverTransaction *transaction;
1072 g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE);
1073 g_return_val_if_fail(plover_transaction_helper_get_installed(helper) != NULL,FALSE);
1074 selected_packages=plover_transaction_helper_group_get_default_packages(
1075 helper,group,error);
1076 if (!selected_packages)
1078 if (!selected_packages->len)
1080 g_set_error(error,PLOVER_GENERAL_ERROR,
1081 PLOVER_GENERAL_ERROR_FAILED,"%s: no default packages",group);
1082 plover_vector_free(selected_packages);
1085 transaction=plover_transaction_new();
1086 plover_transaction_set_installed(transaction,helper->installed);
1087 if (!plover_transaction_remove(transaction,selected_packages->strings,
1090 plover_vector_free(selected_packages);
1091 g_object_unref(transaction);
1094 retval=plover_transaction_helper_add_transaction(helper,transaction,
1095 NULL,PLOVER_TRANSACTION_HELPER_REPORT_REMOVE,error);
1096 g_object_unref(transaction);
1097 plover_vector_free(selected_packages);
1102 * Returns TRUE if there is work to be done or FALSE if all updates have
1103 * already been applied or on error.
1105 gboolean plover_transaction_helper_update(PloverTransactionHelper *helper,
1109 PloverTransaction *transaction;
1110 g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE);
1111 transaction=plover_transaction_helper_new_transaction(helper,error);
1114 if (!plover_transaction_update(transaction,NULL,error))
1116 g_object_unref(transaction);
1119 retval=plover_transaction_helper_add_transaction(helper,transaction,
1120 NULL,PLOVER_TRANSACTION_HELPER_REPORT_UPDATE,error);
1121 g_object_unref(transaction);
1125 gboolean plover_transaction_helper_get_visible(PloverTransactionHelper *helper)
1127 g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE);
1128 if (helper->error_dialog)
1130 else if (!helper->assistant)
1133 return gtk_widget_get_visible(GTK_WIDGET(helper->assistant));
1136 void plover_transaction_helper_present(PloverTransactionHelper *helper)
1138 g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper));
1139 if (helper->error_dialog)
1140 gtk_window_present(GTK_WINDOW(helper->error_dialog));
1141 else if (helper->assistant)
1142 gtk_window_present(GTK_WINDOW(helper->assistant));
1146 plover_transaction_helper_error_dialog_response(GtkDialog *error_dialog,
1147 int response_id,PloverTransactionHelper *helper)
1149 g_signal_handlers_disconnect_by_data(error_dialog,helper);
1150 if ((GtkWidget *)error_dialog==helper->error_dialog)
1152 gtk_widget_destroy(helper->error_dialog);
1153 helper->error_dialog=NULL;
1154 if (helper->assistant)
1156 gtk_widget_hide(GTK_WIDGET(helper->assistant));
1157 gtk_assistant_set_current_page(helper->assistant,0);
1159 g_signal_emit(helper,signals[CLOSE],0);
1163 const char *plover_transaction_helper_get_error(PloverTransactionHelper *helper,
1164 const GError **error)
1166 g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper));
1167 if (!helper->error_dialog)
1170 *error=helper->error;
1171 return helper->error_primary_text;
1174 void plover_transaction_helper_set_error(PloverTransactionHelper *helper,
1175 const GError *error,const char *primary_text)
1177 GtkMessageType type;
1179 g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper));
1180 g_return_if_fail(error != NULL);
1181 g_return_if_fail(primary_text != NULL);
1182 if (helper->pulse_handler)
1184 g_source_remove(helper->pulse_handler);
1185 helper->pulse_handler=0;
1187 if (helper->error_dialog)
1189 gtk_widget_destroy(helper->error_dialog);
1190 helper->error_dialog=NULL;
1192 g_free(helper->error_primary_text);
1193 helper->error_primary_text=g_strdup(primary_text);
1194 g_clear_error(&helper->error);
1195 helper->error=g_error_copy(error);
1196 if (g_error_matches(error,PLOVER_GENERAL_ERROR,
1197 PLOVER_GENERAL_ERROR_NO_WORK))
1198 type=GTK_MESSAGE_INFO;
1200 type=GTK_MESSAGE_ERROR;
1201 if (helper->assistant)
1202 window=GTK_WINDOW(helper->assistant);
1205 helper->error_dialog=gtk_message_dialog_new(window,
1206 GTK_DIALOG_DESTROY_WITH_PARENT,type,GTK_BUTTONS_CLOSE,primary_text);
1207 gtk_message_dialog_format_secondary_text(
1208 GTK_MESSAGE_DIALOG(helper->error_dialog),error->message);
1209 gtk_widget_show(helper->error_dialog);
1210 g_signal_connect(helper->error_dialog,"response",
1211 G_CALLBACK(plover_transaction_helper_error_dialog_response),helper);