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

« back to all changes in this revision

Viewing changes to src/update.c

  • Committer: Balint Reczey
  • Date: 2020-06-11 18:46:02 UTC
  • Revision ID: balint.reczey@canonical.com-20200611184602-2rv1zan3xu723x2u
Moved to git at https://git.launchpad.net/update-notifier

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
 
#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)},
41
 
   { NULL, NULL, NULL }
42
 
};
43
 
 
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
60
 
static gboolean
61
 
gtk_status_icon_click_activate_cb (GtkWidget *widget, 
62
 
             TrayApplet *ta)
63
 
{
64
 
   UpdateTrayAppletPrivate *priv = (UpdateTrayAppletPrivate*)ta->user_data;
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
78
 
update_trayicon_update_tooltip (TrayApplet *ta, int num_upgrades)
79
 
{
80
 
   //g_print("update_tooltip: %p %p %p\n", ta, ta->tooltip, ta->eventbox);
81
 
   gchar *updates;
82
 
 
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);
88
 
   g_free(updates);
89
 
}
90
 
 
91
 
 
92
 
static void 
93
 
cb_action(GObject *self, void *user_data __attribute__ ((__unused__)))
94
 
{
95
 
   int i = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(self), "action"));
96
 
   invoke (actions[i][0], _(actions[i][2]), (long)actions[i][3]);
97
 
}
98
 
 
99
 
static void 
100
 
cb_preferences(GObject *self, void *user_data)
101
 
{
102
 
   invoke("/usr/bin/software-properties-gtk",
103
 
          "/usr/share/applications/software-properties.desktop",
104
 
          FALSE);    
105
 
}
106
 
 
107
 
static void 
108
 
cb_toggled_show_notifications(GObject *self, void *data)
109
 
{
110
 
      TrayApplet *ta = (TrayApplet*)data;
111
 
      UpdateTrayAppletPrivate *priv = (UpdateTrayAppletPrivate*)ta->user_data;
112
 
 
113
 
      gboolean b = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(self));
114
 
      g_settings_set_boolean(ta->un->settings,
115
 
                             SETTINGS_KEY_NO_UPDATE_NOTIFICATIONS, !b);
116
 
      
117
 
 
118
 
      if(priv->active_notification != NULL) {
119
 
         notify_notification_close(priv->active_notification, NULL);
120
 
         g_clear_object(&priv->active_notification);
121
 
      }
122
 
}
123
 
 
124
 
 
125
 
static void
126
 
update_trayicon_create_menu(TrayApplet *ta)
127
 
{
128
 
        GtkWidget *menuitem;
129
 
        int i;
130
 
 
131
 
        ta->menu = gtk_menu_new ();
132
 
 
133
 
        for(i=0;actions[i][0]!=NULL;i++) {
134
 
            if (!g_file_test(actions[i][2], G_FILE_TEST_EXISTS))
135
 
                continue;
136
 
           menuitem = gtk_menu_item_new_with_label (_(actions[i][1]));
137
 
 
138
 
           gtk_menu_shell_append (GTK_MENU_SHELL (ta->menu), menuitem);
139
 
           g_object_set_data(G_OBJECT(menuitem), "action", GINT_TO_POINTER(i));
140
 
           g_signal_connect(G_OBJECT(menuitem), "activate", 
141
 
                            G_CALLBACK(cb_action), ta);
142
 
        }
143
 
 
144
 
        menuitem = gtk_separator_menu_item_new();
145
 
        gtk_menu_shell_append (GTK_MENU_SHELL (ta->menu), menuitem);
146
 
 
147
 
        menuitem = gtk_check_menu_item_new_with_label(_("Show notifications"));
148
 
        gboolean b = g_settings_get_boolean(ta->un->settings,
149
 
                                            SETTINGS_KEY_NO_UPDATE_NOTIFICATIONS);
150
 
        gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), !b);
151
 
        gtk_menu_shell_append (GTK_MENU_SHELL (ta->menu), menuitem);
152
 
        g_signal_connect(G_OBJECT(menuitem), "toggled", 
153
 
                         G_CALLBACK(cb_toggled_show_notifications), ta);
154
 
        
155
 
 
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);
159
 
        g_signal_connect(G_OBJECT(menuitem), "activate", 
160
 
                         G_CALLBACK(cb_preferences), (void*)ta);
161
 
 
162
 
        gtk_widget_show_all (ta->menu);
163
 
}
164
 
 
165
 
/* this tells the trayicon that apt is downloading something */
166
 
void 
167
 
update_apt_is_running(TrayApplet *ta, gboolean is_running)
168
 
{
169
 
   g_debug_update("update_apt_is_running: %i",is_running);
170
 
   if(ta == NULL)
171
 
      return;
172
 
 
173
 
   // update internal status
174
 
   UpdateTrayAppletPrivate *priv = (UpdateTrayAppletPrivate*)ta->user_data;
175
 
   priv->apt_is_running = is_running;
176
 
 
177
 
   // everybody wants auto-launch mode
178
 
   return;
179
 
 
180
 
   if(is_running) {
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
 
 
208
 
      if(priv->active_notification != NULL) {
209
 
         notify_notification_close(priv->active_notification, NULL);
210
 
         g_clear_object(&priv->active_notification);
211
 
      }
212
 
   } else {
213
 
      tray_applet_ui_set_icon(ta, ta->name);
214
 
   }
215
 
}
216
 
 
217
 
// actually show the notification 
218
 
static gint 
219
 
show_notification(gpointer user_data)
220
 
