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

« back to all changes in this revision

Viewing changes to src/update.c

  • Committer: Michael Vogt
  • Date: 2012-03-22 20:17:55 UTC
  • Revision ID: michael.vogt@ubuntu.com-20120322201755-v7kc7wlrtuax2p82
add empty test in preparation for more to come :)

Show diffs side-by-side

added added

removed removed

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