Merge lp:~azzar1/update-notifier/ubuntu-bionic-livepatch into lp:~ubuntu-core-dev/update-notifier/ubuntu-bionic

Proposed by Andrea Azzarone on 2019-04-05
Status: Merged
Approved by: Sebastien Bacher on 2019-04-11
Approved revision: 942
Merged at revision: 940
Proposed branch: lp:~azzar1/update-notifier/ubuntu-bionic-livepatch
Merge into: lp:~ubuntu-core-dev/update-notifier/ubuntu-bionic
Diff against target: 980 lines (+698/-63)
19 files modified
configure.ac (+2/-1)
data/com.ubuntu.update-notifier.gschema.xml.in (+5/-0)
debian/changelog (+7/-0)
debian/control (+1/-0)
debian/update-notifier.install (+1/-0)
pixmaps/Makefile.am (+1/-1)
pixmaps/scalable/Makefile.am (+5/-0)
pixmaps/scalable/livepatch-off.svg (+1/-0)
pixmaps/scalable/livepatch-on.svg (+1/-0)
pixmaps/scalable/livepatch-warning.svg (+1/-0)
po/POTFILES.in (+1/-0)
src/Makefile.am (+7/-1)
src/livepatch-tray.c (+289/-0)
src/livepatch-tray.h (+22/-0)
src/livepatch-utils.c (+242/-0)
src/livepatch-utils.h (+39/-0)
src/livepatch.c (+65/-60)
src/update-notifier.c (+6/-0)
src/update-notifier.h (+2/-0)
To merge this branch: bzr merge lp:~azzar1/update-notifier/ubuntu-bionic-livepatch
Reviewer Review Type Date Requested Status
Sebastien Bacher 2019-04-05 Approve on 2019-04-11
Review via email: mp+365597@code.launchpad.net

Commit message

Cherry-pick changes to Livepatch from Disco.

To post a comment you must log in.
Sebastien Bacher (seb128) wrote :

