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

« back to all changes in this revision

Viewing changes to src/update-notifier.c

  • Committer: Balint Reczey
  • Date: 2020-06-11 18:46:02 UTC
  • Revision ID: balint.reczey@canonical.com-20200611184602-2rv1zan3xu723x2u
Moved to git at https://git.launchpad.net/update-notifier

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* update-notifier.c
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
6
 
 *
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.
11
 
 *
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.
16
 
 *
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.
21
 
 */
22
 
 
23
 
#ifdef HAVE_CONFIG_H
24
 
#include "config.h"
25
 
#endif
26
 
 
27
 
#include <unistd.h>
28
 
#include <sys/types.h>
29
 
#include <stdlib.h>
30
 
#include <signal.h>
31
 
#include <errno.h>
32
 
#include <sys/file.h>
33
 
#include <grp.h>
34
 
#include <pwd.h>
35
 
#include <limits.h>
36
 
#include <glib.h>
37
 
#include <glib/gstdio.h>
38
 
#include <glib-unix.h>
39
 
#include <gtk/gtk.h>
40
 
#include <gdk/gdkx.h>
41
 
#include <gio/gio.h>
42
 
 
43
 
#include "update-notifier.h"
44
 
#include "livepatch-tray.h"
45
 
#include "update.h"
46
 
#include "hooks.h"
47
 
#include "uevent.h"
48
 
#include "crash.h"
49
 
#include "release.h"
50
 
#include "trayappletui.h"
51
 
 
52
 
/* some prototypes */
53
 
gboolean update_timer_finished(gpointer data);
54
 
 
55
 
// the time when we check for fam events, in seconds
56
 
#define TIMEOUT_FAM 180
57
 
 
58
 
// the timeout (in sec) when a further activity from dpkg/apt
59
 
// causes the applet to "ungray"
60
 
#define TIMEOUT_APT_RUN 600 /* 600 sec */
61
 
 
62
 
// force startup even if the user is not in the sudo/admin group
63
 
static gboolean FORCE_START=FALSE;
64
 
 
65
 
// force pkexec for all menu actions
66
 
static gboolean FORCE_PKEXEC=FALSE;
67
 
 
68
 
// delay on startup (to make session startup faster)
69
 
static int STARTUP_DELAY=1;
70
 
 
71
 
// make the release upgrader check for a devel release
72
 
gboolean DEVEL_RELEASE=FALSE;
73
 
 
74
 
// force release check
75
 
static gboolean FORCE_RELEASE_CHECK=FALSE;
76
 
 
77
 
// global debug options
78
 
static gboolean HOOK_DEBUG = FALSE;
79
 
static gboolean UPDATE_DEBUG = FALSE;
80
 
static gboolean INOTIFY_DEBUG = FALSE;
81
 
static gboolean UEVENT_DEBUG = FALSE;
82
 
static gboolean RELEASE_DEBUG = FALSE;
83
 
static gboolean MISC_DEBUG = FALSE;
84
 
 
85
 
// logging stuff
86
 
static void debug_log_handler (const gchar   *log_domain,
87
 
                              GLogLevelFlags log_level,
88
 
                              const gchar   *message,
89
 
                              gpointer       user_data)
90
 
{
91
 
   if (log_domain && strcmp(log_domain, "hooks") == 0) {
92
 
      if (HOOK_DEBUG)
93
 
         g_log_default_handler (log_domain, log_level, message, user_data);
94
 
   }
95
 
   else if (log_domain && strcmp(log_domain, "update") == 0) {
96
 
      if (UPDATE_DEBUG)
97
 
         g_log_default_handler (log_domain, log_level, message, user_data);
98
 
   }
99
 
   else if (log_domain && strcmp(log_domain, "inotify") == 0) {
100
 
      if (INOTIFY_DEBUG)
101
 
          g_log_default_handler (log_domain, log_level, message, user_data);
102
 
   }
103
 
   else if (log_domain && strcmp(log_domain, "uevent") == 0) {
104
 
      if (UEVENT_DEBUG)
105
 
         g_log_default_handler (log_domain, log_level, message, user_data);
106
 
   }
107
 
   else if (log_domain && strcmp(log_domain, "release") == 0) {
108
 
      if (RELEASE_DEBUG)
109
 
         g_log_default_handler (log_domain, log_level, message, user_data);
110
 
   }
111
 
   else if (MISC_DEBUG)
112
 
      g_log_default_handler (log_domain, log_level, message, user_data);
113
 
}
114
 
 
115
 
