~ubuntu-core-dev/update-notifier/ubuntu

« back to all changes in this revision

Viewing changes to src/update.c

  • Committer: Julian Andres Klode
  • Date: 2020-01-28 09:55:09 UTC
  • Revision ID: juliank@ubuntu.com-20200128095509-93wh1wcbisnm9xtq
Tags: 3.192.28
releasing package update-notifier version 3.192.28

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
#include "config.h"
3
3
#endif
4
4
 
5
 
#include <glade/glade.h>   
6
5
#include <libintl.h>
7
6
#include <sys/wait.h>
 
7
#include <glob.h>
 
8
 
 
9
#include <glib.h>
 
10
#include <glib/gstdio.h>
 
11
#include <gio/gio.h>
 
12
 
 
13
#include <sys/types.h>
 
14
#include <sys/stat.h>
 
15
#include <unistd.h>
 
16
#include <stdlib.h>
8
17
 
9
18
#include "update-notifier.h"
10
19
#include "update.h"
 
20
#include "trayappletui.h"
11
21
 
12
22
#define UPGRADE_CHECKER PACKAGE_LIB_DIR"/update-notifier/apt-check"
13
 
 
14
 
// command, description, desktopfile, needs_gksu
15
 
char* actions[][4] = {
16
 
   { "/usr/bin/update-manager", N_("Show updates"), 
17
 
     "/usr/share/applications/update-manager.desktop", FALSE },
18
 
 
19
 
   { "/usr/sbin/synaptic --dist-upgrade-mode --non-interactive --hide-main-window -o Synaptic::AskRelated=true",
20
 
     N_("Install all updates"), "/usr/share/applications/synaptic.desktop", TRUE
21
 
 },
22
 
   { "/usr/sbin/synaptic --update-at-startup --non-interactive --hide-main-window", N_("Check for updates"), "/usr/share/applications/synaptic.desktop", TRUE },
23
 
   { "/usr/sbin/synaptic", N_("Start package manager"), 
24
 
     "/usr/share/applications/synaptic.desktop", TRUE},
 
23
#define PACKAGE_SYSTEM_LOCKED PACKAGE_LIB_DIR"/update-notifier/package-system-locked"
 
24
 
 
25
// command, description, desktopfile, needs_pkexec
 
26
const char* actions[][4] = {
 
27
   { "/usr/lib/update-notifier/backend_helper.py show_updates",
 
28
     N_("Show updates"), 
 
29
     "/usr/share/applications/update-manager.desktop", 
 
30
     GINT_TO_POINTER(FALSE) },
 
31
   { "/usr/lib/update-notifier/backend_helper.py install_all_updates",
 
32
     N_("Install all updates"), "/usr/share/applications/synaptic.desktop", 
 
33
     GINT_TO_POINTER(FALSE)
 
34
   },
 
35
   { "/usr/lib/update-notifier/backend_helper.py check_updates",
 
36
     N_("Check for updates"), "/usr/share/applications/synaptic.desktop", 
 
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)},
25
41
   { NULL, NULL, NULL }
26
42
};
27
43
 
28
 
 
29
 
enum { NOTIFICATION_DEFAULT, NOTIFICATION_IGNORE, NOTIFICATION_SHOW_UPDATES };
30
 
 
 
44
enum { 
 
45
   NOTIFICATION_DEFAULT, 
 
46
   NOTIFICATION_IGNORE, 
 
47
   NOTIFICATION_SHOW_UPDATES 
 
48
};
 
49
 
 
50
static inline void
 
51
g_debug_update(const char *msg, ...)
 
52
{
 
53
   va_list va;
 
54
   va_start(va, msg);
 
55
   g_logv("update",G_LOG_LEVEL_DEBUG, msg, va);
 
56
   va_end(va);
 
57
}
 
58
 
 
59
#ifndef HAVE_APP_INDICATOR
31
60
static gboolean
32
 
