mbox series

[meta-oe,v2,0/2] polkit: switch from mozjs to duktape javascript engine

Message ID 20220324102115.31112-1-mikko.rapeli@bmw.de
Headers show
Series polkit: switch from mozjs to duktape javascript engine | expand

Message

Mikko Rapeli March 24, 2022, 10:21 a.m. UTC
From: Mikko Rapeli <mikko.rapeli@bmw.de>

polkit 0.121 will contain support for duktape but the patch applies
to 0.119 already so use it to get rid of mozjs and free 20 Mb of space.
Pick some CVE patches from master while at it.

v2: added Signed-off-By lines to all patches

Mikko Rapeli (2):
  polkit: add patches for CVE-2021-4034 and CVE-2021-4115
  polkit: switch from mozjs to duktape javascript engine

 ...l-privilege-escalation-CVE-2021-4034.patch |   84 +
 ...0002-CVE-2021-4115-GHSL-2021-077-fix.patch |   88 +
 .../0002-jsauthority-port-to-mozjs-91.patch   |   38 -
 ...ded-support-for-duktape-as-JS-engine.patch | 3463 +++++++++++++++++
 ...re-to-call-JS_Init-and-JS_ShutDown-e.patch |   63 -
 .../recipes-extended/polkit/polkit_0.119.bb   |    8 +-
 6 files changed, 3640 insertions(+), 104 deletions(-)
 create mode 100644 meta-oe/recipes-extended/polkit/polkit/0001-pkexec-local-privilege-escalation-CVE-2021-4034.patch
 create mode 100644 meta-oe/recipes-extended/polkit/polkit/0002-CVE-2021-4115-GHSL-2021-077-fix.patch
 delete mode 100644 meta-oe/recipes-extended/polkit/polkit/0002-jsauthority-port-to-mozjs-91.patch
 create mode 100644 meta-oe/recipes-extended/polkit/polkit/0003-Added-support-for-duktape-as-JS-engine.patch
 delete mode 100644 meta-oe/recipes-extended/polkit/polkit/0003-jsauthority-ensure-to-call-JS_Init-and-JS_ShutDown-e.patch

Comments

Jose Quaresma March 24, 2022, 10:45 a.m. UTC | #1
Hi Mikko,

Mikko Rapeli <mikko.rapeli@bmw.de> escreveu no dia quinta, 24/03/2022 à(s)
10:21:

