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

« back to all changes in this revision

Viewing changes to src/update.c

  • Committer: Lionel Le Folgoc
  • Date: 2011-02-13 12:39:35 UTC
  • Revision ID: mrpouit@ubuntu.com-20110213123935-hc46lt2c32z8z8qb
Tags: 0.110.3ubuntu2
releasing version 0.110.3ubuntu2

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>
8
11
 
9
12
#include <sys/types.h>
10
13
#include <sys/stat.h>
11
14
#include <unistd.h>
 
15
#include <stdlib.h>
 
16
#include <dbus/dbus-glib.h>
12
17
 
13
18
#include "update-notifier.h"
14
19
#include "update.h"
18
23
// command, description, desktopfile, needs_gksu
19
24
const char* actions[][4] = {
20
25
   { "/usr/bin/update-manager", N_("Show updates"), 
21
 
     "/usr/share/applications/update-manager.desktop", FALSE },
 
26
     "/usr/share/applications/update-manager.desktop", 
 
27
     GINT_TO_POINTER(FALSE) },
22
28
 
23
29
   { "/usr/sbin/synaptic --dist-upgrade-mode --non-interactive --hide-main-window -o Synaptic::AskRelated=true",
24
 
     N_("Install all updates"), "/usr/share/applications/synaptic.desktop", TRUE
 
30
     N_("Install all updates"), "/usr/share/applications/synaptic.desktop", 
 
31
     GINT_TO_POINTER(TRUE)
25
32
   },
26
33
   { "/usr/sbin/synaptic --update-at-startup --non-interactive --hide-main-window", 
27
 
     N_("Check for updates"), "/usr/share/applications/synaptic.desktop", TRUE },
 
34
     N_("Check for updates"), "/usr/share/applications/synaptic.desktop", 
 
35
     GINT_TO_POINTER(TRUE) },
28
36
   { "/usr/sbin/synaptic", N_("Start package manager"), 
29
 
     "/usr/share/applications/synaptic.desktop", TRUE},
 
37
     "/usr/share/applications/synaptic.desktop", 
 
38
     GINT_TO_POINTER(TRUE)},
30
39
   { NULL, NULL, NULL }
31
40
};
32
41
 
 
42
enum { 
 
43
   NOTIFICATION_DEFAULT, 
 
44
   NOTIFICATION_IGNORE, 
 
45
   NOTIFICATION_SHOW_UPDATES 
 
46
};
33
47
 
34
 
enum { NOTIFICATION_DEFAULT, NOTIFICATION_IGNORE, NOTIFICATION_SHOW_UPDATES };
 
48
static inline void
 
49
g_debug_update(const char *msg, ...)
 
50
{
 
51
   va_list va;
 
52
   va_start(va, msg);
 
53
   g_logv("update",G_LOG_LEVEL_DEBUG, msg, va);
 
54
   va_end(va);
 
55
}
35
56
 
36
57
static gboolean
37
58
activate_cb (GtkWidget *widget, 
121
142
update_trayicon_create_menu(TrayApplet *ta)
122
143
{
123
144
        GtkWidget *menuitem;
124
 
        GtkAccelGroup *accelgroup;
125
145
        int i;
126
146
 
127
147
        ta->menu = gtk_menu_new ();
128
148
 
129
149
        for(i=0;actions[i][0]!=NULL;i++) {
 
150
            if (!g_file_test(actions[i][2], G_FILE_TEST_EXISTS))
 
151
                continue;
130
152
           menuitem = gtk_menu_item_new_with_label (_(actions[i][1]));
 
153
 
131
154
           gtk_menu_shell_append (GTK_MENU_SHELL (ta->menu), menuitem);
132
155
           g_object_set_data(G_OBJECT(menuitem), "action", GINT_TO_POINTER(i));
133
156
           g_signal_connect(G_OBJECT(menuitem), "activate", 
148
171
                         G_CALLBACK(cb_toggled_show_notifications), ta);
149
172
        
150
173
 
151
 
        menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_PREFERENCES, accelgroup);
152
 
        gtk_menu_shell_append (GTK_MENU_SHELL (ta->menu), menuitem);
 
174
        menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_PREFERENCES, NULL);
 
175
        if (g_file_test("/usr/bin/software-properties-gtk", G_FILE_TEST_EXISTS))
 
176
            gtk_menu_shell_append (GTK_MENU_SHELL (ta->menu), menuitem);
153
177
        g_signal_connect(G_OBJECT(menuitem), "activate", 
154
178
                         G_CALLBACK(cb_preferences), (void*)ta);
155
179
 
160
184
void 
161
185
update_apt_is_running(TrayApplet *ta, gboolean is_running)
162
186
{
 
187
   g_debug_update("update_apt_is_running: %i\n",is_running);
163
188
   if(ta == NULL)
164
189
      return;
165
190
 
 
191
   // update internal status
166
192
   UpdateTrayAppletPrivate *priv = (UpdateTrayAppletPrivate*)ta->user_data;
167
 
   //g_print("update_apt_is_running: %i\n",is_running);
 
193
   priv->apt_is_running = is_running;
 
194
 
 
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))
 
