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

« back to all changes in this revision

Viewing changes to src/livepatch-tray.c

  • Committer: Julian Andres Klode
  • Date: 2019-04-02 13:18:28 UTC
  • mfrom: (957.1.5 esm)
  • Revision ID: juliank@ubuntu.com-20190402131828-n7brmzl4gwgvshoe
* Rewrite and extend motd messaging (LP: #1822340)
* Count ESM security updates as security updates

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* livepatch-tray.c
 
2
 * Copyright (C) 2019 Canonical Ltd
 
3
 *
 
4
 * This library is free software; you can redistribute it and/or
 
5
 * modify it under the terms of the GNU Lesser General Public
 
6
 * License as published by the Free Software Foundation; either
 
7
 * version 2 of the License, or (at your option) any later version.
 
8
 *
 
9
 * This library is distributed in the hope that it will be useful,
 
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
12
 * Lesser General Public License for more details.
 
13
 *
 
14
 * You should have received a copy of the GNU Lesser General Public
 
15
 * License along with this library; if not, write to the
 
16
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor
 
17
 * Boston, MA  02110-1301 USA.
 
18
 */
 
19
#include <gio/gdesktopappinfo.h>
 
20
#include <glib.h>
 
21
 
 
22
#include "update-notifier.h"
 
23
#include "livepatch-tray.h"
 
24
#include "livepatch-utils.h"
 
25
#include "trayappletui.h"
 
26
 
 
27
#define LIVEPATCH_COMMON_DIRECTORY "/var/snap/canonical-livepatch/common"
 
28
#define LIVEPATCH_SNAP_DIRECTORY "/snap/canonical-livepatch"
 
29
#define TIMEOUT_SECONDS_DELAY 2
 
30
 
 
31
typedef struct _LivepatchTrayAppletPriv LivepatchTrayAppletPriv;
 
32
struct _LivepatchTrayAppletPriv
 
33
{
 
34
  GtkWidget    *menuitem_enabled;
 
35
  GtkWidget    *menuitem_desc;
 
36
 
 
37
  GFileMonitor *common_dir_monitor;
 
38
  GFileMonitor *snap_dir_monitor;
 
39
 
 
40
  guint         timeout_id;
 
41
};
 
42
 
 
43
static void
 
44
on_settings_menuitem_activated(GObject *self, gpointer user_data)
 
45
{
 
46
  g_autoptr(GDesktopAppInfo) info = NULL;
 
47
  g_autoptr(GdkAppLaunchContext) context = NULL;
 
48
  g_autoptr(GError) error = NULL;
 
49
 
 
50
  info = g_desktop_app_info_new(LIVEPATCH_DESKTOP_FILE);
 
51
  if (info == NULL)
 
52
    {
 
53
      g_warning("Could not find application '%s'", LIVEPATCH_DESKTOP_FILE);
 
54
      return;
 
55
    }
 
56
 
 
57
  context = gdk_display_get_app_launch_context(gdk_display_get_default());
 
58
  if (!g_app_info_launch(G_APP_INFO(info), NULL,
 
59
                         G_APP_LAUNCH_CONTEXT(context), &error))
 
60
    {
 
61
      g_warning("Could not launch application '%s'", LIVEPATCH_DESKTOP_FILE);
 
62
    }
 
63
}
 
64
 
 
65
 
 
66
static void
 
67
livepatch_trayicon_create_menu(TrayApplet *ta)
 
68
{
 
69
  LivepatchTrayAppletPriv *priv = (LivepatchTrayAppletPriv *) ta->user_data;
 
70
  GtkWidget *menuitem;
 
71
 
 
72
  ta->menu = gtk_menu_new();
 
73
 
 
74
  priv->menuitem_enabled = gtk_menu_item_new();
 
75
  gtk_widget_set_sensitive(priv->menuitem_enabled, FALSE);
 
76
  gtk_menu_shell_append(GTK_MENU_SHELL(ta->menu), priv->menuitem_enabled);
 
77
 
 
78
  priv->menuitem_desc = gtk_menu_item_new();
 
79
  gtk_widget_set_sensitive(priv->menuitem_desc, FALSE);
 
80
  gtk_menu_shell_append(GTK_MENU_SHELL(ta->menu), priv->menuitem_desc);
 
81
 
 
82
  menuitem = gtk_separator_menu_item_new();
 
83
  gtk_menu_shell_append(GTK_MENU_SHELL(ta->menu), menuitem);
 
84
 
 
85
  menuitem = gtk_menu_item_new_with_label(_("Livepatch Settings…"));
 
86
  gtk_menu_shell_append(GTK_MENU_SHELL(ta->menu), menuitem);
 
87
  g_signal_connect(G_OBJECT(menuitem), "activate",
 
88
                   G_CALLBACK(on_settings_menuitem_activated), ta);
 
89
 
 
90
  gtk_widget_show_all(ta->menu);
 
91
}
 
92
 
 
93
static gboolean
 
94
check_livepatch(TrayApplet *ta)
 
95
{
 
96
  LivepatchTrayAppletPriv *priv = (LivepatchTrayAppletPriv *) ta->user_data;
 
97
  gboolean show_status_icon;
 
98
  g_autofree gchar *check_state = NULL;
 
99
  g_autofree gchar *state = NULL;
 
100
  g_autoptr(GError) error = NULL;
 
101
 
 
102
  show_status_icon = g_settings_get_boolean(ta->un->settings,
 
103
                                            SETTINGS_KEY_SHOW_LIVEPATCH_ICON);
 
104
 
 
105
  if (!show_status_icon || !livepatch_is_supported())
 
106
    {
 
107
      tray_applet_ui_set_visible(ta, FALSE);
 
108
 
 
109
      priv->timeout_id = 0;
 
110
      return G_SOURCE_REMOVE;
 
111
    }
 
112
 
 
113
  tray_applet_ui_set_visible(ta, TRUE);
 
114
 
 
115
  if (!livepatch_is_running())
 
116
    {
 
117
      tray_applet_ui_set_icon(ta, "livepatch-off");
 
118
      gtk_menu_item_set_label(GTK_MENU_ITEM(priv->menuitem_enabled),
 
119
                              _("Livepatch is off"));
 
120
      gtk_widget_set_visible(priv->menuitem_desc, FALSE);
 
121
 
 
122
      priv->timeout_id = 0;
 
123
      return G_SOURCE_REMOVE;
 
124
    }
 
125
 
 
126
  gtk_menu_item_set_label(GTK_MENU_ITEM(priv->menuitem_enabled),
 
127
                          _("Livepatch is on"));
 
128
 
 
129
  check_state = livepatch_get_check_state(&error);
 
130
  if (check_state == NULL)
 
131
    g_warning("Cannot get Livepatch check-state: %s", error->message);
 
132
 
 
133
  state = livepatch_get_state(&error);
 
134
  if (state == NULL)
 
135
    g_warning("Cannot get Livepatch state: %s", error->message);
 
136
 
 
137
  if (!g_strcmp0(check_state, "checked") &&
 
138
      (!g_strcmp0(state, "applied") || !g_strcmp0(state, "nothing-to-apply")))
 
139
    {
 
140
      gssize num_fixes;
 
141
      g_autofree gchar *label = NULL;
 
142
 
 
143
      tray_applet_ui_set_icon(ta, "livepatch-on");
 
144
 
 
145
      num_fixes = livepatch_get_num_fixes(&error);
 
146
      if (num_fixes == -1)
 
147
        {
 
148
          g_warning("Cannot get applied Livepatch fixes: %s", error->message);
 
149
          num_fixes = 0;
 
150
        }
 
151
 
 
152
      gtk_widget_set_visible(priv->menuitem_desc, TRUE);
 
153
 
 
154
      if (num_fixes == 0)
 
155
        label = g_strdup(_("No current updates"));
 
156
      else
 
157
        label = g_strdup_printf(ngettext("%" G_GSSIZE_FORMAT " current update",
 
158
                                         "%" G_GSSIZE_FORMAT " current updates",
 
159
                                         num_fixes), num_fixes);
 
160
 
 
161
      gtk_menu_item_set_label(GTK_MENU_ITEM(priv->menuitem_desc), label);
 
162
    }
 
163
  else if (!g_strcmp0(check_state, "needs-check") ||
 
164
           !g_strcmp0(state, "unapplied"))
 
165
    {
 
166
      /* Check livepatch status again */
 
167
      return G_SOURCE_CONTINUE;
 
168
    }
 
169
  else
 
170
    {
 
171
      tray_applet_ui_set_icon(ta, "livepatch-warning");
 
172
 
 
173
      if (check_state == NULL || !g_strcmp0(check_state, "check-failed"))
 
174
        {
 
175
          gtk_menu_item_set_label(GTK_MENU_ITEM(priv->menuitem_desc),
 
176
            _("An error occured when checking for Livepatch updates."));
 
177
        }
 
178
      else
 
179
        {
 
180
          gtk_menu_item_set_label(GTK_MENU_ITEM(priv->menuitem_desc),
 
181
            _("An error occured when applying Livepatch updates."));
 
182
        }
 
183
    }
 
184
 
 
185
    priv->timeout_id = 0;
 
186
    return G_SOURCE_REMOVE;
 
187
}
 
188
 
 
189
static void
 
190
gsettings_visibility_changed_cb(GSettings *settings,
 
191
                                gchar     *key,
 
192
                                gpointer   user_data)
 
193
{
 
194
  TrayApplet *ta = (TrayApplet *) user_data;
 
195
  LivepatchTrayAppletPriv *priv = (LivepatchTrayAppletPriv *) ta->user_data;
 
196
 
 
197
  if (priv->timeout_id <= 0)
 
198
    priv->timeout_id = g_timeout_add_seconds(0, (GSourceFunc) check_livepatch, ta);
 
199
}
 
200
 
 
201
static void
 
202
livepatch_directory_changed_cb(GFileMonitor     *monitor,
 
203
                               GFile            *file,
 
204
                               GFile            *other_file,
 
205
                               GFileMonitorEvent event_type,
 
206
                               gpointer          user_data)
 
207
{
 
208
  TrayApplet *ta = (TrayApplet *) user_data;
 
209
  LivepatchTrayAppletPriv *priv = (LivepatchTrayAppletPriv *) ta->user_data;
 
210
 
 
211
  if (priv->timeout_id <= 0)
 
212
    priv->timeout_id = g_timeout_add_seconds(TIMEOUT_SECONDS_DELAY,
 
213
                                             (GSourceFunc) check_livepatch, ta);
 
214
}
 
215
 
 
216
void
 
217
livepatch_tray_icon_init(TrayApplet *ta)
 
218
{
 
219
  g_autoptr(GFile) livepatch_common_dir = NULL;
 
220
  g_autoptr(GFile) livepatch_snap_dir = NULL;
 
221
  g_autoptr(GFileMonitor) common_dir_monitor = NULL;
 
222
  g_autoptr(GFileMonitor) snap_dir_monitor = NULL;
 
223
  g_autoptr(GError) error = NULL;
 
224
  LivepatchTrayAppletPriv *priv;
 
225
 
 
226
  if (!livepatch_is_supported())
 
227
    return;
 
228
 
 
229
  if (!livepatch_has_settings_ui())
 
230
    {
 
231
      g_warning("There is no graphical application installed to manage "
 
232
                "Livepatch. The livepatch status icon will not be displayed.");
 
233
      return;
 
234
    }
 
235
 
 
236
  /* Monitor the directory LIVEPATCH_COMMON_DIRECTORY for changes to update
 
237
     the status of the indicator. */
 
238
  livepatch_common_dir = g_file_new_for_path(LIVEPATCH_COMMON_DIRECTORY);
 
239
  common_dir_monitor = g_file_monitor_directory(livepatch_common_dir,
 
240
                                                G_FILE_MONITOR_NONE,
 
241
                                                NULL, &error);
 
242
  if (common_dir_monitor == NULL)
 
243
    {
 
244
        g_warning("Couldn't create directory monitor on %s. Error: %s\n",
 
245
                  LIVEPATCH_COMMON_DIRECTORY, error->message);
 
246
        return;
 
247
    }
 
248
 
 
249
  /* We also need to monitor the directory LIVEPATCH_SNAP_DIRECTORY in order to
 
250
     detect if the snap was enabled/disabled. This consider the case
 
251
     canonical-livepatch is enabled but the snap is disabled. */
 
252
  livepatch_snap_dir = g_file_new_for_path(LIVEPATCH_SNAP_DIRECTORY);
 
253
  snap_dir_monitor = g_file_monitor_directory(livepatch_snap_dir,
 
254
                                              G_FILE_MONITOR_WATCH_MOUNTS,
 
255
                                              NULL, &error);
 
256
  if (snap_dir_monitor == NULL)
 
257
    {
 
258
        g_warning("Couldn't create directory monitor on %s. Error: %s\n",
 
259
                  LIVEPATCH_SNAP_DIRECTORY, error->message);
 
260
        return;
 
261
    }
 
262
 
 
263
  priv = g_new0(LivepatchTrayAppletPriv, 1);
 
264
  priv->common_dir_monitor = g_steal_pointer(&common_dir_monitor);
 
265
  priv->snap_dir_monitor = g_steal_pointer(&snap_dir_monitor);
 
266
  ta->user_data = priv;
 
267
 
 
268
  tray_applet_ui_ensure(ta);
 
269
 
 
270
  /* Menu initialization */
 
271
  livepatch_trayicon_create_menu(ta);
 
272
  tray_applet_ui_set_menu(ta, ta->menu);
 
273
 
 
274
  g_signal_connect(ta->un->settings,
 
275
                   "changed::"SETTINGS_KEY_SHOW_LIVEPATCH_ICON,
 
276
                   G_CALLBACK(gsettings_visibility_changed_cb), ta);
 
277
  g_signal_connect(priv->common_dir_monitor,
 
278
                   "changed",
 
279
                   G_CALLBACK(livepatch_directory_changed_cb), ta);
 
280
  g_signal_connect(priv->snap_dir_monitor,
 
281
                   "changed",
 
282
                   G_CALLBACK(livepatch_directory_changed_cb), ta);
 
283
 
 
284
  /* We always run check_livepatch in a timeout beacuse this allows us to easily
 
285
     check again the status of livepatch if it is in a transistion state (e.g.
 
286
     it is downloading the patches or applying them, etc.) */
 
287
  priv->timeout_id = g_timeout_add_seconds(TIMEOUT_SECONDS_DELAY,
 
288
                                           (GSourceFunc) check_livepatch, ta);
 
289
}