Merge lp:~azzar1/update-notifier/ubuntu-bionic-livepatch into lp:~ubuntu-core-dev/update-notifier/ubuntu-bionic
- ubuntu-bionic-livepatch
- Merge into ubuntu-bionic
Proposed by
Andrea Azzarone
Status: | Merged | ||||||||
---|---|---|---|---|---|---|---|---|---|
Approved by: | Sebastien Bacher | ||||||||
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 | ||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Sebastien Bacher | Approve | ||
Review via email: mp+365597@code.launchpad.net |
Commit message
Cherry-pick changes to Livepatch from Disco.
Description of the change
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'configure.ac' |
2 | --- configure.ac 2013-02-01 00:51:16 +0000 |
3 | +++ configure.ac 2019-04-05 14:32:08 +0000 |
4 | @@ -13,7 +13,7 @@ |
5 | GNOME_COMMON_INIT |
6 | GLIB_GSETTINGS |
7 | |
8 | -pkg_modules="glib-2.0 >= 2.34 gtk+-3.0 libnotify gio-2.0 >= 2.26 x11" |
9 | +pkg_modules="glib-2.0 >= 2.34 gtk+-3.0 libnotify gio-2.0 >= 2.26 x11 json-glib-1.0" |
10 | |
11 | PKG_CHECK_EXISTS(gudev-1.0, [ HAVE_GUDEV=1 ]) |
12 | if test "x$HAVE_GUDEV" != "x"; then |
13 | @@ -100,6 +100,7 @@ |
14 | pixmaps/22x22/Makefile |
15 | pixmaps/24x24/Makefile |
16 | pixmaps/48x48/Makefile |
17 | +pixmaps/scalable/Makefile |
18 | po/Makefile.in |
19 | ]) |
20 | |
21 | |
22 | === modified file 'data/com.ubuntu.update-notifier.gschema.xml.in' |
23 | --- data/com.ubuntu.update-notifier.gschema.xml.in 2013-06-05 17:31:47 +0000 |
24 | +++ data/com.ubuntu.update-notifier.gschema.xml.in 2019-04-05 14:32:08 +0000 |
25 | @@ -25,5 +25,10 @@ |
26 | <summary>Time of last release check</summary> |
27 | <description>The last time update-notifier checked for a new release. The format is seconds since UNIX epoch.</description> |
28 | </key> |
29 | + <key name="show-livepatch-status-icon" type="b"> |
30 | + <default>true</default> |
31 | + <summary>Show Livepatch status icon</summary> |
32 | + <description>Enable or diable the Livepatch status icon in the systray.</description> |
33 | + </key> |
34 | </schema> |
35 | </schemalist> |
36 | |
37 | === modified file 'debian/changelog' |
38 | --- debian/changelog 2019-01-17 14:17:50 +0000 |
39 | +++ debian/changelog 2019-04-05 14:32:08 +0000 |
40 | @@ -1,3 +1,10 @@ |
41 | +update-notifier (3.192.1.6) UNRELEASED; urgency=medium |
42 | + |
43 | + * Add a Livepatch indicator in the system tray. (LP: #1820259) |
44 | + * Add a "Settings..." button to the Livepatch notification. (LP: #1823351) |
45 | + |
46 | + -- Andrea Azzarone <andrea.azzarone@canonical.com> Fri, 05 Apr 2019 15:20:05 +0100 |
47 | + |
48 | update-notifier (3.192.1.5) bionic; urgency=medium |
49 | |
50 | * [ Andrea Azzarone ] |
51 | |
52 | === modified file 'debian/control' |
53 | --- debian/control 2018-04-09 11:35:03 +0000 |
54 | +++ debian/control 2019-04-05 14:32:08 +0000 |
55 | @@ -6,6 +6,7 @@ |
56 | dh-python, |
57 | libgtk-3-dev, |
58 | libglib2.0-dev (>= 2.34), |
59 | + libjson-glib-dev, |
60 | intltool, |
61 | libnotify-dev (>= 0.7), |
62 | libgudev-1.0-dev, |
63 | |
64 | === modified file 'debian/update-notifier.install' |
65 | --- debian/update-notifier.install 2017-09-29 14:37:12 +0000 |
66 | +++ debian/update-notifier.install 2019-04-05 14:32:08 +0000 |
67 | @@ -4,6 +4,7 @@ |
68 | usr/share/icons/hicolor/22x22/ |
69 | usr/share/icons/hicolor/24x24/ |
70 | usr/share/icons/hicolor/48x48/ |
71 | +usr/share/icons/hicolor/scalable/ |
72 | usr/share/glib-2.0/schemas/com.ubuntu.update-notifier.gschema.xml |
73 | usr/bin/update-notifier |
74 | usr/bin/distro-cd-updater usr/lib/update-notifier/ |
75 | |
76 | === modified file 'pixmaps/Makefile.am' |
77 | --- pixmaps/Makefile.am 2007-11-16 09:44:48 +0000 |
78 | +++ pixmaps/Makefile.am 2019-04-05 14:32:08 +0000 |
79 | @@ -1,1 +1,1 @@ |
80 | -SUBDIRS = 16x16 22x22 24x24 48x48 |
81 | +SUBDIRS = 16x16 22x22 24x24 48x48 scalable |
82 | |
83 | === added directory 'pixmaps/scalable' |
84 | === added file 'pixmaps/scalable/Makefile.am' |
85 | --- pixmaps/scalable/Makefile.am 1970-01-01 00:00:00 +0000 |
86 | +++ pixmaps/scalable/Makefile.am 2019-04-05 14:32:08 +0000 |
87 | @@ -0,0 +1,5 @@ |
88 | + |
89 | +appicondir = $(datadir)/icons/hicolor/scalable/apps |
90 | +appicon_DATA = *.svg |
91 | + |
92 | +EXTRA_DIST = $(appicon_DATA) |
93 | |
94 | === added file 'pixmaps/scalable/livepatch-off.svg' |
95 | --- pixmaps/scalable/livepatch-off.svg 1970-01-01 00:00:00 +0000 |
96 | +++ pixmaps/scalable/livepatch-off.svg 2019-04-05 14:32:08 +0000 |
97 | @@ -0,0 +1,1 @@ |
98 | +<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> |
99 | \ No newline at end of file |
100 | |
101 | === added file 'pixmaps/scalable/livepatch-on.svg' |
102 | --- pixmaps/scalable/livepatch-on.svg 1970-01-01 00:00:00 +0000 |
103 | +++ pixmaps/scalable/livepatch-on.svg 2019-04-05 14:32:08 +0000 |
104 | @@ -0,0 +1,1 @@ |
105 | +<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> |
106 | \ No newline at end of file |
107 | |
108 | === added file 'pixmaps/scalable/livepatch-warning.svg' |
109 | --- pixmaps/scalable/livepatch-warning.svg 1970-01-01 00:00:00 +0000 |
110 | +++ pixmaps/scalable/livepatch-warning.svg 2019-04-05 14:32:08 +0000 |
111 | @@ -0,0 +1,1 @@ |
112 | +<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> |
113 | \ No newline at end of file |
114 | |
115 | === modified file 'po/POTFILES.in' |
116 | --- po/POTFILES.in 2017-08-21 17:30:53 +0000 |
117 | +++ po/POTFILES.in 2019-04-05 14:32:08 +0000 |
118 | @@ -11,6 +11,7 @@ |
119 | src/cdroms.c |
120 | src/hooks.c |
121 | src/livepatch.c |
122 | +src/livepatch-tray.c |
123 | src/update.c |
124 | src/update-notifier.c |
125 | [type: gettext/glade]ui/hooks-dialog.ui |
126 | |
127 | === modified file 'src/Makefile.am' |
128 | --- src/Makefile.am 2018-04-20 18:02:03 +0000 |
129 | +++ src/Makefile.am 2019-04-05 14:32:08 +0000 |
130 | @@ -23,6 +23,10 @@ |
131 | hooks.c\ |
132 | crash.c\ |
133 | crash.h\ |
134 | + livepatch-tray.c\ |
135 | + livepatch-tray.h\ |
136 | + livepatch-utils.c\ |
137 | + livepatch-utils.h\ |
138 | update.c\ |
139 | update.h\ |
140 | release.c\ |
141 | @@ -41,7 +45,9 @@ |
142 | system_crash_notification_SOURCES = system-crash.c\ |
143 | system-crash.h |
144 | |
145 | -livepatch_notification_SOURCES = livepatch.c |
146 | +livepatch_notification_SOURCES = livepatch.c\ |
147 | + livepatch-utils.c\ |
148 | + livepatch-utils.h |
149 | |
150 | update_notifier_LDADD = $(PACKAGE_LIBS) $(INTLLIBS) $(APP_INDICATOR_LIBS) |
151 | update_notifier_LDFLAGS = -export-dynamic |
152 | |
153 | === added file 'src/livepatch-tray.c' |
154 | --- src/livepatch-tray.c 1970-01-01 00:00:00 +0000 |
155 | +++ src/livepatch-tray.c 2019-04-05 14:32:08 +0000 |
156 | @@ -0,0 +1,289 @@ |
157 | +/* livepatch-tray.c |
158 | + * Copyright (C) 2019 Canonical Ltd |
159 | + * |
160 | + * This library is free software; you can redistribute it and/or |
161 | + * modify it under the terms of the GNU Lesser General Public |
162 | + * License as published by the Free Software Foundation; either |
163 | + * version 2 of the License, or (at your option) any later version. |
164 | + * |
165 | + * This library is distributed in the hope that it will be useful, |
166 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
167 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
168 | + * Lesser General Public License for more details. |
169 | + * |
170 | + * You should have received a copy of the GNU Lesser General Public |
171 | + * License along with this library; if not, write to the |
172 | + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor |
173 | + * Boston, MA 02110-1301 USA. |
174 | + */ |
175 | +#include <gio/gdesktopappinfo.h> |
176 | +#include <glib.h> |
177 | + |
178 | +#include "update-notifier.h" |
179 | +#include "livepatch-tray.h" |
180 | +#include "livepatch-utils.h" |
181 | +#include "trayappletui.h" |
182 | + |
183 | +#define LIVEPATCH_COMMON_DIRECTORY "/var/snap/canonical-livepatch/common" |
184 | +#define LIVEPATCH_SNAP_DIRECTORY "/snap/canonical-livepatch" |
185 | +#define TIMEOUT_SECONDS_DELAY 2 |
186 | + |
187 | +typedef struct _LivepatchTrayAppletPriv LivepatchTrayAppletPriv; |
188 | +struct _LivepatchTrayAppletPriv |
189 | +{ |
190 | + GtkWidget *menuitem_enabled; |
191 | + GtkWidget *menuitem_desc; |
192 | + |
193 | + GFileMonitor *common_dir_monitor; |
194 | + GFileMonitor *snap_dir_monitor; |
195 | + |
196 | + guint timeout_id; |
197 | +}; |
198 | + |
199 | +static void |
200 | +on_settings_menuitem_activated(GObject *self, gpointer user_data) |
201 | +{ |
202 | + g_autoptr(GDesktopAppInfo) info = NULL; |
203 | + g_autoptr(GdkAppLaunchContext) context = NULL; |
204 | + g_autoptr(GError) error = NULL; |
205 | + |
206 | + info = g_desktop_app_info_new(LIVEPATCH_DESKTOP_FILE); |
207 | + if (info == NULL) |
208 | + { |
209 | + g_warning("Could not find application '%s'", LIVEPATCH_DESKTOP_FILE); |
210 | + return; |
211 | + } |
212 | + |
213 | + context = gdk_display_get_app_launch_context(gdk_display_get_default()); |
214 | + if (!g_app_info_launch(G_APP_INFO(info), NULL, |
215 | + G_APP_LAUNCH_CONTEXT(context), &error)) |
216 | + { |
217 | + g_warning("Could not launch application '%s'", LIVEPATCH_DESKTOP_FILE); |
218 | + } |
219 | +} |
220 | + |
221 | + |
222 | +static void |
223 | +livepatch_trayicon_create_menu(TrayApplet *ta) |
224 | +{ |
225 | + LivepatchTrayAppletPriv *priv = (LivepatchTrayAppletPriv *) ta->user_data; |
226 | + GtkWidget *menuitem; |
227 | + |
228 | + ta->menu = gtk_menu_new(); |
229 | + |
230 | + priv->menuitem_enabled = gtk_menu_item_new(); |
231 | + gtk_widget_set_sensitive(priv->menuitem_enabled, FALSE); |
232 | + gtk_menu_shell_append(GTK_MENU_SHELL(ta->menu), priv->menuitem_enabled); |
233 | + |
234 | + priv->menuitem_desc = gtk_menu_item_new(); |
235 | + gtk_widget_set_sensitive(priv->menuitem_desc, FALSE); |
236 | + gtk_menu_shell_append(GTK_MENU_SHELL(ta->menu), priv->menuitem_desc); |
237 | + |
238 | + menuitem = gtk_separator_menu_item_new(); |
239 | + gtk_menu_shell_append(GTK_MENU_SHELL(ta->menu), menuitem); |
240 | + |
241 | + menuitem = gtk_menu_item_new_with_label(_("Livepatch Settings…")); |
242 | + gtk_menu_shell_append(GTK_MENU_SHELL(ta->menu), menuitem); |
243 | + g_signal_connect(G_OBJECT(menuitem), "activate", |
244 | + G_CALLBACK(on_settings_menuitem_activated), ta); |
245 | + |
246 | + gtk_widget_show_all(ta->menu); |
247 | +} |
248 | + |
249 | +static gboolean |
250 | +check_livepatch(TrayApplet *ta) |
251 | +{ |
252 | + LivepatchTrayAppletPriv *priv = (LivepatchTrayAppletPriv *) ta->user_data; |
253 | + gboolean show_status_icon; |
254 | + g_autofree gchar *check_state = NULL; |
255 | + g_autofree gchar *state = NULL; |
256 | + g_autoptr(GError) error = NULL; |
257 | + |
258 | + show_status_icon = g_settings_get_boolean(ta->un->settings, |
259 | + SETTINGS_KEY_SHOW_LIVEPATCH_ICON); |
260 | + |
261 | + if (!show_status_icon || !livepatch_is_supported()) |
262 | + { |
263 | + tray_applet_ui_set_visible(ta, FALSE); |
264 | + |
265 | + priv->timeout_id = 0; |
266 | + return G_SOURCE_REMOVE; |
267 | + } |
268 | + |
269 | + tray_applet_ui_set_visible(ta, TRUE); |
270 | + |
271 | + if (!livepatch_is_running()) |
272 | + { |
273 | + tray_applet_ui_set_icon(ta, "livepatch-off"); |
274 | + gtk_menu_item_set_label(GTK_MENU_ITEM(priv->menuitem_enabled), |
275 | + _("Livepatch is off")); |
276 | + gtk_widget_set_visible(priv->menuitem_desc, FALSE); |
277 | + |
278 | + priv->timeout_id = 0; |
279 | + return G_SOURCE_REMOVE; |
280 | + } |
281 | + |
282 | + gtk_menu_item_set_label(GTK_MENU_ITEM(priv->menuitem_enabled), |
283 | + _("Livepatch is on")); |
284 | + |
285 | + check_state = livepatch_get_check_state(&error); |
286 | + if (check_state == NULL) |
287 | + g_warning("Cannot get Livepatch check-state: %s", error->message); |
288 | + |
289 | + state = livepatch_get_state(&error); |
290 | + if (state == NULL) |
291 | + g_warning("Cannot get Livepatch state: %s", error->message); |
292 | + |
293 | + if (!g_strcmp0(check_state, "checked") && |
294 | + (!g_strcmp0(state, "applied") || !g_strcmp0(state, "nothing-to-apply"))) |
295 | + { |
296 | + gssize num_fixes; |
297 | + g_autofree gchar *label = NULL; |
298 | + |
299 | + tray_applet_ui_set_icon(ta, "livepatch-on"); |
300 | + |
301 | + num_fixes = livepatch_get_num_fixes(&error); |
302 | + if (num_fixes == -1) |
303 | + { |
304 | + g_warning("Cannot get applied Livepatch fixes: %s", error->message); |
305 | + num_fixes = 0; |
306 | + } |
307 | + |
308 | + gtk_widget_set_visible(priv->menuitem_desc, TRUE); |
309 | + |
310 | + if (num_fixes == 0) |
311 | + label = g_strdup(_("No current updates")); |
312 | + else |
313 | + label = g_strdup_printf(ngettext("%" G_GSSIZE_FORMAT " current update", |
314 | + "%" G_GSSIZE_FORMAT " current updates", |
315 | + num_fixes), num_fixes); |
316 | + |
317 | + gtk_menu_item_set_label(GTK_MENU_ITEM(priv->menuitem_desc), label); |
318 | + } |
319 | + else if (!g_strcmp0(check_state, "needs-check") || |
320 | + !g_strcmp0(state, "unapplied")) |
321 | + { |
322 | + /* Check livepatch status again */ |
323 | + return G_SOURCE_CONTINUE; |
324 | + } |
325 | + else |
326 | + { |
327 | + tray_applet_ui_set_icon(ta, "livepatch-warning"); |
328 | + |
329 | + if (check_state == NULL || !g_strcmp0(check_state, "check-failed")) |
330 | + { |
331 | + gtk_menu_item_set_label(GTK_MENU_ITEM(priv->menuitem_desc), |
332 | + _("An error occured when checking for Livepatch updates.")); |
333 | + } |
334 | + else |
335 | + { |
336 | + gtk_menu_item_set_label(GTK_MENU_ITEM(priv->menuitem_desc), |
337 | + _("An error occured when applying Livepatch updates.")); |
338 | + } |
339 | + } |
340 | + |
341 | + priv->timeout_id = 0; |
342 | + return G_SOURCE_REMOVE; |
343 | +} |
344 | + |
345 | +static void |
346 | +gsettings_visibility_changed_cb(GSettings *settings, |
347 | + gchar *key, |
348 | + gpointer user_data) |
349 | +{ |
350 | + TrayApplet *ta = (TrayApplet *) user_data; |
351 | + LivepatchTrayAppletPriv *priv = (LivepatchTrayAppletPriv *) ta->user_data; |
352 | + |
353 | + if (priv->timeout_id <= 0) |
354 | + priv->timeout_id = g_timeout_add_seconds(0, (GSourceFunc) check_livepatch, ta); |
355 | +} |
356 | + |
357 | +static void |
358 | +livepatch_directory_changed_cb(GFileMonitor *monitor, |
359 | + GFile *file, |
360 | + GFile *other_file, |
361 | + GFileMonitorEvent event_type, |
362 | + gpointer user_data) |
363 | +{ |
364 | + TrayApplet *ta = (TrayApplet *) user_data; |
365 | + LivepatchTrayAppletPriv *priv = (LivepatchTrayAppletPriv *) ta->user_data; |
366 | + |
367 | + if (priv->timeout_id <= 0) |
368 | + priv->timeout_id = g_timeout_add_seconds(TIMEOUT_SECONDS_DELAY, |
369 | + (GSourceFunc) check_livepatch, ta); |
370 | +} |
371 | + |
372 | +void |
373 | +livepatch_tray_icon_init(TrayApplet *ta) |
374 | +{ |
375 | + g_autoptr(GFile) livepatch_common_dir = NULL; |
376 | + g_autoptr(GFile) livepatch_snap_dir = NULL; |
377 | + g_autoptr(GFileMonitor) common_dir_monitor = NULL; |
378 | + g_autoptr(GFileMonitor) snap_dir_monitor = NULL; |
379 | + g_autoptr(GError) error = NULL; |
380 | + LivepatchTrayAppletPriv *priv; |
381 | + |
382 | + if (!livepatch_is_supported()) |
383 | + return; |
384 | + |
385 | + if (!livepatch_has_settings_ui()) |
386 | + { |
387 | + g_warning("There is no graphical application installed to manage " |
388 | + "Livepatch. The livepatch status icon will not be displayed."); |
389 | + return; |
390 | + } |
391 | + |
392 | + /* Monitor the directory LIVEPATCH_COMMON_DIRECTORY for changes to update |
393 | + the status of the indicator. */ |
394 | + livepatch_common_dir = g_file_new_for_path(LIVEPATCH_COMMON_DIRECTORY); |
395 | + common_dir_monitor = g_file_monitor_directory(livepatch_common_dir, |
396 | + G_FILE_MONITOR_NONE, |
397 | + NULL, &error); |
398 | + if (common_dir_monitor == NULL) |
399 | + { |
400 | + g_warning("Couldn't create directory monitor on %s. Error: %s\n", |
401 | + LIVEPATCH_COMMON_DIRECTORY, error->message); |
402 | + return; |
403 | + } |
404 | + |
405 | + /* We also need to monitor the directory LIVEPATCH_SNAP_DIRECTORY in order to |
406 | + detect if the snap was enabled/disabled. This consider the case |
407 | + canonical-livepatch is enabled but the snap is disabled. */ |
408 | + livepatch_snap_dir = g_file_new_for_path(LIVEPATCH_SNAP_DIRECTORY); |
409 | + snap_dir_monitor = g_file_monitor_directory(livepatch_snap_dir, |
410 | + G_FILE_MONITOR_WATCH_MOUNTS, |
411 | + NULL, &error); |
412 | + if (snap_dir_monitor == NULL) |
413 | + { |
414 | + g_warning("Couldn't create directory monitor on %s. Error: %s\n", |
415 | + LIVEPATCH_SNAP_DIRECTORY, error->message); |
416 | + return; |
417 | + } |
418 | + |
419 | + priv = g_new0(LivepatchTrayAppletPriv, 1); |
420 | + priv->common_dir_monitor = g_steal_pointer(&common_dir_monitor); |
421 | + priv->snap_dir_monitor = g_steal_pointer(&snap_dir_monitor); |
422 | + ta->user_data = priv; |
423 | + |
424 | + tray_applet_ui_ensure(ta); |
425 | + |
426 | + /* Menu initialization */ |
427 | + livepatch_trayicon_create_menu(ta); |
428 | + tray_applet_ui_set_menu(ta, ta->menu); |
429 | + |
430 | + g_signal_connect(ta->un->settings, |
431 | + "changed::"SETTINGS_KEY_SHOW_LIVEPATCH_ICON, |
432 | + G_CALLBACK(gsettings_visibility_changed_cb), ta); |
433 | + g_signal_connect(priv->common_dir_monitor, |
434 | + "changed", |
435 | + G_CALLBACK(livepatch_directory_changed_cb), ta); |
436 | + g_signal_connect(priv->snap_dir_monitor, |
437 | + "changed", |
438 | + G_CALLBACK(livepatch_directory_changed_cb), ta); |
439 | + |
440 | + /* We always run check_livepatch in a timeout beacuse this allows us to easily |
441 | + check again the status of livepatch if it is in a transistion state (e.g. |
442 | + it is downloading the patches or applying them, etc.) */ |
443 | + priv->timeout_id = g_timeout_add_seconds(TIMEOUT_SECONDS_DELAY, |
444 | + (GSourceFunc) check_livepatch, ta); |
445 | +} |
446 | |
447 | === added file 'src/livepatch-tray.h' |
448 | --- src/livepatch-tray.h 1970-01-01 00:00:00 +0000 |
449 | +++ src/livepatch-tray.h 2019-04-05 14:32:08 +0000 |
450 | @@ -0,0 +1,22 @@ |
451 | +/* livepatch-tray.h |
452 | + * Copyright (C) 2019 Canonical Ltd |
453 | + * |
454 | + * This library is free software; you can redistribute it and/or |
455 | + * modify it under the terms of the GNU Lesser General Public |
456 | + * License as published by the Free Software Foundation; either |
457 | + * version 2 of the License, or (at your option) any later version. |
458 | + * |
459 | + * This library is distributed in the hope that it will be useful, |
460 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
461 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
462 | + * Lesser General Public License for more details. |
463 | + * |
464 | + * You should have received a copy of the GNU Lesser General Public |
465 | + * License along with this library; if not, write to the |
466 | + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor |
467 | + * Boston, MA 02110-1301 USA. |
468 | + */ |
469 | + |
470 | +#pragma once |
471 | + |
472 | +void livepatch_tray_icon_init(TrayApplet *ta); |
473 | |
474 | === added file 'src/livepatch-utils.c' |
475 | --- src/livepatch-utils.c 1970-01-01 00:00:00 +0000 |
476 | +++ src/livepatch-utils.c 2019-04-05 14:32:08 +0000 |
477 | @@ -0,0 +1,242 @@ |
478 | +/* livepatch-utils.c |
479 | + * Copyright (C) 2019 Canonical Ltd |
480 | + * |
481 | + * This library is free software; you can redistribute it and/or |
482 | + * modify it under the terms of the GNU Lesser General Public |
483 | + * License as published by the Free Software Foundation; either |
484 | + * version 2 of the License, or (at your option) any later version. |
485 | + * |
486 | + * This library is distributed in the hope that it will be useful, |
487 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
488 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
489 | + * Lesser General Public License for more details. |
490 | + * |
491 | + * You should have received a copy of the GNU Lesser General Public |
492 | + * License along with this library; if not, write to the |
493 | + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor |
494 | + * Boston, MA 02110-1301 USA. |
495 | + */ |
496 | + |
497 | +#include "livepatch-utils.h" |
498 | + |
499 | +#include <gio/gdesktopappinfo.h> |
500 | +#include <glib.h> |
501 | +#include <json-glib/json-glib.h> |
502 | + |
503 | +#define OS_RELEASE_PATH "/etc/os-release" |
504 | + |
505 | +GQuark |
506 | +livepatch_error_quark(void) |
507 | +{ |
508 | + static GQuark q = 0; |
509 | + |
510 | + if (G_UNLIKELY(!q)) |
511 | + q = g_quark_from_static_string("livepatch-error-quark"); |
512 | + |
513 | + return q; |
514 | +} |
515 | + |
516 | +static GKeyFile * |
517 | +parse_osrelease() |
518 | +{ |
519 | + g_autoptr(GKeyFile) keys = NULL; |
520 | + g_autofree gchar *content = NULL; |
521 | + g_autofree gchar *content_with_group = NULL; |
522 | + const gchar *group = "[os-release]\n"; |
523 | + g_autoptr(GError) error = NULL; |
524 | + |
525 | + if (!g_file_get_contents(OS_RELEASE_PATH, &content, NULL, &error)) |
526 | + { |
527 | + g_warning("Failed to read '%s', error: %s", |
528 | + OS_RELEASE_PATH, error->message); |
529 | + return NULL; |
530 | + } |
531 | + |
532 | + content_with_group = g_strdup_printf("%s%s", group, content); |
533 | + |
534 | + keys = g_key_file_new(); |
535 | + if (!g_key_file_load_from_data(keys, content_with_group, -1, |
536 | + G_KEY_FILE_NONE, &error)) |
537 | + { |
538 | + g_warning("Failed to parse /etc/os-release: %s", error->message); |
539 | + return NULL; |
540 | + } |
541 | + |
542 | + return g_steal_pointer(&keys); |
543 | +} |
544 | + |
545 | +static JsonNode * |
546 | +livepatch_get_status(GError **error) |
547 | +{ |
548 | + g_autofree gchar *std_output = NULL; |
549 | + g_autofree gchar *std_error = NULL; |
550 | + g_autoptr(JsonParser) json_parser = NULL; |
551 | + JsonNode *root_node; |
552 | + |
553 | + if (!g_spawn_command_line_sync("canonical-livepatch status --format=json", |
554 | + &std_output, &std_error, NULL, error)) |
555 | + return NULL; |
556 | + |
557 | + if (std_output == NULL || strlen(std_output) == 0) |
558 | + { |
559 | + if (std_error == NULL || strlen(std_error) == 0) |
560 | + { |
561 | + g_set_error(error, |
562 | + LIVEPATCH_ERROR, LIVEPATCH_ERROR_CMD_FAILED, |
563 | + "canonical-livepatch returned an empty status."); |
564 | + } |
565 | + else |
566 | + { |
567 | + g_set_error(error, |
568 | + LIVEPATCH_ERROR, LIVEPATCH_ERROR_CMD_FAILED, |
569 | + "canonical-livepatch status returned an error: %s", |
570 | + std_error); |
571 | + } |
572 | + |
573 | + return NULL; |
574 | + } |
575 | + |
576 | + json_parser = json_parser_new(); |
577 | + if (!json_parser_load_from_data(json_parser, std_output, -1, error)) |
578 | + return NULL; |
579 | + |
580 | + root_node = json_parser_get_root(json_parser); |
581 | + if (root_node == NULL) |
582 | + { |
583 | + g_set_error(error, |
584 | + LIVEPATCH_ERROR, LIVEPATCH_ERROR_CMD_FAILED, |
585 | + "The output of canonical-livepatch has no json root node."); |
586 | + return NULL; |
587 | + } |
588 | + |
589 | + return json_node_copy(root_node); |
590 | +} |
591 | + |
592 | +static JsonNode * |
593 | +livepatch_get_status_node(const gchar *expr, GError **error) |
594 | +{ |
595 | + g_autoptr(JsonNode) root = NULL; |
596 | + |
597 | + root = livepatch_get_status(error); |
598 | + if (root == NULL) |
599 | + return NULL; |
600 | + |
601 | + return json_path_query(expr, root, error); |
602 | +} |
603 | + |
604 | +gboolean |
605 | +livepatch_has_settings_ui() |
606 | +{ |
607 | + g_autoptr(GDesktopAppInfo) info = NULL; |
608 | + |
609 | + info = g_desktop_app_info_new(LIVEPATCH_DESKTOP_FILE); |
610 | + return info != NULL; |
611 | +} |
612 | + |
613 | +gboolean |
614 | +livepatch_is_supported() |
615 | +{ |
616 | + g_autoptr(GKeyFile) file = NULL; |
617 | + g_autofree gchar *version = NULL; |
618 | + g_autoptr(GError) error = NULL; |
619 | + |
620 | + file = parse_osrelease(); |
621 | + if (file == NULL) |
622 | + return FALSE; |
623 | + |
624 | + version = g_key_file_get_string(file, "os-release", "VERSION", &error); |
625 | + if (version == NULL) |
626 | + { |
627 | + g_warning("Failed to get the version from the file %s: %s", |
628 | + OS_RELEASE_PATH, error->message); |
629 | + return FALSE; |
630 | + } |
631 | + |
632 | + return g_strstr_len(version, -1, "LTS") != NULL; |
633 | +} |
634 | + |
635 | +gboolean |
636 | +livepatch_is_running() |
637 | +{ |
638 | + g_autoptr(JsonNode) result_node = NULL; |
639 | + JsonNode *running_node = NULL; |
640 | + JsonArray *result_array; |
641 | + |
642 | + result_node = livepatch_get_status_node("$.Status[0].Running", NULL); |
643 | + if (result_node == NULL) |
644 | + return FALSE; |
645 | + |
646 | + result_array = json_node_get_array(result_node); |
647 | + |
648 | + if (json_array_get_length(result_array) == 0) |
649 | + return FALSE; |
650 | + |
651 | + running_node = json_array_get_element(result_array, 0); |
652 | + return json_node_get_boolean(running_node); |
653 | +} |
654 | + |
655 | +gchar * |
656 | +livepatch_get_state(GError **error) |
657 | +{ |
658 | + g_autoptr(JsonNode) result_node = NULL; |
659 | + JsonNode *state_node = NULL; |
660 | + JsonArray *result_array; |
661 | + const gchar *expr = "$.Status[0].Livepatch.State"; |
662 | + |
663 | + result_node = livepatch_get_status_node(expr, error); |
664 | + if (result_node == NULL) |
665 | + return NULL; |
666 | + |
667 | + result_array = json_node_get_array(result_node); |
668 | + |
669 | + if (json_array_get_length(result_array) == 0) |
670 | + { |
671 | + g_set_error(error, |
672 | + LIVEPATCH_ERROR, LIVEPATCH_ERROR_NOMATCH, |
673 | + "No matches for: %s", expr); |
674 | + return NULL; |
675 | + } |
676 | + |
677 | + state_node = json_array_get_element(result_array, 0); |
678 | + return g_strdup(json_node_get_string(state_node)); |
679 | +} |
680 | + |
681 | +gchar * |
682 | +livepatch_get_check_state(GError **error) |
683 | +{ |
684 | + g_autoptr(JsonNode) result_node = NULL; |
685 | + JsonNode *state_node = NULL; |
686 | + JsonArray *result_array; |
687 | + const gchar *expr = "$.Status[0].Livepatch.CheckState"; |
688 | + |
689 | + result_node = livepatch_get_status_node(expr, error); |
690 | + if (result_node == NULL) |
691 | + return NULL; |
692 | + |
693 | + result_array = json_node_get_array(result_node); |
694 | + |
695 | + if (json_array_get_length(result_array) == 0) |
696 | + { |
697 | + g_set_error(error, |
698 | + LIVEPATCH_ERROR, LIVEPATCH_ERROR_NOMATCH, |
699 | + "No matches for: %s", expr); |
700 | + return NULL; |
701 | + } |
702 | + |
703 | + state_node = json_array_get_element(result_array, 0); |
704 | + return g_strdup(json_node_get_string(state_node)); |
705 | +} |
706 | + |
707 | +gssize |
708 | +livepatch_get_num_fixes(GError **error) |
709 | +{ |
710 | + g_autoptr(JsonNode) node = NULL; |
711 | + JsonArray *array; |
712 | + |
713 | + node = livepatch_get_status_node("$.Status[0].Livepatch.Fixes[*]", error); |
714 | + if (node == NULL) |
715 | + return -1; |
716 | + |
717 | + array = json_node_get_array(node); |
718 | + return json_array_get_length(array); |
719 | +} |
720 | |
721 | === added file 'src/livepatch-utils.h' |
722 | --- src/livepatch-utils.h 1970-01-01 00:00:00 +0000 |
723 | +++ src/livepatch-utils.h 2019-04-05 14:32:08 +0000 |
724 | @@ -0,0 +1,39 @@ |
725 | +/* livepatch-utils.h |
726 | + * Copyright (C) 2019 Canonical Ltd |
727 | + * |
728 | + * This library is free software; you can redistribute it and/or |
729 | + * modify it under the terms of the GNU Lesser General Public |
730 | + * License as published by the Free Software Foundation; either |
731 | + * version 2 of the License, or (at your option) any later version. |
732 | + * |
733 | + * This library is distributed in the hope that it will be useful, |
734 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
735 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
736 | + * Lesser General Public License for more details. |
737 | + * |
738 | + * You should have received a copy of the GNU Lesser General Public |
739 | + * License along with this library; if not, write to the |
740 | + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor |
741 | + * Boston, MA 02110-1301 USA. |
742 | + */ |
743 | + |
744 | +#pragma once |
745 | + |
746 | +#include <gio/gio.h> |
747 | + |
748 | +#define LIVEPATCH_ERROR livepatch_error_quark() |
749 | +#define LIVEPATCH_DESKTOP_FILE "software-properties-livepatch.desktop" |
750 | + |
751 | +typedef enum |
752 | +{ |
753 | + LIVEPATCH_ERROR_CMD_FAILED, |
754 | + LIVEPATCH_ERROR_NOMATCH |
755 | +} LivepatchError; |
756 | + |
757 | + |
758 | +gboolean livepatch_has_settings_ui (); |
759 | +gboolean livepatch_is_supported (); |
760 | +gboolean livepatch_is_running (); |
761 | +gchar* livepatch_get_state (GError **error); |
762 | +gchar* livepatch_get_check_state (GError **error); |
763 | +gssize livepatch_get_num_fixes (GError **error); |
764 | |
765 | === modified file 'src/livepatch.c' |
766 | --- src/livepatch.c 2018-11-12 15:58:56 +0000 |
767 | +++ src/livepatch.c 2019-04-05 14:32:08 +0000 |
768 | @@ -2,14 +2,14 @@ |
769 | #include "config.h" |
770 | #endif |
771 | |
772 | -#include <errno.h> |
773 | +#include <gio/gdesktopappinfo.h> |
774 | #include <glib.h> |
775 | #include <glib/gstdio.h> |
776 | #include <libnotify/notify.h> |
777 | -#include <stdlib.h> |
778 | #include <sys/sysinfo.h> |
779 | |
780 | #include "update-notifier.h" |
781 | +#include "livepatch-utils.h" |
782 | |
783 | #define STATUS_PATH "/var/snap/canonical-livepatch/current/status" |
784 | |
785 | @@ -29,37 +29,54 @@ |
786 | } |
787 | |
788 | static void |
789 | +notify_action_cb (NotifyNotification *notification, |
790 | + char *action, |
791 | + gpointer user_data) |
792 | +{ |
793 | + g_autoptr(GDesktopAppInfo) info = NULL; |
794 | + g_autoptr(GdkAppLaunchContext) context = NULL; |
795 | + g_autoptr(GError) error = NULL; |
796 | + |
797 | + info = g_desktop_app_info_new (LIVEPATCH_DESKTOP_FILE); |
798 | + if (!info) { |
799 | + g_warning ("Could not find application '%s'", LIVEPATCH_DESKTOP_FILE); |
800 | + return; |
801 | + } |
802 | + |
803 | + context = gdk_display_get_app_launch_context (gdk_display_get_default ()); |
804 | + if (!g_app_info_launch (G_APP_INFO (info), NULL, G_APP_LAUNCH_CONTEXT (context), &error)) { |
805 | + g_warning ("Could not launch application '%s'", LIVEPATCH_DESKTOP_FILE); |
806 | + } |
807 | +} |
808 | + |
809 | +static gboolean |
810 | show_notification (const char *summary, const char *body, const char *icon) |
811 | { |
812 | - NotifyNotification *n = notify_notification_new (summary, body, icon); |
813 | + NotifyNotification *n; |
814 | + g_autoptr(GError) error = NULL; |
815 | + |
816 | + n = notify_notification_new (summary, body, icon); |
817 | notify_notification_set_timeout (n, 60000); |
818 | - notify_notification_show (n, NULL); |
819 | - g_object_unref (n); |
820 | -} |
821 | - |
822 | -static void |
823 | -get_event_from_file (const char* filename, char **event, char **description) |
824 | -{ |
825 | - g_autofree gchar *content = NULL; |
826 | - |
827 | - g_return_if_fail (filename != NULL); |
828 | - g_return_if_fail (event != NULL); |
829 | - g_return_if_fail (description != NULL); |
830 | - |
831 | - *event = *description = NULL; |
832 | - |
833 | - g_file_get_contents (filename, &content, NULL, NULL); |
834 | - |
835 | - if (content) { |
836 | - gchar **strings = g_strsplit (content, " ", 2); |
837 | - |
838 | - if (g_strv_length (strings) > 0) |
839 | - *event = g_strdup (g_strstrip (strings[0])); |
840 | - if (g_strv_length (strings) > 1) |
841 | - *description = g_strdup (g_strstrip (strings[1])); |
842 | - |
843 | - g_strfreev (strings); |
844 | - } |
845 | + |
846 | + if (livepatch_has_settings_ui()) { |
847 | + notify_notification_add_action (n, "settings", _("Show Settings…"), |
848 | + notify_action_cb, NULL, NULL); |
849 | + notify_notification_add_action (n, "default", _("Show Settings…"), |
850 | + notify_action_cb, NULL, NULL); |
851 | + } else { |
852 | + g_warning ("There is no graphical application installed to manage " |
853 | + "Livepatch. The notification will not have a " |
854 | + "'Show Settings…' button."); |
855 | + } |
856 | + |
857 | + g_signal_connect (n, "closed", G_CALLBACK (gtk_main_quit), NULL); |
858 | + |
859 | + if (!notify_notification_show (n, &error)) { |
860 | + g_warning ("Could not show notification: '%s", error->message); |
861 | + return FALSE; |
862 | + } |
863 | + |
864 | + return TRUE; |
865 | } |
866 | |
867 | static long |
868 | @@ -94,51 +111,39 @@ |
869 | return difftime (status_stat.st_mtim.tv_sec, boot_timestamp) >= 0; |
870 | } |
871 | |
872 | -static void |
873 | +static gboolean |
874 | show_status_notification () |
875 | { |
876 | - g_autofree gchar *event = NULL; |
877 | - g_autofree gchar *description = NULL; |
878 | + g_autofree gchar *state = NULL; |
879 | + g_autoptr(GError) error = NULL; |
880 | |
881 | if (!g_file_test (STATUS_PATH, G_FILE_TEST_EXISTS)) |
882 | - return; |
883 | + return FALSE; |
884 | |
885 | if (!file_modified_after_boot (STATUS_PATH)) |
886 | - return; |
887 | - |
888 | - get_event_from_file (STATUS_PATH, &event, &description); |
889 | - |
890 | - if (g_strcmp0 (event, "applied") == 0) { |
891 | - g_autofree gchar *body = NULL; |
892 | - gchar *endptr; |
893 | - gboolean is_overflow, conversion_failed; |
894 | - |
895 | - errno = 0; |
896 | - guint64 num_updates = g_ascii_strtoull (description, &endptr, 10); |
897 | - is_overflow = (num_updates == G_MAXUINT64 && errno == ERANGE); |
898 | - conversion_failed = (num_updates == 0 && description == endptr); |
899 | - |
900 | - if (is_overflow || conversion_failed) { |
901 | - g_warning ("Failed to parse the status file"); |
902 | - } else if (num_updates != 0) { |
903 | - body = g_strdup_printf ( |
904 | - ngettext ("%" G_GUINT64_FORMAT " Livepatch update has been successfully applied.", |
905 | - "%" G_GUINT64_FORMAT " Livepatch updates have been successfully applied.", |
906 | - num_updates), |
907 | - num_updates); |
908 | - |
909 | - show_notification (_("Canonical Livepatch"), body, NULL); |
910 | - } |
911 | + return FALSE; |
912 | + |
913 | + state = livepatch_get_state (&error); |
914 | + if (state == NULL) { |
915 | + g_warning ("Failed to get Livepatch state: %s", error->message); |
916 | + return FALSE; |
917 | } |
918 | + |
919 | + if (g_strcmp0(state, "applied") != 0) |
920 | + return FALSE; |
921 | + |
922 | + return show_notification ("Livepatch", _("An update has just been applied."), NULL); |
923 | } |
924 | |
925 | int |
926 | main (int argc, char **argv) |
927 | { |
928 | + gtk_init (&argc, &argv); |
929 | init_notification (); |
930 | init_gettext (); |
931 | |
932 | - show_status_notification (); |
933 | + if (show_status_notification ()) |
934 | + gtk_main (); |
935 | |
936 | return EXIT_SUCCESS; |
937 | } |
938 | |
939 | === modified file 'src/update-notifier.c' |
940 | --- src/update-notifier.c 2019-01-17 14:17:33 +0000 |
941 | +++ src/update-notifier.c 2019-04-05 14:32:08 +0000 |
942 | @@ -41,6 +41,7 @@ |
943 | #include <gio/gio.h> |
944 | |
945 | #include "update-notifier.h" |
946 | +#include "livepatch-tray.h" |
947 | #include "update.h" |
948 | #include "hooks.h" |
949 | #include "uevent.h" |
950 | @@ -475,6 +476,11 @@ |
951 | trayapplet_create(un->crashreport, un, "apport"); |
952 | crashreport_tray_icon_init(un->crashreport); |
953 | |
954 | + /* livepatch icon */ |
955 | + un->livepatch = g_new0(TrayApplet, 1); |
956 | + trayapplet_create(un->livepatch, un, "livepatch"); |
957 | + livepatch_tray_icon_init(un->livepatch); |
958 | + |
959 | return FALSE; // for the tray_destroyed_cb |
960 | } |
961 | |
962 | |
963 | === modified file 'src/update-notifier.h' |
964 | --- src/update-notifier.h 2018-06-27 08:00:27 +0000 |
965 | +++ src/update-notifier.h 2019-04-05 14:32:08 +0000 |
966 | @@ -37,6 +37,7 @@ |
967 | #define SETTINGS_KEY_END_SYSTEM_UIDS "end-system-uids" |
968 | #define SETTINGS_KEY_AUTO_LAUNCH_INTERVAL "regular-auto-launch-interval" |
969 | #define SETTINGS_KEY_LAST_RELEASE_CHECK "release-check-time" |
970 | +#define SETTINGS_KEY_SHOW_LIVEPATCH_ICON "show-livepatch-status-icon" |
971 | |
972 | #define SETTINGS_UM_SCHEMA "com.ubuntu.update-manager" |
973 | #define SETTINGS_UM_KEY_LAST_LAUNCH "launch-time" |
974 | @@ -102,6 +103,7 @@ |
975 | TrayApplet *update; |
976 | TrayApplet *hook; |
977 | TrayApplet *crashreport; |
978 | + TrayApplet *livepatch; |
979 | |
980 | guint update_finished_timer; |
981 |
Thanks, code looks fine and work as expected!