198
      return;
168
199
 
169
200
   if(is_running) {
170
201
 
196
227
   } else {
197
228
      gtk_status_icon_set_from_icon_name(ta->tray_icon, ta->name);
198
229
   }
199
 
 
200
 
   priv->apt_is_running = is_running;
201
 
 
202
230
}
203
231
 
204
232
// actually show the notification 
225
253
      return TRUE;
226
254
 
227
255
   // now show a notification handle 
 
256
   gchar *updates;
 
257
   updates = g_strdup_printf(ngettext("There is %i update available. "
 
258
                                      "Click on the notification "
 
259
                                      "icon to show the "
 
260
                                      "available update.",
 
261
                                      "There are %i updates available. "
 
262
                                      "Click on the notification "
 
263
                                      "icon to show the "
 
264
                                      "available updates.",
 
265
                                      priv->num_upgrades), 
 
266
                             priv->num_upgrades);
228
267
   NotifyNotification *n = notify_notification_new_with_status_icon(
229
268
                              _("Software updates available"),
230
 
                              _("Click on the notification"
231
 
                                " icon to show the "
232
 
                                "available updates.\n"),
 
269
                              updates,
233
270
                              NULL, 
234
271
                              ta->tray_icon);
235
 
 
 
272
 
236
273
   GdkPixbuf* pix= gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), 
237
274
                                            GTK_STOCK_DIALOG_INFO, 48,0,NULL);
238
275
   notify_notification_set_icon_from_pixbuf (n, pix);
243
280
   priv->active_notification = n;
244
281
 
245
282
   // remove this from the timeout now
 
283
   g_free(updates);
246
284
   return FALSE;
247
285
}
248
286
 
261
299
   if ((stat("/var/lib/apt/periodic/update-success-stamp", &buf) == 0) &&
262
300
       (time(NULL) - buf.st_mtime > OUTDATED_NAG_AGE) ) {
263
301
      gtk_status_icon_set_visible (ta->tray_icon, TRUE);
264
 
      ta->name = "gtk-dialog-warning";
265
 
      gtk_status_icon_set_from_icon_name(ta->tray_icon, ta->name);
 
302
      ta->name = "gtk-dialog-warning-panel";
 
303
      GdkPixbuf *src = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(),
 
304
                                                ta->name, 48, 
 
305
                                                GTK_ICON_LOOKUP_GENERIC_FALLBACK, 
 
306
                                                NULL);
 
307
      gtk_status_icon_set_from_pixbuf(ta->tray_icon, src);
 
308
      g_object_unref(src);
266
309
      gtk_status_icon_set_tooltip(ta->tray_icon,
267
310
                                  _("The update information is outdated. "
268
311
                                    "This may be caused by network "
278
321
   return FALSE;
279
322
}
280
323
 
 
324
// use ubuntu-system-service (if available) to check
 
325
// if the dpkg lock is taken currently or not
 
326
//
 
327
// if uncertain, return FALSE 
 
328
static gboolean
 
329
dpkg_lock_is_taken ()
 
330
{
 
331
   DBusGConnection *connection;
 
332
   GError *error;
 
333
   DBusGProxy *proxy;
 
334
   gboolean locked = FALSE;
 
335
  
 
336
   error = NULL;
 
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);
 
341
      return FALSE;
 
342
   }
 