activate_cb (GtkWidget *widget, 
 
61
gtk_status_icon_click_activate_cb (GtkWidget *widget, 
33
62
             TrayApplet *ta)
34
63
{
35
64
   UpdateTrayAppletPrivate *priv = (UpdateTrayAppletPrivate*)ta->user_data;
36
 
 
37
 
   int index;
38
 
   GTimeVal current_time;
39
 
   static GTimeVal last_launch = { 0, 0 };
40
 
 
41
 
   index = 0;
42
 
   invoke (actions[index][0], actions[index][2], actions[index][3]);
43
 
 
44
 
   return TRUE;
45
 
}
46
 
 
47
 
static gboolean
48
 
popup_cb (GtkStatusIcon *status_icon,
49
 
          guint          button,
50
 
          guint          activate_time,
51
 
          TrayApplet     *un)
52
 
{
53
 
   gtk_menu_set_screen (GTK_MENU (un->menu),
54
 
                        gtk_status_icon_get_screen(un->tray_icon));
55
 
   gtk_menu_popup (GTK_MENU (un->menu), NULL, NULL, 
56
 
                   gtk_status_icon_position_menu, un->tray_icon,
57
 
                   button, activate_time);
58
 
   return TRUE;
59
 
}
60
 
 
61
 
void
 
65
   int index = priv->apt_is_running ? 3 : 0;
 
66
   
 
67
   // on double click we get two activate signals, so slow down things
 
68
   // here a bit to avoid double starting
 
69
   static time_t last_action = 0;
 
70
   if (time(NULL) - last_action > 1) 
 
71
      invoke (actions[index][0], actions[index][2], (long)actions[index][3]);
 
72
   last_action = time(NULL);
 
73
   return TRUE;
 
74
}
 
75
#endif
 
76
 
 
77
static void
62
78
update_trayicon_update_tooltip (TrayApplet *ta, int num_upgrades)
63
79
{
64
80
   //g_print("update_tooltip: %p %p %p\n", ta, ta->tooltip, ta->eventbox);
65
81
   gchar *updates;
66
 
   gchar *explanation;
67
82
 
68
 
   updates=g_strdup_printf(ngettext("There is %i update available",
69
 
                                    "There are %i updates available",
70
 
                                    num_upgrades),
71
 
                           num_upgrades);
72
 
   gtk_status_icon_set_tooltip(ta->tray_icon, updates);
 
83
   updates = g_strdup_printf(ngettext("There is %i update available",
 
84
                                      "There are %i updates available",
 
85
                                      num_upgrades),
 
86
                             num_upgrades);
 
87
   tray_applet_ui_set_tooltip_text(ta, updates);
73
88
   g_free(updates);
74
89
}
75
90
 
76
91
 
77
 
void 
78
 
cb_action(GObject *self, void *user_data)
 
92
static void 
 
93
cb_action(GObject *self, void *user_data __attribute__ ((__unused__)))
79
94
{
80
 
   TrayApplet *ta = user_data;
81
 
        
82
95
   int i = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(self), "action"));
83
 
   invoke (actions[i][0], _(actions[i][2]), actions[i][3]);
84
 
 
85
 
   UpdateTrayAppletPrivate *priv = (UpdateTrayAppletPrivate*)ta->user_data;
86
 
   gconf_client_set_int(priv->gconf, GCONF_KEY_DEFAULT_ACTION, i, NULL);
 
96
   invoke (actions[i][0], _(actions[i][2]), (long)actions[i][3]);
87
97
}
88
98
 
89
 
void 
 
99
static void 
90
100
cb_preferences(GObject *self, void *user_data)
91
101
{
92
 
   invoke_with_gksu("/usr/bin/software-properties-gtk",
93
 
                    "/usr/share/applications/software-properties.desktop",
94
 
                    FALSE);    
 
102
   invoke("/usr/bin/software-properties-gtk",
 
103
          "/usr/share/applications/software-properties.desktop",
 
104
          FALSE);    
95
105
}
96
106
 
97
 
void 
 
107
static void 
98
108
cb_toggled_show_notifications(GObject *self, void *data)
99
109
{
100
110
      TrayApplet *ta = (TrayApplet*)data;
101
111
      UpdateTrayAppletPrivate *priv = (UpdateTrayAppletPrivate*)ta->user_data;
102
112
 
103
113
      gboolean b = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(self));
104
 
      gconf_client_set_bool(priv->gconf, GCONF_KEY_NO_UPDATE_NOTIFICATIONS, 
105
 
                            !b,NULL);
 
114
      g_settings_set_boolean(ta->un->settings,
 
115
                             SETTINGS_KEY_NO_UPDATE_NOTIFICATIONS, !b);
106
116
      
107
117
 
108
 
      NotifyNotification *n = priv->active_notification;
109
 
      if(n != NULL) {
110
 
         notify_notification_close(n, NULL);
111
 
         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);
112
121
      }
113
122
}
114
123
 
115
124
 
116
 
void
 
