1From 4af72493cb380ab5ce0dd7c5bcd25a8b5457d770 Mon Sep 17 00:00:00 2001
2From: Gustavo Lima Chaves <limachaves@gmail.com>
3Date: Tue, 25 Jan 2022 09:43:21 +0000
4Subject: [PATCH] Added support for duktape as JS engine
5
6Original author: Wu Xiaotian (@yetist)
7Resurrection author, runaway-killer author: Gustavo Lima Chaves (@limachaves)
8
9Signed-off-by: Mikko Rapeli <mikko.rapeli@bmw.de>
10
11Upstream-Status: Backport [c7fc4e1b61f0fd82fc697c19c604af7e9fb291a2]
12Dropped change to .gitlab-ci.yml and adapted configure.ac due to other
13patches in meta-oe.
14
15---
16 buildutil/ax_pthread.m4                       |  522 ++++++++
17 configure.ac                                  |   34 +-
18 docs/man/polkit.xml                           |    4 +-
19 meson.build                                   |   16 +-
20 meson_options.txt                             |    1 +
21 src/polkitbackend/Makefile.am                 |   17 +-
22 src/polkitbackend/meson.build                 |   14 +-
23 src/polkitbackend/polkitbackendcommon.c       |  530 +++++++++
24 src/polkitbackend/polkitbackendcommon.h       |  158 +++
25 .../polkitbackendduktapeauthority.c           | 1051 +++++++++++++++++
26 .../polkitbackendjsauthority.cpp              |  721 +----------
27 .../etc/polkit-1/rules.d/10-testing.rules     |    6 +-
28 .../test-polkitbackendjsauthority.c           |    2 +-
29 13 files changed, 2398 insertions(+), 678 deletions(-)
30 create mode 100644 buildutil/ax_pthread.m4
31 create mode 100644 src/polkitbackend/polkitbackendcommon.c
32 create mode 100644 src/polkitbackend/polkitbackendcommon.h
33 create mode 100644 src/polkitbackend/polkitbackendduktapeauthority.c
34
35diff --git a/buildutil/ax_pthread.m4 b/buildutil/ax_pthread.m4
36new file mode 100644
37index 0000000..9f35d13
38--- /dev/null
39+++ b/buildutil/ax_pthread.m4
40@@ -0,0 +1,522 @@
41+# ===========================================================================
42+#        https://www.gnu.org/software/autoconf-archive/ax_pthread.html
43+# ===========================================================================
44+#
45+# SYNOPSIS
46+#
47+#   AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
48+#
49+# DESCRIPTION
50+#
51+#   This macro figures out how to build C programs using POSIX threads. It
52+#   sets the PTHREAD_LIBS output variable to the threads library and linker
53+#   flags, and the PTHREAD_CFLAGS output variable to any special C compiler
54+#   flags that are needed. (The user can also force certain compiler
55+#   flags/libs to be tested by setting these environment variables.)
56+#
57+#   Also sets PTHREAD_CC and PTHREAD_CXX to any special C compiler that is
58+#   needed for multi-threaded programs (defaults to the value of CC
59+#   respectively CXX otherwise). (This is necessary on e.g. AIX to use the
60+#   special cc_r/CC_r compiler alias.)
61+#
62+#   NOTE: You are assumed to not only compile your program with these flags,
63+#   but also to link with them as well. For example, you might link with
64+#   $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
65+#   $PTHREAD_CXX $CXXFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
66+#
67+#   If you are only building threaded programs, you may wish to use these
68+#   variables in your default LIBS, CFLAGS, and CC:
69+#
70+#     LIBS="$PTHREAD_LIBS $LIBS"
71+#     CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
72+#     CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS"
73+#     CC="$PTHREAD_CC"
74+#     CXX="$PTHREAD_CXX"
75+#
76+#   In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
77+#   has a nonstandard name, this macro defines PTHREAD_CREATE_JOINABLE to
78+#   that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
79+#
80+#   Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the
81+#   PTHREAD_PRIO_INHERIT symbol is defined when compiling with
82+#   PTHREAD_CFLAGS.
83+#
84+#   ACTION-IF-FOUND is a list of shell commands to run if a threads library
85+#   is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it
86+#   is not found. If ACTION-IF-FOUND is not specified, the default action
87+#   will define HAVE_PTHREAD.
88+#
89+#   Please let the authors know if this macro fails on any platform, or if
90+#   you have any other suggestions or comments. This macro was based on work
91+#   by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help
92+#   from M. Frigo), as well as ac_pthread and hb_pthread macros posted by
93+#   Alejandro Forero Cuervo to the autoconf macro repository. We are also
94+#   grateful for the helpful feedback of numerous users.
95+#
96+#   Updated for Autoconf 2.68 by Daniel Richard G.
97+#
98+# LICENSE
99+#
100+#   Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
101+#   Copyright (c) 2011 Daniel Richard G. <skunk@iSKUNK.ORG>
102+#   Copyright (c) 2019 Marc Stevens <marc.stevens@cwi.nl>
103+#
104+#   This program is free software: you can redistribute it and/or modify it
105+#   under the terms of the GNU General Public License as published by the
106+#   Free Software Foundation, either version 3 of the License, or (at your
107+#   option) any later version.
108+#
109+#   This program is distributed in the hope that it will be useful, but
110+#   WITHOUT ANY WARRANTY; without even the implied warranty of
111+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
112+#   Public License for more details.
113+#
114+#   You should have received a copy of the GNU General Public License along
115+#   with this program. If not, see <https://www.gnu.org/licenses/>.
116+#
117+#   As a special exception, the respective Autoconf Macro's copyright owner
118+#   gives unlimited permission to copy, distribute and modify the configure
119+#   scripts that are the output of Autoconf when processing the Macro. You
120+#   need not follow the terms of the GNU General Public License when using
121+#   or distributing such scripts, even though portions of the text of the
122+#   Macro appear in them. The GNU General Public License (GPL) does govern
123+#   all other use of the material that constitutes the Autoconf Macro.
124+#
125+#   This special exception to the GPL applies to versions of the Autoconf
126+#   Macro released by the Autoconf Archive. When you make and distribute a
127+#   modified version of the Autoconf Macro, you may extend this special
128+#   exception to the GPL to apply to your modified version as well.
129+
130+#serial 31
131+
132+AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
133+AC_DEFUN([AX_PTHREAD], [
134+AC_REQUIRE([AC_CANONICAL_HOST])
135+AC_REQUIRE([AC_PROG_CC])
136+AC_REQUIRE([AC_PROG_SED])
137+AC_LANG_PUSH([C])
138+ax_pthread_ok=no
139+
140+# We used to check for pthread.h first, but this fails if pthread.h
141+# requires special compiler flags (e.g. on Tru64 or Sequent).
142+# It gets checked for in the link test anyway.
143+
144+# First of all, check if the user has set any of the PTHREAD_LIBS,
145+# etcetera environment variables, and if threads linking works using
146+# them:
147+if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then
148+        ax_pthread_save_CC="$CC"
149+        ax_pthread_save_CFLAGS="$CFLAGS"
150+        ax_pthread_save_LIBS="$LIBS"
151+        AS_IF([test "x$PTHREAD_CC" != "x"], [CC="$PTHREAD_CC"])
152+        AS_IF([test "x$PTHREAD_CXX" != "x"], [CXX="$PTHREAD_CXX"])
153+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
154+        LIBS="$PTHREAD_LIBS $LIBS"
155+        AC_MSG_CHECKING([for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS])
156+        AC_LINK_IFELSE([AC_LANG_CALL([], [pthread_join])], [ax_pthread_ok=yes])
157+        AC_MSG_RESULT([$ax_pthread_ok])
158+        if test "x$ax_pthread_ok" = "xno"; then
159+                PTHREAD_LIBS=""
160+                PTHREAD_CFLAGS=""
161+        fi
162+        CC="$ax_pthread_save_CC"
163+        CFLAGS="$ax_pthread_save_CFLAGS"
164+        LIBS="$ax_pthread_save_LIBS"
165+fi
166+
167+# We must check for the threads library under a number of different
168+# names; the ordering is very important because some systems
169+# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
170+# libraries is broken (non-POSIX).
171+
172+# Create a list of thread flags to try. Items with a "," contain both
173+# C compiler flags (before ",") and linker flags (after ","). Other items
174+# starting with a "-" are C compiler flags, and remaining items are
175+# library names, except for "none" which indicates that we try without
176+# any flags at all, and "pthread-config" which is a program returning
177+# the flags for the Pth emulation library.
178+
179+ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
180+
181+# The ordering *is* (sometimes) important.  Some notes on the
182+# individual items follow:
183+
184+# pthreads: AIX (must check this before -lpthread)
185+# none: in case threads are in libc; should be tried before -Kthread and
186+#       other compiler flags to prevent continual compiler warnings
187+# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
188+# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads), Tru64
189+#           (Note: HP C rejects this with "bad form for `-t' option")
190+# -pthreads: Solaris/gcc (Note: HP C also rejects)
191+# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
192+#      doesn't hurt to check since this sometimes defines pthreads and
193+#      -D_REENTRANT too), HP C (must be checked before -lpthread, which
194+#      is present but should not be used directly; and before -mthreads,
195+#      because the compiler interprets this as "-mt" + "-hreads")
196+# -mthreads: Mingw32/gcc, Lynx/gcc
197+# pthread: Linux, etcetera
198+# --thread-safe: KAI C++
199+# pthread-config: use pthread-config program (for GNU Pth library)
200+
201+case $host_os in
202+
203+        freebsd*)
204+
205+        # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
206+        # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
207+
208+        ax_pthread_flags="-kthread lthread $ax_pthread_flags"
209+        ;;
210+
211+        hpux*)
212+
213+        # From the cc(1) man page: "[-mt] Sets various -D flags to enable
214+        # multi-threading and also sets -lpthread."
215+
216+        ax_pthread_flags="-mt -pthread pthread $ax_pthread_flags"
217+        ;;
218+
219+        openedition*)
220+
221+        # IBM z/OS requires a feature-test macro to be defined in order to
222+        # enable POSIX threads at all, so give the user a hint if this is
223+        # not set. (We don't define these ourselves, as they can affect
224+        # other portions of the system API in unpredictable ways.)
225+
226+        AC_EGREP_CPP([AX_PTHREAD_ZOS_MISSING],
227+            [
228+#            if !defined(_OPEN_THREADS) && !defined(_UNIX03_THREADS)
229+             AX_PTHREAD_ZOS_MISSING
230+#            endif
231+            ],
232+            [AC_MSG_WARN([IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support.])])
233+        ;;
234+
235+        solaris*)
236+
237+        # On Solaris (at least, for some versions), libc contains stubbed
238+        # (non-functional) versions of the pthreads routines, so link-based
239+        # tests will erroneously succeed. (N.B.: The stubs are missing
240+        # pthread_cleanup_push, or rather a function called by this macro,
241+        # so we could check for that, but who knows whether they'll stub
242+        # that too in a future libc.)  So we'll check first for the
243+        # standard Solaris way of linking pthreads (-mt -lpthread).
244+
245+        ax_pthread_flags="-mt,-lpthread pthread $ax_pthread_flags"
246+        ;;
247+esac
248+
249+# Are we compiling with Clang?
250+
251+AC_CACHE_CHECK([whether $CC is Clang],
252+    [ax_cv_PTHREAD_CLANG],
253+    [ax_cv_PTHREAD_CLANG=no
254+     # Note that Autoconf sets GCC=yes for Clang as well as GCC
255+     if test "x$GCC" = "xyes"; then
256+        AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG],
257+            [/* Note: Clang 2.7 lacks __clang_[a-z]+__ */
258+#            if defined(__clang__) && defined(__llvm__)
259+             AX_PTHREAD_CC_IS_CLANG
260+#            endif
261+            ],
262+            [ax_cv_PTHREAD_CLANG=yes])
263+     fi
264+    ])
265+ax_pthread_clang="$ax_cv_PTHREAD_CLANG"
266+
267+
268+# GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC)
269+
270+# Note that for GCC and Clang -pthread generally implies -lpthread,
271+# except when -nostdlib is passed.
272+# This is problematic using libtool to build C++ shared libraries with pthread:
273+# [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25460
274+# [2] https://bugzilla.redhat.com/show_bug.cgi?id=661333
275+# [3] https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=468555
276+# To solve this, first try -pthread together with -lpthread for GCC
277+
278+AS_IF([test "x$GCC" = "xyes"],
279+      [ax_pthread_flags="-pthread,-lpthread -pthread -pthreads $ax_pthread_flags"])
280+
281+# Clang takes -pthread (never supported any other flag), but we'll try with -lpthread first
282+
283+AS_IF([test "x$ax_pthread_clang" = "xyes"],
284+      [ax_pthread_flags="-pthread,-lpthread -pthread"])
285+
286+
287+# The presence of a feature test macro requesting re-entrant function
288+# definitions is, on some systems, a strong hint that pthreads support is
289+# correctly enabled
290+
291+case $host_os in
292+        darwin* | hpux* | linux* | osf* | solaris*)
293+        ax_pthread_check_macro="_REENTRANT"
294+        ;;
295+
296+        aix*)
297+        ax_pthread_check_macro="_THREAD_SAFE"
298+        ;;
299+
300+        *)
301+        ax_pthread_check_macro="--"
302+        ;;
303+esac
304+AS_IF([test "x$ax_pthread_check_macro" = "x--"],
305+      [ax_pthread_check_cond=0],
306+      [ax_pthread_check_cond="!defined($ax_pthread_check_macro)"])
307+
308+
309+if test "x$ax_pthread_ok" = "xno"; then
310+for ax_pthread_try_flag in $ax_pthread_flags; do
311+
312+        case $ax_pthread_try_flag in
313+                none)
314+                AC_MSG_CHECKING([whether pthreads work without any flags])
315+                ;;
316+
317+                *,*)
318+                PTHREAD_CFLAGS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\1/"`
319+                PTHREAD_LIBS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\2/"`
320+                AC_MSG_CHECKING([whether pthreads work with "$PTHREAD_CFLAGS" and "$PTHREAD_LIBS"])
321+                ;;
322+
323+                -*)
324+                AC_MSG_CHECKING([whether pthreads work with $ax_pthread_try_flag])
325+                PTHREAD_CFLAGS="$ax_pthread_try_flag"
326+                ;;
327+
328+                pthread-config)
329+                AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no])
330+                AS_IF([test "x$ax_pthread_config" = "xno"], [continue])
331+                PTHREAD_CFLAGS="`pthread-config --cflags`"
332+                PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
333+                ;;
334+
335+                *)
336+                AC_MSG_CHECKING([for the pthreads library -l$ax_pthread_try_flag])
337+                PTHREAD_LIBS="-l$ax_pthread_try_flag"
338+                ;;
339+        esac
340+
341+        ax_pthread_save_CFLAGS="$CFLAGS"
342+        ax_pthread_save_LIBS="$LIBS"
343+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
344+        LIBS="$PTHREAD_LIBS $LIBS"
345+
346+        # Check for various functions.  We must include pthread.h,
347+        # since some functions may be macros.  (On the Sequent, we
348+        # need a special flag -Kthread to make this header compile.)
349+        # We check for pthread_join because it is in -lpthread on IRIX
350+        # while pthread_create is in libc.  We check for pthread_attr_init
351+        # due to DEC craziness with -lpthreads.  We check for
352+        # pthread_cleanup_push because it is one of the few pthread
353+        # functions on Solaris that doesn't have a non-functional libc stub.
354+        # We try pthread_create on general principles.
355+
356+        AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>
357+#                       if $ax_pthread_check_cond
358+#                        error "$ax_pthread_check_macro must be defined"
359+#                       endif
360+                        static void *some_global = NULL;
361+                        static void routine(void *a)
362+                          {
363+                             /* To avoid any unused-parameter or
364+                                unused-but-set-parameter warning.  */
365+                             some_global = a;
366+                          }
367+                        static void *start_routine(void *a) { return a; }],
368+                       [pthread_t th; pthread_attr_t attr;
369+                        pthread_create(&th, 0, start_routine, 0);
370+                        pthread_join(th, 0);
371+                        pthread_attr_init(&attr);
372+                        pthread_cleanup_push(routine, 0);
373+                        pthread_cleanup_pop(0) /* ; */])],
374+            [ax_pthread_ok=yes],
375+            [])
376+
377+        CFLAGS="$ax_pthread_save_CFLAGS"
378+        LIBS="$ax_pthread_save_LIBS"
379+
380+        AC_MSG_RESULT([$ax_pthread_ok])
381+        AS_IF([test "x$ax_pthread_ok" = "xyes"], [break])
382+
383+        PTHREAD_LIBS=""
384+        PTHREAD_CFLAGS=""
385+done
386+fi
387+
388+
389+# Clang needs special handling, because older versions handle the -pthread
390+# option in a rather... idiosyncratic way
391+
392+if test "x$ax_pthread_clang" = "xyes"; then
393+
394+        # Clang takes -pthread; it has never supported any other flag
395+
396+        # (Note 1: This will need to be revisited if a system that Clang
397+        # supports has POSIX threads in a separate library.  This tends not
398+        # to be the way of modern systems, but it's conceivable.)
399+
400+        # (Note 2: On some systems, notably Darwin, -pthread is not needed
401+        # to get POSIX threads support; the API is always present and
402+        # active.  We could reasonably leave PTHREAD_CFLAGS empty.  But
403+        # -pthread does define _REENTRANT, and while the Darwin headers
404+        # ignore this macro, third-party headers might not.)
405+
406+        # However, older versions of Clang make a point of warning the user
407+        # that, in an invocation where only linking and no compilation is
408+        # taking place, the -pthread option has no effect ("argument unused
409+        # during compilation").  They expect -pthread to be passed in only
410+        # when source code is being compiled.
411+        #
412+        # Problem is, this is at odds with the way Automake and most other
413+        # C build frameworks function, which is that the same flags used in
414+        # compilation (CFLAGS) are also used in linking.  Many systems
415+        # supported by AX_PTHREAD require exactly this for POSIX threads
416+        # support, and in fact it is often not straightforward to specify a
417+        # flag that is used only in the compilation phase and not in
418+        # linking.  Such a scenario is extremely rare in practice.
419+        #
420+        # Even though use of the -pthread flag in linking would only print
421+        # a warning, this can be a nuisance for well-run software projects
422+        # that build with -Werror.  So if the active version of Clang has
423+        # this misfeature, we search for an option to squash it.
424+
425+        AC_CACHE_CHECK([whether Clang needs flag to prevent "argument unused" warning when linking with -pthread],
426+            [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG],
427+            [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown
428+             # Create an alternate version of $ac_link that compiles and
429+             # links in two steps (.c -> .o, .o -> exe) instead of one
430+             # (.c -> exe), because the warning occurs only in the second
431+             # step
432+             ax_pthread_save_ac_link="$ac_link"
433+             ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g'
434+             ax_pthread_link_step=`AS_ECHO(["$ac_link"]) | sed "$ax_pthread_sed"`
435+             ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)"
436+             ax_pthread_save_CFLAGS="$CFLAGS"
437+             for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do
438+                AS_IF([test "x$ax_pthread_try" = "xunknown"], [break])
439+                CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS"
440+                ac_link="$ax_pthread_save_ac_link"
441+                AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])],
442+                    [ac_link="$ax_pthread_2step_ac_link"
443+                     AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])],
444+                         [break])
445+                    ])
446+             done
447+             ac_link="$ax_pthread_save_ac_link"
448+             CFLAGS="$ax_pthread_save_CFLAGS"
449+             AS_IF([test "x$ax_pthread_try" = "x"], [ax_pthread_try=no])
450+             ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try"
451+            ])
452+
453+        case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in
454+                no | unknown) ;;
455+                *) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;;
456+        esac
457+
458+fi # $ax_pthread_clang = yes
459+
460+
461+
462+# Various other checks:
463+if test "x$ax_pthread_ok" = "xyes"; then
464+        ax_pthread_save_CFLAGS="$CFLAGS"
465+        ax_pthread_save_LIBS="$LIBS"
466+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
467+        LIBS="$PTHREAD_LIBS $LIBS"
468+
469+        # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
470+        AC_CACHE_CHECK([for joinable pthread attribute],
471+            [ax_cv_PTHREAD_JOINABLE_ATTR],
472+            [ax_cv_PTHREAD_JOINABLE_ATTR=unknown
473+             for ax_pthread_attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
474+                 AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>],
475+                                                 [int attr = $ax_pthread_attr; return attr /* ; */])],
476+                                [ax_cv_PTHREAD_JOINABLE_ATTR=$ax_pthread_attr; break],
477+                                [])
478+             done
479+            ])
480+        AS_IF([test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xunknown" && \
481+               test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xPTHREAD_CREATE_JOINABLE" && \
482+               test "x$ax_pthread_joinable_attr_defined" != "xyes"],
483+              [AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE],
484+                                  [$ax_cv_PTHREAD_JOINABLE_ATTR],
485+                                  [Define to necessary symbol if this constant
486+                                   uses a non-standard name on your system.])
487+               ax_pthread_joinable_attr_defined=yes
488+              ])
489+
490+        AC_CACHE_CHECK([whether more special flags are required for pthreads],
491+            [ax_cv_PTHREAD_SPECIAL_FLAGS],
492+            [ax_cv_PTHREAD_SPECIAL_FLAGS=no
493+             case $host_os in
494+             solaris*)
495+             ax_cv_PTHREAD_SPECIAL_FLAGS="-D_POSIX_PTHREAD_SEMANTICS"
496+             ;;
497+             esac
498+            ])
499+        AS_IF([test "x$ax_cv_PTHREAD_SPECIAL_FLAGS" != "xno" && \
500+               test "x$ax_pthread_special_flags_added" != "xyes"],
501+              [PTHREAD_CFLAGS="$ax_cv_PTHREAD_SPECIAL_FLAGS $PTHREAD_CFLAGS"
502+               ax_pthread_special_flags_added=yes])
503+
504+        AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT],
505+            [ax_cv_PTHREAD_PRIO_INHERIT],
506+            [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread.h>]],
507+                                             [[int i = PTHREAD_PRIO_INHERIT;
508+                                               return i;]])],
509+                            [ax_cv_PTHREAD_PRIO_INHERIT=yes],
510+                            [ax_cv_PTHREAD_PRIO_INHERIT=no])
511+            ])
512+        AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes" && \
513+               test "x$ax_pthread_prio_inherit_defined" != "xyes"],
514+              [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.])
515+               ax_pthread_prio_inherit_defined=yes
516+              ])
517+
518+        CFLAGS="$ax_pthread_save_CFLAGS"
519+        LIBS="$ax_pthread_save_LIBS"
520+
521+        # More AIX lossage: compile with *_r variant
522+        if test "x$GCC" != "xyes"; then
523+            case $host_os in
524+                aix*)
525+                AS_CASE(["x/$CC"],
526+                    [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6],
527+                    [#handle absolute path differently from PATH based program lookup
528+                     AS_CASE(["x$CC"],
529+                         [x/*],
530+                         [
531+			   AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])
532+			   AS_IF([test "x${CXX}" != "x"], [AS_IF([AS_EXECUTABLE_P([${CXX}_r])],[PTHREAD_CXX="${CXX}_r"])])
533+			 ],
534+                         [
535+			   AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])
536+			   AS_IF([test "x${CXX}" != "x"], [AC_CHECK_PROGS([PTHREAD_CXX],[${CXX}_r],[$CXX])])
537+			 ]
538+                     )
539+                    ])
540+                ;;
541+            esac
542+        fi
543+fi
544+
545+test -n "$PTHREAD_CC" || PTHREAD_CC="$CC"
546+test -n "$PTHREAD_CXX" || PTHREAD_CXX="$CXX"
547+
548+AC_SUBST([PTHREAD_LIBS])
549+AC_SUBST([PTHREAD_CFLAGS])
550+AC_SUBST([PTHREAD_CC])
551+AC_SUBST([PTHREAD_CXX])
552+
553+# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
554+if test "x$ax_pthread_ok" = "xyes"; then
555+        ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1])
556+        :
557+else
558+        ax_pthread_ok=no
559+        $2
560+fi
561+AC_LANG_POP
562+])dnl AX_PTHREAD
563diff --git a/configure.ac b/configure.ac
564index b625743..bbf4768 100644
565--- a/configure.ac
566+++ b/configure.ac
567@@ -80,11 +80,22 @@ PKG_CHECK_MODULES(GLIB, [gmodule-2.0 gio-unix-2.0 >= 2.30.0])
568 AC_SUBST(GLIB_CFLAGS)
569 AC_SUBST(GLIB_LIBS)
570
571-PKG_CHECK_MODULES(LIBJS, [mozjs-78])
572-
573-AC_SUBST(LIBJS_CFLAGS)
574-AC_SUBST(LIBJS_CXXFLAGS)
575-AC_SUBST(LIBJS_LIBS)
576+dnl ---------------------------------------------------------------------------
577+dnl - Check javascript backend
578+dnl ---------------------------------------------------------------------------
579+AC_ARG_WITH(duktape, AS_HELP_STRING([--with-duktape],[Use Duktape as javascript backend]),with_duktape=yes,with_duktape=no)
580+AS_IF([test x${with_duktape} == xyes], [
581+  PKG_CHECK_MODULES(LIBJS, [duktape >= 2.2.0 ])
582+  AC_SUBST(LIBJS_CFLAGS)
583+  AC_SUBST(LIBJS_LIBS)
584+], [
585+  PKG_CHECK_MODULES(LIBJS, [mozjs-78])
586+
587+  AC_SUBST(LIBJS_CFLAGS)
588+  AC_SUBST(LIBJS_CXXFLAGS)
589+  AC_SUBST(LIBJS_LIBS)
590+])
591+AM_CONDITIONAL(USE_DUKTAPE, [test x$with_duktape == xyes], [Using duktape as javascript engine library])
592
593 EXPAT_LIB=""
594 AC_ARG_WITH(expat, [  --with-expat=<dir>      Use expat from here],
595@@ -100,6 +111,12 @@ AC_CHECK_LIB(expat,XML_ParserCreate,[EXPAT_LIBS="-lexpat"],
596 	     [AC_MSG_ERROR([Can't find expat library. Please install expat.])])
597 AC_SUBST(EXPAT_LIBS)
598
599+AX_PTHREAD([], [AC_MSG_ERROR([Cannot find the way to enable pthread support.])])
600+LIBS="$PTHREAD_LIBS $LIBS"
601+CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
602+CC="$PTHREAD_CC"
603+AC_CHECK_FUNCS([pthread_condattr_setclock])
604+
605 AC_CHECK_FUNCS(clearenv fdatasync)
606
607 if test "x$GCC" = "xyes"; then
608@@ -581,6 +598,13 @@ echo "
609         PAM support:                ${have_pam}
610         systemdsystemunitdir:       ${systemdsystemunitdir}
611         polkitd user:               ${POLKITD_USER}"
612+if test "x${with_duktape}" = xyes; then
613+echo "
614+        Javascript engine:          Duktape"
615+else
616+echo "
617+        Javascript engine:          Mozjs"
618+fi
619
620 if test "$have_pam" = yes ; then
621 echo "
622diff --git a/docs/man/polkit.xml b/docs/man/polkit.xml
623index 99aa474..90715a5 100644
624--- a/docs/man/polkit.xml
625+++ b/docs/man/polkit.xml
626@@ -639,7 +639,9 @@ polkit.Result = {
627         If user-provided code takes a long time to execute, an exception
628         will be thrown which normally results in the function being
629         terminated (the current limit is 15 seconds). This is used to
630-        catch runaway scripts.
631+        catch runaway scripts. If the duktape JavaScript backend is
632+        compiled in, instead of mozjs, no exception will be thrown—the
633+        script will be killed right away (same timeout).
634       </para>
635
636       <para>
637diff --git a/meson.build b/meson.build
638index b3702be..7506231 100644
639--- a/meson.build
640+++ b/meson.build
641@@ -126,7 +126,18 @@ expat_dep = dependency('expat')
642 assert(cc.has_header('expat.h', dependencies: expat_dep), 'Can\'t find expat.h. Please install expat.')
643 assert(cc.has_function('XML_ParserCreate', dependencies: expat_dep), 'Can\'t find expat library. Please install expat.')
644
645-mozjs_dep = dependency('mozjs-78')
646+duktape_req_version = '>= 2.2.0'
647+
648+js_engine = get_option('js_engine')
649+if js_engine == 'duktape'
650+  js_dep = dependency('duktape', version: duktape_req_version)
651+  libm_dep = cc.find_library('m')
652+  thread_dep = dependency('threads')
653+  func = 'pthread_condattr_setclock'
654+  config_h.set('HAVE_' + func.to_upper(), cc.has_function(func, prefix : '#include <pthread.h>'))
655+elif js_engine == 'mozjs'
656+  js_dep = dependency('mozjs-78')
657+endif
658
659 dbus_dep = dependency('dbus-1')
660 dbus_confdir = dbus_dep.get_pkgconfig_variable('datadir', define_variable: ['datadir', pk_prefix / pk_datadir])   #changed from sysconfdir with respect to commit#8eada3836465838
661@@ -350,6 +361,9 @@ if enable_logind
662   output += '        systemdsystemunitdir:     ' + systemd_systemdsystemunitdir + '\n'
663 endif
664 output += '        polkitd user:             ' + polkitd_user + ' \n'
665+output += '        Javascript engine:        ' + js_engine + '\n'
666+if enable_logind
667+endif
668 output += '        PAM support:              ' + enable_pam.to_string() + '\n\n'
669 if enable_pam
670   output += '        PAM file auth:            ' + pam_conf['PAM_FILE_INCLUDE_AUTH'] + '\n'
671diff --git a/meson_options.txt b/meson_options.txt
672index 25e3e77..76aa311 100644
673--- a/meson_options.txt
674+++ b/meson_options.txt
675@@ -16,3 +16,4 @@ option('introspection', type: 'boolean', value: true, description: 'Enable intro
676
677 option('gtk_doc', type: 'boolean', value: false, description: 'use gtk-doc to build documentation')
678 option('man', type: 'boolean', value: false, description: 'build manual pages')
679+option('js_engine', type: 'combo', choices: ['mozjs', 'duktape'], value: 'duktape', description: 'javascript engine')
680diff --git a/src/polkitbackend/Makefile.am b/src/polkitbackend/Makefile.am
681index 7e3c080..935fb98 100644
682--- a/src/polkitbackend/Makefile.am
683+++ b/src/polkitbackend/Makefile.am
684@@ -17,6 +17,8 @@ AM_CPPFLAGS =                                                   \
685         -DPACKAGE_LIB_DIR=\""$(libdir)"\"                       \
686         -D_POSIX_PTHREAD_SEMANTICS                              \
687         -D_REENTRANT                                            \
688+        -D_XOPEN_SOURCE=700                                     \
689+        -D_GNU_SOURCE=1                                         \
690         $(NULL)
691
692 noinst_LTLIBRARIES=libpolkit-backend-1.la
693@@ -31,9 +33,10 @@ libpolkit_backend_1_la_SOURCES =                                   			\
694         polkitbackend.h									\
695 	polkitbackendtypes.h								\
696 	polkitbackendprivate.h								\
697+	polkitbackendcommon.h			polkitbackendcommon.c			\
698 	polkitbackendauthority.h		polkitbackendauthority.c		\
699 	polkitbackendinteractiveauthority.h	polkitbackendinteractiveauthority.c	\
700-	polkitbackendjsauthority.h		polkitbackendjsauthority.cpp		\
701+	polkitbackendjsauthority.h				\
702 	polkitbackendactionpool.h		polkitbackendactionpool.c		\
703 	polkitbackendactionlookup.h		polkitbackendactionlookup.c		\
704         $(NULL)
705@@ -51,19 +54,27 @@ libpolkit_backend_1_la_CFLAGS =                                        	\
706         -D_POLKIT_BACKEND_COMPILATION                                  	\
707         $(GLIB_CFLAGS)							\
708 	$(LIBSYSTEMD_CFLAGS)						\
709-	$(LIBJS_CFLAGS)							\
710+	$(LIBJS_CFLAGS)						\
711         $(NULL)
712
713 libpolkit_backend_1_la_CXXFLAGS = $(libpolkit_backend_1_la_CFLAGS)
714
715 libpolkit_backend_1_la_LIBADD =                               		\
716         $(GLIB_LIBS)							\
717+        $(DUKTAPE_LIBS)							\
718 	$(LIBSYSTEMD_LIBS)						\
719 	$(top_builddir)/src/polkit/libpolkit-gobject-1.la		\
720 	$(EXPAT_LIBS)							\
721-	$(LIBJS_LIBS)							\
722+	$(LIBJS_LIBS)                                                   \
723         $(NULL)
724
725+if USE_DUKTAPE
726+libpolkit_backend_1_la_SOURCES += polkitbackendduktapeauthority.c
727+libpolkit_backend_1_la_LIBADD += -lm
728+else
729+libpolkit_backend_1_la_SOURCES += polkitbackendjsauthority.cpp
730+endif
731+
732 rulesdir = $(sysconfdir)/polkit-1/rules.d
733 rules_DATA = 50-default.rules
734
735diff --git a/src/polkitbackend/meson.build b/src/polkitbackend/meson.build
736index 93c3c34..99f8e33 100644
737--- a/src/polkitbackend/meson.build
738+++ b/src/polkitbackend/meson.build
739@@ -4,8 +4,8 @@ sources = files(
740   'polkitbackendactionlookup.c',
741   'polkitbackendactionpool.c',
742   'polkitbackendauthority.c',
743+  'polkitbackendcommon.c',
744   'polkitbackendinteractiveauthority.c',
745-  'polkitbackendjsauthority.cpp',
746 )
747
748 output = 'initjs.h'
749@@ -21,7 +21,7 @@ sources += custom_target(
750 deps = [
751   expat_dep,
752   libpolkit_gobject_dep,
753-  mozjs_dep,
754+  js_dep,
755 ]
756
757 c_flags = [
758@@ -29,8 +29,18 @@ c_flags = [
759   '-D_POLKIT_BACKEND_COMPILATION',
760   '-DPACKAGE_DATA_DIR="@0@"'.format(pk_prefix / pk_datadir),
761   '-DPACKAGE_SYSCONF_DIR="@0@"'.format(pk_prefix / pk_sysconfdir),
762+  '-D_XOPEN_SOURCE=700',
763+  '-D_GNU_SOURCE=1',
764 ]
765
766+if js_engine == 'duktape'
767+  sources += files('polkitbackendduktapeauthority.c')
768+  deps += libm_dep
769+  deps += thread_dep
770+elif js_engine == 'mozjs'
771+  sources += files('polkitbackendjsauthority.cpp')
772+endif
773+
774 if enable_logind
775   sources += files('polkitbackendsessionmonitor-systemd.c')
776
777diff --git a/src/polkitbackend/polkitbackendcommon.c b/src/polkitbackend/polkitbackendcommon.c
778new file mode 100644
779index 0000000..6783dff
780--- /dev/null
781+++ b/src/polkitbackend/polkitbackendcommon.c
782@@ -0,0 +1,530 @@
783+/*
784+ * Copyright (C) 2008 Red Hat, Inc.
785+ *
786+ * This library is free software; you can redistribute it and/or
787+ * modify it under the terms of the GNU Lesser General Public
788+ * License as published by the Free Software Foundation; either
789+ * version 2 of the License, or (at your option) any later version.
790+ *
791+ * This library is distributed in the hope that it will be useful,
792+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
793+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
794+ * Lesser General Public License for more details.
795+ *
796+ * You should have received a copy of the GNU Lesser General
797+ * Public License along with this library; if not, write to the
798+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
799+ * Boston, MA 02111-1307, USA.
800+ *
801+ * Author: David Zeuthen <davidz@redhat.com>
802+ */
803+
804+#include "polkitbackendcommon.h"
805+
806+static void
807+utils_child_watch_from_release_cb (GPid     pid,
808+                                   gint     status,
809+                                   gpointer user_data)
810+{
811+}
812+
813+static void
814+utils_spawn_data_free (UtilsSpawnData *data)
815+{
816+  if (data->timeout_source != NULL)
817+    {
818+      g_source_destroy (data->timeout_source);
819+      data->timeout_source = NULL;
820+    }
821+
822+  /* Nuke the child, if necessary */
823+  if (data->child_watch_source != NULL)
824+    {
825+      g_source_destroy (data->child_watch_source);
826+      data->child_watch_source = NULL;
827+    }
828+
829+  if (data->child_pid != 0)
830+    {
831+      GSource *source;
832+      kill (data->child_pid, SIGTERM);
833+      /* OK, we need to reap for the child ourselves - we don't want
834+       * to use waitpid() because that might block the calling
835+       * thread (the child might handle SIGTERM and use several
836+       * seconds for cleanup/rollback).
837+       *
838+       * So we use GChildWatch instead.
839+       *
840+       * Avoid taking a references to ourselves. but note that we need
841+       * to pass the GSource so we can nuke it once handled.
842+       */
843+      source = g_child_watch_source_new (data->child_pid);
844+      g_source_set_callback (source,
845+                             (GSourceFunc) utils_child_watch_from_release_cb,
846+                             source,
847+                             (GDestroyNotify) g_source_destroy);
848+      g_source_attach (source, data->main_context);
849+      g_source_unref (source);
850+      data->child_pid = 0;
851+    }
852+
853+  if (data->child_stdout != NULL)
854+    {
855+      g_string_free (data->child_stdout, TRUE);
856+      data->child_stdout = NULL;
857+    }
858+
859+  if (data->child_stderr != NULL)
860+    {
861+      g_string_free (data->child_stderr, TRUE);
862+      data->child_stderr = NULL;
863+    }
864+
865+  if (data->child_stdout_channel != NULL)
866+    {
867+      g_io_channel_unref (data->child_stdout_channel);
868+      data->child_stdout_channel = NULL;
869+    }
870+  if (data->child_stderr_channel != NULL)
871+    {
872+      g_io_channel_unref (data->child_stderr_channel);
873+      data->child_stderr_channel = NULL;
874+    }
875+
876+  if (data->child_stdout_source != NULL)
877+    {
878+      g_source_destroy (data->child_stdout_source);
879+      data->child_stdout_source = NULL;
880+    }
881+  if (data->child_stderr_source != NULL)
882+    {
883+      g_source_destroy (data->child_stderr_source);
884+      data->child_stderr_source = NULL;
885+    }
886+
887+  if (data->child_stdout_fd != -1)
888+    {
889+      g_warn_if_fail (close (data->child_stdout_fd) == 0);
890+      data->child_stdout_fd = -1;
891+    }
892+  if (data->child_stderr_fd != -1)
893+    {
894+      g_warn_if_fail (close (data->child_stderr_fd) == 0);
895+      data->child_stderr_fd = -1;
896+    }
897+
898+  if (data->cancellable_handler_id > 0)
899+    {
900+      g_cancellable_disconnect (data->cancellable, data->cancellable_handler_id);
901+      data->cancellable_handler_id = 0;
902+    }
903+
904+  if (data->main_context != NULL)
905+    g_main_context_unref (data->main_context);
906+
907+  if (data->cancellable != NULL)
908+    g_object_unref (data->cancellable);
909+
910+  g_slice_free (UtilsSpawnData, data);
911+}
912+
913+/* called in the thread where @cancellable was cancelled */
914+static void
915+utils_on_cancelled (GCancellable *cancellable,
916+                    gpointer      user_data)
917+{
918+  UtilsSpawnData *data = (UtilsSpawnData *)user_data;
919+  GError *error;
920+
921+  error = NULL;
922+  g_warn_if_fail (g_cancellable_set_error_if_cancelled (cancellable, &error));
923+  g_simple_async_result_take_error (data->simple, error);
924+  g_simple_async_result_complete_in_idle (data->simple);
925+  g_object_unref (data->simple);
926+}
927+
928+static gboolean
929+utils_timeout_cb (gpointer user_data)
930+{
931+  UtilsSpawnData *data = (UtilsSpawnData *)user_data;
932+
933+  data->timed_out = TRUE;
934+
935+  /* ok, timeout is history, make sure we don't free it in spawn_data_free() */
936+  data->timeout_source = NULL;
937+
938+  /* we're done */
939+  g_simple_async_result_complete_in_idle (data->simple);
940+  g_object_unref (data->simple);
941+
942+  return FALSE; /* remove source */
943+}
944+
945+static void
946+utils_child_watch_cb (GPid     pid,
947+                      gint     status,
948+                      gpointer user_data)
949+{
950+  UtilsSpawnData *data = (UtilsSpawnData *)user_data;
951+  gchar *buf;
952+  gsize buf_size;
953+
954+  if (g_io_channel_read_to_end (data->child_stdout_channel, &buf, &buf_size, NULL) == G_IO_STATUS_NORMAL)
955+    {
956+      g_string_append_len (data->child_stdout, buf, buf_size);
957+      g_free (buf);
958+    }
959+  if (g_io_channel_read_to_end (data->child_stderr_channel, &buf, &buf_size, NULL) == G_IO_STATUS_NORMAL)
960+    {
961+      g_string_append_len (data->child_stderr, buf, buf_size);
962+      g_free (buf);
963+    }
964+
965+  data->exit_status = status;
966+
967+  /* ok, child watch is history, make sure we don't free it in spawn_data_free() */
968+  data->child_pid = 0;
969+  data->child_watch_source = NULL;
970+
971+  /* we're done */
972+  g_simple_async_result_complete_in_idle (data->simple);
973+  g_object_unref (data->simple);
974+}
975+
976+static gboolean
977+utils_read_child_stderr (GIOChannel *channel,
978+                         GIOCondition condition,
979+                         gpointer user_data)
980+{
981+  UtilsSpawnData *data = (UtilsSpawnData *)user_data;
982+  gchar buf[1024];
983+  gsize bytes_read;
984+
985+  g_io_channel_read_chars (channel, buf, sizeof buf, &bytes_read, NULL);
986+  g_string_append_len (data->child_stderr, buf, bytes_read);
987+  return TRUE;
988+}
989+
990+static gboolean
991+utils_read_child_stdout (GIOChannel *channel,
992+                         GIOCondition condition,
993+                         gpointer user_data)
994+{
995+  UtilsSpawnData *data = (UtilsSpawnData *)user_data;
996+  gchar buf[1024];
997+  gsize bytes_read;
998+
999+  g_io_channel_read_chars (channel, buf, sizeof buf, &bytes_read, NULL);
1000+  g_string_append_len (data->child_stdout, buf, bytes_read);
1001+  return TRUE;
1002+}
1003+
1004+void
1005+polkit_backend_common_spawn (const gchar *const  *argv,
1006+                             guint                timeout_seconds,
1007+                             GCancellable        *cancellable,
1008+                             GAsyncReadyCallback  callback,
1009+                             gpointer             user_data)
1010+{
1011+  UtilsSpawnData *data;
1012+  GError *error;
1013+
1014+  data = g_slice_new0 (UtilsSpawnData);
1015+  data->timeout_seconds = timeout_seconds;
1016+  data->simple = g_simple_async_result_new (NULL,
1017+                                            callback,
1018+                                            user_data,
1019+                                            (gpointer*)polkit_backend_common_spawn);
1020+  data->main_context = g_main_context_get_thread_default ();
1021+  if (data->main_context != NULL)
1022+    g_main_context_ref (data->main_context);
1023+
1024+  data->cancellable = cancellable != NULL ? (GCancellable*)g_object_ref (cancellable) : NULL;
1025+
1026+  data->child_stdout = g_string_new (NULL);
1027+  data->child_stderr = g_string_new (NULL);
1028+  data->child_stdout_fd = -1;
1029+  data->child_stderr_fd = -1;
1030+
1031+  /* the life-cycle of UtilsSpawnData is tied to its GSimpleAsyncResult */
1032+  g_simple_async_result_set_op_res_gpointer (data->simple, data, (GDestroyNotify) utils_spawn_data_free);
1033+
1034+  error = NULL;
1035+  if (data->cancellable != NULL)
1036+    {
1037+      /* could already be cancelled */
1038+      error = NULL;
1039+      if (g_cancellable_set_error_if_cancelled (data->cancellable, &error))
1040+        {
1041+          g_simple_async_result_take_error (data->simple, error);
1042+          g_simple_async_result_complete_in_idle (data->simple);
1043+          g_object_unref (data->simple);
1044+          goto out;
1045+        }
1046+
1047+      data->cancellable_handler_id = g_cancellable_connect (data->cancellable,
1048+                                                            G_CALLBACK (utils_on_cancelled),
1049+                                                            data,
1050+                                                            NULL);
1051+    }
1052+
1053+  error = NULL;
1054+  if (!g_spawn_async_with_pipes (NULL, /* working directory */
1055+                                 (gchar **) argv,
1056+                                 NULL, /* envp */
1057+                                 G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
1058+                                 NULL, /* child_setup */
1059+                                 NULL, /* child_setup's user_data */
1060+                                 &(data->child_pid),
1061+                                 NULL, /* gint *stdin_fd */
1062+                                 &(data->child_stdout_fd),
1063+                                 &(data->child_stderr_fd),
1064+                                 &error))
1065+    {
1066+      g_prefix_error (&error, "Error spawning: ");
1067+      g_simple_async_result_take_error (data->simple, error);
1068+      g_simple_async_result_complete_in_idle (data->simple);
1069+      g_object_unref (data->simple);
1070+      goto out;
1071+    }
1072+
1073+  if (timeout_seconds > 0)
1074+    {
1075+      data->timeout_source = g_timeout_source_new_seconds (timeout_seconds);
1076+      g_source_set_priority (data->timeout_source, G_PRIORITY_DEFAULT);
1077+      g_source_set_callback (data->timeout_source, utils_timeout_cb, data, NULL);
1078+      g_source_attach (data->timeout_source, data->main_context);
1079+      g_source_unref (data->timeout_source);
1080+    }
1081+
1082+  data->child_watch_source = g_child_watch_source_new (data->child_pid);
1083+  g_source_set_callback (data->child_watch_source, (GSourceFunc) utils_child_watch_cb, data, NULL);
1084+  g_source_attach (data->child_watch_source, data->main_context);
1085+  g_source_unref (data->child_watch_source);
1086+
1087+  data->child_stdout_channel = g_io_channel_unix_new (data->child_stdout_fd);
1088+  g_io_channel_set_flags (data->child_stdout_channel, G_IO_FLAG_NONBLOCK, NULL);
1089+  data->child_stdout_source = g_io_create_watch (data->child_stdout_channel, G_IO_IN);
1090+  g_source_set_callback (data->child_stdout_source, (GSourceFunc) utils_read_child_stdout, data, NULL);
1091+  g_source_attach (data->child_stdout_source, data->main_context);
1092+  g_source_unref (data->child_stdout_source);
1093+
1094+  data->child_stderr_channel = g_io_channel_unix_new (data->child_stderr_fd);
1095+  g_io_channel_set_flags (data->child_stderr_channel, G_IO_FLAG_NONBLOCK, NULL);
1096+  data->child_stderr_source = g_io_create_watch (data->child_stderr_channel, G_IO_IN);
1097+  g_source_set_callback (data->child_stderr_source, (GSourceFunc) utils_read_child_stderr, data, NULL);
1098+  g_source_attach (data->child_stderr_source, data->main_context);
1099+  g_source_unref (data->child_stderr_source);
1100+
1101+ out:
1102+  ;
1103+}
1104+
1105+void
1106+polkit_backend_common_on_dir_monitor_changed (GFileMonitor     *monitor,
1107+                                              GFile            *file,
1108+                                              GFile            *other_file,
1109+                                              GFileMonitorEvent event_type,
1110+                                              gpointer          user_data)
1111+{
1112+  PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (user_data);
1113+
1114+  /* TODO: maybe rate-limit so storms of events are collapsed into one with a 500ms resolution?
1115+   *       Because when editing a file with emacs we get 4-8 events..
1116+   */
1117+
1118+  if (file != NULL)
1119+    {
1120+      gchar *name;
1121+
1122+      name = g_file_get_basename (file);
1123+
1124+      /* g_print ("event_type=%d file=%p name=%s\n", event_type, file, name); */
1125+      if (!g_str_has_prefix (name, ".") &&
1126+          !g_str_has_prefix (name, "#") &&
1127+          g_str_has_suffix (name, ".rules") &&
1128+          (event_type == G_FILE_MONITOR_EVENT_CREATED ||
1129+           event_type == G_FILE_MONITOR_EVENT_DELETED ||
1130+           event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT))
1131+        {
1132+          polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
1133+                                        "Reloading rules");
1134+          polkit_backend_common_reload_scripts (authority);
1135+        }
1136+      g_free (name);
1137+    }
1138+}
1139+
1140+gboolean
1141+polkit_backend_common_spawn_finish (GAsyncResult   *res,
1142+                                    gint           *out_exit_status,
1143+                                    gchar         **out_standard_output,
1144+                                    gchar         **out_standard_error,
1145+                                    GError        **error)
1146+{
1147+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
1148+  UtilsSpawnData *data;
1149+  gboolean ret = FALSE;
1150+
1151+  g_return_val_if_fail (G_IS_ASYNC_RESULT (res), FALSE);
1152+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1153+
1154+  g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == polkit_backend_common_spawn);
1155+
1156+  if (g_simple_async_result_propagate_error (simple, error))
1157+    goto out;
1158+
1159+  data = (UtilsSpawnData*)g_simple_async_result_get_op_res_gpointer (simple);
1160+
1161+  if (data->timed_out)
1162+    {
1163+      g_set_error (error,
1164+                   G_IO_ERROR,
1165+                   G_IO_ERROR_TIMED_OUT,
1166+                   "Timed out after %d seconds",
1167+                   data->timeout_seconds);
1168+      goto out;
1169+    }
1170+
1171+  if (out_exit_status != NULL)
1172+    *out_exit_status = data->exit_status;
1173+
1174+  if (out_standard_output != NULL)
1175+    *out_standard_output = g_strdup (data->child_stdout->str);
1176+
1177+  if (out_standard_error != NULL)
1178+    *out_standard_error = g_strdup (data->child_stderr->str);
1179+
1180+  ret = TRUE;
1181+
1182+ out:
1183+  return ret;
1184+}
1185+
1186+static const gchar *
1187+polkit_backend_js_authority_get_name (PolkitBackendAuthority *authority)
1188+{
1189+  return "js";
1190+}
1191+
1192+static const gchar *
1193+polkit_backend_js_authority_get_version (PolkitBackendAuthority *authority)
1194+{
1195+  return PACKAGE_VERSION;
1196+}
1197+
1198+static PolkitAuthorityFeatures
1199+polkit_backend_js_authority_get_features (PolkitBackendAuthority *authority)
1200+{
1201+  return POLKIT_AUTHORITY_FEATURES_TEMPORARY_AUTHORIZATION;
1202+}
1203+
1204+void
1205+polkit_backend_common_js_authority_class_init_common (PolkitBackendJsAuthorityClass *klass)
1206+{
1207+  GObjectClass *gobject_class;
1208+  PolkitBackendAuthorityClass *authority_class;
1209+  PolkitBackendInteractiveAuthorityClass *interactive_authority_class;
1210+
1211+  gobject_class = G_OBJECT_CLASS (klass);
1212+  gobject_class->finalize                               = polkit_backend_common_js_authority_finalize;
1213+  gobject_class->set_property                           = polkit_backend_common_js_authority_set_property;
1214+  gobject_class->constructed                            = polkit_backend_common_js_authority_constructed;
1215+
1216+  authority_class = POLKIT_BACKEND_AUTHORITY_CLASS (klass);
1217+  authority_class->get_name                             = polkit_backend_js_authority_get_name;
1218+  authority_class->get_version                          = polkit_backend_js_authority_get_version;
1219+  authority_class->get_features                         = polkit_backend_js_authority_get_features;
1220+
1221+  interactive_authority_class = POLKIT_BACKEND_INTERACTIVE_AUTHORITY_CLASS (klass);
1222+  interactive_authority_class->get_admin_identities     = polkit_backend_common_js_authority_get_admin_auth_identities;
1223+  interactive_authority_class->check_authorization_sync = polkit_backend_common_js_authority_check_authorization_sync;
1224+
1225+  g_object_class_install_property (gobject_class,
1226+                                   PROP_RULES_DIRS,
1227+                                   g_param_spec_boxed ("rules-dirs",
1228+                                                       NULL,
1229+                                                       NULL,
1230+                                                       G_TYPE_STRV,
1231+                                                       G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
1232+}
1233+
1234+gint
1235+polkit_backend_common_rules_file_name_cmp (const gchar *a,
1236+                                           const gchar *b)
1237+{
1238+  gint ret;
1239+  const gchar *a_base;
1240+  const gchar *b_base;
1241+
1242+  a_base = strrchr (a, '/');
1243+  b_base = strrchr (b, '/');
1244+
1245+  g_assert (a_base != NULL);
1246+  g_assert (b_base != NULL);
1247+  a_base += 1;
1248+  b_base += 1;
1249+
1250+  ret = g_strcmp0 (a_base, b_base);
1251+  if (ret == 0)
1252+    {
1253+      /* /etc wins over /usr */
1254+      ret = g_strcmp0 (a, b);
1255+      g_assert (ret != 0);
1256+    }
1257+
1258+  return ret;
1259+}
1260+
1261+const gchar *
1262+polkit_backend_common_get_signal_name (gint signal_number)
1263+{
1264+  switch (signal_number)
1265+    {
1266+#define _HANDLE_SIG(sig) case sig: return #sig;
1267+    _HANDLE_SIG (SIGHUP);
1268+    _HANDLE_SIG (SIGINT);
1269+    _HANDLE_SIG (SIGQUIT);
1270+    _HANDLE_SIG (SIGILL);
1271+    _HANDLE_SIG (SIGABRT);
1272+    _HANDLE_SIG (SIGFPE);
1273+    _HANDLE_SIG (SIGKILL);
1274+    _HANDLE_SIG (SIGSEGV);
1275+    _HANDLE_SIG (SIGPIPE);
1276+    _HANDLE_SIG (SIGALRM);
1277+    _HANDLE_SIG (SIGTERM);
1278+    _HANDLE_SIG (SIGUSR1);
1279+    _HANDLE_SIG (SIGUSR2);
1280+    _HANDLE_SIG (SIGCHLD);
1281+    _HANDLE_SIG (SIGCONT);
1282+    _HANDLE_SIG (SIGSTOP);
1283+    _HANDLE_SIG (SIGTSTP);
1284+    _HANDLE_SIG (SIGTTIN);
1285+    _HANDLE_SIG (SIGTTOU);
1286+    _HANDLE_SIG (SIGBUS);
1287+#ifdef SIGPOLL
1288+    _HANDLE_SIG (SIGPOLL);
1289+#endif
1290+    _HANDLE_SIG (SIGPROF);
1291+    _HANDLE_SIG (SIGSYS);
1292+    _HANDLE_SIG (SIGTRAP);
1293+    _HANDLE_SIG (SIGURG);
1294+    _HANDLE_SIG (SIGVTALRM);
1295+    _HANDLE_SIG (SIGXCPU);
1296+    _HANDLE_SIG (SIGXFSZ);
1297+#undef _HANDLE_SIG
1298+    default:
1299+      break;
1300+    }
1301+  return "UNKNOWN_SIGNAL";
1302+}
1303+
1304+void
1305+polkit_backend_common_spawn_cb (GObject       *source_object,
1306+                                GAsyncResult  *res,
1307+                                gpointer       user_data)
1308+{
1309+  SpawnData *data = (SpawnData *)user_data;
1310+  data->res = (GAsyncResult*)g_object_ref (res);
1311+  g_main_loop_quit (data->loop);
1312+}
1313diff --git a/src/polkitbackend/polkitbackendcommon.h b/src/polkitbackend/polkitbackendcommon.h
1314new file mode 100644
1315index 0000000..dd700fc
1316--- /dev/null
1317+++ b/src/polkitbackend/polkitbackendcommon.h
1318@@ -0,0 +1,158 @@
1319+/*
1320+ * Copyright (C) 2008 Red Hat, Inc.
1321+ *
1322+ * This library is free software; you can redistribute it and/or
1323+ * modify it under the terms of the GNU Lesser General Public
1324+ * License as published by the Free Software Foundation; either
1325+ * version 2 of the License, or (at your option) any later version.
1326+ *
1327+ * This library is distributed in the hope that it will be useful,
1328+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1329+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
1330+ * Lesser General Public License for more details.
1331+ *
1332+ * You should have received a copy of the GNU Lesser General
1333+ * Public License along with this library; if not, write to the
1334+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
1335+ * Boston, MA 02111-1307, USA.
1336+ *
1337+ * Author: David Zeuthen <davidz@redhat.com>
1338+ */
1339+
1340+#if !defined (_POLKIT_BACKEND_COMPILATION) && !defined(_POLKIT_BACKEND_INSIDE_POLKIT_BACKEND_H)
1341+#error "Only <polkitbackend/polkitbackend.h> can be included directly, this file may disappear or change contents."
1342+#endif
1343+
1344+#ifndef __POLKIT_BACKEND_COMMON_H
1345+#define __POLKIT_BACKEND_COMMON_H
1346+
1347+#include "config.h"
1348+#include <sys/wait.h>
1349+#include <errno.h>
1350+#include <pwd.h>
1351+#include <grp.h>
1352+#ifdef HAVE_NETGROUP_H
1353+#include <netgroup.h>
1354+#else
1355+#include <netdb.h>
1356+#endif
1357+#include <string.h>
1358+#include <glib/gstdio.h>
1359+#include <locale.h>
1360+#include <glib/gi18n-lib.h> //here, all things glib via glib.h (including -> gspawn.h)
1361+
1362+#include <polkit/polkit.h>
1363+#include "polkitbackendjsauthority.h"
1364+
1365+#include <polkit/polkitprivate.h>
1366+
1367+#ifdef HAVE_LIBSYSTEMD
1368+#include <systemd/sd-login.h>
1369+#endif /* HAVE_LIBSYSTEMD */
1370+
1371+#define RUNAWAY_KILLER_TIMEOUT (15)
1372+
1373+#ifdef __cplusplus
1374+extern "C" {
1375+#endif
1376+
1377+enum
1378+{
1379+  PROP_0,
1380+  PROP_RULES_DIRS,
1381+};
1382+
1383+typedef struct
1384+{
1385+  GSimpleAsyncResult *simple; /* borrowed reference */
1386+  GMainContext *main_context; /* may be NULL */
1387+
1388+  GCancellable *cancellable;  /* may be NULL */
1389+  gulong cancellable_handler_id;
1390+
1391+  GPid child_pid;
1392+  gint child_stdout_fd;
1393+  gint child_stderr_fd;
1394+
1395+  GIOChannel *child_stdout_channel;
1396+  GIOChannel *child_stderr_channel;
1397+
1398+  GSource *child_watch_source;
1399+  GSource *child_stdout_source;
1400+  GSource *child_stderr_source;
1401+
1402+  guint timeout_seconds;
1403+  gboolean timed_out;
1404+  GSource *timeout_source;
1405+
1406+  GString *child_stdout;
1407+  GString *child_stderr;
1408+
1409+  gint exit_status;
1410+} UtilsSpawnData;
1411+
1412+typedef struct
1413+{
1414+  GMainLoop *loop;
1415+  GAsyncResult *res;
1416+} SpawnData;
1417+
1418+void polkit_backend_common_spawn (const gchar *const  *argv,
1419+                                  guint                timeout_seconds,
1420+                                  GCancellable        *cancellable,
1421+                                  GAsyncReadyCallback  callback,
1422+                                  gpointer             user_data);
1423+void polkit_backend_common_spawn_cb (GObject       *source_object,
1424+                                     GAsyncResult  *res,
1425+                                     gpointer       user_data);
1426+gboolean polkit_backend_common_spawn_finish (GAsyncResult   *res,
1427+                                             gint           *out_exit_status,
1428+                                             gchar         **out_standard_output,
1429+                                             gchar         **out_standard_error,
1430+                                             GError        **error);
1431+
1432+void polkit_backend_common_on_dir_monitor_changed (GFileMonitor     *monitor,
1433+                                                   GFile            *file,
1434+                                                   GFile            *other_file,
1435+                                                   GFileMonitorEvent event_type,
1436+                                                   gpointer          user_data);
1437+
1438+void polkit_backend_common_js_authority_class_init_common (PolkitBackendJsAuthorityClass *klass);
1439+
1440+gint polkit_backend_common_rules_file_name_cmp (const gchar *a,
1441+                                                const gchar *b);
1442+
1443+const gchar *polkit_backend_common_get_signal_name (gint signal_number);
1444+
1445+/* To be provided by each JS backend, from here onwards  ---------------------------------------------- */
1446+
1447+void polkit_backend_common_reload_scripts (PolkitBackendJsAuthority *authority);
1448+void polkit_backend_common_js_authority_finalize (GObject *object);
1449+void polkit_backend_common_js_authority_constructed (GObject *object);
1450+GList *polkit_backend_common_js_authority_get_admin_auth_identities (PolkitBackendInteractiveAuthority *_authority,
1451+                                                                     PolkitSubject                     *caller,
1452+                                                                     PolkitSubject                     *subject,
1453+                                                                     PolkitIdentity                    *user_for_subject,
1454+                                                                     gboolean                           subject_is_local,
1455+                                                                     gboolean                           subject_is_active,
1456+                                                                     const gchar                       *action_id,
1457+                                                                     PolkitDetails                     *details);
1458+void polkit_backend_common_js_authority_set_property (GObject      *object,
1459+                                                      guint         property_id,
1460+                                                      const GValue *value,
1461+                                                      GParamSpec   *pspec);
1462+PolkitImplicitAuthorization polkit_backend_common_js_authority_check_authorization_sync (PolkitBackendInteractiveAuthority *_authority,
1463+                                                                                         PolkitSubject                     *caller,
1464+                                                                                         PolkitSubject                     *subject,
1465+                                                                                         PolkitIdentity                    *user_for_subject,
1466+                                                                                         gboolean                           subject_is_local,
1467+                                                                                         gboolean                           subject_is_active,
1468+                                                                                         const gchar                       *action_id,
1469+                                                                                         PolkitDetails                     *details,
1470+                                                                                         PolkitImplicitAuthorization        implicit);
1471+#ifdef __cplusplus
1472+}
1473+#endif
1474+
1475+#endif /* __POLKIT_BACKEND_COMMON_H */
1476+
1477diff --git a/src/polkitbackend/polkitbackendduktapeauthority.c b/src/polkitbackend/polkitbackendduktapeauthority.c
1478new file mode 100644
1479index 0000000..c89dbcf
1480--- /dev/null
1481+++ b/src/polkitbackend/polkitbackendduktapeauthority.c
1482@@ -0,0 +1,1051 @@
1483+/*
1484+ * Copyright (C) 2008-2012 Red Hat, Inc.
1485+ * Copyright (C) 2015 Tangent Space <jstpierre@mecheye.net>
1486+ * Copyright (C) 2019 Wu Xiaotian <yetist@gmail.com>
1487+ *
1488+ * This library is free software; you can redistribute it and/or
1489+ * modify it under the terms of the GNU Lesser General Public
1490+ * License as published by the Free Software Foundation; either
1491+ * version 2 of the License, or (at your option) any later version.
1492+ *
1493+ * This library is distributed in the hope that it will be useful,
1494+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1495+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
1496+ * Lesser General Public License for more details.
1497+ *
1498+ * You should have received a copy of the GNU Lesser General
1499+ * Public License along with this library; if not, write to the
1500+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
1501+ * Boston, MA 02111-1307, USA.
1502+ *
1503+ * Author: David Zeuthen <davidz@redhat.com>
1504+ */
1505+
1506+#include <pthread.h>
1507+
1508+#include "polkitbackendcommon.h"
1509+
1510+#include "duktape.h"
1511+
1512+/* Built source and not too big to worry about deduplication */
1513+#include "initjs.h" /* init.js */
1514+
1515+/**
1516+ * SECTION:polkitbackendjsauthority
1517+ * @title: PolkitBackendJsAuthority
1518+ * @short_description: JS Authority
1519+ * @stability: Unstable
1520+ *
1521+ * An (Duktape-based) implementation of #PolkitBackendAuthority that reads and
1522+ * evaluates Javascript files and supports interaction with authentication
1523+ * agents (virtue of being based on #PolkitBackendInteractiveAuthority).
1524+ */
1525+
1526+/* ---------------------------------------------------------------------------------------------------- */
1527+
1528+struct _PolkitBackendJsAuthorityPrivate
1529+{
1530+  gchar **rules_dirs;
1531+  GFileMonitor **dir_monitors; /* NULL-terminated array of GFileMonitor instances */
1532+
1533+  duk_context *cx;
1534+
1535+  pthread_t runaway_killer_thread;
1536+};
1537+
1538+enum
1539+{
1540+  RUNAWAY_KILLER_THREAD_EXIT_STATUS_UNSET,
1541+  RUNAWAY_KILLER_THREAD_EXIT_STATUS_SUCCESS,
1542+  RUNAWAY_KILLER_THREAD_EXIT_STATUS_FAILURE,
1543+};
1544+
1545+static gboolean execute_script_with_runaway_killer(PolkitBackendJsAuthority *authority,
1546+                                                   const gchar *filename);
1547+
1548+/* ---------------------------------------------------------------------------------------------------- */
1549+
1550+G_DEFINE_TYPE (PolkitBackendJsAuthority, polkit_backend_js_authority, POLKIT_BACKEND_TYPE_INTERACTIVE_AUTHORITY);
1551+
1552+/* ---------------------------------------------------------------------------------------------------- */
1553+
1554+static duk_ret_t js_polkit_log (duk_context *cx);
1555+static duk_ret_t js_polkit_spawn (duk_context *cx);
1556+static duk_ret_t js_polkit_user_is_in_netgroup (duk_context *cx);
1557+
1558+static const duk_function_list_entry js_polkit_functions[] =
1559+{
1560+  { "log", js_polkit_log, 1 },
1561+  { "spawn", js_polkit_spawn, 1 },
1562+  { "_userIsInNetGroup", js_polkit_user_is_in_netgroup, 2 },
1563+  { NULL, NULL, 0 },
1564+};
1565+
1566+static void report_error (void     *udata,
1567+                          const char *msg)
1568+{
1569+    PolkitBackendJsAuthority *authority = udata;
1570+    polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
1571+                                  "fatal Duktape JS backend error: %s",
1572+                                  (msg ? msg : "no message"));
1573+}
1574+
1575+static void
1576+polkit_backend_js_authority_init (PolkitBackendJsAuthority *authority)
1577+{
1578+  authority->priv = G_TYPE_INSTANCE_GET_PRIVATE (authority,
1579+                                                 POLKIT_BACKEND_TYPE_JS_AUTHORITY,
1580+                                                 PolkitBackendJsAuthorityPrivate);
1581+}
1582+
1583+static void
1584+load_scripts (PolkitBackendJsAuthority  *authority)
1585+{
1586+  GList *files = NULL;
1587+  GList *l;
1588+  guint num_scripts = 0;
1589+  GError *error = NULL;
1590+  guint n;
1591+
1592+  files = NULL;
1593+
1594+  for (n = 0; authority->priv->rules_dirs != NULL && authority->priv->rules_dirs[n] != NULL; n++)
1595+    {
1596+      const gchar *dir_name = authority->priv->rules_dirs[n];
1597+      GDir *dir = NULL;
1598+
1599+      polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
1600+                                    "Loading rules from directory %s",
1601+                                    dir_name);
1602+
1603+      dir = g_dir_open (dir_name,
1604+                        0,
1605+                        &error);
1606+      if (dir == NULL)
1607+        {
1608+          polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
1609+                                        "Error opening rules directory: %s (%s, %d)",
1610+                                        error->message, g_quark_to_string (error->domain), error->code);
1611+          g_clear_error (&error);
1612+        }
1613+      else
1614+        {
1615+          const gchar *name;
1616+          while ((name = g_dir_read_name (dir)) != NULL)
1617+            {
1618+              if (g_str_has_suffix (name, ".rules"))
1619+                files = g_list_prepend (files, g_strdup_printf ("%s/%s", dir_name, name));
1620+            }
1621+          g_dir_close (dir);
1622+        }
1623+    }
1624+
1625+  files = g_list_sort (files, (GCompareFunc) polkit_backend_common_rules_file_name_cmp);
1626+
1627+  for (l = files; l != NULL; l = l->next)
1628+    {
1629+      const gchar *filename = (gchar *)l->data;
1630+
1631+      if (!execute_script_with_runaway_killer(authority, filename))
1632+          continue;
1633+      num_scripts++;
1634+    }
1635+
1636+  polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
1637+                                "Finished loading, compiling and executing %d rules",
1638+                                num_scripts);
1639+  g_list_free_full (files, g_free);
1640+}
1641+
1642+void
1643+polkit_backend_common_reload_scripts (PolkitBackendJsAuthority *authority)
1644+{
1645+  duk_context *cx = authority->priv->cx;
1646+
1647+  duk_set_top (cx, 0);
1648+  if (!duk_get_global_string (cx, "polkit")) {
1649+      polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
1650+                                    "Error deleting old rules, not loading new ones");
1651+      return;
1652+  }
1653+  duk_push_string (cx, "_deleteRules");
1654+
1655+  duk_call_prop (cx, 0, 0);
1656+
1657+  polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
1658+                                "Collecting garbage unconditionally...");
1659+
1660+  load_scripts (authority);
1661+
1662+  /* Let applications know we have new rules... */
1663+  g_signal_emit_by_name (authority, "changed");
1664+}
1665+
1666+static void
1667+setup_file_monitors (PolkitBackendJsAuthority *authority)
1668+{
1669+  guint n;
1670+  GPtrArray *p;
1671+
1672+  p = g_ptr_array_new ();
1673+  for (n = 0; authority->priv->rules_dirs != NULL && authority->priv->rules_dirs[n] != NULL; n++)
1674+    {
1675+      GFile *file;
1676+      GError *error;
1677+      GFileMonitor *monitor;
1678+
1679+      file = g_file_new_for_path (authority->priv->rules_dirs[n]);
1680+      error = NULL;
1681+      monitor = g_file_monitor_directory (file,
1682+                                          G_FILE_MONITOR_NONE,
1683+                                          NULL,
1684+                                          &error);
1685+      g_object_unref (file);
1686+      if (monitor == NULL)
1687+        {
1688+          g_warning ("Error monitoring directory %s: %s",
1689+                     authority->priv->rules_dirs[n],
1690+                     error->message);
1691+          g_clear_error (&error);
1692+        }
1693+      else
1694+        {
1695+          g_signal_connect (monitor,
1696+                            "changed",
1697+                            G_CALLBACK (polkit_backend_common_on_dir_monitor_changed),
1698+                            authority);
1699+          g_ptr_array_add (p, monitor);
1700+        }
1701+    }
1702+  g_ptr_array_add (p, NULL);
1703+  authority->priv->dir_monitors = (GFileMonitor**) g_ptr_array_free (p, FALSE);
1704+}
1705+
1706+void
1707+polkit_backend_common_js_authority_constructed (GObject *object)
1708+{
1709+  PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (object);
1710+  duk_context *cx;
1711+
1712+  cx = duk_create_heap (NULL, NULL, NULL, authority, report_error);
1713+  if (cx == NULL)
1714+    goto fail;
1715+
1716+  authority->priv->cx = cx;
1717+
1718+  duk_push_global_object (cx);
1719+  duk_push_object (cx);
1720+  duk_put_function_list (cx, -1, js_polkit_functions);
1721+  duk_put_prop_string (cx, -2, "polkit");
1722+
1723+  /* load polkit objects/functions into JS context (e.g. addRule(),
1724+   * _deleteRules(), _runRules() et al)
1725+   */
1726+  duk_eval_string (cx, init_js);
1727+
1728+  if (authority->priv->rules_dirs == NULL)
1729+    {
1730+      authority->priv->rules_dirs = g_new0 (gchar *, 3);
1731+      authority->priv->rules_dirs[0] = g_strdup (PACKAGE_SYSCONF_DIR "/polkit-1/rules.d");
1732+      authority->priv->rules_dirs[1] = g_strdup (PACKAGE_DATA_DIR "/polkit-1/rules.d");
1733+    }
1734+
1735+  setup_file_monitors (authority);
1736+  load_scripts (authority);
1737+
1738+  G_OBJECT_CLASS (polkit_backend_js_authority_parent_class)->constructed (object);
1739+  return;
1740+
1741+ fail:
1742+  g_critical ("Error initializing JavaScript environment");
1743+  g_assert_not_reached ();
1744+}
1745+
1746+void
1747+polkit_backend_common_js_authority_finalize (GObject *object)
1748+{
1749+  PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (object);
1750+  guint n;
1751+
1752+  for (n = 0; authority->priv->dir_monitors != NULL && authority->priv->dir_monitors[n] != NULL; n++)
1753+    {
1754+      GFileMonitor *monitor = authority->priv->dir_monitors[n];
1755+      g_signal_handlers_disconnect_by_func (monitor,
1756+                                            G_CALLBACK (polkit_backend_common_on_dir_monitor_changed),
1757+                                            authority);
1758+      g_object_unref (monitor);
1759+    }
1760+  g_free (authority->priv->dir_monitors);
1761+  g_strfreev (authority->priv->rules_dirs);
1762+
1763+  duk_destroy_heap (authority->priv->cx);
1764+
1765+  G_OBJECT_CLASS (polkit_backend_js_authority_parent_class)->finalize (object);
1766+}
1767+
1768+void
1769+polkit_backend_common_js_authority_set_property (GObject      *object,
1770+                                                 guint         property_id,
1771+                                                 const GValue *value,
1772+                                                 GParamSpec   *pspec)
1773+{
1774+  PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (object);
1775+
1776+  switch (property_id)
1777+    {
1778+      case PROP_RULES_DIRS:
1779+        g_assert (authority->priv->rules_dirs == NULL);
1780+        authority->priv->rules_dirs = (gchar **) g_value_dup_boxed (value);
1781+        break;
1782+
1783+      default:
1784+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1785+        break;
1786+    }
1787+}
1788+
1789+static void
1790+polkit_backend_js_authority_class_init (PolkitBackendJsAuthorityClass *klass)
1791+{
1792+  polkit_backend_common_js_authority_class_init_common (klass);
1793+  g_type_class_add_private (klass, sizeof (PolkitBackendJsAuthorityPrivate));
1794+}
1795+
1796+/* ---------------------------------------------------------------------------------------------------- */
1797+
1798+static void
1799+set_property_str (duk_context *cx,
1800+                  const gchar *name,
1801+                  const gchar *value)
1802+{
1803+  duk_push_string (cx, value);
1804+  duk_put_prop_string (cx, -2, name);
1805+}
1806+
1807+static void
1808+set_property_strv (duk_context *cx,
1809+                   const gchar *name,
1810+                   GPtrArray   *value)
1811+{
1812+  guint n;
1813+  duk_push_array (cx);
1814+  for (n = 0; n < value->len; n++)
1815+    {
1816+      duk_push_string (cx, g_ptr_array_index (value, n));
1817+      duk_put_prop_index (cx, -2, n);
1818+    }
1819+  duk_put_prop_string (cx, -2, name);
1820+}
1821+
1822+static void
1823+set_property_int32 (duk_context *cx,
1824+                    const gchar *name,
1825+                    gint32       value)
1826+{
1827+  duk_push_int (cx, value);
1828+  duk_put_prop_string (cx, -2, name);
1829+}
1830+
1831+static void
1832+set_property_bool (duk_context *cx,
1833+                   const char  *name,
1834+                   gboolean     value)
1835+{
1836+  duk_push_boolean (cx, value);
1837+  duk_put_prop_string (cx, -2, name);
1838+}
1839+
1840+/* ---------------------------------------------------------------------------------------------------- */
1841+
1842+static gboolean
1843+push_subject (duk_context               *cx,
1844+              PolkitSubject             *subject,
1845+              PolkitIdentity            *user_for_subject,
1846+              gboolean                   subject_is_local,
1847+              gboolean                   subject_is_active,
1848+              GError                   **error)
1849+{
1850+  gboolean ret = FALSE;
1851+  pid_t pid;
1852+  uid_t uid;
1853+  gchar *user_name = NULL;
1854+  GPtrArray *groups = NULL;
1855+  struct passwd *passwd;
1856+  char *seat_str = NULL;
1857+  char *session_str = NULL;
1858+
1859+  if (!duk_get_global_string (cx, "Subject")) {
1860+    return FALSE;
1861+  }
1862+
1863+  duk_new (cx, 0);
1864+
1865+  if (POLKIT_IS_UNIX_PROCESS (subject))
1866+    {
1867+      pid = polkit_unix_process_get_pid (POLKIT_UNIX_PROCESS (subject));
1868+    }
1869+  else if (POLKIT_IS_SYSTEM_BUS_NAME (subject))
1870+    {
1871+      PolkitSubject *process;
1872+      process = polkit_system_bus_name_get_process_sync (POLKIT_SYSTEM_BUS_NAME (subject), NULL, error);
1873+      if (process == NULL)
1874+        goto out;
1875+      pid = polkit_unix_process_get_pid (POLKIT_UNIX_PROCESS (process));
1876+      g_object_unref (process);
1877+    }
1878+  else
1879+    {
1880+      g_assert_not_reached ();
1881+    }
1882+
1883+#ifdef HAVE_LIBSYSTEMD
1884+  if (sd_pid_get_session (pid, &session_str) == 0)
1885+    {
1886+      if (sd_session_get_seat (session_str, &seat_str) == 0)
1887+        {
1888+          /* do nothing */
1889+        }
1890+    }
1891+#endif /* HAVE_LIBSYSTEMD */
1892+
1893+  g_assert (POLKIT_IS_UNIX_USER (user_for_subject));
1894+  uid = polkit_unix_user_get_uid (POLKIT_UNIX_USER (user_for_subject));
1895+
1896+  groups = g_ptr_array_new_with_free_func (g_free);
1897+
1898+  passwd = getpwuid (uid);
1899+  if (passwd == NULL)
1900+    {
1901+      user_name = g_strdup_printf ("%d", (gint) uid);
1902+      g_warning ("Error looking up info for uid %d: %m", (gint) uid);
1903+    }
1904+  else
1905+    {
1906+      gid_t gids[512];
1907+      int num_gids = 512;
1908+
1909+      user_name = g_strdup (passwd->pw_name);
1910+
1911+      if (getgrouplist (passwd->pw_name,
1912+                        passwd->pw_gid,
1913+                        gids,
1914+                        &num_gids) < 0)
1915+        {
1916+          g_warning ("Error looking up groups for uid %d: %m", (gint) uid);
1917+        }
1918+      else
1919+        {
1920+          gint n;
1921+          for (n = 0; n < num_gids; n++)
1922+            {
1923+              struct group *group;
1924+              group = getgrgid (gids[n]);
1925+              if (group == NULL)
1926+                {
1927+                  g_ptr_array_add (groups, g_strdup_printf ("%d", (gint) gids[n]));
1928+                }
1929+              else
1930+                {
1931+                  g_ptr_array_add (groups, g_strdup (group->gr_name));
1932+                }
1933+            }
1934+        }
1935+    }
1936+
1937+  set_property_int32 (cx, "pid", pid);
1938+  set_property_str (cx, "user", user_name);
1939+  set_property_strv (cx, "groups", groups);
1940+  set_property_str (cx, "seat", seat_str);
1941+  set_property_str (cx, "session", session_str);
1942+  set_property_bool (cx, "local", subject_is_local);
1943+  set_property_bool (cx, "active", subject_is_active);
1944+
1945+  ret = TRUE;
1946+
1947+ out:
1948+  free (session_str);
1949+  free (seat_str);
1950+  g_free (user_name);
1951+  if (groups != NULL)
1952+    g_ptr_array_unref (groups);
1953+
1954+  return ret;
1955+}
1956+
1957+/* ---------------------------------------------------------------------------------------------------- */
1958+
1959+static gboolean
1960+push_action_and_details (duk_context               *cx,
1961+                         const gchar               *action_id,
1962+                         PolkitDetails             *details,
1963+                         GError                   **error)
1964+{
1965+  gchar **keys;
1966+  guint n;
1967+
1968+  if (!duk_get_global_string (cx, "Action")) {
1969+    return FALSE;
1970+  }
1971+
1972+  duk_new (cx, 0);
1973+
1974+  set_property_str (cx, "id", action_id);
1975+
1976+  keys = polkit_details_get_keys (details);
1977+  for (n = 0; keys != NULL && keys[n] != NULL; n++)
1978+    {
1979+      gchar *key;
1980+      const gchar *value;
1981+      key = g_strdup_printf ("_detail_%s", keys[n]);
1982+      value = polkit_details_lookup (details, keys[n]);
1983+      set_property_str (cx, key, value);
1984+      g_free (key);
1985+    }
1986+  g_strfreev (keys);
1987+
1988+  return TRUE;
1989+}
1990+
1991+/* ---------------------------------------------------------------------------------------------------- */
1992+
1993+typedef struct {
1994+  PolkitBackendJsAuthority *authority;
1995+  const gchar *filename;
1996+  pthread_cond_t cond;
1997+  pthread_mutex_t mutex;
1998+  gint ret;
1999+} RunawayKillerCtx;
2000+
2001+static gpointer
2002+runaway_killer_thread_execute_js (gpointer user_data)
2003+{
2004+  RunawayKillerCtx *ctx = user_data;
2005+  duk_context *cx = ctx->authority->priv->cx;
2006+
2007+  int oldtype, pthread_err;
2008+
2009+  if ((pthread_err = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype))) {
2010+    polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (ctx->authority),
2011+                                  "Error setting thread cancel type: %s",
2012+                                  strerror(pthread_err));
2013+    goto err;
2014+  }
2015+
2016+  GFile *file = g_file_new_for_path(ctx->filename);
2017+  char *contents;
2018+  gsize len;
2019+
2020+  if (!g_file_load_contents(file, NULL, &contents, &len, NULL, NULL)) {
2021+    polkit_backend_authority_log(POLKIT_BACKEND_AUTHORITY(ctx->authority),
2022+                                 "Error loading script %s", ctx->filename);
2023+    g_object_unref(file);
2024+    goto err;
2025+  }
2026+
2027+  g_object_unref(file);
2028+
2029+  /* evaluate the script, trying to print context in any syntax errors
2030+     found */
2031+  if (duk_peval_lstring(cx, contents, len) != 0)
2032+  {
2033+    polkit_backend_authority_log(POLKIT_BACKEND_AUTHORITY(ctx->authority),
2034+                                 "Error compiling script %s: %s", ctx->filename,
2035+                                 duk_safe_to_string(cx, -1));
2036+    duk_pop(cx);
2037+    goto free_err;
2038+  }
2039+  g_free(contents);
2040+
2041+  ctx->ret = RUNAWAY_KILLER_THREAD_EXIT_STATUS_SUCCESS;
2042+  goto end;
2043+
2044+free_err:
2045+  g_free(contents);
2046+err:
2047+  ctx->ret = RUNAWAY_KILLER_THREAD_EXIT_STATUS_FAILURE;
2048+end:
2049+  if ((pthread_err = pthread_cond_signal(&ctx->cond))) {
2050+    polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (ctx->authority),
2051+                                  "Error signaling on condition variable: %s",
2052+                                  strerror(pthread_err));
2053+    ctx->ret = RUNAWAY_KILLER_THREAD_EXIT_STATUS_FAILURE;
2054+  }
2055+  return NULL;
2056+}
2057+
2058+static gpointer
2059+runaway_killer_thread_call_js (gpointer user_data)
2060+{
2061+  RunawayKillerCtx *ctx = user_data;
2062+  duk_context *cx = ctx->authority->priv->cx;
2063+  int oldtype, pthread_err;
2064+
2065+  if ((pthread_err = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype))) {
2066+    polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (ctx->authority),
2067+                                  "Error setting thread cancel type: %s",
2068+                                  strerror(pthread_err));
2069+    goto err;
2070+  }
2071+
2072+  if (duk_pcall_prop (cx, 0, 2) != DUK_EXEC_SUCCESS)
2073+    {
2074+      polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (ctx->authority),
2075+                                    "Error evaluating admin rules: ",
2076+                                    duk_safe_to_string (cx, -1));
2077+      goto err;
2078+    }
2079+
2080+  ctx->ret = RUNAWAY_KILLER_THREAD_EXIT_STATUS_SUCCESS;
2081+  goto end;
2082+
2083+err:
2084+  ctx->ret = RUNAWAY_KILLER_THREAD_EXIT_STATUS_FAILURE;
2085+end:
2086+  if ((pthread_err = pthread_cond_signal(&ctx->cond))) {
2087+    polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (ctx->authority),
2088+                                  "Error signaling on condition variable: %s",
2089+                                  strerror(pthread_err));
2090+    ctx->ret = RUNAWAY_KILLER_THREAD_EXIT_STATUS_FAILURE;
2091+  }
2092+  return NULL;
2093+}
2094+
2095+#if defined (HAVE_PTHREAD_CONDATTR_SETCLOCK)
2096+#  if defined(CLOCK_MONOTONIC)
2097+#    define PK_CLOCK CLOCK_MONOTONIC
2098+#  elif defined(CLOCK_BOOTTIME)
2099+#    define PK_CLOCK CLOCK_BOOTTIME
2100+#  else
2101+     /* No suitable clock */
2102+#    undef HAVE_PTHREAD_CONDATTR_SETCLOCK
2103+#    define PK_CLOCK CLOCK_REALTIME
2104+#  endif
2105+#else  /* ! HAVE_PTHREAD_CONDATTR_SETCLOCK */
2106+#  define PK_CLOCK CLOCK_REALTIME
2107+#endif /* ! HAVE_PTHREAD_CONDATTR_SETCLOCK */
2108+
2109+static gboolean
2110+runaway_killer_common(PolkitBackendJsAuthority *authority, RunawayKillerCtx *ctx, void *js_context_cb (void *user_data))
2111+{
2112+  int pthread_err;
2113+  gboolean cancel = FALSE;
2114+  pthread_condattr_t attr;
2115+  struct timespec abs_time;
2116+
2117+#ifdef HAVE_PTHREAD_CONDATTR_SETCLOCK
2118+  if ((pthread_err = pthread_condattr_init(&attr))) {
2119+    polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
2120+                                  "Error initializing condition variable attributes: %s",
2121+                                  strerror(pthread_err));
2122+    return FALSE;
2123+  }
2124+  if ((pthread_err = pthread_condattr_setclock(&attr, PK_CLOCK))) {
2125+    polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
2126+                                  "Error setting condition variable attributes: %s",
2127+                                  strerror(pthread_err));
2128+    goto err_clean_condattr;
2129+  }
2130+  /* Init again, with needed attr */
2131+  if ((pthread_err = pthread_cond_init(&ctx->cond, &attr))) {
2132+    polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
2133+                                  "Error initializing condition variable: %s",
2134+                                  strerror(pthread_err));
2135+    goto err_clean_condattr;
2136+  }
2137+#endif
2138+
2139+  if ((pthread_err = pthread_mutex_lock(&ctx->mutex))) {
2140+    polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
2141+                                  "Error locking mutex: %s",
2142+                                  strerror(pthread_err));
2143+    goto err_clean_cond;
2144+  }
2145+
2146+  if (clock_gettime(PK_CLOCK, &abs_time)) {
2147+    polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
2148+                                  "Error getting system's monotonic time: %s",
2149+                                  strerror(errno));
2150+    goto err_clean_cond;
2151+  }
2152+  abs_time.tv_sec += RUNAWAY_KILLER_TIMEOUT;
2153+
2154+  if ((pthread_err = pthread_create(&authority->priv->runaway_killer_thread, NULL,
2155+                                    js_context_cb, ctx))) {
2156+    polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
2157+                                  "Error creating runaway JS killer thread: %s",
2158+                                  strerror(pthread_err));
2159+    goto err_clean_cond;
2160+  }
2161+
2162+  while (ctx->ret == RUNAWAY_KILLER_THREAD_EXIT_STATUS_UNSET) /* loop to treat spurious wakeups */
2163+    if (pthread_cond_timedwait(&ctx->cond, &ctx->mutex, &abs_time) == ETIMEDOUT) {
2164+      cancel = TRUE;
2165+
2166+      /* Log that we are terminating the script */
2167+      polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
2168+                                    "Terminating runaway script after %d seconds",
2169+                                    RUNAWAY_KILLER_TIMEOUT);
2170+
2171+      break;
2172+    }
2173+
2174+  if ((pthread_err = pthread_mutex_unlock(&ctx->mutex))) {
2175+    polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
2176+                                  "Error unlocking mutex: %s",
2177+                                  strerror(pthread_err));
2178+    goto err_clean_cond;
2179+  }
2180+
2181+  if (cancel) {
2182+    if ((pthread_err = pthread_cancel (authority->priv->runaway_killer_thread))) {
2183+      polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
2184+                                    "Error cancelling runaway JS killer thread: %s",
2185+                                    strerror(pthread_err));
2186+      goto err_clean_cond;
2187+    }
2188+  }
2189+  if ((pthread_err = pthread_join (authority->priv->runaway_killer_thread, NULL))) {
2190+      polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
2191+                                    "Error joining runaway JS killer thread: %s",
2192+                                    strerror(pthread_err));
2193+      goto err_clean_cond;
2194+    }
2195+
2196+  return ctx->ret == RUNAWAY_KILLER_THREAD_EXIT_STATUS_SUCCESS;
2197+
2198+    err_clean_cond:
2199+#ifdef HAVE_PTHREAD_CONDATTR_SETCLOCK
2200+  pthread_cond_destroy(&ctx->cond);
2201+#endif
2202+    err_clean_condattr:
2203+#ifdef HAVE_PTHREAD_CONDATTR_SETCLOCK
2204+  pthread_condattr_destroy(&attr);
2205+#endif
2206+  return FALSE;
2207+}
2208+
2209+/* Blocking for at most RUNAWAY_KILLER_TIMEOUT */
2210+static gboolean
2211+execute_script_with_runaway_killer(PolkitBackendJsAuthority *authority,
2212+                                   const gchar *filename)
2213+{
2214+  RunawayKillerCtx ctx = {.authority = authority, .filename = filename,
2215+                          .ret = RUNAWAY_KILLER_THREAD_EXIT_STATUS_UNSET,
2216+                          .mutex = PTHREAD_MUTEX_INITIALIZER,
2217+                          .cond = PTHREAD_COND_INITIALIZER};
2218+
2219+  return runaway_killer_common(authority, &ctx, &runaway_killer_thread_execute_js);
2220+}
2221+
2222+/* Calls already stacked function and args. Blocking for at most
2223+ * RUNAWAY_KILLER_TIMEOUT. If timeout is the case, ctx.ret will be
2224+ * RUNAWAY_KILLER_THREAD_EXIT_STATUS_UNSET, thus returning FALSE.
2225+ */
2226+static gboolean
2227+call_js_function_with_runaway_killer(PolkitBackendJsAuthority *authority)
2228+{
2229+  RunawayKillerCtx ctx = {.authority = authority,
2230+                          .ret = RUNAWAY_KILLER_THREAD_EXIT_STATUS_UNSET,
2231+                          .mutex = PTHREAD_MUTEX_INITIALIZER,
2232+                          .cond = PTHREAD_COND_INITIALIZER};
2233+
2234+  return runaway_killer_common(authority, &ctx, &runaway_killer_thread_call_js);
2235+}
2236+
2237+/* ---------------------------------------------------------------------------------------------------- */
2238+
2239+GList *
2240+polkit_backend_common_js_authority_get_admin_auth_identities (PolkitBackendInteractiveAuthority *_authority,
2241+                                                              PolkitSubject                     *caller,
2242+                                                              PolkitSubject                     *subject,
2243+                                                              PolkitIdentity                    *user_for_subject,
2244+                                                              gboolean                           subject_is_local,
2245+                                                              gboolean                           subject_is_active,
2246+                                                              const gchar                       *action_id,
2247+                                                              PolkitDetails                     *details)
2248+{
2249+  PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (_authority);
2250+  GList *ret = NULL;
2251+  guint n;
2252+  GError *error = NULL;
2253+  const char *ret_str = NULL;
2254+  gchar **ret_strs = NULL;
2255+  duk_context *cx = authority->priv->cx;
2256+
2257+  duk_set_top (cx, 0);
2258+  if (!duk_get_global_string (cx, "polkit")) {
2259+      polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
2260+                                    "Error deleting old rules, not loading new ones");
2261+      goto out;
2262+  }
2263+
2264+  duk_push_string (cx, "_runAdminRules");
2265+
2266+  if (!push_action_and_details (cx, action_id, details, &error))
2267+    {
2268+      polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
2269+                                    "Error converting action and details to JS object: %s",
2270+                                    error->message);
2271+      g_clear_error (&error);
2272+      goto out;
2273+    }
2274+
2275+  if (!push_subject (cx, subject, user_for_subject, subject_is_local, subject_is_active, &error))
2276+    {
2277+      polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
2278+                                    "Error converting subject to JS object: %s",
2279+                                    error->message);
2280+      g_clear_error (&error);
2281+      goto out;
2282+    }
2283+
2284+  if (!call_js_function_with_runaway_killer (authority))
2285+    goto out;
2286+
2287+  ret_str = duk_require_string (cx, -1);
2288+
2289+  ret_strs = g_strsplit (ret_str, ",", -1);
2290+  for (n = 0; ret_strs != NULL && ret_strs[n] != NULL; n++)
2291+    {
2292+      const gchar *identity_str = ret_strs[n];
2293+      PolkitIdentity *identity;
2294+
2295+      error = NULL;
2296+      identity = polkit_identity_from_string (identity_str, &error);
2297+      if (identity == NULL)
2298+        {
2299+          polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
2300+                                        "Identity `%s' is not valid, ignoring: %s",
2301+                                        identity_str, error->message);
2302+          g_clear_error (&error);
2303+        }
2304+      else
2305+        {
2306+          ret = g_list_prepend (ret, identity);
2307+        }
2308+    }
2309+  ret = g_list_reverse (ret);
2310+
2311+ out:
2312+  g_strfreev (ret_strs);
2313+  /* fallback to root password auth */
2314+  if (ret == NULL)
2315+    ret = g_list_prepend (ret, polkit_unix_user_new (0));
2316+
2317+  return ret;
2318+}
2319+
2320+/* ---------------------------------------------------------------------------------------------------- */
2321+
2322+PolkitImplicitAuthorization
2323+polkit_backend_common_js_authority_check_authorization_sync (PolkitBackendInteractiveAuthority *_authority,
2324+                                                             PolkitSubject                     *caller,
2325+                                                             PolkitSubject                     *subject,
2326+                                                             PolkitIdentity                    *user_for_subject,
2327+                                                             gboolean                           subject_is_local,
2328+                                                             gboolean                           subject_is_active,
2329+                                                             const gchar                       *action_id,
2330+                                                             PolkitDetails                     *details,
2331+                                                             PolkitImplicitAuthorization        implicit)
2332+{
2333+  PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (_authority);
2334+  PolkitImplicitAuthorization ret = implicit;
2335+  GError *error = NULL;
2336+  gchar *ret_str = NULL;
2337+  gboolean good = FALSE;
2338+  duk_context *cx = authority->priv->cx;
2339+
2340+  duk_set_top (cx, 0);
2341+  if (!duk_get_global_string (cx, "polkit")) {
2342+      goto out;
2343+  }
2344+
2345+  duk_push_string (cx, "_runRules");
2346+
2347+  if (!push_action_and_details (cx, action_id, details, &error))
2348+    {
2349+      polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
2350+                                    "Error converting action and details to JS object: %s",
2351+                                    error->message);
2352+      g_clear_error (&error);
2353+      goto out;
2354+    }
2355+
2356+  if (!push_subject (cx, subject, user_for_subject, subject_is_local, subject_is_active, &error))
2357+    {
2358+      polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
2359+                                    "Error converting subject to JS object: %s",
2360+                                    error->message);
2361+      g_clear_error (&error);
2362+      goto out;
2363+    }
2364+
2365+  // If any error is the js context happened (ctx.ret ==
2366+  // RUNAWAY_KILLER_THREAD_EXIT_STATUS_FAILURE) or it never properly returned
2367+  // (runaway scripts or ctx.ret == RUNAWAY_KILLER_THREAD_EXIT_STATUS_UNSET),
2368+  // unauthorize
2369+  if (!call_js_function_with_runaway_killer (authority))
2370+    goto out;
2371+
2372+  if (duk_is_null(cx, -1)) {
2373+    /* this is fine, means there was no match, use implicit authorizations */
2374+    good = TRUE;
2375+    goto out;
2376+  }
2377+  ret_str = g_strdup (duk_require_string (cx, -1));
2378+  if (!polkit_implicit_authorization_from_string (ret_str, &ret))
2379+    {
2380+      polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
2381+                                    "Returned result `%s' is not valid",
2382+                                    ret_str);
2383+      goto out;
2384+    }
2385+
2386+  good = TRUE;
2387+
2388+ out:
2389+  if (!good)
2390+    ret = POLKIT_IMPLICIT_AUTHORIZATION_NOT_AUTHORIZED;
2391+  if (ret_str != NULL)
2392+      g_free (ret_str);
2393+
2394+  return ret;
2395+}
2396+
2397+/* ---------------------------------------------------------------------------------------------------- */
2398+
2399+static duk_ret_t
2400+js_polkit_log (duk_context *cx)
2401+{
2402+  const char *str = duk_require_string (cx, 0);
2403+  fprintf (stderr, "%s\n", str);
2404+  return 0;
2405+}
2406+
2407+/* ---------------------------------------------------------------------------------------------------- */
2408+
2409+static duk_ret_t
2410+js_polkit_spawn (duk_context *cx)
2411+{
2412+  duk_ret_t ret = DUK_RET_ERROR;
2413+  gchar *standard_output = NULL;
2414+  gchar *standard_error = NULL;
2415+  gint exit_status;
2416+  GError *error = NULL;
2417+  guint32 array_len;
2418+  gchar **argv = NULL;
2419+  GMainContext *context = NULL;
2420+  GMainLoop *loop = NULL;
2421+  SpawnData data = {0};
2422+  char *err_str = NULL;
2423+  guint n;
2424+
2425+  if (!duk_is_array (cx, 0))
2426+    goto out;
2427+
2428+  array_len = duk_get_length (cx, 0);
2429+
2430+  argv = g_new0 (gchar*, array_len + 1);
2431+  for (n = 0; n < array_len; n++)
2432+    {
2433+      duk_get_prop_index (cx, 0, n);
2434+      argv[n] = g_strdup (duk_to_string (cx, -1));
2435+      duk_pop (cx);
2436+    }
2437+
2438+  context = g_main_context_new ();
2439+  loop = g_main_loop_new (context, FALSE);
2440+
2441+  g_main_context_push_thread_default (context);
2442+
2443+  data.loop = loop;
2444+  polkit_backend_common_spawn ((const gchar *const *) argv,
2445+                               10, /* timeout_seconds */
2446+                               NULL, /* cancellable */
2447+                               polkit_backend_common_spawn_cb,
2448+                               &data);
2449+
2450+  g_main_loop_run (loop);
2451+
2452+  g_main_context_pop_thread_default (context);
2453+
2454+  if (!polkit_backend_common_spawn_finish (data.res,
2455+                                           &exit_status,
2456+                                           &standard_output,
2457+                                           &standard_error,
2458+                                           &error))
2459+    {
2460+      err_str = g_strdup_printf ("Error spawning helper: %s (%s, %d)",
2461+                                 error->message, g_quark_to_string (error->domain), error->code);
2462+      g_clear_error (&error);
2463+      goto out;
2464+    }
2465+
2466+  if (!(WIFEXITED (exit_status) && WEXITSTATUS (exit_status) == 0))
2467+    {
2468+      GString *gstr;
2469+      gstr = g_string_new (NULL);
2470+      if (WIFEXITED (exit_status))
2471+        {
2472+          g_string_append_printf (gstr,
2473+                                  "Helper exited with non-zero exit status %d",
2474+                                  WEXITSTATUS (exit_status));
2475+        }
2476+      else if (WIFSIGNALED (exit_status))
2477+        {
2478+          g_string_append_printf (gstr,
2479+                                  "Helper was signaled with signal %s (%d)",
2480+                                  polkit_backend_common_get_signal_name (WTERMSIG (exit_status)),
2481+                                  WTERMSIG (exit_status));
2482+        }
2483+      g_string_append_printf (gstr, ", stdout=`%s', stderr=`%s'",
2484+                              standard_output, standard_error);
2485+      err_str = g_string_free (gstr, FALSE);
2486+      goto out;
2487+    }
2488+
2489+  duk_push_string (cx, standard_output);
2490+  ret = 1;
2491+
2492+ out:
2493+  g_strfreev (argv);
2494+  g_free (standard_output);
2495+  g_free (standard_error);
2496+  g_clear_object (&data.res);
2497+  if (loop != NULL)
2498+    g_main_loop_unref (loop);
2499+  if (context != NULL)
2500+    g_main_context_unref (context);
2501+
2502+  if (err_str)
2503+    duk_error (cx, DUK_ERR_ERROR, err_str);
2504+
2505+  return ret;
2506+}
2507+
2508+/* ---------------------------------------------------------------------------------------------------- */
2509+
2510+
2511+static duk_ret_t
2512+js_polkit_user_is_in_netgroup (duk_context *cx)
2513+{
2514+  const char *user;
2515+  const char *netgroup;
2516+  gboolean is_in_netgroup = FALSE;
2517+
2518+  user = duk_require_string (cx, 0);
2519+  netgroup = duk_require_string (cx, 1);
2520+
2521+  if (innetgr (netgroup,
2522+               NULL,  /* host */
2523+               user,
2524+               NULL)) /* domain */
2525+    {
2526+      is_in_netgroup = TRUE;
2527+    }
2528+
2529+  duk_push_boolean (cx, is_in_netgroup);
2530+  return 1;
2531+}
2532+
2533+/* ---------------------------------------------------------------------------------------------------- */
2534diff --git a/src/polkitbackend/polkitbackendjsauthority.cpp b/src/polkitbackend/polkitbackendjsauthority.cpp
2535index ca17108..11e91c0 100644
2536--- a/src/polkitbackend/polkitbackendjsauthority.cpp
2537+++ b/src/polkitbackend/polkitbackendjsauthority.cpp
2538@@ -19,29 +19,7 @@
2539  * Author: David Zeuthen <davidz@redhat.com>
2540  */
2541
2542-#include "config.h"
2543-#include <sys/wait.h>
2544-#include <errno.h>
2545-#include <pwd.h>
2546-#include <grp.h>
2547-#ifdef HAVE_NETGROUP_H
2548-#include <netgroup.h>
2549-#else
2550-#include <netdb.h>
2551-#endif
2552-#include <string.h>
2553-#include <glib/gstdio.h>
2554-#include <locale.h>
2555-#include <glib/gi18n-lib.h>
2556-
2557-#include <polkit/polkit.h>
2558-#include "polkitbackendjsauthority.h"
2559-
2560-#include <polkit/polkitprivate.h>
2561-
2562-#ifdef HAVE_LIBSYSTEMD
2563-#include <systemd/sd-login.h>
2564-#endif /* HAVE_LIBSYSTEMD */
2565+#include "polkitbackendcommon.h"
2566
2567 #include <js/CompilationAndEvaluation.h>
2568 #include <js/ContextOptions.h>
2569@@ -52,6 +30,7 @@
2570 #include <js/Array.h>
2571 #include <jsapi.h>
2572
2573+/* Built source and not too big to worry about deduplication */
2574 #include "initjs.h" /* init.js */
2575
2576 #ifdef JSGC_USE_EXACT_ROOTING
2577@@ -67,10 +46,9 @@
2578  * @short_description: JS Authority
2579  * @stability: Unstable
2580  *
2581- * An implementation of #PolkitBackendAuthority that reads and
2582- * evalates Javascript files and supports interaction with
2583- * authentication agents (virtue of being based on
2584- * #PolkitBackendInteractiveAuthority).
2585+ * An (SpiderMonkey-based) implementation of #PolkitBackendAuthority that reads
2586+ * and evaluates Javascript files and supports interaction with authentication
2587+ * agents (virtue of being based on #PolkitBackendInteractiveAuthority).
2588  */
2589
2590 /* ---------------------------------------------------------------------------------------------------- */
2591@@ -100,57 +78,11 @@ static bool execute_script_with_runaway_killer (PolkitBackendJsAuthority *author
2592                                     JS::HandleScript                 script,
2593                                     JS::MutableHandleValue           rval);
2594
2595-static void utils_spawn (const gchar *const  *argv,
2596-                         guint                timeout_seconds,
2597-                         GCancellable        *cancellable,
2598-                         GAsyncReadyCallback  callback,
2599-                         gpointer             user_data);
2600-
2601-gboolean utils_spawn_finish (GAsyncResult   *res,
2602-                             gint           *out_exit_status,
2603-                             gchar         **out_standard_output,
2604-                             gchar         **out_standard_error,
2605-                             GError        **error);
2606-
2607-static void on_dir_monitor_changed (GFileMonitor     *monitor,
2608-                                    GFile            *file,
2609-                                    GFile            *other_file,
2610-                                    GFileMonitorEvent event_type,
2611-                                    gpointer          user_data);
2612-
2613-/* ---------------------------------------------------------------------------------------------------- */
2614-
2615-enum
2616-{
2617-  PROP_0,
2618-  PROP_RULES_DIRS,
2619-};
2620-
2621 /* ---------------------------------------------------------------------------------------------------- */
2622
2623 static gpointer runaway_killer_thread_func (gpointer user_data);
2624 static void runaway_killer_terminate (PolkitBackendJsAuthority *authority);
2625
2626-static GList *polkit_backend_js_authority_get_admin_auth_identities (PolkitBackendInteractiveAuthority *authority,
2627-                                                                     PolkitSubject                     *caller,
2628-                                                                     PolkitSubject                     *subject,
2629-                                                                     PolkitIdentity                    *user_for_subject,
2630-                                                                     gboolean                           subject_is_local,
2631-                                                                     gboolean                           subject_is_active,
2632-                                                                     const gchar                       *action_id,
2633-                                                                     PolkitDetails                     *details);
2634-
2635-static PolkitImplicitAuthorization polkit_backend_js_authority_check_authorization_sync (
2636-                                                          PolkitBackendInteractiveAuthority *authority,
2637-                                                          PolkitSubject                     *caller,
2638-                                                          PolkitSubject                     *subject,
2639-                                                          PolkitIdentity                    *user_for_subject,
2640-                                                          gboolean                           subject_is_local,
2641-                                                          gboolean                           subject_is_active,
2642-                                                          const gchar                       *action_id,
2643-                                                          PolkitDetails                     *details,
2644-                                                          PolkitImplicitAuthorization        implicit);
2645-
2646 G_DEFINE_TYPE (PolkitBackendJsAuthority, polkit_backend_js_authority, POLKIT_BACKEND_TYPE_INTERACTIVE_AUTHORITY);
2647
2648 /* ---------------------------------------------------------------------------------------------------- */
2649@@ -229,33 +161,6 @@ polkit_backend_js_authority_init (PolkitBackendJsAuthority *authority)
2650                                                  PolkitBackendJsAuthorityPrivate);
2651 }
2652
2653-static gint
2654-rules_file_name_cmp (const gchar *a,
2655-                     const gchar *b)
2656-{
2657-  gint ret;
2658-  const gchar *a_base;
2659-  const gchar *b_base;
2660-
2661-  a_base = strrchr (a, '/');
2662-  b_base = strrchr (b, '/');
2663-
2664-  g_assert (a_base != NULL);
2665-  g_assert (b_base != NULL);
2666-  a_base += 1;
2667-  b_base += 1;
2668-
2669-  ret = g_strcmp0 (a_base, b_base);
2670-  if (ret == 0)
2671-    {
2672-      /* /etc wins over /usr */
2673-      ret = g_strcmp0 (a, b);
2674-      g_assert (ret != 0);
2675-    }
2676-
2677-  return ret;
2678-}
2679-
2680 /* authority->priv->cx must be within a request */
2681 static void
2682 load_scripts (PolkitBackendJsAuthority  *authority)
2683@@ -299,7 +204,7 @@ load_scripts (PolkitBackendJsAuthority  *authority)
2684         }
2685     }
2686
2687-  files = g_list_sort (files, (GCompareFunc) rules_file_name_cmp);
2688+  files = g_list_sort (files, (GCompareFunc) polkit_backend_common_rules_file_name_cmp);
2689
2690   for (l = files; l != NULL; l = l->next)
2691     {
2692@@ -365,8 +270,8 @@ load_scripts (PolkitBackendJsAuthority  *authority)
2693   g_list_free_full (files, g_free);
2694 }
2695
2696-static void
2697-reload_scripts (PolkitBackendJsAuthority *authority)
2698+void
2699+polkit_backend_common_reload_scripts (PolkitBackendJsAuthority *authority)
2700 {
2701   JS::RootedValueArray<1> args(authority->priv->cx);
2702   JS::RootedValue rval(authority->priv->cx);
2703@@ -395,42 +300,6 @@ reload_scripts (PolkitBackendJsAuthority *authority)
2704   g_signal_emit_by_name (authority, "changed");
2705 }
2706
2707-static void
2708-on_dir_monitor_changed (GFileMonitor     *monitor,
2709-                        GFile            *file,
2710-                        GFile            *other_file,
2711-                        GFileMonitorEvent event_type,
2712-                        gpointer          user_data)
2713-{
2714-  PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (user_data);
2715-
2716-  /* TODO: maybe rate-limit so storms of events are collapsed into one with a 500ms resolution?
2717-   *       Because when editing a file with emacs we get 4-8 events..
2718-   */
2719-
2720-  if (file != NULL)
2721-    {
2722-      gchar *name;
2723-
2724-      name = g_file_get_basename (file);
2725-
2726-      /* g_print ("event_type=%d file=%p name=%s\n", event_type, file, name); */
2727-      if (!g_str_has_prefix (name, ".") &&
2728-          !g_str_has_prefix (name, "#") &&
2729-          g_str_has_suffix (name, ".rules") &&
2730-          (event_type == G_FILE_MONITOR_EVENT_CREATED ||
2731-           event_type == G_FILE_MONITOR_EVENT_DELETED ||
2732-           event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT))
2733-        {
2734-          polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
2735-                                        "Reloading rules");
2736-          reload_scripts (authority);
2737-        }
2738-      g_free (name);
2739-    }
2740-}
2741-
2742-
2743 static void
2744 setup_file_monitors (PolkitBackendJsAuthority *authority)
2745 {
2746@@ -462,7 +331,7 @@ setup_file_monitors (PolkitBackendJsAuthority *authority)
2747         {
2748           g_signal_connect (monitor,
2749                             "changed",
2750-                            G_CALLBACK (on_dir_monitor_changed),
2751+                            G_CALLBACK (polkit_backend_common_on_dir_monitor_changed),
2752                             authority);
2753           g_ptr_array_add (p, monitor);
2754         }
2755@@ -471,8 +340,8 @@ setup_file_monitors (PolkitBackendJsAuthority *authority)
2756   authority->priv->dir_monitors = (GFileMonitor**) g_ptr_array_free (p, FALSE);
2757 }
2758
2759-static void
2760-polkit_backend_js_authority_constructed (GObject *object)
2761+void
2762+polkit_backend_common_js_authority_constructed (GObject *object)
2763 {
2764   PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (object);
2765
2766@@ -561,8 +430,8 @@ polkit_backend_js_authority_constructed (GObject *object)
2767   g_assert_not_reached ();
2768 }
2769
2770-static void
2771-polkit_backend_js_authority_finalize (GObject *object)
2772+void
2773+polkit_backend_common_js_authority_finalize (GObject *object)
2774 {
2775   PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (object);
2776   guint n;
2777@@ -577,7 +446,7 @@ polkit_backend_js_authority_finalize (GObject *object)
2778     {
2779       GFileMonitor *monitor = authority->priv->dir_monitors[n];
2780       g_signal_handlers_disconnect_by_func (monitor,
2781-                                            (gpointer*)G_CALLBACK (on_dir_monitor_changed),
2782+                                            (gpointer*)G_CALLBACK (polkit_backend_common_on_dir_monitor_changed),
2783                                             authority);
2784       g_object_unref (monitor);
2785     }
2786@@ -594,11 +463,11 @@ polkit_backend_js_authority_finalize (GObject *object)
2787   G_OBJECT_CLASS (polkit_backend_js_authority_parent_class)->finalize (object);
2788 }
2789
2790-static void
2791-polkit_backend_js_authority_set_property (GObject      *object,
2792-                                          guint         property_id,
2793-                                          const GValue *value,
2794-                                          GParamSpec   *pspec)
2795+void
2796+polkit_backend_common_js_authority_set_property (GObject      *object,
2797+                                                 guint         property_id,
2798+                                                 const GValue *value,
2799+                                                 GParamSpec   *pspec)
2800 {
2801   PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (object);
2802
2803@@ -615,57 +484,12 @@ polkit_backend_js_authority_set_property (GObject      *object,
2804     }
2805 }
2806
2807-static const gchar *
2808-polkit_backend_js_authority_get_name (PolkitBackendAuthority *authority)
2809-{
2810-  return "js";
2811-}
2812-
2813-static const gchar *
2814-polkit_backend_js_authority_get_version (PolkitBackendAuthority *authority)
2815-{
2816-  return PACKAGE_VERSION;
2817-}
2818-
2819-static PolkitAuthorityFeatures
2820-polkit_backend_js_authority_get_features (PolkitBackendAuthority *authority)
2821-{
2822-  return POLKIT_AUTHORITY_FEATURES_TEMPORARY_AUTHORIZATION;
2823-}
2824-
2825 static void
2826 polkit_backend_js_authority_class_init (PolkitBackendJsAuthorityClass *klass)
2827 {
2828-  GObjectClass *gobject_class;
2829-  PolkitBackendAuthorityClass *authority_class;
2830-  PolkitBackendInteractiveAuthorityClass *interactive_authority_class;
2831-
2832-
2833-  gobject_class = G_OBJECT_CLASS (klass);
2834-  gobject_class->finalize                               = polkit_backend_js_authority_finalize;
2835-  gobject_class->set_property                           = polkit_backend_js_authority_set_property;
2836-  gobject_class->constructed                            = polkit_backend_js_authority_constructed;
2837-
2838-  authority_class = POLKIT_BACKEND_AUTHORITY_CLASS (klass);
2839-  authority_class->get_name                             = polkit_backend_js_authority_get_name;
2840-  authority_class->get_version                          = polkit_backend_js_authority_get_version;
2841-  authority_class->get_features                         = polkit_backend_js_authority_get_features;
2842-
2843-  interactive_authority_class = POLKIT_BACKEND_INTERACTIVE_AUTHORITY_CLASS (klass);
2844-  interactive_authority_class->get_admin_identities     = polkit_backend_js_authority_get_admin_auth_identities;
2845-  interactive_authority_class->check_authorization_sync = polkit_backend_js_authority_check_authorization_sync;
2846-
2847-  g_object_class_install_property (gobject_class,
2848-                                   PROP_RULES_DIRS,
2849-                                   g_param_spec_boxed ("rules-dirs",
2850-                                                       NULL,
2851-                                                       NULL,
2852-                                                       G_TYPE_STRV,
2853-                                                       GParamFlags(G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE)));
2854-
2855+  polkit_backend_common_js_authority_class_init_common (klass);
2856
2857   g_type_class_add_private (klass, sizeof (PolkitBackendJsAuthorityPrivate));
2858-
2859   JS_Init ();
2860 }
2861
2862@@ -1005,11 +829,14 @@ runaway_killer_setup (PolkitBackendJsAuthority *authority)
2863 {
2864   g_assert (authority->priv->rkt_source == NULL);
2865
2866-  /* set-up timer for runaway scripts, will be executed in runaway_killer_thread */
2867+  /* set-up timer for runaway scripts, will be executed in
2868+     runaway_killer_thread, that is one, permanent thread running a glib
2869+     mainloop (rkt_loop) whose context (rkt_context) has a timeout source
2870+     (rkt_source) */
2871   g_mutex_lock (&authority->priv->rkt_timeout_pending_mutex);
2872   authority->priv->rkt_timeout_pending = FALSE;
2873   g_mutex_unlock (&authority->priv->rkt_timeout_pending_mutex);
2874-  authority->priv->rkt_source = g_timeout_source_new_seconds (15);
2875+  authority->priv->rkt_source = g_timeout_source_new_seconds (RUNAWAY_KILLER_TIMEOUT);
2876   g_source_set_callback (authority->priv->rkt_source, rkt_on_timeout, authority, NULL);
2877   g_source_attach (authority->priv->rkt_source, authority->priv->rkt_context);
2878
2879@@ -1069,6 +896,9 @@ execute_script_with_runaway_killer (PolkitBackendJsAuthority *authority,
2880 {
2881   bool ret;
2882
2883+  // tries to JS_ExecuteScript(), may hang for > RUNAWAY_KILLER_TIMEOUT,
2884+  // runaway_killer_thread makes sure the call returns, due to exception
2885+  // injection
2886   runaway_killer_setup (authority);
2887   ret = JS_ExecuteScript (authority->priv->cx,
2888                           script,
2889@@ -1099,15 +929,15 @@ call_js_function_with_runaway_killer (PolkitBackendJsAuthority *authority,
2890
2891 /* ---------------------------------------------------------------------------------------------------- */
2892
2893-static GList *
2894-polkit_backend_js_authority_get_admin_auth_identities (PolkitBackendInteractiveAuthority *_authority,
2895-                                                       PolkitSubject                     *caller,
2896-                                                       PolkitSubject                     *subject,
2897-                                                       PolkitIdentity                    *user_for_subject,
2898-                                                       gboolean                           subject_is_local,
2899-                                                       gboolean                           subject_is_active,
2900-                                                       const gchar                       *action_id,
2901-                                                       PolkitDetails                     *details)
2902+GList *
2903+polkit_backend_common_js_authority_get_admin_auth_identities (PolkitBackendInteractiveAuthority *_authority,
2904+                                                              PolkitSubject                     *caller,
2905+                                                              PolkitSubject                     *subject,
2906+                                                              PolkitIdentity                    *user_for_subject,
2907+                                                              gboolean                           subject_is_local,
2908+                                                              gboolean                           subject_is_active,
2909+                                                              const gchar                       *action_id,
2910+                                                              PolkitDetails                     *details)
2911 {
2912   PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (_authority);
2913   GList *ret = NULL;
2914@@ -1202,16 +1032,16 @@ polkit_backend_js_authority_get_admin_auth_identities (PolkitBackendInteractiveA
2915
2916 /* ---------------------------------------------------------------------------------------------------- */
2917
2918-static PolkitImplicitAuthorization
2919-polkit_backend_js_authority_check_authorization_sync (PolkitBackendInteractiveAuthority *_authority,
2920-                                                      PolkitSubject                     *caller,
2921-                                                      PolkitSubject                     *subject,
2922-                                                      PolkitIdentity                    *user_for_subject,
2923-                                                      gboolean                           subject_is_local,
2924-                                                      gboolean                           subject_is_active,
2925-                                                      const gchar                       *action_id,
2926-                                                      PolkitDetails                     *details,
2927-                                                      PolkitImplicitAuthorization        implicit)
2928+PolkitImplicitAuthorization
2929+polkit_backend_common_js_authority_check_authorization_sync (PolkitBackendInteractiveAuthority *_authority,
2930+                                                             PolkitSubject                     *caller,
2931+                                                             PolkitSubject                     *subject,
2932+                                                             PolkitIdentity                    *user_for_subject,
2933+                                                             gboolean                           subject_is_local,
2934+                                                             gboolean                           subject_is_active,
2935+                                                             const gchar                       *action_id,
2936+                                                             PolkitDetails                     *details,
2937+                                                             PolkitImplicitAuthorization        implicit)
2938 {
2939   PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (_authority);
2940   PolkitImplicitAuthorization ret = implicit;
2941@@ -1324,65 +1154,6 @@ js_polkit_log (JSContext  *cx,
2942
2943 /* ---------------------------------------------------------------------------------------------------- */
2944
2945-static const gchar *
2946-get_signal_name (gint signal_number)
2947-{
2948-  switch (signal_number)
2949-    {
2950-#define _HANDLE_SIG(sig) case sig: return #sig;
2951-    _HANDLE_SIG (SIGHUP);
2952-    _HANDLE_SIG (SIGINT);
2953-    _HANDLE_SIG (SIGQUIT);
2954-    _HANDLE_SIG (SIGILL);
2955-    _HANDLE_SIG (SIGABRT);
2956-    _HANDLE_SIG (SIGFPE);
2957-    _HANDLE_SIG (SIGKILL);
2958-    _HANDLE_SIG (SIGSEGV);
2959-    _HANDLE_SIG (SIGPIPE);
2960-    _HANDLE_SIG (SIGALRM);
2961-    _HANDLE_SIG (SIGTERM);
2962-    _HANDLE_SIG (SIGUSR1);
2963-    _HANDLE_SIG (SIGUSR2);
2964-    _HANDLE_SIG (SIGCHLD);
2965-    _HANDLE_SIG (SIGCONT);
2966-    _HANDLE_SIG (SIGSTOP);
2967-    _HANDLE_SIG (SIGTSTP);
2968-    _HANDLE_SIG (SIGTTIN);
2969-    _HANDLE_SIG (SIGTTOU);
2970-    _HANDLE_SIG (SIGBUS);
2971-#ifdef SIGPOLL
2972-    _HANDLE_SIG (SIGPOLL);
2973-#endif
2974-    _HANDLE_SIG (SIGPROF);
2975-    _HANDLE_SIG (SIGSYS);
2976-    _HANDLE_SIG (SIGTRAP);
2977-    _HANDLE_SIG (SIGURG);
2978-    _HANDLE_SIG (SIGVTALRM);
2979-    _HANDLE_SIG (SIGXCPU);
2980-    _HANDLE_SIG (SIGXFSZ);
2981-#undef _HANDLE_SIG
2982-    default:
2983-      break;
2984-    }
2985-  return "UNKNOWN_SIGNAL";
2986-}
2987-
2988-typedef struct
2989-{
2990-  GMainLoop *loop;
2991-  GAsyncResult *res;
2992-} SpawnData;
2993-
2994-static void
2995-spawn_cb (GObject       *source_object,
2996-          GAsyncResult  *res,
2997-          gpointer       user_data)
2998-{
2999-  SpawnData *data = (SpawnData *)user_data;
3000-  data->res = (GAsyncResult*)g_object_ref (res);
3001-  g_main_loop_quit (data->loop);
3002-}
3003-
3004 static bool
3005 js_polkit_spawn (JSContext  *cx,
3006                  unsigned    js_argc,
3007@@ -1440,21 +1211,21 @@ js_polkit_spawn (JSContext  *cx,
3008   g_main_context_push_thread_default (context);
3009
3010   data.loop = loop;
3011-  utils_spawn ((const gchar *const *) argv,
3012-               10, /* timeout_seconds */
3013-               NULL, /* cancellable */
3014-               spawn_cb,
3015-               &data);
3016+  polkit_backend_common_spawn ((const gchar *const *) argv,
3017+                               10, /* timeout_seconds */
3018+                               NULL, /* cancellable */
3019+                               polkit_backend_common_spawn_cb,
3020+                               &data);
3021
3022   g_main_loop_run (loop);
3023
3024   g_main_context_pop_thread_default (context);
3025
3026-  if (!utils_spawn_finish (data.res,
3027-                           &exit_status,
3028-                           &standard_output,
3029-                           &standard_error,
3030-                           &error))
3031+  if (!polkit_backend_common_spawn_finish (data.res,
3032+                                           &exit_status,
3033+                                           &standard_output,
3034+                                           &standard_error,
3035+                                           &error))
3036     {
3037       JS_ReportErrorUTF8 (cx,
3038                       "Error spawning helper: %s (%s, %d)",
3039@@ -1477,7 +1248,7 @@ js_polkit_spawn (JSContext  *cx,
3040         {
3041           g_string_append_printf (gstr,
3042                                   "Helper was signaled with signal %s (%d)",
3043-                                  get_signal_name (WTERMSIG (exit_status)),
3044+                                  polkit_backend_common_get_signal_name (WTERMSIG (exit_status)),
3045                                   WTERMSIG (exit_status));
3046         }
3047       g_string_append_printf (gstr, ", stdout=`%s', stderr=`%s'",
3048@@ -1542,381 +1313,5 @@ js_polkit_user_is_in_netgroup (JSContext  *cx,
3049   return ret;
3050 }
3051
3052-
3053-
3054 /* ---------------------------------------------------------------------------------------------------- */
3055
3056-typedef struct
3057-{
3058-  GSimpleAsyncResult *simple; /* borrowed reference */
3059-  GMainContext *main_context; /* may be NULL */
3060-
3061-  GCancellable *cancellable;  /* may be NULL */
3062-  gulong cancellable_handler_id;
3063-
3064-  GPid child_pid;
3065-  gint child_stdout_fd;
3066-  gint child_stderr_fd;
3067-
3068-  GIOChannel *child_stdout_channel;
3069-  GIOChannel *child_stderr_channel;
3070-
3071-  GSource *child_watch_source;
3072-  GSource *child_stdout_source;
3073-  GSource *child_stderr_source;
3074-
3075-  guint timeout_seconds;
3076-  gboolean timed_out;
3077-  GSource *timeout_source;
3078-
3079-  GString *child_stdout;
3080-  GString *child_stderr;
3081-
3082-  gint exit_status;
3083-} UtilsSpawnData;
3084-
3085-static void
3086-utils_child_watch_from_release_cb (GPid     pid,
3087-                                   gint     status,
3088-                                   gpointer user_data)
3089-{
3090-}
3091-
3092-static void
3093-utils_spawn_data_free (UtilsSpawnData *data)
3094-{
3095-  if (data->timeout_source != NULL)
3096-    {
3097-      g_source_destroy (data->timeout_source);
3098-      data->timeout_source = NULL;
3099-    }
3100-
3101-  /* Nuke the child, if necessary */
3102-  if (data->child_watch_source != NULL)
3103-    {
3104-      g_source_destroy (data->child_watch_source);
3105-      data->child_watch_source = NULL;
3106-    }
3107-
3108-  if (data->child_pid != 0)
3109-    {
3110-      GSource *source;
3111-      kill (data->child_pid, SIGTERM);
3112-      /* OK, we need to reap for the child ourselves - we don't want
3113-       * to use waitpid() because that might block the calling
3114-       * thread (the child might handle SIGTERM and use several
3115-       * seconds for cleanup/rollback).
3116-       *
3117-       * So we use GChildWatch instead.
3118-       *
3119-       * Avoid taking a references to ourselves. but note that we need
3120-       * to pass the GSource so we can nuke it once handled.
3121-       */
3122-      source = g_child_watch_source_new (data->child_pid);
3123-      g_source_set_callback (source,
3124-                             (GSourceFunc) utils_child_watch_from_release_cb,
3125-                             source,
3126-                             (GDestroyNotify) g_source_destroy);
3127-      /* attach source to the global default main context */
3128-      g_source_attach (source, NULL);
3129-      g_source_unref (source);
3130-      data->child_pid = 0;
3131-    }
3132-
3133-  if (data->child_stdout != NULL)
3134-    {
3135-      g_string_free (data->child_stdout, TRUE);
3136-      data->child_stdout = NULL;
3137-    }
3138-
3139-  if (data->child_stderr != NULL)
3140-    {
3141-      g_string_free (data->child_stderr, TRUE);
3142-      data->child_stderr = NULL;
3143-    }
3144-
3145-  if (data->child_stdout_channel != NULL)
3146-    {
3147-      g_io_channel_unref (data->child_stdout_channel);
3148-      data->child_stdout_channel = NULL;
3149-    }
3150-  if (data->child_stderr_channel != NULL)
3151-    {
3152-      g_io_channel_unref (data->child_stderr_channel);
3153-      data->child_stderr_channel = NULL;
3154-    }
3155-
3156-  if (data->child_stdout_source != NULL)
3157-    {
3158-      g_source_destroy (data->child_stdout_source);
3159-      data->child_stdout_source = NULL;
3160-    }
3161-  if (data->child_stderr_source != NULL)
3162-    {
3163-      g_source_destroy (data->child_stderr_source);
3164-      data->child_stderr_source = NULL;
3165-    }
3166-
3167-  if (data->child_stdout_fd != -1)
3168-    {
3169-      g_warn_if_fail (close (data->child_stdout_fd) == 0);
3170-      data->child_stdout_fd = -1;
3171-    }
3172-  if (data->child_stderr_fd != -1)
3173-    {
3174-      g_warn_if_fail (close (data->child_stderr_fd) == 0);
3175-      data->child_stderr_fd = -1;
3176-    }
3177-
3178-  if (data->cancellable_handler_id > 0)
3179-    {
3180-      g_cancellable_disconnect (data->cancellable, data->cancellable_handler_id);
3181-      data->cancellable_handler_id = 0;
3182-    }
3183-
3184-  if (data->main_context != NULL)
3185-    g_main_context_unref (data->main_context);
3186-
3187-  if (data->cancellable != NULL)
3188-    g_object_unref (data->cancellable);
3189-
3190-  g_slice_free (UtilsSpawnData, data);
3191-}
3192-
3193-/* called in the thread where @cancellable was cancelled */
3194-static void
3195-utils_on_cancelled (GCancellable *cancellable,
3196-                    gpointer      user_data)
3197-{
3198-  UtilsSpawnData *data = (UtilsSpawnData *)user_data;
3199-  GError *error;
3200-
3201-  error = NULL;
3202-  g_warn_if_fail (g_cancellable_set_error_if_cancelled (cancellable, &error));
3203-  g_simple_async_result_take_error (data->simple, error);
3204-  g_simple_async_result_complete_in_idle (data->simple);
3205-  g_object_unref (data->simple);
3206-}
3207-
3208-static gboolean
3209-utils_read_child_stderr (GIOChannel *channel,
3210-                         GIOCondition condition,
3211-                         gpointer user_data)
3212-{
3213-  UtilsSpawnData *data = (UtilsSpawnData *)user_data;
3214-  gchar buf[1024];
3215-  gsize bytes_read;
3216-
3217-  g_io_channel_read_chars (channel, buf, sizeof buf, &bytes_read, NULL);
3218-  g_string_append_len (data->child_stderr, buf, bytes_read);
3219-  return TRUE;
3220-}
3221-
3222-static gboolean
3223-utils_read_child_stdout (GIOChannel *channel,
3224-                         GIOCondition condition,
3225-                         gpointer user_data)
3226-{
3227-  UtilsSpawnData *data = (UtilsSpawnData *)user_data;
3228-  gchar buf[1024];
3229-  gsize bytes_read;
3230-
3231-  g_io_channel_read_chars (channel, buf, sizeof buf, &bytes_read, NULL);
3232-  g_string_append_len (data->child_stdout, buf, bytes_read);
3233-  return TRUE;
3234-}
3235-
3236-static void
3237-utils_child_watch_cb (GPid     pid,
3238-                      gint     status,
3239-                      gpointer user_data)
3240-{
3241-  UtilsSpawnData *data = (UtilsSpawnData *)user_data;
3242-  gchar *buf;
3243-  gsize buf_size;
3244-
3245-  if (g_io_channel_read_to_end (data->child_stdout_channel, &buf, &buf_size, NULL) == G_IO_STATUS_NORMAL)
3246-    {
3247-      g_string_append_len (data->child_stdout, buf, buf_size);
3248-      g_free (buf);
3249-    }
3250-  if (g_io_channel_read_to_end (data->child_stderr_channel, &buf, &buf_size, NULL) == G_IO_STATUS_NORMAL)
3251-    {
3252-      g_string_append_len (data->child_stderr, buf, buf_size);
3253-      g_free (buf);
3254-    }
3255-
3256-  data->exit_status = status;
3257-
3258-  /* ok, child watch is history, make sure we don't free it in spawn_data_free() */
3259-  data->child_pid = 0;
3260-  data->child_watch_source = NULL;
3261-
3262-  /* we're done */
3263-  g_simple_async_result_complete_in_idle (data->simple);
3264-  g_object_unref (data->simple);
3265-}
3266-
3267-static gboolean
3268-utils_timeout_cb (gpointer user_data)
3269-{
3270-  UtilsSpawnData *data = (UtilsSpawnData *)user_data;
3271-
3272-  data->timed_out = TRUE;
3273-
3274-  /* ok, timeout is history, make sure we don't free it in spawn_data_free() */
3275-  data->timeout_source = NULL;
3276-
3277-  /* we're done */
3278-  g_simple_async_result_complete_in_idle (data->simple);
3279-  g_object_unref (data->simple);
3280-
3281-  return FALSE; /* remove source */
3282-}
3283-
3284-static void
3285-utils_spawn (const gchar *const  *argv,
3286-             guint                timeout_seconds,
3287-             GCancellable        *cancellable,
3288-             GAsyncReadyCallback  callback,
3289-             gpointer             user_data)
3290-{
3291-  UtilsSpawnData *data;
3292-  GError *error;
3293-
3294-  data = g_slice_new0 (UtilsSpawnData);
3295-  data->timeout_seconds = timeout_seconds;
3296-  data->simple = g_simple_async_result_new (NULL,
3297-                                            callback,
3298-                                            user_data,
3299-                                            (gpointer*)utils_spawn);
3300-  data->main_context = g_main_context_get_thread_default ();
3301-  if (data->main_context != NULL)
3302-    g_main_context_ref (data->main_context);
3303-
3304-  data->cancellable = cancellable != NULL ? (GCancellable*)g_object_ref (cancellable) : NULL;
3305-
3306-  data->child_stdout = g_string_new (NULL);
3307-  data->child_stderr = g_string_new (NULL);
3308-  data->child_stdout_fd = -1;
3309-  data->child_stderr_fd = -1;
3310-
3311-  /* the life-cycle of UtilsSpawnData is tied to its GSimpleAsyncResult */
3312-  g_simple_async_result_set_op_res_gpointer (data->simple, data, (GDestroyNotify) utils_spawn_data_free);
3313-
3314-  error = NULL;
3315-  if (data->cancellable != NULL)
3316-    {
3317-      /* could already be cancelled */
3318-      error = NULL;
3319-      if (g_cancellable_set_error_if_cancelled (data->cancellable, &error))
3320-        {
3321-          g_simple_async_result_take_error (data->simple, error);
3322-          g_simple_async_result_complete_in_idle (data->simple);
3323-          g_object_unref (data->simple);
3324-          goto out;
3325-        }
3326-
3327-      data->cancellable_handler_id = g_cancellable_connect (data->cancellable,
3328-                                                            G_CALLBACK (utils_on_cancelled),
3329-                                                            data,
3330-                                                            NULL);
3331-    }
3332-
3333-  error = NULL;
3334-  if (!g_spawn_async_with_pipes (NULL, /* working directory */
3335-                                 (gchar **) argv,
3336-                                 NULL, /* envp */
3337-                                 GSpawnFlags(G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD),
3338-                                 NULL, /* child_setup */
3339-                                 NULL, /* child_setup's user_data */
3340-                                 &(data->child_pid),
3341-                                 NULL, /* gint *stdin_fd */
3342-                                 &(data->child_stdout_fd),
3343-                                 &(data->child_stderr_fd),
3344-                                 &error))
3345-    {
3346-      g_prefix_error (&error, "Error spawning: ");
3347-      g_simple_async_result_take_error (data->simple, error);
3348-      g_simple_async_result_complete_in_idle (data->simple);
3349-      g_object_unref (data->simple);
3350-      goto out;
3351-    }
3352-
3353-  if (timeout_seconds > 0)
3354-    {
3355-      data->timeout_source = g_timeout_source_new_seconds (timeout_seconds);
3356-      g_source_set_priority (data->timeout_source, G_PRIORITY_DEFAULT);
3357-      g_source_set_callback (data->timeout_source, utils_timeout_cb, data, NULL);
3358-      g_source_attach (data->timeout_source, data->main_context);
3359-      g_source_unref (data->timeout_source);
3360-    }
3361-
3362-  data->child_watch_source = g_child_watch_source_new (data->child_pid);
3363-  g_source_set_callback (data->child_watch_source, (GSourceFunc) utils_child_watch_cb, data, NULL);
3364-  g_source_attach (data->child_watch_source, data->main_context);
3365-  g_source_unref (data->child_watch_source);
3366-
3367-  data->child_stdout_channel = g_io_channel_unix_new (data->child_stdout_fd);
3368-  g_io_channel_set_flags (data->child_stdout_channel, G_IO_FLAG_NONBLOCK, NULL);
3369-  data->child_stdout_source = g_io_create_watch (data->child_stdout_channel, G_IO_IN);
3370-  g_source_set_callback (data->child_stdout_source, (GSourceFunc) utils_read_child_stdout, data, NULL);
3371-  g_source_attach (data->child_stdout_source, data->main_context);
3372-  g_source_unref (data->child_stdout_source);
3373-
3374-  data->child_stderr_channel = g_io_channel_unix_new (data->child_stderr_fd);
3375-  g_io_channel_set_flags (data->child_stderr_channel, G_IO_FLAG_NONBLOCK, NULL);
3376-  data->child_stderr_source = g_io_create_watch (data->child_stderr_channel, G_IO_IN);
3377-  g_source_set_callback (data->child_stderr_source, (GSourceFunc) utils_read_child_stderr, data, NULL);
3378-  g_source_attach (data->child_stderr_source, data->main_context);
3379-  g_source_unref (data->child_stderr_source);
3380-
3381- out:
3382-  ;
3383-}
3384-
3385-gboolean
3386-utils_spawn_finish (GAsyncResult   *res,
3387-                    gint           *out_exit_status,
3388-                    gchar         **out_standard_output,
3389-                    gchar         **out_standard_error,
3390-                    GError        **error)
3391-{
3392-  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
3393-  UtilsSpawnData *data;
3394-  gboolean ret = FALSE;
3395-
3396-  g_return_val_if_fail (G_IS_ASYNC_RESULT (res), FALSE);
3397-  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
3398-
3399-  g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == utils_spawn);
3400-
3401-  if (g_simple_async_result_propagate_error (simple, error))
3402-    goto out;
3403-
3404-  data = (UtilsSpawnData*)g_simple_async_result_get_op_res_gpointer (simple);
3405-
3406-  if (data->timed_out)
3407-    {
3408-      g_set_error (error,
3409-                   G_IO_ERROR,
3410-                   G_IO_ERROR_TIMED_OUT,
3411-                   "Timed out after %d seconds",
3412-                   data->timeout_seconds);
3413-      goto out;
3414-    }
3415-
3416-  if (out_exit_status != NULL)
3417-    *out_exit_status = data->exit_status;
3418-
3419-  if (out_standard_output != NULL)
3420-    *out_standard_output = g_strdup (data->child_stdout->str);
3421-
3422-  if (out_standard_error != NULL)
3423-    *out_standard_error = g_strdup (data->child_stderr->str);
3424-
3425-  ret = TRUE;
3426-
3427- out:
3428-  return ret;
3429-}
3430diff --git a/test/data/etc/polkit-1/rules.d/10-testing.rules b/test/data/etc/polkit-1/rules.d/10-testing.rules
3431index 98bf062..e346b5d 100644
3432--- a/test/data/etc/polkit-1/rules.d/10-testing.rules
3433+++ b/test/data/etc/polkit-1/rules.d/10-testing.rules
3434@@ -189,8 +189,10 @@ polkit.addRule(function(action, subject) {
3435                 ;
3436         } catch (error) {
3437             if (error == "Terminating runaway script")
3438-                return polkit.Result.YES;
3439-            return polkit.Result.NO;
3440+                // Inverted logic to accomodate Duktape's model as well, which
3441+                // will always fail with negation, on timeouts
3442+                return polkit.Result.NO;
3443+            return polkit.Result.YES;
3444         }
3445     }
3446 });
3447diff --git a/test/polkitbackend/test-polkitbackendjsauthority.c b/test/polkitbackend/test-polkitbackendjsauthority.c
3448index f97e0e0..2103b17 100644
3449--- a/test/polkitbackend/test-polkitbackendjsauthority.c
3450+++ b/test/polkitbackend/test-polkitbackendjsauthority.c
3451@@ -328,7 +328,7 @@ static const RulesTestCase rules_test_cases[] = {
3452     "net.company.run_away_script",
3453     "unix-user:root",
3454     NULL,
3455-    POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED,
3456+    POLKIT_IMPLICIT_AUTHORIZATION_NOT_AUTHORIZED,
3457   },
3458
3459   {
3460