1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
|
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gio/gdesktopappinfo.h>
#include <glib.h>
#include <glib/gstdio.h>
#include <libnotify/notify.h>
#include <sys/sysinfo.h>
#include "update-notifier.h"
#include "livepatch-utils.h"
#define STATUS_PATH "/var/snap/canonical-livepatch/current/status"
static void
init_notification ()
{
notify_init ("update-notifier");
}
static void
init_gettext ()
{
setlocale (LC_ALL, "");
bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
textdomain (GETTEXT_PACKAGE);
}
static void
notify_action_cb (NotifyNotification *notification,
char *action,
gpointer user_data)
{
g_autoptr(GDesktopAppInfo) info = NULL;
g_autoptr(GdkAppLaunchContext) context = NULL;
g_autoptr(GError) error = NULL;
info = g_desktop_app_info_new (LIVEPATCH_DESKTOP_FILE);
if (!info) {
g_warning ("Could not find application '%s'", LIVEPATCH_DESKTOP_FILE);
return;
}
context = gdk_display_get_app_launch_context (gdk_display_get_default ());
if (!g_app_info_launch (G_APP_INFO (info), NULL, G_APP_LAUNCH_CONTEXT (context), &error)) {
g_warning ("Could not launch application '%s'", LIVEPATCH_DESKTOP_FILE);
}
}
static gboolean
show_notification (const char *summary, const char *body, const char *icon)
{
NotifyNotification *n;
g_autoptr(GError) error = NULL;
n = notify_notification_new (summary, body, icon);
notify_notification_set_timeout (n, 60000);
if (livepatch_has_settings_ui()) {
notify_notification_add_action (n, "settings", _("Show Settings…"),
notify_action_cb, NULL, NULL);
notify_notification_add_action (n, "default", _("Show Settings…"),
notify_action_cb, NULL, NULL);
} else {
g_warning ("There is no graphical application installed to manage "
"Livepatch. The notification will not have a "
"'Show Settings…' button.");
}
g_signal_connect (n, "closed", G_CALLBACK (gtk_main_quit), NULL);
if (!notify_notification_show (n, &error)) {
g_warning ("Could not show notification: '%s", error->message);
return FALSE;
}
return TRUE;
}
static long
get_uptime ()
{
struct sysinfo info;
if (sysinfo (&info) == -1) {
g_critical ("Failed to get uptime: %m");
return -1;
}
return info.uptime;
}
static gboolean
file_modified_after_boot (const char* filename)
{
GStatBuf status_stat;
long uptime;
time_t boot_timestamp;
/* In case of error it's safer to assume that the status file has been
modified after boot in order to not miss the notification. */
if (g_stat (STATUS_PATH, &status_stat) == -1)
return TRUE;
if ((uptime = get_uptime ()) == -1)
return TRUE;
boot_timestamp = time (NULL) - (time_t) uptime;
return difftime (status_stat.st_mtim.tv_sec, boot_timestamp) >= 0;
}
static gboolean
show_status_notification ()
{
g_autofree gchar *state = NULL;
g_autoptr(GError) error = NULL;
if (!g_file_test (STATUS_PATH, G_FILE_TEST_EXISTS))
return FALSE;
if (!file_modified_after_boot (STATUS_PATH))
return FALSE;
state = livepatch_get_state (&error);
if (state == NULL) {
g_warning ("Failed to get Livepatch state: %s", error->message);
return FALSE;
}
if (g_strcmp0(state, "applied") != 0)
return FALSE;
return show_notification ("Livepatch", _("An update has just been applied."), NULL);
}
int
main (int argc, char **argv)
{
gtk_init (&argc, &argv);
init_notification ();
init_gettext ();
if (show_status_notification ())
gtk_main ();
return EXIT_SUCCESS;
}
|