125
static void
117
126
update_trayicon_create_menu(TrayApplet *ta)
118
127
{
119
128
        GtkWidget *menuitem;
120
 
        GtkAccelGroup *accelgroup;
121
129
        int i;
122
130
 
123
131
        ta->menu = gtk_menu_new ();
124
132
 
125
133
        for(i=0;actions[i][0]!=NULL;i++) {
 
134
            if (!g_file_test(actions[i][2], G_FILE_TEST_EXISTS))
 
135
                continue;
126
136
           menuitem = gtk_menu_item_new_with_label (_(actions[i][1]));
 
137
 
127
138
           gtk_menu_shell_append (GTK_MENU_SHELL (ta->menu), menuitem);
128
139
           g_object_set_data(G_OBJECT(menuitem), "action", GINT_TO_POINTER(i));
129
140
           g_signal_connect(G_OBJECT(menuitem), "activate", 
134
145
        gtk_menu_shell_append (GTK_MENU_SHELL (ta->menu), menuitem);
135
146
 
136
147
        menuitem = gtk_check_menu_item_new_with_label(_("Show notifications"));
137
 
        UpdateTrayAppletPrivate *priv = (UpdateTrayAppletPrivate*)ta->user_data;
138
 
        gboolean b = gconf_client_get_bool(priv->gconf,
139
 
                                           GCONF_KEY_NO_UPDATE_NOTIFICATIONS, 
140
 
                                           NULL);
 
148
        gboolean b = g_settings_get_boolean(ta->un->settings,
 
149
                                            SETTINGS_KEY_NO_UPDATE_NOTIFICATIONS);
141
150
        gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), !b);
142
151
        gtk_menu_shell_append (GTK_MENU_SHELL (ta->menu), menuitem);
143
152
        g_signal_connect(G_OBJECT(menuitem), "toggled", 
144
153
                         G_CALLBACK(cb_toggled_show_notifications), ta);
145
154
        
146
155
 
147
 
        menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_PREFERENCES, accelgroup);
148
 
        gtk_menu_shell_append (GTK_MENU_SHELL (ta->menu), menuitem);
 
156
        menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_PREFERENCES, NULL);
 
157
        if (g_file_test("/usr/bin/software-properties-gtk", G_FILE_TEST_EXISTS))
 
158
            gtk_menu_shell_append (GTK_MENU_SHELL (ta->menu), menuitem);
149
159
        g_signal_connect(G_OBJECT(menuitem), "activate", 
150
160
                         G_CALLBACK(cb_preferences), (void*)ta);
151
161
 
156
166
void 
157
167
update_apt_is_running(TrayApplet *ta, gboolean is_running)
158
168
{
 
169
   g_debug_update("update_apt_is_running: %i",is_running);
 
170
   if(ta == NULL)
 
171
      return;
 
172
 
 
173
   // update internal status
159
174
   UpdateTrayAppletPrivate *priv = (UpdateTrayAppletPrivate*)ta->user_data;
160
 
   //g_print("update_apt_is_running: %i\n",is_running);
161
 
 
162
 
   //  we load a different icon to show that apt is running
163
 
   // FIXME: we need block menu events
164
 
   gchar *inactive_name = g_strdup_printf("%s-inactive", ta->name);
165
 
   if(is_running)
166
 
      gtk_status_icon_set_from_icon_name(ta->tray_icon, inactive_name);
167
 
   else
168
 
      gtk_status_icon_set_from_icon_name(ta->tray_icon, ta->name);
169
 
 
170
 
   g_free(inactive_name);
171
175
   priv->apt_is_running = is_running;
172
176
 
 
177
   // everybody wants auto-launch mode
 
178
   return;
 
179
 
173
180
   if(is_running) {
174
 
      gtk_status_icon_set_tooltip(ta->tray_icon,
175
 
                                  _("A package manager is working"));
 
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);
 
185
#else
 
186
      // gray out the icon if apt is running, we do this only once,
 
187
      // after the first time, the storage_type changes from ICON_NAME to
 
188
      // IMAGE_PIXBUF
 
189
      if(gtk_status_icon_get_storage_type(ta->tray_icon) == GTK_IMAGE_ICON_NAME) {
 
190
         GdkPixbuf *src = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(),
 
191
                                                   gtk_status_icon_get_icon_name(ta->tray_icon),
 
192
                                                   gtk_status_icon_get_size(ta->tray_icon), 
 
193
                                                   0, 
 
194
                                                   NULL);
 
195
         GdkPixbuf *inactive = gdk_pixbuf_copy(src);
 
196
         gdk_pixbuf_saturate_and_pixelate(src, inactive, 0.0, FALSE);
 
197
         gtk_status_icon_set_from_pixbuf(ta->tray_icon, inactive);
 
198
         g_object_unref(src);
 
199
         g_object_unref(inactive);
 
200
      }
 