static inline void
116
 
g_debug_inotify(const char *msg, ...)
117
 
{
118
 
   va_list va;
119
 
   va_start(va, msg);
120
 
   g_logv("inotify",G_LOG_LEVEL_DEBUG, msg, va);
121
 
   va_end(va);
122
 
}
123
 
 
124
 
// launch async scripts one after another
125
 
static void start_next_script(GPid pid,
126
 
                              gint status,
127
 
                              GSList *remaining_scripts_to_run)
128
 
{
129
 
    if(remaining_scripts_to_run) {
130
 
        gboolean     ret = False;
131
 
        gchar*       full_path = remaining_scripts_to_run->data;
132
 
        gchar       *argv[] = { full_path, NULL };
133
 
 
134
 
        g_debug_inotify ("executing: %s", full_path);
135
 
        if (g_file_test(full_path, G_FILE_TEST_IS_EXECUTABLE ))
136
 
            ret = g_spawn_async("/", argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, NULL);
137
 
        remaining_scripts_to_run = g_slist_remove(remaining_scripts_to_run, full_path);
138
 
        g_free(full_path);
139
 
        if(!ret)
140
 
        {
141
 
            g_warning("script execution failed or not executable");
142
 
            start_next_script(0, 0, remaining_scripts_to_run);
143
 
            return;
144
 
        }
145
 
        g_child_watch_add(pid, (GChildWatchFunc)start_next_script, remaining_scripts_to_run);
146
 
    }
147
 
 
148
 
}
149
 
 
150
 
void invoke(const gchar *cmd, const gchar *desktop, gboolean with_pkexec)
151
 
{
152
 
   GdkAppLaunchContext *context;
153
 
   GAppInfo *appinfo;
154
 
   GError *error = NULL;
155
 
   static GtkWidget *w = NULL;
156
 
 
157
 
   // pkexec
158
 
   if(with_pkexec || FORCE_PKEXEC) {
159
 
      invoke_with_pkexec(cmd);
160
 
      return;
161
 
   }
162
 
 
163
 
   // fake window to get the current server time *urgh*
164
 
   if (!w) {
165
 
      w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
166
 
      gtk_widget_realize (w);
167
 
   }
168
 
 
169
 
   // normal launch
170
 
   context = gdk_display_get_app_launch_context (gdk_display_get_default ());
171
 
   guint32 timestamp =  gdk_x11_get_server_time (gtk_widget_get_window (w));
172
 
   appinfo = g_app_info_create_from_commandline(cmd, 
173
 
                                                cmd, 
174
 
                                                G_APP_INFO_CREATE_NONE,
175
 
                                                &error);
176
 
   gdk_app_launch_context_set_timestamp (context, timestamp);
177
 
   if (!g_app_info_launch (appinfo, NULL, (GAppLaunchContext*)context, &error))
178
 
      g_warning ("Launching failed: %s", error->message);
179
 
   g_object_unref (context);
180
 
   g_object_unref (appinfo);
181
 
 
182
 
}
183
 
 
184
 
void
185
 
invoke_with_pkexec(const gchar *cmd)
186
 
{
187
 
        g_debug("invoke_with_pkexec ()");
188
 
        gchar *argv[3];
189
 
        argv[0] = "/usr/bin/pkexec";
190
 
        argv[1] = (gchar*)cmd;
191
 
        argv[2] = NULL;
192
 
        g_spawn_async (NULL, argv, NULL, G_SPAWN_DEFAULT, NULL, NULL, NULL, NULL);
193
 
}
194
 
 
195
 
void
196
 
avahi_disabled()
197
 
{
198
 
        g_debug("avahi disabled ()");
199
 
        gchar *argv[2];
200
 
        argv[0] = "/usr/lib/update-notifier/local-avahi-notification";
201
 
        argv[1] = NULL;
202
 
        g_spawn_async (NULL, argv, NULL, G_SPAWN_DEFAULT, NULL, NULL, NULL, NULL);
203
 
}
204
 
 
205
 
void livepatch_check()
206
 
