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

« back to all changes in this revision

Viewing changes to src/livepatch-tray.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
 
/* 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 ||
106
 
      !livepatch_is_supported() || 
107
 
      !livepatch_is_running ())
108
 
    {
109
 
      tray_applet_ui_set_visible(ta, FALSE);
110
 
 
111
 
      priv->timeout_id = 0;
112
 
      return G_SOURCE_REMOVE;
113
 
    }
114
 
 
115
 
  tray_applet_ui_set_visible(ta, TRUE);
116
 
 
117
 
  gtk_menu_item_set_label(GTK_MENU_ITEM(priv->menuitem_enabled),
118
 
                          _("Livepatch is on"));
119
 
 
120
 
  check_state = livepatch_get_check_state(&error);
121
 
  if (check_state == NULL)
122
 
    g_warning("Cannot get Livepatch check-state: %s", error->message);
123
 
 
124
 
  state = livepatch_get_state(&error);
125
 
  if (state == NULL)
126
 
    g_warning("Cannot get Livepatch state: %s", error->message);
127
 
 
128
 
  if (!g_strcmp0(check_state, "checked") &&
129
 
      (!g_strcmp0(state, "applied") || !g_strcmp0(state, "nothing-to-apply")))
130
 
    {
131
 
      ssize_t num_fixes;
132
 
      g_autofree gchar *label = NULL;
133
 
 
134
 
      tray_applet_ui_set_icon(ta, "livepatch-on");
135
 
 
136
 
      num_fixes = livepatch_get_num_fixes(&error);
137
 
      if (num_fixes == -1)
138
 
        {
139
 
          g_warning("Cannot get applied Livepatch fixes: %s", error->message);
140
 
          num_fixes = 0;
141
 
        }
142
 
 
143
 
      gtk_widget_set_visible(priv->menuitem_desc, TRUE);
144
 
 
145
 
      if (num_fixes == 0)
146
 
        label = g_strdup(_("No current updates"));
147
 
      else
148
 
        label = g_strdup_printf(ngettext("%zd current update",
149
 
                                         "%zd current updates",
150
 
                                         num_fixes), num_fixes);
151
 
 
152
 
      gtk_menu_item_set_label(GTK_MENU_ITEM(priv->menuitem_desc), label);
153
 
    }
154
 
  else if (!g_strcmp0(check_state, "needs-check") ||
155
 
           !g_strcmp0(state, "unapplied") ||
156
 
           !g_strcmp0(state, "applying"))
157
 
    {
158
 
      /* Check livepatch status again */
159
 
      return G_SOURCE_CONTINUE;
160
 
    }
161
 
  else
162
 
    {
163
 
      tray_applet_ui_set_icon(ta, "livepatch-warning");
164
 
 
165
 
      if (check_state == NULL || !g_strcmp0(check_state, "check-failed"))
166
 
        {
167
 
          gtk_menu_item_set_label(GTK_MENU_ITEM(priv->menuitem_desc),
168
 
            _("An error occured when checking for Livepatch updates."));
169
 
        }
170
 
      else
171
 
        {
172
 
          gtk_menu_item_set_label(GTK_MENU_ITEM(priv->menuitem_desc),
173
 
            _("An error occured when applying Livepatch updates."));
174
 
        }
175
 
    }
176
 
 
177
 
    priv->timeout_id = 0;
178
 
    return G_SOURCE_REMOVE;
179
 
}
180
 
 
181
 
static void
182
 
gsettings_visibility_changed_cb(GSettings *settings,
183
 
                                gchar     *key,
184
 
                                gpointer   user_data)
185
 
{
186
 
  TrayApplet *ta = (TrayApplet *) user_data;
187
 
  LivepatchTrayAppletPriv *priv = (LivepatchTrayAppletPriv *) ta->user_data;
188
 
 
189
 
  if (priv->timeout_id <= 0)
190
 
    priv->timeout_id = g_timeout_add_seconds(0, (GSourceFunc) check_livepatch, ta);
191
 
}
192
 
 
193
 
static void
194
 
livepatch_directory_changed_cb(GFileMonitor     *monitor,
195
 
                               GFile            *file,
196
 
                               GFile            *other_file,
197
 
                               GFileMonitorEvent event_type,
198
 
                               gpointer          user_data)
199
 