201
 
 
202
      // and update the tooltip
 
203
      tray_applet_ui_set_tooltip_text(ta->tray_icon, _("A package manager is working"));
 
204
      // and show it
 
205
      tray_applet_ui_set_visible(ta->tray_icon, TRUE);
 
206
#endif
 
207
 
176
208
      if(priv->active_notification != NULL) {
177
209
         notify_notification_close(priv->active_notification, NULL);
178
 
         priv->active_notification = NULL;
 
210
         g_clear_object(&priv->active_notification);
179
211
      }
 
212
   } else {
 
213
      tray_applet_ui_set_icon(ta, ta->name);
180
214
   }
181
215
}
182
216
 
187
221
   TrayApplet *ta = (TrayApplet *)user_data;
188
222
   UpdateTrayAppletPrivate *priv = (UpdateTrayAppletPrivate*)ta->user_data;
189
223
 
190
 
   // apt is runing, no point in showing a notification
 
224
   // apt is running, no point in showing a notification
191
225
   if(priv->apt_is_running) 
192
226
      return TRUE;
193
227
 
194
 
   GdkRectangle area;
195
 
   gtk_status_icon_get_geometry(ta->tray_icon, NULL, &area, NULL);
196
 
 
197
 
   // no usefull coordiante yet, do another timeout
198
 
   if(area.x <= 0 || area.y <= 0 || area.width <= 0 || area.height <= 0)
199
 
      return TRUE;
200
 
 
201
228
   // check if the update-icon is still visible (in the delay time a 
202
229
   // update may already have been performed)
203
 
   if(!gtk_status_icon_get_visible(ta->tray_icon))
 
230
   if(!tray_applet_ui_get_visible(ta))
204
231
      return FALSE;
205
232
 
206
233
   // now show a notification handle 
207
 
   NotifyNotification *n = notify_notification_new_with_status_icon(
 
234
   gchar *updates;
 
235
   updates = g_strdup_printf(ngettext("There is %i update available. "
 
236
                                      "Click on the notification "
 
237
                                      "icon to show the "
 
238
                                      "available update.",
 
239
                                      "There are %i updates available. "
 
240
                                      "Click on the notification "
 
241
                                      "icon to show the "
 
242
                                      "available updates.",
 
243
                                      priv->num_upgrades), 
 
244
                             priv->num_upgrades);
 
245
   NotifyNotification *n = notify_notification_new(
208
246
                              _("Software updates available"),
209
 
                              _("Click on the notification"
210
 
                                " icon to show the "
211
 
                                "available updates.\n"),
212
 
                              NULL, 
213
 
                              ta->tray_icon);
214
 
 
 
247
                              updates,
 
248
                              NULL);
 
249
 
215
250
   GdkPixbuf* pix= gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), 
216
251
                                            GTK_STOCK_DIALOG_INFO, 48,0,NULL);
217
252
   notify_notification_set_icon_from_pixbuf (n, pix);
219
254
   notify_notification_set_timeout (n, 60*1000);
220
255
   notify_notification_show(n, NULL);
221
256
   // save the notification handle
 
257
   if (priv->active_notification)
 
258
      g_object_unref(priv->active_notification);
222
259
   priv->active_notification = n;
223
260
 
224
261
   // remove this from the timeout now
 
262
   g_free(updates);
 
263
   return FALSE;
 
264
}
 
265
 
 
266
static void 
 
267
show_error(TrayApplet *ta, gchar *error_str)
 
268
{
 
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);
 
272
}
 
273
 
 
274
static gboolean
 
275
outdated_nag(TrayApplet *ta)
 
276
{
 
277
   struct stat buf;
 
278
   if ((stat("/var/lib/apt/periodic/update-success-stamp", &buf) == 0) &&
 
279
       (time(NULL) - buf.st_mtime > OUTDATED_NAG_AGE) ) {
 
280
      tray_applet_ui_set_visible (ta, TRUE);
 
281
      ta->name = "gtk-dialog-warning-panel";
 
282
      tray_applet_ui_set_icon(ta, ta->name);
 
283
      tray_applet_ui_set_tooltip_text(ta,
 
284
                                  _("The update information is outdated. "
 
285
                                    "This may be caused by network "
 
286
                                    "problems or by a repository that "
 
287
                                    "is no longer available. "
 
288
                                    "Please update manually "
 
289
                                    "by selecting 'Show updates' from "
 
290
                                    "the indicator menu, and watching "
 
291
                                    "for any failing repositories."
 
292
                                 ));
 
293
   }
 
294
   return FALSE;
 
295
}
 