{
207
 
        g_debug("livepatch_check ()");
208
 
        gchar *argv[2];
209
 
        argv[0] = "/usr/lib/update-notifier/livepatch-notification";
210
 
        argv[1] = NULL;
211
 
        g_spawn_async (NULL, argv, NULL, G_SPAWN_DEFAULT, NULL, NULL, NULL, NULL);
212
 
}
213
 
 
214
 
static gboolean
215
 
trayapplet_create (TrayApplet *ta, UpgradeNotifier *un, char *name)
216
 
{
217
 
        g_debug("trayapplet_create()");
218
 
 
219
 
        ta->un = un;
220
 
        ta->name = name;
221
 
 
222
 
        return TRUE;
223
 
}
224
 
 
225
 
/* 
226
 
 the following files change:
227
 
 on "install":
228
 
  - /var/lib/dpkg/lock
229
 
  - /var/lib/dpkg/ *
230
 
  - /var/lib/update-notifier/dpkg-run-stamp
231
 
 on "update":
232
 
  - /var/lib/apt/lists/lock
233
 
  - /var/lib/apt/lists/ *
234
 
  - /var/lib/dpkg/lock
235
 
  - /var/lib/apt/periodic/update-success-stamp
236
 
*/
237
 
static void
238
 
monitor_cb(GFileMonitor *handle,
239
 
           GFile *monitor_f,
240
 
           GFile *other_monitor_f,
241
 
           GFileMonitorEvent event_type,
242
 
           gpointer user_data)
243
 
{
244
 
   UpgradeNotifier *un = (UpgradeNotifier*)user_data;
245
 
 
246
 
   gchar *info_uri = g_file_get_path(monitor_f);
247
 
#if 0
248
 
   g_debug_inotify("info_uri: %s", info_uri);
249
 
   g_debug_inotify("event_type: %i",event_type);
250
 
#endif
251
 
 
252
 
   // we ignore lock file events because we can only get
253
 
   // when a lock was taken, but not when it was removed
254
 
   if(g_str_has_suffix(info_uri, "lock"))
255
 
      return;
256
 
 
257
 
   // look for apt-get install/update
258
 
   if(g_str_has_prefix(info_uri,"/var/lib/apt/")
259
 
      || g_str_has_prefix(info_uri,"/var/cache/apt/")
260
 
      || strcmp(info_uri,"/var/lib/dpkg/status") == 0) {
261
 
      g_debug_inotify("apt_get_running=TRUE");
262
 
      un->apt_get_running=TRUE;
263
 
   }
264
 
   if(strstr(info_uri, "/var/lib/update-notifier/dpkg-run-stamp") ||
265
 
      strstr(info_uri, "/var/lib/apt/periodic/update-success-stamp")) {
266
 
      g_debug_inotify("dpkg_was_run=TRUE");
267
 
      un->dpkg_was_run = TRUE;
268
 
   }
269
 
   if(strstr(info_uri, HOOKS_DIR)) {
270
 
      g_debug_inotify("new hook!");
271
 
      un->hook_pending = TRUE;
272
 
   }
273
 
   if(strstr(info_uri, CRASHREPORT_DIR)) {
274
 
      g_debug_inotify("crashreport found");
275
 
      un->crashreport_pending = TRUE;
276
 
   }
277
 
   if(strstr(info_uri, UNICAST_LOCAL_AVAHI_FILE)) {
278
 
      g_debug_inotify("avahi disabled due to unicast .local domain");
279
 
      un->unicast_local_avahi_pending = TRUE;
280
 
   }
281
 
   if(strstr(info_uri, LIVEPATCH_FILE)) {
282
 
      g_debug_inotify("livepatch status changed");
283
 
      un->livepatch_pending = TRUE;
284
 
   }
285
 
}
286
 
 
287
 
/*
288
 
 * We periodically check here what actions happened in this "time-slice".
289
 
 * This can be:
290
 
 * - dpkg_was_run=TRUE: set when apt wrote the "dpkg-run-stamp" file
291
 
 * - apt_get_running: set when apt/dpkg activity is detected (in the
292
 
 *                    lists-dir, archive-cache, or /var/lib/dpkg/status)
293
 
 * - hook_pending: we have new upgrade hook information
294
 
 * - crashreport_pending: we have a new crashreport
295
 
 * - unicast_local_avahi_pending: avahi got disabled due to a unicast .local domain
296
 
 * - livepatch_pending: livepatch status changed
297
 
 *
298
 
 */
299
 
static gboolean
300
 
file_monitor_periodic_check(gpointer data)
301
 
