2
* Copyright (C) 2004 Lukas Lipka <lukas@pmad.net>
3
* (C) 2004 Michael Vogt <mvo@debian.org>
4
* (C) 2004 Michiel Sikkes <michiel@eyesopened.nl>
5
* (C) 2004-2009 Canonical
7
* This library is free software; you can redistribute it and/or
8
* modify it under the terms of the GNU Lesser General Public
9
* License as published by the Free Software Foundation; either
10
* version 2 of the License, or (at your option) any later version.
12
* This library is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
* Lesser General Public License for more details.
17
* You should have received a copy of the GNU Lesser General Public
18
* License along with this library; if not, write to the
19
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor
20
* Boston, MA 02110-1301 USA.
28
#include <sys/types.h>
35
#include <glib/gstdio.h>
40
#include "update-notifier.h"
50
extern gboolean up_get_clipboard (void);
51
gboolean update_timer_finished(gpointer data);
53
// the time when we check for fam events
54
#define TIMEOUT_FAM 1000*5 /* 5 sec */
56
// the timeout (in msec) for apt-get update (changes in
57
// /var/lib/apt/lists{/partial})
58
#define TIMEOUT_APT_GET_UPDATE 1000*30 /* 30 sec */
60
// the timeout (in sec) when a further activity from dpkg/apt
61
// causes the applet to "ungray"
62
#define TIMEOUT_APT_RUN 600 /* 600 sec */
64
// force startup even if the user is not in the admin group
65
static gboolean FORCE_START=FALSE;
67
// force gksu for all menu actions
68
static gboolean FORCE_GKSU=FALSE;
70
// delay on startup (to make session startup faster)
71
static int STARTUP_DELAY=1;
73
// make the release upgrader check for a devel release
74
gboolean DEVEL_RELEASE=FALSE;
76
// force release check
77
static gboolean FORCE_RELEASE_CHECK=FALSE;
79
// global debug options
82
int INOTIFY_DEBUG = 0;
84
int RELEASE_DEBUG = 0;
88
static void debug_log_handler (const gchar *log_domain,
89
GLogLevelFlags log_level,
93
if (log_domain && strcmp(log_domain, "hooks") == 0) {
95
g_log_default_handler (log_domain, log_level, message, user_data);
97
else if (log_domain && strcmp(log_domain, "update") == 0) {
99
g_log_default_handler (log_domain, log_level, message, user_data);
101
else if (log_domain && strcmp(log_domain, "inotify") == 0) {
102
if (INOTIFY_DEBUG > 0)
103
g_log_default_handler (log_domain, log_level, message, user_data);
105
else if (log_domain && strcmp(log_domain, "uevent") == 0) {
106
if (UEVENT_DEBUG > 0)
107
g_log_default_handler (log_domain, log_level, message, user_data);
109
else if (log_domain && strcmp(log_domain, "release") == 0) {
110
if (RELEASE_DEBUG > 0)
111
g_log_default_handler (log_domain, log_level, message, user_data);
113
else if (MISC_DEBUG > 0)
114
g_log_default_handler (log_domain, log_level, message, user_data);
118
g_debug_inotify(const char *msg, ...)
122
g_logv("inotify",G_LOG_LEVEL_DEBUG, msg, va);
126
// launch async scripts one after another
127
static void start_next_script(GPid pid,
129
GSList *remaining_scripts_to_run)
131
if(remaining_scripts_to_run) {
132
gboolean ret = False;
133
gchar* full_path = remaining_scripts_to_run->data;
134
gchar *argv[] = { full_path, NULL };
136
g_debug_inotify ("executing: %s\n", full_path);
137
if (g_file_test(full_path, G_FILE_TEST_IS_EXECUTABLE ))
138
ret = g_spawn_async("/", argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, NULL);
139
remaining_scripts_to_run = g_slist_remove(remaining_scripts_to_run, full_path);
143
g_warning("script execution failed or not executable");
144
start_next_script(0, 0, remaining_scripts_to_run);
147
g_child_watch_add(pid, (GChildWatchFunc)start_next_script, remaining_scripts_to_run);
152
void invoke(const gchar *cmd, const gchar *desktop, gboolean with_gksu)
154
GdkAppLaunchContext *context;
156
GError *error = NULL;
157
static GtkWidget *w = NULL;
160
if(with_gksu || FORCE_GKSU) {
161
invoke_with_gksu(cmd, desktop, FALSE);
165
// fake window to get the current server time *urgh*
167
w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
168
gtk_widget_realize (w);
172
context = gdk_app_launch_context_new ();
173
guint32 timestamp = gdk_x11_get_server_time (gtk_widget_get_window (w));
174
appinfo = g_app_info_create_from_commandline(cmd,
176
G_APP_INFO_CREATE_NONE,
178
gdk_app_launch_context_set_timestamp (context, timestamp);
179
if (!g_app_info_launch (appinfo, NULL, (GAppLaunchContext*)context, &error))
180
g_warning ("Launching failed: %s\n", error->message);
181
g_object_unref (context);
182
g_object_unref (appinfo);
187
invoke_with_gksu(const gchar *cmd, const gchar *descr, gboolean whole_message)
189
g_debug("invoke_update_manager ()\n");
191
argv[0] = "/usr/bin/gksu";
192
argv[1] = whole_message ? "--message" : "--desktop";
193
argv[2] = (gchar*)descr;
194
argv[3] = (gchar*)cmd;
197
g_spawn_async (NULL, argv, NULL, 0, NULL, NULL, NULL, NULL);
203
trayapplet_create (TrayApplet *un, char *name)
205
g_debug("trayicon_create()\n");
208
un->tray_icon = gtk_status_icon_new_from_icon_name (name);
210
gtk_status_icon_set_visible (un->tray_icon, FALSE);
217
the following files change:
221
- /var/lib/update-notifier/dpkg-run-stamp
223
- /var/lib/apt/lists/lock
224
- /var/lib/apt/lists/ *
226
- /var/lib/apt/periodic/update-success-stamp
229
monitor_cb(GFileMonitor *handle,
231
GFile *other_monitor_f,
232
GFileMonitorEvent event_type,
235
UpgradeNotifier *un = (UpgradeNotifier*)user_data;
237
gchar *info_uri = g_file_get_path(monitor_f);
239
g_debug_inotify("info_uri: %s\n", info_uri);
240
g_debug_inotify("event_type: %i\n",event_type);
243
// we ignore lock file events because we can only get
244
// when a lock was taken, but not when it was removed
245
if(g_str_has_suffix(info_uri, "lock"))
248
// look for apt-get install/update
249
if(g_str_has_prefix(info_uri,"/var/lib/apt/")
250
|| g_str_has_prefix(info_uri,"/var/cache/apt/")
251
|| strcmp(info_uri,"/var/lib/dpkg/status") == 0) {
252
g_debug_inotify("apt_get_running=TRUE");
253
un->apt_get_runing=TRUE;
255
if(strstr(info_uri, "/var/lib/update-notifier/dpkg-run-stamp") ||
256
strstr(info_uri, "/var/lib/apt/periodic/update-success-stamp")) {
257
g_debug_inotify("dpkg_was_run=TRUE");
258
un->dpkg_was_run = TRUE;
260
if(strstr(info_uri, REBOOT_FILE)) {
261
g_debug_inotify("reboot required\n");
262
un->reboot_pending = TRUE;
264
if(strstr(info_uri, HOOKS_DIR)) {
265
g_debug_inotify("new hook!\n");
266
un->hook_pending = TRUE;
268
if(strstr(info_uri, CRASHREPORT_DIR)) {
269
g_debug_inotify("crashreport found\n");
270
un->crashreport_pending = TRUE;
272
if(strstr(info_uri, UNICAST_LOCAL_AVAHI_FILE)) {
273
g_debug_inotify("avahi disabled due to unicast .local domain\n");
274
un->unicast_local_avahi_pending = TRUE;
279
* We periodically check here what actions happend in this "time-slice".
281
* - dpkg_was_run=TRUE: set when apt wrote the "dpkg-run-stamp" file
282
* - apt_get_runing: set when apt/dpkg activity is detected (in the
283
* lists-dir, archive-cache, or /var/lib/dpkg/status
284
* - hook_pending: we have new upgrade hoook information
285
* - reboot_pending: we need to reboot
286
* - crashreport_pending: we have a new crashreport
287
* - unicast_local_avahi_pending: avahi got disabled due to a unicast .local domain
290
gboolean file_monitor_periodic_check(gpointer data)
293
UpgradeNotifier *un = (UpgradeNotifier *)data;
295
// we are not ready yet, wait for the next timeslice
296
if((un->reboot == NULL) || (un->crashreport == NULL))
299
// DPkg::Post-Invoke has written a stamp file, that means a install/remove
300
// operation finished, we can show hooks/reboot notifications then
301
if(un->dpkg_was_run) {
304
update_check(un->update);
306
// run cache-changed plugin scripts
307
GDir *dir = g_dir_open(CACHE_CHANGED_PLUGIN_DIR, 0, NULL);
308
const gchar *fname, *full_path;
309
GSList *cache_changed_scripts = NULL;
310
while ( (fname = g_dir_read_name(dir)) != NULL ) {
311
full_path = g_build_filename(CACHE_CHANGED_PLUGIN_DIR, fname, NULL);
312
cache_changed_scripts = g_slist_insert_sorted(cache_changed_scripts,
313
(gpointer)full_path, (GCompareFunc)g_strcmp0);
315
// launch first script and queue others
316
start_next_script(0, 0, cache_changed_scripts);
318
// any apt-get update must be finished, otherwise
319
// apt-get install wouldn't be finished
320
update_apt_is_running(un->update, FALSE);
321
if(un->update_finished_timer > 0)
322
g_source_remove(un->update_finished_timer);
324
// show pending hooks/reboots
325
if(un->hook_pending) {
326
//g_print("checking hooks now\n");
327
check_update_hooks(un->hook);
328
un->hook_pending = FALSE;
330
if(un->reboot_pending) {
331
//g_print("checking reboot now\n");
332
reboot_check (un->reboot);
333
un->reboot_pending = FALSE;
336
// apt must be finished when a new stamp file was writen, so we
337
// reset the apt_get_runing time-slice field because its not
338
// important anymore (it finished runing)
340
// This may leave a 5s race condition when a apt-get install finished
341
// and something new (apt-get) was started
342
un->apt_get_runing = FALSE;
343
un->last_apt_action = 0;
346
// apt-get update/install or dpkg is runing (and updates files in
347
// it's list/cache dir) or in /var/lib/dpkg/status
348
if(un->apt_get_runing)
349
update_apt_is_running(un->update, TRUE);
351
// update time information for apt/dpkg
352
time_t now = time(NULL);
353
if(un->apt_get_runing)
354
un->last_apt_action = now;
356
// no apt operation for a long time
357
if(un->last_apt_action > 0 &&
358
(now - un->last_apt_action) > TIMEOUT_APT_RUN) {
359
update_apt_is_running(un->update, FALSE);
360
update_check(un->update);
361
un->last_apt_action = 0;
364
if(un->crashreport_pending) {
365
g_debug("checking for valid crashreport now\n");
366
crashreport_check (un->crashreport);
367
un->crashreport_pending = FALSE;
370
if(un->unicast_local_avahi_pending) {
371
g_debug("checking for disabled avahi due to unicast .local domain now\n");
372
avahi_disabled_check ();
373
un->unicast_local_avahi_pending = FALSE;
376
// reset the bitfields (for the next "time-slice")
377
un->dpkg_was_run = FALSE;
378
un->apt_get_runing = FALSE;
386
/* u_abort seems like an internal error notification.
387
* End user might not understand the message at all */
388
void u_abort(gchar *msg)
390
const char *fmt = "<span weight=\"bold\" size=\"larger\">%s</span>\n\n%s\n";
391
gtk_dialog_run(GTK_DIALOG(gtk_message_dialog_new_with_markup(NULL, 0,
401
// FIXME: get the apt directories with apt-config or something
403
monitor_init(UpgradeNotifier *un)
406
GFileMonitor *monitor_handle;
408
// monitor thise dirs
409
static const char *monitor_dirs[] = {
410
"/var/lib/apt/lists/", "/var/lib/apt/lists/partial/",
411
"/var/cache/apt/archives/", "/var/cache/apt/archives/partial/",
415
for(i=0;monitor_dirs[i] != NULL;i++) {
416
GError *error = NULL;
417
GFile *gf = g_file_new_for_path(monitor_dirs[i]);
418
monitor_handle = g_file_monitor_directory(gf, 0, NULL, &error);
420
g_signal_connect(monitor_handle, "changed", (GCallback)monitor_cb, un);
422
g_warning("can not add '%s'\n", monitor_dirs[i]);
426
static const char *monitor_files[] = {
427
"/var/lib/dpkg/status",
428
"/var/lib/update-notifier/dpkg-run-stamp",
429
"/var/lib/apt/periodic/update-success-stamp",
431
UNICAST_LOCAL_AVAHI_FILE,
433
for(i=0;monitor_files[i] != NULL;i++) {
434
GError *error = NULL;
435
GFile *gf = g_file_new_for_path(monitor_files[i]);
436
monitor_handle = g_file_monitor_file(gf, 0, NULL, &error);
438
g_signal_connect(monitor_handle, "changed", (GCallback)monitor_cb, un);
440
g_warning("can not add '%s'\n", monitor_dirs[i]);
443
g_timeout_add (TIMEOUT_FAM, (GSourceFunc)file_monitor_periodic_check, un);
453
tray_icons_init(UpgradeNotifier *un)
455
//g_debug_inotify("tray_icons_init");
457
/* new upates tray icon */
458
un->update = g_new0 (TrayApplet, 1);
460
// check if the updates icon should be displayed
461
if (in_admin_group() || FORCE_START) {
462
trayapplet_create(un->update, "software-update-available");
463
update_tray_icon_init(un->update);
467
/* update hook icon*/
468
un->hook = g_new0 (TrayApplet, 1);
469
trayapplet_create(un->hook, "hook-notifier");
470
hook_tray_icon_init(un->hook);
472
/* reboot required icon */
473
un->reboot = g_new0 (TrayApplet, 1);
474
trayapplet_create(un->reboot, "reboot-notifier");
475
reboot_tray_icon_init(un->reboot);
477
/* crashreport detected icon */
478
un->crashreport = g_new0 (TrayApplet, 1);
479
trayapplet_create(un->crashreport, "apport");
480
crashreport_tray_icon_init(un->crashreport);
482
return FALSE; // for the tray_destroyed_cb
488
/* do not start for system users, e.g. the guest session
489
* (see /usr/share/gdm/guest-session/guest-session-setup.sh)
491
int end_system_uid = 500;
492
GConfClient *gconf = gconf_client_get_default();
494
int i = gconf_client_get_int(gconf, GCONF_KEY_END_SYSTEM_UIDS, NULL);
499
uid_t uid = getuid();
500
if (uid < end_system_uid)
505
// this function checks if the user is in the admin group
506
// if there is no admin group, we return true becuase there
507
// is no way to figure if the user is a admin or not
512
gid_t *groups = NULL;
514
struct group *grp = getgrnam("admin");
518
ng = getgroups (0, NULL);
519
groups = (gid_t *) malloc (ng * sizeof (gid_t));
521
i = getgroups (ng, groups);
528
if(groups[i] == grp->gr_gid) {
540
static GOptionEntry entries[] =
542
{ "debug-hooks", 0, 0, G_OPTION_ARG_NONE, &HOOK_DEBUG, "Enable hooks debugging"},
543
{ "debug-updates", 0, 0, G_OPTION_ARG_NONE, &UPDATE_DEBUG, "Enable updates/autolaunch debugging"},
544
{ "debug-inotify", 0, 0, G_OPTION_ARG_NONE, &INOTIFY_DEBUG, "Enable inotify debugging"},
545
{ "debug-uevent", 0, 0, G_OPTION_ARG_NONE, &UEVENT_DEBUG, "Enable uevent debugging"},
546
{ "debug-new-release-check", 0, 0, G_OPTION_ARG_NONE, &RELEASE_DEBUG, "Enable new release check debugging"},
547
{ "debug-misc", 0, 0, G_OPTION_ARG_NONE, &MISC_DEBUG, "Enable uncategorized debug prints"},
548
{ "force", 0, 0, G_OPTION_ARG_NONE, &FORCE_START, "Force start even if the user is not in the admin group"},
549
{ "force-use-gksu", 0, 0, G_OPTION_ARG_NONE, &FORCE_GKSU, "Force running all commands (update-manager, synaptic) with gksu" },
550
{ "startup-delay", 0, 0, G_OPTION_ARG_INT, &STARTUP_DELAY, "Delay startup by given amount of seconds" },
551
{ "devel-release", 0, 0, G_OPTION_ARG_NONE, &DEVEL_RELEASE, "Make the release checker check for a new development release" },
552
{ "force-release-check", 0, 0, G_OPTION_ARG_NONE, &FORCE_RELEASE_CHECK, "Force release check" },
557
main (int argc, char **argv)
560
GError *error = NULL;
563
if(!gtk_init_with_args (&argc, &argv,
564
_("- inform about updates"), entries,
565
"update-notifier", &error) ) {
566
fprintf(stderr, _("Failed to init the UI: %s\n"),
567
error ? error->message : _("unknown error"));
571
notify_init("update-notifier");
572
bindtextdomain(PACKAGE, PACKAGE_LOCALE_DIR);
573
bind_textdomain_codeset(PACKAGE, "UTF-8");
576
// setup a custom debug log handler
577
g_log_set_handler ("inotify", G_LOG_LEVEL_DEBUG,
578
debug_log_handler, NULL);
579
g_log_set_handler ("hooks", G_LOG_LEVEL_DEBUG,
580
debug_log_handler, NULL);
581
g_log_set_handler ("update", G_LOG_LEVEL_DEBUG,
582
debug_log_handler, NULL);
583
g_log_set_handler ("uevent", G_LOG_LEVEL_DEBUG,
584
debug_log_handler, NULL);
585
g_log_set_handler ("release", G_LOG_LEVEL_DEBUG,
586
debug_log_handler, NULL);
587
g_log_set_handler (NULL, G_LOG_LEVEL_DEBUG,
588
debug_log_handler, NULL);
590
g_set_application_name (_("update-notifier"));
591
gtk_window_set_default_icon_name ("update-notifier");
593
//g_print("starting update-notifier\n");
595
// do not run as system user (e.g. guest user)
596
if (system_user() && !FORCE_START) {
597
g_warning("not starting for system user\n");
601
// do not run as system user (e.g. guest user)
602
if (FORCE_RELEASE_CHECK) {
603
GConfClient *gconf = gconf_client_get_default();
604
gconf_client_unset(gconf, GCONF_KEY_LAST_RELEASE_CHECK, NULL);
607
// check if it is running already
608
if (!up_get_clipboard ()) {
609
g_warning ("already running?\n");
613
/* Create the UpgradeNotifier object */
614
un = g_new0 (UpgradeNotifier, 1);
616
// check for update-notifier dir and create if needed
617
gchar *dirname = g_strdup_printf("%s/update-notifier",
618
g_get_user_config_dir());
619
if(!g_file_test(dirname, G_FILE_TEST_IS_DIR))
620
g_mkdir(dirname, 0700);
623
// delay icon creation for 30s so that the desktop
624
// login is not slowed down by update-notifier
625
g_timeout_add_seconds(STARTUP_DELAY,
626
(GSourceFunc)(tray_icons_init), un);
628
// initial check for avahi
629
avahi_disabled_check();
631
/* setup hal so that inserted cdroms can be checked */
632
if(!up_do_hal_init(un)) {
633
g_warning("initializing gdu failed");
636
// init release checker
637
release_checker_init();
639
// init uevent monitoring (missing firmware, etc.)
642
// init gio file monitoring
645
/* Start the main gtk loop */