| From 4af72493cb380ab5ce0dd7c5bcd25a8b5457d770 Mon Sep 17 00:00:00 2001 |
| From: Gustavo Lima Chaves <limachaves@gmail.com> |
| Date: Tue, 25 Jan 2022 09:43:21 +0000 |
| Subject: [PATCH] Added support for duktape as JS engine |
| |
| Original author: Wu Xiaotian (@yetist) |
| Resurrection author, runaway-killer author: Gustavo Lima Chaves (@limachaves) |
| |
| Signed-off-by: Mikko Rapeli <mikko.rapeli@bmw.de> |
| |
| Upstream-Status: Backport [c7fc4e1b61f0fd82fc697c19c604af7e9fb291a2] |
| Dropped change to .gitlab-ci.yml and adapted configure.ac due to other |
| patches in meta-oe. |
| |
| --- |
| buildutil/ax_pthread.m4 | 522 ++++++++ |
| configure.ac | 34 +- |
| docs/man/polkit.xml | 4 +- |
| meson.build | 16 +- |
| meson_options.txt | 1 + |
| src/polkitbackend/Makefile.am | 17 +- |
| src/polkitbackend/meson.build | 14 +- |
| src/polkitbackend/polkitbackendcommon.c | 530 +++++++++ |
| src/polkitbackend/polkitbackendcommon.h | 158 +++ |
| .../polkitbackendduktapeauthority.c | 1051 +++++++++++++++++ |
| .../polkitbackendjsauthority.cpp | 721 +---------- |
| .../etc/polkit-1/rules.d/10-testing.rules | 6 +- |
| .../test-polkitbackendjsauthority.c | 2 +- |
| 13 files changed, 2398 insertions(+), 678 deletions(-) |
| create mode 100644 buildutil/ax_pthread.m4 |
| create mode 100644 src/polkitbackend/polkitbackendcommon.c |
| create mode 100644 src/polkitbackend/polkitbackendcommon.h |
| create mode 100644 src/polkitbackend/polkitbackendduktapeauthority.c |
| |
| diff --git a/buildutil/ax_pthread.m4 b/buildutil/ax_pthread.m4 |
| new file mode 100644 |
| index 0000000..9f35d13 |
| --- /dev/null |
| +++ b/buildutil/ax_pthread.m4 |
| @@ -0,0 +1,522 @@ |
| +# =========================================================================== |
| +# https://www.gnu.org/software/autoconf-archive/ax_pthread.html |
| +# =========================================================================== |
| +# |
| +# SYNOPSIS |
| +# |
| +# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) |
| +# |
| +# DESCRIPTION |
| +# |
| +# This macro figures out how to build C programs using POSIX threads. It |
| +# sets the PTHREAD_LIBS output variable to the threads library and linker |
| +# flags, and the PTHREAD_CFLAGS output variable to any special C compiler |
| +# flags that are needed. (The user can also force certain compiler |
| +# flags/libs to be tested by setting these environment variables.) |
| +# |
| +# Also sets PTHREAD_CC and PTHREAD_CXX to any special C compiler that is |
| +# needed for multi-threaded programs (defaults to the value of CC |
| +# respectively CXX otherwise). (This is necessary on e.g. AIX to use the |
| +# special cc_r/CC_r compiler alias.) |
| +# |
| +# NOTE: You are assumed to not only compile your program with these flags, |
| +# but also to link with them as well. For example, you might link with |
| +# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS |
| +# $PTHREAD_CXX $CXXFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS |
| +# |
| +# If you are only building threaded programs, you may wish to use these |
| +# variables in your default LIBS, CFLAGS, and CC: |
| +# |
| +# LIBS="$PTHREAD_LIBS $LIBS" |
| +# CFLAGS="$CFLAGS $PTHREAD_CFLAGS" |
| +# CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" |
| +# CC="$PTHREAD_CC" |
| +# CXX="$PTHREAD_CXX" |
| +# |
| +# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant |
| +# has a nonstandard name, this macro defines PTHREAD_CREATE_JOINABLE to |
| +# that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX). |
| +# |
| +# Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the |
| +# PTHREAD_PRIO_INHERIT symbol is defined when compiling with |
| +# PTHREAD_CFLAGS. |
| +# |
| +# ACTION-IF-FOUND is a list of shell commands to run if a threads library |
| +# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it |
| +# is not found. If ACTION-IF-FOUND is not specified, the default action |
| +# will define HAVE_PTHREAD. |
| +# |
| +# Please let the authors know if this macro fails on any platform, or if |
| +# you have any other suggestions or comments. This macro was based on work |
| +# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help |
| +# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by |
| +# Alejandro Forero Cuervo to the autoconf macro repository. We are also |
| +# grateful for the helpful feedback of numerous users. |
| +# |
| +# Updated for Autoconf 2.68 by Daniel Richard G. |
| +# |
| +# LICENSE |
| +# |
| +# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu> |
| +# Copyright (c) 2011 Daniel Richard G. <skunk@iSKUNK.ORG> |
| +# Copyright (c) 2019 Marc Stevens <marc.stevens@cwi.nl> |
| +# |
| +# This program is free software: you can redistribute it and/or modify it |
| +# under the terms of the GNU General Public License as published by the |
| +# Free Software Foundation, either version 3 of the License, or (at your |
| +# option) any later version. |
| +# |
| +# This program is distributed in the hope that it will be useful, but |
| +# WITHOUT ANY WARRANTY; without even the implied warranty of |
| +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
| +# Public License for more details. |
| +# |
| +# You should have received a copy of the GNU General Public License along |
| +# with this program. If not, see <https://www.gnu.org/licenses/>. |
| +# |
| +# As a special exception, the respective Autoconf Macro's copyright owner |
| +# gives unlimited permission to copy, distribute and modify the configure |
| +# scripts that are the output of Autoconf when processing the Macro. You |
| +# need not follow the terms of the GNU General Public License when using |
| +# or distributing such scripts, even though portions of the text of the |
| +# Macro appear in them. The GNU General Public License (GPL) does govern |
| +# all other use of the material that constitutes the Autoconf Macro. |
| +# |
| +# This special exception to the GPL applies to versions of the Autoconf |
| +# Macro released by the Autoconf Archive. When you make and distribute a |
| +# modified version of the Autoconf Macro, you may extend this special |
| +# exception to the GPL to apply to your modified version as well. |
| + |
| +#serial 31 |
| + |
| +AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) |
| +AC_DEFUN([AX_PTHREAD], [ |
| +AC_REQUIRE([AC_CANONICAL_HOST]) |
| +AC_REQUIRE([AC_PROG_CC]) |
| +AC_REQUIRE([AC_PROG_SED]) |
| +AC_LANG_PUSH([C]) |
| +ax_pthread_ok=no |
| + |
| +# We used to check for pthread.h first, but this fails if pthread.h |
| +# requires special compiler flags (e.g. on Tru64 or Sequent). |
| +# It gets checked for in the link test anyway. |
| + |
| +# First of all, check if the user has set any of the PTHREAD_LIBS, |
| +# etcetera environment variables, and if threads linking works using |
| +# them: |
| +if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then |
| + ax_pthread_save_CC="$CC" |
| + ax_pthread_save_CFLAGS="$CFLAGS" |
| + ax_pthread_save_LIBS="$LIBS" |
| + AS_IF([test "x$PTHREAD_CC" != "x"], [CC="$PTHREAD_CC"]) |
| + AS_IF([test "x$PTHREAD_CXX" != "x"], [CXX="$PTHREAD_CXX"]) |
| + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" |
| + LIBS="$PTHREAD_LIBS $LIBS" |
| + AC_MSG_CHECKING([for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS]) |
| + AC_LINK_IFELSE([AC_LANG_CALL([], [pthread_join])], [ax_pthread_ok=yes]) |
| + AC_MSG_RESULT([$ax_pthread_ok]) |
| + if test "x$ax_pthread_ok" = "xno"; then |
| + PTHREAD_LIBS="" |
| + PTHREAD_CFLAGS="" |
| + fi |
| + CC="$ax_pthread_save_CC" |
| + CFLAGS="$ax_pthread_save_CFLAGS" |
| + LIBS="$ax_pthread_save_LIBS" |
| +fi |
| + |
| +# We must check for the threads library under a number of different |
| +# names; the ordering is very important because some systems |
| +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the |
| +# libraries is broken (non-POSIX). |
| + |
| +# Create a list of thread flags to try. Items with a "," contain both |
| +# C compiler flags (before ",") and linker flags (after ","). Other items |
| +# starting with a "-" are C compiler flags, and remaining items are |
| +# library names, except for "none" which indicates that we try without |
| +# any flags at all, and "pthread-config" which is a program returning |
| +# the flags for the Pth emulation library. |
| + |
| +ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" |
| + |
| +# The ordering *is* (sometimes) important. Some notes on the |
| +# individual items follow: |
| + |
| +# pthreads: AIX (must check this before -lpthread) |
| +# none: in case threads are in libc; should be tried before -Kthread and |
| +# other compiler flags to prevent continual compiler warnings |
| +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) |
| +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads), Tru64 |
| +# (Note: HP C rejects this with "bad form for `-t' option") |
| +# -pthreads: Solaris/gcc (Note: HP C also rejects) |
| +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it |
| +# doesn't hurt to check since this sometimes defines pthreads and |
| +# -D_REENTRANT too), HP C (must be checked before -lpthread, which |
| +# is present but should not be used directly; and before -mthreads, |
| +# because the compiler interprets this as "-mt" + "-hreads") |
| +# -mthreads: Mingw32/gcc, Lynx/gcc |
| +# pthread: Linux, etcetera |
| +# --thread-safe: KAI C++ |
| +# pthread-config: use pthread-config program (for GNU Pth library) |
| + |
| +case $host_os in |
| + |
| + freebsd*) |
| + |
| + # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) |
| + # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) |
| + |
| + ax_pthread_flags="-kthread lthread $ax_pthread_flags" |
| + ;; |
| + |
| + hpux*) |
| + |
| + # From the cc(1) man page: "[-mt] Sets various -D flags to enable |
| + # multi-threading and also sets -lpthread." |
| + |
| + ax_pthread_flags="-mt -pthread pthread $ax_pthread_flags" |
| + ;; |
| + |
| + openedition*) |
| + |
| + # IBM z/OS requires a feature-test macro to be defined in order to |
| + # enable POSIX threads at all, so give the user a hint if this is |
| + # not set. (We don't define these ourselves, as they can affect |
| + # other portions of the system API in unpredictable ways.) |
| + |
| + AC_EGREP_CPP([AX_PTHREAD_ZOS_MISSING], |
| + [ |
| +# if !defined(_OPEN_THREADS) && !defined(_UNIX03_THREADS) |
| + AX_PTHREAD_ZOS_MISSING |
| +# endif |
| + ], |
| + [AC_MSG_WARN([IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support.])]) |
| + ;; |
| + |
| + solaris*) |
| + |
| + # On Solaris (at least, for some versions), libc contains stubbed |
| + # (non-functional) versions of the pthreads routines, so link-based |
| + # tests will erroneously succeed. (N.B.: The stubs are missing |
| + # pthread_cleanup_push, or rather a function called by this macro, |
| + # so we could check for that, but who knows whether they'll stub |
| + # that too in a future libc.) So we'll check first for the |
| + # standard Solaris way of linking pthreads (-mt -lpthread). |
| + |
| + ax_pthread_flags="-mt,-lpthread pthread $ax_pthread_flags" |
| + ;; |
| +esac |
| + |
| +# Are we compiling with Clang? |
| + |
| +AC_CACHE_CHECK([whether $CC is Clang], |
| + [ax_cv_PTHREAD_CLANG], |
| + [ax_cv_PTHREAD_CLANG=no |
| + # Note that Autoconf sets GCC=yes for Clang as well as GCC |
| + if test "x$GCC" = "xyes"; then |
| + AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG], |
| + [/* Note: Clang 2.7 lacks __clang_[a-z]+__ */ |
| +# if defined(__clang__) && defined(__llvm__) |
| + AX_PTHREAD_CC_IS_CLANG |
| +# endif |
| + ], |
| + [ax_cv_PTHREAD_CLANG=yes]) |
| + fi |
| + ]) |
| +ax_pthread_clang="$ax_cv_PTHREAD_CLANG" |
| + |
| + |
| +# GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC) |
| + |
| +# Note that for GCC and Clang -pthread generally implies -lpthread, |
| +# except when -nostdlib is passed. |
| +# This is problematic using libtool to build C++ shared libraries with pthread: |
| +# [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25460 |
| +# [2] https://bugzilla.redhat.com/show_bug.cgi?id=661333 |
| +# [3] https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=468555 |
| +# To solve this, first try -pthread together with -lpthread for GCC |
| + |
| +AS_IF([test "x$GCC" = "xyes"], |
| + [ax_pthread_flags="-pthread,-lpthread -pthread -pthreads $ax_pthread_flags"]) |
| + |
| +# Clang takes -pthread (never supported any other flag), but we'll try with -lpthread first |
| + |
| +AS_IF([test "x$ax_pthread_clang" = "xyes"], |
| + [ax_pthread_flags="-pthread,-lpthread -pthread"]) |
| + |
| + |
| +# The presence of a feature test macro requesting re-entrant function |
| +# definitions is, on some systems, a strong hint that pthreads support is |
| +# correctly enabled |
| + |
| +case $host_os in |
| + darwin* | hpux* | linux* | osf* | solaris*) |
| + ax_pthread_check_macro="_REENTRANT" |
| + ;; |
| + |
| + aix*) |
| + ax_pthread_check_macro="_THREAD_SAFE" |
| + ;; |
| + |
| + *) |
| + ax_pthread_check_macro="--" |
| + ;; |
| +esac |
| +AS_IF([test "x$ax_pthread_check_macro" = "x--"], |
| + [ax_pthread_check_cond=0], |
| + [ax_pthread_check_cond="!defined($ax_pthread_check_macro)"]) |
| + |
| + |
| +if test "x$ax_pthread_ok" = "xno"; then |
| +for ax_pthread_try_flag in $ax_pthread_flags; do |
| + |
| + case $ax_pthread_try_flag in |
| + none) |
| + AC_MSG_CHECKING([whether pthreads work without any flags]) |
| + ;; |
| + |
| + *,*) |
| + PTHREAD_CFLAGS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\1/"` |
| + PTHREAD_LIBS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\2/"` |
| + AC_MSG_CHECKING([whether pthreads work with "$PTHREAD_CFLAGS" and "$PTHREAD_LIBS"]) |
| + ;; |
| + |
| + -*) |
| + AC_MSG_CHECKING([whether pthreads work with $ax_pthread_try_flag]) |
| + PTHREAD_CFLAGS="$ax_pthread_try_flag" |
| + ;; |
| + |
| + pthread-config) |
| + AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no]) |
| + AS_IF([test "x$ax_pthread_config" = "xno"], [continue]) |
| + PTHREAD_CFLAGS="`pthread-config --cflags`" |
| + PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" |
| + ;; |
| + |
| + *) |
| + AC_MSG_CHECKING([for the pthreads library -l$ax_pthread_try_flag]) |
| + PTHREAD_LIBS="-l$ax_pthread_try_flag" |
| + ;; |
| + esac |
| + |
| + ax_pthread_save_CFLAGS="$CFLAGS" |
| + ax_pthread_save_LIBS="$LIBS" |
| + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" |
| + LIBS="$PTHREAD_LIBS $LIBS" |
| + |
| + # Check for various functions. We must include pthread.h, |
| + # since some functions may be macros. (On the Sequent, we |
| + # need a special flag -Kthread to make this header compile.) |
| + # We check for pthread_join because it is in -lpthread on IRIX |
| + # while pthread_create is in libc. We check for pthread_attr_init |
| + # due to DEC craziness with -lpthreads. We check for |
| + # pthread_cleanup_push because it is one of the few pthread |
| + # functions on Solaris that doesn't have a non-functional libc stub. |
| + # We try pthread_create on general principles. |
| + |
| + AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h> |
| +# if $ax_pthread_check_cond |
| +# error "$ax_pthread_check_macro must be defined" |
| +# endif |
| + static void *some_global = NULL; |
| + static void routine(void *a) |
| + { |
| + /* To avoid any unused-parameter or |
| + unused-but-set-parameter warning. */ |
| + some_global = a; |
| + } |
| + static void *start_routine(void *a) { return a; }], |
| + [pthread_t th; pthread_attr_t attr; |
| + pthread_create(&th, 0, start_routine, 0); |
| + pthread_join(th, 0); |
| + pthread_attr_init(&attr); |
| + pthread_cleanup_push(routine, 0); |
| + pthread_cleanup_pop(0) /* ; */])], |
| + [ax_pthread_ok=yes], |
| + []) |
| + |
| + CFLAGS="$ax_pthread_save_CFLAGS" |
| + LIBS="$ax_pthread_save_LIBS" |
| + |
| + AC_MSG_RESULT([$ax_pthread_ok]) |
| + AS_IF([test "x$ax_pthread_ok" = "xyes"], [break]) |
| + |
| + PTHREAD_LIBS="" |
| + PTHREAD_CFLAGS="" |
| +done |
| +fi |
| + |
| + |
| +# Clang needs special handling, because older versions handle the -pthread |
| +# option in a rather... idiosyncratic way |
| + |
| +if test "x$ax_pthread_clang" = "xyes"; then |
| + |
| + # Clang takes -pthread; it has never supported any other flag |
| + |
| + # (Note 1: This will need to be revisited if a system that Clang |
| + # supports has POSIX threads in a separate library. This tends not |
| + # to be the way of modern systems, but it's conceivable.) |
| + |
| + # (Note 2: On some systems, notably Darwin, -pthread is not needed |
| + # to get POSIX threads support; the API is always present and |
| + # active. We could reasonably leave PTHREAD_CFLAGS empty. But |
| + # -pthread does define _REENTRANT, and while the Darwin headers |
| + # ignore this macro, third-party headers might not.) |
| + |
| + # However, older versions of Clang make a point of warning the user |
| + # that, in an invocation where only linking and no compilation is |
| + # taking place, the -pthread option has no effect ("argument unused |
| + # during compilation"). They expect -pthread to be passed in only |
| + # when source code is being compiled. |
| + # |
| + # Problem is, this is at odds with the way Automake and most other |
| + # C build frameworks function, which is that the same flags used in |
| + # compilation (CFLAGS) are also used in linking. Many systems |
| + # supported by AX_PTHREAD require exactly this for POSIX threads |
| + # support, and in fact it is often not straightforward to specify a |
| + # flag that is used only in the compilation phase and not in |
| + # linking. Such a scenario is extremely rare in practice. |
| + # |
| + # Even though use of the -pthread flag in linking would only print |
| + # a warning, this can be a nuisance for well-run software projects |
| + # that build with -Werror. So if the active version of Clang has |
| + # this misfeature, we search for an option to squash it. |
| + |
| + AC_CACHE_CHECK([whether Clang needs flag to prevent "argument unused" warning when linking with -pthread], |
| + [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG], |
| + [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown |
| + # Create an alternate version of $ac_link that compiles and |
| + # links in two steps (.c -> .o, .o -> exe) instead of one |
| + # (.c -> exe), because the warning occurs only in the second |
| + # step |
| + ax_pthread_save_ac_link="$ac_link" |
| + ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g' |
| + ax_pthread_link_step=`AS_ECHO(["$ac_link"]) | sed "$ax_pthread_sed"` |
| + ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)" |
| + ax_pthread_save_CFLAGS="$CFLAGS" |
| + for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do |
| + AS_IF([test "x$ax_pthread_try" = "xunknown"], [break]) |
| + CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS" |
| + ac_link="$ax_pthread_save_ac_link" |
| + AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], |
| + [ac_link="$ax_pthread_2step_ac_link" |
| + AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], |
| + [break]) |
| + ]) |
| + done |
| + ac_link="$ax_pthread_save_ac_link" |
| + CFLAGS="$ax_pthread_save_CFLAGS" |
| + AS_IF([test "x$ax_pthread_try" = "x"], [ax_pthread_try=no]) |
| + ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try" |
| + ]) |
| + |
| + case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in |
| + no | unknown) ;; |
| + *) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;; |
| + esac |
| + |
| +fi # $ax_pthread_clang = yes |
| + |
| + |
| + |
| +# Various other checks: |
| +if test "x$ax_pthread_ok" = "xyes"; then |
| + ax_pthread_save_CFLAGS="$CFLAGS" |
| + ax_pthread_save_LIBS="$LIBS" |
| + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" |
| + LIBS="$PTHREAD_LIBS $LIBS" |
| + |
| + # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. |
| + AC_CACHE_CHECK([for joinable pthread attribute], |
| + [ax_cv_PTHREAD_JOINABLE_ATTR], |
| + [ax_cv_PTHREAD_JOINABLE_ATTR=unknown |
| + for ax_pthread_attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do |
| + AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>], |
| + [int attr = $ax_pthread_attr; return attr /* ; */])], |
| + [ax_cv_PTHREAD_JOINABLE_ATTR=$ax_pthread_attr; break], |
| + []) |
| + done |
| + ]) |
| + AS_IF([test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xunknown" && \ |
| + test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xPTHREAD_CREATE_JOINABLE" && \ |
| + test "x$ax_pthread_joinable_attr_defined" != "xyes"], |
| + [AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE], |
| + [$ax_cv_PTHREAD_JOINABLE_ATTR], |
| + [Define to necessary symbol if this constant |
| + uses a non-standard name on your system.]) |
| + ax_pthread_joinable_attr_defined=yes |
| + ]) |
| + |
| + AC_CACHE_CHECK([whether more special flags are required for pthreads], |
| + [ax_cv_PTHREAD_SPECIAL_FLAGS], |
| + [ax_cv_PTHREAD_SPECIAL_FLAGS=no |
| + case $host_os in |
| + solaris*) |
| + ax_cv_PTHREAD_SPECIAL_FLAGS="-D_POSIX_PTHREAD_SEMANTICS" |
| + ;; |
| + esac |
| + ]) |
| + AS_IF([test "x$ax_cv_PTHREAD_SPECIAL_FLAGS" != "xno" && \ |
| + test "x$ax_pthread_special_flags_added" != "xyes"], |
| + [PTHREAD_CFLAGS="$ax_cv_PTHREAD_SPECIAL_FLAGS $PTHREAD_CFLAGS" |
| + ax_pthread_special_flags_added=yes]) |
| + |
| + AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], |
| + [ax_cv_PTHREAD_PRIO_INHERIT], |
| + [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread.h>]], |
| + [[int i = PTHREAD_PRIO_INHERIT; |
| + return i;]])], |
| + [ax_cv_PTHREAD_PRIO_INHERIT=yes], |
| + [ax_cv_PTHREAD_PRIO_INHERIT=no]) |
| + ]) |
| + AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes" && \ |
| + test "x$ax_pthread_prio_inherit_defined" != "xyes"], |
| + [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.]) |
| + ax_pthread_prio_inherit_defined=yes |
| + ]) |
| + |
| + CFLAGS="$ax_pthread_save_CFLAGS" |
| + LIBS="$ax_pthread_save_LIBS" |
| + |
| + # More AIX lossage: compile with *_r variant |
| + if test "x$GCC" != "xyes"; then |
| + case $host_os in |
| + aix*) |
| + AS_CASE(["x/$CC"], |
| + [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6], |
| + [#handle absolute path differently from PATH based program lookup |
| + AS_CASE(["x$CC"], |
| + [x/*], |
| + [ |
| + AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"]) |
| + AS_IF([test "x${CXX}" != "x"], [AS_IF([AS_EXECUTABLE_P([${CXX}_r])],[PTHREAD_CXX="${CXX}_r"])]) |
| + ], |
| + [ |
| + AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC]) |
| + AS_IF([test "x${CXX}" != "x"], [AC_CHECK_PROGS([PTHREAD_CXX],[${CXX}_r],[$CXX])]) |
| + ] |
| + ) |
| + ]) |
| + ;; |
| + esac |
| + fi |
| +fi |
| + |
| +test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" |
| +test -n "$PTHREAD_CXX" || PTHREAD_CXX="$CXX" |
| + |
| +AC_SUBST([PTHREAD_LIBS]) |
| +AC_SUBST([PTHREAD_CFLAGS]) |
| +AC_SUBST([PTHREAD_CC]) |
| +AC_SUBST([PTHREAD_CXX]) |
| + |
| +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: |
| +if test "x$ax_pthread_ok" = "xyes"; then |
| + ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1]) |
| + : |
| +else |
| + ax_pthread_ok=no |
| + $2 |
| +fi |
| +AC_LANG_POP |
| +])dnl AX_PTHREAD |
| diff --git a/configure.ac b/configure.ac |
| index b625743..bbf4768 100644 |
| --- a/configure.ac |
| +++ b/configure.ac |
| @@ -80,11 +80,22 @@ PKG_CHECK_MODULES(GLIB, [gmodule-2.0 gio-unix-2.0 >= 2.30.0]) |
| AC_SUBST(GLIB_CFLAGS) |
| AC_SUBST(GLIB_LIBS) |
| |
| -PKG_CHECK_MODULES(LIBJS, [mozjs-78]) |
| - |
| -AC_SUBST(LIBJS_CFLAGS) |
| -AC_SUBST(LIBJS_CXXFLAGS) |
| -AC_SUBST(LIBJS_LIBS) |
| +dnl --------------------------------------------------------------------------- |
| +dnl - Check javascript backend |
| +dnl --------------------------------------------------------------------------- |
| +AC_ARG_WITH(duktape, AS_HELP_STRING([--with-duktape],[Use Duktape as javascript backend]),with_duktape=yes,with_duktape=no) |
| +AS_IF([test x${with_duktape} == xyes], [ |
| + PKG_CHECK_MODULES(LIBJS, [duktape >= 2.2.0 ]) |
| + AC_SUBST(LIBJS_CFLAGS) |
| + AC_SUBST(LIBJS_LIBS) |
| +], [ |
| + PKG_CHECK_MODULES(LIBJS, [mozjs-78]) |
| + |
| + AC_SUBST(LIBJS_CFLAGS) |
| + AC_SUBST(LIBJS_CXXFLAGS) |
| + AC_SUBST(LIBJS_LIBS) |
| +]) |
| +AM_CONDITIONAL(USE_DUKTAPE, [test x$with_duktape == xyes], [Using duktape as javascript engine library]) |
| |
| EXPAT_LIB="" |
| AC_ARG_WITH(expat, [ --with-expat=<dir> Use expat from here], |
| @@ -100,6 +111,12 @@ AC_CHECK_LIB(expat,XML_ParserCreate,[EXPAT_LIBS="-lexpat"], |
| [AC_MSG_ERROR([Can't find expat library. Please install expat.])]) |
| AC_SUBST(EXPAT_LIBS) |
| |
| +AX_PTHREAD([], [AC_MSG_ERROR([Cannot find the way to enable pthread support.])]) |
| +LIBS="$PTHREAD_LIBS $LIBS" |
| +CFLAGS="$CFLAGS $PTHREAD_CFLAGS" |
| +CC="$PTHREAD_CC" |
| +AC_CHECK_FUNCS([pthread_condattr_setclock]) |
| + |
| AC_CHECK_FUNCS(clearenv fdatasync) |
| |
| if test "x$GCC" = "xyes"; then |
| @@ -581,6 +598,13 @@ echo " |
| PAM support: ${have_pam} |
| systemdsystemunitdir: ${systemdsystemunitdir} |
| polkitd user: ${POLKITD_USER}" |
| +if test "x${with_duktape}" = xyes; then |
| +echo " |
| + Javascript engine: Duktape" |
| +else |
| +echo " |
| + Javascript engine: Mozjs" |
| +fi |
| |
| if test "$have_pam" = yes ; then |
| echo " |
| diff --git a/docs/man/polkit.xml b/docs/man/polkit.xml |
| index 99aa474..90715a5 100644 |
| --- a/docs/man/polkit.xml |
| +++ b/docs/man/polkit.xml |
| @@ -639,7 +639,9 @@ polkit.Result = { |
| If user-provided code takes a long time to execute, an exception |
| will be thrown which normally results in the function being |
| terminated (the current limit is 15 seconds). This is used to |
| - catch runaway scripts. |
| + catch runaway scripts. If the duktape JavaScript backend is |
| + compiled in, instead of mozjs, no exception will be thrown—the |
| + script will be killed right away (same timeout). |
| </para> |
| |
| <para> |
| diff --git a/meson.build b/meson.build |
| index b3702be..7506231 100644 |
| --- a/meson.build |
| +++ b/meson.build |
| @@ -126,7 +126,18 @@ expat_dep = dependency('expat') |
| assert(cc.has_header('expat.h', dependencies: expat_dep), 'Can\'t find expat.h. Please install expat.') |
| assert(cc.has_function('XML_ParserCreate', dependencies: expat_dep), 'Can\'t find expat library. Please install expat.') |
| |
| -mozjs_dep = dependency('mozjs-78') |
| +duktape_req_version = '>= 2.2.0' |
| + |
| +js_engine = get_option('js_engine') |
| +if js_engine == 'duktape' |
| + js_dep = dependency('duktape', version: duktape_req_version) |
| + libm_dep = cc.find_library('m') |
| + thread_dep = dependency('threads') |
| + func = 'pthread_condattr_setclock' |
| + config_h.set('HAVE_' + func.to_upper(), cc.has_function(func, prefix : '#include <pthread.h>')) |
| +elif js_engine == 'mozjs' |
| + js_dep = dependency('mozjs-78') |
| +endif |
| |
| dbus_dep = dependency('dbus-1') |
| dbus_confdir = dbus_dep.get_pkgconfig_variable('datadir', define_variable: ['datadir', pk_prefix / pk_datadir]) #changed from sysconfdir with respect to commit#8eada3836465838 |
| @@ -350,6 +361,9 @@ if enable_logind |
| output += ' systemdsystemunitdir: ' + systemd_systemdsystemunitdir + '\n' |
| endif |
| output += ' polkitd user: ' + polkitd_user + ' \n' |
| +output += ' Javascript engine: ' + js_engine + '\n' |
| +if enable_logind |
| +endif |
| output += ' PAM support: ' + enable_pam.to_string() + '\n\n' |
| if enable_pam |
| output += ' PAM file auth: ' + pam_conf['PAM_FILE_INCLUDE_AUTH'] + '\n' |
| diff --git a/meson_options.txt b/meson_options.txt |
| index 25e3e77..76aa311 100644 |
| --- a/meson_options.txt |
| +++ b/meson_options.txt |
| @@ -16,3 +16,4 @@ option('introspection', type: 'boolean', value: true, description: 'Enable intro |
| |
| option('gtk_doc', type: 'boolean', value: false, description: 'use gtk-doc to build documentation') |
| option('man', type: 'boolean', value: false, description: 'build manual pages') |
| +option('js_engine', type: 'combo', choices: ['mozjs', 'duktape'], value: 'duktape', description: 'javascript engine') |
| diff --git a/src/polkitbackend/Makefile.am b/src/polkitbackend/Makefile.am |
| index 7e3c080..935fb98 100644 |
| --- a/src/polkitbackend/Makefile.am |
| +++ b/src/polkitbackend/Makefile.am |
| @@ -17,6 +17,8 @@ AM_CPPFLAGS = \ |
| -DPACKAGE_LIB_DIR=\""$(libdir)"\" \ |
| -D_POSIX_PTHREAD_SEMANTICS \ |
| -D_REENTRANT \ |
| + -D_XOPEN_SOURCE=700 \ |
| + -D_GNU_SOURCE=1 \ |
| $(NULL) |
| |
| noinst_LTLIBRARIES=libpolkit-backend-1.la |
| @@ -31,9 +33,10 @@ libpolkit_backend_1_la_SOURCES = \ |
| polkitbackend.h \ |
| polkitbackendtypes.h \ |
| polkitbackendprivate.h \ |
| + polkitbackendcommon.h polkitbackendcommon.c \ |
| polkitbackendauthority.h polkitbackendauthority.c \ |
| polkitbackendinteractiveauthority.h polkitbackendinteractiveauthority.c \ |
| - polkitbackendjsauthority.h polkitbackendjsauthority.cpp \ |
| + polkitbackendjsauthority.h \ |
| polkitbackendactionpool.h polkitbackendactionpool.c \ |
| polkitbackendactionlookup.h polkitbackendactionlookup.c \ |
| $(NULL) |
| @@ -51,19 +54,27 @@ libpolkit_backend_1_la_CFLAGS = \ |
| -D_POLKIT_BACKEND_COMPILATION \ |
| $(GLIB_CFLAGS) \ |
| $(LIBSYSTEMD_CFLAGS) \ |
| - $(LIBJS_CFLAGS) \ |
| + $(LIBJS_CFLAGS) \ |
| $(NULL) |
| |
| libpolkit_backend_1_la_CXXFLAGS = $(libpolkit_backend_1_la_CFLAGS) |
| |
| libpolkit_backend_1_la_LIBADD = \ |
| $(GLIB_LIBS) \ |
| + $(DUKTAPE_LIBS) \ |
| $(LIBSYSTEMD_LIBS) \ |
| $(top_builddir)/src/polkit/libpolkit-gobject-1.la \ |
| $(EXPAT_LIBS) \ |
| - $(LIBJS_LIBS) \ |
| + $(LIBJS_LIBS) \ |
| $(NULL) |
| |
| +if USE_DUKTAPE |
| +libpolkit_backend_1_la_SOURCES += polkitbackendduktapeauthority.c |
| +libpolkit_backend_1_la_LIBADD += -lm |
| +else |
| +libpolkit_backend_1_la_SOURCES += polkitbackendjsauthority.cpp |
| +endif |
| + |
| rulesdir = $(sysconfdir)/polkit-1/rules.d |
| rules_DATA = 50-default.rules |
| |
| diff --git a/src/polkitbackend/meson.build b/src/polkitbackend/meson.build |
| index 93c3c34..99f8e33 100644 |
| --- a/src/polkitbackend/meson.build |
| +++ b/src/polkitbackend/meson.build |
| @@ -4,8 +4,8 @@ sources = files( |
| 'polkitbackendactionlookup.c', |
| 'polkitbackendactionpool.c', |
| 'polkitbackendauthority.c', |
| + 'polkitbackendcommon.c', |
| 'polkitbackendinteractiveauthority.c', |
| - 'polkitbackendjsauthority.cpp', |
| ) |
| |
| output = 'initjs.h' |
| @@ -21,7 +21,7 @@ sources += custom_target( |
| deps = [ |
| expat_dep, |
| libpolkit_gobject_dep, |
| - mozjs_dep, |
| + js_dep, |
| ] |
| |
| c_flags = [ |
| @@ -29,8 +29,18 @@ c_flags = [ |
| '-D_POLKIT_BACKEND_COMPILATION', |
| '-DPACKAGE_DATA_DIR="@0@"'.format(pk_prefix / pk_datadir), |
| '-DPACKAGE_SYSCONF_DIR="@0@"'.format(pk_prefix / pk_sysconfdir), |
| + '-D_XOPEN_SOURCE=700', |
| + '-D_GNU_SOURCE=1', |
| ] |
| |
| +if js_engine == 'duktape' |
| + sources += files('polkitbackendduktapeauthority.c') |
| + deps += libm_dep |
| + deps += thread_dep |
| +elif js_engine == 'mozjs' |
| + sources += files('polkitbackendjsauthority.cpp') |
| +endif |
| + |
| if enable_logind |
| sources += files('polkitbackendsessionmonitor-systemd.c') |
| |
| diff --git a/src/polkitbackend/polkitbackendcommon.c b/src/polkitbackend/polkitbackendcommon.c |
| new file mode 100644 |
| index 0000000..6783dff |
| --- /dev/null |
| +++ b/src/polkitbackend/polkitbackendcommon.c |
| @@ -0,0 +1,530 @@ |
| +/* |
| + * Copyright (C) 2008 Red Hat, Inc. |
| + * |
| + * This library is free software; you can redistribute it and/or |
| + * modify it under the terms of the GNU Lesser General Public |
| + * License as published by the Free Software Foundation; either |
| + * version 2 of the License, or (at your option) any later version. |
| + * |
| + * This library is distributed in the hope that it will be useful, |
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| + * Lesser General Public License for more details. |
| + * |
| + * You should have received a copy of the GNU Lesser General |
| + * Public License along with this library; if not, write to the |
| + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, |
| + * Boston, MA 02111-1307, USA. |
| + * |
| + * Author: David Zeuthen <davidz@redhat.com> |
| + */ |
| + |
| +#include "polkitbackendcommon.h" |
| + |
| +static void |
| +utils_child_watch_from_release_cb (GPid pid, |
| + gint status, |
| + gpointer user_data) |
| +{ |
| +} |
| + |
| +static void |
| +utils_spawn_data_free (UtilsSpawnData *data) |
| +{ |
| + if (data->timeout_source != NULL) |
| + { |
| + g_source_destroy (data->timeout_source); |
| + data->timeout_source = NULL; |
| + } |
| + |
| + /* Nuke the child, if necessary */ |
| + if (data->child_watch_source != NULL) |
| + { |
| + g_source_destroy (data->child_watch_source); |
| + data->child_watch_source = NULL; |
| + } |
| + |
| + if (data->child_pid != 0) |
| + { |
| + GSource *source; |
| + kill (data->child_pid, SIGTERM); |
| + /* OK, we need to reap for the child ourselves - we don't want |
| + * to use waitpid() because that might block the calling |
| + * thread (the child might handle SIGTERM and use several |
| + * seconds for cleanup/rollback). |
| + * |
| + * So we use GChildWatch instead. |
| + * |
| + * Avoid taking a references to ourselves. but note that we need |
| + * to pass the GSource so we can nuke it once handled. |
| + */ |
| + source = g_child_watch_source_new (data->child_pid); |
| + g_source_set_callback (source, |
| + (GSourceFunc) utils_child_watch_from_release_cb, |
| + source, |
| + (GDestroyNotify) g_source_destroy); |
| + g_source_attach (source, data->main_context); |
| + g_source_unref (source); |
| + data->child_pid = 0; |
| + } |
| + |
| + if (data->child_stdout != NULL) |
| + { |
| + g_string_free (data->child_stdout, TRUE); |
| + data->child_stdout = NULL; |
| + } |
| + |
| + if (data->child_stderr != NULL) |
| + { |
| + g_string_free (data->child_stderr, TRUE); |
| + data->child_stderr = NULL; |
| + } |
| + |
| + if (data->child_stdout_channel != NULL) |
| + { |
| + g_io_channel_unref (data->child_stdout_channel); |
| + data->child_stdout_channel = NULL; |
| + } |
| + if (data->child_stderr_channel != NULL) |
| + { |
| + g_io_channel_unref (data->child_stderr_channel); |
| + data->child_stderr_channel = NULL; |
| + } |
| + |
| + if (data->child_stdout_source != NULL) |
| + { |
| + g_source_destroy (data->child_stdout_source); |
| + data->child_stdout_source = NULL; |
| + } |
| + if (data->child_stderr_source != NULL) |
| + { |
| + g_source_destroy (data->child_stderr_source); |
| + data->child_stderr_source = NULL; |
| + } |
| + |
| + if (data->child_stdout_fd != -1) |
| + { |
| + g_warn_if_fail (close (data->child_stdout_fd) == 0); |
| + data->child_stdout_fd = -1; |
| + } |
| + if (data->child_stderr_fd != -1) |
| + { |
| + g_warn_if_fail (close (data->child_stderr_fd) == 0); |
| + data->child_stderr_fd = -1; |
| + } |
| + |
| + if (data->cancellable_handler_id > 0) |
| + { |
| + g_cancellable_disconnect (data->cancellable, data->cancellable_handler_id); |
| + data->cancellable_handler_id = 0; |
| + } |
| + |
| + if (data->main_context != NULL) |
| + g_main_context_unref (data->main_context); |
| + |
| + if (data->cancellable != NULL) |
| + g_object_unref (data->cancellable); |
| + |
| + g_slice_free (UtilsSpawnData, data); |
| +} |
| + |
| +/* called in the thread where @cancellable was cancelled */ |
| +static void |
| +utils_on_cancelled (GCancellable *cancellable, |
| + gpointer user_data) |
| +{ |
| + UtilsSpawnData *data = (UtilsSpawnData *)user_data; |
| + GError *error; |
| + |
| + error = NULL; |
| + g_warn_if_fail (g_cancellable_set_error_if_cancelled (cancellable, &error)); |
| + g_simple_async_result_take_error (data->simple, error); |
| + g_simple_async_result_complete_in_idle (data->simple); |
| + g_object_unref (data->simple); |
| +} |
| + |
| +static gboolean |
| +utils_timeout_cb (gpointer user_data) |
| +{ |
| + UtilsSpawnData *data = (UtilsSpawnData *)user_data; |
| + |
| + data->timed_out = TRUE; |
| + |
| + /* ok, timeout is history, make sure we don't free it in spawn_data_free() */ |
| + data->timeout_source = NULL; |
| + |
| + /* we're done */ |
| + g_simple_async_result_complete_in_idle (data->simple); |
| + g_object_unref (data->simple); |
| + |
| + return FALSE; /* remove source */ |
| +} |
| + |
| +static void |
| +utils_child_watch_cb (GPid pid, |
| + gint status, |
| + gpointer user_data) |
| +{ |
| + UtilsSpawnData *data = (UtilsSpawnData *)user_data; |
| + gchar *buf; |
| + gsize buf_size; |
| + |
| + if (g_io_channel_read_to_end (data->child_stdout_channel, &buf, &buf_size, NULL) == G_IO_STATUS_NORMAL) |
| + { |
| + g_string_append_len (data->child_stdout, buf, buf_size); |
| + g_free (buf); |
| + } |
| + if (g_io_channel_read_to_end (data->child_stderr_channel, &buf, &buf_size, NULL) == G_IO_STATUS_NORMAL) |
| + { |
| + g_string_append_len (data->child_stderr, buf, buf_size); |
| + g_free (buf); |
| + } |
| + |
| + data->exit_status = status; |
| + |
| + /* ok, child watch is history, make sure we don't free it in spawn_data_free() */ |
| + data->child_pid = 0; |
| + data->child_watch_source = NULL; |
| + |
| + /* we're done */ |
| + g_simple_async_result_complete_in_idle (data->simple); |
| + g_object_unref (data->simple); |
| +} |
| + |
| +static gboolean |
| +utils_read_child_stderr (GIOChannel *channel, |
| + GIOCondition condition, |
| + gpointer user_data) |
| +{ |
| + UtilsSpawnData *data = (UtilsSpawnData *)user_data; |
| + gchar buf[1024]; |
| + gsize bytes_read; |
| + |
| + g_io_channel_read_chars (channel, buf, sizeof buf, &bytes_read, NULL); |
| + g_string_append_len (data->child_stderr, buf, bytes_read); |
| + return TRUE; |
| +} |
| + |
| +static gboolean |
| +utils_read_child_stdout (GIOChannel *channel, |
| + GIOCondition condition, |
| + gpointer user_data) |
| +{ |
| + UtilsSpawnData *data = (UtilsSpawnData *)user_data; |
| + gchar buf[1024]; |
| + gsize bytes_read; |
| + |
| + g_io_channel_read_chars (channel, buf, sizeof buf, &bytes_read, NULL); |
| + g_string_append_len (data->child_stdout, buf, bytes_read); |
| + return TRUE; |
| +} |
| + |
| +void |
| +polkit_backend_common_spawn (const gchar *const *argv, |
| + guint timeout_seconds, |
| + GCancellable *cancellable, |
| + GAsyncReadyCallback callback, |
| + gpointer user_data) |
| +{ |
| + UtilsSpawnData *data; |
| + GError *error; |
| + |
| + data = g_slice_new0 (UtilsSpawnData); |
| + data->timeout_seconds = timeout_seconds; |
| + data->simple = g_simple_async_result_new (NULL, |
| + callback, |
| + user_data, |
| + (gpointer*)polkit_backend_common_spawn); |
| + data->main_context = g_main_context_get_thread_default (); |
| + if (data->main_context != NULL) |
| + g_main_context_ref (data->main_context); |
| + |
| + data->cancellable = cancellable != NULL ? (GCancellable*)g_object_ref (cancellable) : NULL; |
| + |
| + data->child_stdout = g_string_new (NULL); |
| + data->child_stderr = g_string_new (NULL); |
| + data->child_stdout_fd = -1; |
| + data->child_stderr_fd = -1; |
| + |
| + /* the life-cycle of UtilsSpawnData is tied to its GSimpleAsyncResult */ |
| + g_simple_async_result_set_op_res_gpointer (data->simple, data, (GDestroyNotify) utils_spawn_data_free); |
| + |
| + error = NULL; |
| + if (data->cancellable != NULL) |
| + { |
| + /* could already be cancelled */ |
| + error = NULL; |
| + if (g_cancellable_set_error_if_cancelled (data->cancellable, &error)) |
| + { |
| + g_simple_async_result_take_error (data->simple, error); |
| + g_simple_async_result_complete_in_idle (data->simple); |
| + g_object_unref (data->simple); |
| + goto out; |
| + } |
| + |
| + data->cancellable_handler_id = g_cancellable_connect (data->cancellable, |
| + G_CALLBACK (utils_on_cancelled), |
| + data, |
| + NULL); |
| + } |
| + |
| + error = NULL; |
| + if (!g_spawn_async_with_pipes (NULL, /* working directory */ |
| + (gchar **) argv, |
| + NULL, /* envp */ |
| + G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD, |
| + NULL, /* child_setup */ |
| + NULL, /* child_setup's user_data */ |
| + &(data->child_pid), |
| + NULL, /* gint *stdin_fd */ |
| + &(data->child_stdout_fd), |
| + &(data->child_stderr_fd), |
| + &error)) |
| + { |
| + g_prefix_error (&error, "Error spawning: "); |
| + g_simple_async_result_take_error (data->simple, error); |
| + g_simple_async_result_complete_in_idle (data->simple); |
| + g_object_unref (data->simple); |
| + goto out; |
| + } |
| + |
| + if (timeout_seconds > 0) |
| + { |
| + data->timeout_source = g_timeout_source_new_seconds (timeout_seconds); |
| + g_source_set_priority (data->timeout_source, G_PRIORITY_DEFAULT); |
| + g_source_set_callback (data->timeout_source, utils_timeout_cb, data, NULL); |
| + g_source_attach (data->timeout_source, data->main_context); |
| + g_source_unref (data->timeout_source); |
| + } |
| + |
| + data->child_watch_source = g_child_watch_source_new (data->child_pid); |
| + g_source_set_callback (data->child_watch_source, (GSourceFunc) utils_child_watch_cb, data, NULL); |
| + g_source_attach (data->child_watch_source, data->main_context); |
| + g_source_unref (data->child_watch_source); |
| + |
| + data->child_stdout_channel = g_io_channel_unix_new (data->child_stdout_fd); |
| + g_io_channel_set_flags (data->child_stdout_channel, G_IO_FLAG_NONBLOCK, NULL); |
| + data->child_stdout_source = g_io_create_watch (data->child_stdout_channel, G_IO_IN); |
| + g_source_set_callback (data->child_stdout_source, (GSourceFunc) utils_read_child_stdout, data, NULL); |
| + g_source_attach (data->child_stdout_source, data->main_context); |
| + g_source_unref (data->child_stdout_source); |
| + |
| + data->child_stderr_channel = g_io_channel_unix_new (data->child_stderr_fd); |
| + g_io_channel_set_flags (data->child_stderr_channel, G_IO_FLAG_NONBLOCK, NULL); |
| + data->child_stderr_source = g_io_create_watch (data->child_stderr_channel, G_IO_IN); |
| + g_source_set_callback (data->child_stderr_source, (GSourceFunc) utils_read_child_stderr, data, NULL); |
| + g_source_attach (data->child_stderr_source, data->main_context); |
| + g_source_unref (data->child_stderr_source); |
| + |
| + out: |
| + ; |
| +} |
| + |
| +void |
| +polkit_backend_common_on_dir_monitor_changed (GFileMonitor *monitor, |
| + GFile *file, |
| + GFile *other_file, |
| + GFileMonitorEvent event_type, |
| + gpointer user_data) |
| +{ |
| + PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (user_data); |
| + |
| + /* TODO: maybe rate-limit so storms of events are collapsed into one with a 500ms resolution? |
| + * Because when editing a file with emacs we get 4-8 events.. |
| + */ |
| + |
| + if (file != NULL) |
| + { |
| + gchar *name; |
| + |
| + name = g_file_get_basename (file); |
| + |
| + /* g_print ("event_type=%d file=%p name=%s\n", event_type, file, name); */ |
| + if (!g_str_has_prefix (name, ".") && |
| + !g_str_has_prefix (name, "#") && |
| + g_str_has_suffix (name, ".rules") && |
| + (event_type == G_FILE_MONITOR_EVENT_CREATED || |
| + event_type == G_FILE_MONITOR_EVENT_DELETED || |
| + event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)) |
| + { |
| + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), |
| + "Reloading rules"); |
| + polkit_backend_common_reload_scripts (authority); |
| + } |
| + g_free (name); |
| + } |
| +} |
| + |
| +gboolean |
| +polkit_backend_common_spawn_finish (GAsyncResult *res, |
| + gint *out_exit_status, |
| + gchar **out_standard_output, |
| + gchar **out_standard_error, |
| + GError **error) |
| +{ |
| + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res); |
| + UtilsSpawnData *data; |
| + gboolean ret = FALSE; |
| + |
| + g_return_val_if_fail (G_IS_ASYNC_RESULT (res), FALSE); |
| + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); |
| + |
| + g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == polkit_backend_common_spawn); |
| + |
| + if (g_simple_async_result_propagate_error (simple, error)) |
| + goto out; |
| + |
| + data = (UtilsSpawnData*)g_simple_async_result_get_op_res_gpointer (simple); |
| + |
| + if (data->timed_out) |
| + { |
| + g_set_error (error, |
| + G_IO_ERROR, |
| + G_IO_ERROR_TIMED_OUT, |
| + "Timed out after %d seconds", |
| + data->timeout_seconds); |
| + goto out; |
| + } |
| + |
| + if (out_exit_status != NULL) |
| + *out_exit_status = data->exit_status; |
| + |
| + if (out_standard_output != NULL) |
| + *out_standard_output = g_strdup (data->child_stdout->str); |
| + |
| + if (out_standard_error != NULL) |
| + *out_standard_error = g_strdup (data->child_stderr->str); |
| + |
| + ret = TRUE; |
| + |
| + out: |
| + return ret; |
| +} |
| + |
| +static const gchar * |
| +polkit_backend_js_authority_get_name (PolkitBackendAuthority *authority) |
| +{ |
| + return "js"; |
| +} |
| + |
| +static const gchar * |
| +polkit_backend_js_authority_get_version (PolkitBackendAuthority *authority) |
| +{ |
| + return PACKAGE_VERSION; |
| +} |
| + |
| +static PolkitAuthorityFeatures |
| +polkit_backend_js_authority_get_features (PolkitBackendAuthority *authority) |
| +{ |
| + return POLKIT_AUTHORITY_FEATURES_TEMPORARY_AUTHORIZATION; |
| +} |
| + |
| +void |
| +polkit_backend_common_js_authority_class_init_common (PolkitBackendJsAuthorityClass *klass) |
| +{ |
| + GObjectClass *gobject_class; |
| + PolkitBackendAuthorityClass *authority_class; |
| + PolkitBackendInteractiveAuthorityClass *interactive_authority_class; |
| + |
| + gobject_class = G_OBJECT_CLASS (klass); |
| + gobject_class->finalize = polkit_backend_common_js_authority_finalize; |
| + gobject_class->set_property = polkit_backend_common_js_authority_set_property; |
| + gobject_class->constructed = polkit_backend_common_js_authority_constructed; |
| + |
| + authority_class = POLKIT_BACKEND_AUTHORITY_CLASS (klass); |
| + authority_class->get_name = polkit_backend_js_authority_get_name; |
| + authority_class->get_version = polkit_backend_js_authority_get_version; |
| + authority_class->get_features = polkit_backend_js_authority_get_features; |
| + |
| + interactive_authority_class = POLKIT_BACKEND_INTERACTIVE_AUTHORITY_CLASS (klass); |
| + interactive_authority_class->get_admin_identities = polkit_backend_common_js_authority_get_admin_auth_identities; |
| + interactive_authority_class->check_authorization_sync = polkit_backend_common_js_authority_check_authorization_sync; |
| + |
| + g_object_class_install_property (gobject_class, |
| + PROP_RULES_DIRS, |
| + g_param_spec_boxed ("rules-dirs", |
| + NULL, |
| + NULL, |
| + G_TYPE_STRV, |
| + G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE)); |
| +} |
| + |
| +gint |
| +polkit_backend_common_rules_file_name_cmp (const gchar *a, |
| + const gchar *b) |
| +{ |
| + gint ret; |
| + const gchar *a_base; |
| + const gchar *b_base; |
| + |
| + a_base = strrchr (a, '/'); |
| + b_base = strrchr (b, '/'); |
| + |
| + g_assert (a_base != NULL); |
| + g_assert (b_base != NULL); |
| + a_base += 1; |
| + b_base += 1; |
| + |
| + ret = g_strcmp0 (a_base, b_base); |
| + if (ret == 0) |
| + { |
| + /* /etc wins over /usr */ |
| + ret = g_strcmp0 (a, b); |
| + g_assert (ret != 0); |
| + } |
| + |
| + return ret; |
| +} |
| + |
| +const gchar * |
| +polkit_backend_common_get_signal_name (gint signal_number) |
| +{ |
| + switch (signal_number) |
| + { |
| +#define _HANDLE_SIG(sig) case sig: return #sig; |
| + _HANDLE_SIG (SIGHUP); |
| + _HANDLE_SIG (SIGINT); |
| + _HANDLE_SIG (SIGQUIT); |
| + _HANDLE_SIG (SIGILL); |
| + _HANDLE_SIG (SIGABRT); |
| + _HANDLE_SIG (SIGFPE); |
| + _HANDLE_SIG (SIGKILL); |
| + _HANDLE_SIG (SIGSEGV); |
| + _HANDLE_SIG (SIGPIPE); |
| + _HANDLE_SIG (SIGALRM); |
| + _HANDLE_SIG (SIGTERM); |
| + _HANDLE_SIG (SIGUSR1); |
| + _HANDLE_SIG (SIGUSR2); |
| + _HANDLE_SIG (SIGCHLD); |
| + _HANDLE_SIG (SIGCONT); |
| + _HANDLE_SIG (SIGSTOP); |
| + _HANDLE_SIG (SIGTSTP); |
| + _HANDLE_SIG (SIGTTIN); |
| + _HANDLE_SIG (SIGTTOU); |
| + _HANDLE_SIG (SIGBUS); |
| +#ifdef SIGPOLL |
| + _HANDLE_SIG (SIGPOLL); |
| +#endif |
| + _HANDLE_SIG (SIGPROF); |
| + _HANDLE_SIG (SIGSYS); |
| + _HANDLE_SIG (SIGTRAP); |
| + _HANDLE_SIG (SIGURG); |
| + _HANDLE_SIG (SIGVTALRM); |
| + _HANDLE_SIG (SIGXCPU); |
| + _HANDLE_SIG (SIGXFSZ); |
| +#undef _HANDLE_SIG |
| + default: |
| + break; |
| + } |
| + return "UNKNOWN_SIGNAL"; |
| +} |
| + |
| +void |
| +polkit_backend_common_spawn_cb (GObject *source_object, |
| + GAsyncResult *res, |
| + gpointer user_data) |
| +{ |
| + SpawnData *data = (SpawnData *)user_data; |
| + data->res = (GAsyncResult*)g_object_ref (res); |
| + g_main_loop_quit (data->loop); |
| +} |
| diff --git a/src/polkitbackend/polkitbackendcommon.h b/src/polkitbackend/polkitbackendcommon.h |
| new file mode 100644 |
| index 0000000..dd700fc |
| --- /dev/null |
| +++ b/src/polkitbackend/polkitbackendcommon.h |
| @@ -0,0 +1,158 @@ |
| +/* |
| + * Copyright (C) 2008 Red Hat, Inc. |
| + * |
| + * This library is free software; you can redistribute it and/or |
| + * modify it under the terms of the GNU Lesser General Public |
| + * License as published by the Free Software Foundation; either |
| + * version 2 of the License, or (at your option) any later version. |
| + * |
| + * This library is distributed in the hope that it will be useful, |
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| + * Lesser General Public License for more details. |
| + * |
| + * You should have received a copy of the GNU Lesser General |
| + * Public License along with this library; if not, write to the |
| + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, |
| + * Boston, MA 02111-1307, USA. |
| + * |
| + * Author: David Zeuthen <davidz@redhat.com> |
| + */ |
| + |
| +#if !defined (_POLKIT_BACKEND_COMPILATION) && !defined(_POLKIT_BACKEND_INSIDE_POLKIT_BACKEND_H) |
| +#error "Only <polkitbackend/polkitbackend.h> can be included directly, this file may disappear or change contents." |
| +#endif |
| + |
| +#ifndef __POLKIT_BACKEND_COMMON_H |
| +#define __POLKIT_BACKEND_COMMON_H |
| + |
| +#include "config.h" |
| +#include <sys/wait.h> |
| +#include <errno.h> |
| +#include <pwd.h> |
| +#include <grp.h> |
| +#ifdef HAVE_NETGROUP_H |
| +#include <netgroup.h> |
| +#else |
| +#include <netdb.h> |
| +#endif |
| +#include <string.h> |
| +#include <glib/gstdio.h> |
| +#include <locale.h> |
| +#include <glib/gi18n-lib.h> //here, all things glib via glib.h (including -> gspawn.h) |
| + |
| +#include <polkit/polkit.h> |
| +#include "polkitbackendjsauthority.h" |
| + |
| +#include <polkit/polkitprivate.h> |
| + |
| +#ifdef HAVE_LIBSYSTEMD |
| +#include <systemd/sd-login.h> |
| +#endif /* HAVE_LIBSYSTEMD */ |
| + |
| +#define RUNAWAY_KILLER_TIMEOUT (15) |
| + |
| +#ifdef __cplusplus |
| +extern "C" { |
| +#endif |
| + |
| +enum |
| +{ |
| + PROP_0, |
| + PROP_RULES_DIRS, |
| +}; |
| + |
| +typedef struct |
| +{ |
| + GSimpleAsyncResult *simple; /* borrowed reference */ |
| + GMainContext *main_context; /* may be NULL */ |
| + |
| + GCancellable *cancellable; /* may be NULL */ |
| + gulong cancellable_handler_id; |
| + |
| + GPid child_pid; |
| + gint child_stdout_fd; |
| + gint child_stderr_fd; |
| + |
| + GIOChannel *child_stdout_channel; |
| + GIOChannel *child_stderr_channel; |
| + |
| + GSource *child_watch_source; |
| + GSource *child_stdout_source; |
| + GSource *child_stderr_source; |
| + |
| + guint timeout_seconds; |
| + gboolean timed_out; |
| + GSource *timeout_source; |
| + |
| + GString *child_stdout; |
| + GString *child_stderr; |
| + |
| + gint exit_status; |
| +} UtilsSpawnData; |
| + |
| +typedef struct |
| +{ |
| + GMainLoop *loop; |
| + GAsyncResult *res; |
| +} SpawnData; |
| + |
| +void polkit_backend_common_spawn (const gchar *const *argv, |
| + guint timeout_seconds, |
| + GCancellable *cancellable, |
| + GAsyncReadyCallback callback, |
| + gpointer user_data); |
| +void polkit_backend_common_spawn_cb (GObject *source_object, |
| + GAsyncResult *res, |
| + gpointer user_data); |
| +gboolean polkit_backend_common_spawn_finish (GAsyncResult *res, |
| + gint *out_exit_status, |
| + gchar **out_standard_output, |
| + gchar **out_standard_error, |
| + GError **error); |
| + |
| +void polkit_backend_common_on_dir_monitor_changed (GFileMonitor *monitor, |
| + GFile *file, |
| + GFile *other_file, |
| + GFileMonitorEvent event_type, |
| + gpointer user_data); |
| + |
| +void polkit_backend_common_js_authority_class_init_common (PolkitBackendJsAuthorityClass *klass); |
| + |
| +gint polkit_backend_common_rules_file_name_cmp (const gchar *a, |
| + const gchar *b); |
| + |
| +const gchar *polkit_backend_common_get_signal_name (gint signal_number); |
| + |
| +/* To be provided by each JS backend, from here onwards ---------------------------------------------- */ |
| + |
| +void polkit_backend_common_reload_scripts (PolkitBackendJsAuthority *authority); |
| +void polkit_backend_common_js_authority_finalize (GObject *object); |
| +void polkit_backend_common_js_authority_constructed (GObject *object); |
| +GList *polkit_backend_common_js_authority_get_admin_auth_identities (PolkitBackendInteractiveAuthority *_authority, |
| + PolkitSubject *caller, |
| + PolkitSubject *subject, |
| + PolkitIdentity *user_for_subject, |
| + gboolean subject_is_local, |
| + gboolean subject_is_active, |
| + const gchar *action_id, |
| + PolkitDetails *details); |
| +void polkit_backend_common_js_authority_set_property (GObject *object, |
| + guint property_id, |
| + const GValue *value, |
| + GParamSpec *pspec); |
| +PolkitImplicitAuthorization polkit_backend_common_js_authority_check_authorization_sync (PolkitBackendInteractiveAuthority *_authority, |
| + PolkitSubject *caller, |
| + PolkitSubject *subject, |
| + PolkitIdentity *user_for_subject, |
| + gboolean subject_is_local, |
| + gboolean subject_is_active, |
| + const gchar *action_id, |
| + PolkitDetails *details, |
| + PolkitImplicitAuthorization implicit); |
| +#ifdef __cplusplus |
| +} |
| +#endif |
| + |
| +#endif /* __POLKIT_BACKEND_COMMON_H */ |
| + |
| diff --git a/src/polkitbackend/polkitbackendduktapeauthority.c b/src/polkitbackend/polkitbackendduktapeauthority.c |
| new file mode 100644 |
| index 0000000..c89dbcf |
| --- /dev/null |
| +++ b/src/polkitbackend/polkitbackendduktapeauthority.c |
| @@ -0,0 +1,1051 @@ |
| +/* |
| + * Copyright (C) 2008-2012 Red Hat, Inc. |
| + * Copyright (C) 2015 Tangent Space <jstpierre@mecheye.net> |
| + * Copyright (C) 2019 Wu Xiaotian <yetist@gmail.com> |
| + * |
| + * This library is free software; you can redistribute it and/or |
| + * modify it under the terms of the GNU Lesser General Public |
| + * License as published by the Free Software Foundation; either |
| + * version 2 of the License, or (at your option) any later version. |
| + * |
| + * This library is distributed in the hope that it will be useful, |
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| + * Lesser General Public License for more details. |
| + * |
| + * You should have received a copy of the GNU Lesser General |
| + * Public License along with this library; if not, write to the |
| + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, |
| + * Boston, MA 02111-1307, USA. |
| + * |
| + * Author: David Zeuthen <davidz@redhat.com> |
| + */ |
| + |
| +#include <pthread.h> |
| + |
| +#include "polkitbackendcommon.h" |
| + |
| +#include "duktape.h" |
| + |
| +/* Built source and not too big to worry about deduplication */ |
| +#include "initjs.h" /* init.js */ |
| + |
| +/** |
| + * SECTION:polkitbackendjsauthority |
| + * @title: PolkitBackendJsAuthority |
| + * @short_description: JS Authority |
| + * @stability: Unstable |
| + * |
| + * An (Duktape-based) implementation of #PolkitBackendAuthority that reads and |
| + * evaluates Javascript files and supports interaction with authentication |
| + * agents (virtue of being based on #PolkitBackendInteractiveAuthority). |
| + */ |
| + |
| +/* ---------------------------------------------------------------------------------------------------- */ |
| + |
| +struct _PolkitBackendJsAuthorityPrivate |
| +{ |
| + gchar **rules_dirs; |
| + GFileMonitor **dir_monitors; /* NULL-terminated array of GFileMonitor instances */ |
| + |
| + duk_context *cx; |
| + |
| + pthread_t runaway_killer_thread; |
| +}; |
| + |
| +enum |
| +{ |
| + RUNAWAY_KILLER_THREAD_EXIT_STATUS_UNSET, |
| + RUNAWAY_KILLER_THREAD_EXIT_STATUS_SUCCESS, |
| + RUNAWAY_KILLER_THREAD_EXIT_STATUS_FAILURE, |
| +}; |
| + |
| +static gboolean execute_script_with_runaway_killer(PolkitBackendJsAuthority *authority, |
| + const gchar *filename); |
| + |
| +/* ---------------------------------------------------------------------------------------------------- */ |
| + |
| +G_DEFINE_TYPE (PolkitBackendJsAuthority, polkit_backend_js_authority, POLKIT_BACKEND_TYPE_INTERACTIVE_AUTHORITY); |
| + |
| +/* ---------------------------------------------------------------------------------------------------- */ |
| + |
| +static duk_ret_t js_polkit_log (duk_context *cx); |
| +static duk_ret_t js_polkit_spawn (duk_context *cx); |
| +static duk_ret_t js_polkit_user_is_in_netgroup (duk_context *cx); |
| + |
| +static const duk_function_list_entry js_polkit_functions[] = |
| +{ |
| + { "log", js_polkit_log, 1 }, |
| + { "spawn", js_polkit_spawn, 1 }, |
| + { "_userIsInNetGroup", js_polkit_user_is_in_netgroup, 2 }, |
| + { NULL, NULL, 0 }, |
| +}; |
| + |
| +static void report_error (void *udata, |
| + const char *msg) |
| +{ |
| + PolkitBackendJsAuthority *authority = udata; |
| + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), |
| + "fatal Duktape JS backend error: %s", |
| + (msg ? msg : "no message")); |
| +} |
| + |
| +static void |
| +polkit_backend_js_authority_init (PolkitBackendJsAuthority *authority) |
| +{ |
| + authority->priv = G_TYPE_INSTANCE_GET_PRIVATE (authority, |
| + POLKIT_BACKEND_TYPE_JS_AUTHORITY, |
| + PolkitBackendJsAuthorityPrivate); |
| +} |
| + |
| +static void |
| +load_scripts (PolkitBackendJsAuthority *authority) |
| +{ |
| + GList *files = NULL; |
| + GList *l; |
| + guint num_scripts = 0; |
| + GError *error = NULL; |
| + guint n; |
| + |
| + files = NULL; |
| + |
| + for (n = 0; authority->priv->rules_dirs != NULL && authority->priv->rules_dirs[n] != NULL; n++) |
| + { |
| + const gchar *dir_name = authority->priv->rules_dirs[n]; |
| + GDir *dir = NULL; |
| + |
| + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), |
| + "Loading rules from directory %s", |
| + dir_name); |
| + |
| + dir = g_dir_open (dir_name, |
| + 0, |
| + &error); |
| + if (dir == NULL) |
| + { |
| + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), |
| + "Error opening rules directory: %s (%s, %d)", |
| + error->message, g_quark_to_string (error->domain), error->code); |
| + g_clear_error (&error); |
| + } |
| + else |
| + { |
| + const gchar *name; |
| + while ((name = g_dir_read_name (dir)) != NULL) |
| + { |
| + if (g_str_has_suffix (name, ".rules")) |
| + files = g_list_prepend (files, g_strdup_printf ("%s/%s", dir_name, name)); |
| + } |
| + g_dir_close (dir); |
| + } |
| + } |
| + |
| + files = g_list_sort (files, (GCompareFunc) polkit_backend_common_rules_file_name_cmp); |
| + |
| + for (l = files; l != NULL; l = l->next) |
| + { |
| + const gchar *filename = (gchar *)l->data; |
| + |
| + if (!execute_script_with_runaway_killer(authority, filename)) |
| + continue; |
| + num_scripts++; |
| + } |
| + |
| + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), |
| + "Finished loading, compiling and executing %d rules", |
| + num_scripts); |
| + g_list_free_full (files, g_free); |
| +} |
| + |
| +void |
| +polkit_backend_common_reload_scripts (PolkitBackendJsAuthority *authority) |
| +{ |
| + duk_context *cx = authority->priv->cx; |
| + |
| + duk_set_top (cx, 0); |
| + if (!duk_get_global_string (cx, "polkit")) { |
| + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), |
| + "Error deleting old rules, not loading new ones"); |
| + return; |
| + } |
| + duk_push_string (cx, "_deleteRules"); |
| + |
| + duk_call_prop (cx, 0, 0); |
| + |
| + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), |
| + "Collecting garbage unconditionally..."); |
| + |
| + load_scripts (authority); |
| + |
| + /* Let applications know we have new rules... */ |
| + g_signal_emit_by_name (authority, "changed"); |
| +} |
| + |
| +static void |
| +setup_file_monitors (PolkitBackendJsAuthority *authority) |
| +{ |
| + guint n; |
| + GPtrArray *p; |
| + |
| + p = g_ptr_array_new (); |
| + for (n = 0; authority->priv->rules_dirs != NULL && authority->priv->rules_dirs[n] != NULL; n++) |
| + { |
| + GFile *file; |
| + GError *error; |
| + GFileMonitor *monitor; |
| + |
| + file = g_file_new_for_path (authority->priv->rules_dirs[n]); |
| + error = NULL; |
| + monitor = g_file_monitor_directory (file, |
| + G_FILE_MONITOR_NONE, |
| + NULL, |
| + &error); |
| + g_object_unref (file); |
| + if (monitor == NULL) |
| + { |
| + g_warning ("Error monitoring directory %s: %s", |
| + authority->priv->rules_dirs[n], |
| + error->message); |
| + g_clear_error (&error); |
| + } |
| + else |
| + { |
| + g_signal_connect (monitor, |
| + "changed", |
| + G_CALLBACK (polkit_backend_common_on_dir_monitor_changed), |
| + authority); |
| + g_ptr_array_add (p, monitor); |
| + } |
| + } |
| + g_ptr_array_add (p, NULL); |
| + authority->priv->dir_monitors = (GFileMonitor**) g_ptr_array_free (p, FALSE); |
| +} |
| + |
| +void |
| +polkit_backend_common_js_authority_constructed (GObject *object) |
| +{ |
| + PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (object); |
| + duk_context *cx; |
| + |
| + cx = duk_create_heap (NULL, NULL, NULL, authority, report_error); |
| + if (cx == NULL) |
| + goto fail; |
| + |
| + authority->priv->cx = cx; |
| + |
| + duk_push_global_object (cx); |
| + duk_push_object (cx); |
| + duk_put_function_list (cx, -1, js_polkit_functions); |
| + duk_put_prop_string (cx, -2, "polkit"); |
| + |
| + /* load polkit objects/functions into JS context (e.g. addRule(), |
| + * _deleteRules(), _runRules() et al) |
| + */ |
| + duk_eval_string (cx, init_js); |
| + |
| + if (authority->priv->rules_dirs == NULL) |
| + { |
| + authority->priv->rules_dirs = g_new0 (gchar *, 3); |
| + authority->priv->rules_dirs[0] = g_strdup (PACKAGE_SYSCONF_DIR "/polkit-1/rules.d"); |
| + authority->priv->rules_dirs[1] = g_strdup (PACKAGE_DATA_DIR "/polkit-1/rules.d"); |
| + } |
| + |
| + setup_file_monitors (authority); |
| + load_scripts (authority); |
| + |
| + G_OBJECT_CLASS (polkit_backend_js_authority_parent_class)->constructed (object); |
| + return; |
| + |
| + fail: |
| + g_critical ("Error initializing JavaScript environment"); |
| + g_assert_not_reached (); |
| +} |
| + |
| +void |
| +polkit_backend_common_js_authority_finalize (GObject *object) |
| +{ |
| + PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (object); |
| + guint n; |
| + |
| + for (n = 0; authority->priv->dir_monitors != NULL && authority->priv->dir_monitors[n] != NULL; n++) |
| + { |
| + GFileMonitor *monitor = authority->priv->dir_monitors[n]; |
| + g_signal_handlers_disconnect_by_func (monitor, |
| + G_CALLBACK (polkit_backend_common_on_dir_monitor_changed), |
| + authority); |
| + g_object_unref (monitor); |
| + } |
| + g_free (authority->priv->dir_monitors); |
| + g_strfreev (authority->priv->rules_dirs); |
| + |
| + duk_destroy_heap (authority->priv->cx); |
| + |
| + G_OBJECT_CLASS (polkit_backend_js_authority_parent_class)->finalize (object); |
| +} |
| + |
| +void |
| +polkit_backend_common_js_authority_set_property (GObject *object, |
| + guint property_id, |
| + const GValue *value, |
| + GParamSpec *pspec) |
| +{ |
| + PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (object); |
| + |
| + switch (property_id) |
| + { |
| + case PROP_RULES_DIRS: |
| + g_assert (authority->priv->rules_dirs == NULL); |
| + authority->priv->rules_dirs = (gchar **) g_value_dup_boxed (value); |
| + break; |
| + |
| + default: |
| + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); |
| + break; |
| + } |
| +} |
| + |
| +static void |
| +polkit_backend_js_authority_class_init (PolkitBackendJsAuthorityClass *klass) |
| +{ |
| + polkit_backend_common_js_authority_class_init_common (klass); |
| + g_type_class_add_private (klass, sizeof (PolkitBackendJsAuthorityPrivate)); |
| +} |
| + |
| +/* ---------------------------------------------------------------------------------------------------- */ |
| + |
| +static void |
| +set_property_str (duk_context *cx, |
| + const gchar *name, |
| + const gchar *value) |
| +{ |
| + duk_push_string (cx, value); |
| + duk_put_prop_string (cx, -2, name); |
| +} |
| + |
| +static void |
| +set_property_strv (duk_context *cx, |
| + const gchar *name, |
| + GPtrArray *value) |
| +{ |
| + guint n; |
| + duk_push_array (cx); |
| + for (n = 0; n < value->len; n++) |
| + { |
| + duk_push_string (cx, g_ptr_array_index (value, n)); |
| + duk_put_prop_index (cx, -2, n); |
| + } |
| + duk_put_prop_string (cx, -2, name); |
| +} |
| + |
| +static void |
| +set_property_int32 (duk_context *cx, |
| + const gchar *name, |
| + gint32 value) |
| +{ |
| + duk_push_int (cx, value); |
| + duk_put_prop_string (cx, -2, name); |
| +} |
| + |
| +static void |
| +set_property_bool (duk_context *cx, |
| + const char *name, |
| + gboolean value) |
| +{ |
| + duk_push_boolean (cx, value); |
| + duk_put_prop_string (cx, -2, name); |
| +} |
| + |
| +/* ---------------------------------------------------------------------------------------------------- */ |
| + |
| +static gboolean |
| +push_subject (duk_context *cx, |
| + PolkitSubject *subject, |
| + PolkitIdentity *user_for_subject, |
| + gboolean subject_is_local, |
| + gboolean subject_is_active, |
| + GError **error) |
| +{ |
| + gboolean ret = FALSE; |
| + pid_t pid; |
| + uid_t uid; |
| + gchar *user_name = NULL; |
| + GPtrArray *groups = NULL; |
| + struct passwd *passwd; |
| + char *seat_str = NULL; |
| + char *session_str = NULL; |
| + |
| + if (!duk_get_global_string (cx, "Subject")) { |
| + return FALSE; |
| + } |
| + |
| + duk_new (cx, 0); |
| + |
| + if (POLKIT_IS_UNIX_PROCESS (subject)) |
| + { |
| + pid = polkit_unix_process_get_pid (POLKIT_UNIX_PROCESS (subject)); |
| + } |
| + else if (POLKIT_IS_SYSTEM_BUS_NAME (subject)) |
| + { |
| + PolkitSubject *process; |
| + process = polkit_system_bus_name_get_process_sync (POLKIT_SYSTEM_BUS_NAME (subject), NULL, error); |
| + if (process == NULL) |
| + goto out; |
| + pid = polkit_unix_process_get_pid (POLKIT_UNIX_PROCESS (process)); |
| + g_object_unref (process); |
| + } |
| + else |
| + { |
| + g_assert_not_reached (); |
| + } |
| + |
| +#ifdef HAVE_LIBSYSTEMD |
| + if (sd_pid_get_session (pid, &session_str) == 0) |
| + { |
| + if (sd_session_get_seat (session_str, &seat_str) == 0) |
| + { |
| + /* do nothing */ |
| + } |
| + } |
| +#endif /* HAVE_LIBSYSTEMD */ |
| + |
| + g_assert (POLKIT_IS_UNIX_USER (user_for_subject)); |
| + uid = polkit_unix_user_get_uid (POLKIT_UNIX_USER (user_for_subject)); |
| + |
| + groups = g_ptr_array_new_with_free_func (g_free); |
| + |
| + passwd = getpwuid (uid); |
| + if (passwd == NULL) |
| + { |
| + user_name = g_strdup_printf ("%d", (gint) uid); |
| + g_warning ("Error looking up info for uid %d: %m", (gint) uid); |
| + } |
| + else |
| + { |
| + gid_t gids[512]; |
| + int num_gids = 512; |
| + |
| + user_name = g_strdup (passwd->pw_name); |
| + |
| + if (getgrouplist (passwd->pw_name, |
| + passwd->pw_gid, |
| + gids, |
| + &num_gids) < 0) |
| + { |
| + g_warning ("Error looking up groups for uid %d: %m", (gint) uid); |
| + } |
| + else |
| + { |
| + gint n; |
| + for (n = 0; n < num_gids; n++) |
| + { |
| + struct group *group; |
| + group = getgrgid (gids[n]); |
| + if (group == NULL) |
| + { |
| + g_ptr_array_add (groups, g_strdup_printf ("%d", (gint) gids[n])); |
| + } |
| + else |
| + { |
| + g_ptr_array_add (groups, g_strdup (group->gr_name)); |
| + } |
| + } |
| + } |
| + } |
| + |
| + set_property_int32 (cx, "pid", pid); |
| + set_property_str (cx, "user", user_name); |
| + set_property_strv (cx, "groups", groups); |
| + set_property_str (cx, "seat", seat_str); |
| + set_property_str (cx, "session", session_str); |
| + set_property_bool (cx, "local", subject_is_local); |
| + set_property_bool (cx, "active", subject_is_active); |
| + |
| + ret = TRUE; |
| + |
| + out: |
| + free (session_str); |
| + free (seat_str); |
| + g_free (user_name); |
| + if (groups != NULL) |
| + g_ptr_array_unref (groups); |
| + |
| + return ret; |
| +} |
| + |
| +/* ---------------------------------------------------------------------------------------------------- */ |
| + |
| +static gboolean |
| +push_action_and_details (duk_context *cx, |
| + const gchar *action_id, |
| + PolkitDetails *details, |
| + GError **error) |
| +{ |
| + gchar **keys; |
| + guint n; |
| + |
| + if (!duk_get_global_string (cx, "Action")) { |
| + return FALSE; |
| + } |
| + |
| + duk_new (cx, 0); |
| + |
| + set_property_str (cx, "id", action_id); |
| + |
| + keys = polkit_details_get_keys (details); |
| + for (n = 0; keys != NULL && keys[n] != NULL; n++) |
| + { |
| + gchar *key; |
| + const gchar *value; |
| + key = g_strdup_printf ("_detail_%s", keys[n]); |
| + value = polkit_details_lookup (details, keys[n]); |
| + set_property_str (cx, key, value); |
| + g_free (key); |
| + } |
| + g_strfreev (keys); |
| + |
| + return TRUE; |
| +} |
| + |
| +/* ---------------------------------------------------------------------------------------------------- */ |
| + |
| +typedef struct { |
| + PolkitBackendJsAuthority *authority; |
| + const gchar *filename; |
| + pthread_cond_t cond; |
| + pthread_mutex_t mutex; |
| + gint ret; |
| +} RunawayKillerCtx; |
| + |
| +static gpointer |
| +runaway_killer_thread_execute_js (gpointer user_data) |
| +{ |
| + RunawayKillerCtx *ctx = user_data; |
| + duk_context *cx = ctx->authority->priv->cx; |
| + |
| + int oldtype, pthread_err; |
| + |
| + if ((pthread_err = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype))) { |
| + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (ctx->authority), |
| + "Error setting thread cancel type: %s", |
| + strerror(pthread_err)); |
| + goto err; |
| + } |
| + |
| + GFile *file = g_file_new_for_path(ctx->filename); |
| + char *contents; |
| + gsize len; |
| + |
| + if (!g_file_load_contents(file, NULL, &contents, &len, NULL, NULL)) { |
| + polkit_backend_authority_log(POLKIT_BACKEND_AUTHORITY(ctx->authority), |
| + "Error loading script %s", ctx->filename); |
| + g_object_unref(file); |
| + goto err; |
| + } |
| + |
| + g_object_unref(file); |
| + |
| + /* evaluate the script, trying to print context in any syntax errors |
| + found */ |
| + if (duk_peval_lstring(cx, contents, len) != 0) |
| + { |
| + polkit_backend_authority_log(POLKIT_BACKEND_AUTHORITY(ctx->authority), |
| + "Error compiling script %s: %s", ctx->filename, |
| + duk_safe_to_string(cx, -1)); |
| + duk_pop(cx); |
| + goto free_err; |
| + } |
| + g_free(contents); |
| + |
| + ctx->ret = RUNAWAY_KILLER_THREAD_EXIT_STATUS_SUCCESS; |
| + goto end; |
| + |
| +free_err: |
| + g_free(contents); |
| +err: |
| + ctx->ret = RUNAWAY_KILLER_THREAD_EXIT_STATUS_FAILURE; |
| +end: |
| + if ((pthread_err = pthread_cond_signal(&ctx->cond))) { |
| + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (ctx->authority), |
| + "Error signaling on condition variable: %s", |
| + strerror(pthread_err)); |
| + ctx->ret = RUNAWAY_KILLER_THREAD_EXIT_STATUS_FAILURE; |
| + } |
| + return NULL; |
| +} |
| + |
| +static gpointer |
| +runaway_killer_thread_call_js (gpointer user_data) |
| +{ |
| + RunawayKillerCtx *ctx = user_data; |
| + duk_context *cx = ctx->authority->priv->cx; |
| + int oldtype, pthread_err; |
| + |
| + if ((pthread_err = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype))) { |
| + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (ctx->authority), |
| + "Error setting thread cancel type: %s", |
| + strerror(pthread_err)); |
| + goto err; |
| + } |
| + |
| + if (duk_pcall_prop (cx, 0, 2) != DUK_EXEC_SUCCESS) |
| + { |
| + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (ctx->authority), |
| + "Error evaluating admin rules: ", |
| + duk_safe_to_string (cx, -1)); |
| + goto err; |
| + } |
| + |
| + ctx->ret = RUNAWAY_KILLER_THREAD_EXIT_STATUS_SUCCESS; |
| + goto end; |
| + |
| +err: |
| + ctx->ret = RUNAWAY_KILLER_THREAD_EXIT_STATUS_FAILURE; |
| +end: |
| + if ((pthread_err = pthread_cond_signal(&ctx->cond))) { |
| + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (ctx->authority), |
| + "Error signaling on condition variable: %s", |
| + strerror(pthread_err)); |
| + ctx->ret = RUNAWAY_KILLER_THREAD_EXIT_STATUS_FAILURE; |
| + } |
| + return NULL; |
| +} |
| + |
| +#if defined (HAVE_PTHREAD_CONDATTR_SETCLOCK) |
| +# if defined(CLOCK_MONOTONIC) |
| +# define PK_CLOCK CLOCK_MONOTONIC |
| +# elif defined(CLOCK_BOOTTIME) |
| +# define PK_CLOCK CLOCK_BOOTTIME |
| +# else |
| + /* No suitable clock */ |
| +# undef HAVE_PTHREAD_CONDATTR_SETCLOCK |
| +# define PK_CLOCK CLOCK_REALTIME |
| +# endif |
| +#else /* ! HAVE_PTHREAD_CONDATTR_SETCLOCK */ |
| +# define PK_CLOCK CLOCK_REALTIME |
| +#endif /* ! HAVE_PTHREAD_CONDATTR_SETCLOCK */ |
| + |
| +static gboolean |
| +runaway_killer_common(PolkitBackendJsAuthority *authority, RunawayKillerCtx *ctx, void *js_context_cb (void *user_data)) |
| +{ |
| + int pthread_err; |
| + gboolean cancel = FALSE; |
| + pthread_condattr_t attr; |
| + struct timespec abs_time; |
| + |
| +#ifdef HAVE_PTHREAD_CONDATTR_SETCLOCK |
| + if ((pthread_err = pthread_condattr_init(&attr))) { |
| + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), |
| + "Error initializing condition variable attributes: %s", |
| + strerror(pthread_err)); |
| + return FALSE; |
| + } |
| + if ((pthread_err = pthread_condattr_setclock(&attr, PK_CLOCK))) { |
| + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), |
| + "Error setting condition variable attributes: %s", |
| + strerror(pthread_err)); |
| + goto err_clean_condattr; |
| + } |
| + /* Init again, with needed attr */ |
| + if ((pthread_err = pthread_cond_init(&ctx->cond, &attr))) { |
| + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), |
| + "Error initializing condition variable: %s", |
| + strerror(pthread_err)); |
| + goto err_clean_condattr; |
| + } |
| +#endif |
| + |
| + if ((pthread_err = pthread_mutex_lock(&ctx->mutex))) { |
| + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), |
| + "Error locking mutex: %s", |
| + strerror(pthread_err)); |
| + goto err_clean_cond; |
| + } |
| + |
| + if (clock_gettime(PK_CLOCK, &abs_time)) { |
| + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), |
| + "Error getting system's monotonic time: %s", |
| + strerror(errno)); |
| + goto err_clean_cond; |
| + } |
| + abs_time.tv_sec += RUNAWAY_KILLER_TIMEOUT; |
| + |
| + if ((pthread_err = pthread_create(&authority->priv->runaway_killer_thread, NULL, |
| + js_context_cb, ctx))) { |
| + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), |
| + "Error creating runaway JS killer thread: %s", |
| + strerror(pthread_err)); |
| + goto err_clean_cond; |
| + } |
| + |
| + while (ctx->ret == RUNAWAY_KILLER_THREAD_EXIT_STATUS_UNSET) /* loop to treat spurious wakeups */ |
| + if (pthread_cond_timedwait(&ctx->cond, &ctx->mutex, &abs_time) == ETIMEDOUT) { |
| + cancel = TRUE; |
| + |
| + /* Log that we are terminating the script */ |
| + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), |
| + "Terminating runaway script after %d seconds", |
| + RUNAWAY_KILLER_TIMEOUT); |
| + |
| + break; |
| + } |
| + |
| + if ((pthread_err = pthread_mutex_unlock(&ctx->mutex))) { |
| + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), |
| + "Error unlocking mutex: %s", |
| + strerror(pthread_err)); |
| + goto err_clean_cond; |
| + } |
| + |
| + if (cancel) { |
| + if ((pthread_err = pthread_cancel (authority->priv->runaway_killer_thread))) { |
| + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), |
| + "Error cancelling runaway JS killer thread: %s", |
| + strerror(pthread_err)); |
| + goto err_clean_cond; |
| + } |
| + } |
| + if ((pthread_err = pthread_join (authority->priv->runaway_killer_thread, NULL))) { |
| + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), |
| + "Error joining runaway JS killer thread: %s", |
| + strerror(pthread_err)); |
| + goto err_clean_cond; |
| + } |
| + |
| + return ctx->ret == RUNAWAY_KILLER_THREAD_EXIT_STATUS_SUCCESS; |
| + |
| + err_clean_cond: |
| +#ifdef HAVE_PTHREAD_CONDATTR_SETCLOCK |
| + pthread_cond_destroy(&ctx->cond); |
| +#endif |
| + err_clean_condattr: |
| +#ifdef HAVE_PTHREAD_CONDATTR_SETCLOCK |
| + pthread_condattr_destroy(&attr); |
| +#endif |
| + return FALSE; |
| +} |
| + |
| +/* Blocking for at most RUNAWAY_KILLER_TIMEOUT */ |
| +static gboolean |
| +execute_script_with_runaway_killer(PolkitBackendJsAuthority *authority, |
| + const gchar *filename) |
| +{ |
| + RunawayKillerCtx ctx = {.authority = authority, .filename = filename, |
| + .ret = RUNAWAY_KILLER_THREAD_EXIT_STATUS_UNSET, |
| + .mutex = PTHREAD_MUTEX_INITIALIZER, |
| + .cond = PTHREAD_COND_INITIALIZER}; |
| + |
| + return runaway_killer_common(authority, &ctx, &runaway_killer_thread_execute_js); |
| +} |
| + |
| +/* Calls already stacked function and args. Blocking for at most |
| + * RUNAWAY_KILLER_TIMEOUT. If timeout is the case, ctx.ret will be |
| + * RUNAWAY_KILLER_THREAD_EXIT_STATUS_UNSET, thus returning FALSE. |
| + */ |
| +static gboolean |
| +call_js_function_with_runaway_killer(PolkitBackendJsAuthority *authority) |
| +{ |
| + RunawayKillerCtx ctx = {.authority = authority, |
| + .ret = RUNAWAY_KILLER_THREAD_EXIT_STATUS_UNSET, |
| + .mutex = PTHREAD_MUTEX_INITIALIZER, |
| + .cond = PTHREAD_COND_INITIALIZER}; |
| + |
| + return runaway_killer_common(authority, &ctx, &runaway_killer_thread_call_js); |
| +} |
| + |
| +/* ---------------------------------------------------------------------------------------------------- */ |
| + |
| +GList * |
| +polkit_backend_common_js_authority_get_admin_auth_identities (PolkitBackendInteractiveAuthority *_authority, |
| + PolkitSubject *caller, |
| + PolkitSubject *subject, |
| + PolkitIdentity *user_for_subject, |
| + gboolean subject_is_local, |
| + gboolean subject_is_active, |
| + const gchar *action_id, |
| + PolkitDetails *details) |
| +{ |
| + PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (_authority); |
| + GList *ret = NULL; |
| + guint n; |
| + GError *error = NULL; |
| + const char *ret_str = NULL; |
| + gchar **ret_strs = NULL; |
| + duk_context *cx = authority->priv->cx; |
| + |
| + duk_set_top (cx, 0); |
| + if (!duk_get_global_string (cx, "polkit")) { |
| + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), |
| + "Error deleting old rules, not loading new ones"); |
| + goto out; |
| + } |
| + |
| + duk_push_string (cx, "_runAdminRules"); |
| + |
| + if (!push_action_and_details (cx, action_id, details, &error)) |
| + { |
| + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), |
| + "Error converting action and details to JS object: %s", |
| + error->message); |
| + g_clear_error (&error); |
| + goto out; |
| + } |
| + |
| + if (!push_subject (cx, subject, user_for_subject, subject_is_local, subject_is_active, &error)) |
| + { |
| + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), |
| + "Error converting subject to JS object: %s", |
| + error->message); |
| + g_clear_error (&error); |
| + goto out; |
| + } |
| + |
| + if (!call_js_function_with_runaway_killer (authority)) |
| + goto out; |
| + |
| + ret_str = duk_require_string (cx, -1); |
| + |
| + ret_strs = g_strsplit (ret_str, ",", -1); |
| + for (n = 0; ret_strs != NULL && ret_strs[n] != NULL; n++) |
| + { |
| + const gchar *identity_str = ret_strs[n]; |
| + PolkitIdentity *identity; |
| + |
| + error = NULL; |
| + identity = polkit_identity_from_string (identity_str, &error); |
| + if (identity == NULL) |
| + { |
| + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), |
| + "Identity `%s' is not valid, ignoring: %s", |
| + identity_str, error->message); |
| + g_clear_error (&error); |
| + } |
| + else |
| + { |
| + ret = g_list_prepend (ret, identity); |
| + } |
| + } |
| + ret = g_list_reverse (ret); |
| + |
| + out: |
| + g_strfreev (ret_strs); |
| + /* fallback to root password auth */ |
| + if (ret == NULL) |
| + ret = g_list_prepend (ret, polkit_unix_user_new (0)); |
| + |
| + return ret; |
| +} |
| + |
| +/* ---------------------------------------------------------------------------------------------------- */ |
| + |
| +PolkitImplicitAuthorization |
| +polkit_backend_common_js_authority_check_authorization_sync (PolkitBackendInteractiveAuthority *_authority, |
| + PolkitSubject *caller, |
| + PolkitSubject *subject, |
| + PolkitIdentity *user_for_subject, |
| + gboolean subject_is_local, |
| + gboolean subject_is_active, |
| + const gchar *action_id, |
| + PolkitDetails *details, |
| + PolkitImplicitAuthorization implicit) |
| +{ |
| + PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (_authority); |
| + PolkitImplicitAuthorization ret = implicit; |
| + GError *error = NULL; |
| + gchar *ret_str = NULL; |
| + gboolean good = FALSE; |
| + duk_context *cx = authority->priv->cx; |
| + |
| + duk_set_top (cx, 0); |
| + if (!duk_get_global_string (cx, "polkit")) { |
| + goto out; |
| + } |
| + |
| + duk_push_string (cx, "_runRules"); |
| + |
| + if (!push_action_and_details (cx, action_id, details, &error)) |
| + { |
| + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), |
| + "Error converting action and details to JS object: %s", |
| + error->message); |
| + g_clear_error (&error); |
| + goto out; |
| + } |
| + |
| + if (!push_subject (cx, subject, user_for_subject, subject_is_local, subject_is_active, &error)) |
| + { |
| + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), |
| + "Error converting subject to JS object: %s", |
| + error->message); |
| + g_clear_error (&error); |
| + goto out; |
| + } |
| + |
| + // If any error is the js context happened (ctx.ret == |
| + // RUNAWAY_KILLER_THREAD_EXIT_STATUS_FAILURE) or it never properly returned |
| + // (runaway scripts or ctx.ret == RUNAWAY_KILLER_THREAD_EXIT_STATUS_UNSET), |
| + // unauthorize |
| + if (!call_js_function_with_runaway_killer (authority)) |
| + goto out; |
| + |
| + if (duk_is_null(cx, -1)) { |
| + /* this is fine, means there was no match, use implicit authorizations */ |
| + good = TRUE; |
| + goto out; |
| + } |
| + ret_str = g_strdup (duk_require_string (cx, -1)); |
| + if (!polkit_implicit_authorization_from_string (ret_str, &ret)) |
| + { |
| + polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), |
| + "Returned result `%s' is not valid", |
| + ret_str); |
| + goto out; |
| + } |
| + |
| + good = TRUE; |
| + |
| + out: |
| + if (!good) |
| + ret = POLKIT_IMPLICIT_AUTHORIZATION_NOT_AUTHORIZED; |
| + if (ret_str != NULL) |
| + g_free (ret_str); |
| + |
| + return ret; |
| +} |
| + |
| +/* ---------------------------------------------------------------------------------------------------- */ |
| + |
| +static duk_ret_t |
| +js_polkit_log (duk_context *cx) |
| +{ |
| + const char *str = duk_require_string (cx, 0); |
| + fprintf (stderr, "%s\n", str); |
| + return 0; |
| +} |
| + |
| +/* ---------------------------------------------------------------------------------------------------- */ |
| + |
| +static duk_ret_t |
| +js_polkit_spawn (duk_context *cx) |
| +{ |
| + duk_ret_t ret = DUK_RET_ERROR; |
| + gchar *standard_output = NULL; |
| + gchar *standard_error = NULL; |
| + gint exit_status; |
| + GError *error = NULL; |
| + guint32 array_len; |
| + gchar **argv = NULL; |
| + GMainContext *context = NULL; |
| + GMainLoop *loop = NULL; |
| + SpawnData data = {0}; |
| + char *err_str = NULL; |
| + guint n; |
| + |
| + if (!duk_is_array (cx, 0)) |
| + goto out; |
| + |
| + array_len = duk_get_length (cx, 0); |
| + |
| + argv = g_new0 (gchar*, array_len + 1); |
| + for (n = 0; n < array_len; n++) |
| + { |
| + duk_get_prop_index (cx, 0, n); |
| + argv[n] = g_strdup (duk_to_string (cx, -1)); |
| + duk_pop (cx); |
| + } |
| + |
| + context = g_main_context_new (); |
| + loop = g_main_loop_new (context, FALSE); |
| + |
| + g_main_context_push_thread_default (context); |
| + |
| + data.loop = loop; |
| + polkit_backend_common_spawn ((const gchar *const *) argv, |
| + 10, /* timeout_seconds */ |
| + NULL, /* cancellable */ |
| + polkit_backend_common_spawn_cb, |
| + &data); |
| + |
| + g_main_loop_run (loop); |
| + |
| + g_main_context_pop_thread_default (context); |
| + |
| + if (!polkit_backend_common_spawn_finish (data.res, |
| + &exit_status, |
| + &standard_output, |
| + &standard_error, |
| + &error)) |
| + { |
| + err_str = g_strdup_printf ("Error spawning helper: %s (%s, %d)", |
| + error->message, g_quark_to_string (error->domain), error->code); |
| + g_clear_error (&error); |
| + goto out; |
| + } |
| + |
| + if (!(WIFEXITED (exit_status) && WEXITSTATUS (exit_status) == 0)) |
| + { |
| + GString *gstr; |
| + gstr = g_string_new (NULL); |
| + if (WIFEXITED (exit_status)) |
| + { |
| + g_string_append_printf (gstr, |
| + "Helper exited with non-zero exit status %d", |
| + WEXITSTATUS (exit_status)); |
| + } |
| + else if (WIFSIGNALED (exit_status)) |
| + { |
| + g_string_append_printf (gstr, |
| + "Helper was signaled with signal %s (%d)", |
| + polkit_backend_common_get_signal_name (WTERMSIG (exit_status)), |
| + WTERMSIG (exit_status)); |
| + } |
| + g_string_append_printf (gstr, ", stdout=`%s', stderr=`%s'", |
| + standard_output, standard_error); |
| + err_str = g_string_free (gstr, FALSE); |
| + goto out; |
| + } |
| + |
| + duk_push_string (cx, standard_output); |
| + ret = 1; |
| + |
| + out: |
| + g_strfreev (argv); |
| + g_free (standard_output); |
| + g_free (standard_error); |
| + g_clear_object (&data.res); |
| + if (loop != NULL) |
| + g_main_loop_unref (loop); |
| + if (context != NULL) |
| + g_main_context_unref (context); |
| + |
| + if (err_str) |
| + duk_error (cx, DUK_ERR_ERROR, err_str); |
| + |
| + return ret; |
| +} |
| + |
| +/* ---------------------------------------------------------------------------------------------------- */ |
| + |
| + |
| +static duk_ret_t |
| +js_polkit_user_is_in_netgroup (duk_context *cx) |
| +{ |
| + const char *user; |
| + const char *netgroup; |
| + gboolean is_in_netgroup = FALSE; |
| + |
| + user = duk_require_string (cx, 0); |
| + netgroup = duk_require_string (cx, 1); |
| + |
| + if (innetgr (netgroup, |
| + NULL, /* host */ |
| + user, |
| + NULL)) /* domain */ |
| + { |
| + is_in_netgroup = TRUE; |
| + } |
| + |
| + duk_push_boolean (cx, is_in_netgroup); |
| + return 1; |
| +} |
| + |
| +/* ---------------------------------------------------------------------------------------------------- */ |
| diff --git a/src/polkitbackend/polkitbackendjsauthority.cpp b/src/polkitbackend/polkitbackendjsauthority.cpp |
| index ca17108..11e91c0 100644 |
| --- a/src/polkitbackend/polkitbackendjsauthority.cpp |
| +++ b/src/polkitbackend/polkitbackendjsauthority.cpp |
| @@ -19,29 +19,7 @@ |
| * Author: David Zeuthen <davidz@redhat.com> |
| */ |
| |
| -#include "config.h" |
| -#include <sys/wait.h> |
| -#include <errno.h> |
| -#include <pwd.h> |
| -#include <grp.h> |
| -#ifdef HAVE_NETGROUP_H |
| -#include <netgroup.h> |
| -#else |
| -#include <netdb.h> |
| -#endif |
| -#include <string.h> |
| -#include <glib/gstdio.h> |
| -#include <locale.h> |
| -#include <glib/gi18n-lib.h> |
| - |
| -#include <polkit/polkit.h> |
| -#include "polkitbackendjsauthority.h" |
| - |
| -#include <polkit/polkitprivate.h> |
| - |
| -#ifdef HAVE_LIBSYSTEMD |
| -#include <systemd/sd-login.h> |
| -#endif /* HAVE_LIBSYSTEMD */ |
| +#include "polkitbackendcommon.h" |
| |
| #include <js/CompilationAndEvaluation.h> |
| #include <js/ContextOptions.h> |
| @@ -52,6 +30,7 @@ |
| #include <js/Array.h> |
| #include <jsapi.h> |
| |
| +/* Built source and not too big to worry about deduplication */ |
| #include "initjs.h" /* init.js */ |
| |
| #ifdef JSGC_USE_EXACT_ROOTING |
| @@ -67,10 +46,9 @@ |
| * @short_description: JS Authority |
| * @stability: Unstable |
| * |
| - * An implementation of #PolkitBackendAuthority that reads and |
| - * evalates Javascript files and supports interaction with |
| - * authentication agents (virtue of being based on |
| - * #PolkitBackendInteractiveAuthority). |
| + * An (SpiderMonkey-based) implementation of #PolkitBackendAuthority that reads |
| + * and evaluates Javascript files and supports interaction with authentication |
| + * agents (virtue of being based on #PolkitBackendInteractiveAuthority). |
| */ |
| |
| /* ---------------------------------------------------------------------------------------------------- */ |
| @@ -100,57 +78,11 @@ static bool execute_script_with_runaway_killer (PolkitBackendJsAuthority *author |
| JS::HandleScript script, |
| JS::MutableHandleValue rval); |
| |
| -static void utils_spawn (const gchar *const *argv, |
| - guint timeout_seconds, |
| - GCancellable *cancellable, |
| - GAsyncReadyCallback callback, |
| - gpointer user_data); |
| - |
| -gboolean utils_spawn_finish (GAsyncResult *res, |
| - gint *out_exit_status, |
| - gchar **out_standard_output, |
| - gchar **out_standard_error, |
| - GError **error); |
| - |
| -static void on_dir_monitor_changed (GFileMonitor *monitor, |
| - GFile *file, |
| - GFile *other_file, |
| - GFileMonitorEvent event_type, |
| - gpointer user_data); |
| - |
| -/* ---------------------------------------------------------------------------------------------------- */ |
| - |
| -enum |
| -{ |
| - PROP_0, |
| - PROP_RULES_DIRS, |
| -}; |
| - |
| /* ---------------------------------------------------------------------------------------------------- */ |
| |
| static gpointer runaway_killer_thread_func (gpointer user_data); |
| static void runaway_killer_terminate (PolkitBackendJsAuthority *authority); |
| |
| -static GList *polkit_backend_js_authority_get_admin_auth_identities (PolkitBackendInteractiveAuthority *authority, |
| - PolkitSubject *caller, |
| - PolkitSubject *subject, |
| - PolkitIdentity *user_for_subject, |
| - gboolean subject_is_local, |
| - gboolean subject_is_active, |
| - const gchar *action_id, |
| - PolkitDetails *details); |
| - |
| -static PolkitImplicitAuthorization polkit_backend_js_authority_check_authorization_sync ( |
| - PolkitBackendInteractiveAuthority *authority, |
| - PolkitSubject *caller, |
| - PolkitSubject *subject, |
| - PolkitIdentity *user_for_subject, |
| - gboolean subject_is_local, |
| - gboolean subject_is_active, |
| - const gchar *action_id, |
| - PolkitDetails *details, |
| - PolkitImplicitAuthorization implicit); |
| - |
| G_DEFINE_TYPE (PolkitBackendJsAuthority, polkit_backend_js_authority, POLKIT_BACKEND_TYPE_INTERACTIVE_AUTHORITY); |
| |
| /* ---------------------------------------------------------------------------------------------------- */ |
| @@ -229,33 +161,6 @@ polkit_backend_js_authority_init (PolkitBackendJsAuthority *authority) |
| PolkitBackendJsAuthorityPrivate); |
| } |
| |
| -static gint |
| -rules_file_name_cmp (const gchar *a, |
| - const gchar *b) |
| -{ |
| - gint ret; |
| - const gchar *a_base; |
| - const gchar *b_base; |
| - |
| - a_base = strrchr (a, '/'); |
| - b_base = strrchr (b, '/'); |
| - |
| - g_assert (a_base != NULL); |
| - g_assert (b_base != NULL); |
| - a_base += 1; |
| - b_base += 1; |
| - |
| - ret = g_strcmp0 (a_base, b_base); |
| - if (ret == 0) |
| - { |
| - /* /etc wins over /usr */ |
| - ret = g_strcmp0 (a, b); |
| - g_assert (ret != 0); |
| - } |
| - |
| - return ret; |
| -} |
| - |
| /* authority->priv->cx must be within a request */ |
| static void |
| load_scripts (PolkitBackendJsAuthority *authority) |
| @@ -299,7 +204,7 @@ load_scripts (PolkitBackendJsAuthority *authority) |
| } |
| } |
| |
| - files = g_list_sort (files, (GCompareFunc) rules_file_name_cmp); |
| + files = g_list_sort (files, (GCompareFunc) polkit_backend_common_rules_file_name_cmp); |
| |
| for (l = files; l != NULL; l = l->next) |
| { |
| @@ -365,8 +270,8 @@ load_scripts (PolkitBackendJsAuthority *authority) |
| g_list_free_full (files, g_free); |
| } |
| |
| -static void |
| -reload_scripts (PolkitBackendJsAuthority *authority) |
| +void |
| +polkit_backend_common_reload_scripts (PolkitBackendJsAuthority *authority) |
| { |
| JS::RootedValueArray<1> args(authority->priv->cx); |
| JS::RootedValue rval(authority->priv->cx); |
| @@ -395,42 +300,6 @@ reload_scripts (PolkitBackendJsAuthority *authority) |
| g_signal_emit_by_name (authority, "changed"); |
| } |
| |
| -static void |
| -on_dir_monitor_changed (GFileMonitor *monitor, |
| - GFile *file, |
| - GFile *other_file, |
| - GFileMonitorEvent event_type, |
| - gpointer user_data) |
| -{ |
| - PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (user_data); |
| - |
| - /* TODO: maybe rate-limit so storms of events are collapsed into one with a 500ms resolution? |
| - * Because when editing a file with emacs we get 4-8 events.. |
| - */ |
| - |
| - if (file != NULL) |
| - { |
| - gchar *name; |
| - |
| - name = g_file_get_basename (file); |
| - |
| - /* g_print ("event_type=%d file=%p name=%s\n", event_type, file, name); */ |
| - if (!g_str_has_prefix (name, ".") && |
| - !g_str_has_prefix (name, "#") && |
| - g_str_has_suffix (name, ".rules") && |
| - (event_type == G_FILE_MONITOR_EVENT_CREATED || |
| - event_type == G_FILE_MONITOR_EVENT_DELETED || |
| - event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)) |
| - { |
| - polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority), |
| - "Reloading rules"); |
| - reload_scripts (authority); |
| - } |
| - g_free (name); |
| - } |
| -} |
| - |
| - |
| static void |
| setup_file_monitors (PolkitBackendJsAuthority *authority) |
| { |
| @@ -462,7 +331,7 @@ setup_file_monitors (PolkitBackendJsAuthority *authority) |
| { |
| g_signal_connect (monitor, |
| "changed", |
| - G_CALLBACK (on_dir_monitor_changed), |
| + G_CALLBACK (polkit_backend_common_on_dir_monitor_changed), |
| authority); |
| g_ptr_array_add (p, monitor); |
| } |
| @@ -471,8 +340,8 @@ setup_file_monitors (PolkitBackendJsAuthority *authority) |
| authority->priv->dir_monitors = (GFileMonitor**) g_ptr_array_free (p, FALSE); |
| } |
| |
| -static void |
| -polkit_backend_js_authority_constructed (GObject *object) |
| +void |
| +polkit_backend_common_js_authority_constructed (GObject *object) |
| { |
| PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (object); |
| |
| @@ -561,8 +430,8 @@ polkit_backend_js_authority_constructed (GObject *object) |
| g_assert_not_reached (); |
| } |
| |
| -static void |
| -polkit_backend_js_authority_finalize (GObject *object) |
| +void |
| +polkit_backend_common_js_authority_finalize (GObject *object) |
| { |
| PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (object); |
| guint n; |
| @@ -577,7 +446,7 @@ polkit_backend_js_authority_finalize (GObject *object) |
| { |
| GFileMonitor *monitor = authority->priv->dir_monitors[n]; |
| g_signal_handlers_disconnect_by_func (monitor, |
| - (gpointer*)G_CALLBACK (on_dir_monitor_changed), |
| + (gpointer*)G_CALLBACK (polkit_backend_common_on_dir_monitor_changed), |
| authority); |
| g_object_unref (monitor); |
| } |
| @@ -594,11 +463,11 @@ polkit_backend_js_authority_finalize (GObject *object) |
| G_OBJECT_CLASS (polkit_backend_js_authority_parent_class)->finalize (object); |
| } |
| |
| -static void |
| -polkit_backend_js_authority_set_property (GObject *object, |
| - guint property_id, |
| - const GValue *value, |
| - GParamSpec *pspec) |
| +void |
| +polkit_backend_common_js_authority_set_property (GObject *object, |
| + guint property_id, |
| + const GValue *value, |
| + GParamSpec *pspec) |
| { |
| PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (object); |
| |
| @@ -615,57 +484,12 @@ polkit_backend_js_authority_set_property (GObject *object, |
| } |
| } |
| |
| -static const gchar * |
| -polkit_backend_js_authority_get_name (PolkitBackendAuthority *authority) |
| -{ |
| - return "js"; |
| -} |
| - |
| -static const gchar * |
| -polkit_backend_js_authority_get_version (PolkitBackendAuthority *authority) |
| -{ |
| - return PACKAGE_VERSION; |
| -} |
| - |
| -static PolkitAuthorityFeatures |
| -polkit_backend_js_authority_get_features (PolkitBackendAuthority *authority) |
| -{ |
| - return POLKIT_AUTHORITY_FEATURES_TEMPORARY_AUTHORIZATION; |
| -} |
| - |
| static void |
| polkit_backend_js_authority_class_init (PolkitBackendJsAuthorityClass *klass) |
| { |
| - GObjectClass *gobject_class; |
| - PolkitBackendAuthorityClass *authority_class; |
| - PolkitBackendInteractiveAuthorityClass *interactive_authority_class; |
| - |
| - |
| - gobject_class = G_OBJECT_CLASS (klass); |
| - gobject_class->finalize = polkit_backend_js_authority_finalize; |
| - gobject_class->set_property = polkit_backend_js_authority_set_property; |
| - gobject_class->constructed = polkit_backend_js_authority_constructed; |
| - |
| - authority_class = POLKIT_BACKEND_AUTHORITY_CLASS (klass); |
| - authority_class->get_name = polkit_backend_js_authority_get_name; |
| - authority_class->get_version = polkit_backend_js_authority_get_version; |
| - authority_class->get_features = polkit_backend_js_authority_get_features; |
| - |
| - interactive_authority_class = POLKIT_BACKEND_INTERACTIVE_AUTHORITY_CLASS (klass); |
| - interactive_authority_class->get_admin_identities = polkit_backend_js_authority_get_admin_auth_identities; |
| - interactive_authority_class->check_authorization_sync = polkit_backend_js_authority_check_authorization_sync; |
| - |
| - g_object_class_install_property (gobject_class, |
| - PROP_RULES_DIRS, |
| - g_param_spec_boxed ("rules-dirs", |
| - NULL, |
| - NULL, |
| - G_TYPE_STRV, |
| - GParamFlags(G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE))); |
| - |
| + polkit_backend_common_js_authority_class_init_common (klass); |
| |
| g_type_class_add_private (klass, sizeof (PolkitBackendJsAuthorityPrivate)); |
| - |
| JS_Init (); |
| } |
| |
| @@ -1005,11 +829,14 @@ runaway_killer_setup (PolkitBackendJsAuthority *authority) |
| { |
| g_assert (authority->priv->rkt_source == NULL); |
| |
| - /* set-up timer for runaway scripts, will be executed in runaway_killer_thread */ |
| + /* set-up timer for runaway scripts, will be executed in |
| + runaway_killer_thread, that is one, permanent thread running a glib |
| + mainloop (rkt_loop) whose context (rkt_context) has a timeout source |
| + (rkt_source) */ |
| g_mutex_lock (&authority->priv->rkt_timeout_pending_mutex); |
| authority->priv->rkt_timeout_pending = FALSE; |
| g_mutex_unlock (&authority->priv->rkt_timeout_pending_mutex); |
| - authority->priv->rkt_source = g_timeout_source_new_seconds (15); |
| + authority->priv->rkt_source = g_timeout_source_new_seconds (RUNAWAY_KILLER_TIMEOUT); |
| g_source_set_callback (authority->priv->rkt_source, rkt_on_timeout, authority, NULL); |
| g_source_attach (authority->priv->rkt_source, authority->priv->rkt_context); |
| |
| @@ -1069,6 +896,9 @@ execute_script_with_runaway_killer (PolkitBackendJsAuthority *authority, |
| { |
| bool ret; |
| |
| + // tries to JS_ExecuteScript(), may hang for > RUNAWAY_KILLER_TIMEOUT, |
| + // runaway_killer_thread makes sure the call returns, due to exception |
| + // injection |
| runaway_killer_setup (authority); |
| ret = JS_ExecuteScript (authority->priv->cx, |
| script, |
| @@ -1099,15 +929,15 @@ call_js_function_with_runaway_killer (PolkitBackendJsAuthority *authority, |
| |
| /* ---------------------------------------------------------------------------------------------------- */ |
| |
| -static GList * |
| -polkit_backend_js_authority_get_admin_auth_identities (PolkitBackendInteractiveAuthority *_authority, |
| - PolkitSubject *caller, |
| - PolkitSubject *subject, |
| - PolkitIdentity *user_for_subject, |
| - gboolean subject_is_local, |
| - gboolean subject_is_active, |
| - const gchar *action_id, |
| - PolkitDetails *details) |
| +GList * |
| +polkit_backend_common_js_authority_get_admin_auth_identities (PolkitBackendInteractiveAuthority *_authority, |
| + PolkitSubject *caller, |
| + PolkitSubject *subject, |
| + PolkitIdentity *user_for_subject, |
| + gboolean subject_is_local, |
| + gboolean subject_is_active, |
| + const gchar *action_id, |
| + PolkitDetails *details) |
| { |
| PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (_authority); |
| GList *ret = NULL; |
| @@ -1202,16 +1032,16 @@ polkit_backend_js_authority_get_admin_auth_identities (PolkitBackendInteractiveA |
| |
| /* ---------------------------------------------------------------------------------------------------- */ |
| |
| -static PolkitImplicitAuthorization |
| -polkit_backend_js_authority_check_authorization_sync (PolkitBackendInteractiveAuthority *_authority, |
| - PolkitSubject *caller, |
| - PolkitSubject *subject, |
| - PolkitIdentity *user_for_subject, |
| - gboolean subject_is_local, |
| - gboolean subject_is_active, |
| - const gchar *action_id, |
| - PolkitDetails *details, |
| - PolkitImplicitAuthorization implicit) |
| +PolkitImplicitAuthorization |
| +polkit_backend_common_js_authority_check_authorization_sync (PolkitBackendInteractiveAuthority *_authority, |
| + PolkitSubject *caller, |
| + PolkitSubject *subject, |
| + PolkitIdentity *user_for_subject, |
| + gboolean subject_is_local, |
| + gboolean subject_is_active, |
| + const gchar *action_id, |
| + PolkitDetails *details, |
| + PolkitImplicitAuthorization implicit) |
| { |
| PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (_authority); |
| PolkitImplicitAuthorization ret = implicit; |
| @@ -1324,65 +1154,6 @@ js_polkit_log (JSContext *cx, |
| |
| /* ---------------------------------------------------------------------------------------------------- */ |
| |
| -static const gchar * |
| -get_signal_name (gint signal_number) |
| -{ |
| - switch (signal_number) |
| - { |
| -#define _HANDLE_SIG(sig) case sig: return #sig; |
| - _HANDLE_SIG (SIGHUP); |
| - _HANDLE_SIG (SIGINT); |
| - _HANDLE_SIG (SIGQUIT); |
| - _HANDLE_SIG (SIGILL); |
| - _HANDLE_SIG (SIGABRT); |
| - _HANDLE_SIG (SIGFPE); |
| - _HANDLE_SIG (SIGKILL); |
| - _HANDLE_SIG (SIGSEGV); |
| - _HANDLE_SIG (SIGPIPE); |
| - _HANDLE_SIG (SIGALRM); |
| - _HANDLE_SIG (SIGTERM); |
| - _HANDLE_SIG (SIGUSR1); |
| - _HANDLE_SIG (SIGUSR2); |
| - _HANDLE_SIG (SIGCHLD); |
| - _HANDLE_SIG (SIGCONT); |
| - _HANDLE_SIG (SIGSTOP); |
| - _HANDLE_SIG (SIGTSTP); |
| - _HANDLE_SIG (SIGTTIN); |
| - _HANDLE_SIG (SIGTTOU); |
| - _HANDLE_SIG (SIGBUS); |
| -#ifdef SIGPOLL |
| - _HANDLE_SIG (SIGPOLL); |
| -#endif |
| - _HANDLE_SIG (SIGPROF); |
| - _HANDLE_SIG (SIGSYS); |
| - _HANDLE_SIG (SIGTRAP); |
| - _HANDLE_SIG (SIGURG); |
| - _HANDLE_SIG (SIGVTALRM); |
| - _HANDLE_SIG (SIGXCPU); |
| - _HANDLE_SIG (SIGXFSZ); |
| -#undef _HANDLE_SIG |
| - default: |
| - break; |
| - } |
| - return "UNKNOWN_SIGNAL"; |
| -} |
| - |
| -typedef struct |
| -{ |
| - GMainLoop *loop; |
| - GAsyncResult *res; |
| -} SpawnData; |
| - |
| -static void |
| -spawn_cb (GObject *source_object, |
| - GAsyncResult *res, |
| - gpointer user_data) |
| -{ |
| - SpawnData *data = (SpawnData *)user_data; |
| - data->res = (GAsyncResult*)g_object_ref (res); |
| - g_main_loop_quit (data->loop); |
| -} |
| - |
| static bool |
| js_polkit_spawn (JSContext *cx, |
| unsigned js_argc, |
| @@ -1440,21 +1211,21 @@ js_polkit_spawn (JSContext *cx, |
| g_main_context_push_thread_default (context); |
| |
| data.loop = loop; |
| - utils_spawn ((const gchar *const *) argv, |
| - 10, /* timeout_seconds */ |
| - NULL, /* cancellable */ |
| - spawn_cb, |
| - &data); |
| + polkit_backend_common_spawn ((const gchar *const *) argv, |
| + 10, /* timeout_seconds */ |
| + NULL, /* cancellable */ |
| + polkit_backend_common_spawn_cb, |
| + &data); |
| |
| g_main_loop_run (loop); |
| |
| g_main_context_pop_thread_default (context); |
| |
| - if (!utils_spawn_finish (data.res, |
| - &exit_status, |
| - &standard_output, |
| - &standard_error, |
| - &error)) |
| + if (!polkit_backend_common_spawn_finish (data.res, |
| + &exit_status, |
| + &standard_output, |
| + &standard_error, |
| + &error)) |
| { |
| JS_ReportErrorUTF8 (cx, |
| "Error spawning helper: %s (%s, %d)", |
| @@ -1477,7 +1248,7 @@ js_polkit_spawn (JSContext *cx, |
| { |
| g_string_append_printf (gstr, |
| "Helper was signaled with signal %s (%d)", |
| - get_signal_name (WTERMSIG (exit_status)), |
| + polkit_backend_common_get_signal_name (WTERMSIG (exit_status)), |
| WTERMSIG (exit_status)); |
| } |
| g_string_append_printf (gstr, ", stdout=`%s', stderr=`%s'", |
| @@ -1542,381 +1313,5 @@ js_polkit_user_is_in_netgroup (JSContext *cx, |
| return ret; |
| } |
| |
| - |
| - |
| /* ---------------------------------------------------------------------------------------------------- */ |
| |
| -typedef struct |
| -{ |
| - GSimpleAsyncResult *simple; /* borrowed reference */ |
| - GMainContext *main_context; /* may be NULL */ |
| - |
| - GCancellable *cancellable; /* may be NULL */ |
| - gulong cancellable_handler_id; |
| - |
| - GPid child_pid; |
| - gint child_stdout_fd; |
| - gint child_stderr_fd; |
| - |
| - GIOChannel *child_stdout_channel; |
| - GIOChannel *child_stderr_channel; |
| - |
| - GSource *child_watch_source; |
| - GSource *child_stdout_source; |
| - GSource *child_stderr_source; |
| - |
| - guint timeout_seconds; |
| - gboolean timed_out; |
| - GSource *timeout_source; |
| - |
| - GString *child_stdout; |
| - GString *child_stderr; |
| - |
| - gint exit_status; |
| -} UtilsSpawnData; |
| - |
| -static void |
| -utils_child_watch_from_release_cb (GPid pid, |
| - gint status, |
| - gpointer user_data) |
| -{ |
| -} |
| - |
| -static void |
| -utils_spawn_data_free (UtilsSpawnData *data) |
| -{ |
| - if (data->timeout_source != NULL) |
| - { |
| - g_source_destroy (data->timeout_source); |
| - data->timeout_source = NULL; |
| - } |
| - |
| - /* Nuke the child, if necessary */ |
| - if (data->child_watch_source != NULL) |
| - { |
| - g_source_destroy (data->child_watch_source); |
| - data->child_watch_source = NULL; |
| - } |
| - |
| - if (data->child_pid != 0) |
| - { |
| - GSource *source; |
| - kill (data->child_pid, SIGTERM); |
| - /* OK, we need to reap for the child ourselves - we don't want |
| - * to use waitpid() because that might block the calling |
| - * thread (the child might handle SIGTERM and use several |
| - * seconds for cleanup/rollback). |
| - * |
| - * So we use GChildWatch instead. |
| - * |
| - * Avoid taking a references to ourselves. but note that we need |
| - * to pass the GSource so we can nuke it once handled. |
| - */ |
| - source = g_child_watch_source_new (data->child_pid); |
| - g_source_set_callback (source, |
| - (GSourceFunc) utils_child_watch_from_release_cb, |
| - source, |
| - (GDestroyNotify) g_source_destroy); |
| - /* attach source to the global default main context */ |
| - g_source_attach (source, NULL); |
| - g_source_unref (source); |
| - data->child_pid = 0; |
| - } |
| - |
| - if (data->child_stdout != NULL) |
| - { |
| - g_string_free (data->child_stdout, TRUE); |
| - data->child_stdout = NULL; |
| - } |
| - |
| - if (data->child_stderr != NULL) |
| - { |
| - g_string_free (data->child_stderr, TRUE); |
| - data->child_stderr = NULL; |
| - } |
| - |
| - if (data->child_stdout_channel != NULL) |
| - { |
| - g_io_channel_unref (data->child_stdout_channel); |
| - data->child_stdout_channel = NULL; |
| - } |
| - if (data->child_stderr_channel != NULL) |
| - { |
| - g_io_channel_unref (data->child_stderr_channel); |
| - data->child_stderr_channel = NULL; |
| - } |
| - |
| - if (data->child_stdout_source != NULL) |
| - { |
| - g_source_destroy (data->child_stdout_source); |
| - data->child_stdout_source = NULL; |
| - } |
| - if (data->child_stderr_source != NULL) |
| - { |
| - g_source_destroy (data->child_stderr_source); |
| - data->child_stderr_source = NULL; |
| - } |
| - |
| - if (data->child_stdout_fd != -1) |
| - { |
| - g_warn_if_fail (close (data->child_stdout_fd) == 0); |
| - data->child_stdout_fd = -1; |
| - } |
| - if (data->child_stderr_fd != -1) |
| - { |
| - g_warn_if_fail (close (data->child_stderr_fd) == 0); |
| - data->child_stderr_fd = -1; |
| - } |
| - |
| - if (data->cancellable_handler_id > 0) |
| - { |
| - g_cancellable_disconnect (data->cancellable, data->cancellable_handler_id); |
| - data->cancellable_handler_id = 0; |
| - } |
| - |
| - if (data->main_context != NULL) |
| - g_main_context_unref (data->main_context); |
| - |
| - if (data->cancellable != NULL) |
| - g_object_unref (data->cancellable); |
| - |
| - g_slice_free (UtilsSpawnData, data); |
| -} |
| - |
| -/* called in the thread where @cancellable was cancelled */ |
| -static void |
| -utils_on_cancelled (GCancellable *cancellable, |
| - gpointer user_data) |
| -{ |
| - UtilsSpawnData *data = (UtilsSpawnData *)user_data; |
| - GError *error; |
| - |
| - error = NULL; |
| - g_warn_if_fail (g_cancellable_set_error_if_cancelled (cancellable, &error)); |
| - g_simple_async_result_take_error (data->simple, error); |
| - g_simple_async_result_complete_in_idle (data->simple); |
| - g_object_unref (data->simple); |
| -} |
| - |
| -static gboolean |
| -utils_read_child_stderr (GIOChannel *channel, |
| - GIOCondition condition, |
| - gpointer user_data) |
| -{ |
| - UtilsSpawnData *data = (UtilsSpawnData *)user_data; |
| - gchar buf[1024]; |
| - gsize bytes_read; |
| - |
| - g_io_channel_read_chars (channel, buf, sizeof buf, &bytes_read, NULL); |
| - g_string_append_len (data->child_stderr, buf, bytes_read); |
| - return TRUE; |
| -} |
| - |
| -static gboolean |
| -utils_read_child_stdout (GIOChannel *channel, |
| - GIOCondition condition, |
| - gpointer user_data) |
| -{ |
| - UtilsSpawnData *data = (UtilsSpawnData *)user_data; |
| - gchar buf[1024]; |
| - gsize bytes_read; |
| - |
| - g_io_channel_read_chars (channel, buf, sizeof buf, &bytes_read, NULL); |
| - g_string_append_len (data->child_stdout, buf, bytes_read); |
| - return TRUE; |
| -} |
| - |
| -static void |
| -utils_child_watch_cb (GPid pid, |
| - gint status, |
| - gpointer user_data) |
| -{ |
| - UtilsSpawnData *data = (UtilsSpawnData *)user_data; |
| - gchar *buf; |
| - gsize buf_size; |
| - |
| - if (g_io_channel_read_to_end (data->child_stdout_channel, &buf, &buf_size, NULL) == G_IO_STATUS_NORMAL) |
| - { |
| - g_string_append_len (data->child_stdout, buf, buf_size); |
| - g_free (buf); |
| - } |
| - if (g_io_channel_read_to_end (data->child_stderr_channel, &buf, &buf_size, NULL) == G_IO_STATUS_NORMAL) |
| - { |
| - g_string_append_len (data->child_stderr, buf, buf_size); |
| - g_free (buf); |
| - } |
| - |
| - data->exit_status = status; |
| - |
| - /* ok, child watch is history, make sure we don't free it in spawn_data_free() */ |
| - data->child_pid = 0; |
| - data->child_watch_source = NULL; |
| - |
| - /* we're done */ |
| - g_simple_async_result_complete_in_idle (data->simple); |
| - g_object_unref (data->simple); |
| -} |
| - |
| -static gboolean |
| -utils_timeout_cb (gpointer user_data) |
| -{ |
| - UtilsSpawnData *data = (UtilsSpawnData *)user_data; |
| - |
| - data->timed_out = TRUE; |
| - |
| - /* ok, timeout is history, make sure we don't free it in spawn_data_free() */ |
| - data->timeout_source = NULL; |
| - |
| - /* we're done */ |
| - g_simple_async_result_complete_in_idle (data->simple); |
| - g_object_unref (data->simple); |
| - |
| - return FALSE; /* remove source */ |
| -} |
| - |
| -static void |
| -utils_spawn (const gchar *const *argv, |
| - guint timeout_seconds, |
| - GCancellable *cancellable, |
| - GAsyncReadyCallback callback, |
| - gpointer user_data) |
| -{ |
| - UtilsSpawnData *data; |
| - GError *error; |
| - |
| - data = g_slice_new0 (UtilsSpawnData); |
| - data->timeout_seconds = timeout_seconds; |
| - data->simple = g_simple_async_result_new (NULL, |
| - callback, |
| - user_data, |
| - (gpointer*)utils_spawn); |
| - data->main_context = g_main_context_get_thread_default (); |
| - if (data->main_context != NULL) |
| - g_main_context_ref (data->main_context); |
| - |
| - data->cancellable = cancellable != NULL ? (GCancellable*)g_object_ref (cancellable) : NULL; |
| - |
| - data->child_stdout = g_string_new (NULL); |
| - data->child_stderr = g_string_new (NULL); |
| - data->child_stdout_fd = -1; |
| - data->child_stderr_fd = -1; |
| - |
| - /* the life-cycle of UtilsSpawnData is tied to its GSimpleAsyncResult */ |
| - g_simple_async_result_set_op_res_gpointer (data->simple, data, (GDestroyNotify) utils_spawn_data_free); |
| - |
| - error = NULL; |
| - if (data->cancellable != NULL) |
| - { |
| - /* could already be cancelled */ |
| - error = NULL; |
| - if (g_cancellable_set_error_if_cancelled (data->cancellable, &error)) |
| - { |
| - g_simple_async_result_take_error (data->simple, error); |
| - g_simple_async_result_complete_in_idle (data->simple); |
| - g_object_unref (data->simple); |
| - goto out; |
| - } |
| - |
| - data->cancellable_handler_id = g_cancellable_connect (data->cancellable, |
| - G_CALLBACK (utils_on_cancelled), |
| - data, |
| - NULL); |
| - } |
| - |
| - error = NULL; |
| - if (!g_spawn_async_with_pipes (NULL, /* working directory */ |
| - (gchar **) argv, |
| - NULL, /* envp */ |
| - GSpawnFlags(G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD), |
| - NULL, /* child_setup */ |
| - NULL, /* child_setup's user_data */ |
| - &(data->child_pid), |
| - NULL, /* gint *stdin_fd */ |
| - &(data->child_stdout_fd), |
| - &(data->child_stderr_fd), |
| - &error)) |
| - { |
| - g_prefix_error (&error, "Error spawning: "); |
| - g_simple_async_result_take_error (data->simple, error); |
| - g_simple_async_result_complete_in_idle (data->simple); |
| - g_object_unref (data->simple); |
| - goto out; |
| - } |
| - |
| - if (timeout_seconds > 0) |
| - { |
| - data->timeout_source = g_timeout_source_new_seconds (timeout_seconds); |
| - g_source_set_priority (data->timeout_source, G_PRIORITY_DEFAULT); |
| - g_source_set_callback (data->timeout_source, utils_timeout_cb, data, NULL); |
| - g_source_attach (data->timeout_source, data->main_context); |
| - g_source_unref (data->timeout_source); |
| - } |
| - |
| - data->child_watch_source = g_child_watch_source_new (data->child_pid); |
| - g_source_set_callback (data->child_watch_source, (GSourceFunc) utils_child_watch_cb, data, NULL); |
| - g_source_attach (data->child_watch_source, data->main_context); |
| - g_source_unref (data->child_watch_source); |
| - |
| - data->child_stdout_channel = g_io_channel_unix_new (data->child_stdout_fd); |
| - g_io_channel_set_flags (data->child_stdout_channel, G_IO_FLAG_NONBLOCK, NULL); |
| - data->child_stdout_source = g_io_create_watch (data->child_stdout_channel, G_IO_IN); |
| - g_source_set_callback (data->child_stdout_source, (GSourceFunc) utils_read_child_stdout, data, NULL); |
| - g_source_attach (data->child_stdout_source, data->main_context); |
| - g_source_unref (data->child_stdout_source); |
| - |
| - data->child_stderr_channel = g_io_channel_unix_new (data->child_stderr_fd); |
| - g_io_channel_set_flags (data->child_stderr_channel, G_IO_FLAG_NONBLOCK, NULL); |
| - data->child_stderr_source = g_io_create_watch (data->child_stderr_channel, G_IO_IN); |
| - g_source_set_callback (data->child_stderr_source, (GSourceFunc) utils_read_child_stderr, data, NULL); |
| - g_source_attach (data->child_stderr_source, data->main_context); |
| - g_source_unref (data->child_stderr_source); |
| - |
| - out: |
| - ; |
| -} |
| - |
| -gboolean |
| -utils_spawn_finish (GAsyncResult *res, |
| - gint *out_exit_status, |
| - gchar **out_standard_output, |
| - gchar **out_standard_error, |
| - GError **error) |
| -{ |
| - GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res); |
| - UtilsSpawnData *data; |
| - gboolean ret = FALSE; |
| - |
| - g_return_val_if_fail (G_IS_ASYNC_RESULT (res), FALSE); |
| - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); |
| - |
| - g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == utils_spawn); |
| - |
| - if (g_simple_async_result_propagate_error (simple, error)) |
| - goto out; |
| - |
| - data = (UtilsSpawnData*)g_simple_async_result_get_op_res_gpointer (simple); |
| - |
| - if (data->timed_out) |
| - { |
| - g_set_error (error, |
| - G_IO_ERROR, |
| - G_IO_ERROR_TIMED_OUT, |
| - "Timed out after %d seconds", |
| - data->timeout_seconds); |
| - goto out; |
| - } |
| - |
| - if (out_exit_status != NULL) |
| - *out_exit_status = data->exit_status; |
| - |
| - if (out_standard_output != NULL) |
| - *out_standard_output = g_strdup (data->child_stdout->str); |
| - |
| - if (out_standard_error != NULL) |
| - *out_standard_error = g_strdup (data->child_stderr->str); |
| - |
| - ret = TRUE; |
| - |
| - out: |
| - return ret; |
| -} |
| diff --git a/test/data/etc/polkit-1/rules.d/10-testing.rules b/test/data/etc/polkit-1/rules.d/10-testing.rules |
| index 98bf062..e346b5d 100644 |
| --- a/test/data/etc/polkit-1/rules.d/10-testing.rules |
| +++ b/test/data/etc/polkit-1/rules.d/10-testing.rules |
| @@ -189,8 +189,10 @@ polkit.addRule(function(action, subject) { |
| ; |
| } catch (error) { |
| if (error == "Terminating runaway script") |
| - return polkit.Result.YES; |
| - return polkit.Result.NO; |
| + // Inverted logic to accomodate Duktape's model as well, which |
| + // will always fail with negation, on timeouts |
| + return polkit.Result.NO; |
| + return polkit.Result.YES; |
| } |
| } |
| }); |
| diff --git a/test/polkitbackend/test-polkitbackendjsauthority.c b/test/polkitbackend/test-polkitbackendjsauthority.c |
| index f97e0e0..2103b17 100644 |
| --- a/test/polkitbackend/test-polkitbackendjsauthority.c |
| +++ b/test/polkitbackend/test-polkitbackendjsauthority.c |
| @@ -328,7 +328,7 @@ static const RulesTestCase rules_test_cases[] = { |
| "net.company.run_away_script", |
| "unix-user:root", |
| NULL, |
| - POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED, |
| + POLKIT_IMPLICIT_AUTHORIZATION_NOT_AUTHORIZED, |
| }, |
| |
| { |