> From: Mikko Rapeli <mikko.rapeli@bmw.de>
>
> cherry-pick the change from polkit 0.120+ upstream since
> it applies directly to 0.119. Drop mozjs patches.
>
> Removes mozjs and its dependency nspr from images. They account for
> roughly 21 Mb on 64bit ARM machines. The replacement libduktape is
> roughly 300 kb in size. Thus this saves at least 20 Mb in rootfs size
> when polkit is used.
>
> Signed-off-by: Mikko Rapeli <mikko.rapeli@bmw.de>
> ---
>  .../0002-jsauthority-port-to-mozjs-91.patch   |   38 -
>  ...ded-support-for-duktape-as-JS-engine.patch | 3463 +++++++++++++++++
>  ...re-to-call-JS_Init-and-JS_ShutDown-e.patch |   63 -
>  .../recipes-extended/polkit/polkit_0.119.bb   |    6 +-
>  4 files changed, 3466 insertions(+), 104 deletions(-)
>  delete mode 100644
> meta-oe/recipes-extended/polkit/polkit/0002-jsauthority-port-to-mozjs-91.patch
>  create mode 100644
> meta-oe/recipes-extended/polkit/polkit/0003-Added-support-for-duktape-as-JS-engine.patch
>  delete mode 100644
> meta-oe/recipes-extended/polkit/polkit/0003-jsauthority-ensure-to-call-JS_Init-and-JS_ShutDown-e.patch
>
> diff --git
> a/meta-oe/recipes-extended/polkit/polkit/0002-jsauthority-port-to-mozjs-91.patch
> b/meta-oe/recipes-extended/polkit/polkit/0002-jsauthority-port-to-mozjs-91.patch
> deleted file mode 100644
> index 5b3660da2..000000000
> ---
> a/meta-oe/recipes-extended/polkit/polkit/0002-jsauthority-port-to-mozjs-91.patch
> +++ /dev/null
> @@ -1,38 +0,0 @@
> -From 4ce27b66bb07b72cb96d3d43a75108a5a6e7e156 Mon Sep 17 00:00:00 2001
> -From: Xi Ruoyao <xry111@mengyan1223.wang>
> -Date: Tue, 10 Aug 2021 19:09:42 +0800
> -Subject: [PATCH] jsauthority: port to mozjs-91
> -
> -Upstream-Status: Submitted [
> https://gitlab.freedesktop.org/polkit/polkit/-/merge_requests/92]
> -Signed-off-by: Alexander Kanavin <alex@linutronix.de>
> ----
> - configure.ac | 2 +-
> - meson.build  | 2 +-
> - 2 files changed, 2 insertions(+), 2 deletions(-)
> -
> -diff --git a/configure.ac b/configure.ac
> -index d807086..5a7fc11 100644
> ---- a/configure.ac
> -+++ b/configure.ac
> -@@ -80,7 +80,7 @@ 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])
> -+PKG_CHECK_MODULES(LIBJS, [mozjs-91])
> -
> - AC_SUBST(LIBJS_CFLAGS)
> - AC_SUBST(LIBJS_CXXFLAGS)
> -diff --git a/meson.build b/meson.build
> -index b3702be..733bbff 100644
> ---- a/meson.build
> -+++ b/meson.build
> -@@ -126,7 +126,7 @@ 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')
> -+mozjs_dep = dependency('mozjs-91')
> -
> - dbus_dep = dependency('dbus-1')
> - dbus_confdir = dbus_dep.get_pkgconfig_variable('datadir',
> define_variable: ['datadir', pk_prefix / pk_datadir])   #changed from
> sysconfdir with respect to commit#8eada3836465838
> diff --git
> a/meta-oe/recipes-extended/polkit/polkit/0003-Added-support-for-duktape-as-JS-engine.patch
> b/meta-oe/recipes-extended/polkit/polkit/0003-Added-support-for-duktape-as-JS-engine.patch
> new file mode 100644
> index 000000000..e44e4f6e4
> --- /dev/null
> +++
> b/meta-oe/recipes-extended/polkit/polkit/0003-Added-support-for-duktape-as-JS-engine.patch
> @@ -0,0 +1,3463 @@
> +From eaecfb21e1bca42e99321cc731e21dbfc1ea0d0c Mon Sep 17 00:00:00 2001
> +From: Gustavo Lima Chaves <limachaves@gmail.com>
> +Date: Tue, 25 Jan 2022 09:43:21 +0000
> +Subject: [PATCH 3/3] Added support for duktape as JS engine
> +
> +Original author: Wu Xiaotian (@yetist)
> +Resurrection author, runaway-killer author: Gustavo Lima Chaves
> (@limachaves)
>

Upstream-Status: Backport [c7fc4e1b61f0fd82fc697c19c604af7e9fb291a2]

The right place for the Upstream-Status is on the commit message and not on
the commit comments ---
as in the commit comments it will be lost when merged.

Jose