343
 
 
344
  proxy = dbus_g_proxy_new_for_name (connection,
 
345
                                     "com.ubuntu.SystemService",
 
346
                                     "/",
 
347
                                     "com.ubuntu.SystemService");
 
348
  error = NULL;
 
349
  if (!dbus_g_proxy_call (proxy, "is_package_system_locked", &error, 
 
350
                          G_TYPE_INVALID,
 
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);
 
355
     return FALSE;
 
356
  }
 
357
  g_object_unref (proxy);
 
358
 
 
359
  g_debug_update ("is_package_system_locked: %i", locked);
 
360
  return locked;
 
361
}
 
362
 
 
363
#if 0
 
364
// ask devicekit.Power if we run on battery
 
365
static gboolean
 
366
running_on_battery ()
 
367
{
 
368
   DBusGConnection *connection;
 
369
   GError *error;
 
370
   DBusGProxy *proxy;
 
371
   gboolean on_battery = FALSE;
 
372
  
 
373
   error = NULL;
 
374
   connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
 
375
   if (connection == NULL) {
 
376
      g_debug_update ("Failed to open connection to bus: %s\n", error->message);
 
377
      g_error_free (error);
 
378
      return FALSE;
 
379
   }
 
380
 
 
381
  proxy = dbus_g_proxy_new_for_name (connection,
 
382
                                     "org.freedesktop.DeviceKit.Power", 
 
383
                                     "/org/freedesktop/DeviceKit/Power",
 
384
                                     "org.freedesktop.DBus.Properties");
 
385
  error = NULL;
 
386
  if (!dbus_g_proxy_call (proxy, "Get", &error, 
 
387
                          G_TYPE_STRING, "org.freedesktop.DeviceKit.Power", 
 
388
                          G_TYPE_STRING, "on_battery",
 
389
                          G_TYPE_INVALID,
 
390
                          G_TYPE_BOOLEAN, &on_battery, 
 
391
                          G_TYPE_INVALID)) {
 
392
     g_debug_update ("failed Get dbus call: %s\n", 
 
393
                     error?error->message:"no error given");
 
394
     if(error)
 
395
        g_error_free (error);
 
396
     return FALSE;
 
397
  }
 
398
  g_debug_update ("on_battery: %i", on_battery);
 
399
  return on_battery;
 
400
}
 
401
#endif
 
402
 
 
403
static gboolean
 
404
security_updates_are_installed_unattended()
 
405
{
 
406
   int ret;
 
407
   GError *error = NULL;
 
408
 
 
409
   // run apt_check.py in "check if we install updates unattended" mode
 
410
   // a exit status > 0 indicates that its unattended-upgrades is used
 
411
   if ( g_file_test ("/usr/bin/unattended-upgrades", 
 
412
                     G_FILE_TEST_IS_EXECUTABLE) ) {
 
413
      char *cmd[] = { "/usr/bin/nice",
 
414
                      "/usr/bin/ionice", "-c3",
 
415
                      UPGRADE_CHECKER, 
 
416
                      "--security-updates-unattended",        
 
417
                      NULL };
 
418
 
 
419
      if (g_spawn_sync("/", cmd, NULL, 0, NULL, NULL, 
 
420
                       NULL, NULL, &ret, &error)) {
 
421
         g_debug("--security-updates-unattended: %i\n", WEXITSTATUS(ret));
 
422
         if( WEXITSTATUS(ret) > 0 )
 
423
            return TRUE;
 
424
      } else if (error) {
 
425
         g_print("error: %s\n", error->message);
 
426
      }
 
427
   }
 
428
   return FALSE;
 
429
}
 
430
 
 
431
// check if the security auto launch interval is over
 
432
// and if the user is not using auto install of security
 
433
// updates
 
434
static gboolean
 
435
auto_launch_security_now(UpdateTrayAppletPrivate *priv, 
 
436
                         time_t now, 
 
437
                         time_t last_launch)
 
438
{
 
439
 
 
440
   // no security updates
 
441
   if (priv->num_security == 0)
 
442
      return FALSE;
 
443
         
 
444
   // security updates, but already launched recently
 
445
   if ((last_launch + AUTOLAUNCH_MINIMAL_SECURITY_INTERVAL) > now) {
 
446
      g_debug_update("security updates, but update-manager was launched "
 
447
                     "within the AUTOLAUNCH_MINIMAL_SECURITY_INTERVAL\n");
 
448
      return FALSE;
 
449
   }
 
450
   
 
451
   // if security updates are installed unattended, there is nothing
 
452
   // to do for us
 
453
   if (security_updates_are_installed_unattended())
 
454
      return FALSE;
 
455
 
 
456
   g_debug_update("security updates, auto-launching");
 
457
   return TRUE;
 
458
}
 