{
302
 
   UpgradeNotifier *un = (UpgradeNotifier *)data;
303
 
 
304
 
   // we are not ready yet, wait for the next timeslice
305
 
   if(un->crashreport == NULL)
306
 
      return TRUE;
307
 
 
308
 
   // DPkg::Post-Invoke has written a stamp file, that means an install/remove
309
 
   // operation finished, we can show hooks/reboot notifications then
310
 
   if(un->dpkg_was_run) {
311
 
 
312
 
      // check updates
313
 
      update_check(un->update);
314
 
 
315
 
      // run cache-changed plugin scripts
316
 
      GDir *dir = g_dir_open(CACHE_CHANGED_PLUGIN_DIR, 0, NULL);
317
 
      const gchar *fname, *full_path;
318
 
      GSList *cache_changed_scripts = NULL;
319
 
      while ( (fname = g_dir_read_name(dir)) != NULL ) {
320
 
        full_path = g_build_filename(CACHE_CHANGED_PLUGIN_DIR, fname, NULL);
321
 
        cache_changed_scripts = g_slist_insert_sorted(cache_changed_scripts,
322
 
                                 (gpointer)full_path, (GCompareFunc)g_strcmp0);
323
 
      }
324
 
      g_dir_close(dir);
325
 
      // launch first script and queue others
326
 
      start_next_script(0, 0, cache_changed_scripts);
327
 
 
328
 
      // any apt-get update must be finished, otherwise
329
 
      // apt-get install wouldn't be finished
330
 
      update_apt_is_running(un->update, FALSE);
331
 
      if(un->update_finished_timer > 0) 
332
 
         g_source_remove(un->update_finished_timer);
333
 
 
334
 
      // apt must be finished when a new stamp file was written, so we
335
 
      // reset the apt_get_running time-slice field because its not
336
 
      // important anymore (it finished running)
337
 
      //
338
 
      // This may leave a 5s race condition when a apt-get install finished
339
 
      // and something new (apt-get) was started
340
 
      un->apt_get_running = FALSE;
341
 
      un->last_apt_action = 0;
342
 
   }
343
 
 
344
 
   // show pending hooks
345
 
   if(un->hook_pending) {
346
 
      //g_print("checking hooks now\n");
347
 
      check_update_hooks(un->hook);
348
 
      un->hook_pending = FALSE;
349
 
   }
350
 
 
351
 
   // apt-get update/install or dpkg is running (and updates files in
352
 
   // its list/cache dir) or in /var/lib/dpkg/status
353
 
   if(un->apt_get_running)
354
 
      update_apt_is_running(un->update, TRUE);
355
 
 
356
 
   // update time information for apt/dpkg
357
 
   time_t now = time(NULL);
358
 
   if(un->apt_get_running)
359
 
      un->last_apt_action = now;
360
 
 
361
 
   // no apt operation for a long time
362
 
   if(un->last_apt_action > 0 &&
363
 
      (now - un->last_apt_action) >  TIMEOUT_APT_RUN) {
364
 
      update_apt_is_running(un->update, FALSE);
365
 
      update_check(un->update);
366
 
      un->last_apt_action = 0;
367
 
   }
368
 
 
369
 
   if(un->crashreport_pending) {
370
 
      g_debug("checking for valid crashreport now");
371
 
      crashreport_check (un->crashreport);
372
 
      un->crashreport_pending = FALSE;
373
 
   }
374
 
 
375
 
   if(un->unicast_local_avahi_pending) {
376
 
      g_debug("checking for disabled avahi due to unicast .local domain now");
377
 
      avahi_disabled ();
378
 
      un->unicast_local_avahi_pending = FALSE;
379
 
   }
380
 
 
381
 
  if(un->livepatch_pending) {
382
 
      g_debug("checking for livepatch status now");
383
 
      livepatch_check ();
384
 
      un->livepatch_pending = FALSE;
385
 
   } 
386
 
 
387
 
   // reset the bitfields (for the next "time-slice")
388
 
   un->dpkg_was_run = FALSE;
389
 
   un->apt_get_running = FALSE;
390
 
 
391
 
   return TRUE;
392
 
}
393
 
 
394
 
 
395
 
 
396
 
 
397
 
// FIXME: get the apt directories with apt-config or something
398
 
static gboolean
399
 
monitor_init(UpgradeNotifier *un)
400
 