296
 
 
297
// use package-system-locked to check
 
298
// if the dpkg lock is taken currently or not
 
299
//
 
300
// if uncertain, return FALSE 
 
301
static gboolean
 
302
dpkg_lock_is_taken (void)
 
303
{
 
304
    gchar *cmd[] = { "pkexec", PACKAGE_SYSTEM_LOCKED, NULL };
 
305
    gint exit_status;
 
306
    GError *err = NULL;
 
307
 
 
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);
 
311
        g_error_free(err);
 
312
        return FALSE;
 
313
    }
 
314
 
 
315
    if(!WIFEXITED(exit_status)) {
 
316
        g_warning(PACKAGE_SYSTEM_LOCKED " exited abnormally with code %i", exit_status);
 
317
        return FALSE;
 
318
    }
 
319
 
 
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;
 
323
}
 
324
 
 
325
#if 0
 
326
// ask devicekit.Power if we run on battery
 
327
static gboolean
 
328
running_on_battery (void)
 
329
{
 
330
   DBusGConnection *connection;
 
331
   GError *error;
 
332
   DBusGProxy *proxy;
 
333
   gboolean on_battery = FALSE;
 
334
  
 
335
   error = NULL;
 
336
   connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
 
337
   if (connection == NULL) {
 
338
      g_debug_update ("Failed to open connection to bus: %s", error->message);
 
339
      g_error_free (error);
 
340
      return FALSE;
 
341
   }
 
342
 
 
343
  proxy = dbus_g_proxy_new_for_name (connection,
 
344
                                     "org.freedesktop.DeviceKit.Power", 
 
345
                                     "/org/freedesktop/DeviceKit/Power",
 
346
                                     "org.freedesktop.DBus.Properties");
 
347
  error = NULL;
 
348
  if (!dbus_g_proxy_call (proxy, "Get", &error, 
 
349
                          G_TYPE_STRING, "org.freedesktop.DeviceKit.Power", 
 
350
                          G_TYPE_STRING, "on_battery",
 
351
                          G_TYPE_INVALID,
 
352
                          G_TYPE_BOOLEAN, &on_battery, 
 
353
                          G_TYPE_INVALID)) {
 
354
     g_debug_update ("failed Get dbus call: %s", 
 
355
                     error?error->message:"no error given");
 
356
     if(error)
 
357
        g_error_free (error);
 
358
     return FALSE;
 
359
  }
 
360
  g_debug_update ("on_battery: %i", on_battery);
 
361
  return on_battery;
 
362
}
 
363
#endif
 
364
 
 
365
static gboolean
 
366
security_updates_are_installed_unattended(void)
 
367
{
 
368
   int ret;
 
369
   GError *error = NULL;
 
370
 
 
371
   // run apt_check.py in "check if we install updates unattended" mode
 
372
   // a exit status > 0 indicates that its unattended-upgrades is used
 
373
   if ( g_file_test ("/usr/bin/unattended-upgrades", 
 
374
                     G_FILE_TEST_IS_EXECUTABLE) ) {
 
375
      char *cmd[] = { "/usr/bin/nice",
 
376
                      "/usr/bin/ionice", "-c3",
 
377
                      UPGRADE_CHECKER, 
 
378
                      "--security-updates-unattended",        
 
379
                      NULL };
 
380
 
 
381
      if (g_spawn_sync("/", cmd, NULL, 0, NULL, NULL, 
 
382
                       NULL, NULL, &ret, &error)) {
 
383
         g_debug("--security-updates-unattended: %i", WEXITSTATUS(ret));
 
384
         if( WEXITSTATUS(ret) > 0 )
 
385
            return TRUE;
 
386
      } else if (error) {
 
387
         g_print("error: %s\n", error->message);
 
388
      }
 
389
   }
 
390
   return FALSE;
 
391
}
 
392
 
 
393
// check if the security auto launch interval is over
 
394
// and if the user is not using auto install of security
 
395
// updates
 
396
static gboolean
 
397
auto_launch_security_now(UpdateTrayAppletPrivate *priv, 
 
398
                         time_t now, 
 
399
                         time_t last_launch)
 