459
 
 
460
// check the logs of dpkg and apt for timestamps, the idea
 
461
// is to not auto launch if dpkg/apt were run manually by the
 
462
// user
 
463
static time_t
 
464
newest_log_file_timestamp()
 
465
{
 
466
   struct stat buf;
 
467
   int i, j;
 
468
   time_t newest_log_stamp = 0;
 
469
 
 
470
   char *log_glob[] = { "/var/log/dpkg.log*",
 
471
                    "/var/log/apt/term.log*",
 
472
                    NULL };
 
473
 
 
474
   for (i=0; log_glob[i] != NULL; i++) 
 
475
   {
 
476
      glob_t pglob;
 
477
      if(glob(log_glob[i], 0, NULL, &pglob) != 0) {
 
478
         g_warning("error from glob %s\n", log_glob[i]);
 
479
         continue;
 
480
      }
 
481
      for(j=0;j < pglob.gl_pathc; j++) {
 
482
 
 
483
         const char *log = pglob.gl_pathv[j];
 
484
         if(g_stat(log, &buf) <0) {
 
485
            g_warning("can't stat %s\n", log);
 
486
            continue;
 
487
         }
 
488
         if(buf.st_size == 0) {
 
489
            g_warning("log file empty (logrotate?) %s\n", log);
 
490
            continue;
 
491
         }
 
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));
 
498
      }
 
499
      globfree(&pglob);
 
500
   }
 
501
   return newest_log_stamp;
 
502
}
 
503
 
 
504
// check if the auto launch interval is over and its 
 
505
// time to launch again and if the dpkg lock is currently 
 
506
// not taken
 
507
static gboolean 
 
508
auto_launch_now (UpdateTrayAppletPrivate *priv)
 
509
{
 
510
   int interval_days = 0;
 
511
   time_t last_launch = 0;
 
512
 
 
513
   if (dpkg_lock_is_taken())
 
514
      return FALSE;
 
515
 
 
516
#if 0 // disabled because we need more cleverness here in order to cover
 
517
      // the case where people always work on battery (and charge overnight)
 
518
   if (running_on_battery())
 
519
      return FALSE;
 
520
#endif
 
521
 
 
522
   // 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, 
 
526
                                        NULL);
 
527
   g_debug_update ("interval_days from gconf: %i\n", interval_days);
 
528
 
 
529
   if (interval_days <= 0) 
 
530
      return TRUE;
 
531
 
 
532
   // check last launch time 
 
533
   last_launch = gconf_client_get_int(priv->gconf,
 
534
                                      GCONF_KEY_LAST_LAUNCH,
 
535
                                      NULL);
 
536
   g_debug_update ("last_launch from gconf: %i (%s)\n", last_launch, ctime(&last_launch));
 
537
 
 
538
   time_t now = time(NULL);
 
539
   if (auto_launch_security_now(priv, now, last_launch))
 
540
      return TRUE;
 
541
 
 
542
   time_t log_files = newest_log_file_timestamp();
 
543
   last_launch = MAX(log_files, last_launch);
 
544
 
 
545
   g_debug_update("time now %i (%s), delta: %i", now, ctime(&now), now-last_launch);
 
546
   if ((last_launch + (24*60*60*interval_days)) < now) {
 
547
      g_debug_update ("need to auto launch");
 
548
      return TRUE;
 
549
   }
 
550
 
 
551
   return FALSE;
 
552
}
 
553
 