{
401
 
   int i;
402
 
   GFileMonitor *monitor_handle;
403
 
 
404
 
   // monitor these dirs
405
 
   static const char *monitor_dirs[] = {
406
 
      "/var/lib/apt/lists/", "/var/lib/apt/lists/partial/",
407
 
      "/var/cache/apt/archives/", "/var/cache/apt/archives/partial/",
408
 
      HOOKS_DIR,
409
 
      CRASHREPORT_DIR,
410
 
      NULL};
411
 
   for(i=0;monitor_dirs[i] != NULL;i++) {
412
 
      if (getenv("UPSTART_SESSION") && monitor_dirs[i] == CRASHREPORT_DIR) {
413
 
         continue;
414
 
      }
415
 
      GError *error = NULL;
416
 
      GFile *gf = g_file_new_for_path(monitor_dirs[i]);
417
 
      monitor_handle = g_file_monitor_directory(gf, 0, NULL, &error);
418
 
      if(monitor_handle)
419
 
         g_signal_connect(monitor_handle, "changed", (GCallback)monitor_cb, un);
420
 
      else
421
 
         g_warning("can not add '%s'", monitor_dirs[i]);
422
 
   }
423
 
 
424
 
   // and those files
425
 
   static const char *monitor_files[] = {
426
 
      "/var/lib/dpkg/status",
427
 
      "/var/lib/update-notifier/dpkg-run-stamp",
428
 
      "/var/lib/apt/periodic/update-success-stamp",
429
 
      UNICAST_LOCAL_AVAHI_FILE,
430
 
      LIVEPATCH_FILE,
431
 
      NULL};
432
 
   for(i=0;monitor_files[i] != NULL;i++) {
433
 
      if (getenv("UPSTART_SESSION") && monitor_files[i] == UNICAST_LOCAL_AVAHI_FILE) {
434
 
         continue;
435
 
      }
436
 
      GError *error = NULL;
437
 
      GFile *gf = g_file_new_for_path(monitor_files[i]);
438
 
      monitor_handle = g_file_monitor_file(gf, 0, NULL, &error);
439
 
      if(monitor_handle)
440
 
         g_signal_connect(monitor_handle, "changed", (GCallback)monitor_cb, un);
441
 
      else
442
 
         g_warning("can not add '%s'", monitor_dirs[i]);
443
 
   }
444
 
   g_timeout_add_seconds (TIMEOUT_FAM,
445
 
                          (GSourceFunc)file_monitor_periodic_check, un);
446
 
 
447
 
 
448
 
   return TRUE;
449
 
}
450
 
 
451
 
 
452
 
 
453
 
 
454
 
static gboolean
455
 
tray_icons_init(UpgradeNotifier *un)
456
 
{
457
 
   //g_debug_inotify("tray_icons_init");
458
 
 
459
 
   /* new updates tray icon */
460
 
   un->update = g_new0 (TrayApplet, 1);
461
 
 
462
 
   // check if the updates icon should be displayed
463
 
   if (in_admin_group() || FORCE_START) {
464
 
      trayapplet_create(un->update, un, "software-update-available");
465
 
      update_tray_icon_init(un->update);
466
 
   } else
467
 
      un->update = NULL;
468
 
 
469
 
   /* update hook icon*/
470
 
   un->hook = g_new0 (TrayApplet, 1);
471
 
   trayapplet_create(un->hook, un, "hook-notifier");
472
 
   hook_tray_icon_init(un->hook);
473
 
 
474
 
   /* crashreport detected icon */
475
 
   un->crashreport = g_new0 (TrayApplet, 1);
476
 
   trayapplet_create(un->crashreport, un, "apport");
477
 
   crashreport_tray_icon_init(un->crashreport);
478
 
 
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
 
 
484
 
   return FALSE; // for the tray_destroyed_cb
485
 
}
486
 
 
487
 
static gboolean
488
 
system_user(UpgradeNotifier *un)
489
 
{
490
 
   /* do not start for system users, e.g. the guest session
491
 
    * (see /usr/share/gdm/guest-session/guest-session-setup.sh)
492
 
    */
493
 
   int end_system_uid = 500;
494
 
   int i = g_settings_get_int(un->settings, SETTINGS_KEY_END_SYSTEM_UIDS);
495
 
   if (i>0)
496
 
      end_system_uid = i;
497
 
 
498
 
   uid_t uid = getuid();
499
 
   if (uid < end_system_uid)
500
 
      return TRUE;
501
 
   return FALSE;
502
 
}
503
 
 
504
 