400
{
 
401
 
 
402
   // no security updates and no reboot pending
 
403
   if ((priv->num_security == 0) && !priv->reboot_pending)
 
404
      return FALSE;
 
405
         
 
406
   // security updates, but already launched recently
 
407
   if ((last_launch + AUTOLAUNCH_MINIMAL_SECURITY_INTERVAL) > now) {
 
408
      g_debug_update("security updates, but update-manager was launched "
 
409
                     "within the AUTOLAUNCH_MINIMAL_SECURITY_INTERVAL");
 
410
      return FALSE;
 
411
   }
 
412
   
 
413
   // if security updates are installed unattended, there is nothing
 
414
   // to do for us
 
415
   if (security_updates_are_installed_unattended())
 
416
      return FALSE;
 
417
 
 
418
   g_debug_update("security updates, auto-launching");
 
419
   return TRUE;
 
420
}
 
421
 
 
422
// Like ctime, but without the annoying trailing newline.  Caller must
 
423
// g_free the result.
 
424
static gchar *
 
425
readable_timestamp(time_t time)
 
426
{
 
427
   GDateTime *datetime;
 
428
   gchar *formatted;
 
429
 
 
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);
 
433
   return formatted;
 
434
}
 
435
 
 
436
// check the logs of dpkg and apt for timestamps, the idea
 
437
// is to not auto launch if dpkg/apt were run manually by the
 
438
// user
 
439
static time_t
 
440
newest_log_file_timestamp(void)
 
441
{
 
442
   struct stat buf;
 
443
   int i, j;
 
444
   time_t newest_log_stamp = 0;
 
445
 
 
446
   char *log_glob[] = { "/var/log/dpkg.log*",
 
447
                    "/var/log/apt/term.log*",
 
448
                    NULL };
 
449
 
 
450
   for (i=0; log_glob[i] != NULL; i++) 
 
451
   {
 
452
      glob_t pglob;
 
453
      if(glob(log_glob[i], 0, NULL, &pglob) != 0) {
 
454
         g_warning("error from glob %s", log_glob[i]);
 
455
         continue;
 
456
      }
 
457
      for(j=0;j < pglob.gl_pathc; j++) {
 
458
         const char *log = pglob.gl_pathv[j];
 
459
         time_t mtime;
 
460
         gchar *timestamp;
 
461
 
 
462
         if(g_stat(log, &buf) <0) {
 
463
            g_warning("can't stat %s", log);
 
464
            continue;
 
465
         }
 
466
         if(buf.st_size == 0) {
 
467
            g_warning("log file empty (logrotate?) %s", log);
 
468
            continue;
 
469
         }
 
470
 
 
471
         mtime = buf.st_mtime;
 
472
         timestamp = readable_timestamp (mtime);
 
473
         g_debug_update ("mtime from %s: %i (%s)", log, mtime, timestamp);
 
474
         g_free (timestamp);
 
475
 
 
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);
 
479
         g_free (timestamp);
 
480
      }
 
481
      globfree(&pglob);
 
482
   }
 
483
   return newest_log_stamp;
 
484
}
 
485
 
 
486
// check if the auto launch interval is over and its 
 
487
// time to launch again and if the dpkg lock is currently 
 
488
// not taken
 
489
static gboolean 
 
490
auto_launch_now (TrayApplet *ta)
 
491
{
 
492
   UpdateTrayAppletPrivate *priv = (UpdateTrayAppletPrivate*)ta->user_data;
 
493
   int interval_days = 0;
 
494
   time_t last_launch = 0;
 
495
 
 
496
   if (dpkg_lock_is_taken())
 
497
      return FALSE;
 
498
 
 
499
#if 0 // disabled because we need more cleverness here in order to cover
 
500
      // the case where people always work on battery (and charge overnight)
 
501
   if (running_on_battery())
 
502
      return FALSE;
 
503
#endif
 
504
 
 
505
   // when checking for regular updates honor the 
 
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);
 
510
 
 
511
   if (interval_days <= 0) 
 
512
      return TRUE;
 
513
 
 
514
   // check last launch time 
 
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);
 
520
 
 
521
   time_t now = time(NULL);
 
522
   if (auto_launch_security_now(priv, now, last_launch))
 
523
      return TRUE;
 
524
 
 
525
   g_debug_update("time now %i (%s), delta: %i", now, ctime(&now), now-last_launch);
 
526
   if ((last_launch + (24*60*60*interval_days)) < now) {
 
527
      g_debug_update ("need to auto launch");
 
528
      return TRUE;
 
529
   }
 
530
 
225
531
   return FALSE;