+
> +Signed-off-by: Mikko Rapeli <mikko.rapeli@bmw.de>
> +
> +---
> + .gitlab-ci.yml                                |    1 +
> + buildutil/ax_pthread.m4                       |  522 ++++++++
> + configure.ac                                  |   34 +-
> + docs/man/polkit.xml                           |    4 +-
> + meson.build                                   |   16 +-
> + meson_options.txt                             |    1 +
> + src/polkitbackend/Makefile.am                 |   17 +-
> + src/polkitbackend/meson.build                 |   14 +-
> + src/polkitbackend/polkitbackendcommon.c       |  530 +++++++++
> + src/polkitbackend/polkitbackendcommon.h       |  158 +++
> + .../polkitbackendduktapeauthority.c           | 1051 +++++++++++++++++
> + .../polkitbackendjsauthority.cpp              |  721 +----------
> + .../etc/polkit-1/rules.d/10-testing.rules     |    6 +-
> + .../test-polkitbackendjsauthority.c           |    2 +-
> + 14 files changed, 2399 insertions(+), 678 deletions(-)
> + create mode 100644 buildutil/ax_pthread.m4
> + create mode 100644 src/polkitbackend/polkitbackendcommon.c
> + create mode 100644 src/polkitbackend/polkitbackendcommon.h
> + create mode 100644 src/polkitbackend/polkitbackendduktapeauthority.c
> +
> +Upstream-Status: Backport [c7fc4e1b61f0fd82fc697c19c604af7e9fb291a2]
> +Dropped change to .gitlab-ci.yml and adapted configure.ac due to other
> +patches in meta-oe.
> +
> +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 <stevenj@alum.mit.edu>
> ++#   Copyright (c) 2011 Daniel Richard G. <skunk@iSKUNK.ORG>
> ++#   Copyright (c) 2019 Marc Stevens <marc.stevens@cwi.nl>
> ++#
> ++#   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 <https://www.gnu.org/licenses/>.
> ++#
> ++#   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 <pthread.h>
> ++#                       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 <pthread.h>],
> ++                                                 [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 <pthread.h>]],
> ++                                             [[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 b625743..bbf4768 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.2.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=<dir>      Use expat from here],
> +@@ -100,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 setnetgrent)
> +
> + if test "x$GCC" = "xyes"; then
> +@@ -581,6 +598,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/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).
> +       </para>
> +
> +       <para>
> +diff --git a/meson.build b/meson.build
> +index b3702be..7506231 100644
> +--- a/meson.build
> ++++ b/meson.build
> +@@ -126,7 +126,18 @@ 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')
> ++duktape_req_version = '>= 2.2.0'
> ++
> ++js_engine = get_option('js_engine')
> ++if js_engine == '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 <pthread.h>'))
> ++elif js_engine == 'mozjs'
> ++  js_dep = dependency('mozjs-78')
> ++endif
> +
> + dbus_dep = dependency('dbus-1')
> + dbus_confdir = dbus_dep.get_pkgconfig_variable('datadir',
> define_variable: ['datadir', pk_prefix / pk_datadir])   #changed from
> sysconfdir with respect to commit#8eada3836465838
> +@@ -350,6 +361,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/Makefile.am b/src/polkitbackend/Makefile.am
> +index 7e3c080..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
> +@@ -31,9 +33,10 @@ 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
> polkitbackendjsauthority.cpp            \
> ++      polkitbackendjsauthority.h                              \
> +       polkitbackendactionpool.h               polkitbackendactionpool.c
>              \
> +       polkitbackendactionlookup.h
>  polkitbackendactionlookup.c             \
> +         $(NULL)
> +@@ -51,19 +54,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 93c3c34..99f8e33 100644
> +--- a/src/polkitbackend/meson.build
> ++++ b/src/polkitbackend/meson.build
> +@@ -4,8 +4,8 @@ sources = files(
> +   'polkitbackendactionlookup.c',
> +   'polkitbackendactionpool.c',
> +   'polkitbackendauthority.c',
> ++  'polkitbackendcommon.c',
> +   'polkitbackendinteractiveauthority.c',
> +-  'polkitbackendjsauthority.cpp',
> + )
> +
> + output = 'initjs.h'
> +@@ -21,7 +21,7 @@ sources += custom_target(
> + deps = [
> +   expat_dep,
> +   libpolkit_gobject_dep,
> +-  mozjs_dep,
> ++  js_dep,
> + ]
> +
> + c_flags = [
> +@@ -29,8 +29,18 @@ 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
> ++
> + if enable_logind
> +   sources += files('polkitbackendsessionmonitor-systemd.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 <davidz@redhat.com>
> ++ */
> ++
> ++#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..dd700fc
> +--- /dev/null
> ++++ b/src/polkitbackend/polkitbackendcommon.h
> +@@ -0,0 +1,158 @@
> ++/*
> ++ * 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 <davidz@redhat.com>
> ++ */
> ++
> ++#if !defined (_POLKIT_BACKEND_COMPILATION) &&
> !defined(_POLKIT_BACKEND_INSIDE_POLKIT_BACKEND_H)
> ++#error "Only <polkitbackend/polkitbackend.h> 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 <sys/wait.h>
> ++#include <errno.h>
> ++#include <pwd.h>
> ++#include <grp.h>
> ++#ifdef HAVE_NETGROUP_H
> ++#include <netgroup.h>
> ++#else
> ++#include <netdb.h>
> ++#endif
> ++#include <string.h>
> ++#include <glib/gstdio.h>
> ++#include <locale.h>
> ++#include <glib/gi18n-lib.h> //here, all things glib via glib.h
> (including -> gspawn.h)
> ++
> ++#include <polkit/polkit.h>
> ++#include "polkitbackendjsauthority.h"
> ++
> ++#include <polkit/polkitprivate.h>
> ++
> ++#ifdef HAVE_LIBSYSTEMD
> ++#include <systemd/sd-login.h>
> ++#endif /* HAVE_LIBSYSTEMD */
> ++
> ++#define RUNAWAY_KILLER_TIMEOUT (15)
> ++
> ++#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
> +new file mode 100644
> +index 0000000..c89dbcf
> +--- /dev/null
> ++++ b/src/polkitbackend/polkitbackendduktapeauthority.c
> +@@ -0,0 +1,1051 @@
> ++/*
> ++ * Copyright (C) 2008-2012 Red Hat, Inc.
> ++ * Copyright (C) 2015 Tangent Space <jstpierre@mecheye.net>
> ++ * Copyright (C) 2019 Wu Xiaotian <yetist@gmail.com>
> ++ *
> ++ * 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 <davidz@redhat.com>
> ++ */
> ++
> ++#include <pthread.h>
> ++
> ++#include "polkitbackendcommon.h"
> ++
> ++#include "duktape.h"
> ++
> ++/* Built source and not too big to worry about deduplication */
> ++#include "initjs.h" /* init.js */
> ++
> ++/**
> ++ * SECTION:polkitbackendjsauthority
> ++ * @title: PolkitBackendJsAuthority
> ++ * @short_description: JS Authority
> ++ * @stability: Unstable
> ++ *
> ++ * An (Duktape-based) implementation of #PolkitBackendAuthority that
> reads and
> ++ * evaluates 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;
> ++
> ++  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);
> ++
> ++/*
> ----------------------------------------------------------------------------------------------------
> */
> ++
> ++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 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)
> ++{
> ++  authority->priv = G_TYPE_INSTANCE_GET_PRIVATE (authority,
> ++
>  POLKIT_BACKEND_TYPE_JS_AUTHORITY,
> ++
>  PolkitBackendJsAuthorityPrivate);
> ++}
> ++
> ++static void
> ++load_scripts (PolkitBackendJsAuthority  *authority)
> ++{
> ++  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)
> polkit_backend_common_rules_file_name_cmp);
> ++
> ++  for (l = files; l != NULL; l = l->next)
> ++    {
> ++      const gchar *filename = (gchar *)l->data;
> ++
> ++      if (!execute_script_with_runaway_killer(authority, filename))
> ++          continue;
> ++      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);
> ++}
> ++
> ++void
> ++polkit_backend_common_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
> ++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
> (polkit_backend_common_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);
> ++}
> ++
> ++void
> ++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, report_error);
> ++  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");
> ++
> ++  /* 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)
> ++    {
> ++      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 ();
> ++}
> ++
> ++void
> ++polkit_backend_common_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
> (polkit_backend_common_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);
> ++}
> ++
> ++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);
> ++
> ++  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 void
> ++polkit_backend_js_authority_class_init (PolkitBackendJsAuthorityClass
> *klass)
> ++{
> ++  polkit_backend_common_js_authority_class_init_common (klass);
> ++  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;
> ++}
> ++
> ++/*
> ----------------------------------------------------------------------------------------------------
> */
> ++
> ++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 *
> ++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;
> ++  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 (!call_js_function_with_runaway_killer (authority))
> ++    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;
> ++}
> ++
> ++/*
> ----------------------------------------------------------------------------------------------------
> */
> ++
> ++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;
> ++  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 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;
> ++  }
> ++  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 duk_ret_t
> ++js_polkit_spawn (duk_context *cx)
> ++{
> ++  duk_ret_t ret = DUK_RET_ERROR;
> ++  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;
> ++  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 (!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);
> ++      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)",
> ++                                  polkit_backend_common_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;
> ++}
> ++
> ++/*
> ----------------------------------------------------------------------------------------------------
> */
> +diff --git a/src/polkitbackend/polkitbackendjsauthority.cpp
> b/src/polkitbackend/polkitbackendjsauthority.cpp
> +index ca17108..11e91c0 100644
> +--- a/src/polkitbackend/polkitbackendjsauthority.cpp
> ++++ b/src/polkitbackend/polkitbackendjsauthority.cpp
> +@@ -19,29 +19,7 @@
> +  * Author: David Zeuthen <davidz@redhat.com>
> +  */
> +
> +-#include "config.h"
> +-#include <sys/wait.h>
> +-#include <errno.h>
> +-#include <pwd.h>
> +-#include <grp.h>
> +-#ifdef HAVE_NETGROUP_H
> +-#include <netgroup.h>
> +-#else
> +-#include <netdb.h>
> +-#endif
> +-#include <string.h>
> +-#include <glib/gstdio.h>
> +-#include <locale.h>
> +-#include <glib/gi18n-lib.h>
> +-
> +-#include <polkit/polkit.h>
> +-#include "polkitbackendjsauthority.h"
> +-
> +-#include <polkit/polkitprivate.h>
> +-
> +-#ifdef HAVE_LIBSYSTEMD
> +-#include <systemd/sd-login.h>
> +-#endif /* HAVE_LIBSYSTEMD */
> ++#include "polkitbackendcommon.h"
> +
> + #include <js/CompilationAndEvaluation.h>
> + #include <js/ContextOptions.h>
> +@@ -52,6 +30,7 @@
> + #include <js/Array.h>
> + #include <jsapi.h>
> +
> ++/* 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 ();
> + }
> +
> +@@ -1005,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);
> +
> +@@ -1069,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,
> +@@ -1099,15 +929,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 +1032,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 +1154,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 +1211,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 +1248,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 +1313,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;
> +-}
> +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,
> +   },
> +
> +   {
> +--
> +2.20.1
> +
> diff --git
> a/meta-oe/recipes-extended/polkit/polkit/0003-jsauthority-ensure-to-call-JS_Init-and-JS_ShutDown-e.patch
> b/meta-oe/recipes-extended/polkit/polkit/0003-jsauthority-ensure-to-call-JS_Init-and-JS_ShutDown-e.patch
> deleted file mode 100644
> index 9e9755e44..000000000
> ---
> a/meta-oe/recipes-extended/polkit/polkit/0003-jsauthority-ensure-to-call-JS_Init-and-JS_ShutDown-e.patch
> +++ /dev/null
> @@ -1,63 +0,0 @@
> -From 7799441b9aa55324160deefbc65f9d918b8c94c1 Mon Sep 17 00:00:00 2001
> -From: Xi Ruoyao <xry111@mengyan1223.wang>
> -Date: Tue, 10 Aug 2021 18:52:56 +0800
> -Subject: [PATCH] jsauthority: ensure to call JS_Init() and JS_ShutDown()
> - exactly once
> -
> -Before this commit, we were calling JS_Init() in
> -polkit_backend_js_authority_class_init and never called JS_ShutDown.
> -This is actually a misusage of SpiderMonkey API.  Quote from a comment
> -in js/Initialization.h (both mozjs-78 and mozjs-91):
> -
> -    It is currently not possible to initialize SpiderMonkey multiple
> -    times (that is, calling JS_Init/JSAPI methods/JS_ShutDown in that
> -    order, then doing so again).
> -
> -This misusage does not cause severe issues with mozjs-78.  However, when
> -we eventually port jsauthority to use mozjs-91, bad thing will happen:
> -see the test failure mentioned in #150.
> -
> -This commit is tested with both mozjs-78 and mozjs-91, all tests pass
> -with it.
> -
> -Upstream-Status: Submitted [
> https://gitlab.freedesktop.org/polkit/polkit/-/merge_requests/91]
> -Signed-off-by: Alexander Kanavin <alex@linutronix.de>
> ----
> - src/polkitbackend/polkitbackendjsauthority.cpp | 10 +++++++---
> - 1 file changed, 7 insertions(+), 3 deletions(-)
> -
> -diff --git a/src/polkitbackend/polkitbackendjsauthority.cpp
> b/src/polkitbackend/polkitbackendjsauthority.cpp
> -index 41d8d5c..38dc001 100644
> ---- a/src/polkitbackend/polkitbackendjsauthority.cpp
> -+++ b/src/polkitbackend/polkitbackendjsauthority.cpp
> -@@ -75,6 +75,13 @@
> -
> - /*
> ----------------------------------------------------------------------------------------------------
> */
> -
> -+static class JsInitHelperType
> -+{
> -+public:
> -+      JsInitHelperType() { JS_Init(); }
> -+      ~JsInitHelperType() { JS_ShutDown(); }
> -+} JsInitHelper;
> -+
> - struct _PolkitBackendJsAuthorityPrivate
> - {
> -   gchar **rules_dirs;
> -@@ -589,7 +596,6 @@ polkit_backend_js_authority_finalize (GObject *object)
> -   delete authority->priv->js_polkit;
> -
> -   JS_DestroyContext (authority->priv->cx);
> --  /* JS_ShutDown (); */
> -
> -   G_OBJECT_CLASS (polkit_backend_js_authority_parent_class)->finalize
> (object);
> - }
> -@@ -665,8 +671,6 @@ polkit_backend_js_authority_class_init
> (PolkitBackendJsAuthorityClass *klass)
> -
> -
> -   g_type_class_add_private (klass, sizeof
> (PolkitBackendJsAuthorityPrivate));
> --
> --  JS_Init ();
> - }
> -
> - /*
> ----------------------------------------------------------------------------------------------------
> */
> diff --git a/meta-oe/recipes-extended/polkit/polkit_0.119.bb
> b/meta-oe/recipes-extended/polkit/polkit_0.119.bb
> index a9559c4db..186801a09 100644
> --- a/meta-oe/recipes-extended/polkit/polkit_0.119.bb
> +++ b/meta-oe/recipes-extended/polkit/polkit_0.119.bb
> @@ -5,7 +5,7 @@ LICENSE = "LGPL-2.0-or-later"
>  LIC_FILES_CHKSUM = "file://COPYING;md5=155db86cdbafa7532b41f390409283eb \
>
>  file://src/polkit/polkit.h;beginline=1;endline=20;md5=0a8630b0133176d0504c87a0ded39db4"
>
> -DEPENDS = "expat glib-2.0 intltool-native mozjs-91"
> +DEPENDS = "expat glib-2.0 intltool-native duktape"
>
>  inherit autotools gtk-doc pkgconfig useradd systemd gobject-introspection
> features_check
>
> @@ -25,16 +25,16 @@ PAM_SRC_URI = "file://polkit-1_pam.patch"
>  SRC_URI = "
> http://www.freedesktop.org/software/polkit/releases/polkit-${PV}.tar.gz \
>             ${@bb.utils.contains('DISTRO_FEATURES', 'pam',
> '${PAM_SRC_URI}', '', d)} \
>             file://0003-make-netgroup-support-optional.patch \
> -           file://0002-jsauthority-port-to-mozjs-91.patch \
> -
>  file://0003-jsauthority-ensure-to-call-JS_Init-and-JS_ShutDown-e.patch \
>
> file://0001-pkexec-local-privilege-escalation-CVE-2021-4034.patch \
>             file://0002-CVE-2021-4115-GHSL-2021-077-fix.patch \
> +           file://0003-Added-support-for-duktape-as-JS-engine.patch \
>             "
>  SRC_URI[sha256sum] =
> "c8579fdb86e94295404211285fee0722ad04893f0213e571bd75c00972fd1f5c"
>
>  EXTRA_OECONF = "--with-os-type=moblin \
>                  --disable-man-pages \
>                  --disable-libelogind \
> +                --with-duktape \
>                 "
>
>  do_compile:prepend () {
> --
> 2.20.1
>
>
> -=-=-=-=-=-=-=-=-=-=-=-
> Links: You receive all messages sent to this group.
> View/Reply Online (#96194):
> https://lists.openembedded.org/g/openembedded-devel/message/96194
> Mute This Topic: https://lists.openembedded.org/mt/89996103/5052612
> Group Owner: openembedded-devel+owner@lists.openembedded.org
> Unsubscribe: https://lists.openembedded.org/g/openembedded-devel/unsub [
> quaresma.jose@gmail.com]
> -=-=-=-=-=-=-=-=-=-=-=-
>
>