// this function checks if the user is in the given group; return 1 if user is
505
 
// a member, 0 if not; return 2 if group does not exist.
506
 
static int
507
 
user_in_group(const char* grpname)
508
 
{
509
 
   int i, ng = 0;
510
 
   gid_t *groups = NULL;
511
 
 
512
 
   struct group *grp = getgrnam(grpname);
513
 
   if(grp == NULL) 
514
 
      return 2;
515
 
 
516
 
   ng = getgroups (0, NULL);
517
 
   groups = (gid_t *) malloc (ng * sizeof (gid_t));
518
 
 
519
 
   i = getgroups (ng, groups);
520
 
   if (i != ng) {
521
 
     free (groups);
522
 
     return 1;
523
 
   }
524
 
 
525
 
   for(i=0;i<ng;i++) {
526
 
      if(groups[i] == grp->gr_gid) {
527
 
        free(groups);
528
 
        return 1;
529
 
      }
530
 
   }
531
 
 
532
 
   if(groups != NULL)
533
 
      free(groups);
534
 
 
535
 
   return 0;
536
 
}
537
 
 
538
 
gboolean
539
 
in_admin_group(void)
540
 
{
541
 
   int is_admin, is_sudo;
542
 
 
543
 
   // we consider the user an admin if the user is in the "sudo" or "admin"
544
 
   // group or neither of these groups exist
545
 
   is_admin = user_in_group("admin");
546
 
   is_sudo = user_in_group("sudo");
547
 
   return is_admin == 1 || is_sudo == 1 || (is_admin + is_sudo == 4);
548
 
}
549
 
 
550
 
 
551
 
static gboolean
552
 
sigint_cb (gpointer user_data __attribute__ ((__unused__)))
553
 
{
554
 
        gtk_main_quit ();
555
 
        return FALSE;
556
 
}
557
 
 
558
 
 
559
 
static GOptionEntry entries[] =
560
 
{
561
 
   { "debug-hooks", 0, 0, G_OPTION_ARG_NONE, &HOOK_DEBUG, "Enable hooks debugging"},
562
 
   { "debug-updates", 0, 0, G_OPTION_ARG_NONE, &UPDATE_DEBUG, "Enable updates/autolaunch debugging"},
563
 
   { "debug-inotify", 0, 0, G_OPTION_ARG_NONE, &INOTIFY_DEBUG, "Enable inotify debugging"},
564
 
   { "debug-uevent", 0, 0, G_OPTION_ARG_NONE, &UEVENT_DEBUG, "Enable uevent debugging"},
565
 
   { "debug-new-release-check", 0, 0, G_OPTION_ARG_NONE, &RELEASE_DEBUG, "Enable new release check debugging"},
566
 
   { "debug-misc", 0, 0, G_OPTION_ARG_NONE, &MISC_DEBUG, "Enable uncategorized debug prints"},
567
 
   { "force", 0, 0, G_OPTION_ARG_NONE, &FORCE_START, "Force start even if the user is not in the admin group"},
568
 
   { "force-use-pkexec", 0, 0, G_OPTION_ARG_NONE, &FORCE_PKEXEC, "Force running all commands (update-manager, synaptic) with pkexec" },
569
 
   { "startup-delay", 0, 0, G_OPTION_ARG_INT, &STARTUP_DELAY, "Delay startup by given amount of seconds" },
570
 
   { "devel-release", 0, 0, G_OPTION_ARG_NONE, &DEVEL_RELEASE, "Make the release checker check for a new development release" },
571
 
   { "force-release-check", 0, 0, G_OPTION_ARG_NONE, &FORCE_RELEASE_CHECK, "Force release check" },
572
 
   { NULL }
573
 
};
574
 
 
575
 
int
576
 
main (int argc, char **argv)
577
 
