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