{
200
 
  TrayApplet *ta = (TrayApplet *) user_data;
201
 
  LivepatchTrayAppletPriv *priv = (LivepatchTrayAppletPriv *) ta->user_data;
202
 
 
203
 
  if (priv->timeout_id <= 0)
204
 
    priv->timeout_id = g_timeout_add_seconds(TIMEOUT_SECONDS_DELAY,
205
 
                                             (GSourceFunc) check_livepatch, ta);
206
 
}
207
 
 
208
 
void
209
 
livepatch_tray_icon_init(TrayApplet *ta)
210
 
{
211
 
  g_autoptr(GFile) livepatch_common_dir = NULL;
212
 
  g_autoptr(GFile) livepatch_snap_dir = NULL;
213
 
  g_autoptr(GFileMonitor) common_dir_monitor = NULL;
214
 
  g_autoptr(GFileMonitor) snap_dir_monitor = NULL;
215
 
  g_autoptr(GError) error = NULL;
216
 
  LivepatchTrayAppletPriv *priv;
217
 
 
218
 
  if (!livepatch_is_supported())
219
 
    return;
220
 
 
221
 
  if (!livepatch_has_settings_ui())
222
 
    {
223
 
      g_warning("There is no graphical application installed to manage "
224
 
                "Livepatch. The livepatch status icon will not be displayed.");
225
 
      return;
226
 
    }
227
 
 
228
 
  /* Monitor the directory LIVEPATCH_COMMON_DIRECTORY for changes to update
229
 
     the status of the indicator. */
230
 
  livepatch_common_dir = g_file_new_for_path(LIVEPATCH_COMMON_DIRECTORY);
231
 
  common_dir_monitor = g_file_monitor_directory(livepatch_common_dir,
232
 
                                                G_FILE_MONITOR_NONE,
233
 
                                                NULL, &error);
234
 
  if (common_dir_monitor == NULL)
235
 
    {
236
 
        g_warning("Couldn't create directory monitor on %s. Error: %s\n",
237
 
                  LIVEPATCH_COMMON_DIRECTORY, error->message);
238
 
        return;
239
 
    }
240
 
 
241
 
  /* We also need to monitor the directory LIVEPATCH_SNAP_DIRECTORY in order to
242
 
     detect if the snap was enabled/disabled. This consider the case
243
 
     canonical-livepatch is enabled but the snap is disabled. */
244
 
  livepatch_snap_dir = g_file_new_for_path(LIVEPATCH_SNAP_DIRECTORY);
245
 
  snap_dir_monitor = g_file_monitor_directory(livepatch_snap_dir,
246
 
                                              G_FILE_MONITOR_WATCH_MOUNTS,
247
 
                                              NULL, &error);
248
 
  if (snap_dir_monitor == NULL)
249
 
    {
250
 
        g_warning("Couldn't create directory monitor on %s. Error: %s\n",
251
 
                  LIVEPATCH_SNAP_DIRECTORY, error->message);
252
 
        return;
253
 
    }
254
 
 
255
 
  priv = g_new0(LivepatchTrayAppletPriv, 1);
256
 
  priv->common_dir_monitor = g_steal_pointer(&common_dir_monitor);
257
 
  priv->snap_dir_monitor = g_steal_pointer(&snap_dir_monitor);
258
 
  ta->user_data = priv;
259
 
 
260
 
  tray_applet_ui_ensure(ta);
261
 
 
262
 
  /* Menu initialization */
263
 
  livepatch_trayicon_create_menu(ta);
264
 
  tray_applet_ui_set_menu(ta, ta->menu);
265
 
 
266
 
  g_signal_connect(ta->un->settings,
267
 
                   "changed::"SETTINGS_KEY_SHOW_LIVEPATCH_ICON,
268
 
                   G_CALLBACK(gsettings_visibility_changed_cb), ta);
269
 
  g_signal_connect(priv->common_dir_monitor,
270
 
                   "changed",
271
 
                   G_CALLBACK(livepatch_directory_changed_cb), ta);
272
 
  g_signal_connect(priv->snap_dir_monitor,
273
 
                   "changed",
274
 
                   G_CALLBACK(livepatch_directory_changed_cb), ta);
275
 
 
276
 
  /* We always run check_livepatch in a timeout beacuse this allows us to easily
277
 
     check again the status of livepatch if it is in a transistion state (e.g.
278
 
     it is downloading the patches or applying them, etc.) */
279
 
  priv->timeout_id = g_timeout_add_seconds(TIMEOUT_SECONDS_DELAY,
280
 
                                           (GSourceFunc) check_livepatch, ta);
281
 
}