226
532
}
227
533
 
228
534
gboolean
229
535
update_check (TrayApplet *ta)
230
536
{
231
 
   //g_print("update_check()\n");
 
537
   if(ta == NULL)
 
538
      return FALSE;
 
539
 
 
540
   g_debug_update ("update_check()");
232
541
   UpdateTrayAppletPrivate *priv = (UpdateTrayAppletPrivate*)ta->user_data;
233
542
 
234
 
   int num_upgrades = 0;
 
543
   priv->num_upgrades = 0;
 
544
   priv->num_security = 0;
 
545
   priv->reboot_pending = FALSE;
235
546
   int exit_status = 0;
236
 
   gboolean res = FALSE;
237
 
   GError *error;
 
547
   GError *error = NULL;
238
548
 
239
 
   char *cmd[] = { UPGRADE_CHECKER, NULL };
 
549
   char *cmd[] = { "/usr/bin/nice",
 
550
                   "/usr/bin/ionice", "-c3",
 
551
                   UPGRADE_CHECKER, 
 
552
                   NULL };
240
553
   char *ret;
 
554
   struct stat buf;
241
555
 
242
556
   if(g_spawn_sync(NULL, cmd, NULL,  
243
557
                   G_SPAWN_STDOUT_TO_DEV_NULL, 
244
558
                   NULL, NULL, NULL, &ret,
245
559
                   &exit_status, &error)) {
 
560
 
 
561
      // check if we are running from the live-cd, it
 
562
      // symlinks apt-check to /bin/true
 
563
      if(exit_status == 0 && strlen(ret) == 0 &&
 
564
         g_file_test (UPGRADE_CHECKER, G_FILE_TEST_IS_SYMLINK) ) {
 
565
         g_free(ret);
 
566
         return TRUE;
 
567
      }
 
568
 
246
569
      // read the value from childs stderr, E: indicates a problem
247
570
      if(ret[0] == 'E') {
248
 
         gchar *error;
 
571
         GString *error = g_string_new("");
249
572
         if(strlen(ret) > 3) 
250
 
            error = g_strdup_printf(_("An error occurred, please run "
 
573
            g_string_append_printf(error,
 
574
                                   _("An error occurred, please run "
251
575
                                      "Package Manager from the "
252
576
                                      "right-click menu or apt-get in "
253
577
                                      "a terminal to see what is wrong.\n"
254
 
                                      "The error message was: '%s'"),
 
578
                                      "The error message was: '%s'. "),
255
579
                                    &ret[2]);
256
580
         else
257
 
            error = g_strdup_printf(_("An error occurred, please run "
 
581
            g_string_append(error, _("An error occurred, please run "
258
582
                                      "Package Manager from the "
259
583
                                      "right-click menu or apt-get in "
260
584
                                      "a terminal to see what is wrong."));
261
 
         gtk_status_icon_set_tooltip(ta->tray_icon, 
262
 
                                   _("This usually means that your installed "
263
 
                                     "packages have unmet dependencies"));
264
 
         gtk_status_icon_set_visible(ta->tray_icon, TRUE);
 
585
         g_string_append(error, _("This usually means that your installed "
 
586
                                    "packages have unmet dependencies"));
 
587
         show_error(ta, error->str);
 
588
         g_string_free(error, TRUE);
265
589
         g_free(ret);
266
590
         return TRUE;
267
591
         
268
 
      } 
269
 
      num_upgrades = atoi(ret);
 
592
      }
 
593
 
 
594
      // we get "number_of_upgrades;number_of_security_upgrades" 
 
595
      // (e.g. "15;2")
 
596
      gchar **ret_array = g_strsplit(ret, ";", 2);
 
597
      if(g_strv_length(ret_array) < 2)  {
 
598
         show_error(ta, _("A problem occurred when checking for the updates."));
 
599
         g_free(ret);
 
600
         g_strfreev(ret_array);
 
601
         return TRUE;
 
602
      }
 
603
      priv->num_upgrades = atoi(ret_array[0]);
 
604
      priv->num_security = atoi(ret_array[1]);
270
605
      g_free(ret);
 
606
      g_strfreev(ret_array);
271
607
   } else
272
608
      g_warning("Error launching %s", UPGRADE_CHECKER);
273
609
   
274
 
   if (num_upgrades == 0) {
275
 
      gtk_status_icon_set_visible (ta->tray_icon, FALSE);
276
 
      
 
610
   g_debug_update("%s returned %i (security: %i)", UPGRADE_CHECKER, priv->num_upgrades, priv->num_security);
 
611
 
 
612
   if (stat("/run/reboot-required", &buf) == 0) {
 
613
      priv->reboot_pending = TRUE;
 
614
      g_debug_update("A reboot is currently pending.");
 
615
   }
 
616
 
 
617
   if ((priv->num_upgrades == 0) && !priv->reboot_pending) {
 
618
      // check if the periodic file is very old
 
619
      if ((stat("/var/lib/apt/periodic/update-success-stamp", &buf) == 0) &&
 
620
          (time(NULL) - buf.st_mtime > OUTDATED_NAG_AGE) ) 
 
621
         g_timeout_add_seconds(OUTDATED_NAG_WAIT, (GSourceFunc)outdated_nag, ta);
 
622
 
 
623
      tray_applet_ui_set_visible (ta, FALSE);
277
624
      if(priv->active_notification != NULL) {
278
625
         notify_notification_close(priv->active_notification, NULL);
279
 
         priv->active_notification = NULL; 
 
626
         g_clear_object(&priv->active_notification);
280
627
      }
281
628
      return TRUE;
282
629
   } 
283
630
 
 
631
   if (priv->num_security == 0)
 
632
      ta->name = "software-update-available";
 
633
   else
 
634
      ta->name = "software-update-urgent";
 
635
   tray_applet_ui_set_icon(ta, ta->name);
 
636
 
 
637
   // update the tooltip
 
638
   update_trayicon_update_tooltip(ta, priv->num_upgrades);
 
639
 
 
640
   tray_applet_ui_set_visible(ta, FALSE);
 
641
   if (auto_launch_now(ta))
 
642
   {
 
643
      g_spawn_command_line_async("nice ionice -c3 update-manager "
 
644
                                 "--no-update --no-focus-on-map", NULL);
 
645
   }
 
646
   return TRUE;
 
647
 
284
648
   // if we are already visible, skip the rest
285
 
   if(gtk_status_icon_get_visible (ta->tray_icon))
 
649
   if(tray_applet_ui_get_visible (ta))
286
650
      return TRUE;
287
651
 
288
 
   // update the tooltip
289
 
   update_trayicon_update_tooltip(ta, num_upgrades);
290
 
      
291
652
   // show the icon
292
 
   gtk_status_icon_set_visible(ta->tray_icon, TRUE);
293
 
 
 
653
   tray_applet_ui_set_visible(ta, TRUE);
 
654
   
294
655
   // the user does not no notification messages
295
 
   if(gconf_client_get_bool(priv->gconf,
296
 
                            GCONF_KEY_NO_UPDATE_NOTIFICATIONS, NULL))
 
656
   if(g_settings_get_boolean(ta->un->settings,
 
657
                             SETTINGS_KEY_NO_UPDATE_NOTIFICATIONS))
297
658
      return TRUE;
298
659
 
299
660
   // show the notification with some delay. otherwise on a login
300
661
   // the origin of the window is 0,0 and that looks ugly
301
 
   g_timeout_add(5000, show_notification, ta);
302
 
   
 
662
   g_timeout_add_seconds(5, show_notification, ta);
 
663
 
303
664
   return TRUE;
304
665
}
305
666
 
309
670
{
310
671
   // create the private data struct
311
672
   UpdateTrayAppletPrivate *priv = g_new0(UpdateTrayAppletPrivate, 1);
312
 
   priv->gconf = gconf_client_get_default();
313
673
   priv->apt_is_running = FALSE;
314
674
   priv->active_notification = NULL;
315
675
   ta->user_data = priv;
316
676
 
317
 
   // 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);
 
680
 
 
681
#ifndef HAVE_APP_INDICATOR
 
682
   // the default action for the gtk_status_icon
318
683
   g_signal_connect (G_OBJECT(ta->tray_icon),
319
684
                     "activate",
320
 
                     G_CALLBACK (activate_cb),
321
 
                     ta);
322
 
   g_signal_connect (G_OBJECT(ta->tray_icon),
323
 
                     "popup-menu",
324
 
                     G_CALLBACK (popup_cb),
325
 
                     ta);
 
685
                     G_CALLBACK (gtk_status_icon_click_activate_cb),
 
686
                     ta);
 
687
#endif
326
688
 
327
689
   /* Menu initialization */
328
690
   update_trayicon_create_menu (ta);
 
691
   tray_applet_ui_set_menu (ta, ta->menu);
329
692
   
330
693
   /* Check for updates for the first time */
331
694
   update_check (ta);