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