1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/plover-gtk/transactionhelper.c Mon Nov 02 19:01:50 2015 +0000
1.3 @@ -0,0 +1,1037 @@
1.4 +/*
1.5 + * Copyright (C) 2014 J. Ali Harlow <ali@juiblex.co.uk>
1.6 + *
1.7 + * This program is free software; you can redistribute it and/or modify
1.8 + * it under the terms of the GNU General Public License as published by
1.9 + * the Free Software Foundation; either version 2 of the License, or
1.10 + * (at your option) any later version.
1.11 + *
1.12 + * This program is distributed in the hope that it will be useful,
1.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.15 + * GNU General Public License for more details.
1.16 + *
1.17 + * You should have received a copy of the GNU General Public License along
1.18 + * with this program; if not, write to the Free Software Foundation, Inc.,
1.19 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
1.20 + */
1.21 +
1.22 +#include "config.h"
1.23 +#include <stdlib.h>
1.24 +#include <errno.h>
1.25 +#include <gtk/gtk.h>
1.26 +#include <plover/plover.h>
1.27 +#include <plover/transaction.h>
1.28 +#include <plover-gtk/transactionhelper.h>
1.29 +
1.30 +/*
1.31 + * A PloverTransactionHelper uses a GtkAssistant to help a user run a
1.32 + * transaction.
1.33 + */
1.34 +
1.35 +G_DEFINE_TYPE(PloverTransactionHelper,plover_transaction_helper,G_TYPE_OBJECT)
1.36 +
1.37 +enum {
1.38 + CLOSE=0,
1.39 + N_SIGNALS
1.40 +};
1.41 +
1.42 +static guint signals[N_SIGNALS];
1.43 +
1.44 +static void plover_transaction_helper_finalize(PloverTransactionHelper *helper)
1.45 +{
1.46 + g_free(helper->error_primary_text);
1.47 + g_free(helper->base);
1.48 + g_free(helper->unsatisfied);
1.49 + if (helper->comps)
1.50 + plover_comps_free(helper->comps);
1.51 + plover_vector_free(helper->report_adding);
1.52 + plover_vector_free(helper->report_removing);
1.53 +}
1.54 +
1.55 +static void plover_transaction_helper_dispose(PloverTransactionHelper *helper)
1.56 +{
1.57 + g_clear_error(&helper->error);
1.58 + if (helper->error_dialog)
1.59 + {
1.60 + g_signal_handlers_disconnect_by_data(helper->error_dialog,helper);
1.61 + gtk_widget_destroy(helper->error_dialog);
1.62 + helper->error_dialog=NULL;
1.63 + }
1.64 + if (helper->assistant)
1.65 + {
1.66 + g_signal_handlers_disconnect_by_data(helper->assistant,helper);
1.67 + g_clear_object(&helper->assistant);
1.68 + }
1.69 + g_clear_object(&helper->ui);
1.70 + g_slist_foreach(helper->transactions,(GFunc)g_object_unref,NULL);
1.71 + g_slist_free(helper->transactions);
1.72 + helper->transactions=NULL;
1.73 + g_clear_object(&helper->installed);
1.74 + g_clear_object(&helper->upstream);
1.75 + g_clear_object(&helper->relocated_upstream);
1.76 +}
1.77 +
1.78 +static void
1.79 + plover_transaction_helper_class_init(PloverTransactionHelperClass *klass)
1.80 +{
1.81 + GObjectClass *gobject_class=G_OBJECT_CLASS(klass);
1.82 + gobject_class->finalize=
1.83 + (void (*)(GObject *))plover_transaction_helper_finalize;
1.84 + gobject_class->dispose=
1.85 + (void (*)(GObject *))plover_transaction_helper_dispose;
1.86 + signals[CLOSE]=g_signal_newv("close",
1.87 + G_TYPE_FROM_CLASS(klass),G_SIGNAL_RUN_LAST,NULL,NULL,NULL,
1.88 + g_cclosure_marshal_VOID__VOID,G_TYPE_NONE,0,NULL);
1.89 +}
1.90 +
1.91 +static void plover_transaction_helper_init(PloverTransactionHelper *helper)
1.92 +{
1.93 + helper->report_adding=plover_vector_new();
1.94 + helper->report_removing=plover_vector_new();
1.95 +}
1.96 +
1.97 +static void plover_transaction_helper_assistant_cancel(GtkAssistant *assistant,
1.98 + PloverTransactionHelper *helper)
1.99 +{
1.100 + gtk_widget_hide(GTK_WIDGET(helper->assistant));
1.101 + gtk_assistant_set_current_page(helper->assistant,0);
1.102 + g_signal_emit(helper,signals[CLOSE],0);
1.103 +}
1.104 +
1.105 +static void plover_transaction_helper_assistant_close(GtkAssistant *assistant,
1.106 + PloverTransactionHelper *helper)
1.107 +{
1.108 + gtk_widget_hide(GTK_WIDGET(helper->assistant));
1.109 + gtk_assistant_set_current_page(helper->assistant,0);
1.110 + g_signal_emit(helper,signals[CLOSE],0);
1.111 +}
1.112 +
1.113 +static void
1.114 + plover_transaction_helper_prepare_confirm(PloverTransactionHelper *helper)
1.115 +{
1.116 + gchar *package_list,*add,*remove,*s;
1.117 + GtkLabel *label;
1.118 + struct plover_vector *report;
1.119 + if (helper->report_adding->len)
1.120 + {
1.121 + plover_vector_sort(helper->report_adding);
1.122 + if (helper->report_adding_dependencies)
1.123 + {
1.124 + report=plover_vector_dup(helper->report_adding);
1.125 + if (helper->report_adding->len==1)
1.126 + plover_vector_append(report,"its dependencies");
1.127 + else
1.128 + plover_vector_append(report,"their dependencies");
1.129 + package_list=plover_vector_format_for_display(report);
1.130 + plover_vector_free(report);
1.131 + }
1.132 + else
1.133 + package_list=
1.134 + plover_vector_format_for_display(helper->report_adding);
1.135 + add=g_strconcat("Packages to be installed or updated: ",package_list,
1.136 + ".",NULL);
1.137 + g_free(package_list);
1.138 + }
1.139 + else
1.140 + add=NULL;
1.141 + if (helper->report_removing->len)
1.142 + {
1.143 + plover_vector_sort(helper->report_removing);
1.144 + if (helper->report_removing_dependants)
1.145 + {
1.146 + report=plover_vector_dup(helper->report_removing);
1.147 + if (helper->report_adding->len==1)
1.148 + plover_vector_append(report,"its dependants");
1.149 + else
1.150 + plover_vector_append(report,"their dependants");
1.151 + package_list=plover_vector_format_for_display(report);
1.152 + plover_vector_free(report);
1.153 + }
1.154 + else
1.155 + package_list=
1.156 + plover_vector_format_for_display(helper->report_removing);
1.157 + remove=g_strconcat("Packages to be removed: ",package_list,".",NULL);
1.158 + g_free(package_list);
1.159 + }
1.160 + else
1.161 + remove=NULL;
1.162 + label=GTK_LABEL(gtk_builder_get_object(helper->ui,"SISummaryOfWork"));
1.163 + if (add && remove)
1.164 + s=g_strconcat("<b>Installation Summary</b>\n\n",remove,"\n\n",add,NULL);
1.165 + else if (add || remove)
1.166 + s=g_strconcat("<b>Installation Summary</b>\n\n",add?add:remove,NULL);
1.167 + else
1.168 + s=g_strdup("<b>Installation Summary</b>\n\nNo changes scheduled");
1.169 + gtk_label_set_markup(label,s);
1.170 + g_free(s);
1.171 + g_free(add);
1.172 + g_free(remove);
1.173 +}
1.174 +
1.175 +static void plover_transaction_helper_run(PloverTransactionHelper *helper);
1.176 +
1.177 +static void plover_transaction_helper_callback(GObject *source,
1.178 + GAsyncResult *result,gpointer user_data)
1.179 +{
1.180 + GError *error=NULL;
1.181 + PloverTransactionHelper *helper=user_data;
1.182 + PloverTransaction *transaction=PLOVER_TRANSACTION(source);
1.183 + if (!plover_transaction_commit_finish(transaction,result,&error))
1.184 + {
1.185 + plover_transaction_helper_set_error(helper,error,
1.186 + "Software installation failed");
1.187 + g_error_free(error);
1.188 + }
1.189 + else
1.190 + plover_transaction_helper_run(helper);
1.191 + g_signal_handlers_disconnect_by_data(transaction,helper);
1.192 + g_object_unref(transaction);
1.193 +}
1.194 +
1.195 +static void plover_transaction_helper_transaction_status_changed(
1.196 + PloverTransaction *transaction,const char *status,
1.197 + PloverTransactionHelper *helper)
1.198 +{
1.199 + GtkProgressBar *bar;
1.200 + bar=GTK_PROGRESS_BAR(gtk_builder_get_object(helper->ui,"SIProgressBar"));
1.201 + gtk_progress_bar_set_text(bar,status);
1.202 +}
1.203 +
1.204 +static void plover_transaction_helper_run(PloverTransactionHelper *helper)
1.205 +{
1.206 + PloverTransaction *transaction;
1.207 + GtkWidget *page;
1.208 + page=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIProgress"));
1.209 + if (helper->transactions)
1.210 + {
1.211 + if (helper->assistant)
1.212 + gtk_assistant_set_page_complete(helper->assistant,page,FALSE);
1.213 + transaction=helper->transactions->data;
1.214 + helper->transactions=g_slist_delete_link(helper->transactions,
1.215 + helper->transactions);
1.216 + g_signal_connect(transaction,"status-changed",
1.217 + G_CALLBACK(plover_transaction_helper_transaction_status_changed),
1.218 + helper);
1.219 + plover_transaction_commit_async(transaction,NULL,
1.220 + plover_transaction_helper_callback,helper);
1.221 + }
1.222 + else if (helper->assistant)
1.223 + gtk_assistant_set_page_complete(helper->assistant,page,TRUE);
1.224 +}
1.225 +
1.226 +static gboolean plover_transaction_helper_pulse(gpointer user_data)
1.227 +{
1.228 + PloverTransactionHelper *helper=user_data;
1.229 + GtkWidget *w;
1.230 + GtkProgressBar *bar;
1.231 + if (!helper->assistant)
1.232 + return FALSE;
1.233 + w=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIProgress"));
1.234 + bar=GTK_PROGRESS_BAR(gtk_builder_get_object(helper->ui,"SIProgressBar"));
1.235 + if (gtk_assistant_get_page_complete(helper->assistant,w))
1.236 + {
1.237 + gtk_progress_bar_set_fraction(bar,1.0);
1.238 + helper->pulse_handler=0;
1.239 + return FALSE;
1.240 + }
1.241 + else
1.242 + {
1.243 + gtk_progress_bar_pulse(bar);
1.244 + return TRUE;
1.245 + }
1.246 +}
1.247 +
1.248 +static void
1.249 + plover_transaction_helper_prepare_progress(PloverTransactionHelper *helper)
1.250 +{
1.251 + GError *error=NULL;
1.252 + GtkToggleButton *button;
1.253 + PloverTransaction *transaction;
1.254 + button=GTK_TOGGLE_BUTTON(gtk_builder_get_object(helper->ui,
1.255 + "SIRemoveExisting"));
1.256 + if (gtk_toggle_button_get_active(button))
1.257 + {
1.258 + transaction=plover_transaction_new_remove(NULL,&error);
1.259 + if (!transaction)
1.260 + {
1.261 + if (g_error_matches(error,PLOVER_POSIX_ERROR,ENOENT))
1.262 + g_clear_error(&error);
1.263 + if (error)
1.264 + {
1.265 + plover_transaction_helper_set_error(helper,error,
1.266 + "Software installation failed");
1.267 + g_error_free(error);
1.268 + return;
1.269 + }
1.270 + }
1.271 + else
1.272 + helper->transactions=
1.273 + g_slist_prepend(helper->transactions,transaction);
1.274 + }
1.275 + /*
1.276 + * Note that PloverTransaction does support cancelling a transaction, but
1.277 + * there are a number of challenges with using it:
1.278 + * - cancellation is only supported during the file phase if razor
1.279 + * has atomic rollback,
1.280 + * - cancellation is not supported during post-transaction scripts at all
1.281 + * (since by the time the first script is started the atomic has already
1.282 + * been committed) and these can take quite some time,
1.283 + * - where a transaction has an embedded COMMIT, any rollback won't
1.284 + * go back beyond this point.
1.285 + * To support user-cancel, then, we would need some mechanism to:
1.286 + * - Comunicate that the operation is being cancelled and this may take
1.287 + * some time,
1.288 + * - Not allow cancellation at all after the last post-transaction script
1.289 + * phase is started,
1.290 + * - Report the partially completed transaction where cancellation
1.291 + * occurred after a COMMIT point.
1.292 + * At present, this doesn't appear worth the effort.
1.293 + */
1.294 + if (helper->assistant)
1.295 + gtk_assistant_commit(helper->assistant);
1.296 + plover_transaction_helper_run(helper);
1.297 + helper->pulse_handler=g_timeout_add(100,plover_transaction_helper_pulse,
1.298 + helper);
1.299 +}
1.300 +
1.301 +static void plover_transaction_helper_assistant_prepare(GtkAssistant *assistant,
1.302 + GtkWidget *page,PloverTransactionHelper *helper)
1.303 +{
1.304 + if (page==GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIConfirm")))
1.305 + plover_transaction_helper_prepare_confirm(helper);
1.306 + else if (page==GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIProgress")))
1.307 + plover_transaction_helper_prepare_progress(helper);
1.308 +}
1.309 +
1.310 +static void
1.311 + plover_transaction_helper_remove_existing_toggled(GtkToggleButton *button,
1.312 + PloverTransactionHelper *helper)
1.313 +{
1.314 + GtkWidget *w;
1.315 + if (helper->assistant)
1.316 + {
1.317 + w=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIConfirm"));
1.318 + gtk_assistant_set_page_complete(helper->assistant,w,
1.319 + gtk_toggle_button_get_active(button));
1.320 + }
1.321 +}
1.322 +
1.323 +PloverTransactionHelper *plover_transaction_helper_new(GtkBuilder *ui)
1.324 +{
1.325 + gsize len;
1.326 + gchar *s,*directory,*contents;
1.327 + GError *error=NULL;
1.328 + GtkWidget *w;
1.329 + PloverTransactionHelper *helper;
1.330 + g_return_val_if_fail(ui == NULL || GTK_IS_BUILDER(ui),NULL);
1.331 + helper=PLOVER_TRANSACTION_HELPER(
1.332 + g_object_new(PLOVER_TYPE_TRANSACTION_HELPER,NULL));
1.333 + if (ui)
1.334 + helper->ui=g_object_ref(ui);
1.335 + else
1.336 + helper->ui=gtk_builder_new();
1.337 + helper->assistant=
1.338 + GTK_ASSISTANT(gtk_builder_get_object(helper->ui,"SoftwareInstallation"));
1.339 + if (!helper->assistant)
1.340 + {
1.341 + directory=g_strdup(g_getenv("PLOVER_DATADIR"));
1.342 + if (!directory)
1.343 + {
1.344 +#ifdef WIN32
1.345 + s=g_win32_get_package_installation_directory_of_module(NULL);
1.346 + directory=g_build_filename(s,"share","plover",NULL);
1.347 + g_free(s);
1.348 +#else
1.349 + directory=g_strdup(PLOVER_DATADIR);
1.350 +#endif
1.351 + }
1.352 + s=g_build_filename(directory,"software-installation.ui",NULL);
1.353 + g_free(directory);
1.354 + (void)g_file_get_contents(s,&contents,&len,&error);
1.355 + g_free(s);
1.356 + if (!error)
1.357 + {
1.358 + (void)gtk_builder_add_from_string(helper->ui,contents,len,&error);
1.359 + g_free(contents);
1.360 + }
1.361 + if (error)
1.362 + {
1.363 + g_critical("software-installation.ui: %s",error->message);
1.364 + g_clear_error(&error);
1.365 + g_set_error(&error,PLOVER_GENERAL_ERROR,PLOVER_GENERAL_ERROR_FAILED,
1.366 + "Internal error (no user interface)");
1.367 + plover_transaction_helper_set_error(helper,error,
1.368 + "Can't start installer");
1.369 + return helper;
1.370 + }
1.371 + helper->assistant=GTK_ASSISTANT(gtk_builder_get_object(helper->ui,
1.372 + "SoftwareInstallation"));
1.373 + }
1.374 + if (!helper->assistant)
1.375 + {
1.376 + g_critical("\"SoftwareInstallation\" object not found");
1.377 + g_set_error(&error,PLOVER_GENERAL_ERROR,PLOVER_GENERAL_ERROR_FAILED,
1.378 + "Internal error (missing wizard)");
1.379 + plover_transaction_helper_set_error(helper,error,
1.380 + "Can't start installer");
1.381 + g_error_free(error);
1.382 + return helper;
1.383 + }
1.384 + else
1.385 + g_object_ref(helper->assistant);
1.386 + if (!GTK_IS_ASSISTANT(helper->assistant))
1.387 + {
1.388 + g_critical("\"SoftwareInstallation\" is not a GtkAssistant");
1.389 + g_set_error(&error,PLOVER_GENERAL_ERROR,PLOVER_GENERAL_ERROR_FAILED,
1.390 + "Internal error (unexpected wizard type)");
1.391 + plover_transaction_helper_set_error(helper,error,
1.392 + "Can't start installer");
1.393 + g_error_free(error);
1.394 + return helper;
1.395 + }
1.396 + g_signal_connect(helper->assistant,"cancel",
1.397 + G_CALLBACK(plover_transaction_helper_assistant_cancel),helper);
1.398 + g_signal_connect(helper->assistant,"close",
1.399 + G_CALLBACK(plover_transaction_helper_assistant_close),helper);
1.400 + g_signal_connect(helper->assistant,"prepare",
1.401 + G_CALLBACK(plover_transaction_helper_assistant_prepare),helper);
1.402 + w=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIRemoveExisting"));
1.403 + if (w)
1.404 + g_signal_connect(w,"toggled",
1.405 + G_CALLBACK(plover_transaction_helper_remove_existing_toggled),helper);
1.406 + w=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIIntroduction"));
1.407 + if (w)
1.408 + gtk_assistant_set_page_complete(helper->assistant,w,TRUE);
1.409 + return helper;
1.410 +}
1.411 +
1.412 +PloverPackageSet *
1.413 + plover_transaction_helper_get_installed(PloverTransactionHelper *helper)
1.414 +{
1.415 + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL);
1.416 + return helper->installed;
1.417 +}
1.418 +
1.419 +void plover_transaction_helper_set_installed(PloverTransactionHelper *helper,
1.420 + PloverPackageSet *installed)
1.421 +{
1.422 + g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper));
1.423 + g_return_if_fail(PLOVER_IS_PACKAGE_SET(installed));
1.424 + g_return_if_fail(helper->installed == NULL);
1.425 + helper->installed=g_object_ref(installed);
1.426 +}
1.427 +
1.428 +PloverRepository *
1.429 + plover_transaction_helper_get_upstream(PloverTransactionHelper *helper,
1.430 + GError **error)
1.431 +{
1.432 + const char *base;
1.433 +#if 0
1.434 + const char *prefix;
1.435 + struct razor_relocations *relocations=NULL;
1.436 +#endif
1.437 + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL);
1.438 + if (!helper->upstream)
1.439 + {
1.440 +#if 0
1.441 + prefix=plover_transaction_helper_get_prefix(helper,error);
1.442 + if (!prefix)
1.443 + return NULL;
1.444 +#endif
1.445 + base=plover_transaction_helper_get_base(helper);
1.446 +#if 0
1.447 + if (prefix)
1.448 + {
1.449 + relocations=razor_relocations_create();
1.450 + razor_relocations_add(relocations,"/usr",prefix);
1.451 + }
1.452 +#endif
1.453 + helper->upstream=plover_repository_new_from_yum(base,error);
1.454 +#if 0
1.455 + if (relocations)
1.456 + razor_relocations_destroy(relocations);
1.457 +#endif
1.458 + }
1.459 + return helper->upstream;
1.460 +}
1.461 +
1.462 +static PloverPackageSet *plover_transaction_helper_get_relocated_upstream(
1.463 + PloverTransactionHelper *helper,GError **error)
1.464 +{
1.465 + const char *prefix;
1.466 + struct razor_relocations *relocations=NULL;
1.467 + GError *tmp_error=NULL;
1.468 + PloverRepository *upstream;
1.469 + PloverPackageSet *set;
1.470 + if (!helper->relocated_upstream)
1.471 + {
1.472 + upstream=plover_transaction_helper_get_upstream(helper,error);
1.473 + if (!upstream)
1.474 + return NULL;
1.475 + prefix=plover_transaction_helper_get_prefix(helper,&tmp_error);
1.476 + if (tmp_error)
1.477 + {
1.478 + g_propagate_error(error,tmp_error);
1.479 + return NULL;
1.480 + }
1.481 + set=plover_repository_get_package_set(upstream);
1.482 + if (prefix)
1.483 + {
1.484 + relocations=razor_relocations_create();
1.485 + razor_relocations_add(relocations,"/usr",prefix);
1.486 + helper->relocated_upstream=
1.487 + plover_package_set_new_from_repository(upstream,relocations,
1.488 + error);
1.489 + if (relocations)
1.490 + razor_relocations_destroy(relocations);
1.491 + }
1.492 + else
1.493 + helper->relocated_upstream=g_object_ref(set);
1.494 + }
1.495 + return helper->relocated_upstream;
1.496 +}
1.497 +
1.498 +void plover_transaction_helper_set_upstream(PloverTransactionHelper *helper,
1.499 + PloverRepository *upstream)
1.500 +{
1.501 + g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper));
1.502 + g_return_if_fail(PLOVER_IS_REPOSITORY(upstream));
1.503 + g_return_if_fail(helper->upstream == NULL);
1.504 + helper->upstream=g_object_ref(upstream);
1.505 +}
1.506 +
1.507 +const char *plover_transaction_helper_get_base(PloverTransactionHelper *helper)
1.508 +{
1.509 + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL);
1.510 + return helper->base;
1.511 +}
1.512 +
1.513 +void plover_transaction_helper_set_base(PloverTransactionHelper *helper,
1.514 + const char *base)
1.515 +{
1.516 + g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper));
1.517 + g_return_if_fail(helper->transactions == NULL);
1.518 + g_free(helper->base);
1.519 + helper->base=g_strdup(base);
1.520 +}
1.521 +
1.522 +struct comps *
1.523 + plover_transaction_helper_get_comps(PloverTransactionHelper *helper,
1.524 + GError **error)
1.525 +{
1.526 + gchar *s;
1.527 + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL);
1.528 + g_return_val_if_fail(helper->base != NULL,NULL);
1.529 + if (!helper->comps)
1.530 + {
1.531 + s=g_strconcat(helper->base,"/repodata/comps.xml",NULL);
1.532 + helper->comps=plover_comps_new_from_file(s);
1.533 + if (!helper->comps)
1.534 + g_set_error(error,PLOVER_GENERAL_ERROR,
1.535 + PLOVER_GENERAL_ERROR_FAILED,"%s: %s",s,g_strerror(errno));
1.536 + g_free(s);
1.537 + }
1.538 + return helper->comps;
1.539 +}
1.540 +
1.541 +const char *
1.542 + plover_transaction_helper_get_prefix(PloverTransactionHelper *helper,
1.543 + GError **error)
1.544 +{
1.545 + const char *prefix;
1.546 + struct comps *comps;
1.547 + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL);
1.548 + g_return_val_if_fail(helper->base != NULL || helper->installed != NULL,NULL);
1.549 + if (helper->base)
1.550 + {
1.551 + comps=plover_transaction_helper_get_comps(helper,error);
1.552 + if (!comps)
1.553 + return NULL;
1.554 + return plover_default_prefix_for_vendor(comps->vendor);
1.555 + }
1.556 + prefix=plover_package_set_guess_prefix(helper->installed,error);
1.557 + return prefix;
1.558 +}
1.559 +
1.560 +static int plover_transaction_helper_package_count(void)
1.561 +{
1.562 + int count=0;
1.563 + char *install_root;
1.564 + struct razor_set *set;
1.565 + struct razor_package *package;
1.566 + struct razor_package_iterator *pi;
1.567 + install_root=getenv("RAZOR_ROOT");
1.568 + if (!install_root)
1.569 + install_root="";
1.570 + set=razor_root_open_read_only(install_root,NULL);
1.571 + if (set)
1.572 + {
1.573 + pi=razor_package_iterator_create(set);
1.574 + while (razor_package_iterator_next(pi,&package,RAZOR_DETAIL_LAST))
1.575 + count++;
1.576 + razor_package_iterator_destroy(pi);
1.577 + razor_set_unref(set);
1.578 + }
1.579 + return count;
1.580 +}
1.581 +
1.582 +static gboolean
1.583 + plover_transaction_helper_check_vendor(PloverTransactionHelper *helper,
1.584 + GError **error)
1.585 +{
1.586 + int i;
1.587 + gchar *prefix=NULL,*s;
1.588 + struct comps *comps=NULL;
1.589 + GtkWidget *container,*page;
1.590 + GtkButton *button;
1.591 + GtkLabel *label;
1.592 + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE);
1.593 + if (helper->check_vendor)
1.594 + {
1.595 + comps=plover_transaction_helper_get_comps(helper,error);
1.596 + if (!comps)
1.597 + return FALSE;
1.598 + prefix=plover_default_prefix_for_vendor(comps->vendor);
1.599 + }
1.600 + button=GTK_BUTTON(gtk_builder_get_object(helper->ui,"SIRemoveExisting"));
1.601 + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),FALSE);
1.602 + container=GTK_WIDGET(gtk_builder_get_object(helper->ui,
1.603 + "SIIncompatibleInstallation"));
1.604 + page=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIConfirm"));
1.605 + if (helper->check_vendor && prefix &&
1.606 + !plover_installed_files_match_prefix(prefix))
1.607 + {
1.608 + label=GTK_LABEL(gtk_builder_get_object(helper->ui,
1.609 + "SIIncompatibleInstallationLabel"));
1.610 + s=g_strdup_printf("<b>Incompatible Installation</b>\n\n"
1.611 + "The existing installation is not from %s.\n"
1.612 + "In order to continue, all the existing packages must be removed.",
1.613 + comps->vendor);
1.614 + gtk_label_set_markup(label,s);
1.615 + g_free(s);
1.616 + i=plover_transaction_helper_package_count();
1.617 + s=g_strdup_printf("Remove %d existing package%s",i,i==1?"":"s");
1.618 + gtk_button_set_label(button,s);
1.619 + g_free(s);
1.620 + gtk_widget_show(container);
1.621 + if (helper->assistant)
1.622 + gtk_assistant_set_page_complete(helper->assistant,page,FALSE);
1.623 + }
1.624 + else
1.625 + {
1.626 + gtk_widget_hide(container);
1.627 + if (helper->assistant)
1.628 + gtk_assistant_set_page_complete(helper->assistant,page,TRUE);
1.629 + }
1.630 + return TRUE;
1.631 +}
1.632 +
1.633 +void plover_transaction_helper_set_check_vendor(PloverTransactionHelper *helper,
1.634 + gboolean check_vendor)
1.635 +{
1.636 + g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper));
1.637 + if (helper->check_vendor!=check_vendor)
1.638 + {
1.639 + helper->check_vendor=check_vendor;
1.640 + if (helper->transactions)
1.641 + plover_transaction_helper_check_vendor(helper,NULL);
1.642 + }
1.643 +}
1.644 +
1.645 +/*
1.646 + * If plover_transaction_helper_add_transaction() failes with an error
1.647 + * of PLOVER_GENERAL_ERROR,PLOVER_GENERAL_ERROR_REQUIREMENTS_NOT_MET
1.648 + * then plover_transaction_helper_get_unsatisfied() can be used to
1.649 + * retrieve a textual description of the problem.
1.650 + */
1.651 +
1.652 +const char *
1.653 + plover_transaction_helper_get_unsatisfied(PloverTransactionHelper *helper)
1.654 +{
1.655 + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL);
1.656 + return helper->unsatisfied;
1.657 +}
1.658 +
1.659 +gboolean
1.660 + plover_transaction_helper_add_transaction(PloverTransactionHelper *helper,
1.661 + PloverTransaction *transaction,struct plover_vector *report_packages,
1.662 + enum razor_install_action report_action,GError **error)
1.663 +{
1.664 + int i,count;
1.665 + gboolean other_packages;
1.666 + const char *s,*name;
1.667 + enum razor_install_action action;
1.668 + struct razor_install_iterator *ii;
1.669 + struct razor_set *next;
1.670 + struct razor_package *package;
1.671 + struct plover_vector *tasked_packages;
1.672 + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE);
1.673 + g_return_val_if_fail(PLOVER_IS_TRANSACTION(transaction),FALSE);
1.674 + g_return_val_if_fail(report_action==RAZOR_INSTALL_ACTION_ADD || report_action==RAZOR_INSTALL_ACTION_REMOVE,FALSE);
1.675 + g_free(helper->unsatisfied);
1.676 + helper->unsatisfied=NULL;
1.677 + if (!plover_transaction_resolve(transaction,error))
1.678 + {
1.679 + s=plover_transaction_get_unsatisfied(transaction);
1.680 + helper->unsatisfied=g_strdup(s);
1.681 + return FALSE;
1.682 + }
1.683 + ii=plover_transaction_get_install_iterator(transaction,error);
1.684 + if (!ii)
1.685 + return FALSE;
1.686 + next=plover_transaction_get_next_set(transaction,error);
1.687 + if (!next)
1.688 + return FALSE;
1.689 + tasked_packages=plover_vector_new();
1.690 + other_packages=FALSE;
1.691 + while (razor_install_iterator_next(ii,&package,&action,&count))
1.692 + {
1.693 + if (action==report_action)
1.694 + {
1.695 + razor_package_get_details(next,package,RAZOR_DETAIL_NAME,&name,
1.696 + RAZOR_DETAIL_LAST);
1.697 + if (!report_packages ||
1.698 + plover_vector_contains(report_packages,name))
1.699 + plover_vector_append(tasked_packages,name);
1.700 + else
1.701 + other_packages=TRUE;
1.702 + }
1.703 + }
1.704 + if (!tasked_packages->len)
1.705 + {
1.706 + /*
1.707 + * If there are no reportable packages tasked for action there
1.708 + * shouldn't by any packages at all, but let's be paranoid.
1.709 + */
1.710 + other_packages=FALSE;
1.711 + razor_install_iterator_rewind(ii);
1.712 + while (razor_install_iterator_next(ii,&package,&action,&count))
1.713 + {
1.714 + if (action==report_action)
1.715 + {
1.716 + razor_package_get_details(next,package,RAZOR_DETAIL_NAME,&name,
1.717 + RAZOR_DETAIL_LAST);
1.718 + plover_vector_append(tasked_packages,name);
1.719 + }
1.720 + }
1.721 + }
1.722 + if (!tasked_packages->len)
1.723 + {
1.724 + g_set_error(error,PLOVER_GENERAL_ERROR,
1.725 + PLOVER_GENERAL_ERROR_NO_WORK,"Transaction includes no %s actions",
1.726 + report_action==RAZOR_INSTALL_ACTION_ADD?"add":"remove");
1.727 + plover_vector_free(tasked_packages);
1.728 + return FALSE;
1.729 + }
1.730 + if (!helper->transactions)
1.731 + plover_transaction_helper_check_vendor(helper,error);
1.732 + g_object_ref(transaction);
1.733 + helper->transactions=g_slist_append(helper->transactions,transaction);
1.734 + if (report_action==RAZOR_INSTALL_ACTION_ADD)
1.735 + {
1.736 + for(i=0;i<tasked_packages->len;i++)
1.737 + {
1.738 + s=tasked_packages->strings[i];
1.739 + if (!plover_vector_contains(helper->report_adding,s))
1.740 + plover_vector_append(helper->report_adding,s);
1.741 + }
1.742 + helper->report_adding_dependencies|=other_packages;
1.743 + }
1.744 + else
1.745 + {
1.746 + for(i=0;i<tasked_packages->len;i++)
1.747 + {
1.748 + s=tasked_packages->strings[i];
1.749 + if (!plover_vector_contains(helper->report_removing,s))
1.750 + plover_vector_append(helper->report_removing,s);
1.751 + }
1.752 + helper->report_removing_dependants|=other_packages;
1.753 + }
1.754 + plover_vector_free(tasked_packages);
1.755 + return TRUE;
1.756 +}
1.757 +
1.758 +static PloverTransaction *
1.759 + plover_transaction_helper_new_transaction(PloverTransactionHelper *helper,
1.760 + GError **error)
1.761 +{
1.762 + gboolean ok;
1.763 + const char *base,*prefix;
1.764 + GError *tmp_error=NULL;
1.765 + PloverTransaction *transaction;
1.766 + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL);
1.767 + prefix=plover_transaction_helper_get_prefix(helper,&tmp_error);
1.768 + if (tmp_error)
1.769 + {
1.770 + g_propagate_error(error,tmp_error);
1.771 + return NULL;
1.772 + }
1.773 + transaction=plover_transaction_new();
1.774 + plover_transaction_set_prefix(transaction,prefix);
1.775 + plover_transaction_set_installed(transaction,helper->installed);
1.776 + if (helper->upstream)
1.777 + ok=plover_transaction_set_upstream(transaction,helper->upstream,error);
1.778 + else
1.779 + {
1.780 + base=plover_transaction_helper_get_base(helper);
1.781 + ok=plover_transaction_set_upstream_from_yum(transaction,base,error);
1.782 + }
1.783 + if (!ok)
1.784 + {
1.785 + g_object_unref(transaction);
1.786 + transaction=NULL;
1.787 + }
1.788 + return transaction;
1.789 +}
1.790 +
1.791 +struct plover_vector *plover_transaction_helper_group_get_default_packages(
1.792 + PloverTransactionHelper *helper,const char *group,GError **error)
1.793 +{
1.794 + gboolean changed;
1.795 + struct comps *comps;
1.796 + struct comps_group *grp;
1.797 + struct comps_requirement *pkg;
1.798 + struct plover_vector *default_packages;
1.799 + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE);
1.800 + comps=plover_transaction_helper_get_comps(helper,error);
1.801 + if (!comps)
1.802 + return NULL;
1.803 + grp=plover_comps_lookup_group(comps,group);
1.804 + if (!grp)
1.805 + {
1.806 + g_set_error(error,PLOVER_GENERAL_ERROR,
1.807 + PLOVER_GENERAL_ERROR_FAILED,"%s: group not found",group);
1.808 + return NULL;
1.809 + }
1.810 + default_packages=plover_vector_new();
1.811 + do
1.812 + {
1.813 + changed=FALSE;
1.814 + for(pkg=grp->packages;pkg;pkg=pkg->next)
1.815 + {
1.816 + if (plover_vector_contains(default_packages,pkg->name))
1.817 + continue;
1.818 + if (pkg->type==COMPS_REQUIREMENT_DEFAULT ||
1.819 + pkg->type==COMPS_REQUIREMENT_MANDATORY ||
1.820 + pkg->type==COMPS_REQUIREMENT_CONDITIONAL &&
1.821 + plover_vector_contains(default_packages,pkg->requires))
1.822 + {
1.823 + changed=TRUE;
1.824 + plover_vector_append(default_packages,pkg->name);
1.825 + }
1.826 + }
1.827 + } while(changed);
1.828 + return default_packages;
1.829 +}
1.830 +
1.831 +/*
1.832 + * Returns TRUE if there is work to be done or FALSE if the packages are
1.833 + * already installed or on error.
1.834 + */
1.835 +gboolean
1.836 + plover_transaction_helper_install_packages(PloverTransactionHelper *helper,
1.837 + struct plover_vector *packages,GError **error)
1.838 +{
1.839 + gboolean retval;
1.840 + PloverTransaction *transaction;
1.841 + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE);
1.842 + g_return_val_if_fail(packages != NULL,FALSE);
1.843 + if (!packages->len)
1.844 + {
1.845 + g_set_error(error,PLOVER_GENERAL_ERROR,
1.846 + PLOVER_GENERAL_ERROR_NO_WORK,"No packages listed to be installed");
1.847 + return FALSE;
1.848 + }
1.849 + transaction=plover_transaction_helper_new_transaction(helper,error);
1.850 + if (!transaction)
1.851 + return FALSE;
1.852 + if (!plover_transaction_install(transaction,packages->strings,error))
1.853 + {
1.854 + g_object_unref(transaction);
1.855 + return FALSE;
1.856 + }
1.857 + retval=plover_transaction_helper_add_transaction(helper,transaction,
1.858 + packages,RAZOR_INSTALL_ACTION_ADD,error);
1.859 + g_object_unref(transaction);
1.860 + return retval;
1.861 +}
1.862 +
1.863 +/*
1.864 + * Returns TRUE if there is work to be done or FALSE if the group is
1.865 + * already installed or on error.
1.866 + */
1.867 +gboolean
1.868 + plover_transaction_helper_install_group(PloverTransactionHelper *helper,
1.869 + const char *group,GError **error)
1.870 +{
1.871 + gboolean retval;
1.872 + struct plover_vector *selected_packages;
1.873 + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE);
1.874 + selected_packages=plover_transaction_helper_group_get_default_packages(
1.875 + helper,group,error);
1.876 + if (!selected_packages)
1.877 + return FALSE;
1.878 + if (!selected_packages->len)
1.879 + {
1.880 + g_set_error(error,PLOVER_GENERAL_ERROR,
1.881 + PLOVER_GENERAL_ERROR_FAILED,"%s: no default packages",group);
1.882 + plover_vector_free(selected_packages);
1.883 + return FALSE;
1.884 + }
1.885 + retval=plover_transaction_helper_install_packages(helper,selected_packages,
1.886 + error);
1.887 + plover_vector_free(selected_packages);
1.888 + return retval;
1.889 +}
1.890 +
1.891 +/*
1.892 + * Returns TRUE if there is work to be done or FALSE if the group is
1.893 + * not installed or on error.
1.894 + */
1.895 +gboolean plover_transaction_helper_remove_group(PloverTransactionHelper *helper,
1.896 + const char *group,GError **error)
1.897 +{
1.898 + gboolean retval;
1.899 + struct plover_vector *selected_packages;
1.900 + PloverTransaction *transaction;
1.901 + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE);
1.902 + selected_packages=plover_transaction_helper_group_get_default_packages(
1.903 + helper,group,error);
1.904 + if (!selected_packages)
1.905 + return FALSE;
1.906 + if (!selected_packages->len)
1.907 + {
1.908 + g_set_error(error,PLOVER_GENERAL_ERROR,
1.909 + PLOVER_GENERAL_ERROR_FAILED,"%s: no default packages",group);
1.910 + plover_vector_free(selected_packages);
1.911 + return FALSE;
1.912 + }
1.913 + transaction=plover_transaction_new();
1.914 + plover_transaction_set_installed(transaction,helper->installed);
1.915 + if (!plover_transaction_remove(transaction,selected_packages->strings,
1.916 + error))
1.917 + {
1.918 + plover_vector_free(selected_packages);
1.919 + g_object_unref(transaction);
1.920 + return FALSE;
1.921 + }
1.922 + retval=plover_transaction_helper_add_transaction(helper,transaction,
1.923 + NULL,RAZOR_INSTALL_ACTION_REMOVE,error);
1.924 + g_object_unref(transaction);
1.925 + plover_vector_free(selected_packages);
1.926 + return retval;
1.927 +}
1.928 +
1.929 +/*
1.930 + * Returns TRUE if there is work to be done or FALSE if all updates have
1.931 + * already been applied or on error.
1.932 + */
1.933 +gboolean plover_transaction_helper_update(PloverTransactionHelper *helper,
1.934 + GError **error)
1.935 +{
1.936 + gboolean retval;
1.937 + PloverTransaction *transaction;
1.938 + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE);
1.939 + transaction=plover_transaction_helper_new_transaction(helper,error);
1.940 + if (!transaction)
1.941 + return FALSE;
1.942 + if (!plover_transaction_update(transaction,NULL,error))
1.943 + {
1.944 + g_object_unref(transaction);
1.945 + return FALSE;
1.946 + }
1.947 + retval=plover_transaction_helper_add_transaction(helper,transaction,
1.948 + NULL,RAZOR_INSTALL_ACTION_ADD,error);
1.949 + g_object_unref(transaction);
1.950 + return retval;
1.951 +}
1.952 +
1.953 +gboolean plover_transaction_helper_get_visible(PloverTransactionHelper *helper)
1.954 +{
1.955 + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE);
1.956 + if (helper->error_dialog)
1.957 + return TRUE;
1.958 + else if (!helper->assistant)
1.959 + return FALSE;
1.960 + else
1.961 + return gtk_widget_get_visible(GTK_WIDGET(helper->assistant));
1.962 +}
1.963 +
1.964 +void plover_transaction_helper_present(PloverTransactionHelper *helper)
1.965 +{
1.966 + g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper));
1.967 + if (helper->error_dialog)
1.968 + gtk_window_present(GTK_WINDOW(helper->error_dialog));
1.969 + else if (helper->assistant)
1.970 + gtk_window_present(GTK_WINDOW(helper->assistant));
1.971 +}
1.972 +
1.973 +static void
1.974 + plover_transaction_helper_error_dialog_response(GtkDialog *error_dialog,
1.975 + int response_id,PloverTransactionHelper *helper)
1.976 +{
1.977 + g_signal_handlers_disconnect_by_data(error_dialog,helper);
1.978 + if ((GtkWidget *)error_dialog==helper->error_dialog)
1.979 + {
1.980 + gtk_widget_destroy(helper->error_dialog);
1.981 + helper->error_dialog=NULL;
1.982 + if (helper->assistant)
1.983 + {
1.984 + gtk_widget_hide(GTK_WIDGET(helper->assistant));
1.985 + gtk_assistant_set_current_page(helper->assistant,0);
1.986 + }
1.987 + g_signal_emit(helper,signals[CLOSE],0);
1.988 + }
1.989 +}
1.990 +
1.991 +const char *plover_transaction_helper_get_error(PloverTransactionHelper *helper,
1.992 + const GError **error)
1.993 +{
1.994 + g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper));
1.995 + if (!helper->error_dialog)
1.996 + return NULL;
1.997 + if (error)
1.998 + *error=helper->error;
1.999 + return helper->error_primary_text;
1.1000 +}
1.1001 +
1.1002 +void plover_transaction_helper_set_error(PloverTransactionHelper *helper,
1.1003 + const GError *error,const char *primary_text)
1.1004 +{
1.1005 + GtkMessageType type;
1.1006 + GtkWindow *window;
1.1007 + g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper));
1.1008 + g_return_if_fail(error != NULL);
1.1009 + g_return_if_fail(primary_text != NULL);
1.1010 + if (helper->pulse_handler)
1.1011 + {
1.1012 + g_source_remove(helper->pulse_handler);
1.1013 + helper->pulse_handler=0;
1.1014 + }
1.1015 + if (helper->error_dialog)
1.1016 + {
1.1017 + gtk_widget_destroy(helper->error_dialog);
1.1018 + helper->error_dialog=NULL;
1.1019 + }
1.1020 + g_free(helper->error_primary_text);
1.1021 + helper->error_primary_text=g_strdup(primary_text);
1.1022 + g_clear_error(&helper->error);
1.1023 + helper->error=g_error_copy(error);
1.1024 + if (g_error_matches(error,PLOVER_GENERAL_ERROR,
1.1025 + PLOVER_GENERAL_ERROR_NO_WORK))
1.1026 + type=GTK_MESSAGE_INFO;
1.1027 + else
1.1028 + type=GTK_MESSAGE_ERROR;
1.1029 + if (helper->assistant)
1.1030 + window=GTK_WINDOW(helper->assistant);
1.1031 + else
1.1032 + window=NULL;
1.1033 + helper->error_dialog=gtk_message_dialog_new(window,
1.1034 + GTK_DIALOG_DESTROY_WITH_PARENT,type,GTK_BUTTONS_CLOSE,primary_text);
1.1035 + gtk_message_dialog_format_secondary_text(
1.1036 + GTK_MESSAGE_DIALOG(helper->error_dialog),error->message);
1.1037 + gtk_widget_show(helper->error_dialog);
1.1038 + g_signal_connect(helper->error_dialog,"response",
1.1039 + G_CALLBACK(plover_transaction_helper_error_dialog_response),helper);
1.1040 +}