{
578
 
        UpgradeNotifier *un;
579
 
        GError *error = NULL;
580
 
        int pid_file, rc;
581
 
        gchar *lockfn;
582
 
 
583
 
        // init
584
 
        if(!gtk_init_with_args (&argc, &argv,
585
 
                                _("- inform about updates"), entries,
586
 
                                "update-notifier", &error) ) {
587
 
           fprintf(stderr, _("Failed to init the UI: %s\n"),
588
 
                   error ? error->message : _("unknown error"));
589
 
           exit(1);
590
 
        }
591
 
 
592
 
        notify_init("update-notifier");
593
 
        bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
594
 
        bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
595
 
        textdomain(GETTEXT_PACKAGE);
596
 
 
597
 
        if (HOOK_DEBUG || UPDATE_DEBUG || INOTIFY_DEBUG ||
598
 
            UEVENT_DEBUG || RELEASE_DEBUG || MISC_DEBUG)
599
 
                g_setenv ("G_MESSAGES_DEBUG", "all", TRUE);
600
 
 
601
 
        // setup a custom debug log handler
602
 
        g_log_set_handler ("inotify", G_LOG_LEVEL_DEBUG,
603
 
                           debug_log_handler, NULL);
604
 
        g_log_set_handler ("hooks", G_LOG_LEVEL_DEBUG,
605
 
                           debug_log_handler, NULL);
606
 
        g_log_set_handler ("update", G_LOG_LEVEL_DEBUG,
607
 
                           debug_log_handler, NULL);
608
 
        g_log_set_handler ("uevent", G_LOG_LEVEL_DEBUG,
609
 
                           debug_log_handler, NULL);
610
 
        g_log_set_handler ("release", G_LOG_LEVEL_DEBUG,
611
 
                           debug_log_handler, NULL);
612
 
        g_log_set_handler (NULL, G_LOG_LEVEL_DEBUG,
613
 
                           debug_log_handler, NULL);
614
 
 
615
 
        g_set_application_name (_("update-notifier"));
616
 
        gtk_window_set_default_icon_name ("update-notifier");
617
 
 
618
 
        //g_print("starting update-notifier\n");
619
 
 
620
 
        /* Create the UpgradeNotifier object */
621
 
        un = g_new0 (UpgradeNotifier, 1);
622
 
        un->settings = g_settings_new(SETTINGS_SCHEMA);
623
 
 
624
 
        // do not run as system user (e.g. guest user)
625
 
        if (system_user(un) && !FORCE_START) {
626
 
           g_warning("not starting for system user");
627
 
           exit(0);
628
 
        }
629
 
 
630
 
        // do not run as system user (e.g. guest user)
631
 
        if (FORCE_RELEASE_CHECK)
632
 
           g_settings_reset(un->settings, SETTINGS_KEY_LAST_RELEASE_CHECK);
633
 
 
634
 
        // check if it is running already
635
 
        lockfn = g_build_filename (g_get_user_runtime_dir (), "update-notifier.pid", NULL);
636
 
        pid_file = open (lockfn, O_CREAT | O_RDWR, 0600);
637
 
        g_free (lockfn);
638
 
        rc = flock(pid_file, LOCK_EX | LOCK_NB);
639
 
        if (rc) {
640
 
           g_warning ("already running?");
641
 
           return 1;
642
 
        }
643
 
 
644
 
        // check for update-notifier dir and create if needed
645
 
        gchar *dirname = g_strdup_printf("%s/update-notifier",
646
 
                                         g_get_user_config_dir());
647
 
        if(!g_file_test(dirname, G_FILE_TEST_IS_DIR))
648
 
           g_mkdir(dirname, 0700);
649
 
        g_free(dirname);
650
 
 
651
 
        // delay icon creation for 30s so that the desktop
652
 
        // login is not slowed down by update-notifier
653
 
        g_timeout_add_seconds(STARTUP_DELAY,
654
 
                              (GSourceFunc)(tray_icons_init), un);
655
 
 
656
 
        // init release checker
657
 
        release_checker_init(un);
658
 
 
659
 
        // init uevent monitoring (printer support, etc.)
660
 
#ifdef ENABLE_SCP
661
 
        uevent_init();
662
 
#endif
663
 
 
664
 
        /* check if livepatch patches has been applied during boot or before
665
 
           update-notifier has started (see LP: #1800862) */
666
 
        livepatch_check ();
667
 
 
668
 
        // init gio file monitoring
669
 
        monitor_init (un);
670
 
 
671
 
        /* Start the main gtk loop */
672
 
        g_unix_signal_add_full (G_PRIORITY_DEFAULT, SIGINT, sigint_cb,
673
 
                                NULL, NULL);
674
 
        gtk_main ();
675
 
 
676
 
        return 0;
677
 
}