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