Thanks, code looks fine and work as expected!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'configure.ac'
--- configure.ac 2013-02-01 00:51:16 +0000
+++ configure.ac 2019-04-05 14:32:08 +0000
@@ -13,7 +13,7 @@
13GNOME_COMMON_INIT13GNOME_COMMON_INIT
14GLIB_GSETTINGS14GLIB_GSETTINGS
1515
16pkg_modules="glib-2.0 >= 2.34 gtk+-3.0 libnotify gio-2.0 >= 2.26 x11"16pkg_modules="glib-2.0 >= 2.34 gtk+-3.0 libnotify gio-2.0 >= 2.26 x11 json-glib-1.0"
1717
18PKG_CHECK_EXISTS(gudev-1.0, [ HAVE_GUDEV=1 ])18PKG_CHECK_EXISTS(gudev-1.0, [ HAVE_GUDEV=1 ])
19if test "x$HAVE_GUDEV" != "x"; then19if test "x$HAVE_GUDEV" != "x"; then
@@ -100,6 +100,7 @@
100pixmaps/22x22/Makefile100pixmaps/22x22/Makefile
101pixmaps/24x24/Makefile101pixmaps/24x24/Makefile
102pixmaps/48x48/Makefile102pixmaps/48x48/Makefile
103pixmaps/scalable/Makefile
103po/Makefile.in104po/Makefile.in
104])105])
105106
106107
=== modified file 'data/com.ubuntu.update-notifier.gschema.xml.in'
--- data/com.ubuntu.update-notifier.gschema.xml.in 2013-06-05 17:31:47 +0000
+++ data/com.ubuntu.update-notifier.gschema.xml.in 2019-04-05 14:32:08 +0000
@@ -25,5 +25,10 @@
25 <summary>Time of last release check</summary>25 <summary>Time of last release check</summary>
26 <description>The last time update-notifier checked for a new release. The format is seconds since UNIX epoch.</description>26 <description>The last time update-notifier checked for a new release. The format is seconds since UNIX epoch.</description>
27 </key>27 </key>
28 <key name="show-livepatch-status-icon" type="b">
29 <default>true</default>
30 <summary>Show Livepatch status icon</summary>
31 <description>Enable or diable the Livepatch status icon in the systray.</description>
32 </key>
28 </schema>33 </schema>
29</schemalist>34</schemalist>
3035
=== modified file 'debian/changelog'
--- debian/changelog 2019-01-17 14:17:50 +0000
+++ debian/changelog 2019-04-05 14:32:08 +0000
@@ -1,3 +1,10 @@
1update-notifier (3.192.1.6) UNRELEASED; urgency=medium
2
3 * Add a Livepatch indicator in the system tray. (LP: #1820259)
4 * Add a "Settings..." button to the Livepatch notification. (LP: #1823351)
5
6 -- Andrea Azzarone <andrea.azzarone@canonical.com> Fri, 05 Apr 2019 15:20:05 +0100
7
1update-notifier (3.192.1.5) bionic; urgency=medium8update-notifier (3.192.1.5) bionic; urgency=medium
29
3 * [ Andrea Azzarone ]10 * [ Andrea Azzarone ]
411
=== modified file 'debian/control'
--- debian/control 2018-04-09 11:35:03 +0000
+++ debian/control 2019-04-05 14:32:08 +0000
@@ -6,6 +6,7 @@
6 dh-python,6 dh-python,
7 libgtk-3-dev,7 libgtk-3-dev,
8 libglib2.0-dev (>= 2.34),8 libglib2.0-dev (>= 2.34),
9 libjson-glib-dev,
9 intltool,10 intltool,
10 libnotify-dev (>= 0.7),11 libnotify-dev (>= 0.7),
11 libgudev-1.0-dev,12 libgudev-1.0-dev,
1213
=== modified file 'debian/update-notifier.install'
--- debian/update-notifier.install 2017-09-29 14:37:12 +0000
+++ debian/update-notifier.install 2019-04-05 14:32:08 +0000
@@ -4,6 +4,7 @@
4usr/share/icons/hicolor/22x22/4usr/share/icons/hicolor/22x22/
5usr/share/icons/hicolor/24x24/5usr/share/icons/hicolor/24x24/
6usr/share/icons/hicolor/48x48/6usr/share/icons/hicolor/48x48/
7usr/share/icons/hicolor/scalable/
7usr/share/glib-2.0/schemas/com.ubuntu.update-notifier.gschema.xml8usr/share/glib-2.0/schemas/com.ubuntu.update-notifier.gschema.xml
8usr/bin/update-notifier9usr/bin/update-notifier
9usr/bin/distro-cd-updater usr/lib/update-notifier/10usr/bin/distro-cd-updater usr/lib/update-notifier/
1011
=== modified file 'pixmaps/Makefile.am'
--- pixmaps/Makefile.am 2007-11-16 09:44:48 +0000
+++ pixmaps/Makefile.am 2019-04-05 14:32:08 +0000
@@ -1,1 +1,1 @@
1SUBDIRS = 16x16 22x22 24x24 48x48 1SUBDIRS = 16x16 22x22 24x24 48x48 scalable
22
=== added directory 'pixmaps/scalable'
=== added file 'pixmaps/scalable/Makefile.am'
--- pixmaps/scalable/Makefile.am 1970-01-01 00:00:00 +0000
+++ pixmaps/scalable/Makefile.am 2019-04-05 14:32:08 +0000
@@ -0,0 +1,5 @@
1
2appicondir = $(datadir)/icons/hicolor/scalable/apps
3appicon_DATA = *.svg
4
5EXTRA_DIST = $(appicon_DATA)
06
=== added file 'pixmaps/scalable/livepatch-off.svg'
--- pixmaps/scalable/livepatch-off.svg 1970-01-01 00:00:00 +0000
+++ pixmaps/scalable/livepatch-off.svg 2019-04-05 14:32:08 +0000
@@ -0,0 +1,1 @@
1<svg id="Livepatch-_off" data-name="Livepatch- off" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19 19"><defs><style>.cls-1{fill:none;}.cls-2{fill:#ccc;}.cls-3{fill:#df382c;}.cls-4{fill:#fff;}</style></defs><title>Artboard 1</title><g id="layer1"><rect id="rect4782" class="cls-1" y="0.13" width="19" height="19"/><path id="path4210" class="cls-2" d="M9.5.52S7.56,3.77,2.38,3.77c0,11.06,7.12,15,7.12,15s7.13-3.9,7.13-15C11.46,3.77,9.5.52,9.5.52Z"/></g><circle class="cls-3" cx="13.56" cy="11.99" r="4.4"/><path id="path4179" class="cls-4" d="M11.86,9.76l-.4.4,1.68,1.68-1.68,1.68.4.4,1.68-1.68,1.68,1.68.4-.4L14,11.84l1.67-1.68-.4-.4-1.68,1.67Z"/></svg>
0\ No newline at end of file2\ No newline at end of file
13
=== added file 'pixmaps/scalable/livepatch-on.svg'
--- pixmaps/scalable/livepatch-on.svg 1970-01-01 00:00:00 +0000
+++ pixmaps/scalable/livepatch-on.svg 2019-04-05 14:32:08 +0000
@@ -0,0 +1,1 @@
1<svg id="livepatch_-_on" data-name="livepatch - on" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19 19"><defs><style>.cls-1{fill:none;}.cls-2{fill:#ccc;}.cls-3{fill:#38b44a;}.cls-4{fill:#fff;}</style></defs><title>Artboard 1</title><g id="layer1"><rect id="rect4782" class="cls-1" y="0.13" width="19" height="19"/><path id="path4210" class="cls-2" d="M9.5.52S7.56,3.77,2.38,3.77c0,11.06,7.12,15,7.12,15s7.13-3.9,7.13-15C11.46,3.77,9.5.52,9.5.52Z"/></g><path id="path4155" class="cls-3" d="M13.56,7.59A4.4,4.4,0,1,0,18,12,4.4,4.4,0,0,0,13.56,7.59Z"/><path id="path4041-9" class="cls-4" d="M16,10.12l-2.8,2.46L11.57,11.2l-.3.33,1.92,2,3-3.18Z"/></svg>
0\ No newline at end of file2\ No newline at end of file
13
=== added file 'pixmaps/scalable/livepatch-warning.svg'
--- pixmaps/scalable/livepatch-warning.svg 1970-01-01 00:00:00 +0000
+++ pixmaps/scalable/livepatch-warning.svg 2019-04-05 14:32:08 +0000
@@ -0,0 +1,1 @@
1<svg id="livepatch-_warning" data-name="livepatch- warning" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19 19"><defs><style>.cls-1{fill:none;}.cls-2{fill:#ccc;}.cls-3{fill:#e95420;}.cls-4{fill:#fff;}</style></defs><title>Artboard 1</title><g id="layer1"><rect id="rect4782" class="cls-1" x="-0.03" y="0.09" width="19.07" height="19.07"/><path id="path4210" class="cls-2" d="M9.5.49S7.56,3.75,2.35,3.75c0,11.1,7.15,15,7.15,15s7.16-3.91,7.16-15C11.47,3.75,9.5.49,9.5.49Z"/></g><path id="path4155" class="cls-3" d="M13.56,7.59A4.4,4.4,0,1,0,18,12,4.4,4.4,0,0,0,13.56,7.59Z"/><path id="path4159" class="cls-4" d="M13.06,9.07v.84c0,.54,0,1.05,0,1.51s.07.91.12,1.38h.67q.07-.71.12-1.38c0-.46,0-1,0-1.51V9.07Zm.5,4.84a.63.63,0,0,0-.44.19.57.57,0,0,0-.18.44.61.61,0,0,0,.18.45.66.66,0,0,0,.44.17A.64.64,0,0,0,14,15a.61.61,0,0,0,.18-.45A.57.57,0,0,0,14,14.1.62.62,0,0,0,13.56,13.91Z"/></svg>
0\ No newline at end of file2\ No newline at end of file
13
=== modified file 'po/POTFILES.in'
--- po/POTFILES.in 2017-08-21 17:30:53 +0000
+++ po/POTFILES.in 2019-04-05 14:32:08 +0000
@@ -11,6 +11,7 @@
11src/cdroms.c11src/cdroms.c
12src/hooks.c12src/hooks.c
13src/livepatch.c13src/livepatch.c
14src/livepatch-tray.c
14src/update.c15src/update.c
15src/update-notifier.c16src/update-notifier.c
16[type: gettext/glade]ui/hooks-dialog.ui17[type: gettext/glade]ui/hooks-dialog.ui
1718
=== modified file 'src/Makefile.am'
--- src/Makefile.am 2018-04-20 18:02:03 +0000
+++ src/Makefile.am 2019-04-05 14:32:08 +0000
@@ -23,6 +23,10 @@
23 hooks.c\23 hooks.c\
24 crash.c\24 crash.c\
25 crash.h\25 crash.h\
26 livepatch-tray.c\
27 livepatch-tray.h\
28 livepatch-utils.c\
29 livepatch-utils.h\
26 update.c\30 update.c\
27 update.h\31 update.h\
28 release.c\32 release.c\
@@ -41,7 +45,9 @@
41system_crash_notification_SOURCES = system-crash.c\45system_crash_notification_SOURCES = system-crash.c\
42 system-crash.h46 system-crash.h
4347
44livepatch_notification_SOURCES = livepatch.c48livepatch_notification_SOURCES = livepatch.c\
49 livepatch-utils.c\
50 livepatch-utils.h
4551
46update_notifier_LDADD = $(PACKAGE_LIBS) $(INTLLIBS) $(APP_INDICATOR_LIBS)52update_notifier_LDADD = $(PACKAGE_LIBS) $(INTLLIBS) $(APP_INDICATOR_LIBS)
47update_notifier_LDFLAGS = -export-dynamic53update_notifier_LDFLAGS = -export-dynamic
4854
=== added file 'src/livepatch-tray.c'
--- src/livepatch-tray.c 1970-01-01 00:00:00 +0000
+++ src/livepatch-tray.c 2019-04-05 14:32:08 +0000
@@ -0,0 +1,289 @@
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
31typedef struct _LivepatchTrayAppletPriv LivepatchTrayAppletPriv;
32struct _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
43static void
44on_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
66static void
67livepatch_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
93static gboolean
94check_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
189static void
190gsettings_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
201static void
202livepatch_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
216void
217livepatch_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}
0290
=== added file 'src/livepatch-tray.h'
--- src/livepatch-tray.h 1970-01-01 00:00:00 +0000
+++ src/livepatch-tray.h 2019-04-05 14:32:08 +0000
@@ -0,0 +1,22 @@
1/* livepatch-tray.h
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
20#pragma once
21
22void livepatch_tray_icon_init(TrayApplet *ta);
023
=== added file 'src/livepatch-utils.c'
--- src/livepatch-utils.c 1970-01-01 00:00:00 +0000
+++ src/livepatch-utils.c 2019-04-05 14:32:08 +0000
@@ -0,0 +1,242 @@
1/* livepatch-utils.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
20#include "livepatch-utils.h"
21
22#include <gio/gdesktopappinfo.h>
23#include <glib.h>
24#include <json-glib/json-glib.h>
25
26#define OS_RELEASE_PATH "/etc/os-release"
27
28GQuark
29livepatch_error_quark(void)
30{
31 static GQuark q = 0;
32
33 if (G_UNLIKELY(!q))
34 q = g_quark_from_static_string("livepatch-error-quark");
35
36 return q;
37}
38
39static GKeyFile *
40parse_osrelease()
41{
42 g_autoptr(GKeyFile) keys = NULL;
43 g_autofree gchar *content = NULL;
44 g_autofree gchar *content_with_group = NULL;
45 const gchar *group = "[os-release]\n";
46 g_autoptr(GError) error = NULL;
47
48 if (!g_file_get_contents(OS_RELEASE_PATH, &content, NULL, &error))
49 {
50 g_warning("Failed to read '%s', error: %s",
51 OS_RELEASE_PATH, error->message);
52 return NULL;
53 }
54
55 content_with_group = g_strdup_printf("%s%s", group, content);
56
57 keys = g_key_file_new();
58 if (!g_key_file_load_from_data(keys, content_with_group, -1,
59 G_KEY_FILE_NONE, &error))
60 {
61 g_warning("Failed to parse /etc/os-release: %s", error->message);
62 return NULL;
63 }
64
65 return g_steal_pointer(&keys);
66}
67
68static JsonNode *
69livepatch_get_status(GError **error)
70{
71 g_autofree gchar *std_output = NULL;
72 g_autofree gchar *std_error = NULL;
73 g_autoptr(JsonParser) json_parser = NULL;
74 JsonNode *root_node;
75
76 if (!g_spawn_command_line_sync("canonical-livepatch status --format=json",
77 &std_output, &std_error, NULL, error))
78 return NULL;
79
80 if (std_output == NULL || strlen(std_output) == 0)
81 {
82 if (std_error == NULL || strlen(std_error) == 0)
83 {
84 g_set_error(error,
85 LIVEPATCH_ERROR, LIVEPATCH_ERROR_CMD_FAILED,
86 "canonical-livepatch returned an empty status.");
87 }
88 else
89 {
90 g_set_error(error,
91 LIVEPATCH_ERROR, LIVEPATCH_ERROR_CMD_FAILED,
92 "canonical-livepatch status returned an error: %s",
93 std_error);
94 }
95
96 return NULL;
97 }
98
99 json_parser = json_parser_new();
100 if (!json_parser_load_from_data(json_parser, std_output, -1, error))
101 return NULL;
102
103 root_node = json_parser_get_root(json_parser);
104 if (root_node == NULL)
105 {
106 g_set_error(error,
107 LIVEPATCH_ERROR, LIVEPATCH_ERROR_CMD_FAILED,
108 "The output of canonical-livepatch has no json root node.");
109 return NULL;
110 }
111
112 return json_node_copy(root_node);
113}
114
115static JsonNode *
116livepatch_get_status_node(const gchar *expr, GError **error)
117{
118 g_autoptr(JsonNode) root = NULL;
119
120 root = livepatch_get_status(error);
121 if (root == NULL)
122 return NULL;
123
124 return json_path_query(expr, root, error);
125}
126
127gboolean
128livepatch_has_settings_ui()
129{
130 g_autoptr(GDesktopAppInfo) info = NULL;
131
132 info = g_desktop_app_info_new(LIVEPATCH_DESKTOP_FILE);
133 return info != NULL;
134}
135
136gboolean
137livepatch_is_supported()
138{
139 g_autoptr(GKeyFile) file = NULL;
140 g_autofree gchar *version = NULL;
141 g_autoptr(GError) error = NULL;
142
143 file = parse_osrelease();
144 if (file == NULL)
145 return FALSE;
146
147 version = g_key_file_get_string(file, "os-release", "VERSION", &error);
148 if (version == NULL)
149 {
150 g_warning("Failed to get the version from the file %s: %s",
151 OS_RELEASE_PATH, error->message);
152 return FALSE;
153 }
154
155 return g_strstr_len(version, -1, "LTS") != NULL;
156}
157
158gboolean
159livepatch_is_running()
160{
161 g_autoptr(JsonNode) result_node = NULL;
162 JsonNode *running_node = NULL;
163 JsonArray *result_array;
164
165 result_node = livepatch_get_status_node("$.Status[0].Running", NULL);
166 if (result_node == NULL)
167 return FALSE;
168
169 result_array = json_node_get_array(result_node);
170
171 if (json_array_get_length(result_array) == 0)
172 return FALSE;
173
174 running_node = json_array_get_element(result_array, 0);
175 return json_node_get_boolean(running_node);
176}
177
178gchar *
179livepatch_get_state(GError **error)
180{
181 g_autoptr(JsonNode) result_node = NULL;
182 JsonNode *state_node = NULL;
183 JsonArray *result_array;
184 const gchar *expr = "$.Status[0].Livepatch.State";
185
186 result_node = livepatch_get_status_node(expr, error);
187 if (result_node == NULL)
188 return NULL;
189
190 result_array = json_node_get_array(result_node);
191
192 if (json_array_get_length(result_array) == 0)
193 {
194 g_set_error(error,
195 LIVEPATCH_ERROR, LIVEPATCH_ERROR_NOMATCH,
196 "No matches for: %s", expr);
197 return NULL;
198 }
199
200 state_node = json_array_get_element(result_array, 0);
201 return g_strdup(json_node_get_string(state_node));
202}
203
204gchar *
205livepatch_get_check_state(GError **error)
206{
207 g_autoptr(JsonNode) result_node = NULL;
208 JsonNode *state_node = NULL;
209 JsonArray *result_array;
210 const gchar *expr = "$.Status[0].Livepatch.CheckState";
211
212 result_node = livepatch_get_status_node(expr, error);
213 if (result_node == NULL)
214 return NULL;
215
216 result_array = json_node_get_array(result_node);
217
218 if (json_array_get_length(result_array) == 0)
219 {
220 g_set_error(error,
221 LIVEPATCH_ERROR, LIVEPATCH_ERROR_NOMATCH,
222 "No matches for: %s", expr);
223 return NULL;
224 }
225
226 state_node = json_array_get_element(result_array, 0);
227 return g_strdup(json_node_get_string(state_node));
228}
229
230gssize
231livepatch_get_num_fixes(GError **error)
232{
233 g_autoptr(JsonNode) node = NULL;
234 JsonArray *array;
235
236 node = livepatch_get_status_node("$.Status[0].Livepatch.Fixes[*]", error);
237 if (node == NULL)
238 return -1;
239
240 array = json_node_get_array(node);
241 return json_array_get_length(array);
242}
0243
=== added file 'src/livepatch-utils.h'
--- src/livepatch-utils.h 1970-01-01 00:00:00 +0000
+++ src/livepatch-utils.h 2019-04-05 14:32:08 +0000
@@ -0,0 +1,39 @@
1/* livepatch-utils.h
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
20#pragma once
21
22#include <gio/gio.h>
23
24#define LIVEPATCH_ERROR livepatch_error_quark()
25#define LIVEPATCH_DESKTOP_FILE "software-properties-livepatch.desktop"
26
27typedef enum
28{
29 LIVEPATCH_ERROR_CMD_FAILED,
30 LIVEPATCH_ERROR_NOMATCH
31} LivepatchError;
32
33
34gboolean livepatch_has_settings_ui ();
35gboolean livepatch_is_supported ();
36gboolean livepatch_is_running ();
37gchar* livepatch_get_state (GError **error);
38gchar* livepatch_get_check_state (GError **error);
39gssize livepatch_get_num_fixes (GError **error);
040
=== modified file 'src/livepatch.c'
--- src/livepatch.c 2018-11-12 15:58:56 +0000
+++ src/livepatch.c 2019-04-05 14:32:08 +0000
@@ -2,14 +2,14 @@
2#include "config.h"2#include "config.h"
3#endif3#endif
44
5#include <errno.h>5#include <gio/gdesktopappinfo.h>
6#include <glib.h>6#include <glib.h>
7#include <glib/gstdio.h>7#include <glib/gstdio.h>
8#include <libnotify/notify.h>8#include <libnotify/notify.h>
9#include <stdlib.h>
10#include <sys/sysinfo.h>9#include <sys/sysinfo.h>
1110
12#include "update-notifier.h"11#include "update-notifier.h"
12#include "livepatch-utils.h"
1313
14#define STATUS_PATH "/var/snap/canonical-livepatch/current/status"14#define STATUS_PATH "/var/snap/canonical-livepatch/current/status"
1515
@@ -29,37 +29,54 @@
29}29}
3030
31static void31static void
32notify_action_cb (NotifyNotification *notification,
33 char *action,
34 gpointer user_data)
35{
36 g_autoptr(GDesktopAppInfo) info = NULL;
37 g_autoptr(GdkAppLaunchContext) context = NULL;
38 g_autoptr(GError) error = NULL;
39
40 info = g_desktop_app_info_new (LIVEPATCH_DESKTOP_FILE);
41 if (!info) {
42 g_warning ("Could not find application '%s'", LIVEPATCH_DESKTOP_FILE);
43 return;
44 }
45
46 context = gdk_display_get_app_launch_context (gdk_display_get_default ());
47 if (!g_app_info_launch (G_APP_INFO (info), NULL, G_APP_LAUNCH_CONTEXT (context), &error)) {
48 g_warning ("Could not launch application '%s'", LIVEPATCH_DESKTOP_FILE);
49 }
50}
51
52static gboolean
32show_notification (const char *summary, const char *body, const char *icon)53show_notification (const char *summary, const char *body, const char *icon)
33{54{
34 NotifyNotification *n = notify_notification_new (summary, body, icon);55 NotifyNotification *n;
56 g_autoptr(GError) error = NULL;
57
58 n = notify_notification_new (summary, body, icon);
35 notify_notification_set_timeout (n, 60000);59 notify_notification_set_timeout (n, 60000);
36 notify_notification_show (n, NULL);60
37 g_object_unref (n);61 if (livepatch_has_settings_ui()) {
38}62 notify_notification_add_action (n, "settings", _("Show Settings…"),
3963 notify_action_cb, NULL, NULL);
40static void64 notify_notification_add_action (n, "default", _("Show Settings…"),
41get_event_from_file (const char* filename, char **event, char **description)65 notify_action_cb, NULL, NULL);
42{66 } else {
43 g_autofree gchar *content = NULL;67 g_warning ("There is no graphical application installed to manage "
4468 "Livepatch. The notification will not have a "
45 g_return_if_fail (filename != NULL);69 "'Show Settings…' button.");
46 g_return_if_fail (event != NULL);70 }
47 g_return_if_fail (description != NULL);71
4872 g_signal_connect (n, "closed", G_CALLBACK (gtk_main_quit), NULL);
49 *event = *description = NULL;73
5074 if (!notify_notification_show (n, &error)) {
51 g_file_get_contents (filename, &content, NULL, NULL);75 g_warning ("Could not show notification: '%s", error->message);
5276 return FALSE;
53 if (content) {77 }
54 gchar **strings = g_strsplit (content, " ", 2);78
5579 return TRUE;
56 if (g_strv_length (strings) > 0)
57 *event = g_strdup (g_strstrip (strings[0]));
58 if (g_strv_length (strings) > 1)
59 *description = g_strdup (g_strstrip (strings[1]));
60
61 g_strfreev (strings);
62 }
63}80}
6481
65static long82static long
@@ -94,51 +111,39 @@
94 return difftime (status_stat.st_mtim.tv_sec, boot_timestamp) >= 0;111 return difftime (status_stat.st_mtim.tv_sec, boot_timestamp) >= 0;
95}112}
96113
97static void114static gboolean
98show_status_notification ()115show_status_notification ()
99{116{
100 g_autofree gchar *event = NULL;117 g_autofree gchar *state = NULL;
101 g_autofree gchar *description = NULL;118 g_autoptr(GError) error = NULL;
102119
103 if (!g_file_test (STATUS_PATH, G_FILE_TEST_EXISTS))120 if (!g_file_test (STATUS_PATH, G_FILE_TEST_EXISTS))
104 return;121 return FALSE;
105122
106 if (!file_modified_after_boot (STATUS_PATH))123 if (!file_modified_after_boot (STATUS_PATH))
107 return;124 return FALSE;
108125
109 get_event_from_file (STATUS_PATH, &event, &description);126 state = livepatch_get_state (&error);
110127 if (state == NULL) {
111 if (g_strcmp0 (event, "applied") == 0) {128 g_warning ("Failed to get Livepatch state: %s", error->message);
112 g_autofree gchar *body = NULL;129 return FALSE;
113 gchar *endptr;
114 gboolean is_overflow, conversion_failed;
115
116 errno = 0;
117 guint64 num_updates = g_ascii_strtoull (description, &endptr, 10);
118 is_overflow = (num_updates == G_MAXUINT64 && errno == ERANGE);
119 conversion_failed = (num_updates == 0 && description == endptr);
120
121 if (is_overflow || conversion_failed) {
122 g_warning ("Failed to parse the status file");
123 } else if (num_updates != 0) {
124 body = g_strdup_printf (
125 ngettext ("%" G_GUINT64_FORMAT " Livepatch update has been successfully applied.",
126 "%" G_GUINT64_FORMAT " Livepatch updates have been successfully applied.",
127 num_updates),
128 num_updates);
129
130 show_notification (_("Canonical Livepatch"), body, NULL);
131 }
132 }130 }
131
132 if (g_strcmp0(state, "applied") != 0)
133 return FALSE;
134
135 return show_notification ("Livepatch", _("An update has just been applied."), NULL);
133}136}
134137
135int138int
136main (int argc, char **argv)139main (int argc, char **argv)
137{140{
141 gtk_init (&argc, &argv);
138 init_notification ();142 init_notification ();
139 init_gettext ();143 init_gettext ();
140144
141 show_status_notification ();145 if (show_status_notification ())
146 gtk_main ();
142147
143 return EXIT_SUCCESS;148 return EXIT_SUCCESS;
144}149}
145150
=== modified file 'src/update-notifier.c'
--- src/update-notifier.c 2019-01-17 14:17:33 +0000
+++ src/update-notifier.c 2019-04-05 14:32:08 +0000
@@ -41,6 +41,7 @@
41#include <gio/gio.h>41#include <gio/gio.h>
4242
43#include "update-notifier.h"43#include "update-notifier.h"
44#include "livepatch-tray.h"
44#include "update.h"45#include "update.h"
45#include "hooks.h"46#include "hooks.h"
46#include "uevent.h"47#include "uevent.h"
@@ -475,6 +476,11 @@
475 trayapplet_create(un->crashreport, un, "apport");476 trayapplet_create(un->crashreport, un, "apport");
476 crashreport_tray_icon_init(un->crashreport);477 crashreport_tray_icon_init(un->crashreport);
477478
479 /* livepatch icon */
480 un->livepatch = g_new0(TrayApplet, 1);
481 trayapplet_create(un->livepatch, un, "livepatch");
482 livepatch_tray_icon_init(un->livepatch);
483
478 return FALSE; // for the tray_destroyed_cb484 return FALSE; // for the tray_destroyed_cb
479}485}
480486
481487
=== modified file 'src/update-notifier.h'
--- src/update-notifier.h 2018-06-27 08:00:27 +0000
+++ src/update-notifier.h 2019-04-05 14:32:08 +0000
@@ -37,6 +37,7 @@
37#define SETTINGS_KEY_END_SYSTEM_UIDS "end-system-uids"37#define SETTINGS_KEY_END_SYSTEM_UIDS "end-system-uids"
38#define SETTINGS_KEY_AUTO_LAUNCH_INTERVAL "regular-auto-launch-interval"38#define SETTINGS_KEY_AUTO_LAUNCH_INTERVAL "regular-auto-launch-interval"
39#define SETTINGS_KEY_LAST_RELEASE_CHECK "release-check-time"39#define SETTINGS_KEY_LAST_RELEASE_CHECK "release-check-time"
40#define SETTINGS_KEY_SHOW_LIVEPATCH_ICON "show-livepatch-status-icon"
4041
41#define SETTINGS_UM_SCHEMA "com.ubuntu.update-manager"42#define SETTINGS_UM_SCHEMA "com.ubuntu.update-manager"
42#define SETTINGS_UM_KEY_LAST_LAUNCH "launch-time"43#define SETTINGS_UM_KEY_LAST_LAUNCH "launch-time"
@@ -102,6 +103,7 @@
102 TrayApplet *update;103 TrayApplet *update;
103 TrayApplet *hook;104 TrayApplet *hook;
104 TrayApplet *crashreport;105 TrayApplet *crashreport;
106 TrayApplet *livepatch;
105107
106 guint update_finished_timer;108 guint update_finished_timer;
107109

Subscribers

People subscribed via source and target branches