Go to:
Gentoo Home
Documentation
Forums
Lists
Bugs
Planet
Store
Wiki
Get Gentoo!
Gentoo's Bugzilla – Attachment 763734 Details for
Bug 734326
sys-auth/polkit: use dev-lang/duktape instead of dev-lang/spidermonkey
Home
|
New
–
[Ex]
|
Browse
|
Search
|
Privacy Policy
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
add duktape support on top of 0.120
polkit-0.120-duktape-support.patch (text/plain), 126.83 KB, created by
Fab
on 2022-01-27 11:07:11 UTC
(
hide
)
Description:
add duktape support on top of 0.120
Filename:
MIME Type:
Creator:
Fab
Created:
2022-01-27 11:07:11 UTC
Size:
126.83 KB
patch
obsolete
>diff --git a/buildutil/ax_pthread.m4 b/buildutil/ax_pthread.m4 >new file mode 100644 >index 0000000000000000000000000000000000000000..9f35d139149f8d9bda17cddb730cd13bcf775465 >--- /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 e434ca2e5de913fa1d13a67afe8e9efc00f68472..e1822380d5f8097c2c2de6106f4fce8e562d1858 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 >@@ -585,6 +602,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 99aa474fd82b08e2dfa2d90dca11cd2e53cfab4d..90715a53203ec746146131fc24eae015673b19d2 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 858078dd0ffa90076c4c4acea46d55d22ce4709b..ad9ab079b8dab98514135909c6d8c883650e0744 100644 >--- a/meson.build >+++ b/meson.build >@@ -133,7 +133,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', required: false) > dbus_policydir = pk_prefix / pk_datadir / 'dbus-1/system.d' >@@ -361,6 +372,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 25e3e7793ae1671fc65e172386b9392abf4e9352..76aa311c99620129b3594b0d95d675df91dc483b 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 7e3c0809984987d8e77ff6c7717248f132e990f3..935fb986d8db60a31d5f3705b681b9b03ff1caef 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 64f0e4acfd068cc47926fbe38956abd73532559d..266f2808b6eda77db373b71dee4ed146484a0963 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 0000000000000000000000000000000000000000..6783dff145f8ccff7f4021f9bff7bb84c85b8df2 >--- /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 0000000000000000000000000000000000000000..dd700fc06011d0e3cb7040d7aee921e4c9d96de4 >--- /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 0000000000000000000000000000000000000000..c89dbcf5a6ccb2cde49afb803585ec407c184f1b >--- /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 ca171083d835ede41809e7528f49f4428d54dc43..11e91c045da1e1eee607b3f37421a658c08efa11 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 98bf062a08cb11fddb7df95d0bcdec1b1ac3587d..e346b5dd3f08886cd93ceca3c9c81f7802aa4f18 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 f97e0e0f24ad776aaece0acf581e9538e704adbd..2103b174d58dfcab6bb97825d666dfb482e3da8b 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, > }, > > {
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Raw
Actions:
View
Attachments on
bug 734326
: 763734 |
763739
|
763740