281
554
gboolean
282
555
update_check (TrayApplet *ta)
283
556
{
284
557
   if(ta == NULL)
285
558
      return FALSE;
286
559
 
287
 
   //g_print("update_check()\n");
 
560
   g_debug_update ("update_check()\n");
288
561
   UpdateTrayAppletPrivate *priv = (UpdateTrayAppletPrivate*)ta->user_data;
289
562
 
290
 
   int num_upgrades = 0;
291
 
   int num_security = 0;
 
563
   priv->num_upgrades = 0;
 
564
   priv->num_security = 0;
292
565
   int exit_status = 0;
293
 
   GError *error;
 
566
   GError *error = NULL;
294
567
 
295
 
   char *cmd[] = { UPGRADE_CHECKER, NULL };
 
568
   char *cmd[] = { "/usr/bin/nice",
 
569
                   "/usr/bin/ionice", "-c3",
 
570
                   UPGRADE_CHECKER, 
 
571
                   NULL };
296
572
   char *ret;
297
573
 
298
574
   if(g_spawn_sync(NULL, cmd, NULL,  
317
593
                                      "Package Manager from the "
318
594
                                      "right-click menu or apt-get in "
319
595
                                      "a terminal to see what is wrong.\n"
320
 
                                      "The error message was: '%s'"),
 
596
                                      "The error message was: '%s'. "),
321
597
                                    &ret[2]);
322
598
         else
323
599
            g_string_append(error, _("An error occurred, please run "
342
618
         g_strfreev(ret_array);
343
619
         return TRUE;
344
620
      }
345
 
      num_upgrades = atoi(ret_array[0]);
346
 
      num_security = atoi(ret_array[1]);
 
621
      priv->num_upgrades = atoi(ret_array[0]);
 
622
      priv->num_security = atoi(ret_array[1]);
347
623
      g_free(ret);
348
624
      g_strfreev(ret_array);
349
625
   } else
350
626
      g_warning("Error launching %s", UPGRADE_CHECKER);
351
627
   
352
 
   if (num_upgrades == 0) {
 
628
   g_debug_update("%s returned %i (security: %i)", UPGRADE_CHECKER, priv->num_upgrades, priv->num_security);
 
629
 
 
630
   if (priv->num_upgrades == 0) {
353
631
 
354
632
      // check if the periodic file is very old
355
633
      struct stat buf;
356
634
      if ((stat("/var/lib/apt/periodic/update-success-stamp", &buf) == 0) &&
357
635
          (time(NULL) - buf.st_mtime > OUTDATED_NAG_AGE) ) 
358
 
         g_timeout_add_seconds(OUTDATED_NAG_WAIT, outdated_nag, ta);
 
636
         g_timeout_add_seconds(OUTDATED_NAG_WAIT, (GSourceFunc)outdated_nag, ta);
359
637
 
360
638
      gtk_status_icon_set_visible (ta->tray_icon, FALSE);
361
639
      
366
644
      return TRUE;
367
645
   } 
368
646
 
369
 
   if (num_security == 0)
 
647
   if (priv->num_security == 0)
370
648
      ta->name = "software-update-available";
371
649
   else
372
650
      ta->name = "software-update-urgent";
373
651
   gtk_status_icon_set_from_icon_name(ta->tray_icon, ta->name);
374
652
 
375
653
   // update the tooltip
376
 
   update_trayicon_update_tooltip(ta, num_upgrades);
 
654
   update_trayicon_update_tooltip(ta, priv->num_upgrades);
 
655
 
 
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)) 
 
660
   {
 
661
      gtk_status_icon_set_visible(ta->tray_icon, FALSE);
 
662
      if (auto_launch_now(priv)) 
 
663
      {
 
664
         g_spawn_command_line_async("nice ionice -c3 update-manager "
 
665
                                    "--no-focus-on-map", NULL);
 
666
      }
 
667
      return TRUE;
 
668
   }
377
669
 
378
670
   // if we are already visible, skip the rest
379
671
   if(gtk_status_icon_get_visible (ta->tray_icon))
381
673
 
382
674
   // show the icon
383
675
   gtk_status_icon_set_visible(ta->tray_icon, TRUE);
384
 
 
 
676
   
385
677
   // the user does not no notification messages
386
678
   if(gconf_client_get_bool(priv->gconf,
387
679
                            GCONF_KEY_NO_UPDATE_NOTIFICATIONS, NULL))
389
681
 
390
682
   // show the notification with some delay. otherwise on a login
391
683
   // the origin of the window is 0,0 and that looks ugly
392
 
   g_timeout_add(5000, show_notification, ta);
393
 
   
 
684
   g_timeout_add_seconds(5, show_notification, ta);
 
685
 
394
686
   return TRUE;
395
687
}
396
688