{
221
 
   TrayApplet *ta = (TrayApplet *)user_data;
222
 
   UpdateTrayAppletPrivate *priv = (UpdateTrayAppletPrivate*)ta->user_data;
223
 
 
224
 
   // apt is running, no point in showing a notification
225
 
   if(priv->apt_is_running) 
226
 
      return TRUE;
227
 
 
228
 
   // check if the update-icon is still visible (in the delay time a 
229
 
   // update may already have been performed)
230
 
   if(!tray_applet_ui_get_visible(ta))
231
 
      return FALSE;
232
 
 
233
 
   // now show a notification handle 
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(
246
 
                              _("Software updates available"),
247
 
                              updates,
248
 
                              NULL);
249
 
 
250
 
   GdkPixbuf* pix= gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), 
251
 
                                            GTK_STOCK_DIALOG_INFO, 48,0,NULL);
252
 
   notify_notification_set_icon_from_pixbuf (n, pix);
253
 
   g_object_unref(pix);
254
 
   notify_notification_set_timeout (n, 60*1000);
255
 
   notify_notification_show(n, NULL);
256
 
   // save the notification handle
257
 
   if (priv->active_notification)
258
 
      g_object_unref(priv->active_notification);
259
 
   priv->active_notification = n;
260
 
 
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
 
 
531
 
   return FALSE;
532
 
}
533
 
 
534
 
gboolean
535
 
update_check (TrayApplet *ta)
536
 
{
537
 
   if(ta == NULL)
538
 
      return FALSE;
539
 
 
540
 
   g_debug_update ("update_check()");
541
 
   UpdateTrayAppletPrivate *priv = (UpdateTrayAppletPrivate*)ta->user_data;
542
 
 
543
 
   priv->num_upgrades = 0;
544
 
   priv->num_security = 0;
545
 
   priv->reboot_pending = FALSE;
546
 
   int exit_status = 0;
547
 
   GError *error = NULL;
548
 
 
549
 
   char *cmd[] = { "/usr/bin/nice",
550
 
                   "/usr/bin/ionice", "-c3",
551
 
                   UPGRADE_CHECKER, 
552
 
                   NULL };
553
 
   char *ret;
554
 
   struct stat buf;
555
 
 
556
 
   if(g_spawn_sync(NULL, cmd, NULL,  
557
 
                   G_SPAWN_STDOUT_TO_DEV_NULL, 
558
 
                   NULL, NULL, NULL, &ret,
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
 
 
569
 
      // read the value from childs stderr, E: indicates a problem
570
 
      if(ret[0] == 'E') {
571
 
         GString *error = g_string_new("");
572
 
         if(strlen(ret) > 3) 
573
 
            g_string_append_printf(error,
574
 
                                   _("An error occurred, please run "
575
 
                                      "Package Manager from the "
576
 
                                      "right-click menu or apt-get in "
577
 
                                      "a terminal to see what is wrong.\n"
578
 
                                      "The error message was: '%s'. "),
579
 
                                    &ret[2]);
580
 
         else
581
 
            g_string_append(error, _("An error occurred, please run "
582
 
                                      "Package Manager from the "
583
 
                                      "right-click menu or apt-get in "
584
 
                                      "a terminal to see what is wrong."));
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);
589
 
         g_free(ret);
590
 
         return TRUE;
591
 
         
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]);
605
 
      g_free(ret);
606
 
      g_strfreev(ret_array);
607
 
   } else
608
 
      g_warning("Error launching %s", UPGRADE_CHECKER);
609
 
   
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);
624
 
      if(priv->active_notification != NULL) {
625
 
         notify_notification_close(priv->active_notification, NULL);
626
 
         g_clear_object(&priv->active_notification);
627
 
      }
628
 
      return TRUE;
629
 
   } 
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
 
 
648
 
   // if we are already visible, skip the rest
649
 
   if(tray_applet_ui_get_visible (ta))
650
 
      return TRUE;
651
 
 
652
 
   // show the icon
653
 
   tray_applet_ui_set_visible(ta, TRUE);
654
 
   
655
 
   // the user does not no notification messages
656
 
   if(g_settings_get_boolean(ta->un->settings,
657
 
                             SETTINGS_KEY_NO_UPDATE_NOTIFICATIONS))
658
 
      return TRUE;
659
 
 
660
 
   // show the notification with some delay. otherwise on a login
661
 
   // the origin of the window is 0,0 and that looks ugly
662
 
   g_timeout_add_seconds(5, show_notification, ta);
663
 
 
664
 
   return TRUE;
665
 
}
666
 
 
667
 
 
668
 
void 
669
 
update_tray_icon_init(TrayApplet *ta)
670
 
{
671
 
   // create the private data struct
672
 
   UpdateTrayAppletPrivate *priv = g_new0(UpdateTrayAppletPrivate, 1);
673
 
   priv->apt_is_running = FALSE;
674
 
   priv->active_notification = NULL;
675
 
   ta->user_data = priv;
676
 
 
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
683
 
   g_signal_connect (G_OBJECT(ta->tray_icon),
684
 
                     "activate",
685
 
                     G_CALLBACK (gtk_status_icon_click_activate_cb),
686
 
                     ta);
687
 
#endif
688
 
 
689
 
   /* Menu initialization */
690
 
   update_trayicon_create_menu (ta);
691
 
   tray_applet_ui_set_menu (ta, ta->menu);
692
 
   
693
 
   /* Check for updates for the first time */
694
 
   update_check (ta);
695
 
}