diff --git a/sdk_container/src/third_party/coreos-overlay/changelog/security/2022-01-27-polkit.md b/sdk_container/src/third_party/coreos-overlay/changelog/security/2022-01-27-polkit.md
new file mode 100644
index 0000000000..f930a00379
--- /dev/null
+++ b/sdk_container/src/third_party/coreos-overlay/changelog/security/2022-01-27-polkit.md
@@ -0,0 +1 @@
+- polkit ([CVE-2021-4034](https://nvd.nist.gov/vuln/detail/CVE-2021-4034))
diff --git a/sdk_container/src/third_party/coreos-overlay/changelog/updates/2022-01-27-polkit.md b/sdk_container/src/third_party/coreos-overlay/changelog/updates/2022-01-27-polkit.md
new file mode 100644
index 0000000000..ca862e5fc2
--- /dev/null
+++ b/sdk_container/src/third_party/coreos-overlay/changelog/updates/2022-01-27-polkit.md
@@ -0,0 +1 @@
+- polkit ([0.120](https://gitlab.freedesktop.org/polkit/polkit/-/blob/0.120/NEWS))
diff --git a/sdk_container/src/third_party/coreos-overlay/sys-auth/polkit/Manifest b/sdk_container/src/third_party/coreos-overlay/sys-auth/polkit/Manifest
index 151dfefa1b..819f58b24c 100644
--- a/sdk_container/src/third_party/coreos-overlay/sys-auth/polkit/Manifest
+++ b/sdk_container/src/third_party/coreos-overlay/sys-auth/polkit/Manifest
@@ -1 +1 @@
-DIST polkit-0.119.tar.gz 1387409 BLAKE2B aeb605598393d1cab40f7c77954008a0392600584c5fe8cc9acaa0e122418ee48b9cce0b6839189ea415277ff0ae4dbd5b7c71cb910aa349dcaf7e1f3f70ef06 SHA512 0260fb15da1c4c1f429e8223260981e64e297f1be8ced42f6910f09ea6581b8205aca06c9c601eb4a128acba2f468de0223118f96862ba769f95721894cf1578
+DIST polkit-0.120.tar.gz 1626659 BLAKE2B 745727445b4946d44b8ea470d21ac131ca7706e83f5dbaf85cf3541ac60a1bbe23b3bf3172a62d9256ebb3dae02d2b2d476e3e0f7fe79a80c47864a120e62ed9 SHA512 db072769439d5e17d0eed681e7b94251b77828c1474b40fe40b94293903a64333e7fa17515a3270648691f04a1374d8b404405ead6abf292a8eb8483164adc46
diff --git a/sdk_container/src/third_party/coreos-overlay/sys-auth/polkit/files/35_WIP_Add_duktape_as_javascript_engine.patch b/sdk_container/src/third_party/coreos-overlay/sys-auth/polkit/files/35_WIP_Add_duktape_as_javascript_engine.patch
deleted file mode 100644
index 2dba8094ab..0000000000
--- a/sdk_container/src/third_party/coreos-overlay/sys-auth/polkit/files/35_WIP_Add_duktape_as_javascript_engine.patch
+++ /dev/null
@@ -1,1596 +0,0 @@
-diff --git a/configure.ac b/configure.ac
-index 4ac2219770513e41b10605a48bc445144bfc8c06..d51cfbd922e289d30e0fdc0c3df745d0c9bd947f 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -80,11 +80,22 @@ PKG_CHECK_MODULES(GLIB, [gmodule-2.0 gio-unix-2.0 >= 2.30.0])
- AC_SUBST(GLIB_CFLAGS)
- AC_SUBST(GLIB_LIBS)
-
--PKG_CHECK_MODULES(LIBJS, [mozjs-78])
--
--AC_SUBST(LIBJS_CFLAGS)
--AC_SUBST(LIBJS_CXXFLAGS)
--AC_SUBST(LIBJS_LIBS)
-+dnl ---------------------------------------------------------------------------
-+dnl - Check javascript backend
-+dnl ---------------------------------------------------------------------------
-+AC_ARG_WITH(duktape, AS_HELP_STRING([--with-duktape],[Use Duktape as javascript backend]),with_duktape=yes,with_duktape=no)
-+AS_IF([test x${with_duktape} == xyes], [
-+ PKG_CHECK_MODULES(LIBJS, [duktape >= 2.0.0 ])
-+ AC_SUBST(LIBJS_CFLAGS)
-+ AC_SUBST(LIBJS_LIBS)
-+], [
-+ PKG_CHECK_MODULES(LIBJS, [mozjs-78])
-+
-+ AC_SUBST(LIBJS_CFLAGS)
-+ AC_SUBST(LIBJS_CXXFLAGS)
-+ AC_SUBST(LIBJS_LIBS)
-+])
-+AM_CONDITIONAL(USE_DUKTAPE, [test x$with_duktape == xyes], [Using duktape as javascript engine library])
-
- EXPAT_LIB=""
- AC_ARG_WITH(expat, [ --with-expat=
Use expat from here],
-@@ -581,6 +592,13 @@ echo "
- PAM support: ${have_pam}
- systemdsystemunitdir: ${systemdsystemunitdir}
- polkitd user: ${POLKITD_USER}"
-+if test "x${with_duktape}" = xyes; then
-+echo "
-+ Javascript engine: Duktape"
-+else
-+echo "
-+ Javascript engine: Mozjs"
-+fi
-
- if test "$have_pam" = yes ; then
- echo "
-diff --git a/meson.build b/meson.build
-index 6997e7f7c6812cc58ac8b74246e557e9c0b03085..75513c2caf3a6fae7744675f9b4bcecd94d59d42 100644
---- a/meson.build
-+++ b/meson.build
-@@ -126,7 +126,13 @@ expat_dep = dependency('expat')
- assert(cc.has_header('expat.h', dependencies: expat_dep), 'Can\'t find expat.h. Please install expat.')
- assert(cc.has_function('XML_ParserCreate', dependencies: expat_dep), 'Can\'t find expat library. Please install expat.')
-
--mozjs_dep = dependency('mozjs-78')
-+js_engine = get_option('js_engine')
-+if js_engine == 'duktape'
-+ js_dep = dependency('duktape')
-+ libm_dep = cc.find_library('m')
-+elif js_engine == 'mozjs'
-+ js_dep = dependency('mozjs-78')
-+endif
-
- dbus_dep = dependency('dbus-1')
- dbus_sysconfdir = dbus_dep.get_pkgconfig_variable('sysconfdir', define_variable: ['sysconfdir', pk_prefix / pk_sysconfdir])
-@@ -350,6 +356,9 @@ if enable_logind
- output += ' systemdsystemunitdir: ' + systemd_systemdsystemunitdir + '\n'
- endif
- output += ' polkitd user: ' + polkitd_user + ' \n'
-+output += ' Javascript engine: ' + js_engine + '\n'
-+if enable_logind
-+endif
- output += ' PAM support: ' + enable_pam.to_string() + '\n\n'
- if enable_pam
- output += ' PAM file auth: ' + pam_conf['PAM_FILE_INCLUDE_AUTH'] + '\n'
-diff --git a/meson_options.txt b/meson_options.txt
-index 25e3e7793ae1671fc65e172386b9392abf4e9352..76aa311c99620129b3594b0d95d675df91dc483b 100644
---- a/meson_options.txt
-+++ b/meson_options.txt
-@@ -16,3 +16,4 @@ option('introspection', type: 'boolean', value: true, description: 'Enable intro
-
- option('gtk_doc', type: 'boolean', value: false, description: 'use gtk-doc to build documentation')
- option('man', type: 'boolean', value: false, description: 'build manual pages')
-+option('js_engine', type: 'combo', choices: ['mozjs', 'duktape'], value: 'duktape', description: 'javascript engine')
-diff --git a/src/polkitbackend/Makefile.am b/src/polkitbackend/Makefile.am
-index 7e3c0809984987d8e77ff6c7717248f132e990f3..abcbc6ff7457965bbedce95a702921bbcd626428 100644
---- a/src/polkitbackend/Makefile.am
-+++ b/src/polkitbackend/Makefile.am
-@@ -33,7 +33,7 @@ libpolkit_backend_1_la_SOURCES = \
- polkitbackendprivate.h \
- polkitbackendauthority.h polkitbackendauthority.c \
- polkitbackendinteractiveauthority.h polkitbackendinteractiveauthority.c \
-- polkitbackendjsauthority.h polkitbackendjsauthority.cpp \
-+ polkitbackendjsauthority.h \
- polkitbackendactionpool.h polkitbackendactionpool.c \
- polkitbackendactionlookup.h polkitbackendactionlookup.c \
- $(NULL)
-@@ -51,19 +51,27 @@ libpolkit_backend_1_la_CFLAGS = \
- -D_POLKIT_BACKEND_COMPILATION \
- $(GLIB_CFLAGS) \
- $(LIBSYSTEMD_CFLAGS) \
-- $(LIBJS_CFLAGS) \
-+ $(LIBJS_CFLAGS) \
- $(NULL)
-
- libpolkit_backend_1_la_CXXFLAGS = $(libpolkit_backend_1_la_CFLAGS)
-
- libpolkit_backend_1_la_LIBADD = \
- $(GLIB_LIBS) \
-+ $(DUKTAPE_LIBS) \
- $(LIBSYSTEMD_LIBS) \
- $(top_builddir)/src/polkit/libpolkit-gobject-1.la \
- $(EXPAT_LIBS) \
-- $(LIBJS_LIBS) \
-+ $(LIBJS_LIBS) \
- $(NULL)
-
-+if USE_DUKTAPE
-+libpolkit_backend_1_la_SOURCES += polkitbackendduktapeauthority.c
-+libpolkit_backend_1_la_LIBADD += -lm
-+else
-+libpolkit_backend_1_la_SOURCES += polkitbackendjsauthority.cpp
-+endif
-+
- rulesdir = $(sysconfdir)/polkit-1/rules.d
- rules_DATA = 50-default.rules
-
-diff --git a/src/polkitbackend/meson.build b/src/polkitbackend/meson.build
-index 93c3c34574efbedb62cb8f3cc63f6acdfef5563f..d7d3b025679dee94f658e3562172f72de0bf9ddf 100644
---- a/src/polkitbackend/meson.build
-+++ b/src/polkitbackend/meson.build
-@@ -5,7 +5,6 @@ sources = files(
- 'polkitbackendactionpool.c',
- 'polkitbackendauthority.c',
- 'polkitbackendinteractiveauthority.c',
-- 'polkitbackendjsauthority.cpp',
- )
-
- output = 'initjs.h'
-@@ -21,7 +20,7 @@ sources += custom_target(
- deps = [
- expat_dep,
- libpolkit_gobject_dep,
-- mozjs_dep,
-+ js_dep,
- ]
-
- c_flags = [
-@@ -31,6 +30,13 @@ c_flags = [
- '-DPACKAGE_SYSCONF_DIR="@0@"'.format(pk_prefix / pk_sysconfdir),
- ]
-
-+if js_engine == 'duktape'
-+ sources += files('polkitbackendduktapeauthority.c')
-+ deps += libm_dep
-+elif js_engine == 'mozjs'
-+ sources += files('polkitbackendjsauthority.cpp')
-+endif
-+
- if enable_logind
- sources += files('polkitbackendsessionmonitor-systemd.c')
-
-diff --git a/src/polkitbackend/polkitbackendduktapeauthority.c b/src/polkitbackend/polkitbackendduktapeauthority.c
-new file mode 100644
-index 0000000000000000000000000000000000000000..4b4f8fda7102223de1300e9ba9f2fa943708b653
---- /dev/null
-+++ b/src/polkitbackend/polkitbackendduktapeauthority.c
-@@ -0,0 +1,1428 @@
-+/*
-+ * Copyright (C) 2008-2012 Red Hat, Inc.
-+ * Copyright (C) 2015 Tangent Space
-+ * Copyright (C) 2019 Wu Xiaotian
-+ *
-+ * This library is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU Lesser General Public
-+ * License as published by the Free Software Foundation; either
-+ * version 2 of the License, or (at your option) any later version.
-+ *
-+ * This library is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-+ * Lesser General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU Lesser General
-+ * Public License along with this library; if not, write to the
-+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
-+ * Boston, MA 02111-1307, USA.
-+ *
-+ * Author: David Zeuthen
-+ */
-+
-+#include "config.h"
-+#include
-+#include
-+#include
-+#include
-+#ifdef HAVE_NETGROUP_H
-+#include
-+#else
-+#include
-+#endif
-+#include
-+#include
-+#include
-+#include
-+
-+#include
-+#include "polkitbackendjsauthority.h"
-+
-+#include
-+
-+#ifdef HAVE_LIBSYSTEMD
-+#include
-+#endif /* HAVE_LIBSYSTEMD */
-+
-+#include "initjs.h" /* init.js */
-+#include "duktape.h"
-+
-+/**
-+ * SECTION:polkitbackendjsauthority
-+ * @title: PolkitBackendJsAuthority
-+ * @short_description: JS Authority
-+ * @stability: Unstable
-+ *
-+ * An implementation of #PolkitBackendAuthority that reads and
-+ * evalates Javascript files and supports interaction with
-+ * authentication agents (virtue of being based on
-+ * #PolkitBackendInteractiveAuthority).
-+ */
-+
-+/* ---------------------------------------------------------------------------------------------------- */
-+
-+struct _PolkitBackendJsAuthorityPrivate
-+{
-+ gchar **rules_dirs;
-+ GFileMonitor **dir_monitors; /* NULL-terminated array of GFileMonitor instances */
-+ duk_context *cx;
-+};
-+
-+
-+static void utils_spawn (const gchar *const *argv,
-+ guint timeout_seconds,
-+ GCancellable *cancellable,
-+ GAsyncReadyCallback callback,
-+ gpointer user_data);
-+
-+gboolean utils_spawn_finish (GAsyncResult *res,
-+ gint *out_exit_status,
-+ gchar **out_standard_output,
-+ gchar **out_standard_error,
-+ GError **error);
-+
-+static void on_dir_monitor_changed (GFileMonitor *monitor,
-+ GFile *file,
-+ GFile *other_file,
-+ GFileMonitorEvent event_type,
-+ gpointer user_data);
-+
-+/* ---------------------------------------------------------------------------------------------------- */
-+
-+enum
-+{
-+ PROP_0,
-+ PROP_RULES_DIRS,
-+};
-+
-+/* ---------------------------------------------------------------------------------------------------- */
-+
-+static GList *polkit_backend_js_authority_get_admin_auth_identities (PolkitBackendInteractiveAuthority *authority,
-+ PolkitSubject *caller,
-+ PolkitSubject *subject,
-+ PolkitIdentity *user_for_subject,
-+ gboolean subject_is_local,
-+ gboolean subject_is_active,
-+ const gchar *action_id,
-+ PolkitDetails *details);
-+
-+static PolkitImplicitAuthorization polkit_backend_js_authority_check_authorization_sync (
-+ PolkitBackendInteractiveAuthority *authority,
-+ PolkitSubject *caller,
-+ PolkitSubject *subject,
-+ PolkitIdentity *user_for_subject,
-+ gboolean subject_is_local,
-+ gboolean subject_is_active,
-+ const gchar *action_id,
-+ PolkitDetails *details,
-+ PolkitImplicitAuthorization implicit);
-+
-+G_DEFINE_TYPE (PolkitBackendJsAuthority, polkit_backend_js_authority, POLKIT_BACKEND_TYPE_INTERACTIVE_AUTHORITY);
-+
-+/* ---------------------------------------------------------------------------------------------------- */
-+
-+/* ---------------------------------------------------------------------------------------------------- */
-+
-+static duk_ret_t js_polkit_log (duk_context *cx);
-+static duk_ret_t js_polkit_spawn (duk_context *cx);
-+static duk_ret_t js_polkit_user_is_in_netgroup (duk_context *cx);
-+
-+static const duk_function_list_entry js_polkit_functions[] =
-+{
-+ { "log", js_polkit_log, 1 },
-+ { "spawn", js_polkit_spawn, 1 },
-+ { "_userIsInNetGroup", js_polkit_user_is_in_netgroup, 2 },
-+ { NULL, NULL, 0 },
-+};
-+
-+static void
-+polkit_backend_js_authority_init (PolkitBackendJsAuthority *authority)
-+{
-+ authority->priv = G_TYPE_INSTANCE_GET_PRIVATE (authority,
-+ POLKIT_BACKEND_TYPE_JS_AUTHORITY,
-+ PolkitBackendJsAuthorityPrivate);
-+}
-+
-+static gint
-+rules_file_name_cmp (const gchar *a,
-+ const gchar *b)
-+{
-+ gint ret;
-+ const gchar *a_base;
-+ const gchar *b_base;
-+
-+ a_base = strrchr (a, '/');
-+ b_base = strrchr (b, '/');
-+
-+ g_assert (a_base != NULL);
-+ g_assert (b_base != NULL);
-+ a_base += 1;
-+ b_base += 1;
-+
-+ ret = g_strcmp0 (a_base, b_base);
-+ if (ret == 0)
-+ {
-+ /* /etc wins over /usr */
-+ ret = g_strcmp0 (a, b);
-+ g_assert (ret != 0);
-+ }
-+
-+ return ret;
-+}
-+
-+static void
-+load_scripts (PolkitBackendJsAuthority *authority)
-+{
-+ duk_context *cx = authority->priv->cx;
-+ GList *files = NULL;
-+ GList *l;
-+ guint num_scripts = 0;
-+ GError *error = NULL;
-+ guint n;
-+
-+ files = NULL;
-+
-+ for (n = 0; authority->priv->rules_dirs != NULL && authority->priv->rules_dirs[n] != NULL; n++)
-+ {
-+ const gchar *dir_name = authority->priv->rules_dirs[n];
-+ GDir *dir = NULL;
-+
-+ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
-+ "Loading rules from directory %s",
-+ dir_name);
-+
-+ dir = g_dir_open (dir_name,
-+ 0,
-+ &error);
-+ if (dir == NULL)
-+ {
-+ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
-+ "Error opening rules directory: %s (%s, %d)",
-+ error->message, g_quark_to_string (error->domain), error->code);
-+ g_clear_error (&error);
-+ }
-+ else
-+ {
-+ const gchar *name;
-+ while ((name = g_dir_read_name (dir)) != NULL)
-+ {
-+ if (g_str_has_suffix (name, ".rules"))
-+ files = g_list_prepend (files, g_strdup_printf ("%s/%s", dir_name, name));
-+ }
-+ g_dir_close (dir);
-+ }
-+ }
-+
-+ files = g_list_sort (files, (GCompareFunc) rules_file_name_cmp);
-+
-+ for (l = files; l != NULL; l = l->next)
-+ {
-+ const gchar *filename = (gchar *)l->data;
-+#if (DUK_VERSION >= 20000)
-+ GFile *file = g_file_new_for_path (filename);
-+ char *contents;
-+ gsize len;
-+ if (!g_file_load_contents (file, NULL, &contents, &len, NULL, NULL))
-+ {
-+ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
-+ "Error compiling script %s",
-+ filename);
-+ g_object_unref (file);
-+ continue;
-+ }
-+
-+ g_object_unref (file);
-+ if (duk_peval_lstring_noresult(cx, contents,len) != 0)
-+#else
-+ if (duk_peval_file_noresult (cx, filename) != 0)
-+#endif
-+ {
-+ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
-+ "Error compiling script %s: %s",
-+ filename, duk_safe_to_string (authority->priv->cx, -1));
-+#if (DUK_VERSION >= 20000)
-+ g_free (contents);
-+#endif
-+ continue;
-+ }
-+#if (DUK_VERSION >= 20000)
-+ g_free (contents);
-+#endif
-+ num_scripts++;
-+ }
-+
-+ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
-+ "Finished loading, compiling and executing %d rules",
-+ num_scripts);
-+ g_list_free_full (files, g_free);
-+}
-+
-+static void
-+reload_scripts (PolkitBackendJsAuthority *authority)
-+{
-+ duk_context *cx = authority->priv->cx;
-+
-+ duk_set_top (cx, 0);
-+ if (!duk_get_global_string (cx, "polkit")) {
-+ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
-+ "Error deleting old rules, not loading new ones");
-+ return;
-+ }
-+ duk_push_string (cx, "_deleteRules");
-+
-+ duk_call_prop (cx, 0, 0);
-+
-+ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
-+ "Collecting garbage unconditionally...");
-+
-+ load_scripts (authority);
-+
-+ /* Let applications know we have new rules... */
-+ g_signal_emit_by_name (authority, "changed");
-+}
-+
-+static void
-+on_dir_monitor_changed (GFileMonitor *monitor,
-+ GFile *file,
-+ GFile *other_file,
-+ GFileMonitorEvent event_type,
-+ gpointer user_data)
-+{
-+ PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (user_data);
-+
-+ /* TODO: maybe rate-limit so storms of events are collapsed into one with a 500ms resolution?
-+ * Because when editing a file with emacs we get 4-8 events..
-+ */
-+
-+ if (file != NULL)
-+ {
-+ gchar *name;
-+
-+ name = g_file_get_basename (file);
-+
-+ /* g_print ("event_type=%d file=%p name=%s\n", event_type, file, name); */
-+ if (!g_str_has_prefix (name, ".") &&
-+ !g_str_has_prefix (name, "#") &&
-+ g_str_has_suffix (name, ".rules") &&
-+ (event_type == G_FILE_MONITOR_EVENT_CREATED ||
-+ event_type == G_FILE_MONITOR_EVENT_DELETED ||
-+ event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT))
-+ {
-+ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
-+ "Reloading rules");
-+ reload_scripts (authority);
-+ }
-+ g_free (name);
-+ }
-+}
-+
-+
-+static void
-+setup_file_monitors (PolkitBackendJsAuthority *authority)
-+{
-+ guint n;
-+ GPtrArray *p;
-+
-+ p = g_ptr_array_new ();
-+ for (n = 0; authority->priv->rules_dirs != NULL && authority->priv->rules_dirs[n] != NULL; n++)
-+ {
-+ GFile *file;
-+ GError *error;
-+ GFileMonitor *monitor;
-+
-+ file = g_file_new_for_path (authority->priv->rules_dirs[n]);
-+ error = NULL;
-+ monitor = g_file_monitor_directory (file,
-+ G_FILE_MONITOR_NONE,
-+ NULL,
-+ &error);
-+ g_object_unref (file);
-+ if (monitor == NULL)
-+ {
-+ g_warning ("Error monitoring directory %s: %s",
-+ authority->priv->rules_dirs[n],
-+ error->message);
-+ g_clear_error (&error);
-+ }
-+ else
-+ {
-+ g_signal_connect (monitor,
-+ "changed",
-+ G_CALLBACK (on_dir_monitor_changed),
-+ authority);
-+ g_ptr_array_add (p, monitor);
-+ }
-+ }
-+ g_ptr_array_add (p, NULL);
-+ authority->priv->dir_monitors = (GFileMonitor**) g_ptr_array_free (p, FALSE);
-+}
-+
-+static void
-+polkit_backend_js_authority_constructed (GObject *object)
-+{
-+ PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (object);
-+ duk_context *cx;
-+
-+ cx = duk_create_heap (NULL, NULL, NULL, authority, NULL);
-+ if (cx == NULL)
-+ goto fail;
-+
-+ authority->priv->cx = cx;
-+
-+ duk_push_global_object (cx);
-+ duk_push_object (cx);
-+ duk_put_function_list (cx, -1, js_polkit_functions);
-+ duk_put_prop_string (cx, -2, "polkit");
-+
-+ duk_eval_string (cx, init_js);
-+
-+ if (authority->priv->rules_dirs == NULL)
-+ {
-+ authority->priv->rules_dirs = g_new0 (gchar *, 3);
-+ authority->priv->rules_dirs[0] = g_strdup (PACKAGE_SYSCONF_DIR "/polkit-1/rules.d");
-+ authority->priv->rules_dirs[1] = g_strdup (PACKAGE_DATA_DIR "/polkit-1/rules.d");
-+ }
-+
-+ setup_file_monitors (authority);
-+ load_scripts (authority);
-+
-+ G_OBJECT_CLASS (polkit_backend_js_authority_parent_class)->constructed (object);
-+ return;
-+
-+ fail:
-+ g_critical ("Error initializing JavaScript environment");
-+ g_assert_not_reached ();
-+}
-+
-+static void
-+polkit_backend_js_authority_finalize (GObject *object)
-+{
-+ PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (object);
-+ guint n;
-+
-+ for (n = 0; authority->priv->dir_monitors != NULL && authority->priv->dir_monitors[n] != NULL; n++)
-+ {
-+ GFileMonitor *monitor = authority->priv->dir_monitors[n];
-+ g_signal_handlers_disconnect_by_func (monitor,
-+ G_CALLBACK (on_dir_monitor_changed),
-+ authority);
-+ g_object_unref (monitor);
-+ }
-+ g_free (authority->priv->dir_monitors);
-+ g_strfreev (authority->priv->rules_dirs);
-+
-+ duk_destroy_heap (authority->priv->cx);
-+
-+ G_OBJECT_CLASS (polkit_backend_js_authority_parent_class)->finalize (object);
-+}
-+
-+static void
-+polkit_backend_js_authority_set_property (GObject *object,
-+ guint property_id,
-+ const GValue *value,
-+ GParamSpec *pspec)
-+{
-+ PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (object);
-+
-+ switch (property_id)
-+ {
-+ case PROP_RULES_DIRS:
-+ g_assert (authority->priv->rules_dirs == NULL);
-+ authority->priv->rules_dirs = (gchar **) g_value_dup_boxed (value);
-+ break;
-+
-+ default:
-+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
-+ break;
-+ }
-+}
-+
-+static const gchar *
-+polkit_backend_js_authority_get_name (PolkitBackendAuthority *authority)
-+{
-+ return "js";
-+}
-+
-+static const gchar *
-+polkit_backend_js_authority_get_version (PolkitBackendAuthority *authority)
-+{
-+ return PACKAGE_VERSION;
-+}
-+
-+static PolkitAuthorityFeatures
-+polkit_backend_js_authority_get_features (PolkitBackendAuthority *authority)
-+{
-+ return POLKIT_AUTHORITY_FEATURES_TEMPORARY_AUTHORIZATION;
-+}
-+
-+static void
-+polkit_backend_js_authority_class_init (PolkitBackendJsAuthorityClass *klass)
-+{
-+ GObjectClass *gobject_class;
-+ PolkitBackendAuthorityClass *authority_class;
-+ PolkitBackendInteractiveAuthorityClass *interactive_authority_class;
-+
-+
-+ gobject_class = G_OBJECT_CLASS (klass);
-+ gobject_class->finalize = polkit_backend_js_authority_finalize;
-+ gobject_class->set_property = polkit_backend_js_authority_set_property;
-+ gobject_class->constructed = polkit_backend_js_authority_constructed;
-+
-+ authority_class = POLKIT_BACKEND_AUTHORITY_CLASS (klass);
-+ authority_class->get_name = polkit_backend_js_authority_get_name;
-+ authority_class->get_version = polkit_backend_js_authority_get_version;
-+ authority_class->get_features = polkit_backend_js_authority_get_features;
-+
-+ interactive_authority_class = POLKIT_BACKEND_INTERACTIVE_AUTHORITY_CLASS (klass);
-+ interactive_authority_class->get_admin_identities = polkit_backend_js_authority_get_admin_auth_identities;
-+ interactive_authority_class->check_authorization_sync = polkit_backend_js_authority_check_authorization_sync;
-+
-+ g_object_class_install_property (gobject_class,
-+ PROP_RULES_DIRS,
-+ g_param_spec_boxed ("rules-dirs",
-+ NULL,
-+ NULL,
-+ G_TYPE_STRV,
-+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
-+
-+
-+ g_type_class_add_private (klass, sizeof (PolkitBackendJsAuthorityPrivate));
-+}
-+
-+/* ---------------------------------------------------------------------------------------------------- */
-+
-+static void
-+set_property_str (duk_context *cx,
-+ const gchar *name,
-+ const gchar *value)
-+{
-+ duk_push_string (cx, value);
-+ duk_put_prop_string (cx, -2, name);
-+}
-+
-+static void
-+set_property_strv (duk_context *cx,
-+ const gchar *name,
-+ GPtrArray *value)
-+{
-+ guint n;
-+ duk_push_array (cx);
-+ for (n = 0; n < value->len; n++)
-+ {
-+ duk_push_string (cx, g_ptr_array_index (value, n));
-+ duk_put_prop_index (cx, -2, n);
-+ }
-+ duk_put_prop_string (cx, -2, name);
-+}
-+
-+static void
-+set_property_int32 (duk_context *cx,
-+ const gchar *name,
-+ gint32 value)
-+{
-+ duk_push_int (cx, value);
-+ duk_put_prop_string (cx, -2, name);
-+}
-+
-+static void
-+set_property_bool (duk_context *cx,
-+ const char *name,
-+ gboolean value)
-+{
-+ duk_push_boolean (cx, value);
-+ duk_put_prop_string (cx, -2, name);
-+}
-+
-+/* ---------------------------------------------------------------------------------------------------- */
-+
-+static gboolean
-+push_subject (duk_context *cx,
-+ PolkitSubject *subject,
-+ PolkitIdentity *user_for_subject,
-+ gboolean subject_is_local,
-+ gboolean subject_is_active,
-+ GError **error)
-+{
-+ gboolean ret = FALSE;
-+ pid_t pid;
-+ uid_t uid;
-+ gchar *user_name = NULL;
-+ GPtrArray *groups = NULL;
-+ struct passwd *passwd;
-+ char *seat_str = NULL;
-+ char *session_str = NULL;
-+
-+ if (!duk_get_global_string (cx, "Subject")) {
-+ return FALSE;
-+ }
-+
-+ duk_new (cx, 0);
-+
-+ if (POLKIT_IS_UNIX_PROCESS (subject))
-+ {
-+ pid = polkit_unix_process_get_pid (POLKIT_UNIX_PROCESS (subject));
-+ }
-+ else if (POLKIT_IS_SYSTEM_BUS_NAME (subject))
-+ {
-+ PolkitSubject *process;
-+ process = polkit_system_bus_name_get_process_sync (POLKIT_SYSTEM_BUS_NAME (subject), NULL, error);
-+ if (process == NULL)
-+ goto out;
-+ pid = polkit_unix_process_get_pid (POLKIT_UNIX_PROCESS (process));
-+ g_object_unref (process);
-+ }
-+ else
-+ {
-+ g_assert_not_reached ();
-+ }
-+
-+#ifdef HAVE_LIBSYSTEMD
-+ if (sd_pid_get_session (pid, &session_str) == 0)
-+ {
-+ if (sd_session_get_seat (session_str, &seat_str) == 0)
-+ {
-+ /* do nothing */
-+ }
-+ }
-+#endif /* HAVE_LIBSYSTEMD */
-+
-+ g_assert (POLKIT_IS_UNIX_USER (user_for_subject));
-+ uid = polkit_unix_user_get_uid (POLKIT_UNIX_USER (user_for_subject));
-+
-+ groups = g_ptr_array_new_with_free_func (g_free);
-+
-+ passwd = getpwuid (uid);
-+ if (passwd == NULL)
-+ {
-+ user_name = g_strdup_printf ("%d", (gint) uid);
-+ g_warning ("Error looking up info for uid %d: %m", (gint) uid);
-+ }
-+ else
-+ {
-+ gid_t gids[512];
-+ int num_gids = 512;
-+
-+ user_name = g_strdup (passwd->pw_name);
-+
-+ if (getgrouplist (passwd->pw_name,
-+ passwd->pw_gid,
-+ gids,
-+ &num_gids) < 0)
-+ {
-+ g_warning ("Error looking up groups for uid %d: %m", (gint) uid);
-+ }
-+ else
-+ {
-+ gint n;
-+ for (n = 0; n < num_gids; n++)
-+ {
-+ struct group *group;
-+ group = getgrgid (gids[n]);
-+ if (group == NULL)
-+ {
-+ g_ptr_array_add (groups, g_strdup_printf ("%d", (gint) gids[n]));
-+ }
-+ else
-+ {
-+ g_ptr_array_add (groups, g_strdup (group->gr_name));
-+ }
-+ }
-+ }
-+ }
-+
-+ set_property_int32 (cx, "pid", pid);
-+ set_property_str (cx, "user", user_name);
-+ set_property_strv (cx, "groups", groups);
-+ set_property_str (cx, "seat", seat_str);
-+ set_property_str (cx, "session", session_str);
-+ set_property_bool (cx, "local", subject_is_local);
-+ set_property_bool (cx, "active", subject_is_active);
-+
-+ ret = TRUE;
-+
-+ out:
-+ free (session_str);
-+ free (seat_str);
-+ g_free (user_name);
-+ if (groups != NULL)
-+ g_ptr_array_unref (groups);
-+
-+ return ret;
-+}
-+
-+/* ---------------------------------------------------------------------------------------------------- */
-+
-+static gboolean
-+push_action_and_details (duk_context *cx,
-+ const gchar *action_id,
-+ PolkitDetails *details,
-+ GError **error)
-+{
-+ gchar **keys;
-+ guint n;
-+
-+ if (!duk_get_global_string (cx, "Action")) {
-+ return FALSE;
-+ }
-+
-+ duk_new (cx, 0);
-+
-+ set_property_str (cx, "id", action_id);
-+
-+ keys = polkit_details_get_keys (details);
-+ for (n = 0; keys != NULL && keys[n] != NULL; n++)
-+ {
-+ gchar *key;
-+ const gchar *value;
-+ key = g_strdup_printf ("_detail_%s", keys[n]);
-+ value = polkit_details_lookup (details, keys[n]);
-+ set_property_str (cx, key, value);
-+ g_free (key);
-+ }
-+ g_strfreev (keys);
-+
-+ return TRUE;
-+}
-+
-+/* ---------------------------------------------------------------------------------------------------- */
-+
-+/* ---------------------------------------------------------------------------------------------------- */
-+
-+static GList *
-+polkit_backend_js_authority_get_admin_auth_identities (PolkitBackendInteractiveAuthority *_authority,
-+ PolkitSubject *caller,
-+ PolkitSubject *subject,
-+ PolkitIdentity *user_for_subject,
-+ gboolean subject_is_local,
-+ gboolean subject_is_active,
-+ const gchar *action_id,
-+ PolkitDetails *details)
-+{
-+ PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (_authority);
-+ GList *ret = NULL;
-+ guint n;
-+ GError *error = NULL;
-+ const char *ret_str = NULL;
-+ gchar **ret_strs = NULL;
-+ duk_context *cx = authority->priv->cx;
-+
-+ duk_set_top (cx, 0);
-+ if (!duk_get_global_string (cx, "polkit")) {
-+ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
-+ "Error deleting old rules, not loading new ones");
-+ goto out;
-+ }
-+
-+ duk_push_string (cx, "_runAdminRules");
-+
-+ if (!push_action_and_details (cx, action_id, details, &error))
-+ {
-+ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
-+ "Error converting action and details to JS object: %s",
-+ error->message);
-+ g_clear_error (&error);
-+ goto out;
-+ }
-+
-+ if (!push_subject (cx, subject, user_for_subject, subject_is_local, subject_is_active, &error))
-+ {
-+ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
-+ "Error converting subject to JS object: %s",
-+ error->message);
-+ g_clear_error (&error);
-+ goto out;
-+ }
-+
-+ if (duk_pcall_prop (cx, 0, 2) != DUK_ERR_NONE)
-+ {
-+ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
-+ "Error evaluating admin rules: ",
-+ duk_safe_to_string (cx, -1));
-+ goto out;
-+ }
-+
-+ ret_str = duk_require_string (cx, -1);
-+
-+ ret_strs = g_strsplit (ret_str, ",", -1);
-+ for (n = 0; ret_strs != NULL && ret_strs[n] != NULL; n++)
-+ {
-+ const gchar *identity_str = ret_strs[n];
-+ PolkitIdentity *identity;
-+
-+ error = NULL;
-+ identity = polkit_identity_from_string (identity_str, &error);
-+ if (identity == NULL)
-+ {
-+ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
-+ "Identity `%s' is not valid, ignoring: %s",
-+ identity_str, error->message);
-+ g_clear_error (&error);
-+ }
-+ else
-+ {
-+ ret = g_list_prepend (ret, identity);
-+ }
-+ }
-+ ret = g_list_reverse (ret);
-+
-+ out:
-+ g_strfreev (ret_strs);
-+ /* fallback to root password auth */
-+ if (ret == NULL)
-+ ret = g_list_prepend (ret, polkit_unix_user_new (0));
-+
-+ return ret;
-+}
-+
-+/* ---------------------------------------------------------------------------------------------------- */
-+
-+static PolkitImplicitAuthorization
-+polkit_backend_js_authority_check_authorization_sync (PolkitBackendInteractiveAuthority *_authority,
-+ PolkitSubject *caller,
-+ PolkitSubject *subject,
-+ PolkitIdentity *user_for_subject,
-+ gboolean subject_is_local,
-+ gboolean subject_is_active,
-+ const gchar *action_id,
-+ PolkitDetails *details,
-+ PolkitImplicitAuthorization implicit)
-+{
-+ PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (_authority);
-+ PolkitImplicitAuthorization ret = implicit;
-+ GError *error = NULL;
-+ gchar *ret_str = NULL;
-+ gboolean good = FALSE;
-+ duk_context *cx = authority->priv->cx;
-+
-+ duk_set_top (cx, 0);
-+ if (!duk_get_global_string (cx, "polkit")) {
-+ goto out;
-+ }
-+
-+ duk_push_string (cx, "_runRules");
-+
-+ if (!push_action_and_details (cx, action_id, details, &error))
-+ {
-+ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
-+ "Error converting action and details to JS object: %s",
-+ error->message);
-+ g_clear_error (&error);
-+ goto out;
-+ }
-+
-+ if (!push_subject (cx, subject, user_for_subject, subject_is_local, subject_is_active, &error))
-+ {
-+ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
-+ "Error converting subject to JS object: %s",
-+ error->message);
-+ g_clear_error (&error);
-+ goto out;
-+ }
-+
-+ if (duk_pcall_prop (cx, 0, 2) != DUK_ERR_NONE)
-+ {
-+ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
-+ "Error evaluating authorization rules: ",
-+ duk_safe_to_string (cx, -1));
-+ goto out;
-+ }
-+
-+ if (duk_is_null(cx, -1)) {
-+ good = TRUE;
-+ goto out;
-+ }
-+ ret_str = g_strdup (duk_require_string (cx, -1));
-+ if (!polkit_implicit_authorization_from_string (ret_str, &ret))
-+ {
-+ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
-+ "Returned result `%s' is not valid",
-+ ret_str);
-+ goto out;
-+ }
-+
-+ good = TRUE;
-+
-+ out:
-+ if (!good)
-+ ret = POLKIT_IMPLICIT_AUTHORIZATION_NOT_AUTHORIZED;
-+ if (ret_str != NULL)
-+ g_free (ret_str);
-+
-+ return ret;
-+}
-+
-+/* ---------------------------------------------------------------------------------------------------- */
-+
-+static duk_ret_t
-+js_polkit_log (duk_context *cx)
-+{
-+ const char *str = duk_require_string (cx, 0);
-+ fprintf (stderr, "%s\n", str);
-+ return 0;
-+}
-+
-+/* ---------------------------------------------------------------------------------------------------- */
-+
-+static const gchar *
-+get_signal_name (gint signal_number)
-+{
-+ switch (signal_number)
-+ {
-+#define _HANDLE_SIG(sig) case sig: return #sig;
-+ _HANDLE_SIG (SIGHUP);
-+ _HANDLE_SIG (SIGINT);
-+ _HANDLE_SIG (SIGQUIT);
-+ _HANDLE_SIG (SIGILL);
-+ _HANDLE_SIG (SIGABRT);
-+ _HANDLE_SIG (SIGFPE);
-+ _HANDLE_SIG (SIGKILL);
-+ _HANDLE_SIG (SIGSEGV);
-+ _HANDLE_SIG (SIGPIPE);
-+ _HANDLE_SIG (SIGALRM);
-+ _HANDLE_SIG (SIGTERM);
-+ _HANDLE_SIG (SIGUSR1);
-+ _HANDLE_SIG (SIGUSR2);
-+ _HANDLE_SIG (SIGCHLD);
-+ _HANDLE_SIG (SIGCONT);
-+ _HANDLE_SIG (SIGSTOP);
-+ _HANDLE_SIG (SIGTSTP);
-+ _HANDLE_SIG (SIGTTIN);
-+ _HANDLE_SIG (SIGTTOU);
-+ _HANDLE_SIG (SIGBUS);
-+#ifdef SIGPOLL
-+ _HANDLE_SIG (SIGPOLL);
-+#endif
-+ _HANDLE_SIG (SIGPROF);
-+ _HANDLE_SIG (SIGSYS);
-+ _HANDLE_SIG (SIGTRAP);
-+ _HANDLE_SIG (SIGURG);
-+ _HANDLE_SIG (SIGVTALRM);
-+ _HANDLE_SIG (SIGXCPU);
-+ _HANDLE_SIG (SIGXFSZ);
-+#undef _HANDLE_SIG
-+ default:
-+ break;
-+ }
-+ return "UNKNOWN_SIGNAL";
-+}
-+
-+typedef struct
-+{
-+ GMainLoop *loop;
-+ GAsyncResult *res;
-+} SpawnData;
-+
-+static void
-+spawn_cb (GObject *source_object,
-+ GAsyncResult *res,
-+ gpointer user_data)
-+{
-+ SpawnData *data = (SpawnData *)user_data;
-+ data->res = (GAsyncResult*)g_object_ref (res);
-+ g_main_loop_quit (data->loop);
-+}
-+
-+static duk_ret_t
-+js_polkit_spawn (duk_context *cx)
-+{
-+#if (DUK_VERSION >= 20000)
-+ duk_ret_t ret = DUK_RET_ERROR;
-+#else
-+ duk_ret_t ret = DUK_RET_INTERNAL_ERROR;
-+#endif
-+ gchar *standard_output = NULL;
-+ gchar *standard_error = NULL;
-+ gint exit_status;
-+ GError *error = NULL;
-+ guint32 array_len;
-+ gchar **argv = NULL;
-+ GMainContext *context = NULL;
-+ GMainLoop *loop = NULL;
-+ SpawnData data = {0};
-+ char *err_str = NULL;
-+ guint n;
-+
-+ if (!duk_is_array (cx, 0))
-+ goto out;
-+
-+ array_len = duk_get_length (cx, 0);
-+
-+ argv = g_new0 (gchar*, array_len + 1);
-+ for (n = 0; n < array_len; n++)
-+ {
-+ duk_get_prop_index (cx, 0, n);
-+ argv[n] = g_strdup (duk_to_string (cx, -1));
-+ duk_pop (cx);
-+ }
-+
-+ context = g_main_context_new ();
-+ loop = g_main_loop_new (context, FALSE);
-+
-+ g_main_context_push_thread_default (context);
-+
-+ data.loop = loop;
-+ utils_spawn ((const gchar *const *) argv,
-+ 10, /* timeout_seconds */
-+ NULL, /* cancellable */
-+ spawn_cb,
-+ &data);
-+
-+ g_main_loop_run (loop);
-+
-+ g_main_context_pop_thread_default (context);
-+
-+ if (!utils_spawn_finish (data.res,
-+ &exit_status,
-+ &standard_output,
-+ &standard_error,
-+ &error))
-+ {
-+ err_str = g_strdup_printf ("Error spawning helper: %s (%s, %d)",
-+ error->message, g_quark_to_string (error->domain), error->code);
-+ g_clear_error (&error);
-+ goto out;
-+ }
-+
-+ if (!(WIFEXITED (exit_status) && WEXITSTATUS (exit_status) == 0))
-+ {
-+ GString *gstr;
-+ gstr = g_string_new (NULL);
-+ if (WIFEXITED (exit_status))
-+ {
-+ g_string_append_printf (gstr,
-+ "Helper exited with non-zero exit status %d",
-+ WEXITSTATUS (exit_status));
-+ }
-+ else if (WIFSIGNALED (exit_status))
-+ {
-+ g_string_append_printf (gstr,
-+ "Helper was signaled with signal %s (%d)",
-+ get_signal_name (WTERMSIG (exit_status)),
-+ WTERMSIG (exit_status));
-+ }
-+ g_string_append_printf (gstr, ", stdout=`%s', stderr=`%s'",
-+ standard_output, standard_error);
-+ err_str = g_string_free (gstr, FALSE);
-+ goto out;
-+ }
-+
-+ duk_push_string (cx, standard_output);
-+ ret = 1;
-+
-+ out:
-+ g_strfreev (argv);
-+ g_free (standard_output);
-+ g_free (standard_error);
-+ g_clear_object (&data.res);
-+ if (loop != NULL)
-+ g_main_loop_unref (loop);
-+ if (context != NULL)
-+ g_main_context_unref (context);
-+
-+ if (err_str)
-+ duk_error (cx, DUK_ERR_ERROR, err_str);
-+
-+ return ret;
-+}
-+
-+/* ---------------------------------------------------------------------------------------------------- */
-+
-+
-+static duk_ret_t
-+js_polkit_user_is_in_netgroup (duk_context *cx)
-+{
-+ const char *user;
-+ const char *netgroup;
-+ gboolean is_in_netgroup = FALSE;
-+
-+ user = duk_require_string (cx, 0);
-+ netgroup = duk_require_string (cx, 1);
-+
-+ if (innetgr (netgroup,
-+ NULL, /* host */
-+ user,
-+ NULL)) /* domain */
-+ {
-+ is_in_netgroup = TRUE;
-+ }
-+
-+ duk_push_boolean (cx, is_in_netgroup);
-+ return 1;
-+}
-+
-+/* ---------------------------------------------------------------------------------------------------- */
-+
-+typedef struct
-+{
-+ GSimpleAsyncResult *simple; /* borrowed reference */
-+ GMainContext *main_context; /* may be NULL */
-+
-+ GCancellable *cancellable; /* may be NULL */
-+ gulong cancellable_handler_id;
-+
-+ GPid child_pid;
-+ gint child_stdout_fd;
-+ gint child_stderr_fd;
-+
-+ GIOChannel *child_stdout_channel;
-+ GIOChannel *child_stderr_channel;
-+
-+ GSource *child_watch_source;
-+ GSource *child_stdout_source;
-+ GSource *child_stderr_source;
-+
-+ guint timeout_seconds;
-+ gboolean timed_out;
-+ GSource *timeout_source;
-+
-+ GString *child_stdout;
-+ GString *child_stderr;
-+
-+ gint exit_status;
-+} UtilsSpawnData;
-+
-+static void
-+utils_child_watch_from_release_cb (GPid pid,
-+ gint status,
-+ gpointer user_data)
-+{
-+}
-+
-+static void
-+utils_spawn_data_free (UtilsSpawnData *data)
-+{
-+ if (data->timeout_source != NULL)
-+ {
-+ g_source_destroy (data->timeout_source);
-+ data->timeout_source = NULL;
-+ }
-+
-+ /* Nuke the child, if necessary */
-+ if (data->child_watch_source != NULL)
-+ {
-+ g_source_destroy (data->child_watch_source);
-+ data->child_watch_source = NULL;
-+ }
-+
-+ if (data->child_pid != 0)
-+ {
-+ GSource *source;
-+ kill (data->child_pid, SIGTERM);
-+ /* OK, we need to reap for the child ourselves - we don't want
-+ * to use waitpid() because that might block the calling
-+ * thread (the child might handle SIGTERM and use several
-+ * seconds for cleanup/rollback).
-+ *
-+ * So we use GChildWatch instead.
-+ *
-+ * Avoid taking a references to ourselves. but note that we need
-+ * to pass the GSource so we can nuke it once handled.
-+ */
-+ source = g_child_watch_source_new (data->child_pid);
-+ g_source_set_callback (source,
-+ (GSourceFunc) utils_child_watch_from_release_cb,
-+ source,
-+ (GDestroyNotify) g_source_destroy);
-+ g_source_attach (source, data->main_context);
-+ g_source_unref (source);
-+ data->child_pid = 0;
-+ }
-+
-+ if (data->child_stdout != NULL)
-+ {
-+ g_string_free (data->child_stdout, TRUE);
-+ data->child_stdout = NULL;
-+ }
-+
-+ if (data->child_stderr != NULL)
-+ {
-+ g_string_free (data->child_stderr, TRUE);
-+ data->child_stderr = NULL;
-+ }
-+
-+ if (data->child_stdout_channel != NULL)
-+ {
-+ g_io_channel_unref (data->child_stdout_channel);
-+ data->child_stdout_channel = NULL;
-+ }
-+ if (data->child_stderr_channel != NULL)
-+ {
-+ g_io_channel_unref (data->child_stderr_channel);
-+ data->child_stderr_channel = NULL;
-+ }
-+
-+ if (data->child_stdout_source != NULL)
-+ {
-+ g_source_destroy (data->child_stdout_source);
-+ data->child_stdout_source = NULL;
-+ }
-+ if (data->child_stderr_source != NULL)
-+ {
-+ g_source_destroy (data->child_stderr_source);
-+ data->child_stderr_source = NULL;
-+ }
-+
-+ if (data->child_stdout_fd != -1)
-+ {
-+ g_warn_if_fail (close (data->child_stdout_fd) == 0);
-+ data->child_stdout_fd = -1;
-+ }
-+ if (data->child_stderr_fd != -1)
-+ {
-+ g_warn_if_fail (close (data->child_stderr_fd) == 0);
-+ data->child_stderr_fd = -1;
-+ }
-+
-+ if (data->cancellable_handler_id > 0)
-+ {
-+ g_cancellable_disconnect (data->cancellable, data->cancellable_handler_id);
-+ data->cancellable_handler_id = 0;
-+ }
-+
-+ if (data->main_context != NULL)
-+ g_main_context_unref (data->main_context);
-+
-+ if (data->cancellable != NULL)
-+ g_object_unref (data->cancellable);
-+
-+ g_slice_free (UtilsSpawnData, data);
-+}
-+
-+/* called in the thread where @cancellable was cancelled */
-+static void
-+utils_on_cancelled (GCancellable *cancellable,
-+ gpointer user_data)
-+{
-+ UtilsSpawnData *data = (UtilsSpawnData *)user_data;
-+ GError *error;
-+
-+ error = NULL;
-+ g_warn_if_fail (g_cancellable_set_error_if_cancelled (cancellable, &error));
-+ g_simple_async_result_take_error (data->simple, error);
-+ g_simple_async_result_complete_in_idle (data->simple);
-+ g_object_unref (data->simple);
-+}
-+
-+static gboolean
-+utils_read_child_stderr (GIOChannel *channel,
-+ GIOCondition condition,
-+ gpointer user_data)
-+{
-+ UtilsSpawnData *data = (UtilsSpawnData *)user_data;
-+ gchar buf[1024];
-+ gsize bytes_read;
-+
-+ g_io_channel_read_chars (channel, buf, sizeof buf, &bytes_read, NULL);
-+ g_string_append_len (data->child_stderr, buf, bytes_read);
-+ return TRUE;
-+}
-+
-+static gboolean
-+utils_read_child_stdout (GIOChannel *channel,
-+ GIOCondition condition,
-+ gpointer user_data)
-+{
-+ UtilsSpawnData *data = (UtilsSpawnData *)user_data;
-+ gchar buf[1024];
-+ gsize bytes_read;
-+
-+ g_io_channel_read_chars (channel, buf, sizeof buf, &bytes_read, NULL);
-+ g_string_append_len (data->child_stdout, buf, bytes_read);
-+ return TRUE;
-+}
-+
-+static void
-+utils_child_watch_cb (GPid pid,
-+ gint status,
-+ gpointer user_data)
-+{
-+ UtilsSpawnData *data = (UtilsSpawnData *)user_data;
-+ gchar *buf;
-+ gsize buf_size;
-+
-+ if (g_io_channel_read_to_end (data->child_stdout_channel, &buf, &buf_size, NULL) == G_IO_STATUS_NORMAL)
-+ {
-+ g_string_append_len (data->child_stdout, buf, buf_size);
-+ g_free (buf);
-+ }
-+ if (g_io_channel_read_to_end (data->child_stderr_channel, &buf, &buf_size, NULL) == G_IO_STATUS_NORMAL)
-+ {
-+ g_string_append_len (data->child_stderr, buf, buf_size);
-+ g_free (buf);
-+ }
-+
-+ data->exit_status = status;
-+
-+ /* ok, child watch is history, make sure we don't free it in spawn_data_free() */
-+ data->child_pid = 0;
-+ data->child_watch_source = NULL;
-+
-+ /* we're done */
-+ g_simple_async_result_complete_in_idle (data->simple);
-+ g_object_unref (data->simple);
-+}
-+
-+static gboolean
-+utils_timeout_cb (gpointer user_data)
-+{
-+ UtilsSpawnData *data = (UtilsSpawnData *)user_data;
-+
-+ data->timed_out = TRUE;
-+
-+ /* ok, timeout is history, make sure we don't free it in spawn_data_free() */
-+ data->timeout_source = NULL;
-+
-+ /* we're done */
-+ g_simple_async_result_complete_in_idle (data->simple);
-+ g_object_unref (data->simple);
-+
-+ return FALSE; /* remove source */
-+}
-+
-+static void
-+utils_spawn (const gchar *const *argv,
-+ guint timeout_seconds,
-+ GCancellable *cancellable,
-+ GAsyncReadyCallback callback,
-+ gpointer user_data)
-+{
-+ UtilsSpawnData *data;
-+ GError *error;
-+
-+ data = g_slice_new0 (UtilsSpawnData);
-+ data->timeout_seconds = timeout_seconds;
-+ data->simple = g_simple_async_result_new (NULL,
-+ callback,
-+ user_data,
-+ (gpointer*)utils_spawn);
-+ data->main_context = g_main_context_get_thread_default ();
-+ if (data->main_context != NULL)
-+ g_main_context_ref (data->main_context);
-+
-+ data->cancellable = cancellable != NULL ? (GCancellable*)g_object_ref (cancellable) : NULL;
-+
-+ data->child_stdout = g_string_new (NULL);
-+ data->child_stderr = g_string_new (NULL);
-+ data->child_stdout_fd = -1;
-+ data->child_stderr_fd = -1;
-+
-+ /* the life-cycle of UtilsSpawnData is tied to its GSimpleAsyncResult */
-+ g_simple_async_result_set_op_res_gpointer (data->simple, data, (GDestroyNotify) utils_spawn_data_free);
-+
-+ error = NULL;
-+ if (data->cancellable != NULL)
-+ {
-+ /* could already be cancelled */
-+ error = NULL;
-+ if (g_cancellable_set_error_if_cancelled (data->cancellable, &error))
-+ {
-+ g_simple_async_result_take_error (data->simple, error);
-+ g_simple_async_result_complete_in_idle (data->simple);
-+ g_object_unref (data->simple);
-+ goto out;
-+ }
-+
-+ data->cancellable_handler_id = g_cancellable_connect (data->cancellable,
-+ G_CALLBACK (utils_on_cancelled),
-+ data,
-+ NULL);
-+ }
-+
-+ error = NULL;
-+ if (!g_spawn_async_with_pipes (NULL, /* working directory */
-+ (gchar **) argv,
-+ NULL, /* envp */
-+ G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
-+ NULL, /* child_setup */
-+ NULL, /* child_setup's user_data */
-+ &(data->child_pid),
-+ NULL, /* gint *stdin_fd */
-+ &(data->child_stdout_fd),
-+ &(data->child_stderr_fd),
-+ &error))
-+ {
-+ g_prefix_error (&error, "Error spawning: ");
-+ g_simple_async_result_take_error (data->simple, error);
-+ g_simple_async_result_complete_in_idle (data->simple);
-+ g_object_unref (data->simple);
-+ goto out;
-+ }
-+
-+ if (timeout_seconds > 0)
-+ {
-+ data->timeout_source = g_timeout_source_new_seconds (timeout_seconds);
-+ g_source_set_priority (data->timeout_source, G_PRIORITY_DEFAULT);
-+ g_source_set_callback (data->timeout_source, utils_timeout_cb, data, NULL);
-+ g_source_attach (data->timeout_source, data->main_context);
-+ g_source_unref (data->timeout_source);
-+ }
-+
-+ data->child_watch_source = g_child_watch_source_new (data->child_pid);
-+ g_source_set_callback (data->child_watch_source, (GSourceFunc) utils_child_watch_cb, data, NULL);
-+ g_source_attach (data->child_watch_source, data->main_context);
-+ g_source_unref (data->child_watch_source);
-+
-+ data->child_stdout_channel = g_io_channel_unix_new (data->child_stdout_fd);
-+ g_io_channel_set_flags (data->child_stdout_channel, G_IO_FLAG_NONBLOCK, NULL);
-+ data->child_stdout_source = g_io_create_watch (data->child_stdout_channel, G_IO_IN);
-+ g_source_set_callback (data->child_stdout_source, (GSourceFunc) utils_read_child_stdout, data, NULL);
-+ g_source_attach (data->child_stdout_source, data->main_context);
-+ g_source_unref (data->child_stdout_source);
-+
-+ data->child_stderr_channel = g_io_channel_unix_new (data->child_stderr_fd);
-+ g_io_channel_set_flags (data->child_stderr_channel, G_IO_FLAG_NONBLOCK, NULL);
-+ data->child_stderr_source = g_io_create_watch (data->child_stderr_channel, G_IO_IN);
-+ g_source_set_callback (data->child_stderr_source, (GSourceFunc) utils_read_child_stderr, data, NULL);
-+ g_source_attach (data->child_stderr_source, data->main_context);
-+ g_source_unref (data->child_stderr_source);
-+
-+ out:
-+ ;
-+}
-+
-+gboolean
-+utils_spawn_finish (GAsyncResult *res,
-+ gint *out_exit_status,
-+ gchar **out_standard_output,
-+ gchar **out_standard_error,
-+ GError **error)
-+{
-+ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
-+ UtilsSpawnData *data;
-+ gboolean ret = FALSE;
-+
-+ g_return_val_if_fail (G_IS_ASYNC_RESULT (res), FALSE);
-+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
-+
-+ g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == utils_spawn);
-+
-+ if (g_simple_async_result_propagate_error (simple, error))
-+ goto out;
-+
-+ data = (UtilsSpawnData*)g_simple_async_result_get_op_res_gpointer (simple);
-+
-+ if (data->timed_out)
-+ {
-+ g_set_error (error,
-+ G_IO_ERROR,
-+ G_IO_ERROR_TIMED_OUT,
-+ "Timed out after %d seconds",
-+ data->timeout_seconds);
-+ goto out;
-+ }
-+
-+ if (out_exit_status != NULL)
-+ *out_exit_status = data->exit_status;
-+
-+ if (out_standard_output != NULL)
-+ *out_standard_output = g_strdup (data->child_stdout->str);
-+
-+ if (out_standard_error != NULL)
-+ *out_standard_error = g_strdup (data->child_stderr->str);
-+
-+ ret = TRUE;
-+
-+ out:
-+ return ret;
-+}
diff --git a/sdk_container/src/third_party/coreos-overlay/sys-auth/polkit/files/97_Add_duktape_as_javascript_engine.patch b/sdk_container/src/third_party/coreos-overlay/sys-auth/polkit/files/97_Add_duktape_as_javascript_engine.patch
new file mode 100644
index 0000000000..cf181b6372
--- /dev/null
+++ b/sdk_container/src/third_party/coreos-overlay/sys-auth/polkit/files/97_Add_duktape_as_javascript_engine.patch
@@ -0,0 +1,5744 @@
+From 4f66a9549a393e4d74b93eb85301a04ea94bc750 Mon Sep 17 00:00:00 2001
+From: Wu Xiaotian
+Date: Wed, 24 Jul 2019 15:55:17 +0800
+Subject: [PATCH 01/17] Add duktape as javascript engine.
+
+Signed-off-by: Gustavo Lima Chaves
+---
+ configure.ac | 28 +-
+ src/polkitbackend/Makefile.am | 14 +-
+ .../polkitbackendduktapeauthority.c | 1402 +++++++++++++++++
+ 3 files changed, 1436 insertions(+), 8 deletions(-)
+ create mode 100644 src/polkitbackend/polkitbackendduktapeauthority.c
+
+diff --git a/configure.ac b/configure.ac
+index e434ca2..5a03593 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -80,11 +80,22 @@ PKG_CHECK_MODULES(GLIB, [gmodule-2.0 gio-unix-2.0 >= 2.30.0])
+ AC_SUBST(GLIB_CFLAGS)
+ AC_SUBST(GLIB_LIBS)
+
+-PKG_CHECK_MODULES(LIBJS, [mozjs-78])
+-
+-AC_SUBST(LIBJS_CFLAGS)
+-AC_SUBST(LIBJS_CXXFLAGS)
+-AC_SUBST(LIBJS_LIBS)
++dnl ---------------------------------------------------------------------------
++dnl - Check javascript backend
++dnl ---------------------------------------------------------------------------
++AC_ARG_WITH(duktape, AS_HELP_STRING([--with-duktape],[Use Duktape as javascript backend]),with_duktape=yes,with_duktape=no)
++AS_IF([test x${with_duktape} == xyes], [
++ PKG_CHECK_MODULES(LIBJS, [duktape >= 2.0.0 ])
++ AC_SUBST(LIBJS_CFLAGS)
++ AC_SUBST(LIBJS_LIBS)
++], [
++ PKG_CHECK_MODULES(LIBJS, [mozjs-78])
++
++ AC_SUBST(LIBJS_CFLAGS)
++ AC_SUBST(LIBJS_CXXFLAGS)
++ AC_SUBST(LIBJS_LIBS)
++])
++AM_CONDITIONAL(USE_DUKTAPE, [test x$with_duktape == xyes], [Using duktape as javascript engine library])
+
+ EXPAT_LIB=""
+ AC_ARG_WITH(expat, [ --with-expat= Use expat from here],
+@@ -585,6 +596,13 @@ echo "
+ PAM support: ${have_pam}
+ systemdsystemunitdir: ${systemdsystemunitdir}
+ polkitd user: ${POLKITD_USER}"
++if test "x${with_duktape}" = xyes; then
++echo "
++ Javascript engine: Duktape"
++else
++echo "
++ Javascript engine: Mozjs"
++fi
+
+ if test "$have_pam" = yes ; then
+ echo "
+diff --git a/src/polkitbackend/Makefile.am b/src/polkitbackend/Makefile.am
+index 7e3c080..abcbc6f 100644
+--- a/src/polkitbackend/Makefile.am
++++ b/src/polkitbackend/Makefile.am
+@@ -33,7 +33,7 @@ libpolkit_backend_1_la_SOURCES = \
+ polkitbackendprivate.h \
+ polkitbackendauthority.h polkitbackendauthority.c \
+ polkitbackendinteractiveauthority.h polkitbackendinteractiveauthority.c \
+- polkitbackendjsauthority.h polkitbackendjsauthority.cpp \
++ polkitbackendjsauthority.h \
+ polkitbackendactionpool.h polkitbackendactionpool.c \
+ polkitbackendactionlookup.h polkitbackendactionlookup.c \
+ $(NULL)
+@@ -51,19 +51,27 @@ libpolkit_backend_1_la_CFLAGS = \
+ -D_POLKIT_BACKEND_COMPILATION \
+ $(GLIB_CFLAGS) \
+ $(LIBSYSTEMD_CFLAGS) \
+- $(LIBJS_CFLAGS) \
++ $(LIBJS_CFLAGS) \
+ $(NULL)
+
+ libpolkit_backend_1_la_CXXFLAGS = $(libpolkit_backend_1_la_CFLAGS)
+
+ libpolkit_backend_1_la_LIBADD = \
+ $(GLIB_LIBS) \
++ $(DUKTAPE_LIBS) \
+ $(LIBSYSTEMD_LIBS) \
+ $(top_builddir)/src/polkit/libpolkit-gobject-1.la \
+ $(EXPAT_LIBS) \
+- $(LIBJS_LIBS) \
++ $(LIBJS_LIBS) \
+ $(NULL)
+
++if USE_DUKTAPE
++libpolkit_backend_1_la_SOURCES += polkitbackendduktapeauthority.c
++libpolkit_backend_1_la_LIBADD += -lm
++else
++libpolkit_backend_1_la_SOURCES += polkitbackendjsauthority.cpp
++endif
++
+ rulesdir = $(sysconfdir)/polkit-1/rules.d
+ rules_DATA = 50-default.rules
+
+diff --git a/src/polkitbackend/polkitbackendduktapeauthority.c b/src/polkitbackend/polkitbackendduktapeauthority.c
+new file mode 100644
+index 0000000..ae98453
+--- /dev/null
++++ b/src/polkitbackend/polkitbackendduktapeauthority.c
+@@ -0,0 +1,1402 @@
++/*
++ * Copyright (C) 2008-2012 Red Hat, Inc.
++ * Copyright (C) 2015 Tangent Space
++ * Copyright (C) 2019 Wu Xiaotian
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++ * Boston, MA 02111-1307, USA.
++ *
++ * Author: David Zeuthen
++ */
++
++#include "config.h"
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++
++#include
++#include "polkitbackendjsauthority.h"
++
++#include
++
++#ifdef HAVE_LIBSYSTEMD
++#include
++#endif /* HAVE_LIBSYSTEMD */
++
++#include "initjs.h" /* init.js */
++#include "duktape.h"
++
++/**
++ * SECTION:polkitbackendjsauthority
++ * @title: PolkitBackendJsAuthority
++ * @short_description: JS Authority
++ * @stability: Unstable
++ *
++ * An implementation of #PolkitBackendAuthority that reads and
++ * evalates Javascript files and supports interaction with
++ * authentication agents (virtue of being based on
++ * #PolkitBackendInteractiveAuthority).
++ */
++
++/* ---------------------------------------------------------------------------------------------------- */
++
++struct _PolkitBackendJsAuthorityPrivate
++{
++ gchar **rules_dirs;
++ GFileMonitor **dir_monitors; /* NULL-terminated array of GFileMonitor instances */
++ duk_context *cx;
++};
++
++#define WATCHDOG_TIMEOUT (15 * G_TIME_SPAN_SECOND)
++
++static void utils_spawn (const gchar *const *argv,
++ guint timeout_seconds,
++ GCancellable *cancellable,
++ GAsyncReadyCallback callback,
++ gpointer user_data);
++
++gboolean utils_spawn_finish (GAsyncResult *res,
++ gint *out_exit_status,
++ gchar **out_standard_output,
++ gchar **out_standard_error,
++ GError **error);
++
++static void on_dir_monitor_changed (GFileMonitor *monitor,
++ GFile *file,
++ GFile *other_file,
++ GFileMonitorEvent event_type,
++ gpointer user_data);
++
++/* ---------------------------------------------------------------------------------------------------- */
++
++enum
++{
++ PROP_0,
++ PROP_RULES_DIRS,
++};
++
++/* ---------------------------------------------------------------------------------------------------- */
++
++static GList *polkit_backend_js_authority_get_admin_auth_identities (PolkitBackendInteractiveAuthority *authority,
++ PolkitSubject *caller,
++ PolkitSubject *subject,
++ PolkitIdentity *user_for_subject,
++ gboolean subject_is_local,
++ gboolean subject_is_active,
++ const gchar *action_id,
++ PolkitDetails *details);
++
++static PolkitImplicitAuthorization polkit_backend_js_authority_check_authorization_sync (
++ PolkitBackendInteractiveAuthority *authority,
++ PolkitSubject *caller,
++ PolkitSubject *subject,
++ PolkitIdentity *user_for_subject,
++ gboolean subject_is_local,
++ gboolean subject_is_active,
++ const gchar *action_id,
++ PolkitDetails *details,
++ PolkitImplicitAuthorization implicit);
++
++G_DEFINE_TYPE (PolkitBackendJsAuthority, polkit_backend_js_authority, POLKIT_BACKEND_TYPE_INTERACTIVE_AUTHORITY);
++
++/* ---------------------------------------------------------------------------------------------------- */
++
++/* ---------------------------------------------------------------------------------------------------- */
++
++static void
++polkit_backend_js_authority_init (PolkitBackendJsAuthority *authority)
++{
++ authority->priv = G_TYPE_INSTANCE_GET_PRIVATE (authority,
++ POLKIT_BACKEND_TYPE_JS_AUTHORITY,
++ PolkitBackendJsAuthorityPrivate);
++}
++
++static gint
++rules_file_name_cmp (const gchar *a,
++ const gchar *b)
++{
++ gint ret;
++ const gchar *a_base;
++ const gchar *b_base;
++
++ a_base = strrchr (a, '/');
++ b_base = strrchr (b, '/');
++
++ g_assert (a_base != NULL);
++ g_assert (b_base != NULL);
++ a_base += 1;
++ b_base += 1;
++
++ ret = g_strcmp0 (a_base, b_base);
++ if (ret == 0)
++ {
++ /* /etc wins over /usr */
++ ret = g_strcmp0 (a, b);
++ g_assert (ret != 0);
++ }
++
++ return ret;
++}
++
++static void
++load_scripts (PolkitBackendJsAuthority *authority)
++{
++ duk_context *cx = authority->priv->cx;
++ GList *files = NULL;
++ GList *l;
++ guint num_scripts = 0;
++ GError *error = NULL;
++ guint n;
++
++ files = NULL;
++
++ for (n = 0; authority->priv->rules_dirs != NULL && authority->priv->rules_dirs[n] != NULL; n++)
++ {
++ const gchar *dir_name = authority->priv->rules_dirs[n];
++ GDir *dir = NULL;
++
++ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
++ "Loading rules from directory %s",
++ dir_name);
++
++ dir = g_dir_open (dir_name,
++ 0,
++ &error);
++ if (dir == NULL)
++ {
++ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
++ "Error opening rules directory: %s (%s, %d)",
++ error->message, g_quark_to_string (error->domain), error->code);
++ g_clear_error (&error);
++ }
++ else
++ {
++ const gchar *name;
++ while ((name = g_dir_read_name (dir)) != NULL)
++ {
++ if (g_str_has_suffix (name, ".rules"))
++ files = g_list_prepend (files, g_strdup_printf ("%s/%s", dir_name, name));
++ }
++ g_dir_close (dir);
++ }
++ }
++
++ files = g_list_sort (files, (GCompareFunc) rules_file_name_cmp);
++
++ for (l = files; l != NULL; l = l->next)
++ {
++ const gchar *filename = l->data;
++
++#if (DUK_VERSION >= 20000)
++ gchar *contents;
++ gsize length;
++ GError *error = NULL;
++ if (!g_file_get_contents (filename, &contents, &length, &error)){
++ g_warning("Error when file contents of %s: %s\n", filename, error->message);
++ g_error_free (error);
++ continue;
++ }
++ if (duk_peval_lstring_noresult(cx, contents,length) != 0)
++#else
++ if (duk_peval_file_noresult (cx, filename) != 0)
++#endif
++ {
++ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
++ "Error compiling script %s: %s",
++ filename, duk_safe_to_string (authority->priv->cx, -1));
++#if (DUK_VERSION >= 20000)
++ g_free (contents);
++#endif
++ continue;
++ }
++#if (DUK_VERSION >= 20000)
++ g_free (contents);
++#endif
++ num_scripts++;
++ }
++
++ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
++ "Finished loading, compiling and executing %d rules",
++ num_scripts);
++ g_list_free_full (files, g_free);
++}
++
++static void
++reload_scripts (PolkitBackendJsAuthority *authority)
++{
++ duk_context *cx = authority->priv->cx;
++
++ duk_set_top (cx, 0);
++ duk_get_global_string (cx, "polkit");
++ duk_push_string (cx, "_deleteRules");
++
++ duk_call_prop (cx, 0, 0);
++
++ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
++ "Collecting garbage unconditionally...");
++
++ load_scripts (authority);
++
++ /* Let applications know we have new rules... */
++ g_signal_emit_by_name (authority, "changed");
++}
++
++static void
++on_dir_monitor_changed (GFileMonitor *monitor,
++ GFile *file,
++ GFile *other_file,
++ GFileMonitorEvent event_type,
++ gpointer user_data)
++{
++ PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (user_data);
++
++ /* TODO: maybe rate-limit so storms of events are collapsed into one with a 500ms resolution?
++ * Because when editing a file with emacs we get 4-8 events..
++ */
++
++ if (file != NULL)
++ {
++ gchar *name;
++
++ name = g_file_get_basename (file);
++
++ /* g_print ("event_type=%d file=%p name=%s\n", event_type, file, name); */
++ if (!g_str_has_prefix (name, ".") &&
++ !g_str_has_prefix (name, "#") &&
++ g_str_has_suffix (name, ".rules") &&
++ (event_type == G_FILE_MONITOR_EVENT_CREATED ||
++ event_type == G_FILE_MONITOR_EVENT_DELETED ||
++ event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT))
++ {
++ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
++ "Reloading rules");
++ reload_scripts (authority);
++ }
++ g_free (name);
++ }
++}
++
++
++static void
++setup_file_monitors (PolkitBackendJsAuthority *authority)
++{
++ guint n;
++ GPtrArray *p;
++
++ p = g_ptr_array_new ();
++ for (n = 0; authority->priv->rules_dirs != NULL && authority->priv->rules_dirs[n] != NULL; n++)
++ {
++ GFile *file;
++ GError *error;
++ GFileMonitor *monitor;
++
++ file = g_file_new_for_path (authority->priv->rules_dirs[n]);
++ error = NULL;
++ monitor = g_file_monitor_directory (file,
++ G_FILE_MONITOR_NONE,
++ NULL,
++ &error);
++ g_object_unref (file);
++ if (monitor == NULL)
++ {
++ g_warning ("Error monitoring directory %s: %s",
++ authority->priv->rules_dirs[n],
++ error->message);
++ g_clear_error (&error);
++ }
++ else
++ {
++ g_signal_connect (monitor,
++ "changed",
++ G_CALLBACK (on_dir_monitor_changed),
++ authority);
++ g_ptr_array_add (p, monitor);
++ }
++ }
++ g_ptr_array_add (p, NULL);
++ authority->priv->dir_monitors = (GFileMonitor**) g_ptr_array_free (p, FALSE);
++}
++
++static duk_ret_t js_polkit_log (duk_context *cx);
++static duk_ret_t js_polkit_spawn (duk_context *cx);
++static duk_ret_t js_polkit_user_is_in_netgroup (duk_context *cx);
++
++static const duk_function_list_entry js_polkit_functions[] =
++{
++ { "log", js_polkit_log, 1 },
++ { "spawn", js_polkit_spawn, 1 },
++ { "_userIsInNetGroup", js_polkit_user_is_in_netgroup, 2 },
++ { NULL, NULL, 0 },
++};
++
++static void
++polkit_backend_js_authority_constructed (GObject *object)
++{
++ PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (object);
++ duk_context *cx;
++
++ cx = duk_create_heap (NULL, NULL, NULL, authority, NULL);
++ if (cx == NULL)
++ goto fail;
++
++ authority->priv->cx = cx;
++
++ duk_push_global_object (cx);
++ duk_push_object (cx);
++ duk_put_function_list (cx, -1, js_polkit_functions);
++ duk_put_prop_string (cx, -2, "polkit");
++
++ duk_eval_string (cx, init_js);
++
++ if (authority->priv->rules_dirs == NULL)
++ {
++ authority->priv->rules_dirs = g_new0 (gchar *, 3);
++ authority->priv->rules_dirs[0] = g_strdup (PACKAGE_SYSCONF_DIR "/polkit-1/rules.d");
++ authority->priv->rules_dirs[1] = g_strdup (PACKAGE_DATA_DIR "/polkit-1/rules.d");
++ }
++
++ setup_file_monitors (authority);
++ load_scripts (authority);
++
++ G_OBJECT_CLASS (polkit_backend_js_authority_parent_class)->constructed (object);
++ return;
++
++ fail:
++ g_critical ("Error initializing JavaScript environment");
++ g_assert_not_reached ();
++}
++
++static void
++polkit_backend_js_authority_finalize (GObject *object)
++{
++ PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (object);
++ guint n;
++
++ for (n = 0; authority->priv->dir_monitors != NULL && authority->priv->dir_monitors[n] != NULL; n++)
++ {
++ GFileMonitor *monitor = authority->priv->dir_monitors[n];
++ g_signal_handlers_disconnect_by_func (monitor,
++ G_CALLBACK (on_dir_monitor_changed),
++ authority);
++ g_object_unref (monitor);
++ }
++ g_free (authority->priv->dir_monitors);
++ g_strfreev (authority->priv->rules_dirs);
++
++ duk_destroy_heap (authority->priv->cx);
++
++ G_OBJECT_CLASS (polkit_backend_js_authority_parent_class)->finalize (object);
++}
++
++static void
++polkit_backend_js_authority_set_property (GObject *object,
++ guint property_id,
++ const GValue *value,
++ GParamSpec *pspec)
++{
++ PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (object);
++
++ switch (property_id)
++ {
++ case PROP_RULES_DIRS:
++ g_assert (authority->priv->rules_dirs == NULL);
++ authority->priv->rules_dirs = (gchar **) g_value_dup_boxed (value);
++ break;
++
++ default:
++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
++ break;
++ }
++}
++
++static const gchar *
++polkit_backend_js_authority_get_name (PolkitBackendAuthority *authority)
++{
++ return "js";
++}
++
++static const gchar *
++polkit_backend_js_authority_get_version (PolkitBackendAuthority *authority)
++{
++ return PACKAGE_VERSION;
++}
++
++static PolkitAuthorityFeatures
++polkit_backend_js_authority_get_features (PolkitBackendAuthority *authority)
++{
++ return POLKIT_AUTHORITY_FEATURES_TEMPORARY_AUTHORIZATION;
++}
++
++static void
++polkit_backend_js_authority_class_init (PolkitBackendJsAuthorityClass *klass)
++{
++ GObjectClass *gobject_class;
++ PolkitBackendAuthorityClass *authority_class;
++ PolkitBackendInteractiveAuthorityClass *interactive_authority_class;
++
++
++ gobject_class = G_OBJECT_CLASS (klass);
++ gobject_class->finalize = polkit_backend_js_authority_finalize;
++ gobject_class->set_property = polkit_backend_js_authority_set_property;
++ gobject_class->constructed = polkit_backend_js_authority_constructed;
++
++ authority_class = POLKIT_BACKEND_AUTHORITY_CLASS (klass);
++ authority_class->get_name = polkit_backend_js_authority_get_name;
++ authority_class->get_version = polkit_backend_js_authority_get_version;
++ authority_class->get_features = polkit_backend_js_authority_get_features;
++
++ interactive_authority_class = POLKIT_BACKEND_INTERACTIVE_AUTHORITY_CLASS (klass);
++ interactive_authority_class->get_admin_identities = polkit_backend_js_authority_get_admin_auth_identities;
++ interactive_authority_class->check_authorization_sync = polkit_backend_js_authority_check_authorization_sync;
++
++ g_object_class_install_property (gobject_class,
++ PROP_RULES_DIRS,
++ g_param_spec_boxed ("rules-dirs",
++ NULL,
++ NULL,
++ G_TYPE_STRV,
++ G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
++
++
++ g_type_class_add_private (klass, sizeof (PolkitBackendJsAuthorityPrivate));
++}
++
++/* ---------------------------------------------------------------------------------------------------- */
++
++static void
++set_property_str (duk_context *cx,
++ const gchar *name,
++ const gchar *value)
++{
++ duk_push_string (cx, value);
++ duk_put_prop_string (cx, -2, name);
++}
++
++static void
++set_property_strv (duk_context *cx,
++ const gchar *name,
++ GPtrArray *value)
++{
++ guint n;
++ duk_push_array (cx);
++ for (n = 0; n < value->len; n++)
++ {
++ duk_push_string (cx, g_ptr_array_index (value, n));
++ duk_put_prop_index (cx, -2, n);
++ }
++ duk_put_prop_string (cx, -2, name);
++}
++
++static void
++set_property_int32 (duk_context *cx,
++ const gchar *name,
++ gint32 value)
++{
++ duk_push_int (cx, value);
++ duk_put_prop_string (cx, -2, name);
++}
++
++static void
++set_property_bool (duk_context *cx,
++ const char *name,
++ gboolean value)
++{
++ duk_push_boolean (cx, value);
++ duk_put_prop_string (cx, -2, name);
++}
++
++/* ---------------------------------------------------------------------------------------------------- */
++
++static gboolean
++push_subject (duk_context *cx,
++ PolkitSubject *subject,
++ PolkitIdentity *user_for_subject,
++ gboolean subject_is_local,
++ gboolean subject_is_active,
++ GError **error)
++{
++ gboolean ret = FALSE;
++ pid_t pid;
++ uid_t uid;
++ gchar *user_name = NULL;
++ GPtrArray *groups = NULL;
++ struct passwd *passwd;
++ char *seat_str = NULL;
++ char *session_str = NULL;
++
++ duk_get_global_string (cx, "Subject");
++ duk_new (cx, 0);
++
++ if (POLKIT_IS_UNIX_PROCESS (subject))
++ {
++ pid = polkit_unix_process_get_pid (POLKIT_UNIX_PROCESS (subject));
++ }
++ else if (POLKIT_IS_SYSTEM_BUS_NAME (subject))
++ {
++ PolkitSubject *process;
++ process = polkit_system_bus_name_get_process_sync (POLKIT_SYSTEM_BUS_NAME (subject), NULL, error);
++ if (process == NULL)
++ goto out;
++ pid = polkit_unix_process_get_pid (POLKIT_UNIX_PROCESS (process));
++ g_object_unref (process);
++ }
++ else
++ {
++ g_assert_not_reached ();
++ }
++
++#ifdef HAVE_LIBSYSTEMD
++ if (sd_pid_get_session (pid, &session_str) == 0)
++ {
++ if (sd_session_get_seat (session_str, &seat_str) == 0)
++ {
++ /* do nothing */
++ }
++ }
++#endif /* HAVE_LIBSYSTEMD */
++
++ g_assert (POLKIT_IS_UNIX_USER (user_for_subject));
++ uid = polkit_unix_user_get_uid (POLKIT_UNIX_USER (user_for_subject));
++
++ groups = g_ptr_array_new_with_free_func (g_free);
++
++ passwd = getpwuid (uid);
++ if (passwd == NULL)
++ {
++ user_name = g_strdup_printf ("%d", (gint) uid);
++ g_warning ("Error looking up info for uid %d: %m", (gint) uid);
++ }
++ else
++ {
++ gid_t gids[512];
++ int num_gids = 512;
++
++ user_name = g_strdup (passwd->pw_name);
++
++ if (getgrouplist (passwd->pw_name,
++ passwd->pw_gid,
++ gids,
++ &num_gids) < 0)
++ {
++ g_warning ("Error looking up groups for uid %d: %m", (gint) uid);
++ }
++ else
++ {
++ gint n;
++ for (n = 0; n < num_gids; n++)
++ {
++ struct group *group;
++ group = getgrgid (gids[n]);
++ if (group == NULL)
++ {
++ g_ptr_array_add (groups, g_strdup_printf ("%d", (gint) gids[n]));
++ }
++ else
++ {
++ g_ptr_array_add (groups, g_strdup (group->gr_name));
++ }
++ }
++ }
++ }
++
++ set_property_int32 (cx, "pid", pid);
++ set_property_str (cx, "user", user_name);
++ set_property_strv (cx, "groups", groups);
++ set_property_str (cx, "seat", seat_str);
++ set_property_str (cx, "session", session_str);
++ set_property_bool (cx, "local", subject_is_local);
++ set_property_bool (cx, "active", subject_is_active);
++
++ ret = TRUE;
++
++ out:
++ free (session_str);
++ free (seat_str);
++ g_free (user_name);
++ if (groups != NULL)
++ g_ptr_array_unref (groups);
++
++ return ret;
++}
++
++/* ---------------------------------------------------------------------------------------------------- */
++
++static gboolean
++push_action_and_details (duk_context *cx,
++ const gchar *action_id,
++ PolkitDetails *details,
++ GError **error)
++{
++ gchar **keys;
++ guint n;
++
++ duk_get_global_string (cx, "Action");
++ duk_new (cx, 0);
++
++ set_property_str (cx, "id", action_id);
++
++ keys = polkit_details_get_keys (details);
++ for (n = 0; keys != NULL && keys[n] != NULL; n++)
++ {
++ gchar *key;
++ const gchar *value;
++ key = g_strdup_printf ("_detail_%s", keys[n]);
++ value = polkit_details_lookup (details, keys[n]);
++ set_property_str (cx, key, value);
++ g_free (key);
++ }
++ g_strfreev (keys);
++
++ return TRUE;
++}
++
++/* ---------------------------------------------------------------------------------------------------- */
++
++/* ---------------------------------------------------------------------------------------------------- */
++
++static GList *
++polkit_backend_js_authority_get_admin_auth_identities (PolkitBackendInteractiveAuthority *_authority,
++ PolkitSubject *caller,
++ PolkitSubject *subject,
++ PolkitIdentity *user_for_subject,
++ gboolean subject_is_local,
++ gboolean subject_is_active,
++ const gchar *action_id,
++ PolkitDetails *details)
++{
++ PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (_authority);
++ GList *ret = NULL;
++ guint n;
++ GError *error = NULL;
++ const char *ret_str = NULL;
++ gchar **ret_strs = NULL;
++ duk_context *cx = authority->priv->cx;
++
++ duk_set_top (cx, 0);
++ duk_get_global_string (cx, "polkit");
++ duk_push_string (cx, "_runAdminRules");
++
++ if (!push_action_and_details (cx, action_id, details, &error))
++ {
++ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
++ "Error converting action and details to JS object: %s",
++ error->message);
++ g_clear_error (&error);
++ goto out;
++ }
++
++ if (!push_subject (cx, subject, user_for_subject, subject_is_local, subject_is_active, &error))
++ {
++ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
++ "Error converting subject to JS object: %s",
++ error->message);
++ g_clear_error (&error);
++ goto out;
++ }
++
++ if (duk_pcall_prop (cx, 0, 2) != DUK_ERR_NONE)
++ {
++ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
++ "Error evaluating admin rules: ",
++ duk_safe_to_string (cx, -1));
++ goto out;
++ }
++
++ ret_str = duk_require_string (cx, -1);
++
++ ret_strs = g_strsplit (ret_str, ",", -1);
++ for (n = 0; ret_strs != NULL && ret_strs[n] != NULL; n++)
++ {
++ const gchar *identity_str = ret_strs[n];
++ PolkitIdentity *identity;
++
++ error = NULL;
++ identity = polkit_identity_from_string (identity_str, &error);
++ if (identity == NULL)
++ {
++ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
++ "Identity `%s' is not valid, ignoring: %s",
++ identity_str, error->message);
++ g_clear_error (&error);
++ }
++ else
++ {
++ ret = g_list_prepend (ret, identity);
++ }
++ }
++ ret = g_list_reverse (ret);
++
++ out:
++ g_strfreev (ret_strs);
++ /* fallback to root password auth */
++ if (ret == NULL)
++ ret = g_list_prepend (ret, polkit_unix_user_new (0));
++
++ return ret;
++}
++
++/* ---------------------------------------------------------------------------------------------------- */
++
++static PolkitImplicitAuthorization
++polkit_backend_js_authority_check_authorization_sync (PolkitBackendInteractiveAuthority *_authority,
++ PolkitSubject *caller,
++ PolkitSubject *subject,
++ PolkitIdentity *user_for_subject,
++ gboolean subject_is_local,
++ gboolean subject_is_active,
++ const gchar *action_id,
++ PolkitDetails *details,
++ PolkitImplicitAuthorization implicit)
++{
++ PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (_authority);
++ PolkitImplicitAuthorization ret = implicit;
++ GError *error = NULL;
++ gchar *ret_str = NULL;
++ gboolean good = FALSE;
++ duk_context *cx = authority->priv->cx;
++
++ duk_set_top (cx, 0);
++ duk_get_global_string (cx, "polkit");
++ duk_push_string (cx, "_runRules");
++
++ if (!push_action_and_details (cx, action_id, details, &error))
++ {
++ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
++ "Error converting action and details to JS object: %s",
++ error->message);
++ g_clear_error (&error);
++ goto out;
++ }
++
++ if (!push_subject (cx, subject, user_for_subject, subject_is_local, subject_is_active, &error))
++ {
++ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
++ "Error converting subject to JS object: %s",
++ error->message);
++ g_clear_error (&error);
++ goto out;
++ }
++
++ if (duk_pcall_prop (cx, 0, 2) != DUK_ERR_NONE)
++ {
++ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
++ "Error evaluating authorization rules: ",
++ duk_safe_to_string (cx, -1));
++ goto out;
++ }
++
++ if (duk_is_null(cx, -1)) {
++ good = TRUE;
++ goto out;
++ }
++ ret_str = g_strdup (duk_require_string (cx, -1));
++ if (!polkit_implicit_authorization_from_string (ret_str, &ret))
++ {
++ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
++ "Returned result `%s' is not valid",
++ ret_str);
++ goto out;
++ }
++
++ good = TRUE;
++
++ out:
++ if (!good)
++ ret = POLKIT_IMPLICIT_AUTHORIZATION_NOT_AUTHORIZED;
++ g_free (ret_str);
++
++ return ret;
++}
++
++/* ---------------------------------------------------------------------------------------------------- */
++
++static duk_ret_t
++js_polkit_log (duk_context *cx)
++{
++ const char *str = duk_require_string (cx, 0);
++ fprintf (stderr, "%s\n", str);
++ return 0;
++}
++
++/* ---------------------------------------------------------------------------------------------------- */
++
++static const gchar *
++get_signal_name (gint signal_number)
++{
++ switch (signal_number)
++ {
++#define _HANDLE_SIG(sig) case sig: return #sig;
++ _HANDLE_SIG (SIGHUP);
++ _HANDLE_SIG (SIGINT);
++ _HANDLE_SIG (SIGQUIT);
++ _HANDLE_SIG (SIGILL);
++ _HANDLE_SIG (SIGABRT);
++ _HANDLE_SIG (SIGFPE);
++ _HANDLE_SIG (SIGKILL);
++ _HANDLE_SIG (SIGSEGV);
++ _HANDLE_SIG (SIGPIPE);
++ _HANDLE_SIG (SIGALRM);
++ _HANDLE_SIG (SIGTERM);
++ _HANDLE_SIG (SIGUSR1);
++ _HANDLE_SIG (SIGUSR2);
++ _HANDLE_SIG (SIGCHLD);
++ _HANDLE_SIG (SIGCONT);
++ _HANDLE_SIG (SIGSTOP);
++ _HANDLE_SIG (SIGTSTP);
++ _HANDLE_SIG (SIGTTIN);
++ _HANDLE_SIG (SIGTTOU);
++ _HANDLE_SIG (SIGBUS);
++#ifdef SIGPOLL
++ _HANDLE_SIG (SIGPOLL);
++#endif
++ _HANDLE_SIG (SIGPROF);
++ _HANDLE_SIG (SIGSYS);
++ _HANDLE_SIG (SIGTRAP);
++ _HANDLE_SIG (SIGURG);
++ _HANDLE_SIG (SIGVTALRM);
++ _HANDLE_SIG (SIGXCPU);
++ _HANDLE_SIG (SIGXFSZ);
++#undef _HANDLE_SIG
++ default:
++ break;
++ }
++ return "UNKNOWN_SIGNAL";
++}
++
++typedef struct
++{
++ GMainLoop *loop;
++ GAsyncResult *res;
++} SpawnData;
++
++static void
++spawn_cb (GObject *source_object,
++ GAsyncResult *res,
++ gpointer user_data)
++{
++ SpawnData *data = user_data;
++ data->res = g_object_ref (res);
++ g_main_loop_quit (data->loop);
++}
++
++static duk_ret_t
++js_polkit_spawn (duk_context *cx)
++{
++#if (DUK_VERSION >= 20000)
++ duk_ret_t ret = DUK_RET_ERROR;
++#else
++ duk_ret_t ret = DUK_RET_INTERNAL_ERROR;
++#endif
++ gchar *standard_output = NULL;
++ gchar *standard_error = NULL;
++ gint exit_status;
++ GError *error = NULL;
++ guint32 array_len;
++ gchar **argv = NULL;
++ GMainContext *context = NULL;
++ GMainLoop *loop = NULL;
++ SpawnData data = {0};
++ char *err_str = NULL;
++ guint n;
++
++ if (!duk_is_array (cx, 0))
++ goto out;
++
++ array_len = duk_get_length (cx, 0);
++
++ argv = g_new0 (gchar*, array_len + 1);
++ for (n = 0; n < array_len; n++)
++ {
++ duk_get_prop_index (cx, 0, n);
++ argv[n] = g_strdup (duk_to_string (cx, -1));
++ duk_pop (cx);
++ }
++
++ context = g_main_context_new ();
++ loop = g_main_loop_new (context, FALSE);
++
++ g_main_context_push_thread_default (context);
++
++ data.loop = loop;
++ utils_spawn ((const gchar *const *) argv,
++ 10, /* timeout_seconds */
++ NULL, /* cancellable */
++ spawn_cb,
++ &data);
++
++ g_main_loop_run (loop);
++
++ g_main_context_pop_thread_default (context);
++
++ if (!utils_spawn_finish (data.res,
++ &exit_status,
++ &standard_output,
++ &standard_error,
++ &error))
++ {
++ err_str = g_strdup_printf ("Error spawning helper: %s (%s, %d)",
++ error->message, g_quark_to_string (error->domain), error->code);
++ g_clear_error (&error);
++ goto out;
++ }
++
++ if (!(WIFEXITED (exit_status) && WEXITSTATUS (exit_status) == 0))
++ {
++ GString *gstr;
++ gstr = g_string_new (NULL);
++ if (WIFEXITED (exit_status))
++ {
++ g_string_append_printf (gstr,
++ "Helper exited with non-zero exit status %d",
++ WEXITSTATUS (exit_status));
++ }
++ else if (WIFSIGNALED (exit_status))
++ {
++ g_string_append_printf (gstr,
++ "Helper was signaled with signal %s (%d)",
++ get_signal_name (WTERMSIG (exit_status)),
++ WTERMSIG (exit_status));
++ }
++ g_string_append_printf (gstr, ", stdout=`%s', stderr=`%s'",
++ standard_output, standard_error);
++ err_str = g_string_free (gstr, FALSE);
++ goto out;
++ }
++
++ duk_push_string (cx, standard_output);
++ ret = 1;
++
++ out:
++ g_strfreev (argv);
++ g_free (standard_output);
++ g_free (standard_error);
++ g_clear_object (&data.res);
++ if (loop != NULL)
++ g_main_loop_unref (loop);
++ if (context != NULL)
++ g_main_context_unref (context);
++
++ if (err_str)
++ duk_error (cx, DUK_ERR_ERROR, err_str);
++
++ return ret;
++}
++
++/* ---------------------------------------------------------------------------------------------------- */
++
++
++static duk_ret_t
++js_polkit_user_is_in_netgroup (duk_context *cx)
++{
++ const char *user;
++ const char *netgroup;
++ gboolean is_in_netgroup = FALSE;
++
++ user = duk_require_string (cx, 0);
++ netgroup = duk_require_string (cx, 1);
++
++ if (innetgr (netgroup,
++ NULL, /* host */
++ user,
++ NULL)) /* domain */
++ {
++ is_in_netgroup = TRUE;
++ }
++
++ duk_push_boolean (cx, is_in_netgroup);
++ return 1;
++}
++
++/* ---------------------------------------------------------------------------------------------------- */
++
++typedef struct
++{
++ GSimpleAsyncResult *simple; /* borrowed reference */
++ GMainContext *main_context; /* may be NULL */
++
++ GCancellable *cancellable; /* may be NULL */
++ gulong cancellable_handler_id;
++
++ GPid child_pid;
++ gint child_stdout_fd;
++ gint child_stderr_fd;
++
++ GIOChannel *child_stdout_channel;
++ GIOChannel *child_stderr_channel;
++
++ GSource *child_watch_source;
++ GSource *child_stdout_source;
++ GSource *child_stderr_source;
++
++ guint timeout_seconds;
++ gboolean timed_out;
++ GSource *timeout_source;
++
++ GString *child_stdout;
++ GString *child_stderr;
++
++ gint exit_status;
++} UtilsSpawnData;
++
++static void
++utils_child_watch_from_release_cb (GPid pid,
++ gint status,
++ gpointer user_data)
++{
++}
++
++static void
++utils_spawn_data_free (UtilsSpawnData *data)
++{
++ if (data->timeout_source != NULL)
++ {
++ g_source_destroy (data->timeout_source);
++ data->timeout_source = NULL;
++ }
++
++ /* Nuke the child, if necessary */
++ if (data->child_watch_source != NULL)
++ {
++ g_source_destroy (data->child_watch_source);
++ data->child_watch_source = NULL;
++ }
++
++ if (data->child_pid != 0)
++ {
++ GSource *source;
++ kill (data->child_pid, SIGTERM);
++ /* OK, we need to reap for the child ourselves - we don't want
++ * to use waitpid() because that might block the calling
++ * thread (the child might handle SIGTERM and use several
++ * seconds for cleanup/rollback).
++ *
++ * So we use GChildWatch instead.
++ *
++ * Avoid taking a references to ourselves. but note that we need
++ * to pass the GSource so we can nuke it once handled.
++ */
++ source = g_child_watch_source_new (data->child_pid);
++ g_source_set_callback (source,
++ (GSourceFunc) utils_child_watch_from_release_cb,
++ source,
++ (GDestroyNotify) g_source_destroy);
++ g_source_attach (source, data->main_context);
++ g_source_unref (source);
++ data->child_pid = 0;
++ }
++
++ if (data->child_stdout != NULL)
++ {
++ g_string_free (data->child_stdout, TRUE);
++ data->child_stdout = NULL;
++ }
++
++ if (data->child_stderr != NULL)
++ {
++ g_string_free (data->child_stderr, TRUE);
++ data->child_stderr = NULL;
++ }
++
++ if (data->child_stdout_channel != NULL)
++ {
++ g_io_channel_unref (data->child_stdout_channel);
++ data->child_stdout_channel = NULL;
++ }
++ if (data->child_stderr_channel != NULL)
++ {
++ g_io_channel_unref (data->child_stderr_channel);
++ data->child_stderr_channel = NULL;
++ }
++
++ if (data->child_stdout_source != NULL)
++ {
++ g_source_destroy (data->child_stdout_source);
++ data->child_stdout_source = NULL;
++ }
++ if (data->child_stderr_source != NULL)
++ {
++ g_source_destroy (data->child_stderr_source);
++ data->child_stderr_source = NULL;
++ }
++
++ if (data->child_stdout_fd != -1)
++ {
++ g_warn_if_fail (close (data->child_stdout_fd) == 0);
++ data->child_stdout_fd = -1;
++ }
++ if (data->child_stderr_fd != -1)
++ {
++ g_warn_if_fail (close (data->child_stderr_fd) == 0);
++ data->child_stderr_fd = -1;
++ }
++
++ if (data->cancellable_handler_id > 0)
++ {
++ g_cancellable_disconnect (data->cancellable, data->cancellable_handler_id);
++ data->cancellable_handler_id = 0;
++ }
++
++ if (data->main_context != NULL)
++ g_main_context_unref (data->main_context);
++
++ if (data->cancellable != NULL)
++ g_object_unref (data->cancellable);
++
++ g_slice_free (UtilsSpawnData, data);
++}
++
++/* called in the thread where @cancellable was cancelled */
++static void
++utils_on_cancelled (GCancellable *cancellable,
++ gpointer user_data)
++{
++ UtilsSpawnData *data = user_data;
++ GError *error;
++
++ error = NULL;
++ g_warn_if_fail (g_cancellable_set_error_if_cancelled (cancellable, &error));
++ g_simple_async_result_take_error (data->simple, error);
++ g_simple_async_result_complete_in_idle (data->simple);
++ g_object_unref (data->simple);
++}
++
++static gboolean
++utils_read_child_stderr (GIOChannel *channel,
++ GIOCondition condition,
++ gpointer user_data)
++{
++ UtilsSpawnData *data = user_data;
++ gchar buf[1024];
++ gsize bytes_read;
++
++ g_io_channel_read_chars (channel, buf, sizeof buf, &bytes_read, NULL);
++ g_string_append_len (data->child_stderr, buf, bytes_read);
++ return TRUE;
++}
++
++static gboolean
++utils_read_child_stdout (GIOChannel *channel,
++ GIOCondition condition,
++ gpointer user_data)
++{
++ UtilsSpawnData *data = user_data;
++ gchar buf[1024];
++ gsize bytes_read;
++
++ g_io_channel_read_chars (channel, buf, sizeof buf, &bytes_read, NULL);
++ g_string_append_len (data->child_stdout, buf, bytes_read);
++ return TRUE;
++}
++
++static void
++utils_child_watch_cb (GPid pid,
++ gint status,
++ gpointer user_data)
++{
++ UtilsSpawnData *data = user_data;
++ gchar *buf;
++ gsize buf_size;
++
++ if (g_io_channel_read_to_end (data->child_stdout_channel, &buf, &buf_size, NULL) == G_IO_STATUS_NORMAL)
++ {
++ g_string_append_len (data->child_stdout, buf, buf_size);
++ g_free (buf);
++ }
++ if (g_io_channel_read_to_end (data->child_stderr_channel, &buf, &buf_size, NULL) == G_IO_STATUS_NORMAL)
++ {
++ g_string_append_len (data->child_stderr, buf, buf_size);
++ g_free (buf);
++ }
++
++ data->exit_status = status;
++
++ /* ok, child watch is history, make sure we don't free it in spawn_data_free() */
++ data->child_pid = 0;
++ data->child_watch_source = NULL;
++
++ /* we're done */
++ g_simple_async_result_complete_in_idle (data->simple);
++ g_object_unref (data->simple);
++}
++
++static gboolean
++utils_timeout_cb (gpointer user_data)
++{
++ UtilsSpawnData *data = user_data;
++
++ data->timed_out = TRUE;
++
++ /* ok, timeout is history, make sure we don't free it in spawn_data_free() */
++ data->timeout_source = NULL;
++
++ /* we're done */
++ g_simple_async_result_complete_in_idle (data->simple);
++ g_object_unref (data->simple);
++
++ return FALSE; /* remove source */
++}
++
++static void
++utils_spawn (const gchar *const *argv,
++ guint timeout_seconds,
++ GCancellable *cancellable,
++ GAsyncReadyCallback callback,
++ gpointer user_data)
++{
++ UtilsSpawnData *data;
++ GError *error;
++
++ data = g_slice_new0 (UtilsSpawnData);
++ data->timeout_seconds = timeout_seconds;
++ data->simple = g_simple_async_result_new (NULL,
++ callback,
++ user_data,
++ utils_spawn);
++ data->main_context = g_main_context_get_thread_default ();
++ if (data->main_context != NULL)
++ g_main_context_ref (data->main_context);
++
++ data->cancellable = cancellable != NULL ? g_object_ref (cancellable) : NULL;
++
++ data->child_stdout = g_string_new (NULL);
++ data->child_stderr = g_string_new (NULL);
++ data->child_stdout_fd = -1;
++ data->child_stderr_fd = -1;
++
++ /* the life-cycle of UtilsSpawnData is tied to its GSimpleAsyncResult */
++ g_simple_async_result_set_op_res_gpointer (data->simple, data, (GDestroyNotify) utils_spawn_data_free);
++
++ error = NULL;
++ if (data->cancellable != NULL)
++ {
++ /* could already be cancelled */
++ error = NULL;
++ if (g_cancellable_set_error_if_cancelled (data->cancellable, &error))
++ {
++ g_simple_async_result_take_error (data->simple, error);
++ g_simple_async_result_complete_in_idle (data->simple);
++ g_object_unref (data->simple);
++ goto out;
++ }
++
++ data->cancellable_handler_id = g_cancellable_connect (data->cancellable,
++ G_CALLBACK (utils_on_cancelled),
++ data,
++ NULL);
++ }
++
++ error = NULL;
++ if (!g_spawn_async_with_pipes (NULL, /* working directory */
++ (gchar **) argv,
++ NULL, /* envp */
++ G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
++ NULL, /* child_setup */
++ NULL, /* child_setup's user_data */
++ &(data->child_pid),
++ NULL, /* gint *stdin_fd */
++ &(data->child_stdout_fd),
++ &(data->child_stderr_fd),
++ &error))
++ {
++ g_prefix_error (&error, "Error spawning: ");
++ g_simple_async_result_take_error (data->simple, error);
++ g_simple_async_result_complete_in_idle (data->simple);
++ g_object_unref (data->simple);
++ goto out;
++ }
++
++ if (timeout_seconds > 0)
++ {
++ data->timeout_source = g_timeout_source_new_seconds (timeout_seconds);
++ g_source_set_priority (data->timeout_source, G_PRIORITY_DEFAULT);
++ g_source_set_callback (data->timeout_source, utils_timeout_cb, data, NULL);
++ g_source_attach (data->timeout_source, data->main_context);
++ g_source_unref (data->timeout_source);
++ }
++
++ data->child_watch_source = g_child_watch_source_new (data->child_pid);
++ g_source_set_callback (data->child_watch_source, (GSourceFunc) utils_child_watch_cb, data, NULL);
++ g_source_attach (data->child_watch_source, data->main_context);
++ g_source_unref (data->child_watch_source);
++
++ data->child_stdout_channel = g_io_channel_unix_new (data->child_stdout_fd);
++ g_io_channel_set_flags (data->child_stdout_channel, G_IO_FLAG_NONBLOCK, NULL);
++ data->child_stdout_source = g_io_create_watch (data->child_stdout_channel, G_IO_IN);
++ g_source_set_callback (data->child_stdout_source, (GSourceFunc) utils_read_child_stdout, data, NULL);
++ g_source_attach (data->child_stdout_source, data->main_context);
++ g_source_unref (data->child_stdout_source);
++
++ data->child_stderr_channel = g_io_channel_unix_new (data->child_stderr_fd);
++ g_io_channel_set_flags (data->child_stderr_channel, G_IO_FLAG_NONBLOCK, NULL);
++ data->child_stderr_source = g_io_create_watch (data->child_stderr_channel, G_IO_IN);
++ g_source_set_callback (data->child_stderr_source, (GSourceFunc) utils_read_child_stderr, data, NULL);
++ g_source_attach (data->child_stderr_source, data->main_context);
++ g_source_unref (data->child_stderr_source);
++
++ out:
++ ;
++}
++
++gboolean
++utils_spawn_finish (GAsyncResult *res,
++ gint *out_exit_status,
++ gchar **out_standard_output,
++ gchar **out_standard_error,
++ GError **error)
++{
++ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
++ UtilsSpawnData *data;
++ gboolean ret = FALSE;
++
++ g_return_val_if_fail (G_IS_ASYNC_RESULT (res), FALSE);
++ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
++
++ g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == utils_spawn);
++
++ if (g_simple_async_result_propagate_error (simple, error))
++ goto out;
++
++ data = g_simple_async_result_get_op_res_gpointer (simple);
++
++ if (data->timed_out)
++ {
++ g_set_error (error,
++ G_IO_ERROR,
++ G_IO_ERROR_TIMED_OUT,
++ "Timed out after %d seconds",
++ data->timeout_seconds);
++ goto out;
++ }
++
++ if (out_exit_status != NULL)
++ *out_exit_status = data->exit_status;
++
++ if (out_standard_output != NULL)
++ *out_standard_output = g_strdup (data->child_stdout->str);
++
++ if (out_standard_error != NULL)
++ *out_standard_error = g_strdup (data->child_stderr->str);
++
++ ret = TRUE;
++
++ out:
++ return ret;
++}
+--
+GitLab
+
+
+From d74aad8152a7c51999fffa9abe28e4306a052399 Mon Sep 17 00:00:00 2001
+From: Wu Xiaotian
+Date: Sun, 22 Nov 2020 13:15:17 +0800
+Subject: [PATCH 02/17] check netgroup.h header file
+
+Signed-off-by: Gustavo Lima Chaves
+---
+ src/polkitbackend/polkitbackendduktapeauthority.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/src/polkitbackend/polkitbackendduktapeauthority.c b/src/polkitbackend/polkitbackendduktapeauthority.c
+index ae98453..543d6fd 100644
+--- a/src/polkitbackend/polkitbackendduktapeauthority.c
++++ b/src/polkitbackend/polkitbackendduktapeauthority.c
+@@ -26,7 +26,11 @@
+ #include
+ #include
+ #include
++#ifdef HAVE_NETGROUP_H
++#include
++#else
+ #include
++#endif
+ #include
+ #include
+ #include
+--
+GitLab
+
+
+From 69c761506cbe458807e4ae2742c9e05bc60dad3d Mon Sep 17 00:00:00 2001
+From: Wu Xiaotian
+Date: Sun, 22 Nov 2020 10:59:03 +0800
+Subject: [PATCH 03/17] check return value
+
+Signed-off-by: Gustavo Lima Chaves
+---
+ src/polkitbackend/polkitbackendduktapeauthority.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/src/polkitbackend/polkitbackendduktapeauthority.c b/src/polkitbackend/polkitbackendduktapeauthority.c
+index 543d6fd..a54ed5b 100644
+--- a/src/polkitbackend/polkitbackendduktapeauthority.c
++++ b/src/polkitbackend/polkitbackendduktapeauthority.c
+@@ -249,7 +249,11 @@ reload_scripts (PolkitBackendJsAuthority *authority)
+ duk_context *cx = authority->priv->cx;
+
+ duk_set_top (cx, 0);
+- duk_get_global_string (cx, "polkit");
++ if (!duk_get_global_string (cx, "polkit")) {
++ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
++ "Error deleting old rules, not loading new ones");
++ return;
++ }
+ duk_push_string (cx, "_deleteRules");
+
+ duk_call_prop (cx, 0, 0);
+--
+GitLab
+
+
+From f1536c4899934fd3c8243fda2d084a472fe57d2e Mon Sep 17 00:00:00 2001
+From: Wu Xiaotian
+Date: Sun, 22 Nov 2020 11:22:39 +0800
+Subject: [PATCH 04/17] check return value
+
+Signed-off-by: Gustavo Lima Chaves
+---
+ src/polkitbackend/polkitbackendduktapeauthority.c | 12 ++++++++++--
+ 1 file changed, 10 insertions(+), 2 deletions(-)
+
+diff --git a/src/polkitbackend/polkitbackendduktapeauthority.c b/src/polkitbackend/polkitbackendduktapeauthority.c
+index a54ed5b..1a7e6d3 100644
+--- a/src/polkitbackend/polkitbackendduktapeauthority.c
++++ b/src/polkitbackend/polkitbackendduktapeauthority.c
+@@ -656,7 +656,10 @@ push_action_and_details (duk_context *cx,
+ gchar **keys;
+ guint n;
+
+- duk_get_global_string (cx, "Action");
++ if (!duk_get_global_string (cx, "Action")) {
++ return FALSE;
++ }
++
+ duk_new (cx, 0);
+
+ set_property_str (cx, "id", action_id);
+@@ -699,7 +702,12 @@ polkit_backend_js_authority_get_admin_auth_identities (PolkitBackendInteractiveA
+ duk_context *cx = authority->priv->cx;
+
+ duk_set_top (cx, 0);
+- duk_get_global_string (cx, "polkit");
++ if (!duk_get_global_string (cx, "polkit")) {
++ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
++ "Error deleting old rules, not loading new ones");
++ goto out;
++ }
++
+ duk_push_string (cx, "_runAdminRules");
+
+ if (!push_action_and_details (cx, action_id, details, &error))
+--
+GitLab
+
+
+From ca15eecf5dc7755947515c1bfc651fd8770aaf8f Mon Sep 17 00:00:00 2001
+From: Wu Xiaotian
+Date: Sun, 22 Nov 2020 13:17:16 +0800
+Subject: [PATCH 05/17] check return value
+
+Signed-off-by: Gustavo Lima Chaves
+---
+ src/polkitbackend/polkitbackendduktapeauthority.c | 10 ++++++++--
+ 1 file changed, 8 insertions(+), 2 deletions(-)
+
+diff --git a/src/polkitbackend/polkitbackendduktapeauthority.c b/src/polkitbackend/polkitbackendduktapeauthority.c
+index 1a7e6d3..3f1b32d 100644
+--- a/src/polkitbackend/polkitbackendduktapeauthority.c
++++ b/src/polkitbackend/polkitbackendduktapeauthority.c
+@@ -550,7 +550,10 @@ push_subject (duk_context *cx,
+ char *seat_str = NULL;
+ char *session_str = NULL;
+
+- duk_get_global_string (cx, "Subject");
++ if (!duk_get_global_string (cx, "Subject")) {
++ return FALSE;
++ }
++
+ duk_new (cx, 0);
+
+ if (POLKIT_IS_UNIX_PROCESS (subject))
+@@ -789,8 +792,11 @@ polkit_backend_js_authority_check_authorization_sync (PolkitBackendInteractiveAu
+ gboolean good = FALSE;
+ duk_context *cx = authority->priv->cx;
+
++ if (!duk_get_global_string (cx, "polkit")) {
++ goto out;
++ }
++
+ duk_set_top (cx, 0);
+- duk_get_global_string (cx, "polkit");
+ duk_push_string (cx, "_runRules");
+
+ if (!push_action_and_details (cx, action_id, details, &error))
+--
+GitLab
+
+
+From 870348365cc0166e14f28e0d144ed552bba4d794 Mon Sep 17 00:00:00 2001
+From: Wu Xiaotian
+Date: Sun, 22 Nov 2020 13:18:13 +0800
+Subject: [PATCH 06/17] check return value
+
+Signed-off-by: Gustavo Lima Chaves
+---
+ src/polkitbackend/polkitbackendduktapeauthority.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/src/polkitbackend/polkitbackendduktapeauthority.c b/src/polkitbackend/polkitbackendduktapeauthority.c
+index 3f1b32d..6294ad9 100644
+--- a/src/polkitbackend/polkitbackendduktapeauthority.c
++++ b/src/polkitbackend/polkitbackendduktapeauthority.c
+@@ -843,7 +843,8 @@ polkit_backend_js_authority_check_authorization_sync (PolkitBackendInteractiveAu
+ out:
+ if (!good)
+ ret = POLKIT_IMPLICIT_AUTHORIZATION_NOT_AUTHORIZED;
+- g_free (ret_str);
++ if (ret_str != NULL)
++ g_free (ret_str);
+
+ return ret;
+ }
+--
+GitLab
+
+
+From 81c916ff08fdcee3c7340c4b2d4632086b89666c Mon Sep 17 00:00:00 2001
+From: Wu Xiaotian
+Date: Sun, 22 Nov 2020 11:23:04 +0800
+Subject: [PATCH 07/17] fix typecase
+
+Signed-off-by: Gustavo Lima Chaves
+---
+ src/polkitbackend/polkitbackendduktapeauthority.c | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/src/polkitbackend/polkitbackendduktapeauthority.c b/src/polkitbackend/polkitbackendduktapeauthority.c
+index 6294ad9..d466c9d 100644
+--- a/src/polkitbackend/polkitbackendduktapeauthority.c
++++ b/src/polkitbackend/polkitbackendduktapeauthority.c
+@@ -1191,7 +1191,7 @@ static void
+ utils_on_cancelled (GCancellable *cancellable,
+ gpointer user_data)
+ {
+- UtilsSpawnData *data = user_data;
++ UtilsSpawnData *data = (UtilsSpawnData *)user_data;
+ GError *error;
+
+ error = NULL;
+@@ -1206,7 +1206,7 @@ utils_read_child_stderr (GIOChannel *channel,
+ GIOCondition condition,
+ gpointer user_data)
+ {
+- UtilsSpawnData *data = user_data;
++ UtilsSpawnData *data = (UtilsSpawnData *)user_data;
+ gchar buf[1024];
+ gsize bytes_read;
+
+@@ -1220,7 +1220,7 @@ utils_read_child_stdout (GIOChannel *channel,
+ GIOCondition condition,
+ gpointer user_data)
+ {
+- UtilsSpawnData *data = user_data;
++ UtilsSpawnData *data = (UtilsSpawnData *)user_data;
+ gchar buf[1024];
+ gsize bytes_read;
+
+@@ -1234,7 +1234,7 @@ utils_child_watch_cb (GPid pid,
+ gint status,
+ gpointer user_data)
+ {
+- UtilsSpawnData *data = user_data;
++ UtilsSpawnData *data = (UtilsSpawnData *)user_data;
+ gchar *buf;
+ gsize buf_size;
+
+@@ -1263,7 +1263,7 @@ utils_child_watch_cb (GPid pid,
+ static gboolean
+ utils_timeout_cb (gpointer user_data)
+ {
+- UtilsSpawnData *data = user_data;
++ UtilsSpawnData *data = (UtilsSpawnData *)user_data;
+
+ data->timed_out = TRUE;
+
+--
+GitLab
+
+
+From acb956bf52f0a78bf7aaf925876f96e97a146995 Mon Sep 17 00:00:00 2001
+From: Wu Xiaotian
+Date: Sun, 22 Nov 2020 18:04:27 +0800
+Subject: [PATCH 08/17] typecase
+
+Signed-off-by: Gustavo Lima Chaves
+---
+ src/polkitbackend/polkitbackendduktapeauthority.c | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/src/polkitbackend/polkitbackendduktapeauthority.c b/src/polkitbackend/polkitbackendduktapeauthority.c
+index d466c9d..237b1ad 100644
+--- a/src/polkitbackend/polkitbackendduktapeauthority.c
++++ b/src/polkitbackend/polkitbackendduktapeauthority.c
+@@ -915,8 +915,8 @@ spawn_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+ {
+- SpawnData *data = user_data;
+- data->res = g_object_ref (res);
++ SpawnData *data = (SpawnData *)user_data;
++ data->res = (GAsyncResult*)g_object_ref (res);
+ g_main_loop_quit (data->loop);
+ }
+
+@@ -1292,12 +1292,12 @@ utils_spawn (const gchar *const *argv,
+ data->simple = g_simple_async_result_new (NULL,
+ callback,
+ user_data,
+- utils_spawn);
++ (gpointer*)utils_spawn);
+ data->main_context = g_main_context_get_thread_default ();
+ if (data->main_context != NULL)
+ g_main_context_ref (data->main_context);
+
+- data->cancellable = cancellable != NULL ? g_object_ref (cancellable) : NULL;
++ data->cancellable = cancellable != NULL ? (GCancellable*)g_object_ref (cancellable) : NULL;
+
+ data->child_stdout = g_string_new (NULL);
+ data->child_stderr = g_string_new (NULL);
+@@ -1397,7 +1397,7 @@ utils_spawn_finish (GAsyncResult *res,
+ if (g_simple_async_result_propagate_error (simple, error))
+ goto out;
+
+- data = g_simple_async_result_get_op_res_gpointer (simple);
++ data = (UtilsSpawnData*)g_simple_async_result_get_op_res_gpointer (simple);
+
+ if (data->timed_out)
+ {
+--
+GitLab
+
+
+From be060e4d48aceb09af34868b555b6c73c7afdabb Mon Sep 17 00:00:00 2001
+From: Wu Xiaotian
+Date: Sun, 22 Nov 2020 13:53:23 +0800
+Subject: [PATCH 09/17] some change
+
+Signed-off-by: Gustavo Lima Chaves
+---
+ .../polkitbackendduktapeauthority.c | 26 +++++++++++--------
+ 1 file changed, 15 insertions(+), 11 deletions(-)
+
+diff --git a/src/polkitbackend/polkitbackendduktapeauthority.c b/src/polkitbackend/polkitbackendduktapeauthority.c
+index 237b1ad..fad9017 100644
+--- a/src/polkitbackend/polkitbackendduktapeauthority.c
++++ b/src/polkitbackend/polkitbackendduktapeauthority.c
+@@ -207,18 +207,22 @@ load_scripts (PolkitBackendJsAuthority *authority)
+
+ for (l = files; l != NULL; l = l->next)
+ {
+- const gchar *filename = l->data;
+-
++ const gchar *filename = (gchar *)l->data;
+ #if (DUK_VERSION >= 20000)
+- gchar *contents;
+- gsize length;
+- GError *error = NULL;
+- if (!g_file_get_contents (filename, &contents, &length, &error)){
+- g_warning("Error when file contents of %s: %s\n", filename, error->message);
+- g_error_free (error);
+- continue;
+- }
+- if (duk_peval_lstring_noresult(cx, contents,length) != 0)
++ GFile *file = g_file_new_for_path (filename);
++ char *contents;
++ gsize len;
++ if (!g_file_load_contents (file, NULL, &contents, &len, NULL, NULL))
++ {
++ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
++ "Error compiling script %s",
++ filename);
++ g_object_unref (file);
++ continue;
++ }
++
++ g_object_unref (file);
++ if (duk_peval_lstring_noresult(cx, contents,len) != 0)
+ #else
+ if (duk_peval_file_noresult (cx, filename) != 0)
+ #endif
+--
+GitLab
+
+
+From 2ffb62048a5ebedfe3bb053feb7385c7270ede28 Mon Sep 17 00:00:00 2001
+From: Wu Xiaotian
+Date: Sun, 22 Nov 2020 15:25:45 +0800
+Subject: [PATCH 10/17] some change
+
+Signed-off-by: Gustavo Lima Chaves
+---
+ .../polkitbackendduktapeauthority.c | 24 +++++++++----------
+ 1 file changed, 12 insertions(+), 12 deletions(-)
+
+diff --git a/src/polkitbackend/polkitbackendduktapeauthority.c b/src/polkitbackend/polkitbackendduktapeauthority.c
+index fad9017..6fac3be 100644
+--- a/src/polkitbackend/polkitbackendduktapeauthority.c
++++ b/src/polkitbackend/polkitbackendduktapeauthority.c
+@@ -125,6 +125,18 @@ G_DEFINE_TYPE (PolkitBackendJsAuthority, polkit_backend_js_authority, POLKIT_BAC
+
+ /* ---------------------------------------------------------------------------------------------------- */
+
++static duk_ret_t js_polkit_log (duk_context *cx);
++static duk_ret_t js_polkit_spawn (duk_context *cx);
++static duk_ret_t js_polkit_user_is_in_netgroup (duk_context *cx);
++
++static const duk_function_list_entry js_polkit_functions[] =
++{
++ { "log", js_polkit_log, 1 },
++ { "spawn", js_polkit_spawn, 1 },
++ { "_userIsInNetGroup", js_polkit_user_is_in_netgroup, 2 },
++ { NULL, NULL, 0 },
++};
++
+ static void
+ polkit_backend_js_authority_init (PolkitBackendJsAuthority *authority)
+ {
+@@ -347,18 +359,6 @@ setup_file_monitors (PolkitBackendJsAuthority *authority)
+ authority->priv->dir_monitors = (GFileMonitor**) g_ptr_array_free (p, FALSE);
+ }
+
+-static duk_ret_t js_polkit_log (duk_context *cx);
+-static duk_ret_t js_polkit_spawn (duk_context *cx);
+-static duk_ret_t js_polkit_user_is_in_netgroup (duk_context *cx);
+-
+-static const duk_function_list_entry js_polkit_functions[] =
+-{
+- { "log", js_polkit_log, 1 },
+- { "spawn", js_polkit_spawn, 1 },
+- { "_userIsInNetGroup", js_polkit_user_is_in_netgroup, 2 },
+- { NULL, NULL, 0 },
+-};
+-
+ static void
+ polkit_backend_js_authority_constructed (GObject *object)
+ {
+--
+GitLab
+
+
+From edb70ef69eed3275f5654510d135e680eb46c85d Mon Sep 17 00:00:00 2001
+From: Wu Xiaotian
+Date: Sun, 22 Nov 2020 15:25:35 +0800
+Subject: [PATCH 11/17] remove WATCHDOG_TIMEOUT define
+
+Signed-off-by: Gustavo Lima Chaves
+---
+ src/polkitbackend/polkitbackendduktapeauthority.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/src/polkitbackend/polkitbackendduktapeauthority.c b/src/polkitbackend/polkitbackendduktapeauthority.c
+index 6fac3be..51e03fd 100644
+--- a/src/polkitbackend/polkitbackendduktapeauthority.c
++++ b/src/polkitbackend/polkitbackendduktapeauthority.c
+@@ -69,7 +69,6 @@ struct _PolkitBackendJsAuthorityPrivate
+ duk_context *cx;
+ };
+
+-#define WATCHDOG_TIMEOUT (15 * G_TIME_SPAN_SECOND)
+
+ static void utils_spawn (const gchar *const *argv,
+ guint timeout_seconds,
+--
+GitLab
+
+
+From 906ae404f29f15ef8c529b999bf091b5d18ed7ac Mon Sep 17 00:00:00 2001
+From: Wu Xiaotian
+Date: Sun, 22 Nov 2020 12:46:40 +0800
+Subject: [PATCH 12/17] add meson build system support
+
+Signed-off-by: Gustavo Lima Chaves
+---
+ meson.build | 11 ++++++++++-
+ meson_options.txt | 1 +
+ src/polkitbackend/meson.build | 10 ++++++++--
+ 3 files changed, 19 insertions(+), 3 deletions(-)
+
+diff --git a/meson.build b/meson.build
+index 858078d..4e44723 100644
+--- a/meson.build
++++ b/meson.build
+@@ -133,7 +133,13 @@ expat_dep = dependency('expat')
+ assert(cc.has_header('expat.h', dependencies: expat_dep), 'Can\'t find expat.h. Please install expat.')
+ assert(cc.has_function('XML_ParserCreate', dependencies: expat_dep), 'Can\'t find expat library. Please install expat.')
+
+-mozjs_dep = dependency('mozjs-78')
++js_engine = get_option('js_engine')
++if js_engine == 'duktape'
++ js_dep = dependency('duktape')
++ libm_dep = cc.find_library('m')
++elif js_engine == 'mozjs'
++ js_dep = dependency('mozjs-78')
++endif
+
+ dbus_dep = dependency('dbus-1', required: false)
+ dbus_policydir = pk_prefix / pk_datadir / 'dbus-1/system.d'
+@@ -361,6 +367,9 @@ if enable_logind
+ output += ' systemdsystemunitdir: ' + systemd_systemdsystemunitdir + '\n'
+ endif
+ output += ' polkitd user: ' + polkitd_user + ' \n'
++output += ' Javascript engine: ' + js_engine + '\n'
++if enable_logind
++endif
+ output += ' PAM support: ' + enable_pam.to_string() + '\n\n'
+ if enable_pam
+ output += ' PAM file auth: ' + pam_conf['PAM_FILE_INCLUDE_AUTH'] + '\n'
+diff --git a/meson_options.txt b/meson_options.txt
+index 25e3e77..76aa311 100644
+--- a/meson_options.txt
++++ b/meson_options.txt
+@@ -16,3 +16,4 @@ option('introspection', type: 'boolean', value: true, description: 'Enable intro
+
+ option('gtk_doc', type: 'boolean', value: false, description: 'use gtk-doc to build documentation')
+ option('man', type: 'boolean', value: false, description: 'build manual pages')
++option('js_engine', type: 'combo', choices: ['mozjs', 'duktape'], value: 'duktape', description: 'javascript engine')
+diff --git a/src/polkitbackend/meson.build b/src/polkitbackend/meson.build
+index 64f0e4a..489897d 100644
+--- a/src/polkitbackend/meson.build
++++ b/src/polkitbackend/meson.build
+@@ -5,7 +5,6 @@ sources = files(
+ 'polkitbackendactionpool.c',
+ 'polkitbackendauthority.c',
+ 'polkitbackendinteractiveauthority.c',
+- 'polkitbackendjsauthority.cpp',
+ )
+
+ output = 'initjs.h'
+@@ -21,7 +20,7 @@ sources += custom_target(
+ deps = [
+ expat_dep,
+ libpolkit_gobject_dep,
+- mozjs_dep,
++ js_dep,
+ ]
+
+ c_flags = [
+@@ -31,6 +30,13 @@ c_flags = [
+ '-DPACKAGE_SYSCONF_DIR="@0@"'.format(pk_prefix / pk_sysconfdir),
+ ]
+
++if js_engine == 'duktape'
++ sources += files('polkitbackendduktapeauthority.c')
++ deps += libm_dep
++elif js_engine == 'mozjs'
++ sources += files('polkitbackendjsauthority.cpp')
++endif
++
+ if enable_logind
+ sources += files('polkitbackendsessionmonitor-systemd.c')
+
+--
+GitLab
+
+
+From 1380b505c25be4aebe54b1b4223a570d64af83cc Mon Sep 17 00:00:00 2001
+From: Wu Xiaotian
+Date: Sun, 22 Nov 2020 18:49:14 +0800
+Subject: [PATCH 13/17] fix run error
+
+Signed-off-by: Gustavo Lima Chaves
+---
+ src/polkitbackend/polkitbackendduktapeauthority.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/polkitbackend/polkitbackendduktapeauthority.c b/src/polkitbackend/polkitbackendduktapeauthority.c
+index 51e03fd..4b4f8fd 100644
+--- a/src/polkitbackend/polkitbackendduktapeauthority.c
++++ b/src/polkitbackend/polkitbackendduktapeauthority.c
+@@ -795,11 +795,11 @@ polkit_backend_js_authority_check_authorization_sync (PolkitBackendInteractiveAu
+ gboolean good = FALSE;
+ duk_context *cx = authority->priv->cx;
+
++ duk_set_top (cx, 0);
+ if (!duk_get_global_string (cx, "polkit")) {
+ goto out;
+ }
+
+- duk_set_top (cx, 0);
+ duk_push_string (cx, "_runRules");
+
+ if (!push_action_and_details (cx, action_id, details, &error))
+--
+GitLab
+
+
+From 6856a704b70378948ef5f66e9b09555d97d4070b Mon Sep 17 00:00:00 2001
+From: Gustavo Lima Chaves
+Date: Fri, 10 Sep 2021 15:17:58 -0700
+Subject: [PATCH 14/17] Deduplicate code for "Add duktape as JS engine backend"
+ effort/MR
+
+This leverages Wu Xiaotian (@yetist)'s original MR
+(https://gitlab.freedesktop.org/polkit/polkit/-/merge_requests/35), in
+an effort to complete said work.
+
+This is the first of the requests from maintainers--to reduce
+eliminate code duplication.
+
+The runaway-killer missing functionality will come in the sequence.
+
+Signed-off-by: Gustavo Lima Chaves
+---
+ src/polkitbackend/Makefile.am | 1 +
+ src/polkitbackend/meson.build | 1 +
+ src/polkitbackend/polkitbackendcommon.c | 530 +++++++++++++
+ src/polkitbackend/polkitbackendcommon.h | 156 ++++
+ .../polkitbackendduktapeauthority.c | 714 ++----------------
+ .../polkitbackendjsauthority.cpp | 711 ++---------------
+ 6 files changed, 790 insertions(+), 1323 deletions(-)
+ create mode 100644 src/polkitbackend/polkitbackendcommon.c
+ create mode 100644 src/polkitbackend/polkitbackendcommon.h
+
+diff --git a/src/polkitbackend/Makefile.am b/src/polkitbackend/Makefile.am
+index abcbc6f..6a8b4ae 100644
+--- a/src/polkitbackend/Makefile.am
++++ b/src/polkitbackend/Makefile.am
+@@ -31,6 +31,7 @@ libpolkit_backend_1_la_SOURCES = \
+ polkitbackend.h \
+ polkitbackendtypes.h \
+ polkitbackendprivate.h \
++ polkitbackendcommon.h polkitbackendcommon.c \
+ polkitbackendauthority.h polkitbackendauthority.c \
+ polkitbackendinteractiveauthority.h polkitbackendinteractiveauthority.c \
+ polkitbackendjsauthority.h \
+diff --git a/src/polkitbackend/meson.build b/src/polkitbackend/meson.build
+index 489897d..9ec01b2 100644
+--- a/src/polkitbackend/meson.build
++++ b/src/polkitbackend/meson.build
+@@ -4,6 +4,7 @@ sources = files(
+ 'polkitbackendactionlookup.c',
+ 'polkitbackendactionpool.c',
+ 'polkitbackendauthority.c',
++ 'polkitbackendcommon.c',
+ 'polkitbackendinteractiveauthority.c',
+ )
+
+diff --git a/src/polkitbackend/polkitbackendcommon.c b/src/polkitbackend/polkitbackendcommon.c
+new file mode 100644
+index 0000000..6783dff
+--- /dev/null
++++ b/src/polkitbackend/polkitbackendcommon.c
+@@ -0,0 +1,530 @@
++/*
++ * Copyright (C) 2008 Red Hat, Inc.
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++ * Boston, MA 02111-1307, USA.
++ *
++ * Author: David Zeuthen
++ */
++
++#include "polkitbackendcommon.h"
++
++static void
++utils_child_watch_from_release_cb (GPid pid,
++ gint status,
++ gpointer user_data)
++{
++}
++
++static void
++utils_spawn_data_free (UtilsSpawnData *data)
++{
++ if (data->timeout_source != NULL)
++ {
++ g_source_destroy (data->timeout_source);
++ data->timeout_source = NULL;
++ }
++
++ /* Nuke the child, if necessary */
++ if (data->child_watch_source != NULL)
++ {
++ g_source_destroy (data->child_watch_source);
++ data->child_watch_source = NULL;
++ }
++
++ if (data->child_pid != 0)
++ {
++ GSource *source;
++ kill (data->child_pid, SIGTERM);
++ /* OK, we need to reap for the child ourselves - we don't want
++ * to use waitpid() because that might block the calling
++ * thread (the child might handle SIGTERM and use several
++ * seconds for cleanup/rollback).
++ *
++ * So we use GChildWatch instead.
++ *
++ * Avoid taking a references to ourselves. but note that we need
++ * to pass the GSource so we can nuke it once handled.
++ */
++ source = g_child_watch_source_new (data->child_pid);
++ g_source_set_callback (source,
++ (GSourceFunc) utils_child_watch_from_release_cb,
++ source,
++ (GDestroyNotify) g_source_destroy);
++ g_source_attach (source, data->main_context);
++ g_source_unref (source);
++ data->child_pid = 0;
++ }
++
++ if (data->child_stdout != NULL)
++ {
++ g_string_free (data->child_stdout, TRUE);
++ data->child_stdout = NULL;
++ }
++
++ if (data->child_stderr != NULL)
++ {
++ g_string_free (data->child_stderr, TRUE);
++ data->child_stderr = NULL;
++ }
++
++ if (data->child_stdout_channel != NULL)
++ {
++ g_io_channel_unref (data->child_stdout_channel);
++ data->child_stdout_channel = NULL;
++ }
++ if (data->child_stderr_channel != NULL)
++ {
++ g_io_channel_unref (data->child_stderr_channel);
++ data->child_stderr_channel = NULL;
++ }
++
++ if (data->child_stdout_source != NULL)
++ {
++ g_source_destroy (data->child_stdout_source);
++ data->child_stdout_source = NULL;
++ }
++ if (data->child_stderr_source != NULL)
++ {
++ g_source_destroy (data->child_stderr_source);
++ data->child_stderr_source = NULL;
++ }
++
++ if (data->child_stdout_fd != -1)
++ {
++ g_warn_if_fail (close (data->child_stdout_fd) == 0);
++ data->child_stdout_fd = -1;
++ }
++ if (data->child_stderr_fd != -1)
++ {
++ g_warn_if_fail (close (data->child_stderr_fd) == 0);
++ data->child_stderr_fd = -1;
++ }
++
++ if (data->cancellable_handler_id > 0)
++ {
++ g_cancellable_disconnect (data->cancellable, data->cancellable_handler_id);
++ data->cancellable_handler_id = 0;
++ }
++
++ if (data->main_context != NULL)
++ g_main_context_unref (data->main_context);
++
++ if (data->cancellable != NULL)
++ g_object_unref (data->cancellable);
++
++ g_slice_free (UtilsSpawnData, data);
++}
++
++/* called in the thread where @cancellable was cancelled */
++static void
++utils_on_cancelled (GCancellable *cancellable,
++ gpointer user_data)
++{
++ UtilsSpawnData *data = (UtilsSpawnData *)user_data;
++ GError *error;
++
++ error = NULL;
++ g_warn_if_fail (g_cancellable_set_error_if_cancelled (cancellable, &error));
++ g_simple_async_result_take_error (data->simple, error);
++ g_simple_async_result_complete_in_idle (data->simple);
++ g_object_unref (data->simple);
++}
++
++static gboolean
++utils_timeout_cb (gpointer user_data)
++{
++ UtilsSpawnData *data = (UtilsSpawnData *)user_data;
++
++ data->timed_out = TRUE;
++
++ /* ok, timeout is history, make sure we don't free it in spawn_data_free() */
++ data->timeout_source = NULL;
++
++ /* we're done */
++ g_simple_async_result_complete_in_idle (data->simple);
++ g_object_unref (data->simple);
++
++ return FALSE; /* remove source */
++}
++
++static void
++utils_child_watch_cb (GPid pid,
++ gint status,
++ gpointer user_data)
++{
++ UtilsSpawnData *data = (UtilsSpawnData *)user_data;
++ gchar *buf;
++ gsize buf_size;
++
++ if (g_io_channel_read_to_end (data->child_stdout_channel, &buf, &buf_size, NULL) == G_IO_STATUS_NORMAL)
++ {
++ g_string_append_len (data->child_stdout, buf, buf_size);
++ g_free (buf);
++ }
++ if (g_io_channel_read_to_end (data->child_stderr_channel, &buf, &buf_size, NULL) == G_IO_STATUS_NORMAL)
++ {
++ g_string_append_len (data->child_stderr, buf, buf_size);
++ g_free (buf);
++ }
++
++ data->exit_status = status;
++
++ /* ok, child watch is history, make sure we don't free it in spawn_data_free() */
++ data->child_pid = 0;
++ data->child_watch_source = NULL;
++
++ /* we're done */
++ g_simple_async_result_complete_in_idle (data->simple);
++ g_object_unref (data->simple);
++}
++
++static gboolean
++utils_read_child_stderr (GIOChannel *channel,
++ GIOCondition condition,
++ gpointer user_data)
++{
++ UtilsSpawnData *data = (UtilsSpawnData *)user_data;
++ gchar buf[1024];
++ gsize bytes_read;
++
++ g_io_channel_read_chars (channel, buf, sizeof buf, &bytes_read, NULL);
++ g_string_append_len (data->child_stderr, buf, bytes_read);
++ return TRUE;
++}
++
++static gboolean
++utils_read_child_stdout (GIOChannel *channel,
++ GIOCondition condition,
++ gpointer user_data)
++{
++ UtilsSpawnData *data = (UtilsSpawnData *)user_data;
++ gchar buf[1024];
++ gsize bytes_read;
++
++ g_io_channel_read_chars (channel, buf, sizeof buf, &bytes_read, NULL);
++ g_string_append_len (data->child_stdout, buf, bytes_read);
++ return TRUE;
++}
++
++void
++polkit_backend_common_spawn (const gchar *const *argv,
++ guint timeout_seconds,
++ GCancellable *cancellable,
++ GAsyncReadyCallback callback,
++ gpointer user_data)
++{
++ UtilsSpawnData *data;
++ GError *error;
++
++ data = g_slice_new0 (UtilsSpawnData);
++ data->timeout_seconds = timeout_seconds;
++ data->simple = g_simple_async_result_new (NULL,
++ callback,
++ user_data,
++ (gpointer*)polkit_backend_common_spawn);
++ data->main_context = g_main_context_get_thread_default ();
++ if (data->main_context != NULL)
++ g_main_context_ref (data->main_context);
++
++ data->cancellable = cancellable != NULL ? (GCancellable*)g_object_ref (cancellable) : NULL;
++
++ data->child_stdout = g_string_new (NULL);
++ data->child_stderr = g_string_new (NULL);
++ data->child_stdout_fd = -1;
++ data->child_stderr_fd = -1;
++
++ /* the life-cycle of UtilsSpawnData is tied to its GSimpleAsyncResult */
++ g_simple_async_result_set_op_res_gpointer (data->simple, data, (GDestroyNotify) utils_spawn_data_free);
++
++ error = NULL;
++ if (data->cancellable != NULL)
++ {
++ /* could already be cancelled */
++ error = NULL;
++ if (g_cancellable_set_error_if_cancelled (data->cancellable, &error))
++ {
++ g_simple_async_result_take_error (data->simple, error);
++ g_simple_async_result_complete_in_idle (data->simple);
++ g_object_unref (data->simple);
++ goto out;
++ }
++
++ data->cancellable_handler_id = g_cancellable_connect (data->cancellable,
++ G_CALLBACK (utils_on_cancelled),
++ data,
++ NULL);
++ }
++
++ error = NULL;
++ if (!g_spawn_async_with_pipes (NULL, /* working directory */
++ (gchar **) argv,
++ NULL, /* envp */
++ G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
++ NULL, /* child_setup */
++ NULL, /* child_setup's user_data */
++ &(data->child_pid),
++ NULL, /* gint *stdin_fd */
++ &(data->child_stdout_fd),
++ &(data->child_stderr_fd),
++ &error))
++ {
++ g_prefix_error (&error, "Error spawning: ");
++ g_simple_async_result_take_error (data->simple, error);
++ g_simple_async_result_complete_in_idle (data->simple);
++ g_object_unref (data->simple);
++ goto out;
++ }
++
++ if (timeout_seconds > 0)
++ {
++ data->timeout_source = g_timeout_source_new_seconds (timeout_seconds);
++ g_source_set_priority (data->timeout_source, G_PRIORITY_DEFAULT);
++ g_source_set_callback (data->timeout_source, utils_timeout_cb, data, NULL);
++ g_source_attach (data->timeout_source, data->main_context);
++ g_source_unref (data->timeout_source);
++ }
++
++ data->child_watch_source = g_child_watch_source_new (data->child_pid);
++ g_source_set_callback (data->child_watch_source, (GSourceFunc) utils_child_watch_cb, data, NULL);
++ g_source_attach (data->child_watch_source, data->main_context);
++ g_source_unref (data->child_watch_source);
++
++ data->child_stdout_channel = g_io_channel_unix_new (data->child_stdout_fd);
++ g_io_channel_set_flags (data->child_stdout_channel, G_IO_FLAG_NONBLOCK, NULL);
++ data->child_stdout_source = g_io_create_watch (data->child_stdout_channel, G_IO_IN);
++ g_source_set_callback (data->child_stdout_source, (GSourceFunc) utils_read_child_stdout, data, NULL);
++ g_source_attach (data->child_stdout_source, data->main_context);
++ g_source_unref (data->child_stdout_source);
++
++ data->child_stderr_channel = g_io_channel_unix_new (data->child_stderr_fd);
++ g_io_channel_set_flags (data->child_stderr_channel, G_IO_FLAG_NONBLOCK, NULL);
++ data->child_stderr_source = g_io_create_watch (data->child_stderr_channel, G_IO_IN);
++ g_source_set_callback (data->child_stderr_source, (GSourceFunc) utils_read_child_stderr, data, NULL);
++ g_source_attach (data->child_stderr_source, data->main_context);
++ g_source_unref (data->child_stderr_source);
++
++ out:
++ ;
++}
++
++void
++polkit_backend_common_on_dir_monitor_changed (GFileMonitor *monitor,
++ GFile *file,
++ GFile *other_file,
++ GFileMonitorEvent event_type,
++ gpointer user_data)
++{
++ PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (user_data);
++
++ /* TODO: maybe rate-limit so storms of events are collapsed into one with a 500ms resolution?
++ * Because when editing a file with emacs we get 4-8 events..
++ */
++
++ if (file != NULL)
++ {
++ gchar *name;
++
++ name = g_file_get_basename (file);
++
++ /* g_print ("event_type=%d file=%p name=%s\n", event_type, file, name); */
++ if (!g_str_has_prefix (name, ".") &&
++ !g_str_has_prefix (name, "#") &&
++ g_str_has_suffix (name, ".rules") &&
++ (event_type == G_FILE_MONITOR_EVENT_CREATED ||
++ event_type == G_FILE_MONITOR_EVENT_DELETED ||
++ event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT))
++ {
++ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
++ "Reloading rules");
++ polkit_backend_common_reload_scripts (authority);
++ }
++ g_free (name);
++ }
++}
++
++gboolean
++polkit_backend_common_spawn_finish (GAsyncResult *res,
++ gint *out_exit_status,
++ gchar **out_standard_output,
++ gchar **out_standard_error,
++ GError **error)
++{
++ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
++ UtilsSpawnData *data;
++ gboolean ret = FALSE;
++
++ g_return_val_if_fail (G_IS_ASYNC_RESULT (res), FALSE);
++ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
++
++ g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == polkit_backend_common_spawn);
++
++ if (g_simple_async_result_propagate_error (simple, error))
++ goto out;
++
++ data = (UtilsSpawnData*)g_simple_async_result_get_op_res_gpointer (simple);
++
++ if (data->timed_out)
++ {
++ g_set_error (error,
++ G_IO_ERROR,
++ G_IO_ERROR_TIMED_OUT,
++ "Timed out after %d seconds",
++ data->timeout_seconds);
++ goto out;
++ }
++
++ if (out_exit_status != NULL)
++ *out_exit_status = data->exit_status;
++
++ if (out_standard_output != NULL)
++ *out_standard_output = g_strdup (data->child_stdout->str);
++
++ if (out_standard_error != NULL)
++ *out_standard_error = g_strdup (data->child_stderr->str);
++
++ ret = TRUE;
++
++ out:
++ return ret;
++}
++
++static const gchar *
++polkit_backend_js_authority_get_name (PolkitBackendAuthority *authority)
++{
++ return "js";
++}
++
++static const gchar *
++polkit_backend_js_authority_get_version (PolkitBackendAuthority *authority)
++{
++ return PACKAGE_VERSION;
++}
++
++static PolkitAuthorityFeatures
++polkit_backend_js_authority_get_features (PolkitBackendAuthority *authority)
++{
++ return POLKIT_AUTHORITY_FEATURES_TEMPORARY_AUTHORIZATION;
++}
++
++void
++polkit_backend_common_js_authority_class_init_common (PolkitBackendJsAuthorityClass *klass)
++{
++ GObjectClass *gobject_class;
++ PolkitBackendAuthorityClass *authority_class;
++ PolkitBackendInteractiveAuthorityClass *interactive_authority_class;
++
++ gobject_class = G_OBJECT_CLASS (klass);
++ gobject_class->finalize = polkit_backend_common_js_authority_finalize;
++ gobject_class->set_property = polkit_backend_common_js_authority_set_property;
++ gobject_class->constructed = polkit_backend_common_js_authority_constructed;
++
++ authority_class = POLKIT_BACKEND_AUTHORITY_CLASS (klass);
++ authority_class->get_name = polkit_backend_js_authority_get_name;
++ authority_class->get_version = polkit_backend_js_authority_get_version;
++ authority_class->get_features = polkit_backend_js_authority_get_features;
++
++ interactive_authority_class = POLKIT_BACKEND_INTERACTIVE_AUTHORITY_CLASS (klass);
++ interactive_authority_class->get_admin_identities = polkit_backend_common_js_authority_get_admin_auth_identities;
++ interactive_authority_class->check_authorization_sync = polkit_backend_common_js_authority_check_authorization_sync;
++
++ g_object_class_install_property (gobject_class,
++ PROP_RULES_DIRS,
++ g_param_spec_boxed ("rules-dirs",
++ NULL,
++ NULL,
++ G_TYPE_STRV,
++ G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
++}
++
++gint
++polkit_backend_common_rules_file_name_cmp (const gchar *a,
++ const gchar *b)
++{
++ gint ret;
++ const gchar *a_base;
++ const gchar *b_base;
++
++ a_base = strrchr (a, '/');
++ b_base = strrchr (b, '/');
++
++ g_assert (a_base != NULL);
++ g_assert (b_base != NULL);
++ a_base += 1;
++ b_base += 1;
++
++ ret = g_strcmp0 (a_base, b_base);
++ if (ret == 0)
++ {
++ /* /etc wins over /usr */
++ ret = g_strcmp0 (a, b);
++ g_assert (ret != 0);
++ }
++
++ return ret;
++}
++
++const gchar *
++polkit_backend_common_get_signal_name (gint signal_number)
++{
++ switch (signal_number)
++ {
++#define _HANDLE_SIG(sig) case sig: return #sig;
++ _HANDLE_SIG (SIGHUP);
++ _HANDLE_SIG (SIGINT);
++ _HANDLE_SIG (SIGQUIT);
++ _HANDLE_SIG (SIGILL);
++ _HANDLE_SIG (SIGABRT);
++ _HANDLE_SIG (SIGFPE);
++ _HANDLE_SIG (SIGKILL);
++ _HANDLE_SIG (SIGSEGV);
++ _HANDLE_SIG (SIGPIPE);
++ _HANDLE_SIG (SIGALRM);
++ _HANDLE_SIG (SIGTERM);
++ _HANDLE_SIG (SIGUSR1);
++ _HANDLE_SIG (SIGUSR2);
++ _HANDLE_SIG (SIGCHLD);
++ _HANDLE_SIG (SIGCONT);
++ _HANDLE_SIG (SIGSTOP);
++ _HANDLE_SIG (SIGTSTP);
++ _HANDLE_SIG (SIGTTIN);
++ _HANDLE_SIG (SIGTTOU);
++ _HANDLE_SIG (SIGBUS);
++#ifdef SIGPOLL
++ _HANDLE_SIG (SIGPOLL);
++#endif
++ _HANDLE_SIG (SIGPROF);
++ _HANDLE_SIG (SIGSYS);
++ _HANDLE_SIG (SIGTRAP);
++ _HANDLE_SIG (SIGURG);
++ _HANDLE_SIG (SIGVTALRM);
++ _HANDLE_SIG (SIGXCPU);
++ _HANDLE_SIG (SIGXFSZ);
++#undef _HANDLE_SIG
++ default:
++ break;
++ }
++ return "UNKNOWN_SIGNAL";
++}
++
++void
++polkit_backend_common_spawn_cb (GObject *source_object,
++ GAsyncResult *res,
++ gpointer user_data)
++{
++ SpawnData *data = (SpawnData *)user_data;
++ data->res = (GAsyncResult*)g_object_ref (res);
++ g_main_loop_quit (data->loop);
++}
+diff --git a/src/polkitbackend/polkitbackendcommon.h b/src/polkitbackend/polkitbackendcommon.h
+new file mode 100644
+index 0000000..6d0d267
+--- /dev/null
++++ b/src/polkitbackend/polkitbackendcommon.h
+@@ -0,0 +1,156 @@
++/*
++ * Copyright (C) 2008 Red Hat, Inc.
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++ * Boston, MA 02111-1307, USA.
++ *
++ * Author: David Zeuthen
++ */
++
++#if !defined (_POLKIT_BACKEND_COMPILATION) && !defined(_POLKIT_BACKEND_INSIDE_POLKIT_BACKEND_H)
++#error "Only can be included directly, this file may disappear or change contents."
++#endif
++
++#ifndef __POLKIT_BACKEND_COMMON_H
++#define __POLKIT_BACKEND_COMMON_H
++
++#include "config.h"
++#include
++#include
++#include
++#include
++#ifdef HAVE_NETGROUP_H
++#include
++#else
++#include
++#endif
++#include
++#include
++#include
++#include //here, all things glib via glib.h (including -> gspawn.h)
++
++#include
++#include "polkitbackendjsauthority.h"
++
++#include
++
++#ifdef HAVE_LIBSYSTEMD
++#include
++#endif /* HAVE_LIBSYSTEMD */
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++enum
++{
++ PROP_0,
++ PROP_RULES_DIRS,
++};
++
++typedef struct
++{
++ GSimpleAsyncResult *simple; /* borrowed reference */
++ GMainContext *main_context; /* may be NULL */
++
++ GCancellable *cancellable; /* may be NULL */
++ gulong cancellable_handler_id;
++
++ GPid child_pid;
++ gint child_stdout_fd;
++ gint child_stderr_fd;
++
++ GIOChannel *child_stdout_channel;
++ GIOChannel *child_stderr_channel;
++
++ GSource *child_watch_source;
++ GSource *child_stdout_source;
++ GSource *child_stderr_source;
++
++ guint timeout_seconds;
++ gboolean timed_out;
++ GSource *timeout_source;
++
++ GString *child_stdout;
++ GString *child_stderr;
++
++ gint exit_status;
++} UtilsSpawnData;
++
++typedef struct
++{
++ GMainLoop *loop;
++ GAsyncResult *res;
++} SpawnData;
++
++void polkit_backend_common_spawn (const gchar *const *argv,
++ guint timeout_seconds,
++ GCancellable *cancellable,
++ GAsyncReadyCallback callback,
++ gpointer user_data);
++void polkit_backend_common_spawn_cb (GObject *source_object,
++ GAsyncResult *res,
++ gpointer user_data);
++gboolean polkit_backend_common_spawn_finish (GAsyncResult *res,
++ gint *out_exit_status,
++ gchar **out_standard_output,
++ gchar **out_standard_error,
++ GError **error);
++
++void polkit_backend_common_on_dir_monitor_changed (GFileMonitor *monitor,
++ GFile *file,
++ GFile *other_file,
++ GFileMonitorEvent event_type,
++ gpointer user_data);
++
++void polkit_backend_common_js_authority_class_init_common (PolkitBackendJsAuthorityClass *klass);
++
++gint polkit_backend_common_rules_file_name_cmp (const gchar *a,
++ const gchar *b);
++
++const gchar *polkit_backend_common_get_signal_name (gint signal_number);
++
++/* To be provided by each JS backend, from here onwards ---------------------------------------------- */
++
++void polkit_backend_common_reload_scripts (PolkitBackendJsAuthority *authority);
++void polkit_backend_common_js_authority_finalize (GObject *object);
++void polkit_backend_common_js_authority_constructed (GObject *object);
++GList *polkit_backend_common_js_authority_get_admin_auth_identities (PolkitBackendInteractiveAuthority *_authority,
++ PolkitSubject *caller,
++ PolkitSubject *subject,
++ PolkitIdentity *user_for_subject,
++ gboolean subject_is_local,
++ gboolean subject_is_active,
++ const gchar *action_id,
++ PolkitDetails *details);
++void polkit_backend_common_js_authority_set_property (GObject *object,
++ guint property_id,
++ const GValue *value,
++ GParamSpec *pspec);
++PolkitImplicitAuthorization polkit_backend_common_js_authority_check_authorization_sync (PolkitBackendInteractiveAuthority *_authority,
++ PolkitSubject *caller,
++ PolkitSubject *subject,
++ PolkitIdentity *user_for_subject,
++ gboolean subject_is_local,
++ gboolean subject_is_active,
++ const gchar *action_id,
++ PolkitDetails *details,
++ PolkitImplicitAuthorization implicit);
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* __POLKIT_BACKEND_COMMON_H */
++
+diff --git a/src/polkitbackend/polkitbackendduktapeauthority.c b/src/polkitbackend/polkitbackendduktapeauthority.c
+index 4b4f8fd..a2b4420 100644
+--- a/src/polkitbackend/polkitbackendduktapeauthority.c
++++ b/src/polkitbackend/polkitbackendduktapeauthority.c
+@@ -21,32 +21,12 @@
+ * Author: David Zeuthen
+ */
+
+-#include "config.h"
+-#include
+-#include
+-#include
+-#include
+-#ifdef HAVE_NETGROUP_H
+-#include
+-#else
+-#include
+-#endif
+-#include
+-#include
+-#include
+-#include
+-
+-#include
+-#include "polkitbackendjsauthority.h"
+-
+-#include
++#include "polkitbackendcommon.h"
+
+-#ifdef HAVE_LIBSYSTEMD
+-#include
+-#endif /* HAVE_LIBSYSTEMD */
++#include "duktape.h"
+
++/* Built source and not too big to worry about deduplication */
+ #include "initjs.h" /* init.js */
+-#include "duktape.h"
+
+ /**
+ * SECTION:polkitbackendjsauthority
+@@ -54,10 +34,9 @@
+ * @short_description: JS Authority
+ * @stability: Unstable
+ *
+- * An implementation of #PolkitBackendAuthority that reads and
+- * evalates Javascript files and supports interaction with
+- * authentication agents (virtue of being based on
+- * #PolkitBackendInteractiveAuthority).
++ * An (Duktape-based) implementation of #PolkitBackendAuthority that reads and
++ * evaluates Javascript files and supports interaction with authentication
++ * agents (virtue of being based on #PolkitBackendInteractiveAuthority).
+ */
+
+ /* ---------------------------------------------------------------------------------------------------- */
+@@ -66,64 +45,16 @@ struct _PolkitBackendJsAuthorityPrivate
+ {
+ gchar **rules_dirs;
+ GFileMonitor **dir_monitors; /* NULL-terminated array of GFileMonitor instances */
+- duk_context *cx;
+-};
+-
+-
+-static void utils_spawn (const gchar *const *argv,
+- guint timeout_seconds,
+- GCancellable *cancellable,
+- GAsyncReadyCallback callback,
+- gpointer user_data);
+-
+-gboolean utils_spawn_finish (GAsyncResult *res,
+- gint *out_exit_status,
+- gchar **out_standard_output,
+- gchar **out_standard_error,
+- GError **error);
+
+-static void on_dir_monitor_changed (GFileMonitor *monitor,
+- GFile *file,
+- GFile *other_file,
+- GFileMonitorEvent event_type,
+- gpointer user_data);
+-
+-/* ---------------------------------------------------------------------------------------------------- */
+-
+-enum
+-{
+- PROP_0,
+- PROP_RULES_DIRS,
++ duk_context *cx;
+ };
+
+ /* ---------------------------------------------------------------------------------------------------- */
+
+-static GList *polkit_backend_js_authority_get_admin_auth_identities (PolkitBackendInteractiveAuthority *authority,
+- PolkitSubject *caller,
+- PolkitSubject *subject,
+- PolkitIdentity *user_for_subject,
+- gboolean subject_is_local,
+- gboolean subject_is_active,
+- const gchar *action_id,
+- PolkitDetails *details);
+-
+-static PolkitImplicitAuthorization polkit_backend_js_authority_check_authorization_sync (
+- PolkitBackendInteractiveAuthority *authority,
+- PolkitSubject *caller,
+- PolkitSubject *subject,
+- PolkitIdentity *user_for_subject,
+- gboolean subject_is_local,
+- gboolean subject_is_active,
+- const gchar *action_id,
+- PolkitDetails *details,
+- PolkitImplicitAuthorization implicit);
+-
+ G_DEFINE_TYPE (PolkitBackendJsAuthority, polkit_backend_js_authority, POLKIT_BACKEND_TYPE_INTERACTIVE_AUTHORITY);
+
+ /* ---------------------------------------------------------------------------------------------------- */
+
+-/* ---------------------------------------------------------------------------------------------------- */
+-
+ static duk_ret_t js_polkit_log (duk_context *cx);
+ static duk_ret_t js_polkit_spawn (duk_context *cx);
+ static duk_ret_t js_polkit_user_is_in_netgroup (duk_context *cx);
+@@ -144,33 +75,6 @@ polkit_backend_js_authority_init (PolkitBackendJsAuthority *authority)
+ PolkitBackendJsAuthorityPrivate);
+ }
+
+-static gint
+-rules_file_name_cmp (const gchar *a,
+- const gchar *b)
+-{
+- gint ret;
+- const gchar *a_base;
+- const gchar *b_base;
+-
+- a_base = strrchr (a, '/');
+- b_base = strrchr (b, '/');
+-
+- g_assert (a_base != NULL);
+- g_assert (b_base != NULL);
+- a_base += 1;
+- b_base += 1;
+-
+- ret = g_strcmp0 (a_base, b_base);
+- if (ret == 0)
+- {
+- /* /etc wins over /usr */
+- ret = g_strcmp0 (a, b);
+- g_assert (ret != 0);
+- }
+-
+- return ret;
+-}
+-
+ static void
+ load_scripts (PolkitBackendJsAuthority *authority)
+ {
+@@ -214,7 +118,7 @@ load_scripts (PolkitBackendJsAuthority *authority)
+ }
+ }
+
+- files = g_list_sort (files, (GCompareFunc) rules_file_name_cmp);
++ files = g_list_sort (files, (GCompareFunc) polkit_backend_common_rules_file_name_cmp);
+
+ for (l = files; l != NULL; l = l->next)
+ {
+@@ -258,8 +162,8 @@ load_scripts (PolkitBackendJsAuthority *authority)
+ g_list_free_full (files, g_free);
+ }
+
+-static void
+-reload_scripts (PolkitBackendJsAuthority *authority)
++void
++polkit_backend_common_reload_scripts (PolkitBackendJsAuthority *authority)
+ {
+ duk_context *cx = authority->priv->cx;
+
+@@ -282,42 +186,6 @@ reload_scripts (PolkitBackendJsAuthority *authority)
+ g_signal_emit_by_name (authority, "changed");
+ }
+
+-static void
+-on_dir_monitor_changed (GFileMonitor *monitor,
+- GFile *file,
+- GFile *other_file,
+- GFileMonitorEvent event_type,
+- gpointer user_data)
+-{
+- PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (user_data);
+-
+- /* TODO: maybe rate-limit so storms of events are collapsed into one with a 500ms resolution?
+- * Because when editing a file with emacs we get 4-8 events..
+- */
+-
+- if (file != NULL)
+- {
+- gchar *name;
+-
+- name = g_file_get_basename (file);
+-
+- /* g_print ("event_type=%d file=%p name=%s\n", event_type, file, name); */
+- if (!g_str_has_prefix (name, ".") &&
+- !g_str_has_prefix (name, "#") &&
+- g_str_has_suffix (name, ".rules") &&
+- (event_type == G_FILE_MONITOR_EVENT_CREATED ||
+- event_type == G_FILE_MONITOR_EVENT_DELETED ||
+- event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT))
+- {
+- polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
+- "Reloading rules");
+- reload_scripts (authority);
+- }
+- g_free (name);
+- }
+-}
+-
+-
+ static void
+ setup_file_monitors (PolkitBackendJsAuthority *authority)
+ {
+@@ -349,7 +217,7 @@ setup_file_monitors (PolkitBackendJsAuthority *authority)
+ {
+ g_signal_connect (monitor,
+ "changed",
+- G_CALLBACK (on_dir_monitor_changed),
++ G_CALLBACK (polkit_backend_common_on_dir_monitor_changed),
+ authority);
+ g_ptr_array_add (p, monitor);
+ }
+@@ -358,8 +226,8 @@ setup_file_monitors (PolkitBackendJsAuthority *authority)
+ authority->priv->dir_monitors = (GFileMonitor**) g_ptr_array_free (p, FALSE);
+ }
+
+-static void
+-polkit_backend_js_authority_constructed (GObject *object)
++void
++polkit_backend_common_js_authority_constructed (GObject *object)
+ {
+ PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (object);
+ duk_context *cx;
+@@ -395,8 +263,8 @@ polkit_backend_js_authority_constructed (GObject *object)
+ g_assert_not_reached ();
+ }
+
+-static void
+-polkit_backend_js_authority_finalize (GObject *object)
++void
++polkit_backend_common_js_authority_finalize (GObject *object)
+ {
+ PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (object);
+ guint n;
+@@ -405,7 +273,7 @@ polkit_backend_js_authority_finalize (GObject *object)
+ {
+ GFileMonitor *monitor = authority->priv->dir_monitors[n];
+ g_signal_handlers_disconnect_by_func (monitor,
+- G_CALLBACK (on_dir_monitor_changed),
++ G_CALLBACK (polkit_backend_common_on_dir_monitor_changed),
+ authority);
+ g_object_unref (monitor);
+ }
+@@ -417,11 +285,11 @@ polkit_backend_js_authority_finalize (GObject *object)
+ G_OBJECT_CLASS (polkit_backend_js_authority_parent_class)->finalize (object);
+ }
+
+-static void
+-polkit_backend_js_authority_set_property (GObject *object,
+- guint property_id,
+- const GValue *value,
+- GParamSpec *pspec)
++void
++polkit_backend_common_js_authority_set_property (GObject *object,
++ guint property_id,
++ const GValue *value,
++ GParamSpec *pspec)
+ {
+ PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (object);
+
+@@ -438,55 +306,10 @@ polkit_backend_js_authority_set_property (GObject *object,
+ }
+ }
+
+-static const gchar *
+-polkit_backend_js_authority_get_name (PolkitBackendAuthority *authority)
+-{
+- return "js";
+-}
+-
+-static const gchar *
+-polkit_backend_js_authority_get_version (PolkitBackendAuthority *authority)
+-{
+- return PACKAGE_VERSION;
+-}
+-
+-static PolkitAuthorityFeatures
+-polkit_backend_js_authority_get_features (PolkitBackendAuthority *authority)
+-{
+- return POLKIT_AUTHORITY_FEATURES_TEMPORARY_AUTHORIZATION;
+-}
+-
+ static void
+ polkit_backend_js_authority_class_init (PolkitBackendJsAuthorityClass *klass)
+ {
+- GObjectClass *gobject_class;
+- PolkitBackendAuthorityClass *authority_class;
+- PolkitBackendInteractiveAuthorityClass *interactive_authority_class;
+-
+-
+- gobject_class = G_OBJECT_CLASS (klass);
+- gobject_class->finalize = polkit_backend_js_authority_finalize;
+- gobject_class->set_property = polkit_backend_js_authority_set_property;
+- gobject_class->constructed = polkit_backend_js_authority_constructed;
+-
+- authority_class = POLKIT_BACKEND_AUTHORITY_CLASS (klass);
+- authority_class->get_name = polkit_backend_js_authority_get_name;
+- authority_class->get_version = polkit_backend_js_authority_get_version;
+- authority_class->get_features = polkit_backend_js_authority_get_features;
+-
+- interactive_authority_class = POLKIT_BACKEND_INTERACTIVE_AUTHORITY_CLASS (klass);
+- interactive_authority_class->get_admin_identities = polkit_backend_js_authority_get_admin_auth_identities;
+- interactive_authority_class->check_authorization_sync = polkit_backend_js_authority_check_authorization_sync;
+-
+- g_object_class_install_property (gobject_class,
+- PROP_RULES_DIRS,
+- g_param_spec_boxed ("rules-dirs",
+- NULL,
+- NULL,
+- G_TYPE_STRV,
+- G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
+-
+-
++ polkit_backend_common_js_authority_class_init_common (klass);
+ g_type_class_add_private (klass, sizeof (PolkitBackendJsAuthorityPrivate));
+ }
+
+@@ -689,15 +512,15 @@ push_action_and_details (duk_context *cx,
+
+ /* ---------------------------------------------------------------------------------------------------- */
+
+-static GList *
+-polkit_backend_js_authority_get_admin_auth_identities (PolkitBackendInteractiveAuthority *_authority,
+- PolkitSubject *caller,
+- PolkitSubject *subject,
+- PolkitIdentity *user_for_subject,
+- gboolean subject_is_local,
+- gboolean subject_is_active,
+- const gchar *action_id,
+- PolkitDetails *details)
++GList *
++polkit_backend_common_js_authority_get_admin_auth_identities (PolkitBackendInteractiveAuthority *_authority,
++ PolkitSubject *caller,
++ PolkitSubject *subject,
++ PolkitIdentity *user_for_subject,
++ gboolean subject_is_local,
++ gboolean subject_is_active,
++ const gchar *action_id,
++ PolkitDetails *details)
+ {
+ PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (_authority);
+ GList *ret = NULL;
+@@ -777,16 +600,16 @@ polkit_backend_js_authority_get_admin_auth_identities (PolkitBackendInteractiveA
+
+ /* ---------------------------------------------------------------------------------------------------- */
+
+-static PolkitImplicitAuthorization
+-polkit_backend_js_authority_check_authorization_sync (PolkitBackendInteractiveAuthority *_authority,
+- PolkitSubject *caller,
+- PolkitSubject *subject,
+- PolkitIdentity *user_for_subject,
+- gboolean subject_is_local,
+- gboolean subject_is_active,
+- const gchar *action_id,
+- PolkitDetails *details,
+- PolkitImplicitAuthorization implicit)
++PolkitImplicitAuthorization
++polkit_backend_common_js_authority_check_authorization_sync (PolkitBackendInteractiveAuthority *_authority,
++ PolkitSubject *caller,
++ PolkitSubject *subject,
++ PolkitIdentity *user_for_subject,
++ gboolean subject_is_local,
++ gboolean subject_is_active,
++ const gchar *action_id,
++ PolkitDetails *details,
++ PolkitImplicitAuthorization implicit)
+ {
+ PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (_authority);
+ PolkitImplicitAuthorization ret = implicit;
+@@ -864,65 +687,6 @@ js_polkit_log (duk_context *cx)
+
+ /* ---------------------------------------------------------------------------------------------------- */
+
+-static const gchar *
+-get_signal_name (gint signal_number)
+-{
+- switch (signal_number)
+- {
+-#define _HANDLE_SIG(sig) case sig: return #sig;
+- _HANDLE_SIG (SIGHUP);
+- _HANDLE_SIG (SIGINT);
+- _HANDLE_SIG (SIGQUIT);
+- _HANDLE_SIG (SIGILL);
+- _HANDLE_SIG (SIGABRT);
+- _HANDLE_SIG (SIGFPE);
+- _HANDLE_SIG (SIGKILL);
+- _HANDLE_SIG (SIGSEGV);
+- _HANDLE_SIG (SIGPIPE);
+- _HANDLE_SIG (SIGALRM);
+- _HANDLE_SIG (SIGTERM);
+- _HANDLE_SIG (SIGUSR1);
+- _HANDLE_SIG (SIGUSR2);
+- _HANDLE_SIG (SIGCHLD);
+- _HANDLE_SIG (SIGCONT);
+- _HANDLE_SIG (SIGSTOP);
+- _HANDLE_SIG (SIGTSTP);
+- _HANDLE_SIG (SIGTTIN);
+- _HANDLE_SIG (SIGTTOU);
+- _HANDLE_SIG (SIGBUS);
+-#ifdef SIGPOLL
+- _HANDLE_SIG (SIGPOLL);
+-#endif
+- _HANDLE_SIG (SIGPROF);
+- _HANDLE_SIG (SIGSYS);
+- _HANDLE_SIG (SIGTRAP);
+- _HANDLE_SIG (SIGURG);
+- _HANDLE_SIG (SIGVTALRM);
+- _HANDLE_SIG (SIGXCPU);
+- _HANDLE_SIG (SIGXFSZ);
+-#undef _HANDLE_SIG
+- default:
+- break;
+- }
+- return "UNKNOWN_SIGNAL";
+-}
+-
+-typedef struct
+-{
+- GMainLoop *loop;
+- GAsyncResult *res;
+-} SpawnData;
+-
+-static void
+-spawn_cb (GObject *source_object,
+- GAsyncResult *res,
+- gpointer user_data)
+-{
+- SpawnData *data = (SpawnData *)user_data;
+- data->res = (GAsyncResult*)g_object_ref (res);
+- g_main_loop_quit (data->loop);
+-}
+-
+ static duk_ret_t
+ js_polkit_spawn (duk_context *cx)
+ {
+@@ -962,21 +726,21 @@ js_polkit_spawn (duk_context *cx)
+ g_main_context_push_thread_default (context);
+
+ data.loop = loop;
+- utils_spawn ((const gchar *const *) argv,
+- 10, /* timeout_seconds */
+- NULL, /* cancellable */
+- spawn_cb,
+- &data);
++ polkit_backend_common_spawn ((const gchar *const *) argv,
++ 10, /* timeout_seconds */
++ NULL, /* cancellable */
++ polkit_backend_common_spawn_cb,
++ &data);
+
+ g_main_loop_run (loop);
+
+ g_main_context_pop_thread_default (context);
+
+- if (!utils_spawn_finish (data.res,
+- &exit_status,
+- &standard_output,
+- &standard_error,
+- &error))
++ if (!polkit_backend_common_spawn_finish (data.res,
++ &exit_status,
++ &standard_output,
++ &standard_error,
++ &error))
+ {
+ err_str = g_strdup_printf ("Error spawning helper: %s (%s, %d)",
+ error->message, g_quark_to_string (error->domain), error->code);
+@@ -998,7 +762,7 @@ js_polkit_spawn (duk_context *cx)
+ {
+ g_string_append_printf (gstr,
+ "Helper was signaled with signal %s (%d)",
+- get_signal_name (WTERMSIG (exit_status)),
++ polkit_backend_common_get_signal_name (WTERMSIG (exit_status)),
+ WTERMSIG (exit_status));
+ }
+ g_string_append_printf (gstr, ", stdout=`%s', stderr=`%s'",
+@@ -1052,377 +816,3 @@ js_polkit_user_is_in_netgroup (duk_context *cx)
+ }
+
+ /* ---------------------------------------------------------------------------------------------------- */
+-
+-typedef struct
+-{
+- GSimpleAsyncResult *simple; /* borrowed reference */
+- GMainContext *main_context; /* may be NULL */
+-
+- GCancellable *cancellable; /* may be NULL */
+- gulong cancellable_handler_id;
+-
+- GPid child_pid;
+- gint child_stdout_fd;
+- gint child_stderr_fd;
+-
+- GIOChannel *child_stdout_channel;
+- GIOChannel *child_stderr_channel;
+-
+- GSource *child_watch_source;
+- GSource *child_stdout_source;
+- GSource *child_stderr_source;
+-
+- guint timeout_seconds;
+- gboolean timed_out;
+- GSource *timeout_source;
+-
+- GString *child_stdout;
+- GString *child_stderr;
+-
+- gint exit_status;
+-} UtilsSpawnData;
+-
+-static void
+-utils_child_watch_from_release_cb (GPid pid,
+- gint status,
+- gpointer user_data)
+-{
+-}
+-
+-static void
+-utils_spawn_data_free (UtilsSpawnData *data)
+-{
+- if (data->timeout_source != NULL)
+- {
+- g_source_destroy (data->timeout_source);
+- data->timeout_source = NULL;
+- }
+-
+- /* Nuke the child, if necessary */
+- if (data->child_watch_source != NULL)
+- {
+- g_source_destroy (data->child_watch_source);
+- data->child_watch_source = NULL;
+- }
+-
+- if (data->child_pid != 0)
+- {
+- GSource *source;
+- kill (data->child_pid, SIGTERM);
+- /* OK, we need to reap for the child ourselves - we don't want
+- * to use waitpid() because that might block the calling
+- * thread (the child might handle SIGTERM and use several
+- * seconds for cleanup/rollback).
+- *
+- * So we use GChildWatch instead.
+- *
+- * Avoid taking a references to ourselves. but note that we need
+- * to pass the GSource so we can nuke it once handled.
+- */
+- source = g_child_watch_source_new (data->child_pid);
+- g_source_set_callback (source,
+- (GSourceFunc) utils_child_watch_from_release_cb,
+- source,
+- (GDestroyNotify) g_source_destroy);
+- g_source_attach (source, data->main_context);
+- g_source_unref (source);
+- data->child_pid = 0;
+- }
+-
+- if (data->child_stdout != NULL)
+- {
+- g_string_free (data->child_stdout, TRUE);
+- data->child_stdout = NULL;
+- }
+-
+- if (data->child_stderr != NULL)
+- {
+- g_string_free (data->child_stderr, TRUE);
+- data->child_stderr = NULL;
+- }
+-
+- if (data->child_stdout_channel != NULL)
+- {
+- g_io_channel_unref (data->child_stdout_channel);
+- data->child_stdout_channel = NULL;
+- }
+- if (data->child_stderr_channel != NULL)
+- {
+- g_io_channel_unref (data->child_stderr_channel);
+- data->child_stderr_channel = NULL;
+- }
+-
+- if (data->child_stdout_source != NULL)
+- {
+- g_source_destroy (data->child_stdout_source);
+- data->child_stdout_source = NULL;
+- }
+- if (data->child_stderr_source != NULL)
+- {
+- g_source_destroy (data->child_stderr_source);
+- data->child_stderr_source = NULL;
+- }
+-
+- if (data->child_stdout_fd != -1)
+- {
+- g_warn_if_fail (close (data->child_stdout_fd) == 0);
+- data->child_stdout_fd = -1;
+- }
+- if (data->child_stderr_fd != -1)
+- {
+- g_warn_if_fail (close (data->child_stderr_fd) == 0);
+- data->child_stderr_fd = -1;
+- }
+-
+- if (data->cancellable_handler_id > 0)
+- {
+- g_cancellable_disconnect (data->cancellable, data->cancellable_handler_id);
+- data->cancellable_handler_id = 0;
+- }
+-
+- if (data->main_context != NULL)
+- g_main_context_unref (data->main_context);
+-
+- if (data->cancellable != NULL)
+- g_object_unref (data->cancellable);
+-
+- g_slice_free (UtilsSpawnData, data);
+-}
+-
+-/* called in the thread where @cancellable was cancelled */
+-static void
+-utils_on_cancelled (GCancellable *cancellable,
+- gpointer user_data)
+-{
+- UtilsSpawnData *data = (UtilsSpawnData *)user_data;
+- GError *error;
+-
+- error = NULL;
+- g_warn_if_fail (g_cancellable_set_error_if_cancelled (cancellable, &error));
+- g_simple_async_result_take_error (data->simple, error);
+- g_simple_async_result_complete_in_idle (data->simple);
+- g_object_unref (data->simple);
+-}
+-
+-static gboolean
+-utils_read_child_stderr (GIOChannel *channel,
+- GIOCondition condition,
+- gpointer user_data)
+-{
+- UtilsSpawnData *data = (UtilsSpawnData *)user_data;
+- gchar buf[1024];
+- gsize bytes_read;
+-
+- g_io_channel_read_chars (channel, buf, sizeof buf, &bytes_read, NULL);
+- g_string_append_len (data->child_stderr, buf, bytes_read);
+- return TRUE;
+-}
+-
+-static gboolean
+-utils_read_child_stdout (GIOChannel *channel,
+- GIOCondition condition,
+- gpointer user_data)
+-{
+- UtilsSpawnData *data = (UtilsSpawnData *)user_data;
+- gchar buf[1024];
+- gsize bytes_read;
+-
+- g_io_channel_read_chars (channel, buf, sizeof buf, &bytes_read, NULL);
+- g_string_append_len (data->child_stdout, buf, bytes_read);
+- return TRUE;
+-}
+-
+-static void
+-utils_child_watch_cb (GPid pid,
+- gint status,
+- gpointer user_data)
+-{
+- UtilsSpawnData *data = (UtilsSpawnData *)user_data;
+- gchar *buf;
+- gsize buf_size;
+-
+- if (g_io_channel_read_to_end (data->child_stdout_channel, &buf, &buf_size, NULL) == G_IO_STATUS_NORMAL)
+- {
+- g_string_append_len (data->child_stdout, buf, buf_size);
+- g_free (buf);
+- }
+- if (g_io_channel_read_to_end (data->child_stderr_channel, &buf, &buf_size, NULL) == G_IO_STATUS_NORMAL)
+- {
+- g_string_append_len (data->child_stderr, buf, buf_size);
+- g_free (buf);
+- }
+-
+- data->exit_status = status;
+-
+- /* ok, child watch is history, make sure we don't free it in spawn_data_free() */
+- data->child_pid = 0;
+- data->child_watch_source = NULL;
+-
+- /* we're done */
+- g_simple_async_result_complete_in_idle (data->simple);
+- g_object_unref (data->simple);
+-}
+-
+-static gboolean
+-utils_timeout_cb (gpointer user_data)
+-{
+- UtilsSpawnData *data = (UtilsSpawnData *)user_data;
+-
+- data->timed_out = TRUE;
+-
+- /* ok, timeout is history, make sure we don't free it in spawn_data_free() */
+- data->timeout_source = NULL;
+-
+- /* we're done */
+- g_simple_async_result_complete_in_idle (data->simple);
+- g_object_unref (data->simple);
+-
+- return FALSE; /* remove source */
+-}
+-
+-static void
+-utils_spawn (const gchar *const *argv,
+- guint timeout_seconds,
+- GCancellable *cancellable,
+- GAsyncReadyCallback callback,
+- gpointer user_data)
+-{
+- UtilsSpawnData *data;
+- GError *error;
+-
+- data = g_slice_new0 (UtilsSpawnData);
+- data->timeout_seconds = timeout_seconds;
+- data->simple = g_simple_async_result_new (NULL,
+- callback,
+- user_data,
+- (gpointer*)utils_spawn);
+- data->main_context = g_main_context_get_thread_default ();
+- if (data->main_context != NULL)
+- g_main_context_ref (data->main_context);
+-
+- data->cancellable = cancellable != NULL ? (GCancellable*)g_object_ref (cancellable) : NULL;
+-
+- data->child_stdout = g_string_new (NULL);
+- data->child_stderr = g_string_new (NULL);
+- data->child_stdout_fd = -1;
+- data->child_stderr_fd = -1;
+-
+- /* the life-cycle of UtilsSpawnData is tied to its GSimpleAsyncResult */
+- g_simple_async_result_set_op_res_gpointer (data->simple, data, (GDestroyNotify) utils_spawn_data_free);
+-
+- error = NULL;
+- if (data->cancellable != NULL)
+- {
+- /* could already be cancelled */
+- error = NULL;
+- if (g_cancellable_set_error_if_cancelled (data->cancellable, &error))
+- {
+- g_simple_async_result_take_error (data->simple, error);
+- g_simple_async_result_complete_in_idle (data->simple);
+- g_object_unref (data->simple);
+- goto out;
+- }
+-
+- data->cancellable_handler_id = g_cancellable_connect (data->cancellable,
+- G_CALLBACK (utils_on_cancelled),
+- data,
+- NULL);
+- }
+-
+- error = NULL;
+- if (!g_spawn_async_with_pipes (NULL, /* working directory */
+- (gchar **) argv,
+- NULL, /* envp */
+- G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
+- NULL, /* child_setup */
+- NULL, /* child_setup's user_data */
+- &(data->child_pid),
+- NULL, /* gint *stdin_fd */
+- &(data->child_stdout_fd),
+- &(data->child_stderr_fd),
+- &error))
+- {
+- g_prefix_error (&error, "Error spawning: ");
+- g_simple_async_result_take_error (data->simple, error);
+- g_simple_async_result_complete_in_idle (data->simple);
+- g_object_unref (data->simple);
+- goto out;
+- }
+-
+- if (timeout_seconds > 0)
+- {
+- data->timeout_source = g_timeout_source_new_seconds (timeout_seconds);
+- g_source_set_priority (data->timeout_source, G_PRIORITY_DEFAULT);
+- g_source_set_callback (data->timeout_source, utils_timeout_cb, data, NULL);
+- g_source_attach (data->timeout_source, data->main_context);
+- g_source_unref (data->timeout_source);
+- }
+-
+- data->child_watch_source = g_child_watch_source_new (data->child_pid);
+- g_source_set_callback (data->child_watch_source, (GSourceFunc) utils_child_watch_cb, data, NULL);
+- g_source_attach (data->child_watch_source, data->main_context);
+- g_source_unref (data->child_watch_source);
+-
+- data->child_stdout_channel = g_io_channel_unix_new (data->child_stdout_fd);
+- g_io_channel_set_flags (data->child_stdout_channel, G_IO_FLAG_NONBLOCK, NULL);
+- data->child_stdout_source = g_io_create_watch (data->child_stdout_channel, G_IO_IN);
+- g_source_set_callback (data->child_stdout_source, (GSourceFunc) utils_read_child_stdout, data, NULL);
+- g_source_attach (data->child_stdout_source, data->main_context);
+- g_source_unref (data->child_stdout_source);
+-
+- data->child_stderr_channel = g_io_channel_unix_new (data->child_stderr_fd);
+- g_io_channel_set_flags (data->child_stderr_channel, G_IO_FLAG_NONBLOCK, NULL);
+- data->child_stderr_source = g_io_create_watch (data->child_stderr_channel, G_IO_IN);
+- g_source_set_callback (data->child_stderr_source, (GSourceFunc) utils_read_child_stderr, data, NULL);
+- g_source_attach (data->child_stderr_source, data->main_context);
+- g_source_unref (data->child_stderr_source);
+-
+- out:
+- ;
+-}
+-
+-gboolean
+-utils_spawn_finish (GAsyncResult *res,
+- gint *out_exit_status,
+- gchar **out_standard_output,
+- gchar **out_standard_error,
+- GError **error)
+-{
+- GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+- UtilsSpawnData *data;
+- gboolean ret = FALSE;
+-
+- g_return_val_if_fail (G_IS_ASYNC_RESULT (res), FALSE);
+- g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+-
+- g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == utils_spawn);
+-
+- if (g_simple_async_result_propagate_error (simple, error))
+- goto out;
+-
+- data = (UtilsSpawnData*)g_simple_async_result_get_op_res_gpointer (simple);
+-
+- if (data->timed_out)
+- {
+- g_set_error (error,
+- G_IO_ERROR,
+- G_IO_ERROR_TIMED_OUT,
+- "Timed out after %d seconds",
+- data->timeout_seconds);
+- goto out;
+- }
+-
+- if (out_exit_status != NULL)
+- *out_exit_status = data->exit_status;
+-
+- if (out_standard_output != NULL)
+- *out_standard_output = g_strdup (data->child_stdout->str);
+-
+- if (out_standard_error != NULL)
+- *out_standard_error = g_strdup (data->child_stderr->str);
+-
+- ret = TRUE;
+-
+- out:
+- return ret;
+-}
+diff --git a/src/polkitbackend/polkitbackendjsauthority.cpp b/src/polkitbackend/polkitbackendjsauthority.cpp
+index ca17108..e28091d 100644
+--- a/src/polkitbackend/polkitbackendjsauthority.cpp
++++ b/src/polkitbackend/polkitbackendjsauthority.cpp
+@@ -19,29 +19,7 @@
+ * Author: David Zeuthen
+ */
+
+-#include "config.h"
+-#include
+-#include
+-#include
+-#include
+-#ifdef HAVE_NETGROUP_H
+-#include
+-#else
+-#include
+-#endif
+-#include
+-#include
+-#include
+-#include
+-
+-#include
+-#include "polkitbackendjsauthority.h"
+-
+-#include
+-
+-#ifdef HAVE_LIBSYSTEMD
+-#include
+-#endif /* HAVE_LIBSYSTEMD */
++#include "polkitbackendcommon.h"
+
+ #include
+ #include
+@@ -52,6 +30,7 @@
+ #include
+ #include
+
++/* Built source and not too big to worry about deduplication */
+ #include "initjs.h" /* init.js */
+
+ #ifdef JSGC_USE_EXACT_ROOTING
+@@ -67,10 +46,9 @@
+ * @short_description: JS Authority
+ * @stability: Unstable
+ *
+- * An implementation of #PolkitBackendAuthority that reads and
+- * evalates Javascript files and supports interaction with
+- * authentication agents (virtue of being based on
+- * #PolkitBackendInteractiveAuthority).
++ * An (SpiderMonkey-based) implementation of #PolkitBackendAuthority that reads
++ * and evaluates Javascript files and supports interaction with authentication
++ * agents (virtue of being based on #PolkitBackendInteractiveAuthority).
+ */
+
+ /* ---------------------------------------------------------------------------------------------------- */
+@@ -100,57 +78,11 @@ static bool execute_script_with_runaway_killer (PolkitBackendJsAuthority *author
+ JS::HandleScript script,
+ JS::MutableHandleValue rval);
+
+-static void utils_spawn (const gchar *const *argv,
+- guint timeout_seconds,
+- GCancellable *cancellable,
+- GAsyncReadyCallback callback,
+- gpointer user_data);
+-
+-gboolean utils_spawn_finish (GAsyncResult *res,
+- gint *out_exit_status,
+- gchar **out_standard_output,
+- gchar **out_standard_error,
+- GError **error);
+-
+-static void on_dir_monitor_changed (GFileMonitor *monitor,
+- GFile *file,
+- GFile *other_file,
+- GFileMonitorEvent event_type,
+- gpointer user_data);
+-
+-/* ---------------------------------------------------------------------------------------------------- */
+-
+-enum
+-{
+- PROP_0,
+- PROP_RULES_DIRS,
+-};
+-
+ /* ---------------------------------------------------------------------------------------------------- */
+
+ static gpointer runaway_killer_thread_func (gpointer user_data);
+ static void runaway_killer_terminate (PolkitBackendJsAuthority *authority);
+
+-static GList *polkit_backend_js_authority_get_admin_auth_identities (PolkitBackendInteractiveAuthority *authority,
+- PolkitSubject *caller,
+- PolkitSubject *subject,
+- PolkitIdentity *user_for_subject,
+- gboolean subject_is_local,
+- gboolean subject_is_active,
+- const gchar *action_id,
+- PolkitDetails *details);
+-
+-static PolkitImplicitAuthorization polkit_backend_js_authority_check_authorization_sync (
+- PolkitBackendInteractiveAuthority *authority,
+- PolkitSubject *caller,
+- PolkitSubject *subject,
+- PolkitIdentity *user_for_subject,
+- gboolean subject_is_local,
+- gboolean subject_is_active,
+- const gchar *action_id,
+- PolkitDetails *details,
+- PolkitImplicitAuthorization implicit);
+-
+ G_DEFINE_TYPE (PolkitBackendJsAuthority, polkit_backend_js_authority, POLKIT_BACKEND_TYPE_INTERACTIVE_AUTHORITY);
+
+ /* ---------------------------------------------------------------------------------------------------- */
+@@ -229,33 +161,6 @@ polkit_backend_js_authority_init (PolkitBackendJsAuthority *authority)
+ PolkitBackendJsAuthorityPrivate);
+ }
+
+-static gint
+-rules_file_name_cmp (const gchar *a,
+- const gchar *b)
+-{
+- gint ret;
+- const gchar *a_base;
+- const gchar *b_base;
+-
+- a_base = strrchr (a, '/');
+- b_base = strrchr (b, '/');
+-
+- g_assert (a_base != NULL);
+- g_assert (b_base != NULL);
+- a_base += 1;
+- b_base += 1;
+-
+- ret = g_strcmp0 (a_base, b_base);
+- if (ret == 0)
+- {
+- /* /etc wins over /usr */
+- ret = g_strcmp0 (a, b);
+- g_assert (ret != 0);
+- }
+-
+- return ret;
+-}
+-
+ /* authority->priv->cx must be within a request */
+ static void
+ load_scripts (PolkitBackendJsAuthority *authority)
+@@ -299,7 +204,7 @@ load_scripts (PolkitBackendJsAuthority *authority)
+ }
+ }
+
+- files = g_list_sort (files, (GCompareFunc) rules_file_name_cmp);
++ files = g_list_sort (files, (GCompareFunc) polkit_backend_common_rules_file_name_cmp);
+
+ for (l = files; l != NULL; l = l->next)
+ {
+@@ -365,8 +270,8 @@ load_scripts (PolkitBackendJsAuthority *authority)
+ g_list_free_full (files, g_free);
+ }
+
+-static void
+-reload_scripts (PolkitBackendJsAuthority *authority)
++void
++polkit_backend_common_reload_scripts (PolkitBackendJsAuthority *authority)
+ {
+ JS::RootedValueArray<1> args(authority->priv->cx);
+ JS::RootedValue rval(authority->priv->cx);
+@@ -395,42 +300,6 @@ reload_scripts (PolkitBackendJsAuthority *authority)
+ g_signal_emit_by_name (authority, "changed");
+ }
+
+-static void
+-on_dir_monitor_changed (GFileMonitor *monitor,
+- GFile *file,
+- GFile *other_file,
+- GFileMonitorEvent event_type,
+- gpointer user_data)
+-{
+- PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (user_data);
+-
+- /* TODO: maybe rate-limit so storms of events are collapsed into one with a 500ms resolution?
+- * Because when editing a file with emacs we get 4-8 events..
+- */
+-
+- if (file != NULL)
+- {
+- gchar *name;
+-
+- name = g_file_get_basename (file);
+-
+- /* g_print ("event_type=%d file=%p name=%s\n", event_type, file, name); */
+- if (!g_str_has_prefix (name, ".") &&
+- !g_str_has_prefix (name, "#") &&
+- g_str_has_suffix (name, ".rules") &&
+- (event_type == G_FILE_MONITOR_EVENT_CREATED ||
+- event_type == G_FILE_MONITOR_EVENT_DELETED ||
+- event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT))
+- {
+- polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
+- "Reloading rules");
+- reload_scripts (authority);
+- }
+- g_free (name);
+- }
+-}
+-
+-
+ static void
+ setup_file_monitors (PolkitBackendJsAuthority *authority)
+ {
+@@ -462,7 +331,7 @@ setup_file_monitors (PolkitBackendJsAuthority *authority)
+ {
+ g_signal_connect (monitor,
+ "changed",
+- G_CALLBACK (on_dir_monitor_changed),
++ G_CALLBACK (polkit_backend_common_on_dir_monitor_changed),
+ authority);
+ g_ptr_array_add (p, monitor);
+ }
+@@ -471,8 +340,8 @@ setup_file_monitors (PolkitBackendJsAuthority *authority)
+ authority->priv->dir_monitors = (GFileMonitor**) g_ptr_array_free (p, FALSE);
+ }
+
+-static void
+-polkit_backend_js_authority_constructed (GObject *object)
++void
++polkit_backend_common_js_authority_constructed (GObject *object)
+ {
+ PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (object);
+
+@@ -561,8 +430,8 @@ polkit_backend_js_authority_constructed (GObject *object)
+ g_assert_not_reached ();
+ }
+
+-static void
+-polkit_backend_js_authority_finalize (GObject *object)
++void
++polkit_backend_common_js_authority_finalize (GObject *object)
+ {
+ PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (object);
+ guint n;
+@@ -577,7 +446,7 @@ polkit_backend_js_authority_finalize (GObject *object)
+ {
+ GFileMonitor *monitor = authority->priv->dir_monitors[n];
+ g_signal_handlers_disconnect_by_func (monitor,
+- (gpointer*)G_CALLBACK (on_dir_monitor_changed),
++ (gpointer*)G_CALLBACK (polkit_backend_common_on_dir_monitor_changed),
+ authority);
+ g_object_unref (monitor);
+ }
+@@ -594,11 +463,11 @@ polkit_backend_js_authority_finalize (GObject *object)
+ G_OBJECT_CLASS (polkit_backend_js_authority_parent_class)->finalize (object);
+ }
+
+-static void
+-polkit_backend_js_authority_set_property (GObject *object,
+- guint property_id,
+- const GValue *value,
+- GParamSpec *pspec)
++void
++polkit_backend_common_js_authority_set_property (GObject *object,
++ guint property_id,
++ const GValue *value,
++ GParamSpec *pspec)
+ {
+ PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (object);
+
+@@ -615,57 +484,12 @@ polkit_backend_js_authority_set_property (GObject *object,
+ }
+ }
+
+-static const gchar *
+-polkit_backend_js_authority_get_name (PolkitBackendAuthority *authority)
+-{
+- return "js";
+-}
+-
+-static const gchar *
+-polkit_backend_js_authority_get_version (PolkitBackendAuthority *authority)
+-{
+- return PACKAGE_VERSION;
+-}
+-
+-static PolkitAuthorityFeatures
+-polkit_backend_js_authority_get_features (PolkitBackendAuthority *authority)
+-{
+- return POLKIT_AUTHORITY_FEATURES_TEMPORARY_AUTHORIZATION;
+-}
+-
+ static void
+ polkit_backend_js_authority_class_init (PolkitBackendJsAuthorityClass *klass)
+ {
+- GObjectClass *gobject_class;
+- PolkitBackendAuthorityClass *authority_class;
+- PolkitBackendInteractiveAuthorityClass *interactive_authority_class;
+-
+-
+- gobject_class = G_OBJECT_CLASS (klass);
+- gobject_class->finalize = polkit_backend_js_authority_finalize;
+- gobject_class->set_property = polkit_backend_js_authority_set_property;
+- gobject_class->constructed = polkit_backend_js_authority_constructed;
+-
+- authority_class = POLKIT_BACKEND_AUTHORITY_CLASS (klass);
+- authority_class->get_name = polkit_backend_js_authority_get_name;
+- authority_class->get_version = polkit_backend_js_authority_get_version;
+- authority_class->get_features = polkit_backend_js_authority_get_features;
+-
+- interactive_authority_class = POLKIT_BACKEND_INTERACTIVE_AUTHORITY_CLASS (klass);
+- interactive_authority_class->get_admin_identities = polkit_backend_js_authority_get_admin_auth_identities;
+- interactive_authority_class->check_authorization_sync = polkit_backend_js_authority_check_authorization_sync;
+-
+- g_object_class_install_property (gobject_class,
+- PROP_RULES_DIRS,
+- g_param_spec_boxed ("rules-dirs",
+- NULL,
+- NULL,
+- G_TYPE_STRV,
+- GParamFlags(G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE)));
+-
++ polkit_backend_common_js_authority_class_init_common (klass);
+
+ g_type_class_add_private (klass, sizeof (PolkitBackendJsAuthorityPrivate));
+-
+ JS_Init ();
+ }
+
+@@ -1099,15 +923,15 @@ call_js_function_with_runaway_killer (PolkitBackendJsAuthority *authority,
+
+ /* ---------------------------------------------------------------------------------------------------- */
+
+-static GList *
+-polkit_backend_js_authority_get_admin_auth_identities (PolkitBackendInteractiveAuthority *_authority,
+- PolkitSubject *caller,
+- PolkitSubject *subject,
+- PolkitIdentity *user_for_subject,
+- gboolean subject_is_local,
+- gboolean subject_is_active,
+- const gchar *action_id,
+- PolkitDetails *details)
++GList *
++polkit_backend_common_js_authority_get_admin_auth_identities (PolkitBackendInteractiveAuthority *_authority,
++ PolkitSubject *caller,
++ PolkitSubject *subject,
++ PolkitIdentity *user_for_subject,
++ gboolean subject_is_local,
++ gboolean subject_is_active,
++ const gchar *action_id,
++ PolkitDetails *details)
+ {
+ PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (_authority);
+ GList *ret = NULL;
+@@ -1202,16 +1026,16 @@ polkit_backend_js_authority_get_admin_auth_identities (PolkitBackendInteractiveA
+
+ /* ---------------------------------------------------------------------------------------------------- */
+
+-static PolkitImplicitAuthorization
+-polkit_backend_js_authority_check_authorization_sync (PolkitBackendInteractiveAuthority *_authority,
+- PolkitSubject *caller,
+- PolkitSubject *subject,
+- PolkitIdentity *user_for_subject,
+- gboolean subject_is_local,
+- gboolean subject_is_active,
+- const gchar *action_id,
+- PolkitDetails *details,
+- PolkitImplicitAuthorization implicit)
++PolkitImplicitAuthorization
++polkit_backend_common_js_authority_check_authorization_sync (PolkitBackendInteractiveAuthority *_authority,
++ PolkitSubject *caller,
++ PolkitSubject *subject,
++ PolkitIdentity *user_for_subject,
++ gboolean subject_is_local,
++ gboolean subject_is_active,
++ const gchar *action_id,
++ PolkitDetails *details,
++ PolkitImplicitAuthorization implicit)
+ {
+ PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (_authority);
+ PolkitImplicitAuthorization ret = implicit;
+@@ -1324,65 +1148,6 @@ js_polkit_log (JSContext *cx,
+
+ /* ---------------------------------------------------------------------------------------------------- */
+
+-static const gchar *
+-get_signal_name (gint signal_number)
+-{
+- switch (signal_number)
+- {
+-#define _HANDLE_SIG(sig) case sig: return #sig;
+- _HANDLE_SIG (SIGHUP);
+- _HANDLE_SIG (SIGINT);
+- _HANDLE_SIG (SIGQUIT);
+- _HANDLE_SIG (SIGILL);
+- _HANDLE_SIG (SIGABRT);
+- _HANDLE_SIG (SIGFPE);
+- _HANDLE_SIG (SIGKILL);
+- _HANDLE_SIG (SIGSEGV);
+- _HANDLE_SIG (SIGPIPE);
+- _HANDLE_SIG (SIGALRM);
+- _HANDLE_SIG (SIGTERM);
+- _HANDLE_SIG (SIGUSR1);
+- _HANDLE_SIG (SIGUSR2);
+- _HANDLE_SIG (SIGCHLD);
+- _HANDLE_SIG (SIGCONT);
+- _HANDLE_SIG (SIGSTOP);
+- _HANDLE_SIG (SIGTSTP);
+- _HANDLE_SIG (SIGTTIN);
+- _HANDLE_SIG (SIGTTOU);
+- _HANDLE_SIG (SIGBUS);
+-#ifdef SIGPOLL
+- _HANDLE_SIG (SIGPOLL);
+-#endif
+- _HANDLE_SIG (SIGPROF);
+- _HANDLE_SIG (SIGSYS);
+- _HANDLE_SIG (SIGTRAP);
+- _HANDLE_SIG (SIGURG);
+- _HANDLE_SIG (SIGVTALRM);
+- _HANDLE_SIG (SIGXCPU);
+- _HANDLE_SIG (SIGXFSZ);
+-#undef _HANDLE_SIG
+- default:
+- break;
+- }
+- return "UNKNOWN_SIGNAL";
+-}
+-
+-typedef struct
+-{
+- GMainLoop *loop;
+- GAsyncResult *res;
+-} SpawnData;
+-
+-static void
+-spawn_cb (GObject *source_object,
+- GAsyncResult *res,
+- gpointer user_data)
+-{
+- SpawnData *data = (SpawnData *)user_data;
+- data->res = (GAsyncResult*)g_object_ref (res);
+- g_main_loop_quit (data->loop);
+-}
+-
+ static bool
+ js_polkit_spawn (JSContext *cx,
+ unsigned js_argc,
+@@ -1440,21 +1205,21 @@ js_polkit_spawn (JSContext *cx,
+ g_main_context_push_thread_default (context);
+
+ data.loop = loop;
+- utils_spawn ((const gchar *const *) argv,
+- 10, /* timeout_seconds */
+- NULL, /* cancellable */
+- spawn_cb,
+- &data);
++ polkit_backend_common_spawn ((const gchar *const *) argv,
++ 10, /* timeout_seconds */
++ NULL, /* cancellable */
++ polkit_backend_common_spawn_cb,
++ &data);
+
+ g_main_loop_run (loop);
+
+ g_main_context_pop_thread_default (context);
+
+- if (!utils_spawn_finish (data.res,
+- &exit_status,
+- &standard_output,
+- &standard_error,
+- &error))
++ if (!polkit_backend_common_spawn_finish (data.res,
++ &exit_status,
++ &standard_output,
++ &standard_error,
++ &error))
+ {
+ JS_ReportErrorUTF8 (cx,
+ "Error spawning helper: %s (%s, %d)",
+@@ -1477,7 +1242,7 @@ js_polkit_spawn (JSContext *cx,
+ {
+ g_string_append_printf (gstr,
+ "Helper was signaled with signal %s (%d)",
+- get_signal_name (WTERMSIG (exit_status)),
++ polkit_backend_common_get_signal_name (WTERMSIG (exit_status)),
+ WTERMSIG (exit_status));
+ }
+ g_string_append_printf (gstr, ", stdout=`%s', stderr=`%s'",
+@@ -1542,381 +1307,5 @@ js_polkit_user_is_in_netgroup (JSContext *cx,
+ return ret;
+ }
+
+-
+-
+ /* ---------------------------------------------------------------------------------------------------- */
+
+-typedef struct
+-{
+- GSimpleAsyncResult *simple; /* borrowed reference */
+- GMainContext *main_context; /* may be NULL */
+-
+- GCancellable *cancellable; /* may be NULL */
+- gulong cancellable_handler_id;
+-
+- GPid child_pid;
+- gint child_stdout_fd;
+- gint child_stderr_fd;
+-
+- GIOChannel *child_stdout_channel;
+- GIOChannel *child_stderr_channel;
+-
+- GSource *child_watch_source;
+- GSource *child_stdout_source;
+- GSource *child_stderr_source;
+-
+- guint timeout_seconds;
+- gboolean timed_out;
+- GSource *timeout_source;
+-
+- GString *child_stdout;
+- GString *child_stderr;
+-
+- gint exit_status;
+-} UtilsSpawnData;
+-
+-static void
+-utils_child_watch_from_release_cb (GPid pid,
+- gint status,
+- gpointer user_data)
+-{
+-}
+-
+-static void
+-utils_spawn_data_free (UtilsSpawnData *data)
+-{
+- if (data->timeout_source != NULL)
+- {
+- g_source_destroy (data->timeout_source);
+- data->timeout_source = NULL;
+- }
+-
+- /* Nuke the child, if necessary */
+- if (data->child_watch_source != NULL)
+- {
+- g_source_destroy (data->child_watch_source);
+- data->child_watch_source = NULL;
+- }
+-
+- if (data->child_pid != 0)
+- {
+- GSource *source;
+- kill (data->child_pid, SIGTERM);
+- /* OK, we need to reap for the child ourselves - we don't want
+- * to use waitpid() because that might block the calling
+- * thread (the child might handle SIGTERM and use several
+- * seconds for cleanup/rollback).
+- *
+- * So we use GChildWatch instead.
+- *
+- * Avoid taking a references to ourselves. but note that we need
+- * to pass the GSource so we can nuke it once handled.
+- */
+- source = g_child_watch_source_new (data->child_pid);
+- g_source_set_callback (source,
+- (GSourceFunc) utils_child_watch_from_release_cb,
+- source,
+- (GDestroyNotify) g_source_destroy);
+- /* attach source to the global default main context */
+- g_source_attach (source, NULL);
+- g_source_unref (source);
+- data->child_pid = 0;
+- }
+-
+- if (data->child_stdout != NULL)
+- {
+- g_string_free (data->child_stdout, TRUE);
+- data->child_stdout = NULL;
+- }
+-
+- if (data->child_stderr != NULL)
+- {
+- g_string_free (data->child_stderr, TRUE);
+- data->child_stderr = NULL;
+- }
+-
+- if (data->child_stdout_channel != NULL)
+- {
+- g_io_channel_unref (data->child_stdout_channel);
+- data->child_stdout_channel = NULL;
+- }
+- if (data->child_stderr_channel != NULL)
+- {
+- g_io_channel_unref (data->child_stderr_channel);
+- data->child_stderr_channel = NULL;
+- }
+-
+- if (data->child_stdout_source != NULL)
+- {
+- g_source_destroy (data->child_stdout_source);
+- data->child_stdout_source = NULL;
+- }
+- if (data->child_stderr_source != NULL)
+- {
+- g_source_destroy (data->child_stderr_source);
+- data->child_stderr_source = NULL;
+- }
+-
+- if (data->child_stdout_fd != -1)
+- {
+- g_warn_if_fail (close (data->child_stdout_fd) == 0);
+- data->child_stdout_fd = -1;
+- }
+- if (data->child_stderr_fd != -1)
+- {
+- g_warn_if_fail (close (data->child_stderr_fd) == 0);
+- data->child_stderr_fd = -1;
+- }
+-
+- if (data->cancellable_handler_id > 0)
+- {
+- g_cancellable_disconnect (data->cancellable, data->cancellable_handler_id);
+- data->cancellable_handler_id = 0;
+- }
+-
+- if (data->main_context != NULL)
+- g_main_context_unref (data->main_context);
+-
+- if (data->cancellable != NULL)
+- g_object_unref (data->cancellable);
+-
+- g_slice_free (UtilsSpawnData, data);
+-}
+-
+-/* called in the thread where @cancellable was cancelled */
+-static void
+-utils_on_cancelled (GCancellable *cancellable,
+- gpointer user_data)
+-{
+- UtilsSpawnData *data = (UtilsSpawnData *)user_data;
+- GError *error;
+-
+- error = NULL;
+- g_warn_if_fail (g_cancellable_set_error_if_cancelled (cancellable, &error));
+- g_simple_async_result_take_error (data->simple, error);
+- g_simple_async_result_complete_in_idle (data->simple);
+- g_object_unref (data->simple);
+-}
+-
+-static gboolean
+-utils_read_child_stderr (GIOChannel *channel,
+- GIOCondition condition,
+- gpointer user_data)
+-{
+- UtilsSpawnData *data = (UtilsSpawnData *)user_data;
+- gchar buf[1024];
+- gsize bytes_read;
+-
+- g_io_channel_read_chars (channel, buf, sizeof buf, &bytes_read, NULL);
+- g_string_append_len (data->child_stderr, buf, bytes_read);
+- return TRUE;
+-}
+-
+-static gboolean
+-utils_read_child_stdout (GIOChannel *channel,
+- GIOCondition condition,
+- gpointer user_data)
+-{
+- UtilsSpawnData *data = (UtilsSpawnData *)user_data;
+- gchar buf[1024];
+- gsize bytes_read;
+-
+- g_io_channel_read_chars (channel, buf, sizeof buf, &bytes_read, NULL);
+- g_string_append_len (data->child_stdout, buf, bytes_read);
+- return TRUE;
+-}
+-
+-static void
+-utils_child_watch_cb (GPid pid,
+- gint status,
+- gpointer user_data)
+-{
+- UtilsSpawnData *data = (UtilsSpawnData *)user_data;
+- gchar *buf;
+- gsize buf_size;
+-
+- if (g_io_channel_read_to_end (data->child_stdout_channel, &buf, &buf_size, NULL) == G_IO_STATUS_NORMAL)
+- {
+- g_string_append_len (data->child_stdout, buf, buf_size);
+- g_free (buf);
+- }
+- if (g_io_channel_read_to_end (data->child_stderr_channel, &buf, &buf_size, NULL) == G_IO_STATUS_NORMAL)
+- {
+- g_string_append_len (data->child_stderr, buf, buf_size);
+- g_free (buf);
+- }
+-
+- data->exit_status = status;
+-
+- /* ok, child watch is history, make sure we don't free it in spawn_data_free() */
+- data->child_pid = 0;
+- data->child_watch_source = NULL;
+-
+- /* we're done */
+- g_simple_async_result_complete_in_idle (data->simple);
+- g_object_unref (data->simple);
+-}
+-
+-static gboolean
+-utils_timeout_cb (gpointer user_data)
+-{
+- UtilsSpawnData *data = (UtilsSpawnData *)user_data;
+-
+- data->timed_out = TRUE;
+-
+- /* ok, timeout is history, make sure we don't free it in spawn_data_free() */
+- data->timeout_source = NULL;
+-
+- /* we're done */
+- g_simple_async_result_complete_in_idle (data->simple);
+- g_object_unref (data->simple);
+-
+- return FALSE; /* remove source */
+-}
+-
+-static void
+-utils_spawn (const gchar *const *argv,
+- guint timeout_seconds,
+- GCancellable *cancellable,
+- GAsyncReadyCallback callback,
+- gpointer user_data)
+-{
+- UtilsSpawnData *data;
+- GError *error;
+-
+- data = g_slice_new0 (UtilsSpawnData);
+- data->timeout_seconds = timeout_seconds;
+- data->simple = g_simple_async_result_new (NULL,
+- callback,
+- user_data,
+- (gpointer*)utils_spawn);
+- data->main_context = g_main_context_get_thread_default ();
+- if (data->main_context != NULL)
+- g_main_context_ref (data->main_context);
+-
+- data->cancellable = cancellable != NULL ? (GCancellable*)g_object_ref (cancellable) : NULL;
+-
+- data->child_stdout = g_string_new (NULL);
+- data->child_stderr = g_string_new (NULL);
+- data->child_stdout_fd = -1;
+- data->child_stderr_fd = -1;
+-
+- /* the life-cycle of UtilsSpawnData is tied to its GSimpleAsyncResult */
+- g_simple_async_result_set_op_res_gpointer (data->simple, data, (GDestroyNotify) utils_spawn_data_free);
+-
+- error = NULL;
+- if (data->cancellable != NULL)
+- {
+- /* could already be cancelled */
+- error = NULL;
+- if (g_cancellable_set_error_if_cancelled (data->cancellable, &error))
+- {
+- g_simple_async_result_take_error (data->simple, error);
+- g_simple_async_result_complete_in_idle (data->simple);
+- g_object_unref (data->simple);
+- goto out;
+- }
+-
+- data->cancellable_handler_id = g_cancellable_connect (data->cancellable,
+- G_CALLBACK (utils_on_cancelled),
+- data,
+- NULL);
+- }
+-
+- error = NULL;
+- if (!g_spawn_async_with_pipes (NULL, /* working directory */
+- (gchar **) argv,
+- NULL, /* envp */
+- GSpawnFlags(G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD),
+- NULL, /* child_setup */
+- NULL, /* child_setup's user_data */
+- &(data->child_pid),
+- NULL, /* gint *stdin_fd */
+- &(data->child_stdout_fd),
+- &(data->child_stderr_fd),
+- &error))
+- {
+- g_prefix_error (&error, "Error spawning: ");
+- g_simple_async_result_take_error (data->simple, error);
+- g_simple_async_result_complete_in_idle (data->simple);
+- g_object_unref (data->simple);
+- goto out;
+- }
+-
+- if (timeout_seconds > 0)
+- {
+- data->timeout_source = g_timeout_source_new_seconds (timeout_seconds);
+- g_source_set_priority (data->timeout_source, G_PRIORITY_DEFAULT);
+- g_source_set_callback (data->timeout_source, utils_timeout_cb, data, NULL);
+- g_source_attach (data->timeout_source, data->main_context);
+- g_source_unref (data->timeout_source);
+- }
+-
+- data->child_watch_source = g_child_watch_source_new (data->child_pid);
+- g_source_set_callback (data->child_watch_source, (GSourceFunc) utils_child_watch_cb, data, NULL);
+- g_source_attach (data->child_watch_source, data->main_context);
+- g_source_unref (data->child_watch_source);
+-
+- data->child_stdout_channel = g_io_channel_unix_new (data->child_stdout_fd);
+- g_io_channel_set_flags (data->child_stdout_channel, G_IO_FLAG_NONBLOCK, NULL);
+- data->child_stdout_source = g_io_create_watch (data->child_stdout_channel, G_IO_IN);
+- g_source_set_callback (data->child_stdout_source, (GSourceFunc) utils_read_child_stdout, data, NULL);
+- g_source_attach (data->child_stdout_source, data->main_context);
+- g_source_unref (data->child_stdout_source);
+-
+- data->child_stderr_channel = g_io_channel_unix_new (data->child_stderr_fd);
+- g_io_channel_set_flags (data->child_stderr_channel, G_IO_FLAG_NONBLOCK, NULL);
+- data->child_stderr_source = g_io_create_watch (data->child_stderr_channel, G_IO_IN);
+- g_source_set_callback (data->child_stderr_source, (GSourceFunc) utils_read_child_stderr, data, NULL);
+- g_source_attach (data->child_stderr_source, data->main_context);
+- g_source_unref (data->child_stderr_source);
+-
+- out:
+- ;
+-}
+-
+-gboolean
+-utils_spawn_finish (GAsyncResult *res,
+- gint *out_exit_status,
+- gchar **out_standard_output,
+- gchar **out_standard_error,
+- GError **error)
+-{
+- GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+- UtilsSpawnData *data;
+- gboolean ret = FALSE;
+-
+- g_return_val_if_fail (G_IS_ASYNC_RESULT (res), FALSE);
+- g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+-
+- g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == utils_spawn);
+-
+- if (g_simple_async_result_propagate_error (simple, error))
+- goto out;
+-
+- data = (UtilsSpawnData*)g_simple_async_result_get_op_res_gpointer (simple);
+-
+- if (data->timed_out)
+- {
+- g_set_error (error,
+- G_IO_ERROR,
+- G_IO_ERROR_TIMED_OUT,
+- "Timed out after %d seconds",
+- data->timeout_seconds);
+- goto out;
+- }
+-
+- if (out_exit_status != NULL)
+- *out_exit_status = data->exit_status;
+-
+- if (out_standard_output != NULL)
+- *out_standard_output = g_strdup (data->child_stdout->str);
+-
+- if (out_standard_error != NULL)
+- *out_standard_error = g_strdup (data->child_stderr->str);
+-
+- ret = TRUE;
+-
+- out:
+- return ret;
+-}
+--
+GitLab
+
+
+From 4262c8a5c952e7f5d15d233c1cd2955086b622a9 Mon Sep 17 00:00:00 2001
+From: Gustavo Lima Chaves
+Date: Mon, 20 Sep 2021 17:17:26 -0700
+Subject: [PATCH 16/17] duktape: implement runaway scripts killer timeout
+
+This was missing on Duktape's JS backend proposal, now in. As
+discussed in
+https://gitlab.freedesktop.org/polkit/polkit/-/merge_requests/35 and
+verified by the commit author, Duktape has no interrupt injection
+mechanism (it has no thread-safe API entry whatsoever, even). Using
+DUK_USE_EXEC_TIMEOUT_CHECK is also not feasible, because:
+
+ i) It must be enabled at build time and shared object builds of the
+ lib on distros go with the default options, something we cannot
+ change/control
+
+ ii) That does not account for non-ECMAScript explicit execution
+ contexts, like regex execution, native C calls, etc.
+
+It has been agreed, on that thread, that pthread_cond_timedwait()-ing
+and having proper Duktape evaluation/execution calls take place in a
+separate thread, to be killed after the runaway script killer's
+accorded timeout value, a reasonable approach. We have considered
+using glib wrappers for direct pthread usage, but that way would make
+it impossible to issue
+pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, ...) natively, and
+we want to be paranoid in that regard. Worse than that, they have no
+exposure of pthread_cancel() either. We go for pthreads direct usage,
+then. Fancy threading build system checks courtesy of Evgeny Grin.
+
+On Duktape, we don't get to err from the JS context (to be captured by
+the offending script), but to be forcibly killed on timeout scenarios,
+leading to null returns, thus polkit negation, by definition. It's a
+reasonable design/compromise.
+
+A fatal error handler routine, for the Duktape context, has also been
+added, using the polkit_backend_authority_log() logging infra to
+better assist users on what went wrong.
+
+Finally, the script evaluation routine has been made to use
+duk_peval_lstring() (previously using _noresult variant), so to able
+to present the user with proper error messages, should any occur.
+
+NB: The original runaway script killer test has been adjusted to
+please both JS backends and we now also require Duktape version >=
+2.0.0 on meson builds as well (removing the code checks for previous
+versions).
+
+Signed-off-by: Gustavo Lima Chaves
+Co-authored-by: Evgeny Grin
+---
+ buildutil/ax_pthread.m4 | 522 ++++++++++++++++++
+ configure.ac | 8 +-
+ meson.build | 7 +-
+ src/polkitbackend/Makefile.am | 2 +
+ src/polkitbackend/meson.build | 3 +
+ src/polkitbackend/polkitbackendcommon.h | 2 +
+ .../polkitbackendduktapeauthority.c | 329 +++++++++--
+ .../polkitbackendjsauthority.cpp | 10 +-
+ .../etc/polkit-1/rules.d/10-testing.rules | 6 +-
+ .../test-polkitbackendjsauthority.c | 2 +-
+ 10 files changed, 836 insertions(+), 55 deletions(-)
+ create mode 100644 buildutil/ax_pthread.m4
+
+diff --git a/buildutil/ax_pthread.m4 b/buildutil/ax_pthread.m4
+new file mode 100644
+index 0000000..9f35d13
+--- /dev/null
++++ b/buildutil/ax_pthread.m4
+@@ -0,0 +1,522 @@
++# ===========================================================================
++# https://www.gnu.org/software/autoconf-archive/ax_pthread.html
++# ===========================================================================
++#
++# SYNOPSIS
++#
++# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
++#
++# DESCRIPTION
++#
++# This macro figures out how to build C programs using POSIX threads. It
++# sets the PTHREAD_LIBS output variable to the threads library and linker
++# flags, and the PTHREAD_CFLAGS output variable to any special C compiler
++# flags that are needed. (The user can also force certain compiler
++# flags/libs to be tested by setting these environment variables.)
++#
++# Also sets PTHREAD_CC and PTHREAD_CXX to any special C compiler that is
++# needed for multi-threaded programs (defaults to the value of CC
++# respectively CXX otherwise). (This is necessary on e.g. AIX to use the
++# special cc_r/CC_r compiler alias.)
++#
++# NOTE: You are assumed to not only compile your program with these flags,
++# but also to link with them as well. For example, you might link with
++# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
++# $PTHREAD_CXX $CXXFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
++#
++# If you are only building threaded programs, you may wish to use these
++# variables in your default LIBS, CFLAGS, and CC:
++#
++# LIBS="$PTHREAD_LIBS $LIBS"
++# CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
++# CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS"
++# CC="$PTHREAD_CC"
++# CXX="$PTHREAD_CXX"
++#
++# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
++# has a nonstandard name, this macro defines PTHREAD_CREATE_JOINABLE to
++# that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
++#
++# Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the
++# PTHREAD_PRIO_INHERIT symbol is defined when compiling with
++# PTHREAD_CFLAGS.
++#
++# ACTION-IF-FOUND is a list of shell commands to run if a threads library
++# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it
++# is not found. If ACTION-IF-FOUND is not specified, the default action
++# will define HAVE_PTHREAD.
++#
++# Please let the authors know if this macro fails on any platform, or if
++# you have any other suggestions or comments. This macro was based on work
++# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help
++# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by
++# Alejandro Forero Cuervo to the autoconf macro repository. We are also
++# grateful for the helpful feedback of numerous users.
++#
++# Updated for Autoconf 2.68 by Daniel Richard G.
++#
++# LICENSE
++#
++# Copyright (c) 2008 Steven G. Johnson
++# Copyright (c) 2011 Daniel Richard G.
++# Copyright (c) 2019 Marc Stevens
++#
++# This program is free software: you can redistribute it and/or modify it
++# under the terms of the GNU General Public License as published by the
++# Free Software Foundation, either version 3 of the License, or (at your
++# option) any later version.
++#
++# This program is distributed in the hope that it will be useful, but
++# WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
++# Public License for more details.
++#
++# You should have received a copy of the GNU General Public License along
++# with this program. If not, see .
++#
++# As a special exception, the respective Autoconf Macro's copyright owner
++# gives unlimited permission to copy, distribute and modify the configure
++# scripts that are the output of Autoconf when processing the Macro. You
++# need not follow the terms of the GNU General Public License when using
++# or distributing such scripts, even though portions of the text of the
++# Macro appear in them. The GNU General Public License (GPL) does govern
++# all other use of the material that constitutes the Autoconf Macro.
++#
++# This special exception to the GPL applies to versions of the Autoconf
++# Macro released by the Autoconf Archive. When you make and distribute a
++# modified version of the Autoconf Macro, you may extend this special
++# exception to the GPL to apply to your modified version as well.
++
++#serial 31
++
++AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
++AC_DEFUN([AX_PTHREAD], [
++AC_REQUIRE([AC_CANONICAL_HOST])
++AC_REQUIRE([AC_PROG_CC])
++AC_REQUIRE([AC_PROG_SED])
++AC_LANG_PUSH([C])
++ax_pthread_ok=no
++
++# We used to check for pthread.h first, but this fails if pthread.h
++# requires special compiler flags (e.g. on Tru64 or Sequent).
++# It gets checked for in the link test anyway.
++
++# First of all, check if the user has set any of the PTHREAD_LIBS,
++# etcetera environment variables, and if threads linking works using
++# them:
++if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then
++ ax_pthread_save_CC="$CC"
++ ax_pthread_save_CFLAGS="$CFLAGS"
++ ax_pthread_save_LIBS="$LIBS"
++ AS_IF([test "x$PTHREAD_CC" != "x"], [CC="$PTHREAD_CC"])
++ AS_IF([test "x$PTHREAD_CXX" != "x"], [CXX="$PTHREAD_CXX"])
++ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
++ LIBS="$PTHREAD_LIBS $LIBS"
++ AC_MSG_CHECKING([for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS])
++ AC_LINK_IFELSE([AC_LANG_CALL([], [pthread_join])], [ax_pthread_ok=yes])
++ AC_MSG_RESULT([$ax_pthread_ok])
++ if test "x$ax_pthread_ok" = "xno"; then
++ PTHREAD_LIBS=""
++ PTHREAD_CFLAGS=""
++ fi
++ CC="$ax_pthread_save_CC"
++ CFLAGS="$ax_pthread_save_CFLAGS"
++ LIBS="$ax_pthread_save_LIBS"
++fi
++
++# We must check for the threads library under a number of different
++# names; the ordering is very important because some systems
++# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
++# libraries is broken (non-POSIX).
++
++# Create a list of thread flags to try. Items with a "," contain both
++# C compiler flags (before ",") and linker flags (after ","). Other items
++# starting with a "-" are C compiler flags, and remaining items are
++# library names, except for "none" which indicates that we try without
++# any flags at all, and "pthread-config" which is a program returning
++# the flags for the Pth emulation library.
++
++ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
++
++# The ordering *is* (sometimes) important. Some notes on the
++# individual items follow:
++
++# pthreads: AIX (must check this before -lpthread)
++# none: in case threads are in libc; should be tried before -Kthread and
++# other compiler flags to prevent continual compiler warnings
++# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
++# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads), Tru64
++# (Note: HP C rejects this with "bad form for `-t' option")
++# -pthreads: Solaris/gcc (Note: HP C also rejects)
++# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
++# doesn't hurt to check since this sometimes defines pthreads and
++# -D_REENTRANT too), HP C (must be checked before -lpthread, which
++# is present but should not be used directly; and before -mthreads,
++# because the compiler interprets this as "-mt" + "-hreads")
++# -mthreads: Mingw32/gcc, Lynx/gcc
++# pthread: Linux, etcetera
++# --thread-safe: KAI C++
++# pthread-config: use pthread-config program (for GNU Pth library)
++
++case $host_os in
++
++ freebsd*)
++
++ # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
++ # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
++
++ ax_pthread_flags="-kthread lthread $ax_pthread_flags"
++ ;;
++
++ hpux*)
++
++ # From the cc(1) man page: "[-mt] Sets various -D flags to enable
++ # multi-threading and also sets -lpthread."
++
++ ax_pthread_flags="-mt -pthread pthread $ax_pthread_flags"
++ ;;
++
++ openedition*)
++
++ # IBM z/OS requires a feature-test macro to be defined in order to
++ # enable POSIX threads at all, so give the user a hint if this is
++ # not set. (We don't define these ourselves, as they can affect
++ # other portions of the system API in unpredictable ways.)
++
++ AC_EGREP_CPP([AX_PTHREAD_ZOS_MISSING],
++ [
++# if !defined(_OPEN_THREADS) && !defined(_UNIX03_THREADS)
++ AX_PTHREAD_ZOS_MISSING
++# endif
++ ],
++ [AC_MSG_WARN([IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support.])])
++ ;;
++
++ solaris*)
++
++ # On Solaris (at least, for some versions), libc contains stubbed
++ # (non-functional) versions of the pthreads routines, so link-based
++ # tests will erroneously succeed. (N.B.: The stubs are missing
++ # pthread_cleanup_push, or rather a function called by this macro,
++ # so we could check for that, but who knows whether they'll stub
++ # that too in a future libc.) So we'll check first for the
++ # standard Solaris way of linking pthreads (-mt -lpthread).
++
++ ax_pthread_flags="-mt,-lpthread pthread $ax_pthread_flags"
++ ;;
++esac
++
++# Are we compiling with Clang?
++
++AC_CACHE_CHECK([whether $CC is Clang],
++ [ax_cv_PTHREAD_CLANG],
++ [ax_cv_PTHREAD_CLANG=no
++ # Note that Autoconf sets GCC=yes for Clang as well as GCC
++ if test "x$GCC" = "xyes"; then
++ AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG],
++ [/* Note: Clang 2.7 lacks __clang_[a-z]+__ */
++# if defined(__clang__) && defined(__llvm__)
++ AX_PTHREAD_CC_IS_CLANG
++# endif
++ ],
++ [ax_cv_PTHREAD_CLANG=yes])
++ fi
++ ])
++ax_pthread_clang="$ax_cv_PTHREAD_CLANG"
++
++
++# GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC)
++
++# Note that for GCC and Clang -pthread generally implies -lpthread,
++# except when -nostdlib is passed.
++# This is problematic using libtool to build C++ shared libraries with pthread:
++# [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25460
++# [2] https://bugzilla.redhat.com/show_bug.cgi?id=661333
++# [3] https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=468555
++# To solve this, first try -pthread together with -lpthread for GCC
++
++AS_IF([test "x$GCC" = "xyes"],
++ [ax_pthread_flags="-pthread,-lpthread -pthread -pthreads $ax_pthread_flags"])
++
++# Clang takes -pthread (never supported any other flag), but we'll try with -lpthread first
++
++AS_IF([test "x$ax_pthread_clang" = "xyes"],
++ [ax_pthread_flags="-pthread,-lpthread -pthread"])
++
++
++# The presence of a feature test macro requesting re-entrant function
++# definitions is, on some systems, a strong hint that pthreads support is
++# correctly enabled
++
++case $host_os in
++ darwin* | hpux* | linux* | osf* | solaris*)
++ ax_pthread_check_macro="_REENTRANT"
++ ;;
++
++ aix*)
++ ax_pthread_check_macro="_THREAD_SAFE"
++ ;;
++
++ *)
++ ax_pthread_check_macro="--"
++ ;;
++esac
++AS_IF([test "x$ax_pthread_check_macro" = "x--"],
++ [ax_pthread_check_cond=0],
++ [ax_pthread_check_cond="!defined($ax_pthread_check_macro)"])
++
++
++if test "x$ax_pthread_ok" = "xno"; then
++for ax_pthread_try_flag in $ax_pthread_flags; do
++
++ case $ax_pthread_try_flag in
++ none)
++ AC_MSG_CHECKING([whether pthreads work without any flags])
++ ;;
++
++ *,*)
++ PTHREAD_CFLAGS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\1/"`
++ PTHREAD_LIBS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\2/"`
++ AC_MSG_CHECKING([whether pthreads work with "$PTHREAD_CFLAGS" and "$PTHREAD_LIBS"])
++ ;;
++
++ -*)
++ AC_MSG_CHECKING([whether pthreads work with $ax_pthread_try_flag])
++ PTHREAD_CFLAGS="$ax_pthread_try_flag"
++ ;;
++
++ pthread-config)
++ AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no])
++ AS_IF([test "x$ax_pthread_config" = "xno"], [continue])
++ PTHREAD_CFLAGS="`pthread-config --cflags`"
++ PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
++ ;;
++
++ *)
++ AC_MSG_CHECKING([for the pthreads library -l$ax_pthread_try_flag])
++ PTHREAD_LIBS="-l$ax_pthread_try_flag"
++ ;;
++ esac
++
++ ax_pthread_save_CFLAGS="$CFLAGS"
++ ax_pthread_save_LIBS="$LIBS"
++ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
++ LIBS="$PTHREAD_LIBS $LIBS"
++
++ # Check for various functions. We must include pthread.h,
++ # since some functions may be macros. (On the Sequent, we
++ # need a special flag -Kthread to make this header compile.)
++ # We check for pthread_join because it is in -lpthread on IRIX
++ # while pthread_create is in libc. We check for pthread_attr_init
++ # due to DEC craziness with -lpthreads. We check for
++ # pthread_cleanup_push because it is one of the few pthread
++ # functions on Solaris that doesn't have a non-functional libc stub.
++ # We try pthread_create on general principles.
++
++ AC_LINK_IFELSE([AC_LANG_PROGRAM([#include
++# if $ax_pthread_check_cond
++# error "$ax_pthread_check_macro must be defined"
++# endif
++ static void *some_global = NULL;
++ static void routine(void *a)
++ {
++ /* To avoid any unused-parameter or
++ unused-but-set-parameter warning. */
++ some_global = a;
++ }
++ static void *start_routine(void *a) { return a; }],
++ [pthread_t th; pthread_attr_t attr;
++ pthread_create(&th, 0, start_routine, 0);
++ pthread_join(th, 0);
++ pthread_attr_init(&attr);
++ pthread_cleanup_push(routine, 0);
++ pthread_cleanup_pop(0) /* ; */])],
++ [ax_pthread_ok=yes],
++ [])
++
++ CFLAGS="$ax_pthread_save_CFLAGS"
++ LIBS="$ax_pthread_save_LIBS"
++
++ AC_MSG_RESULT([$ax_pthread_ok])
++ AS_IF([test "x$ax_pthread_ok" = "xyes"], [break])
++
++ PTHREAD_LIBS=""
++ PTHREAD_CFLAGS=""
++done
++fi
++
++
++# Clang needs special handling, because older versions handle the -pthread
++# option in a rather... idiosyncratic way
++
++if test "x$ax_pthread_clang" = "xyes"; then
++
++ # Clang takes -pthread; it has never supported any other flag
++
++ # (Note 1: This will need to be revisited if a system that Clang
++ # supports has POSIX threads in a separate library. This tends not
++ # to be the way of modern systems, but it's conceivable.)
++
++ # (Note 2: On some systems, notably Darwin, -pthread is not needed
++ # to get POSIX threads support; the API is always present and
++ # active. We could reasonably leave PTHREAD_CFLAGS empty. But
++ # -pthread does define _REENTRANT, and while the Darwin headers
++ # ignore this macro, third-party headers might not.)
++
++ # However, older versions of Clang make a point of warning the user
++ # that, in an invocation where only linking and no compilation is
++ # taking place, the -pthread option has no effect ("argument unused
++ # during compilation"). They expect -pthread to be passed in only
++ # when source code is being compiled.
++ #
++ # Problem is, this is at odds with the way Automake and most other
++ # C build frameworks function, which is that the same flags used in
++ # compilation (CFLAGS) are also used in linking. Many systems
++ # supported by AX_PTHREAD require exactly this for POSIX threads
++ # support, and in fact it is often not straightforward to specify a
++ # flag that is used only in the compilation phase and not in
++ # linking. Such a scenario is extremely rare in practice.
++ #
++ # Even though use of the -pthread flag in linking would only print
++ # a warning, this can be a nuisance for well-run software projects
++ # that build with -Werror. So if the active version of Clang has
++ # this misfeature, we search for an option to squash it.
++
++ AC_CACHE_CHECK([whether Clang needs flag to prevent "argument unused" warning when linking with -pthread],
++ [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG],
++ [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown
++ # Create an alternate version of $ac_link that compiles and
++ # links in two steps (.c -> .o, .o -> exe) instead of one
++ # (.c -> exe), because the warning occurs only in the second
++ # step
++ ax_pthread_save_ac_link="$ac_link"
++ ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g'
++ ax_pthread_link_step=`AS_ECHO(["$ac_link"]) | sed "$ax_pthread_sed"`
++ ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)"
++ ax_pthread_save_CFLAGS="$CFLAGS"
++ for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do
++ AS_IF([test "x$ax_pthread_try" = "xunknown"], [break])
++ CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS"
++ ac_link="$ax_pthread_save_ac_link"
++ AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])],
++ [ac_link="$ax_pthread_2step_ac_link"
++ AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])],
++ [break])
++ ])
++ done
++ ac_link="$ax_pthread_save_ac_link"
++ CFLAGS="$ax_pthread_save_CFLAGS"
++ AS_IF([test "x$ax_pthread_try" = "x"], [ax_pthread_try=no])
++ ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try"
++ ])
++
++ case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in
++ no | unknown) ;;
++ *) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;;
++ esac
++
++fi # $ax_pthread_clang = yes
++
++
++
++# Various other checks:
++if test "x$ax_pthread_ok" = "xyes"; then
++ ax_pthread_save_CFLAGS="$CFLAGS"
++ ax_pthread_save_LIBS="$LIBS"
++ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
++ LIBS="$PTHREAD_LIBS $LIBS"
++
++ # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
++ AC_CACHE_CHECK([for joinable pthread attribute],
++ [ax_cv_PTHREAD_JOINABLE_ATTR],
++ [ax_cv_PTHREAD_JOINABLE_ATTR=unknown
++ for ax_pthread_attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
++ AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ],
++ [int attr = $ax_pthread_attr; return attr /* ; */])],
++ [ax_cv_PTHREAD_JOINABLE_ATTR=$ax_pthread_attr; break],
++ [])
++ done
++ ])
++ AS_IF([test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xunknown" && \
++ test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xPTHREAD_CREATE_JOINABLE" && \
++ test "x$ax_pthread_joinable_attr_defined" != "xyes"],
++ [AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE],
++ [$ax_cv_PTHREAD_JOINABLE_ATTR],
++ [Define to necessary symbol if this constant
++ uses a non-standard name on your system.])
++ ax_pthread_joinable_attr_defined=yes
++ ])
++
++ AC_CACHE_CHECK([whether more special flags are required for pthreads],
++ [ax_cv_PTHREAD_SPECIAL_FLAGS],
++ [ax_cv_PTHREAD_SPECIAL_FLAGS=no
++ case $host_os in
++ solaris*)
++ ax_cv_PTHREAD_SPECIAL_FLAGS="-D_POSIX_PTHREAD_SEMANTICS"
++ ;;
++ esac
++ ])
++ AS_IF([test "x$ax_cv_PTHREAD_SPECIAL_FLAGS" != "xno" && \
++ test "x$ax_pthread_special_flags_added" != "xyes"],
++ [PTHREAD_CFLAGS="$ax_cv_PTHREAD_SPECIAL_FLAGS $PTHREAD_CFLAGS"
++ ax_pthread_special_flags_added=yes])
++
++ AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT],
++ [ax_cv_PTHREAD_PRIO_INHERIT],
++ [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]],
++ [[int i = PTHREAD_PRIO_INHERIT;
++ return i;]])],
++ [ax_cv_PTHREAD_PRIO_INHERIT=yes],
++ [ax_cv_PTHREAD_PRIO_INHERIT=no])
++ ])
++ AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes" && \
++ test "x$ax_pthread_prio_inherit_defined" != "xyes"],
++ [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.])
++ ax_pthread_prio_inherit_defined=yes
++ ])
++
++ CFLAGS="$ax_pthread_save_CFLAGS"
++ LIBS="$ax_pthread_save_LIBS"
++
++ # More AIX lossage: compile with *_r variant
++ if test "x$GCC" != "xyes"; then
++ case $host_os in
++ aix*)
++ AS_CASE(["x/$CC"],
++ [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6],
++ [#handle absolute path differently from PATH based program lookup
++ AS_CASE(["x$CC"],
++ [x/*],
++ [
++ AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])
++ AS_IF([test "x${CXX}" != "x"], [AS_IF([AS_EXECUTABLE_P([${CXX}_r])],[PTHREAD_CXX="${CXX}_r"])])
++ ],
++ [
++ AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])
++ AS_IF([test "x${CXX}" != "x"], [AC_CHECK_PROGS([PTHREAD_CXX],[${CXX}_r],[$CXX])])
++ ]
++ )
++ ])
++ ;;
++ esac
++ fi
++fi
++
++test -n "$PTHREAD_CC" || PTHREAD_CC="$CC"
++test -n "$PTHREAD_CXX" || PTHREAD_CXX="$CXX"
++
++AC_SUBST([PTHREAD_LIBS])
++AC_SUBST([PTHREAD_CFLAGS])
++AC_SUBST([PTHREAD_CC])
++AC_SUBST([PTHREAD_CXX])
++
++# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
++if test "x$ax_pthread_ok" = "xyes"; then
++ ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1])
++ :
++else
++ ax_pthread_ok=no
++ $2
++fi
++AC_LANG_POP
++])dnl AX_PTHREAD
+diff --git a/configure.ac b/configure.ac
+index 5a03593..e182238 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -85,7 +85,7 @@ dnl - Check javascript backend
+ dnl ---------------------------------------------------------------------------
+ AC_ARG_WITH(duktape, AS_HELP_STRING([--with-duktape],[Use Duktape as javascript backend]),with_duktape=yes,with_duktape=no)
+ AS_IF([test x${with_duktape} == xyes], [
+- PKG_CHECK_MODULES(LIBJS, [duktape >= 2.0.0 ])
++ PKG_CHECK_MODULES(LIBJS, [duktape >= 2.2.0 ])
+ AC_SUBST(LIBJS_CFLAGS)
+ AC_SUBST(LIBJS_LIBS)
+ ], [
+@@ -111,6 +111,12 @@ AC_CHECK_LIB(expat,XML_ParserCreate,[EXPAT_LIBS="-lexpat"],
+ [AC_MSG_ERROR([Can't find expat library. Please install expat.])])
+ AC_SUBST(EXPAT_LIBS)
+
++AX_PTHREAD([], [AC_MSG_ERROR([Cannot find the way to enable pthread support.])])
++LIBS="$PTHREAD_LIBS $LIBS"
++CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
++CC="$PTHREAD_CC"
++AC_CHECK_FUNCS([pthread_condattr_setclock])
++
+ AC_CHECK_FUNCS(clearenv fdatasync)
+
+ if test "x$GCC" = "xyes"; then
+diff --git a/meson.build b/meson.build
+index 4e44723..ad9ab07 100644
+--- a/meson.build
++++ b/meson.build
+@@ -133,10 +133,15 @@ expat_dep = dependency('expat')
+ assert(cc.has_header('expat.h', dependencies: expat_dep), 'Can\'t find expat.h. Please install expat.')
+ assert(cc.has_function('XML_ParserCreate', dependencies: expat_dep), 'Can\'t find expat library. Please install expat.')
+
++duktape_req_version = '>= 2.2.0'
++
+ js_engine = get_option('js_engine')
+ if js_engine == 'duktape'
+- js_dep = dependency('duktape')
++ js_dep = dependency('duktape', version: duktape_req_version)
+ libm_dep = cc.find_library('m')
++ thread_dep = dependency('threads')
++ func = 'pthread_condattr_setclock'
++ config_h.set('HAVE_' + func.to_upper(), cc.has_function(func, prefix : '#include '))
+ elif js_engine == 'mozjs'
+ js_dep = dependency('mozjs-78')
+ endif
+diff --git a/src/polkitbackend/Makefile.am b/src/polkitbackend/Makefile.am
+index 6a8b4ae..935fb98 100644
+--- a/src/polkitbackend/Makefile.am
++++ b/src/polkitbackend/Makefile.am
+@@ -17,6 +17,8 @@ AM_CPPFLAGS = \
+ -DPACKAGE_LIB_DIR=\""$(libdir)"\" \
+ -D_POSIX_PTHREAD_SEMANTICS \
+ -D_REENTRANT \
++ -D_XOPEN_SOURCE=700 \
++ -D_GNU_SOURCE=1 \
+ $(NULL)
+
+ noinst_LTLIBRARIES=libpolkit-backend-1.la
+diff --git a/src/polkitbackend/meson.build b/src/polkitbackend/meson.build
+index 9ec01b2..266f280 100644
+--- a/src/polkitbackend/meson.build
++++ b/src/polkitbackend/meson.build
+@@ -29,11 +29,14 @@ c_flags = [
+ '-D_POLKIT_BACKEND_COMPILATION',
+ '-DPACKAGE_DATA_DIR="@0@"'.format(pk_prefix / pk_datadir),
+ '-DPACKAGE_SYSCONF_DIR="@0@"'.format(pk_prefix / pk_sysconfdir),
++ '-D_XOPEN_SOURCE=700',
++ '-D_GNU_SOURCE=1',
+ ]
+
+ if js_engine == 'duktape'
+ sources += files('polkitbackendduktapeauthority.c')
+ deps += libm_dep
++ deps += thread_dep
+ elif js_engine == 'mozjs'
+ sources += files('polkitbackendjsauthority.cpp')
+ endif
+diff --git a/src/polkitbackend/polkitbackendcommon.h b/src/polkitbackend/polkitbackendcommon.h
+index 6d0d267..dd700fc 100644
+--- a/src/polkitbackend/polkitbackendcommon.h
++++ b/src/polkitbackend/polkitbackendcommon.h
+@@ -50,6 +50,8 @@
+ #include
+ #endif /* HAVE_LIBSYSTEMD */
+
++#define RUNAWAY_KILLER_TIMEOUT (15)
++
+ #ifdef __cplusplus
+ extern "C" {
+ #endif
+diff --git a/src/polkitbackend/polkitbackendduktapeauthority.c b/src/polkitbackend/polkitbackendduktapeauthority.c
+index a2b4420..c89dbcf 100644
+--- a/src/polkitbackend/polkitbackendduktapeauthority.c
++++ b/src/polkitbackend/polkitbackendduktapeauthority.c
+@@ -21,6 +21,8 @@
+ * Author: David Zeuthen
+ */
+
++#include
++
+ #include "polkitbackendcommon.h"
+
+ #include "duktape.h"
+@@ -47,8 +49,20 @@ struct _PolkitBackendJsAuthorityPrivate
+ GFileMonitor **dir_monitors; /* NULL-terminated array of GFileMonitor instances */
+
+ duk_context *cx;
++
++ pthread_t runaway_killer_thread;
+ };
+
++enum
++{
++ RUNAWAY_KILLER_THREAD_EXIT_STATUS_UNSET,
++ RUNAWAY_KILLER_THREAD_EXIT_STATUS_SUCCESS,
++ RUNAWAY_KILLER_THREAD_EXIT_STATUS_FAILURE,
++};
++
++static gboolean execute_script_with_runaway_killer(PolkitBackendJsAuthority *authority,
++ const gchar *filename);
++
+ /* ---------------------------------------------------------------------------------------------------- */
+
+ G_DEFINE_TYPE (PolkitBackendJsAuthority, polkit_backend_js_authority, POLKIT_BACKEND_TYPE_INTERACTIVE_AUTHORITY);
+@@ -67,6 +81,15 @@ static const duk_function_list_entry js_polkit_functions[] =
+ { NULL, NULL, 0 },
+ };
+
++static void report_error (void *udata,
++ const char *msg)
++{
++ PolkitBackendJsAuthority *authority = udata;
++ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
++ "fatal Duktape JS backend error: %s",
++ (msg ? msg : "no message"));
++}
++
+ static void
+ polkit_backend_js_authority_init (PolkitBackendJsAuthority *authority)
+ {
+@@ -78,7 +101,6 @@ polkit_backend_js_authority_init (PolkitBackendJsAuthority *authority)
+ static void
+ load_scripts (PolkitBackendJsAuthority *authority)
+ {
+- duk_context *cx = authority->priv->cx;
+ GList *files = NULL;
+ GList *l;
+ guint num_scripts = 0;
+@@ -123,36 +145,9 @@ load_scripts (PolkitBackendJsAuthority *authority)
+ for (l = files; l != NULL; l = l->next)
+ {
+ const gchar *filename = (gchar *)l->data;
+-#if (DUK_VERSION >= 20000)
+- GFile *file = g_file_new_for_path (filename);
+- char *contents;
+- gsize len;
+- if (!g_file_load_contents (file, NULL, &contents, &len, NULL, NULL))
+- {
+- polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
+- "Error compiling script %s",
+- filename);
+- g_object_unref (file);
+- continue;
+- }
+
+- g_object_unref (file);
+- if (duk_peval_lstring_noresult(cx, contents,len) != 0)
+-#else
+- if (duk_peval_file_noresult (cx, filename) != 0)
+-#endif
+- {
+- polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
+- "Error compiling script %s: %s",
+- filename, duk_safe_to_string (authority->priv->cx, -1));
+-#if (DUK_VERSION >= 20000)
+- g_free (contents);
+-#endif
++ if (!execute_script_with_runaway_killer(authority, filename))
+ continue;
+- }
+-#if (DUK_VERSION >= 20000)
+- g_free (contents);
+-#endif
+ num_scripts++;
+ }
+
+@@ -232,7 +227,7 @@ polkit_backend_common_js_authority_constructed (GObject *object)
+ PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (object);
+ duk_context *cx;
+
+- cx = duk_create_heap (NULL, NULL, NULL, authority, NULL);
++ cx = duk_create_heap (NULL, NULL, NULL, authority, report_error);
+ if (cx == NULL)
+ goto fail;
+
+@@ -243,6 +238,9 @@ polkit_backend_common_js_authority_constructed (GObject *object)
+ duk_put_function_list (cx, -1, js_polkit_functions);
+ duk_put_prop_string (cx, -2, "polkit");
+
++ /* load polkit objects/functions into JS context (e.g. addRule(),
++ * _deleteRules(), _runRules() et al)
++ */
+ duk_eval_string (cx, init_js);
+
+ if (authority->priv->rules_dirs == NULL)
+@@ -510,6 +508,250 @@ push_action_and_details (duk_context *cx,
+
+ /* ---------------------------------------------------------------------------------------------------- */
+
++typedef struct {
++ PolkitBackendJsAuthority *authority;
++ const gchar *filename;
++ pthread_cond_t cond;
++ pthread_mutex_t mutex;
++ gint ret;
++} RunawayKillerCtx;
++
++static gpointer
++runaway_killer_thread_execute_js (gpointer user_data)
++{
++ RunawayKillerCtx *ctx = user_data;
++ duk_context *cx = ctx->authority->priv->cx;
++
++ int oldtype, pthread_err;
++
++ if ((pthread_err = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype))) {
++ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (ctx->authority),
++ "Error setting thread cancel type: %s",
++ strerror(pthread_err));
++ goto err;
++ }
++
++ GFile *file = g_file_new_for_path(ctx->filename);
++ char *contents;
++ gsize len;
++
++ if (!g_file_load_contents(file, NULL, &contents, &len, NULL, NULL)) {
++ polkit_backend_authority_log(POLKIT_BACKEND_AUTHORITY(ctx->authority),
++ "Error loading script %s", ctx->filename);
++ g_object_unref(file);
++ goto err;
++ }
++
++ g_object_unref(file);
++
++ /* evaluate the script, trying to print context in any syntax errors
++ found */
++ if (duk_peval_lstring(cx, contents, len) != 0)
++ {
++ polkit_backend_authority_log(POLKIT_BACKEND_AUTHORITY(ctx->authority),
++ "Error compiling script %s: %s", ctx->filename,
++ duk_safe_to_string(cx, -1));
++ duk_pop(cx);
++ goto free_err;
++ }
++ g_free(contents);
++
++ ctx->ret = RUNAWAY_KILLER_THREAD_EXIT_STATUS_SUCCESS;
++ goto end;
++
++free_err:
++ g_free(contents);
++err:
++ ctx->ret = RUNAWAY_KILLER_THREAD_EXIT_STATUS_FAILURE;
++end:
++ if ((pthread_err = pthread_cond_signal(&ctx->cond))) {
++ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (ctx->authority),
++ "Error signaling on condition variable: %s",
++ strerror(pthread_err));
++ ctx->ret = RUNAWAY_KILLER_THREAD_EXIT_STATUS_FAILURE;
++ }
++ return NULL;
++}
++
++static gpointer
++runaway_killer_thread_call_js (gpointer user_data)
++{
++ RunawayKillerCtx *ctx = user_data;
++ duk_context *cx = ctx->authority->priv->cx;
++ int oldtype, pthread_err;
++
++ if ((pthread_err = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype))) {
++ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (ctx->authority),
++ "Error setting thread cancel type: %s",
++ strerror(pthread_err));
++ goto err;
++ }
++
++ if (duk_pcall_prop (cx, 0, 2) != DUK_EXEC_SUCCESS)
++ {
++ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (ctx->authority),
++ "Error evaluating admin rules: ",
++ duk_safe_to_string (cx, -1));
++ goto err;
++ }
++
++ ctx->ret = RUNAWAY_KILLER_THREAD_EXIT_STATUS_SUCCESS;
++ goto end;
++
++err:
++ ctx->ret = RUNAWAY_KILLER_THREAD_EXIT_STATUS_FAILURE;
++end:
++ if ((pthread_err = pthread_cond_signal(&ctx->cond))) {
++ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (ctx->authority),
++ "Error signaling on condition variable: %s",
++ strerror(pthread_err));
++ ctx->ret = RUNAWAY_KILLER_THREAD_EXIT_STATUS_FAILURE;
++ }
++ return NULL;
++}
++
++#if defined (HAVE_PTHREAD_CONDATTR_SETCLOCK)
++# if defined(CLOCK_MONOTONIC)
++# define PK_CLOCK CLOCK_MONOTONIC
++# elif defined(CLOCK_BOOTTIME)
++# define PK_CLOCK CLOCK_BOOTTIME
++# else
++ /* No suitable clock */
++# undef HAVE_PTHREAD_CONDATTR_SETCLOCK
++# define PK_CLOCK CLOCK_REALTIME
++# endif
++#else /* ! HAVE_PTHREAD_CONDATTR_SETCLOCK */
++# define PK_CLOCK CLOCK_REALTIME
++#endif /* ! HAVE_PTHREAD_CONDATTR_SETCLOCK */
++
++static gboolean
++runaway_killer_common(PolkitBackendJsAuthority *authority, RunawayKillerCtx *ctx, void *js_context_cb (void *user_data))
++{
++ int pthread_err;
++ gboolean cancel = FALSE;
++ pthread_condattr_t attr;
++ struct timespec abs_time;
++
++#ifdef HAVE_PTHREAD_CONDATTR_SETCLOCK
++ if ((pthread_err = pthread_condattr_init(&attr))) {
++ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
++ "Error initializing condition variable attributes: %s",
++ strerror(pthread_err));
++ return FALSE;
++ }
++ if ((pthread_err = pthread_condattr_setclock(&attr, PK_CLOCK))) {
++ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
++ "Error setting condition variable attributes: %s",
++ strerror(pthread_err));
++ goto err_clean_condattr;
++ }
++ /* Init again, with needed attr */
++ if ((pthread_err = pthread_cond_init(&ctx->cond, &attr))) {
++ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
++ "Error initializing condition variable: %s",
++ strerror(pthread_err));
++ goto err_clean_condattr;
++ }
++#endif
++
++ if ((pthread_err = pthread_mutex_lock(&ctx->mutex))) {
++ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
++ "Error locking mutex: %s",
++ strerror(pthread_err));
++ goto err_clean_cond;
++ }
++
++ if (clock_gettime(PK_CLOCK, &abs_time)) {
++ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
++ "Error getting system's monotonic time: %s",
++ strerror(errno));
++ goto err_clean_cond;
++ }
++ abs_time.tv_sec += RUNAWAY_KILLER_TIMEOUT;
++
++ if ((pthread_err = pthread_create(&authority->priv->runaway_killer_thread, NULL,
++ js_context_cb, ctx))) {
++ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
++ "Error creating runaway JS killer thread: %s",
++ strerror(pthread_err));
++ goto err_clean_cond;
++ }
++
++ while (ctx->ret == RUNAWAY_KILLER_THREAD_EXIT_STATUS_UNSET) /* loop to treat spurious wakeups */
++ if (pthread_cond_timedwait(&ctx->cond, &ctx->mutex, &abs_time) == ETIMEDOUT) {
++ cancel = TRUE;
++
++ /* Log that we are terminating the script */
++ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
++ "Terminating runaway script after %d seconds",
++ RUNAWAY_KILLER_TIMEOUT);
++
++ break;
++ }
++
++ if ((pthread_err = pthread_mutex_unlock(&ctx->mutex))) {
++ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
++ "Error unlocking mutex: %s",
++ strerror(pthread_err));
++ goto err_clean_cond;
++ }
++
++ if (cancel) {
++ if ((pthread_err = pthread_cancel (authority->priv->runaway_killer_thread))) {
++ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
++ "Error cancelling runaway JS killer thread: %s",
++ strerror(pthread_err));
++ goto err_clean_cond;
++ }
++ }
++ if ((pthread_err = pthread_join (authority->priv->runaway_killer_thread, NULL))) {
++ polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
++ "Error joining runaway JS killer thread: %s",
++ strerror(pthread_err));
++ goto err_clean_cond;
++ }
++
++ return ctx->ret == RUNAWAY_KILLER_THREAD_EXIT_STATUS_SUCCESS;
++
++ err_clean_cond:
++#ifdef HAVE_PTHREAD_CONDATTR_SETCLOCK
++ pthread_cond_destroy(&ctx->cond);
++#endif
++ err_clean_condattr:
++#ifdef HAVE_PTHREAD_CONDATTR_SETCLOCK
++ pthread_condattr_destroy(&attr);
++#endif
++ return FALSE;
++}
++
++/* Blocking for at most RUNAWAY_KILLER_TIMEOUT */
++static gboolean
++execute_script_with_runaway_killer(PolkitBackendJsAuthority *authority,
++ const gchar *filename)
++{
++ RunawayKillerCtx ctx = {.authority = authority, .filename = filename,
++ .ret = RUNAWAY_KILLER_THREAD_EXIT_STATUS_UNSET,
++ .mutex = PTHREAD_MUTEX_INITIALIZER,
++ .cond = PTHREAD_COND_INITIALIZER};
++
++ return runaway_killer_common(authority, &ctx, &runaway_killer_thread_execute_js);
++}
++
++/* Calls already stacked function and args. Blocking for at most
++ * RUNAWAY_KILLER_TIMEOUT. If timeout is the case, ctx.ret will be
++ * RUNAWAY_KILLER_THREAD_EXIT_STATUS_UNSET, thus returning FALSE.
++ */
++static gboolean
++call_js_function_with_runaway_killer(PolkitBackendJsAuthority *authority)
++{
++ RunawayKillerCtx ctx = {.authority = authority,
++ .ret = RUNAWAY_KILLER_THREAD_EXIT_STATUS_UNSET,
++ .mutex = PTHREAD_MUTEX_INITIALIZER,
++ .cond = PTHREAD_COND_INITIALIZER};
++
++ return runaway_killer_common(authority, &ctx, &runaway_killer_thread_call_js);
++}
++
+ /* ---------------------------------------------------------------------------------------------------- */
+
+ GList *
+@@ -557,13 +799,8 @@ polkit_backend_common_js_authority_get_admin_auth_identities (PolkitBackendInter
+ goto out;
+ }
+
+- if (duk_pcall_prop (cx, 0, 2) != DUK_ERR_NONE)
+- {
+- polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
+- "Error evaluating admin rules: ",
+- duk_safe_to_string (cx, -1));
+- goto out;
+- }
++ if (!call_js_function_with_runaway_killer (authority))
++ goto out;
+
+ ret_str = duk_require_string (cx, -1);
+
+@@ -643,15 +880,15 @@ polkit_backend_common_js_authority_check_authorization_sync (PolkitBackendIntera
+ goto out;
+ }
+
+- if (duk_pcall_prop (cx, 0, 2) != DUK_ERR_NONE)
+- {
+- polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
+- "Error evaluating authorization rules: ",
+- duk_safe_to_string (cx, -1));
+- goto out;
+- }
++ // If any error is the js context happened (ctx.ret ==
++ // RUNAWAY_KILLER_THREAD_EXIT_STATUS_FAILURE) or it never properly returned
++ // (runaway scripts or ctx.ret == RUNAWAY_KILLER_THREAD_EXIT_STATUS_UNSET),
++ // unauthorize
++ if (!call_js_function_with_runaway_killer (authority))
++ goto out;
+
+ if (duk_is_null(cx, -1)) {
++ /* this is fine, means there was no match, use implicit authorizations */
+ good = TRUE;
+ goto out;
+ }
+@@ -690,11 +927,7 @@ js_polkit_log (duk_context *cx)
+ static duk_ret_t
+ js_polkit_spawn (duk_context *cx)
+ {
+-#if (DUK_VERSION >= 20000)
+ duk_ret_t ret = DUK_RET_ERROR;
+-#else
+- duk_ret_t ret = DUK_RET_INTERNAL_ERROR;
+-#endif
+ gchar *standard_output = NULL;
+ gchar *standard_error = NULL;
+ gint exit_status;
+diff --git a/src/polkitbackend/polkitbackendjsauthority.cpp b/src/polkitbackend/polkitbackendjsauthority.cpp
+index e28091d..11e91c0 100644
+--- a/src/polkitbackend/polkitbackendjsauthority.cpp
++++ b/src/polkitbackend/polkitbackendjsauthority.cpp
+@@ -829,11 +829,14 @@ runaway_killer_setup (PolkitBackendJsAuthority *authority)
+ {
+ g_assert (authority->priv->rkt_source == NULL);
+
+- /* set-up timer for runaway scripts, will be executed in runaway_killer_thread */
++ /* set-up timer for runaway scripts, will be executed in
++ runaway_killer_thread, that is one, permanent thread running a glib
++ mainloop (rkt_loop) whose context (rkt_context) has a timeout source
++ (rkt_source) */
+ g_mutex_lock (&authority->priv->rkt_timeout_pending_mutex);
+ authority->priv->rkt_timeout_pending = FALSE;
+ g_mutex_unlock (&authority->priv->rkt_timeout_pending_mutex);
+- authority->priv->rkt_source = g_timeout_source_new_seconds (15);
++ authority->priv->rkt_source = g_timeout_source_new_seconds (RUNAWAY_KILLER_TIMEOUT);
+ g_source_set_callback (authority->priv->rkt_source, rkt_on_timeout, authority, NULL);
+ g_source_attach (authority->priv->rkt_source, authority->priv->rkt_context);
+
+@@ -893,6 +896,9 @@ execute_script_with_runaway_killer (PolkitBackendJsAuthority *authority,
+ {
+ bool ret;
+
++ // tries to JS_ExecuteScript(), may hang for > RUNAWAY_KILLER_TIMEOUT,
++ // runaway_killer_thread makes sure the call returns, due to exception
++ // injection
+ runaway_killer_setup (authority);
+ ret = JS_ExecuteScript (authority->priv->cx,
+ script,
+diff --git a/test/data/etc/polkit-1/rules.d/10-testing.rules b/test/data/etc/polkit-1/rules.d/10-testing.rules
+index 98bf062..e346b5d 100644
+--- a/test/data/etc/polkit-1/rules.d/10-testing.rules
++++ b/test/data/etc/polkit-1/rules.d/10-testing.rules
+@@ -189,8 +189,10 @@ polkit.addRule(function(action, subject) {
+ ;
+ } catch (error) {
+ if (error == "Terminating runaway script")
+- return polkit.Result.YES;
+- return polkit.Result.NO;
++ // Inverted logic to accomodate Duktape's model as well, which
++ // will always fail with negation, on timeouts
++ return polkit.Result.NO;
++ return polkit.Result.YES;
+ }
+ }
+ });
+diff --git a/test/polkitbackend/test-polkitbackendjsauthority.c b/test/polkitbackend/test-polkitbackendjsauthority.c
+index f97e0e0..2103b17 100644
+--- a/test/polkitbackend/test-polkitbackendjsauthority.c
++++ b/test/polkitbackend/test-polkitbackendjsauthority.c
+@@ -328,7 +328,7 @@ static const RulesTestCase rules_test_cases[] = {
+ "net.company.run_away_script",
+ "unix-user:root",
+ NULL,
+- POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED,
++ POLKIT_IMPLICIT_AUTHORIZATION_NOT_AUTHORIZED,
+ },
+
+ {
+--
+GitLab
+
+
+From 478bcc0b6b4fb44d4199c475d1581d78a8b6c30b Mon Sep 17 00:00:00 2001
+From: Gustavo Lima Chaves
+Date: Thu, 28 Oct 2021 10:04:53 -0700
+Subject: [PATCH 17/17] duktape: document runaway behavior
+
+Signed-off-by: Gustavo Lima Chaves
+---
+ docs/man/polkit.xml | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/docs/man/polkit.xml b/docs/man/polkit.xml
+index 99aa474..90715a5 100644
+--- a/docs/man/polkit.xml
++++ b/docs/man/polkit.xml
+@@ -639,7 +639,9 @@ polkit.Result = {
+ If user-provided code takes a long time to execute, an exception
+ will be thrown which normally results in the function being
+ terminated (the current limit is 15 seconds). This is used to
+- catch runaway scripts.
++ catch runaway scripts. If the duktape JavaScript backend is
++ compiled in, instead of mozjs, no exception will be thrown—the
++ script will be killed right away (same timeout).
+
+
+
+--
+GitLab
+
diff --git a/sdk_container/src/third_party/coreos-overlay/sys-auth/polkit/files/polkit-0.115-elogind.patch b/sdk_container/src/third_party/coreos-overlay/sys-auth/polkit/files/polkit-0.115-elogind.patch
deleted file mode 100644
index 93d672015d..0000000000
--- a/sdk_container/src/third_party/coreos-overlay/sys-auth/polkit/files/polkit-0.115-elogind.patch
+++ /dev/null
@@ -1,28 +0,0 @@
-From 08bb656496cd3d6213bbe9473f63f2d4a110da6e Mon Sep 17 00:00:00 2001
-From: Rasmus Thomsen
-Date: Wed, 11 Apr 2018 13:14:14 +0200
-Subject: [PATCH] configure: fix elogind support
-
-HAVE_LIBSYSTEMD is used to determine which source files to use.
-We have to check if either have_libsystemd or have_libelogind is
-true, as both of these need the source files which are used when
-HAVE_LIBSYSTEMD is true.
----
- configure.ac | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/configure.ac b/configure.ac
-index 36df239..da47ecb 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -221,7 +221,7 @@ AS_IF([test "x$cross_compiling" != "xyes" ], [
-
- AC_SUBST(LIBSYSTEMD_CFLAGS)
- AC_SUBST(LIBSYSTEMD_LIBS)
--AM_CONDITIONAL(HAVE_LIBSYSTEMD, [test "$have_libsystemd" = "yes"], [Using libsystemd])
-+AM_CONDITIONAL(HAVE_LIBSYSTEMD, [test "$have_libsystemd" = "yes" || test "$have_libelogind" = "yes" ], [Using libsystemd])
-
- dnl ---------------------------------------------------------------------------
- dnl - systemd unit / service files
---
-2.17.0
diff --git a/sdk_container/src/third_party/coreos-overlay/sys-auth/polkit/files/polkit-0.120-CVE-2021-4034.patch b/sdk_container/src/third_party/coreos-overlay/sys-auth/polkit/files/polkit-0.120-CVE-2021-4034.patch
new file mode 100644
index 0000000000..22bb71d142
--- /dev/null
+++ b/sdk_container/src/third_party/coreos-overlay/sys-auth/polkit/files/polkit-0.120-CVE-2021-4034.patch
@@ -0,0 +1,72 @@
+https://www.qualys.com/2022/01/25/cve-2021-4034/pwnkit.txt
+https://bugs.gentoo.org/832057
+https://gitlab.freedesktop.org/polkit/polkit/-/commit/a2bf5c9c83b6ae46cbd5c779d3055bff81ded683.patch
+
+From a2bf5c9c83b6ae46cbd5c779d3055bff81ded683 Mon Sep 17 00:00:00 2001
+From: Jan Rybar
+Date: Tue, 25 Jan 2022 17:21:46 +0000
+Subject: [PATCH] pkexec: local privilege escalation (CVE-2021-4034)
+
+--- a/src/programs/pkcheck.c
++++ b/src/programs/pkcheck.c
+@@ -363,6 +363,11 @@ main (int argc, char *argv[])
+ local_agent_handle = NULL;
+ ret = 126;
+
++ if (argc < 1)
++ {
++ exit(126);
++ }
++
+ /* Disable remote file access from GIO. */
+ setenv ("GIO_USE_VFS", "local", 1);
+
+--- a/src/programs/pkexec.c
++++ b/src/programs/pkexec.c
+@@ -488,6 +488,15 @@ main (int argc, char *argv[])
+ pid_t pid_of_caller;
+ gpointer local_agent_handle;
+
++
++ /*
++ * If 'pkexec' is called THIS wrong, someone's probably evil-doing. Don't be nice, just bail out.
++ */
++ if (argc<1)
++ {
++ exit(127);
++ }
++
+ ret = 127;
+ authority = NULL;
+ subject = NULL;
+@@ -614,10 +623,10 @@ main (int argc, char *argv[])
+
+ path = g_strdup (pwstruct.pw_shell);
+ if (!path)
+- {
++ {
+ g_printerr ("No shell configured or error retrieving pw_shell\n");
+ goto out;
+- }
++ }
+ /* If you change this, be sure to change the if (!command_line)
+ case below too */
+ command_line = g_strdup (path);
+@@ -636,7 +645,15 @@ main (int argc, char *argv[])
+ goto out;
+ }
+ g_free (path);
+- argv[n] = path = s;
++ path = s;
++
++ /* argc<2 and pkexec runs just shell, argv is guaranteed to be null-terminated.
++ * /-less shell shouldn't happen, but let's be defensive and don't write to null-termination
++ */
++ if (argv[n] != NULL)
++ {
++ argv[n] = path;
++ }
+ }
+ if (access (path, F_OK) != 0)
+ {
+GitLab
diff --git a/sdk_container/src/third_party/coreos-overlay/sys-auth/polkit/files/polkit-0.120-meson.patch b/sdk_container/src/third_party/coreos-overlay/sys-auth/polkit/files/polkit-0.120-meson.patch
new file mode 100644
index 0000000000..5e144688d3
--- /dev/null
+++ b/sdk_container/src/third_party/coreos-overlay/sys-auth/polkit/files/polkit-0.120-meson.patch
@@ -0,0 +1,42 @@
+From e7f3d9e8341df64e2abc3910dafb1113a84bff07 Mon Sep 17 00:00:00 2001
+From: Simon McVittie
+Date: Mon, 25 Oct 2021 20:21:27 +0100
+Subject: [PATCH] Don't pass positional parameters to i18n.merge_file
+
+These were always ignored, and Meson 0.60.0 disallowed them.
+
+Resolves: https://gitlab.freedesktop.org/polkit/polkit/-/issues/160
+Reference: https://github.com/mesonbuild/meson/pull/9445
+Signed-off-by: Simon McVittie
+---
+ actions/meson.build | 1 -
+ src/examples/meson.build | 1 -
+ 2 files changed, 2 deletions(-)
+
+diff --git a/actions/meson.build b/actions/meson.build
+index 2abaaf3..1e3f370 100644
+--- a/actions/meson.build
++++ b/actions/meson.build
+@@ -1,7 +1,6 @@
+ policy = 'org.freedesktop.policykit.policy'
+
+ i18n.merge_file(
+- policy,
+ input: policy + '.in',
+ output: '@BASENAME@',
+ po_dir: po_dir,
+diff --git a/src/examples/meson.build b/src/examples/meson.build
+index c6305ab..8c18de5 100644
+--- a/src/examples/meson.build
++++ b/src/examples/meson.build
+@@ -1,7 +1,6 @@
+ policy = 'org.freedesktop.policykit.examples.pkexec.policy'
+
+ i18n.merge_file(
+- policy,
+ input: policy + '.in',
+ output: '@BASENAME@',
+ po_dir: po_dir,
+--
+GitLab
+
diff --git a/sdk_container/src/third_party/coreos-overlay/sys-auth/polkit/metadata.xml b/sdk_container/src/third_party/coreos-overlay/sys-auth/polkit/metadata.xml
index b6d2fdc3ca..3a32730179 100644
--- a/sdk_container/src/third_party/coreos-overlay/sys-auth/polkit/metadata.xml
+++ b/sdk_container/src/third_party/coreos-overlay/sys-auth/polkit/metadata.xml
@@ -1,5 +1,5 @@
-
+
freedesktop-bugs@gentoo.org
diff --git a/sdk_container/src/third_party/coreos-overlay/sys-auth/polkit/polkit-0.119-r2.ebuild b/sdk_container/src/third_party/coreos-overlay/sys-auth/polkit/polkit-0.120-r2.ebuild
similarity index 57%
rename from sdk_container/src/third_party/coreos-overlay/sys-auth/polkit/polkit-0.119-r2.ebuild
rename to sdk_container/src/third_party/coreos-overlay/sys-auth/polkit/polkit-0.120-r2.ebuild
index 287131c247..5677f5b041 100644
--- a/sdk_container/src/third_party/coreos-overlay/sys-auth/polkit/polkit-0.119-r2.ebuild
+++ b/sdk_container/src/third_party/coreos-overlay/sys-auth/polkit/polkit-0.120-r2.ebuild
@@ -1,10 +1,10 @@
-# Copyright 1999-2021 Gentoo Authors
+# Copyright 1999-2022 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
EAPI=7
TMPFILES_OPTIONAL=1
-inherit autotools pam pax-utils systemd xdg-utils tmpfiles
+inherit meson pam pax-utils systemd xdg-utils tmpfiles
DESCRIPTION="Policy framework for controlling privileges for system-wide services"
HOMEPAGE="https://www.freedesktop.org/wiki/Software/polkit https://gitlab.freedesktop.org/polkit/polkit"
@@ -13,10 +13,11 @@ SRC_URI="https://www.freedesktop.org/software/${PN}/releases/${P}.tar.gz"
LICENSE="LGPL-2"
SLOT="0"
KEYWORDS="amd64 arm arm64 ~mips ppc64 ~riscv ~s390 x86"
-IUSE="elogind examples gtk +introspection kde nls pam selinux systemd test"
-RESTRICT="!test? ( test )"
-
-REQUIRED_USE="^^ ( elogind systemd )"
+IUSE="examples gtk +introspection kde pam selinux systemd test"
+#RESTRICT="!test? ( test )"
+# Tests currently don't work with meson. See
+# https://gitlab.freedesktop.org/polkit/polkit/-/issues/144
+RESTRICT="test"
BDEPEND="
acct-user/polkitd
@@ -26,8 +27,6 @@ BDEPEND="
dev-libs/gobject-introspection-common
dev-libs/libxslt
dev-util/glib-utils
- dev-util/gtk-doc-am
- dev-util/intltool
sys-devel/gettext
virtual/pkgconfig
introspection? ( dev-libs/gobject-introspection )
@@ -36,13 +35,13 @@ DEPEND="
dev-lang/duktape
dev-libs/glib:2
dev-libs/expat
- elogind? ( sys-auth/elogind )
pam? (
sys-auth/pambase
sys-libs/pam
)
!pam? ( virtual/libcrypt:= )
systemd? ( sys-apps/systemd:0=[policykit] )
+ !systemd? ( sys-auth/elogind )
"
RDEPEND="${DEPEND}
acct-user/polkitd
@@ -58,67 +57,52 @@ PDEPEND="
DOCS=( docs/TODO HACKING NEWS README )
-PATCHES=(
- "${FILESDIR}"/${PN}-0.115-elogind.patch # bug 660880
-
- # from https://gitlab.freedesktop.org/polkit/polkit/-/merge_requests/35
- "${FILESDIR}"/35_WIP_Add_duktape_as_javascript_engine.patch
-)
-
QA_MULTILIB_PATHS="
usr/lib/polkit-1/polkit-agent-helper-1
usr/lib/polkit-1/polkitd"
src_prepare() {
+ local PATCHES=(
+ "${FILESDIR}/polkit-0.120-meson.patch"
+ "${FILESDIR}/polkit-0.120-CVE-2021-4034.patch"
+
+ # from https://gitlab.freedesktop.org/polkit/polkit/-/merge_requests/97
+ "${FILESDIR}/97_Add_duktape_as_javascript_engine.patch"
+ )
default
sed -i -e 's|unix-group:wheel|unix-user:0|' src/polkitbackend/*-default.rules || die #401513
-
- # Workaround upstream hack around standard gtk-doc behavior, bug #552170
- sed -i -e 's/@ENABLE_GTK_DOC_TRUE@\(TARGET_DIR\)/\1/' \
- -e '/install-data-local:/,/uninstall-local:/ s/@ENABLE_GTK_DOC_TRUE@//' \
- -e 's/@ENABLE_GTK_DOC_FALSE@install-data-local://' \
- docs/polkit/Makefile.in || die
-
- # disable broken test - bug #624022
- sed -i -e "/^SUBDIRS/s/polkitbackend//" test/Makefile.am || die
-
- # Fix cross-building, bug #590764, elogind patch, bug #598615
- eautoreconf
}
src_configure() {
xdg_environment_reset
- local myeconfargs=(
+ local emesonargs=(
--localstatedir="${EPREFIX}"/var
- --disable-static
- --enable-man-pages
- --disable-gtk-doc
- --disable-examples
- --with-duktape
- $(use_enable elogind libelogind)
- $(use_enable introspection)
- $(use_enable nls)
- $(usex pam "--with-pam-module-dir=$(getpam_mod_dir)" '')
- --with-authfw=$(usex pam pam shadow)
- $(use_enable systemd libsystemd-login)
- --with-systemdsystemunitdir="$(systemd_get_systemunitdir)"
- $(use_enable test)
- --with-os-type=gentoo
+ -Dauthfw="$(usex pam pam shadow)"
+ -Dexamples=false
+ -Dgtk_doc=false
+ -Dman=true
+ -Dos_type=gentoo
+ -Dsession_tracking="$(usex systemd libsystemd-login libelogind)"
+ -Dsystemdsystemunitdir="$(systemd_get_systemunitdir)"
+ -Dwith-duktape=yes
+ $(meson_use introspection)
+ $(meson_use test tests)
+ $(usex pam "-Dpam_module_dir=$(getpam_mod_dir)" '')
)
- econf "${myeconfargs[@]}"
+ meson_src_configure
}
src_compile() {
- default
+ meson_src_compile
# Required for polkitd on hardened/PaX due to spidermonkey's JIT
pax-mark mr src/polkitbackend/.libs/polkitd test/polkitbackend/.libs/polkitbackendjsauthoritytest
}
src_install() {
- default
+ meson_src_install
dodir /usr/share/polkit-1/rules.d
dodir /usr/lib/pam.d
@@ -130,7 +114,7 @@ src_install() {
dotmpfiles "${FILESDIR}/polkit.conf"
- if use examples; then
+ if use examples ; then
docinto examples
dodoc src/examples/{*.c,*.policy*}
fi
@@ -138,5 +122,10 @@ src_install() {
diropts -m 0700 -o polkitd
keepdir /usr/share/polkit-1/rules.d
- find "${ED}" -name '*.la' -delete || die
+ # meson does not install required files with SUID bit. See
+ # https://bugs.gentoo.org/816393
+ # Remove the following lines once this has been fixed by upstream
+ # (should be fixed in next release: https://gitlab.freedesktop.org/polkit/polkit/-/commit/4ff1abe4a4c1f8c8378b9eaddb0346ac6448abd8)
+ fperms u+s /usr/bin/pkexec
+ fperms u+s /usr/lib/polkit-1/polkit-agent-helper-1
}