guido+pui@0: /* guido+pui@0: * Copyright (C) 2018 Guido Berhoerster guido+pui@0: * guido+pui@0: * Permission is hereby granted, free of charge, to any person obtaining guido+pui@0: * a copy of this software and associated documentation files (the guido+pui@0: * "Software"), to deal in the Software without restriction, including guido+pui@0: * without limitation the rights to use, copy, modify, merge, publish, guido+pui@0: * distribute, sublicense, and/or sell copies of the Software, and to guido+pui@0: * permit persons to whom the Software is furnished to do so, subject to guido+pui@0: * the following conditions: guido+pui@0: * guido+pui@0: * The above copyright notice and this permission notice shall be included guido+pui@0: * in all copies or substantial portions of the Software. guido+pui@0: * guido+pui@0: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, guido+pui@0: * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF guido+pui@0: * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. guido+pui@0: * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY guido+pui@0: * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, guido+pui@0: * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE guido+pui@0: * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. guido+pui@0: */ guido+pui@0: guido+pui@0: #include guido+pui@0: #include guido+pui@0: #include guido+pui@6: #include guido+pui@0: #include guido+pui@0: #include guido+pui@0: #include guido+pui@5: #include guido+pui@0: #include guido+pui@0: guido+pui@0: #include "pui-common.h" guido+pui@0: #include "pui-backend.h" guido+pui@0: #include "pui-get-updates.h" guido+pui@10: #include "pui-types.h" guido+pui@0: guido+pui@31: #define LOW_BATTERY_THRESHOLD 10.0 /* % */ guido+pui@31: #define UPDATES_CHANGED_UNBLOCK_DELAY 4 /* s */ guido+pui@5: guido+pui@0: struct _PuiBackend { guido+pui@0: GObject parent_instance; guido+pui@0: PkControl *pk_control; guido+pui@0: GCancellable *cancellable; guido+pui@10: PkClient *pk_client; guido+pui@10: PkTransactionList *transaction_list; guido+pui@5: UpClient *up_client; guido+pui@5: UpDevice *up_device; guido+pui@6: gchar *proxy_http; guido+pui@6: gchar *proxy_https; guido+pui@6: gchar *proxy_ftp; guido+pui@6: gchar *proxy_socks; guido+pui@6: gchar *no_proxy; guido+pui@6: gchar *pac; guido+pui@0: gint64 last_check; guido+pui@0: PkNetworkEnum network_state; guido+pui@4: gboolean inhibited; guido+pui@5: gboolean is_battery_low; guido+pui@31: guint check_id; guido+pui@31: guint unblock_updates_changed_id; guido+pui@0: guint refresh_interval; guido+pui@4: gboolean use_mobile_connection; guido+pui@0: guint important_updates; guido+pui@0: guint normal_updates; guido+pui@10: PuiRestart restart_type; guido+pui@0: }; guido+pui@0: guido+pui@0: static void pui_backend_async_initable_iface_init(gpointer, gpointer); guido+pui@0: guido+pui@0: G_DEFINE_TYPE_WITH_CODE(PuiBackend, pui_backend, G_TYPE_OBJECT, guido+pui@0: G_IMPLEMENT_INTERFACE(G_TYPE_ASYNC_INITABLE, guido+pui@0: pui_backend_async_initable_iface_init)) guido+pui@0: guido+pui@0: enum { guido+pui@0: STATE_CHANGED, guido+pui@0: RESTART_REQUIRED, guido+pui@0: SIGNAL_LAST guido+pui@0: }; guido+pui@0: guido+pui@0: enum { guido+pui@0: PROP_0, guido+pui@0: PROP_IMPORTANT_UPDATES, guido+pui@0: PROP_NORMAL_UPDATES, guido+pui@10: PROP_RESTART_TYPE, guido+pui@0: PROP_REFRESH_INTERVAL, guido+pui@4: PROP_USE_MOBILE_CONNECTION, guido+pui@0: PROP_LAST guido+pui@0: }; guido+pui@0: guido+pui@0: static guint signals[SIGNAL_LAST] = { 0 }; guido+pui@0: static GParamSpec *properties[PROP_LAST] = { NULL }; guido+pui@0: guido+pui@0: static gboolean periodic_check(gpointer); guido+pui@31: static void on_updates_changed(PkControl *, gpointer); guido+pui@0: guido+pui@0: GQuark guido+pui@0: pui_backend_error_quark(void) guido+pui@0: { guido+pui@0: return (g_quark_from_static_string("pui-backend-error-quark")); guido+pui@0: } guido+pui@0: guido+pui@0: static void guido+pui@0: process_pk_package(gpointer data, gpointer user_data) guido+pui@0: { guido+pui@0: PkPackage *package = data; guido+pui@0: PuiBackend *self = user_data; guido+pui@0: PkInfoEnum type_info = pk_package_get_info(package); guido+pui@0: guido+pui@0: switch (type_info) { guido+pui@0: case PK_INFO_ENUM_LOW: /* FALLTHROUGH */ guido+pui@0: case PK_INFO_ENUM_ENHANCEMENT: /* FALLTHROUGH */ guido+pui@0: case PK_INFO_ENUM_NORMAL: guido+pui@0: self->normal_updates++; guido+pui@0: break; guido+pui@0: case PK_INFO_ENUM_BUGFIX: /* FALLTHROUGH */ guido+pui@0: case PK_INFO_ENUM_IMPORTANT: /* FALLTHROUGH */ guido+pui@0: case PK_INFO_ENUM_SECURITY: guido+pui@0: self->important_updates++; guido+pui@0: break; guido+pui@0: default: guido+pui@0: break; guido+pui@0: } guido+pui@0: } guido+pui@0: guido+pui@31: static gboolean guido+pui@31: unblock_updates_changed(gpointer user_data) guido+pui@31: { guido+pui@31: PuiBackend *self = user_data; guido+pui@31: guido+pui@31: g_signal_handlers_unblock_by_func(self->pk_control, on_updates_changed, guido+pui@31: self); guido+pui@31: self->unblock_updates_changed_id = 0; guido+pui@31: guido+pui@31: return (G_SOURCE_REMOVE); guido+pui@31: } guido+pui@31: guido+pui@0: static void guido+pui@0: on_get_updates_finished(GObject *source_object, GAsyncResult *async_result, guido+pui@0: gpointer user_data) guido+pui@0: { guido+pui@0: PuiBackend *self = user_data; guido+pui@0: GPtrArray *package_list = NULL; guido+pui@0: GError *error = NULL; guido+pui@0: guint prev_normal_updates = self->normal_updates; guido+pui@0: guint prev_important_updates = self->important_updates; guido+pui@0: guido+pui@0: package_list = pui_get_updates_finish(async_result, &error); guido+pui@0: if (package_list == NULL) { guido+pui@0: if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED) || guido+pui@0: g_error_matches(error, PUI_GET_UPDATES_ERROR, guido+pui@0: PUI_GET_UPDATES_ERROR_CANCELLED)) { guido+pui@0: /* cancelled */ guido+pui@0: g_debug("cancelled checking for updates"); guido+pui@0: } else { guido+pui@0: g_warning("failed to check for updates: %s", guido+pui@0: error->message); guido+pui@0: } guido+pui@0: g_error_free(error); guido+pui@0: goto out; guido+pui@0: } guido+pui@0: guido+pui@0: self->normal_updates = 0; guido+pui@0: self->important_updates = 0; guido+pui@0: g_ptr_array_foreach(package_list, process_pk_package, self); guido+pui@0: g_debug("normal updates: %u, important updates: %u", guido+pui@0: self->normal_updates, self->important_updates); guido+pui@0: if (self->normal_updates != prev_normal_updates) { guido+pui@0: g_object_notify_by_pspec(G_OBJECT(self), guido+pui@0: properties[PROP_NORMAL_UPDATES]); guido+pui@0: } guido+pui@0: if (self->important_updates != prev_important_updates) { guido+pui@0: g_object_notify_by_pspec(G_OBJECT(self), guido+pui@0: properties[PROP_IMPORTANT_UPDATES]); guido+pui@0: } guido+pui@0: if ((self->normal_updates != prev_normal_updates) || guido+pui@0: (self->important_updates != prev_important_updates)) { guido+pui@0: g_debug("emitting signal state-changed"); guido+pui@0: g_signal_emit(self, signals[STATE_CHANGED], 0); guido+pui@0: } guido+pui@0: guido+pui@0: /* last successful check */ guido+pui@0: self->last_check = g_get_monotonic_time(); guido+pui@0: guido+pui@0: out: guido+pui@4: g_clear_object(&self->cancellable); guido+pui@4: guido+pui@0: /* reschedule periodic check */ guido+pui@4: if (!self->inhibited) { guido+pui@31: self->check_id = guido+pui@0: g_timeout_add_seconds(PUI_CHECK_UPDATES_INTERVAL, guido+pui@0: periodic_check, self); guido+pui@0: } guido+pui@0: guido+pui@31: /* handle get-updates signals again after a short delay */ guido+pui@31: self->unblock_updates_changed_id = guido+pui@31: g_timeout_add_seconds(UPDATES_CHANGED_UNBLOCK_DELAY, guido+pui@31: unblock_updates_changed, self); guido+pui@31: guido+pui@0: if (package_list != NULL) { guido+pui@0: g_ptr_array_unref(package_list); guido+pui@0: } guido+pui@0: } guido+pui@0: guido+pui@31: static void guido+pui@31: run_check(PuiBackend *self, gboolean refresh_cache) guido+pui@31: { guido+pui@31: /* block any get-updates signals emitted when refreshing the cache */ guido+pui@31: if (self->unblock_updates_changed_id != 0) { guido+pui@31: /* still blocked */ guido+pui@31: g_source_remove(self->unblock_updates_changed_id); guido+pui@31: self->unblock_updates_changed_id = 0; guido+pui@31: } else { guido+pui@31: g_signal_handlers_block_by_func(self->pk_control, guido+pui@31: G_CALLBACK(on_updates_changed), self); guido+pui@31: } guido+pui@31: guido+pui@31: self->cancellable = g_cancellable_new(); guido+pui@31: pui_get_updates_async(self->pk_control, guido+pui@31: refresh_cache ? self->refresh_interval : G_MAXUINT, guido+pui@31: self->cancellable, on_get_updates_finished, self); guido+pui@31: guido+pui@31: /* next periodic check will be scheduled after completion */ guido+pui@31: self->check_id = 0; guido+pui@31: } guido+pui@31: guido+pui@31: static gboolean guido+pui@31: irregular_check(gpointer user_data) guido+pui@31: { guido+pui@31: PuiBackend *self = user_data; guido+pui@31: guido+pui@31: g_debug("running check"); guido+pui@31: guido+pui@31: run_check(self, FALSE); guido+pui@31: guido+pui@31: return (G_SOURCE_REMOVE); guido+pui@31: } guido+pui@31: guido+pui@0: static gboolean guido+pui@0: periodic_check(gpointer user_data) guido+pui@0: { guido+pui@0: PuiBackend *self = user_data; guido+pui@0: guido+pui@0: g_debug("running periodic check"); guido+pui@0: guido+pui@31: run_check(self, TRUE); guido+pui@0: guido+pui@0: return (G_SOURCE_REMOVE); guido+pui@0: } guido+pui@0: guido+pui@0: static void guido+pui@4: check_inhibit(PuiBackend *self) guido+pui@4: { guido+pui@45: gboolean is_offline; guido+pui@45: gboolean is_disallowed_mobile; guido+pui@4: gboolean inhibited; guido+pui@4: guint elapsed_time; guido+pui@4: guint remaining_time; guido+pui@4: guido+pui@45: is_offline = self->network_state == PK_NETWORK_ENUM_OFFLINE; guido+pui@45: is_disallowed_mobile = !self->use_mobile_connection && guido+pui@45: (self->network_state == PK_NETWORK_ENUM_MOBILE); guido+pui@45: inhibited = is_offline || is_disallowed_mobile || self->is_battery_low; guido+pui@4: if (self->inhibited == inhibited) { guido+pui@4: return; guido+pui@4: } guido+pui@4: guido+pui@4: self->inhibited = inhibited; guido+pui@4: if (inhibited) { guido+pui@4: /* cancel periodic checks */ guido+pui@31: if (self->check_id != 0) { guido+pui@31: g_source_remove(self->check_id); guido+pui@4: } guido+pui@4: guido+pui@4: /* cancel running operation */ guido+pui@4: if ((self->cancellable != NULL) && guido+pui@4: !g_cancellable_is_cancelled(self->cancellable)) { guido+pui@4: g_cancellable_cancel(self->cancellable); guido+pui@4: g_clear_object(&self->cancellable); guido+pui@4: } guido+pui@45: guido+pui@45: if (is_offline) { guido+pui@45: g_debug("perioidic checks inhibited: network offline"); guido+pui@45: } guido+pui@45: if (is_disallowed_mobile) { guido+pui@45: g_debug("perioidic checks inhibited: use of mobile " guido+pui@45: "connection not allowed"); guido+pui@45: } guido+pui@45: if (self->is_battery_low) { guido+pui@45: g_debug("perioidic checks inhibited: low battery"); guido+pui@45: } guido+pui@4: } else { guido+pui@4: /* schedule periodic checks when no longer inhibited */ guido+pui@4: elapsed_time = (g_get_monotonic_time() - self->last_check) / guido+pui@4: G_USEC_PER_SEC; guido+pui@4: /* guido+pui@4: * if more time that the check interval has passed since the guido+pui@4: * last check, schedule a check after a short delay, otherwise guido+pui@4: * wait until the interval has passed guido+pui@4: */ guido+pui@4: remaining_time = (elapsed_time < PUI_CHECK_UPDATES_INTERVAL) ? guido+pui@4: PUI_CHECK_UPDATES_INTERVAL - elapsed_time : guido+pui@4: PUI_STARTUP_DELAY; guido+pui@31: self->check_id = g_timeout_add_seconds(remaining_time, guido+pui@4: periodic_check, self); guido+pui@45: guido+pui@45: g_debug("perioidic checks no longer inhibited, time since " guido+pui@45: "last check: %ds, next check in: %ds", elapsed_time, guido+pui@45: remaining_time); guido+pui@4: } guido+pui@4: } guido+pui@4: guido+pui@4: static void guido+pui@0: pui_backend_set_property(GObject *object, guint property_id, guido+pui@0: const GValue *value, GParamSpec *pspec) guido+pui@0: { guido+pui@0: PuiBackend *self = PUI_BACKEND(object); guido+pui@0: guido+pui@0: switch (property_id) { guido+pui@0: case PROP_REFRESH_INTERVAL: guido+pui@0: self->refresh_interval = g_value_get_uint(value); guido+pui@0: g_debug("property \"refresh-interval\" set to %u", guido+pui@0: self->refresh_interval); guido+pui@0: break; guido+pui@4: case PROP_USE_MOBILE_CONNECTION: guido+pui@4: self->use_mobile_connection = g_value_get_boolean(value); guido+pui@4: g_debug("property \"use-mobile-connection\" set to %s", guido+pui@4: self->use_mobile_connection ? "true" : "false"); guido+pui@4: check_inhibit(self); guido+pui@4: break; guido+pui@0: default: guido+pui@0: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); guido+pui@0: break; guido+pui@0: } guido+pui@0: } guido+pui@0: guido+pui@0: static void guido+pui@0: pui_backend_get_property(GObject *object, guint property_id, GValue *value, guido+pui@0: GParamSpec *pspec) guido+pui@0: { guido+pui@0: PuiBackend *self = PUI_BACKEND(object); guido+pui@0: guido+pui@0: switch (property_id) { guido+pui@0: case PROP_IMPORTANT_UPDATES: guido+pui@0: g_value_set_uint(value, self->important_updates); guido+pui@0: break; guido+pui@0: case PROP_NORMAL_UPDATES: guido+pui@0: g_value_set_uint(value, self->normal_updates); guido+pui@0: break; guido+pui@10: case PROP_RESTART_TYPE: guido+pui@10: g_value_set_enum(value, self->restart_type); guido+pui@10: break; guido+pui@0: case PROP_REFRESH_INTERVAL: guido+pui@0: g_value_set_uint(value, self->refresh_interval); guido+pui@0: break; guido+pui@4: case PROP_USE_MOBILE_CONNECTION: guido+pui@4: g_value_set_boolean(value, self->use_mobile_connection); guido+pui@4: break; guido+pui@0: default: guido+pui@0: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); guido+pui@0: break; guido+pui@0: } guido+pui@0: } guido+pui@0: guido+pui@0: static void guido+pui@0: pui_backend_dispose(GObject *object) guido+pui@0: { guido+pui@0: PuiBackend *self = PUI_BACKEND(object); guido+pui@0: guido+pui@31: if (self->check_id != 0) { guido+pui@31: g_source_remove(self->check_id); guido+pui@31: self->check_id = 0; guido+pui@31: } guido+pui@31: guido+pui@31: if (self->unblock_updates_changed_id != 0) { guido+pui@31: g_source_remove(self->unblock_updates_changed_id); guido+pui@31: self->unblock_updates_changed_id = 0; guido+pui@0: } guido+pui@0: guido+pui@10: if (self->transaction_list != NULL) { guido+pui@10: g_clear_object(&self->transaction_list); guido+pui@10: } guido+pui@10: guido+pui@10: if (self->pk_client != NULL) { guido+pui@10: g_clear_object(&self->pk_client); guido+pui@10: } guido+pui@10: guido+pui@0: if (self->cancellable != NULL) { guido+pui@0: g_cancellable_cancel(self->cancellable); guido+pui@0: g_clear_object(&self->cancellable); guido+pui@0: } guido+pui@0: guido+pui@0: if (self->pk_control != NULL) { guido+pui@0: g_clear_object(&self->pk_control); guido+pui@0: } guido+pui@0: guido+pui@5: if (self->up_device != NULL) { guido+pui@5: g_clear_object(&self->up_device); guido+pui@5: } guido+pui@5: guido+pui@5: if (self->up_client != NULL) { guido+pui@5: g_clear_object(&self->up_client); guido+pui@5: } guido+pui@5: guido+pui@0: G_OBJECT_CLASS(pui_backend_parent_class)->dispose(object); guido+pui@0: } guido+pui@0: guido+pui@0: static void guido+pui@6: pui_backend_finalize(GObject *object) guido+pui@6: { guido+pui@6: PuiBackend *self = PUI_BACKEND(object); guido+pui@6: guido+pui@6: g_free(self->proxy_http); guido+pui@6: g_free(self->proxy_https); guido+pui@6: g_free(self->proxy_ftp); guido+pui@6: g_free(self->proxy_socks); guido+pui@6: g_free(self->no_proxy); guido+pui@6: g_free(self->pac); guido+pui@6: guido+pui@6: G_OBJECT_CLASS(pui_backend_parent_class)->finalize(object); guido+pui@6: } guido+pui@6: guido+pui@6: static void guido+pui@0: pui_backend_class_init(PuiBackendClass *klass) guido+pui@0: { guido+pui@0: GObjectClass *object_class = G_OBJECT_CLASS(klass); guido+pui@0: guido+pui@0: object_class->set_property = pui_backend_set_property; guido+pui@0: object_class->get_property = pui_backend_get_property; guido+pui@0: object_class->dispose = pui_backend_dispose; guido+pui@6: object_class->finalize = pui_backend_finalize; guido+pui@0: guido+pui@0: properties[PROP_IMPORTANT_UPDATES] = guido+pui@0: g_param_spec_uint("important-updates", "Important updates", guido+pui@0: "Number of available important updates", 0, G_MAXUINT, 0, guido+pui@0: G_PARAM_READABLE); guido+pui@0: guido+pui@0: properties[PROP_NORMAL_UPDATES] = guido+pui@0: g_param_spec_uint("normal-updates", "Normal updates", guido+pui@0: "Number of available normal updates", 0, G_MAXUINT, 0, guido+pui@0: G_PARAM_READABLE); guido+pui@0: guido+pui@10: properties[PROP_RESTART_TYPE] = guido+pui@10: g_param_spec_enum("restart-type", "Type of restart required", guido+pui@10: "The Type of restart required", PUI_TYPE_RESTART, PUI_RESTART_NONE, guido+pui@10: G_PARAM_READABLE); guido+pui@10: guido+pui@0: properties[PROP_REFRESH_INTERVAL] = guido+pui@0: g_param_spec_uint("refresh-interval", "Refresh interval", guido+pui@0: "Interval in seconds for refreshing the package cache", 0, guido+pui@0: G_MAXUINT, PUI_DEFAULT_REFRESH_INTERVAL, G_PARAM_READWRITE); guido+pui@0: guido+pui@4: properties[PROP_USE_MOBILE_CONNECTION] = guido+pui@4: g_param_spec_boolean("use-mobile-connection", guido+pui@4: "Whether to use a mobile connection", "Whether to use a mobile " guido+pui@4: "connection for refreshing the package cache", FALSE, guido+pui@4: G_PARAM_READWRITE); guido+pui@4: guido+pui@0: g_object_class_install_properties(object_class, PROP_LAST, properties); guido+pui@0: guido+pui@0: signals[STATE_CHANGED] = g_signal_new("state-changed", guido+pui@0: G_TYPE_FROM_CLASS(object_class), guido+pui@0: G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, guido+pui@0: NULL, NULL, NULL, G_TYPE_NONE, 0); guido+pui@0: guido+pui@0: signals[RESTART_REQUIRED] = g_signal_new("restart-required", guido+pui@0: G_TYPE_FROM_CLASS(object_class), guido+pui@0: G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, guido+pui@0: NULL, NULL, NULL, G_TYPE_NONE, 0); guido+pui@0: } guido+pui@0: guido+pui@0: static void guido+pui@0: pui_backend_init(PuiBackend *self) guido+pui@0: { guido+pui@0: self->pk_control = pk_control_new(); guido+pui@5: guido+pui@10: self->pk_client = pk_client_new(); guido+pui@10: guido+pui@4: self->inhibited = TRUE; guido+pui@5: guido+pui@5: self->up_client = up_client_new(); guido+pui@5: if (self->up_client) { guido+pui@5: self->up_device = up_client_get_display_device(self->up_client); guido+pui@5: } guido+pui@0: } guido+pui@0: guido+pui@0: static void guido+pui@0: on_get_properties_finished(GObject *object, GAsyncResult *result, guido+pui@0: gpointer user_data) guido+pui@0: { guido+pui@0: PkControl *control = PK_CONTROL(object); guido+pui@0: PuiBackend *self; guido+pui@0: GTask *task = user_data; guido+pui@0: GError *error = NULL; guido+pui@0: gchar *backend_name = NULL; guido+pui@0: PkBitfield roles = 0; guido+pui@0: gchar *roles_str = NULL; guido+pui@0: guido+pui@0: self = g_task_get_task_data(task); guido+pui@0: guido+pui@0: if (!pk_control_get_properties_finish(control, result, &error)) { guido+pui@0: g_task_return_error(task, error); guido+pui@0: goto out; guido+pui@0: } guido+pui@0: guido+pui@0: /* check whether the backend supports GetUpdates */ guido+pui@0: g_object_get(control, "backend-name", &backend_name, "roles", &roles, guido+pui@0: "network-state", &self->network_state, NULL); guido+pui@0: g_debug("backend: %s", backend_name); guido+pui@0: roles_str = pk_role_bitfield_to_string(roles); guido+pui@0: g_debug("roles: %s", roles_str); guido+pui@0: g_debug("network-state: %s", guido+pui@0: pk_network_enum_to_string(self->network_state)); guido+pui@0: if (!pk_bitfield_contain(roles, PK_ROLE_ENUM_GET_UPDATES)) { guido+pui@0: error = g_error_new(PUI_BACKEND_ERROR, guido+pui@0: PUI_BACKEND_ERROR_GET_UPDATES_NOT_IMPLEMENTED, guido+pui@0: "Getting updates is not implemented in the %s PackageKit " guido+pui@0: "backend", backend_name); guido+pui@0: g_task_return_error(task, error); guido+pui@0: goto out; guido+pui@0: } guido+pui@0: guido+pui@0: g_task_return_boolean(task, TRUE); guido+pui@0: out: guido+pui@0: g_free(roles_str); guido+pui@0: g_free(backend_name); guido+pui@0: g_object_unref(task); guido+pui@0: } guido+pui@0: guido+pui@0: static void guido+pui@5: on_notify_device_charge_percentage(UpDevice *device, GParamSpec *pspec, guido+pui@5: gpointer user_data) guido+pui@5: { guido+pui@5: PuiBackend *self = user_data; guido+pui@5: UpDeviceKind kind; guido+pui@5: gdouble percentage; guido+pui@5: guido+pui@5: g_object_get(device, "kind", &kind, "percentage", &percentage, NULL); guido+pui@5: if ((kind != UP_DEVICE_KIND_BATTERY) && (kind != UP_DEVICE_KIND_UPS)) { guido+pui@5: return; guido+pui@5: } guido+pui@5: g_debug("charge percentage changed: %.0f%%\n", percentage); guido+pui@5: if ((self->is_battery_low && (percentage > LOW_BATTERY_THRESHOLD)) || guido+pui@5: (!self->is_battery_low && (percentage < LOW_BATTERY_THRESHOLD))) { guido+pui@5: self->is_battery_low = !self->is_battery_low; guido+pui@5: check_inhibit(self); guido+pui@5: } guido+pui@5: } guido+pui@5: guido+pui@5: static void guido+pui@0: on_notify_network_state(PkControl *pk_control, GParamSpec *pspec, guido+pui@0: gpointer user_data) guido+pui@0: { guido+pui@0: PuiBackend *self = user_data; guido+pui@0: guido+pui@4: g_object_get(pk_control, "network-state", &self->network_state, NULL); guido+pui@0: g_debug("network state changed: %s", guido+pui@4: pk_network_enum_to_string(self->network_state)); guido+pui@4: check_inhibit(self); guido+pui@0: } guido+pui@0: guido+pui@0: static void guido+pui@0: on_updates_changed(PkControl *control, gpointer user_data) guido+pui@0: { guido+pui@0: PuiBackend *self = user_data; guido+pui@0: guido+pui@31: g_debug("package metatdata cache invalidated"); guido+pui@10: guido+pui@0: /* guido+pui@0: * schedule a check after a short delay so that a rapid succession of guido+pui@0: * signals is coalesced guido+pui@0: */ guido+pui@4: if (!self->inhibited) { guido+pui@31: if (self->check_id != 0) { guido+pui@31: g_source_remove(self->check_id); guido+pui@0: } guido+pui@32: self->check_id = guido+pui@32: g_timeout_add_seconds(PUI_UPDATES_CHANGED_DELAY, guido+pui@31: irregular_check, self); guido+pui@0: } guido+pui@0: } guido+pui@0: guido+pui@0: static void guido+pui@0: on_restart_schedule(PkControl *control, gpointer user_data) guido+pui@0: { guido+pui@0: PuiBackend *self = user_data; guido+pui@0: guido+pui@10: /* guido+pui@10: * do not restart package-update-indicator if a session or system guido+pui@10: * restart is required since that iformation would be lost across the guido+pui@10: * restart, rather keep running and risk errors when interacting with guido+pui@10: * a newer version of the PackageKit daemon guido+pui@10: */ guido+pui@10: if (self->restart_type > PUI_RESTART_NONE) { guido+pui@10: return; guido+pui@10: } guido+pui@10: guido+pui@0: g_debug("emitting signal restart-required"); guido+pui@0: g_signal_emit(self, signals[RESTART_REQUIRED], 0); guido+pui@0: } guido+pui@0: guido+pui@0: static void guido+pui@10: on_transaction_adopt_finish(GObject *source_object, GAsyncResult *result, guido+pui@10: gpointer user_data) guido+pui@10: { guido+pui@10: PuiBackend *self = user_data; guido+pui@10: PkClient *pk_client = PK_CLIENT(source_object); guido+pui@10: PkResults *results; guido+pui@10: GError *error = NULL; guido+pui@10: PkRestartEnum restart; guido+pui@10: guido+pui@10: results = pk_client_generic_finish(pk_client, result, &error); guido+pui@10: if (results == NULL) { guido+pui@10: g_warning("failed to get transaction results: %s", guido+pui@10: error->message); guido+pui@10: g_error_free(error); guido+pui@10: goto out; guido+pui@10: } guido+pui@10: guido+pui@10: /* check if transaction requires a restart */ guido+pui@10: restart = pk_results_get_require_restart_worst(results); guido+pui@10: switch (restart) { guido+pui@10: case PK_RESTART_ENUM_SESSION: /* FALLTHROUGH */ guido+pui@10: case PK_RESTART_ENUM_SECURITY_SESSION: guido+pui@10: if (self->restart_type < PUI_RESTART_SESSION) { guido+pui@10: self->restart_type = PUI_RESTART_SESSION; guido+pui@10: g_object_notify_by_pspec(G_OBJECT(self), guido+pui@10: properties[PROP_RESTART_TYPE]); guido+pui@10: g_signal_emit(self, signals[STATE_CHANGED], 0); guido+pui@10: } guido+pui@10: break; guido+pui@10: case PK_RESTART_ENUM_SYSTEM: /* FALLTHROUGH */ guido+pui@10: case PK_RESTART_ENUM_SECURITY_SYSTEM: guido+pui@10: if (self->restart_type < PUI_RESTART_SYSTEM) { guido+pui@10: self->restart_type = PUI_RESTART_SYSTEM; guido+pui@10: g_object_notify_by_pspec(G_OBJECT(self), guido+pui@10: properties[PROP_RESTART_TYPE]); guido+pui@10: g_signal_emit(self, signals[STATE_CHANGED], 0); guido+pui@10: } guido+pui@10: break; guido+pui@10: default: guido+pui@10: /* do nothing */ guido+pui@10: break; guido+pui@10: } guido+pui@10: guido+pui@10: g_debug("transaction finished, required restart: %s", guido+pui@10: pk_restart_enum_to_string(restart)); guido+pui@10: guido+pui@10: out: guido+pui@10: if (results != NULL) { guido+pui@10: g_object_unref(results); guido+pui@10: } guido+pui@10: } guido+pui@10: guido+pui@10: static void guido+pui@10: on_transaction_list_added(PkTransactionList *transaction_list, guido+pui@10: const gchar *transaction_id, gpointer user_data) guido+pui@10: { guido+pui@10: PuiBackend *self = user_data; guido+pui@10: guido+pui@10: /* adopt transaction in order to monitor it for restart requirements */ guido+pui@10: pk_client_adopt_async(self->pk_client, transaction_id, NULL, NULL, guido+pui@10: NULL, on_transaction_adopt_finish, user_data); guido+pui@10: } guido+pui@10: guido+pui@10: static void guido+pui@0: pui_backend_init_async(GAsyncInitable *initable, int io_priority, guido+pui@0: GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) guido+pui@0: { guido+pui@0: PuiBackend *self = PUI_BACKEND(initable); guido+pui@0: GTask *task; guido+pui@0: guido+pui@0: task = g_task_new(G_OBJECT(initable), cancellable, callback, user_data); guido+pui@0: g_task_set_priority(task, io_priority); guido+pui@0: g_task_set_task_data(task, g_object_ref(self), guido+pui@0: (GDestroyNotify)g_object_unref); guido+pui@0: guido+pui@0: pk_control_get_properties_async(self->pk_control, cancellable, guido+pui@0: on_get_properties_finished, task); guido+pui@0: } guido+pui@0: guido+pui@0: static gboolean guido+pui@0: pui_backend_init_finish(GAsyncInitable *initable, GAsyncResult *result, guido+pui@0: GError **errorp) guido+pui@0: { guido+pui@0: PuiBackend *self = PUI_BACKEND(initable); guido+pui@0: GTask *task = G_TASK(result); guido+pui@5: UpDeviceKind kind; guido+pui@5: gdouble percentage; guido+pui@0: guido+pui@0: if (!g_task_propagate_boolean(task, errorp)) { guido+pui@0: return (FALSE); guido+pui@0: } guido+pui@0: guido+pui@5: if (self->up_device != NULL) { guido+pui@5: /* get the kind of device and charge percentage */ guido+pui@5: g_object_get(self->up_device, "kind", &kind, "percentage", guido+pui@5: &percentage, NULL); guido+pui@5: if ((kind == UP_DEVICE_KIND_BATTERY) || guido+pui@5: (kind == UP_DEVICE_KIND_UPS)) { guido+pui@5: self->is_battery_low = guido+pui@5: (percentage < LOW_BATTERY_THRESHOLD); guido+pui@5: } guido+pui@5: guido+pui@5: /* get notification if the charge percentage changes */ guido+pui@5: g_signal_connect(self->up_device, "notify::percentage", guido+pui@5: G_CALLBACK(on_notify_device_charge_percentage), self); guido+pui@5: } guido+pui@5: guido+pui@0: /* get notification when the network state changes */ guido+pui@0: g_signal_connect(self->pk_control, "notify::network-state", guido+pui@0: G_CALLBACK(on_notify_network_state), self); guido+pui@31: /* get notifications when the package metatdata cache is invalidated */ guido+pui@0: g_signal_connect(self->pk_control, "updates-changed", guido+pui@0: G_CALLBACK(on_updates_changed), self); guido+pui@0: /* get notifications when an application restart is required */ guido+pui@0: g_signal_connect(self->pk_control, "restart-schedule", guido+pui@0: G_CALLBACK(on_restart_schedule), self); guido+pui@10: /* get notifications when a transactions are added */ guido+pui@10: self->transaction_list = pk_transaction_list_new(); guido+pui@10: g_signal_connect(self->transaction_list, "added", guido+pui@10: G_CALLBACK(on_transaction_list_added), self); guido+pui@4: guido+pui@4: check_inhibit(self); guido+pui@0: guido+pui@0: return (TRUE); guido+pui@0: } guido+pui@0: guido+pui@0: static void guido+pui@0: pui_backend_async_initable_iface_init(gpointer g_iface, gpointer iface_data) guido+pui@0: { guido+pui@0: GAsyncInitableIface *iface = g_iface; guido+pui@0: guido+pui@0: iface->init_async = pui_backend_init_async; guido+pui@0: iface->init_finish = pui_backend_init_finish; guido+pui@0: } guido+pui@0: guido+pui@0: void guido+pui@0: pui_backend_new_async(GCancellable *cancellable, GAsyncReadyCallback callback, guido+pui@0: gpointer user_data) guido+pui@0: { guido+pui@0: g_async_initable_new_async(PUI_TYPE_BACKEND, G_PRIORITY_DEFAULT, guido+pui@0: cancellable, callback, user_data, NULL); guido+pui@0: } guido+pui@0: guido+pui@0: PuiBackend * guido+pui@0: pui_backend_new_finish(GAsyncResult *result, GError **errorp) guido+pui@0: { guido+pui@0: GObject *object; guido+pui@0: GObject *source_object; guido+pui@0: guido+pui@0: source_object = g_async_result_get_source_object(result); guido+pui@0: object = g_async_initable_new_finish(G_ASYNC_INITABLE(source_object), guido+pui@0: result, errorp); guido+pui@0: g_object_unref(source_object); guido+pui@0: guido+pui@0: return ((object != NULL) ? PUI_BACKEND(object) : NULL); guido+pui@0: } guido+pui@6: guido+pui@6: static void guido+pui@6: on_set_proxy_finished(GObject *source_object, GAsyncResult *result, guido+pui@6: gpointer user_data) guido+pui@6: { guido+pui@6: PuiBackend *self = user_data; guido+pui@6: GError *error = NULL; guido+pui@6: guido+pui@6: if (!pk_control_set_proxy_finish(self->pk_control, result, &error)) { guido+pui@6: g_warning("failed to set proxies: %s", error->message); guido+pui@6: g_error_free(error); guido+pui@6: } guido+pui@6: } guido+pui@6: guido+pui@6: static void guido+pui@6: on_polkit_permission_finished(GObject *source_object, GAsyncResult *result, guido+pui@6: gpointer user_data) guido+pui@6: { guido+pui@6: PuiBackend *self = user_data; guido+pui@6: GPermission *permission; guido+pui@6: GError *error = NULL; guido+pui@6: guido+pui@6: permission = polkit_permission_new_finish(result, &error); guido+pui@6: if (permission == NULL) { guido+pui@6: g_warning("failed to create PolKit permission for setting the " guido+pui@6: "network proxies: %s", error->message); guido+pui@6: g_error_free(error); guido+pui@6: return; guido+pui@6: } guido+pui@6: guido+pui@6: if (!g_permission_get_allowed(permission)) { guido+pui@6: /* setting the proxy requires authentication or is disallowed */ guido+pui@6: g_debug("setting the network proxy is not allowed"); guido+pui@6: return; guido+pui@6: } guido+pui@6: guido+pui@6: g_debug("setting HTTP proxy to \"%s\"", (self->proxy_http != NULL) ? guido+pui@6: self->proxy_http : "(null)"); guido+pui@6: g_debug("setting HTTPS proxy to \"%s\"", (self->proxy_https != NULL) ? guido+pui@6: self->proxy_https : "(null)"); guido+pui@6: g_debug("setting FTP proxy to \"%s\"", (self->proxy_ftp != NULL) ? guido+pui@6: self->proxy_ftp : "(null)"); guido+pui@6: g_debug("setting SOCKS proxy to \"%s\"", (self->proxy_socks != NULL) ? guido+pui@6: self->proxy_socks : "(null)"); guido+pui@6: g_debug("setting the list of download IPs which should not go through " guido+pui@6: "a proxy to \"%s\"", (self->no_proxy != NULL) ? self->no_proxy : guido+pui@6: "(null)"); guido+pui@6: g_debug("setting the PAC string to \"%s\"", (self->pac != NULL) ? guido+pui@6: self->pac : "(null)"); guido+pui@6: pk_control_set_proxy2_async(self->pk_control, self->proxy_http, guido+pui@6: self->proxy_https, self->proxy_ftp, self->proxy_socks, guido+pui@6: self->no_proxy, self->pac, NULL, on_set_proxy_finished, self); guido+pui@6: } guido+pui@6: guido+pui@6: void guido+pui@6: pui_backend_set_proxy(PuiBackend *self, const gchar *proxy_http, guido+pui@6: const gchar *proxy_https, const gchar *proxy_ftp, const gchar *proxy_socks, guido+pui@6: const gchar *no_proxy, const gchar *pac) guido+pui@6: { guido+pui@6: g_free(self->proxy_http); guido+pui@6: self->proxy_http = (proxy_http != NULL) ? g_strdup(proxy_http) : NULL; guido+pui@6: g_free(self->proxy_https); guido+pui@6: self->proxy_https = (proxy_https != NULL) ? g_strdup(proxy_https) : guido+pui@6: NULL; guido+pui@6: g_free(self->proxy_ftp); guido+pui@6: self->proxy_ftp = (proxy_ftp != NULL) ? g_strdup(proxy_ftp) : NULL; guido+pui@6: g_free(self->proxy_socks); guido+pui@6: self->proxy_socks = (proxy_socks != NULL) ? g_strdup(proxy_socks) : guido+pui@6: NULL; guido+pui@6: g_free(self->no_proxy); guido+pui@6: self->no_proxy = (no_proxy != NULL) ? g_strdup(no_proxy) : NULL; guido+pui@6: g_free(self->pac); guido+pui@6: self->pac = (pac != NULL) ? g_strdup(pac) : NULL; guido+pui@6: guido+pui@6: polkit_permission_new("org.freedesktop.packagekit." guido+pui@6: "system-network-proxy-configure", NULL, NULL, guido+pui@6: on_polkit_permission_finished, self); guido+pui@6: }