10
10
#include <glib/gstdio.h>
12
13
#include <sys/types.h>
13
14
#include <sys/stat.h>
14
15
#include <unistd.h>
15
16
#include <stdlib.h>
16
#include <dbus/dbus-glib.h>
18
18
#include "update-notifier.h"
19
19
#include "update.h"
20
#include "trayappletui.h"
21
22
#define UPGRADE_CHECKER PACKAGE_LIB_DIR"/update-notifier/apt-check"
23
#define PACKAGE_SYSTEM_LOCKED PACKAGE_LIB_DIR"/update-notifier/package-system-locked"
23
// command, description, desktopfile, needs_gksu
25
// command, description, desktopfile, needs_pkexec
24
26
const char* actions[][4] = {
25
{ "/usr/bin/update-manager", N_("Show updates"),
27
{ "/usr/lib/update-notifier/backend_helper.py show_updates",
26
29
"/usr/share/applications/update-manager.desktop",
27
30
GINT_TO_POINTER(FALSE) },
29
{ "/usr/sbin/synaptic --dist-upgrade-mode --non-interactive --hide-main-window -o Synaptic::AskRelated=true",
31
{ "/usr/lib/update-notifier/backend_helper.py install_all_updates",
30
32
N_("Install all updates"), "/usr/share/applications/synaptic.desktop",
33
GINT_TO_POINTER(FALSE)
33
{ "/usr/sbin/synaptic --update-at-startup --non-interactive --hide-main-window",
35
{ "/usr/lib/update-notifier/backend_helper.py check_updates",
34
36
N_("Check for updates"), "/usr/share/applications/synaptic.desktop",
35
GINT_TO_POINTER(TRUE) },
36
{ "/usr/sbin/synaptic", N_("Start package manager"),
37
"/usr/share/applications/synaptic.desktop",
38
GINT_TO_POINTER(TRUE)},
37
GINT_TO_POINTER(FALSE) },
38
{ "/usr/lib/update-notifier/backend_helper.py start_packagemanager",
39
N_("Start package manager"), "/usr/share/applications/synaptic.desktop",
40
GINT_TO_POINTER(FALSE)},
39
41
{ NULL, NULL, NULL }
69
72
last_action = time(NULL);
74
popup_cb (GtkStatusIcon *status_icon,
79
gtk_menu_set_screen (GTK_MENU (un->menu),
80
gtk_status_icon_get_screen(un->tray_icon));
81
gtk_menu_popup (GTK_MENU (un->menu), NULL, NULL,
82
gtk_status_icon_position_menu, un->tray_icon,
83
button, activate_time);
88
78
update_trayicon_update_tooltip (TrayApplet *ta, int num_upgrades)
90
80
//g_print("update_tooltip: %p %p %p\n", ta, ta->tooltip, ta->eventbox);
94
84
"There are %i updates available",
97
gtk_status_icon_set_tooltip(ta->tray_icon, updates);
87
tray_applet_ui_set_tooltip_text(ta, updates);
103
cb_action(GObject *self, void *user_data)
93
cb_action(GObject *self, void *user_data __attribute__ ((__unused__)))
105
TrayApplet *ta = user_data;
107
95
int i = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(self), "action"));
108
96
invoke (actions[i][0], _(actions[i][2]), (long)actions[i][3]);
110
UpdateTrayAppletPrivate *priv = (UpdateTrayAppletPrivate*)ta->user_data;
111
gconf_client_set_int(priv->gconf, GCONF_KEY_DEFAULT_ACTION, i, NULL);
115
100
cb_preferences(GObject *self, void *user_data)
117
invoke_with_gksu("/usr/bin/software-properties-gtk",
118
"/usr/share/applications/software-properties.desktop",
102
invoke("/usr/bin/software-properties-gtk",
103
"/usr/share/applications/software-properties.desktop",
123
108
cb_toggled_show_notifications(GObject *self, void *data)
125
110
TrayApplet *ta = (TrayApplet*)data;
126
111
UpdateTrayAppletPrivate *priv = (UpdateTrayAppletPrivate*)ta->user_data;
128
113
gboolean b = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(self));
129
gconf_client_set_bool(priv->gconf, GCONF_KEY_NO_UPDATE_NOTIFICATIONS,
114
g_settings_set_boolean(ta->un->settings,
115
SETTINGS_KEY_NO_UPDATE_NOTIFICATIONS, !b);
133
NotifyNotification *n = priv->active_notification;
135
notify_notification_close(n, NULL);
136
priv->active_notification = NULL;
118
if(priv->active_notification != NULL) {
119
notify_notification_close(priv->active_notification, NULL);
120
g_clear_object(&priv->active_notification);
142
126
update_trayicon_create_menu(TrayApplet *ta)
144
128
GtkWidget *menuitem;
161
145
gtk_menu_shell_append (GTK_MENU_SHELL (ta->menu), menuitem);
163
147
menuitem = gtk_check_menu_item_new_with_label(_("Show notifications"));
164
UpdateTrayAppletPrivate *priv = (UpdateTrayAppletPrivate*)ta->user_data;
165
gboolean b = gconf_client_get_bool(priv->gconf,
166
GCONF_KEY_NO_UPDATE_NOTIFICATIONS,
148
gboolean b = g_settings_get_boolean(ta->un->settings,
149
SETTINGS_KEY_NO_UPDATE_NOTIFICATIONS);
168
150
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), !b);
169
151
gtk_menu_shell_append (GTK_MENU_SHELL (ta->menu), menuitem);
170
152
g_signal_connect(G_OBJECT(menuitem), "toggled",
192
174
UpdateTrayAppletPrivate *priv = (UpdateTrayAppletPrivate*)ta->user_data;
193
175
priv->apt_is_running = is_running;
195
// if the user wants auto-launch mode, do not show the icon
196
// the auto launch stuff will do its magic later
197
if(gconf_client_get_bool(priv->gconf, GCONF_KEY_AUTO_LAUNCH, NULL))
177
// everybody wants auto-launch mode
181
#ifdef HAVE_APP_INDICATOR
182
// we can't do fancy stuff like the gray-out with app-indicator,
183
// so we just hide the tray thing here
184
tray_applet_ui_set_visible(ta, FALSE);
202
186
// gray out the icon if apt is running, we do this only once,
203
187
// after the first time, the storage_type changes from ICON_NAME to
218
202
// and update the tooltip
219
gtk_status_icon_set_tooltip(ta->tray_icon, _("A package manager is working"));
203
tray_applet_ui_set_tooltip_text(ta->tray_icon, _("A package manager is working"));
221
gtk_status_icon_set_visible(ta->tray_icon, TRUE);
205
tray_applet_ui_set_visible(ta->tray_icon, TRUE);
223
208
if(priv->active_notification != NULL) {
224
209
notify_notification_close(priv->active_notification, NULL);
225
priv->active_notification = NULL;
210
g_clear_object(&priv->active_notification);
228
gtk_status_icon_set_from_icon_name(ta->tray_icon, ta->name);
213
tray_applet_ui_set_icon(ta, ta->name);
236
221
TrayApplet *ta = (TrayApplet *)user_data;
237
222
UpdateTrayAppletPrivate *priv = (UpdateTrayAppletPrivate*)ta->user_data;
239
// apt is runing, no point in showing a notification
224
// apt is running, no point in showing a notification
240
225
if(priv->apt_is_running)
243
228
// check if the update-icon is still visible (in the delay time a
244
229
// update may already have been performed)
245
if(!gtk_status_icon_get_visible(ta->tray_icon))
230
if(!tray_applet_ui_get_visible(ta))
249
gtk_status_icon_get_geometry(ta->tray_icon, NULL, &area, NULL);
251
// no usefull coordiante yet, do another timeout
252
if(area.x <= 0 || area.y <= 0 || area.width <= 0 || area.height <= 0)
255
233
// now show a notification handle
257
235
updates = g_strdup_printf(ngettext("There is %i update available. "
288
267
show_error(TrayApplet *ta, gchar *error_str)
290
gtk_status_icon_set_tooltip(ta->tray_icon, error_str);
291
gtk_status_icon_set_from_icon_name(ta->tray_icon, "dialog-error");
292
gtk_status_icon_set_visible(ta->tray_icon, TRUE);
269
tray_applet_ui_set_tooltip_text(ta, error_str);
270
tray_applet_ui_set_icon(ta, "dialog-error");
271
tray_applet_ui_set_visible(ta, TRUE);
299
278
if ((stat("/var/lib/apt/periodic/update-success-stamp", &buf) == 0) &&
300
279
(time(NULL) - buf.st_mtime > OUTDATED_NAG_AGE) ) {
301
gtk_status_icon_set_visible (ta->tray_icon, TRUE);
280
tray_applet_ui_set_visible (ta, TRUE);
302
281
ta->name = "gtk-dialog-warning-panel";
303
GdkPixbuf *src = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(),
305
GTK_ICON_LOOKUP_GENERIC_FALLBACK,
307
gtk_status_icon_set_from_pixbuf(ta->tray_icon, src);
309
gtk_status_icon_set_tooltip(ta->tray_icon,
282
tray_applet_ui_set_icon(ta, ta->name);
283
tray_applet_ui_set_tooltip_text(ta,
310
284
_("The update information is outdated. "
311
285
"This may be caused by network "
312
286
"problems or by a repository that "
313
287
"is no longer available. "
314
288
"Please update manually "
315
"by clicking on this icon and then "
316
"selecting 'Check for updates' and "
317
"check if some of the listed "
289
"by selecting 'Show updates' from "
290
"the indicator menu, and watching "
291
"for any failing repositories."
324
// use ubuntu-system-service (if available) to check
297
// use package-system-locked to check
325
298
// if the dpkg lock is taken currently or not
327
300
// if uncertain, return FALSE
329
dpkg_lock_is_taken ()
302
dpkg_lock_is_taken (void)
331
DBusGConnection *connection;
334
gboolean locked = FALSE;
337
connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
338
if (connection == NULL) {
339
g_debug_update ("Failed to open connection to bus: %s\n", error->message);
340
g_error_free (error);
344
proxy = dbus_g_proxy_new_for_name (connection,
345
"com.ubuntu.SystemService",
347
"com.ubuntu.SystemService");
349
if (!dbus_g_proxy_call (proxy, "is_package_system_locked", &error,
351
G_TYPE_BOOLEAN, &locked, G_TYPE_INVALID)) {
352
g_debug_update ("error during dbus call: %s\n", error->message);
353
g_error_free (error);
354
g_object_unref (proxy);
357
g_object_unref (proxy);
359
g_debug_update ("is_package_system_locked: %i", locked);
304
gchar *cmd[] = { "pkexec", PACKAGE_SYSTEM_LOCKED, NULL };
308
if(!g_spawn_sync(NULL, cmd, NULL, G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL,
309
NULL, NULL, NULL, NULL, &exit_status, &err)) {
310
g_warning("failed to run " PACKAGE_SYSTEM_LOCKED ": %s", err->message);
315
if(!WIFEXITED(exit_status)) {
316
g_warning(PACKAGE_SYSTEM_LOCKED " exited abnormally with code %i", exit_status);
320
exit_status = WEXITSTATUS(exit_status);
321
g_debug_update ("is_package_system_locked: " PACKAGE_SYSTEM_LOCKED "returned %i", exit_status);
322
return exit_status == 2;
364
326
// ask devicekit.Power if we run on battery
366
running_on_battery ()
328
running_on_battery (void)
368
330
DBusGConnection *connection;
419
381
if (g_spawn_sync("/", cmd, NULL, 0, NULL, NULL,
420
382
NULL, NULL, &ret, &error)) {
421
g_debug("--security-updates-unattended: %i\n", WEXITSTATUS(ret));
383
g_debug("--security-updates-unattended: %i", WEXITSTATUS(ret));
422
384
if( WEXITSTATUS(ret) > 0 )
424
386
} else if (error) {
422
// Like ctime, but without the annoying trailing newline. Caller must
423
// g_free the result.
425
readable_timestamp(time_t time)
430
datetime = g_date_time_new_from_unix_local (time);
431
formatted = g_date_time_format (datetime, "%a %b %e %T %Y");
432
g_date_time_unref (datetime);
460
436
// check the logs of dpkg and apt for timestamps, the idea
461
437
// is to not auto launch if dpkg/apt were run manually by the
464
newest_log_file_timestamp()
440
newest_log_file_timestamp(void)
477
453
if(glob(log_glob[i], 0, NULL, &pglob) != 0) {
478
g_warning("error from glob %s\n", log_glob[i]);
454
g_warning("error from glob %s", log_glob[i]);
481
457
for(j=0;j < pglob.gl_pathc; j++) {
483
458
const char *log = pglob.gl_pathv[j];
484
462
if(g_stat(log, &buf) <0) {
485
g_warning("can't stat %s\n", log);
463
g_warning("can't stat %s", log);
488
466
if(buf.st_size == 0) {
489
g_warning("log file empty (logrotate?) %s\n", log);
467
g_warning("log file empty (logrotate?) %s", log);
492
time_t mtime = buf.st_mtime;
493
g_debug_update ("mtime from %s: %i (%s)\n", log, mtime, ctime(&mtime));
494
time_t bctime = buf.st_ctime;
495
g_debug_update ("ctime from %s: %i (%s)\n", log, bctime, ctime(&bctime));
496
newest_log_stamp = MAX(MAX(mtime, bctime), newest_log_stamp);
497
g_debug_update ("last_launch from %s: %i (%s)\n", log, newest_log_stamp, ctime(&newest_log_stamp));
471
mtime = buf.st_mtime;
472
timestamp = readable_timestamp (mtime);
473
g_debug_update ("mtime from %s: %i (%s)", log, mtime, timestamp);
476
newest_log_stamp = MAX(mtime, newest_log_stamp);
477
timestamp = readable_timestamp (newest_log_stamp);
478
g_debug_update ("last_launch from %s: %i (%s)", log, newest_log_stamp, timestamp);
499
481
globfree(&pglob);
522
505
// when checking for regular updates honor the
523
// regular_auto_launch_interval
524
interval_days = gconf_client_get_int(priv->gconf,
525
GCONF_KEY_AUTO_LAUNCH_INTERVAL,
527
g_debug_update ("interval_days from gconf: %i\n", interval_days);
506
// regular-auto-launch-interval
507
interval_days = g_settings_get_int(ta->un->settings,
508
SETTINGS_KEY_AUTO_LAUNCH_INTERVAL);
509
g_debug_update ("interval_days: %i", interval_days);
529
511
if (interval_days <= 0)
532
514
// check last launch time
533
last_launch = gconf_client_get_int(priv->gconf,
534
GCONF_KEY_LAST_LAUNCH,
536
g_debug_update ("last_launch from gconf: %i (%s)\n", last_launch, ctime(&last_launch));
515
GSettings *um_settings = g_settings_new(SETTINGS_UM_SCHEMA);
516
last_launch = g_settings_get_int64(um_settings,
517
SETTINGS_UM_KEY_LAST_LAUNCH);
518
g_debug_update ("last_launch: %i (%s)", last_launch, ctime(&last_launch));
519
g_object_unref(um_settings);
538
521
time_t now = time(NULL);
539
522
if (auto_launch_security_now(priv, now, last_launch))
542
time_t log_files = newest_log_file_timestamp();
543
last_launch = MAX(log_files, last_launch);
545
525
g_debug_update("time now %i (%s), delta: %i", now, ctime(&now), now-last_launch);
546
526
if ((last_launch + (24*60*60*interval_days)) < now) {
547
527
g_debug_update ("need to auto launch");
628
610
g_debug_update("%s returned %i (security: %i)", UPGRADE_CHECKER, priv->num_upgrades, priv->num_security);
630
if (priv->num_upgrades == 0) {
612
if (stat("/run/reboot-required", &buf) == 0) {
613
priv->reboot_pending = TRUE;
614
g_debug_update("A reboot is currently pending.");
617
if ((priv->num_upgrades == 0) && !priv->reboot_pending) {
632
618
// check if the periodic file is very old
634
619
if ((stat("/var/lib/apt/periodic/update-success-stamp", &buf) == 0) &&
635
620
(time(NULL) - buf.st_mtime > OUTDATED_NAG_AGE) )
636
621
g_timeout_add_seconds(OUTDATED_NAG_WAIT, (GSourceFunc)outdated_nag, ta);
638
gtk_status_icon_set_visible (ta->tray_icon, FALSE);
623
tray_applet_ui_set_visible (ta, FALSE);
640
624
if(priv->active_notification != NULL) {
641
625
notify_notification_close(priv->active_notification, NULL);
642
priv->active_notification = NULL;
626
g_clear_object(&priv->active_notification);
648
632
ta->name = "software-update-available";
650
634
ta->name = "software-update-urgent";
651
gtk_status_icon_set_from_icon_name(ta->tray_icon, ta->name);
635
tray_applet_ui_set_icon(ta, ta->name);
653
637
// update the tooltip
654
638
update_trayicon_update_tooltip(ta, priv->num_upgrades);
656
// check if the user wants to see the icon or launch
657
// the default action
658
if(gconf_client_get_bool(priv->gconf,
659
GCONF_KEY_AUTO_LAUNCH, NULL))
640
tray_applet_ui_set_visible(ta, FALSE);
641
if (auto_launch_now(ta))
661
gtk_status_icon_set_visible(ta->tray_icon, FALSE);
662
if (auto_launch_now(priv))
664
g_spawn_command_line_async("nice ionice -c3 update-manager "
665
"--no-focus-on-map", NULL);
643
g_spawn_command_line_async("nice ionice -c3 update-manager "
644
"--no-update --no-focus-on-map", NULL);
670
648
// if we are already visible, skip the rest
671
if(gtk_status_icon_get_visible (ta->tray_icon))
649
if(tray_applet_ui_get_visible (ta))
675
gtk_status_icon_set_visible(ta->tray_icon, TRUE);
653
tray_applet_ui_set_visible(ta, TRUE);
677
655
// the user does not no notification messages
678
if(gconf_client_get_bool(priv->gconf,
679
GCONF_KEY_NO_UPDATE_NOTIFICATIONS, NULL))
656
if(g_settings_get_boolean(ta->un->settings,
657
SETTINGS_KEY_NO_UPDATE_NOTIFICATIONS))
682
660
// show the notification with some delay. otherwise on a login
693
671
// create the private data struct
694
672
UpdateTrayAppletPrivate *priv = g_new0(UpdateTrayAppletPrivate, 1);
695
priv->gconf = gconf_client_get_default();
696
673
priv->apt_is_running = FALSE;
697
674
priv->active_notification = NULL;
698
675
ta->user_data = priv;
700
// connect the signals we need
677
// create the indicator/icon immediately; we use this enough that it
678
// probably isn't worth trying to save memory by avoiding it
679
tray_applet_ui_ensure (ta);
681
#ifndef HAVE_APP_INDICATOR
682
// the default action for the gtk_status_icon
701
683
g_signal_connect (G_OBJECT(ta->tray_icon),
703
G_CALLBACK (activate_cb),
705
g_signal_connect (G_OBJECT(ta->tray_icon),
707
G_CALLBACK (popup_cb),
685
G_CALLBACK (gtk_status_icon_click_activate_cb),
710
689
/* Menu initialization */
711
690
update_trayicon_create_menu (ta);
691
tray_applet_ui_set_menu (ta, ta->menu);
713
693
/* Check for updates for the first time */
714
694
update_check (ta);