diff --git a/configure.ac b/configure.ac index 296553e3..4ce30677 100644 --- a/configure.ac +++ b/configure.ac @@ -1,954 +1,954 @@ # configure.ac for GPGME # Copyright (C) 2000 Werner Koch (dd9jn) # Copyright (C) 2001-2021 g10 Code GmbH # # This file is part of GPGME. # # GPGME 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.1 of the # License, or (at your option) any later version. # # GPGME 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 program; if not, see . # SPDX-License-Identifier: LGPL-2.1-or-later # (Process this file with autoconf to produce a configure script.) AC_PREREQ([2.59]) min_automake_version="1.14" # To build a release you need to create a tag with the version number # "gpgme-n.m.k" and run "./autogen.sh --force". Please bump the # version number immediately after the release and do another commit # and push so that the git magic is able to work. If you start a new # series by bumping the minor (m) remember to also create a tag named # "gpgme-n.m-base" as the start point for beta numbering. See below # for the LT versions. m4_define([mym4_package],[gpgme]) m4_define([mym4_major], [1]) m4_define([mym4_minor], [16]) m4_define([mym4_micro], [1]) # Below is m4 magic to extract and compute the git revision number, # the decimalized short revision number, a beta version string and a # flag indicating a development version (mym4_isbeta). Note that the # m4 processing is done by autoconf and not during the configure run. m4_define([mym4_verslist], m4_split(m4_esyscmd([./autogen.sh --find-version] \ mym4_package mym4_major mym4_minor mym4_micro),[:])) m4_define([mym4_isbeta], m4_argn(2, mym4_verslist)) m4_define([mym4_version], m4_argn(4, mym4_verslist)) m4_define([mym4_revision], m4_argn(7, mym4_verslist)) m4_define([mym4_revision_dec], m4_argn(8, mym4_verslist)) m4_esyscmd([echo ]mym4_version[>VERSION]) AC_INIT([mym4_package],[mym4_version],[https://bugs.gnupg.org]) # LT Version numbers, remember to change them just *before* a release. # (Code changed: REVISION++) # (Interfaces added/removed/changed: CURRENT++, REVISION=0) # (Interfaces added: AGE++) # (Interfaces removed: AGE=0) # LIBGPGME_LT_CURRENT=36 LIBGPGME_LT_AGE=25 LIBGPGME_LT_REVISION=0 # If there is an ABI break in gpgmepp or qgpgme also bump the # version in IMPORTED_LOCATION in the GpgmeppConfig-w32.cmake.in.in LIBGPGMEPP_LT_CURRENT=19 LIBGPGMEPP_LT_AGE=13 LIBGPGMEPP_LT_REVISION=0 LIBQGPGME_LT_CURRENT=13 LIBQGPGME_LT_AGE=6 LIBQGPGME_LT_REVISION=0 ################################################ AC_SUBST(LIBGPGME_LT_CURRENT) AC_SUBST(LIBGPGME_LT_AGE) AC_SUBST(LIBGPGME_LT_REVISION) AC_SUBST(LIBGPGMEPP_LT_CURRENT) AC_SUBST(LIBGPGMEPP_LT_AGE) AC_SUBST(LIBGPGMEPP_LT_REVISION) AC_SUBST(LIBQGPGME_LT_CURRENT) AC_SUBST(LIBQGPGME_LT_AGE) AC_SUBST(LIBQGPGME_LT_REVISION) # If the API is changed in an incompatible way: increment the next counter. GPGME_CONFIG_API_VERSION=1 ############################################## NEED_GPG_ERROR_VERSION=1.36 NEED_LIBASSUAN_API=2 NEED_LIBASSUAN_VERSION=2.4.2 VERSION_MAJOR=mym4_major VERSION_MINOR=mym4_minor VERSION_MICRO=mym4_micro AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_SRCDIR(src/gpgme.h.in) AC_CONFIG_HEADER(conf/config.h) AM_INIT_AUTOMAKE([serial-tests dist-bzip2 no-dist-gzip]) AM_MAINTAINER_MODE AC_CANONICAL_HOST AM_SILENT_RULES AC_ARG_VAR(SYSROOT,[locate config scripts also below that directory]) # Enable GNU extensions on systems that have them. AC_USE_SYSTEM_EXTENSIONS # Taken from mpfr-4.0.1, then modified for LDADD_FOR_TESTS_KLUDGE dnl Under Linux, make sure that the old dtags are used if LD_LIBRARY_PATH dnl is defined. The issue is that with the new dtags, LD_LIBRARY_PATH has dnl the precedence over the run path, so that if a compatible MPFR library dnl is installed in some directory from $LD_LIBRARY_PATH, then the tested dnl MPFR library will be this library instead of the MPFR library from the dnl build tree. Other OS with the same issue might be added later. dnl dnl References: dnl https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=859732 dnl http://lists.gnu.org/archive/html/libtool/2017-05/msg00000.html dnl dnl We need to check whether --disable-new-dtags is supported as alternate dnl linkers may be used (e.g., with tcc: CC=tcc LD=tcc). dnl case $host in *-*-linux*) if test -n "$LD_LIBRARY_PATH"; then saved_LDFLAGS="$LDFLAGS" LDADD_FOR_TESTS_KLUDGE="-Wl,--disable-new-dtags" LDFLAGS="$LDFLAGS $LDADD_FOR_TESTS_KLUDGE" AC_MSG_CHECKING(whether --disable-new-dtags is supported by the linker) AC_LINK_IFELSE([AC_LANG_SOURCE([[ int main (void) { return 0; } ]])], [AC_MSG_RESULT(yes (use it since LD_LIBRARY_PATH is set))], [AC_MSG_RESULT(no) LDADD_FOR_TESTS_KLUDGE="" ]) LDFLAGS="$saved_LDFLAGS" fi ;; esac AC_SUBST([LDADD_FOR_TESTS_KLUDGE]) AH_VERBATIM([_REENTRANT], [/* To allow the use of GPGME in multithreaded programs we have to use special features from the library. IMPORTANT: gpgme is not yet fully reentrant and you should use it only from one thread. */ #ifndef _REENTRANT # define _REENTRANT 1 #endif]) AC_PROG_CC AC_PROG_CPP AC_PROG_CXX # Note: A suitable gitlog-to-changelog script can be found in GnuPG master. AC_CHECK_PROGS(GITLOG_TO_CHANGELOG, gitlog-to-changelog, [gitlog-to-changelog]) AC_SUBST(VERSION_MAJOR) AC_SUBST(VERSION_MINOR) AC_SUBST(VERSION_MICRO) VERSION_NUMBER=m4_esyscmd(printf "0x%02x%02x%02x" mym4_major \ mym4_minor mym4_micro) AC_SUBST(VERSION_NUMBER) # We need to compile and run a program on the build machine. A # comment in libgpg-error says that the AC_PROG_CC_FOR_BUILD macro in # the AC archive is broken for autoconf 2.57. Given that there is no # newer version of that macro, we assume that it is also broken for # autoconf 2.61 and thus we use a simple but usually sufficient # approach. AC_MSG_CHECKING(for cc for build) if test "$cross_compiling" = "yes"; then CC_FOR_BUILD="${CC_FOR_BUILD-cc}" else CC_FOR_BUILD="${CC_FOR_BUILD-$CC}" fi AC_MSG_RESULT($CC_FOR_BUILD) AC_ARG_VAR(CC_FOR_BUILD,[build system C compiler]) # Don't default to build static libs. LT_PREREQ([2.2.6]) LT_INIT([win32-dll disable-static]) LT_LANG([Windows Resource]) # For now we hardcode the use of version scripts. It would be better # to write a test for this or even implement this within libtool. have_ld_version_script=no case "${host}" in *-*-linux*) have_ld_version_script=yes ;; *-*-gnu*) have_ld_version_script=yes ;; *-apple-darwin*) AC_DEFINE(_DARWIN_C_SOURCE, 1, Expose all libc features (__DARWIN_C_FULL).) AC_DEFINE(_XOPEN_SOURCE, 500, Activate POSIX interface on MacOS X) ;; esac AM_CONDITIONAL(HAVE_LD_VERSION_SCRIPT, test "$have_ld_version_script" = "yes") GPG_DEFAULT=no GPGSM_DEFAULT=no GPGCONF_DEFAULT=no G13_DEFAULT=no component_system=None have_dosish_system=no have_android_system=no have_w32_system=no have_w64_system=no have_macos_system=no build_w32_glib=no build_w32_qt=no available_languages="cl cpp python qt" default_languages="cl cpp python qt" case "${host}" in x86_64-*mingw32*) have_w64_system=yes ;; *-linux-androideabi) have_android_system=yes ;; *-apple-darwin*) have_macos_system=yes ;; esac case "${host}" in *-mingw32*) have_dosish_system=yes have_w32_system=yes GPG_DEFAULT='c:\\gnupg\\gpg.exe' GPGSM_DEFAULT='c:\\gnupg\\gpgsm.exe' GPGCONF_DEFAULT='c:\\gnupg\\gpgconf.exe' G13_DEFAULT='c:\\gnupg\\g13.exe' #component_system='COM+' AM_PATH_GLIB_2_0 AC_ARG_ENABLE(w32-glib, AS_HELP_STRING([--enable-w32-glib],[build GPGME Glib for W32]), build_w32_glib=$enableval) ;; *) # XXX: Probably use exec-prefix here? # GPG_DEFAULT='/usr/bin/gpg' # GPGSM_DEFAULT='/usr/bin/gpgsm' # GPGCONF_DEFAULT='/usr/bin/gpgconf' # G13_DEFAULT='/usr/bin/g13' ;; esac if test "$have_dosish_system" = yes; then AC_DEFINE(HAVE_DOSISH_SYSTEM,1, [Defined if we run on some of the PCDOS like systems (DOS, Windoze. OS/2) with special properties like no file modes]) fi AM_CONDITIONAL(HAVE_DOSISH_SYSTEM, test "$have_dosish_system" = yes) if test "$have_w32_system" = yes; then AC_DEFINE(HAVE_W32_SYSTEM,1, [Defined if we run on any kind of W32 API based system]) fi AM_CONDITIONAL(HAVE_W32_SYSTEM, test "$have_w32_system" = yes) if test "$have_w64_system" = yes; then AC_DEFINE(HAVE_W64_SYSTEM,1, [Defined if we run on a 64 bit W32 API based system]) fi AM_CONDITIONAL(HAVE_W64_SYSTEM, test "$have_w64_system" = yes) if test "$have_android_system" = yes; then AC_DEFINE(HAVE_ANDROID_SYSTEM,1, [Defined if we build for an Android system]) fi AM_CONDITIONAL(HAVE_ANDROID_SYSTEM, test "$have_android_system" = yes) if test "$have_macos_system" = yes; then AC_DEFINE(HAVE_MACOS_SYSTEM,1, [Defined if we build for an MacOS system]) fi AM_CONDITIONAL(HAVE_MACOS_SYSTEM, test "$have_macos_system" = yes) AM_CONDITIONAL(BUILD_W32_GLIB, test "$build_w32_glib" = yes) AC_ARG_ENABLE([fixed-path], AS_HELP_STRING([--enable-fixed-path=PATH], [locate binaries only via this PATH]), [fixed_search_path="$enableval"], [fixed_search_path=""]) if test x$fixed_search_path != x ; then AC_DEFINE_UNQUOTED(FIXED_SEARCH_PATH, "$fixed_search_path", [Locate binaries only via this PATH]) fi # Note: You need to declare all possible languages also in # lang/Makefile.am's DIST_SUBDIRS. AC_ARG_ENABLE([languages], AS_HELP_STRING([--enable-languages=languages], [enable only specific language bindings]), [enabled_languages=`echo $enableval | \ tr ',:' ' ' | tr '[A-Z]' '[a-z]' | \ sed 's/c++/cpp/'`], [enabled_languages="maybe"]) if test "x$enabled_languages" = "x" \ -o "$enabled_languages" = "no"; then enabled_languages= fi # If languages are explicitly set missing requirements # for the languages are treated as errors otherwise # there will be a warning. explicit_languages=1 if test "x$enabled_languages" = "xmaybe"; then explicit_languages=0 enabled_languages="$default_languages" fi for language in $enabled_languages; do LIST_MEMBER($language, $available_languages) if test "$found" = "0"; then AC_MSG_ERROR([unsupported language binding specified]) fi done # Enable C++ 11 if cpp language is requested LIST_MEMBER("cpp", $enabled_languages) if test "$found" = "1"; then AX_CXX_COMPILE_STDCXX(11, noext, optional) if test "$HAVE_CXX11" != "1"; then if test "$explicit_languages" = "1"; then AC_MSG_ERROR([[ *** *** A compiler with c++11 support is required for the c++ binding. ***]]) else enabled_languages=$(echo $enabled_languages | sed 's/cpp//') enabled_languages=$(echo $enabled_languages | sed 's/qt//') AC_MSG_WARN([[ *** *** No c++11 support detected. C++ and Qt bindings will be disabled. ***]]) fi fi fi # Check that if qt is enabled cpp also is enabled LIST_MEMBER("qt", $enabled_languages) if test "$found" = "1"; then # We need to ensure that in the language order qt comes after cpp # so we remove qt first and explicitly add it as last list member. enabled_languages=$(echo $enabled_languages | sed 's/qt//') LIST_MEMBER("cpp", $enabled_languages) if test "$found" = "0"; then AC_MSG_ERROR([[ *** *** Qt language binding depends on cpp binding. ***]]) fi FIND_QT if test "$have_qt5_libs" != "yes"; then if test "$explicit_languages" = "1"; then AC_MSG_ERROR([[ *** *** Qt5 (Qt5Core) is required for Qt binding. ***]]) else AC_MSG_WARN([[ *** *** Qt5 (Qt5Core) not found Qt Binding will be disabled. ***]]) fi else enabled_languages=`echo $enabled_languages qt` AC_CHECK_PROGS([DOXYGEN], [doxygen]) if test -z "$DOXYGEN"; # This is not highlighted because it's not really important. then AC_MSG_WARN([Doxygen not found - Qt binding doc will not be built.]) fi AC_CHECK_PROGS([GRAPHVIZ], [dot]) if test -z "$GRAPHVIZ"; then AC_MSG_WARN([Graphviz not found - Qt binding doc will not have diagrams.]) fi fi fi AM_CONDITIONAL([HAVE_DOXYGEN], [test -n "$DOXYGEN"]) if test -n "$GRAPHVIZ"; then HAVE_DOT="YES" else HAVE_DOT="NO" fi AC_SUBST(HAVE_DOT) # Python bindings. LIST_MEMBER("python", $enabled_languages) found_py=$found if test "$found_py" = "1"; then AX_PKG_SWIG if test -z "$SWIG"; then if test "$explicit_languages" = "1"; then AC_MSG_ERROR([[ *** *** You need SWIG to build the Python bindings. ***]]) else enabled_languages=$(echo $enabled_languages | sed 's/python//') fi else # Reset the version collecting vars. PYTHONS= PYTHON_VERSIONS= if test "$found_py" = "1" -o "$found_py3" = "1"; then # Reset everything, so that we can look for another Python. m4_foreach([mym4pythonver], [[2.7],[3.4],[3.5],[3.6],[3.7],[3.8],[3.9],[all]], [unset PYTHON unset PYTHON_VERSION unset PYTHON_CPPFLAGS unset PYTHON_LDFLAGS unset PYTHON_SITE_PKG unset PYTHON_EXTRA_LIBS unset PYTHON_EXTRA_LDFLAGS unset ac_cv_path_PYTHON unset am_cv_pathless_PYTHON unset am_cv_python_version unset am_cv_python_platform unset am_cv_python_pythondir unset am_cv_python_pyexecdir AM_PATH_PYTHON(mym4pythonver, [ AX_PYTHON_DEVEL if test "$PYTHON_VERSION"; then PYTHONS="$(echo $PYTHONS $PYTHON)" PYTHON_VERSIONS="$(echo $PYTHON_VERSIONS $PYTHON_VERSION)" fi ], :, m4_if([mym4pythonver],[all],[],[python]mym4pythonver)) ]) fi # Recover some values lost in the second attempt to find Python. PYTHON="$(echo $PYTHONS | cut -d ' ' -f 1)" PYTHON_VERSION="$(echo $PYTHON_VERSIONS | cut -d ' ' -f 1)" # Remove duplicates. PYTHONS="$(echo $PYTHONS | tr '[[:space:]]' '\n' | sort | uniq | tr '\n' ' ' | sed -e 's/ $//')" PYTHON_VERSIONS="$(echo $PYTHON_VERSIONS | tr '[[:space:]]' '\n' | sort | uniq | tr '\n' ' ' | sed -e 's/ $//')" if test "$PYTHON_VERSIONS"; then enabled_languages_v=$(echo $enabled_languages | sed -Ee "s/python[[23]]?/python ($PYTHON_VERSIONS)/") enabled_languages=$(echo $enabled_languages | sed -Ee "s/python[[23]]?/python/") else if test "$explicit_languages" = "1"; then AC_MSG_ERROR([[ *** *** Please install the python development packages. ***]]) else enabled_languages=$(echo $enabled_languages | sed 's/python//') fi fi AC_SUBST(PYTHONS, $PYTHONS) fi fi AC_SUBST(ENABLED_LANGUAGES, $enabled_languages) # # Provide information about the build. # BUILD_REVISION="mym4_revision" AC_SUBST(BUILD_REVISION) AC_DEFINE_UNQUOTED(BUILD_REVISION, "$BUILD_REVISION", [GIT commit id revision used to build this package]) changequote(,)dnl BUILD_VERSION=`echo "$PACKAGE_VERSION" | sed 's/\([0-9.]*\).*/\1./'` changequote([,])dnl BUILD_VERSION="${BUILD_VERSION}mym4_revision_dec" BUILD_FILEVERSION=`echo "${BUILD_VERSION}" | tr . ,` AC_SUBST(BUILD_VERSION) AC_SUBST(BUILD_FILEVERSION) AC_ARG_ENABLE([build-timestamp], AS_HELP_STRING([--enable-build-timestamp], [set an explicit build timestamp for reproducibility. (default is the current time in ISO-8601 format)]), [if test "$enableval" = "yes"; then BUILD_TIMESTAMP=`date -u +%Y-%m-%dT%H:%M+0000 2>/dev/null || date` else BUILD_TIMESTAMP="$enableval" fi], [BUILD_TIMESTAMP=""]) AC_SUBST(BUILD_TIMESTAMP) AC_DEFINE_UNQUOTED(BUILD_TIMESTAMP, "$BUILD_TIMESTAMP", [The time this package was configured for a build]) # # Options to disable some regression tests # run_gpgconf_test="yes" AC_ARG_ENABLE(gpgconf-test, AS_HELP_STRING([--disable-gpgconf-test],[disable GPGCONF regression test]), run_gpgconf_test=$enableval) AM_CONDITIONAL(RUN_GPGCONF_TESTS, test "$run_gpgconf_test" = "yes") run_gpg_test="yes" AC_ARG_ENABLE(gpg-test, AS_HELP_STRING([--disable-gpg-test],[disable GPG regression test]), run_gpg_test=$enableval) AM_CONDITIONAL(RUN_GPG_TESTS, test "$run_gpg_test" = "yes") run_gpgsm_test="yes" AC_ARG_ENABLE(gpgsm-test, AS_HELP_STRING([--disable-gpgsm-test],[disable GPGSM regression test]), run_gpgsm_test=$enableval) AM_CONDITIONAL(RUN_GPGSM_TESTS, test "$run_gpgsm_test" = "yes") run_g13_test="yes" AC_ARG_ENABLE(g13-test, AS_HELP_STRING([--disable-g13-test],[disable G13 regression test]), run_g13_test=$enableval) AM_CONDITIONAL(RUN_G13_TESTS, test "$run_g13_test" = "yes") # Checks for header files. AC_CHECK_HEADERS_ONCE([locale.h sys/select.h sys/uio.h argp.h stdint.h - unistd.h sys/time.h sys/types.h sys/stat.h]) + unistd.h poll.h sys/time.h sys/types.h sys/stat.h]) # Type checks. AC_C_INLINE AC_C_FLEXIBLE_ARRAY_MEMBER AC_CHECK_SIZEOF(unsigned int) AC_SYS_LARGEFILE AC_TYPE_OFF_T AC_TYPE_UINTPTR_T # We require uint64_t if test "$ac_cv_header_stdint_h" != yes; then AC_MSG_ERROR([[ *** *** No stdint.h and thus no uint64_t type. Can't build this library. ***]]) fi # A simple compile time check in gpgme.h for GNU/Linux systems that # prevents a file offset bits mismatch between gpgme and the application. NEED__FILE_OFFSET_BITS=0 if test "$have_w32_system" != yes; then case "$ac_cv_sys_file_offset_bits" in "" | no | unknown) ;; *) NEED__FILE_OFFSET_BITS=$ac_cv_sys_file_offset_bits ;; esac fi AC_SUBST(NEED__FILE_OFFSET_BITS) # Figure out platform dependent typedefs for gpgme.h if test "$have_w32_system" = yes; then INSERT__TYPEDEFS_FOR_GPGME_H=" #ifdef _WIN64 # include typedef int64_t gpgme_off_t; typedef int64_t gpgme_ssize_t; #else /* _WIN32 */ typedef long gpgme_off_t; typedef long gpgme_ssize_t; #endif /* _WIN32 */" API__OFF_T="gpgme_off_t" API__SSIZE_T="gpgme_ssize_t" else INSERT__TYPEDEFS_FOR_GPGME_H=" #include typedef off_t gpgme_off_t; typedef ssize_t gpgme_ssize_t;" API__OFF_T="off_t" API__SSIZE_T="ssize_t" fi AC_SUBST(INSERT__TYPEDEFS_FOR_GPGME_H) AM_SUBST_NOTMAKE(INSERT__TYPEDEFS_FOR_GPGME_H) AC_SUBST(API__OFF_T) AM_SUBST_NOTMAKE(API__OFF_T) AC_SUBST(API__SSIZE_T) AM_SUBST_NOTMAKE(API__SSIZE_T) # Checks for compiler features. if test "$GCC" = yes; then CFLAGS="$CFLAGS -Wall -Wcast-align -Wshadow -Wstrict-prototypes" CFLAGS="$CFLAGS -Wno-format-y2k" # If -Wno-missing-field-initializers is supported we can expect a # a larger set of warning options. AC_MSG_CHECKING([if gcc supports -Wno-missing-field-initializers]) _gcc_cflags_save=$CFLAGS CFLAGS="-Wno-missing-field-initializers" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],_gcc_wopt=yes,_gcc_wopt=no) AC_MSG_RESULT($_gcc_wopt) CFLAGS=$_gcc_cflags_save; if test x"$_gcc_wopt" = xyes ; then CFLAGS="$CFLAGS -Wno-missing-field-initializers" CFLAGS="$CFLAGS -Wno-sign-compare" CFLAGS="$CFLAGS -Wno-format-zero-length" CFLAGS="$CFLAGS -Wno-format-truncation" CFLAGS="$CFLAGS -Wno-sizeof-pointer-div" fi if test "$USE_MAINTAINER_MODE" = "yes"; then if test x"$_gcc_wopt" = xyes ; then CFLAGS="$CFLAGS -W -Wextra -Wbad-function-cast" CFLAGS="$CFLAGS -Wwrite-strings" CFLAGS="$CFLAGS -Wdeclaration-after-statement" fi CFLAGS="$CFLAGS -Wformat -Wformat-security" CXXFLAGS="$CXXFLAGS -Wall -Wextra -Wno-shadow" AC_MSG_CHECKING([if gcc supports -Wpointer-arith]) _gcc_cflags_save=$CFLAGS CFLAGS="-Wpointer-arith" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],_gcc_wopt=yes,_gcc_wopt=no) AC_MSG_RESULT($_gcc_wopt) CFLAGS=$_gcc_cflags_save; if test x"$_gcc_wopt" = xyes ; then CFLAGS="$CFLAGS -Wpointer-arith" fi fi if test "$have_w32_system" = yes; then CFLAGS="$CFLAGS -mms-bitfields" fi fi # Only used for debugging, so no serious test needed (for actual # functionality you have to test libc as well, this only tests the # compiler). AC_CACHE_CHECK([for __thread],[gpgme_cv_tls_works], AC_COMPILE_IFELSE([AC_LANG_PROGRAM([__thread int foo;])], gpgme_cv_tls_works=yes,gpgme_cv_tls_works=no)) if test "$gpgme_cv_tls_works" = yes; then AC_DEFINE(HAVE_TLS, [1], [Define if __thread is supported]) fi # Checks for library functions. AC_MSG_NOTICE([checking for libraries]) AC_FUNC_FSEEKO # Try to find a thread-safe version of ttyname(). gnupg_REPLACE_TTYNAME_R if test "$ac_cv_func_ttyname_r" != yes; then AC_MSG_WARN([ *** *** ttyname() is not thread-safe and ttyname_r() does not exist ***]) fi # Try to find a thread-safe version of getenv(). have_thread_safe_getenv=no jm_GLIBC21 if test $GLIBC21 = yes -o $have_w32_system = yes; then have_thread_safe_getenv=yes fi if test $have_thread_safe_getenv = yes; then AC_DEFINE(HAVE_THREAD_SAFE_GETENV, [1], [Define if getenv() is thread-safe]) fi have_getenv_r=no AC_CHECK_FUNCS(getenv_r, have_getenv_r=yes) if test $have_getenv_r = no && test $have_thread_safe_getenv = no; then AC_MSG_WARN([ *** *** getenv() is not thread-safe and getenv_r() does not exist ***]) fi # For converting time strings to seconds since Epoch, we need the timegm # function. AC_CHECK_FUNCS(timegm) if test "$ac_cv_func_timegm" != yes; then AC_MSG_WARN([ *** *** timegm() not available - a non-thread-safe kludge will be used *** and the TZ variable might be changed at runtime. ***]) fi AC_CHECK_FUNCS(setlocale) # Checking for libgpg-error. have_gpg_error=no AM_PATH_GPG_ERROR("$NEED_GPG_ERROR_VERSION", have_gpg_error=yes, have_gpg_error=no) AC_DEFINE(GPG_ERR_SOURCE_DEFAULT, GPG_ERR_SOURCE_GPGME, [The default error source for GPGME.]) # And for libassuan. have_libassuan=no AM_PATH_LIBASSUAN("$NEED_LIBASSUAN_API:$NEED_LIBASSUAN_VERSION", have_libassuan=yes, have_libassuan=no) if test "$have_libassuan" = "yes"; then AC_DEFINE_UNQUOTED(GPGME_LIBASSUAN_VERSION, "$libassuan_version", [version of the libassuan library]) fi # # Other checks # # Check for getgid etc AC_CHECK_FUNCS(getgid getegid closefrom) # Replacement functions. AC_REPLACE_FUNCS(stpcpy) AC_REPLACE_FUNCS(setenv) # Assuan check for descriptor passing. AC_CHECK_MEMBER(struct cmsghdr.cmsg_len, [supports_descriptor_passing=yes], [supports_descriptor_passing=no AC_MSG_WARN([ *** *** Data structure for sending ancillary data missing. *** Descriptor passing won't work. ***])],[ #include #include #include #include #include #include #if HAVE_SYS_UIO_H #include #endif #include ]) use_descriptor_passing=yes AC_ARG_ENABLE(fd-passing, AS_HELP_STRING([--disable-fd-passing],[do not use FD passing]), use_descriptor_passing=$enableval) if test "$supports_descriptor_passing" != "yes"; then use_descriptor_passing=no fi if test "$use_descriptor_passing" = "yes"; then AC_DEFINE(USE_DESCRIPTOR_PASSING,1, [Defined if descriptor passing is enabled and supported]) fi AM_CONDITIONAL(USE_DESCRIPTOR_PASSING, test "$use_descriptor_passing" = "yes") uiserver=no if test "$use_descriptor_passing" = "yes" && test "$have_libassuan" = "yes"; then uiserver=yes fi if test "$uiserver" != "no"; then AC_DEFINE(ENABLE_UISERVER, 1, [Defined if we are building with uiserver support.]) fi AM_CONDITIONAL(HAVE_UISERVER, test "$uiserver" != "no") # Option --disable-linux-getdents # # By default we use SYS_getdents on Linux to optimize fd closing # before an exec. This option allows to switch this optimization off. use_linux_getdents=yes AC_ARG_ENABLE(linux-getdents, AS_HELP_STRING([--disable-linux-getdents], [do not use SYS_getdents on Linux]), use_linux_getdents=$enableval) if test "$use_linux_getdents" = "yes"; then case "${host}" in *-*-linux*) AC_DEFINE(USE_LINUX_GETDENTS,1, [Defined if SYS_getdents can be used on Linux]) ;; esac fi # # Add a few constants to help porting to W32 # AH_VERBATIM([SEPCONSTANTS], [ /* Separators as used in $PATH and file name. */ #ifdef HAVE_DOSISH_SYSTEM #define PATHSEP_C ';' #define DIRSEP_C '\\' #define DIRSEP_S "\\" #else #define PATHSEP_C ':' #define DIRSEP_C '/' #define DIRSEP_S "/" #endif ]) AH_BOTTOM([ /* Definition of GCC specific attributes. */ #if __GNUC__ > 2 # define GPGME_GCC_A_PURE __attribute__ ((__pure__)) #else # define GPGME_GCC_A_PURE #endif /* Under WindowsCE we need gpg-error's strerror macro. */ #define GPG_ERR_ENABLE_ERRNO_MACROS 1 #define CRIGHTBLURB "Copyright (C) 2000 Werner Koch\n" \ "Copyright (C) 2001--2021 g10 Code GmbH\n" ]) # Substitution used for gpgme-config GPGME_CONFIG_LIBS="-lgpgme" GPGME_CONFIG_CFLAGS="" GPGME_CONFIG_HOST="$host" GPGME_CONFIG_AVAIL_LANG="$enabled_languages" AC_SUBST(GPGME_CONFIG_API_VERSION) AC_SUBST(GPGME_CONFIG_LIBS) AC_SUBST(GPGME_CONFIG_CFLAGS) AC_SUBST(GPGME_CONFIG_HOST) AC_SUBST(GPGME_CONFIG_AVAIL_LANG) # Frob'da Variables LTLIBOBJS=`echo "$LIB@&t@OBJS" | sed 's,\.[[^.]]* ,.lo ,g;s,\.[[^.]]*$,.lo,'` AC_SUBST(LTLIBOBJS) # Some checks for gpgme-tool # Done at top: AC_CHECK_HEADER([argp.h]) AC_CHECK_TYPES([error_t], [], [AC_DEFINE([error_t], [int], [Define to a type to use for `error_t' if it is not otherwise available.])], [#include ]) # A substitution to set generated files in a Emacs buffer to read-only. AC_SUBST(emacs_local_vars_begin, [['Local][ ][Variables:']]) AC_SUBST(emacs_local_vars_read_only, ['buffer-read-only: t']) AC_SUBST(emacs_local_vars_end, ['End:']) # Last check. die=no if test "$have_gpg_error" = "no"; then die=yes AC_MSG_NOTICE([[ *** *** You need libgpg-error to build this program. ** This library is for example available at *** https://www.gnupg.org/ftp/gcrypt/libgpg-error/ *** (at least version $NEED_GPG_ERROR_VERSION is required.) ***]]) fi if test "$have_libassuan" = "no"; then die=yes AC_MSG_NOTICE([[ *** *** You need libassuan to build this program. *** This library is for example available at *** https://www.gnupg.org/ftp/gcrypt/libassuan/ *** (at least version $NEED_LIBASSUAN_VERSION (API $NEED_LIBASSUAN_API) is required). ***]]) fi if test "$die" = "yes"; then AC_MSG_ERROR([[ *** *** Required libraries not found. Please consult the above messages *** and install them before running configure again. ***]]) fi # # Create config files AC_CONFIG_FILES(Makefile src/Makefile tests/Makefile tests/gpg/Makefile tests/gpgsm/Makefile tests/opassuan/Makefile tests/json/Makefile doc/Makefile src/versioninfo.rc src/gpgme.pc src/gpgme-glib.pc src/gpgme.h) AC_CONFIG_FILES(src/gpgme-config, chmod +x src/gpgme-config) AC_CONFIG_FILES(lang/cpp/Makefile lang/cpp/src/Makefile) AC_CONFIG_FILES(lang/cpp/tests/Makefile) AC_CONFIG_FILES(lang/cpp/src/GpgmeppConfig-w32.cmake.in) AC_CONFIG_FILES(lang/cpp/src/GpgmeppConfig.cmake.in) AC_CONFIG_FILES(lang/cpp/src/GpgmeppConfigVersion.cmake) AC_CONFIG_FILES(lang/cpp/src/gpgmepp_version.h) AC_CONFIG_FILES(lang/qt/Makefile lang/qt/src/Makefile) AC_CONFIG_FILES(lang/qt/src/QGpgmeConfig-w32.cmake.in) AC_CONFIG_FILES(lang/qt/src/QGpgmeConfig.cmake.in) AC_CONFIG_FILES(lang/qt/src/QGpgmeConfigVersion.cmake) AC_CONFIG_FILES(lang/qt/tests/Makefile) AC_CONFIG_FILES(lang/qt/src/qgpgme_version.h) AC_CONFIG_FILES([lang/Makefile lang/cl/Makefile lang/cl/gpgme.asd]) AM_COND_IF([HAVE_DOXYGEN], [AC_CONFIG_FILES([lang/qt/doc/Doxyfile])]) AC_CONFIG_FILES([lang/js/Makefile lang/js/src/Makefile lang/js/BrowserTestExtension/Makefile lang/js/DemoExtension/Makefile]) AC_CONFIG_FILES(lang/qt/doc/Makefile) AC_CONFIG_FILES([lang/python/Makefile lang/python/version.py lang/python/tests/Makefile lang/python/src/Makefile lang/python/examples/Makefile lang/python/doc/Makefile]) AC_CONFIG_FILES([lang/python/setup.py], [chmod a+x lang/python/setup.py]) AC_OUTPUT echo " GPGME v${VERSION} has been configured as follows: Revision: mym4_revision (mym4_revision_dec) Platform: $host UI Server: $uiserver FD Passing: $use_descriptor_passing Language bindings: ${enabled_languages_v:-$enabled_languages} " if test "x${gpg_config_script_warn}" != x; then cat <. * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #include #ifdef HAVE_UNISTD_H # include #endif -#ifdef HAVE_SYS_SELECT_H -# include +#ifdef HAVE_POLL_H +# include #else -# ifdef HAVE_SYS_TIME_H -# include +# ifdef HAVE_SYS_SELECT_H +# include +# else +# ifdef HAVE_SYS_TIME_H +# include +# endif # endif #endif #ifdef HAVE_SYS_TYPES_H # include #endif #ifndef HAVE_W32_SYSTEM #include #endif #include "gpgme.h" #ifdef _MSC_VER typedef int pid_t; #endif #include "ath.h" #ifdef HAVE_W32_SYSTEM #include uintptr_t ath_self (void) { return (uintptr_t) GetCurrentThreadId (); } #else # ifdef __linux #include uintptr_t ath_self (void) { /* Just to catch users who don't use gpgme-pthread. */ return (uintptr_t) syscall (__NR_gettid); } # else uintptr_t ath_self (void) { return (uintptr_t) getpid (); } # endif #endif gpgme_ssize_t ath_read (int fd, void *buf, size_t nbytes) { return read (fd, buf, nbytes); } gpgme_ssize_t ath_write (int fd, const void *buf, size_t nbytes) { return write (fd, buf, nbytes); } +#if !defined(HAVE_POLL_H) gpgme_ssize_t ath_select (int nfd, fd_set *rset, fd_set *wset, fd_set *eset, struct timeval *timeout) { #ifdef HAVE_W32_SYSTEM return -1; /* Not supported. */ #else return select (nfd, rset, wset, eset, timeout); #endif } - +#endif gpgme_ssize_t ath_waitpid (pid_t pid, int *status, int options) { #ifdef HAVE_W32_SYSTEM return -1; /* Not supported. */ #else return waitpid (pid, status, options); #endif } int ath_accept (int s, struct sockaddr *addr, socklen_t *length_ptr) { #ifdef HAVE_W32_SYSTEM return -1; /* Not supported. */ #else return accept (s, addr, length_ptr); #endif } int ath_connect (int s, const struct sockaddr *addr, socklen_t length) { #ifdef HAVE_W32_SYSTEM return -1; /* Not supported. */ #else return connect (s, addr, length); #endif } int ath_sendmsg (int s, const struct msghdr *msg, int flags) { #ifdef HAVE_W32_SYSTEM return -1; /* Not supported. */ #else return sendmsg (s, msg, flags); #endif } int ath_recvmsg (int s, struct msghdr *msg, int flags) { #ifdef HAVE_W32_SYSTEM return -1; /* Not supported. */ #else return recvmsg (s, msg, flags); #endif } diff --git a/src/ath.h b/src/ath.h index d4c1c6fa..8243864a 100644 --- a/src/ath.h +++ b/src/ath.h @@ -1,85 +1,88 @@ /* ath.h - Interfaces for thread-safeness library. Copyright (C) 2002, 2003, 2004 g10 Code GmbH This file is part of GPGME. GPGME 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.1 of the License, or (at your option) any later version. GPGME 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 program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef ATH_H #define ATH_H #ifdef HAVE_STDINT_H #include #endif #ifdef HAVE_W32_SYSTEM /* fixme: Check how we did it in libgcrypt. */ struct msghdr { int dummy; }; typedef int socklen_t; # include # include #else /*!HAVE_W32_SYSTEM*/ - -# ifdef HAVE_SYS_SELECT_H -# include +# ifdef HAVE_POLL_H +# include # else -# ifdef HAVE_SYS_TIME_H -# include +# ifdef HAVE_SYS_SELECT_H +# include +# else +# ifdef HAVE_SYS_TIME_H +# include +# endif # endif # endif # ifdef HAVE_SYS_TYPES_H # include # endif # include #endif /*!HAVE_W32_SYSTEM*/ /* Define _ATH_EXT_SYM_PREFIX if you want to give all external symbols a prefix. */ #define _ATH_EXT_SYM_PREFIX _gpgme_ #ifdef _ATH_EXT_SYM_PREFIX #define _ATH_PREFIX1(x,y) x ## y #define _ATH_PREFIX2(x,y) _ATH_PREFIX1(x,y) #define _ATH_PREFIX(x) _ATH_PREFIX2(_ATH_EXT_SYM_PREFIX,x) #define ath_read _ATH_PREFIX(ath_read) #define ath_write _ATH_PREFIX(ath_write) #define ath_select _ATH_PREFIX(ath_select) #define ath_waitpid _ATH_PREFIX(ath_waitpid) #define ath_connect _ATH_PREFIX(ath_connect) #define ath_accept _ATH_PREFIX(ath_accept) #define ath_sendmsg _ATH_PREFIX(ath_sendmsg) #define ath_recvmsg _ATH_PREFIX(ath_recvmsg) #endif uintptr_t ath_self (void); /* Replacement for the POSIX functions, which can be used to allow other (user-level) threads to run. */ gpgme_ssize_t ath_read (int fd, void *buf, size_t nbytes); gpgme_ssize_t ath_write (int fd, const void *buf, size_t nbytes); gpgme_ssize_t ath_select (int nfd, fd_set *rset, fd_set *wset, fd_set *eset, struct timeval *timeout); gpgme_ssize_t ath_waitpid (pid_t pid, int *status, int options); int ath_accept (int s, struct sockaddr *addr, socklen_t *length_ptr); int ath_connect (int s, const struct sockaddr *addr, socklen_t length); int ath_sendmsg (int s, const struct msghdr *msg, int flags); int ath_recvmsg (int s, struct msghdr *msg, int flags); #endif /* ATH_H */ diff --git a/src/posix-io.c b/src/posix-io.c index 2a3a81fc..5c6cf1df 100644 --- a/src/posix-io.c +++ b/src/posix-io.c @@ -1,927 +1,1048 @@ /* posix-io.c - Posix I/O functions * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2004, 2005, 2007, 2010 g10 Code GmbH * * This file is part of GPGME. * * GPGME 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.1 of * the License, or (at your option) any later version. * * GPGME 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 program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #include #include #ifdef HAVE_STDINT_H # include #endif #include #include #include #include #include #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #include #ifdef HAVE_SYS_UIO_H # include #endif #include #include #ifdef USE_LINUX_GETDENTS # include # include # include #endif /*USE_LINUX_GETDENTS*/ #include "util.h" #include "priv-io.h" #include "sema.h" #include "ath.h" #include "debug.h" #ifdef USE_LINUX_GETDENTS /* This is not declared in public headers; getdents64(2) says that we must * define it ourselves. */ struct linux_dirent64 { ino64_t d_ino; off64_t d_off; unsigned short d_reclen; unsigned char d_type; char d_name[]; }; # define DIR_BUF_SIZE 1024 #endif /*USE_LINUX_GETDENTS*/ /* Return true if FD is valid file descriptor. */ #if 0 int _gpgme_is_fd_valid (int fd) { int dir_fd; char dir_buf[DIR_BUF_SIZE]; struct linux_dirent64 *dir_entry; int r, pos, x; const char *s; int result = 0; dir_fd = open ("/proc/self/fd", O_RDONLY | O_DIRECTORY); if (dir_fd != -1) { for (;;) { r = syscall(SYS_getdents64, dir_fd, dir_buf, DIR_BUF_SIZE); if (r == -1) break; /* Ooops */ if (r == 0) break; for (pos = 0; pos < r; pos += dir_entry->d_reclen) { dir_entry = (struct linux_dirent64 *) (dir_buf + pos); s = dir_entry->d_name; if (*s < '0' || *s > '9') continue; /* atoi is not guaranteed to be async-signal-safe. */ for (x = 0; *s >= '0' && *s <= '9'; s++) x = x * 10 + (*s - '0'); if (*s) continue; /* Does not look like a file descriptor. */ if (x == fd) { result = 1; goto leave; } } } leave: close (dir_fd); } return result; } #endif /*0*/ void _gpgme_io_subsystem_init (void) { struct sigaction act; sigaction (SIGPIPE, NULL, &act); if (act.sa_handler == SIG_DFL) { act.sa_handler = SIG_IGN; sigemptyset (&act.sa_mask); act.sa_flags = 0; sigaction (SIGPIPE, &act, NULL); } } /* Write the printable version of FD to the buffer BUF of length BUFLEN. The printable version is the representation on the command line that the child process expects. */ int _gpgme_io_fd2str (char *buf, int buflen, int fd) { return snprintf (buf, buflen, "%d", fd); } /* The table to hold notification handlers. We use a linear search and extend the table as needed. */ struct notify_table_item_s { int fd; /* -1 indicates an unused entry. */ _gpgme_close_notify_handler_t handler; void *value; }; typedef struct notify_table_item_s *notify_table_item_t; static notify_table_item_t notify_table; static size_t notify_table_size; DEFINE_STATIC_LOCK (notify_table_lock); int _gpgme_io_read (int fd, void *buffer, size_t count) { int nread; TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_read", NULL, "fd=%d buffer=%p count=%zu", fd, buffer, count); do { nread = _gpgme_ath_read (fd, buffer, count); } while (nread == -1 && errno == EINTR); TRACE_LOGBUFX (buffer, nread); return TRACE_SYSRES (nread); } int _gpgme_io_write (int fd, const void *buffer, size_t count) { int nwritten; TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_write", NULL, "fd=%d buffer=%p count=%zu", fd, buffer, count); TRACE_LOGBUFX (buffer, count); do { nwritten = _gpgme_ath_write (fd, buffer, count); } while (nwritten == -1 && errno == EINTR); return TRACE_SYSRES (nwritten); } int _gpgme_io_pipe (int filedes[2], int inherit_idx) { int saved_errno; int err; TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_pipe", NULL, "inherit_idx=%i (GPGME uses it for %s)", inherit_idx, inherit_idx ? "reading" : "writing"); err = pipe (filedes); if (err < 0) return TRACE_SYSRES (err); /* FIXME: Should get the old flags first. */ err = fcntl (filedes[1 - inherit_idx], F_SETFD, FD_CLOEXEC); saved_errno = errno; if (err < 0) { close (filedes[0]); close (filedes[1]); } errno = saved_errno; if (err) return TRACE_SYSRES (err); TRACE_SUC ("read fd=%d write fd=%d", filedes[0], filedes[1]); return 0; } int _gpgme_io_close (int fd) { int res; _gpgme_close_notify_handler_t handler = NULL; void *handler_value; int idx; TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", NULL, "fd=%d", fd); if (fd == -1) { errno = EINVAL; return TRACE_SYSRES (-1); } /* First call the notify handler. */ LOCK (notify_table_lock); for (idx=0; idx < notify_table_size; idx++) { if (notify_table[idx].fd == fd) { handler = notify_table[idx].handler; handler_value = notify_table[idx].value; notify_table[idx].handler = NULL; notify_table[idx].value = NULL; notify_table[idx].fd = -1; /* Mark slot as free. */ break; } } UNLOCK (notify_table_lock); if (handler) { TRACE_LOG ("invoking close handler %p/%p", handler, handler_value); handler (fd, handler_value); } /* Then do the close. */ res = close (fd); return TRACE_SYSRES (res); } int _gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler, void *value) { int res = 0; int idx; TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_set_close_notify", NULL, "fd=%d close_handler=%p/%p", fd, handler, value); assert (fd != -1); LOCK (notify_table_lock); for (idx=0; idx < notify_table_size; idx++) if (notify_table[idx].fd == -1) break; if (idx == notify_table_size) { /* We need to increase the size of the table. The approach we take is straightforward to minimize the risk of bugs. */ notify_table_item_t newtbl; size_t newsize = notify_table_size + 64; newtbl = calloc (newsize, sizeof *newtbl); if (!newtbl) { res = -1; goto leave; } for (idx=0; idx < notify_table_size; idx++) newtbl[idx] = notify_table[idx]; for (; idx < newsize; idx++) { newtbl[idx].fd = -1; newtbl[idx].handler = NULL; newtbl[idx].value = NULL; } free (notify_table); notify_table = newtbl; idx = notify_table_size; notify_table_size = newsize; } notify_table[idx].fd = fd; notify_table[idx].handler = handler; notify_table[idx].value = value; leave: UNLOCK (notify_table_lock); return TRACE_SYSRES (res); } int _gpgme_io_set_nonblocking (int fd) { int flags; int res; TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_set_nonblocking", NULL, "fd=%d", fd); flags = fcntl (fd, F_GETFL, 0); if (flags == -1) return TRACE_SYSRES (-1); flags |= O_NONBLOCK; res = fcntl (fd, F_SETFL, flags); return TRACE_SYSRES (res); } static long int get_max_fds (void) { const char *source = NULL; long int fds = -1; int rc; /* Under Linux we can figure out the highest used file descriptor by * reading /proc/self/fd. This is in the common cases much faster * than for example doing 4096 close calls where almost all of them * will fail. * * We can't use the normal opendir/readdir/closedir interface between * fork and exec in a multi-threaded process because opendir uses * malloc and thus a mutex which may deadlock with a malloc in another * thread. However, the underlying getdents system call is safe. */ #ifdef USE_LINUX_GETDENTS { int dir_fd; char dir_buf[DIR_BUF_SIZE]; struct linux_dirent64 *dir_entry; int r, pos; const char *s; int x; dir_fd = open ("/proc/self/fd", O_RDONLY | O_DIRECTORY); if (dir_fd != -1) { for (;;) { r = syscall(SYS_getdents64, dir_fd, dir_buf, DIR_BUF_SIZE); if (r == -1) { /* Fall back to other methods. */ fds = -1; break; } if (r == 0) break; for (pos = 0; pos < r; pos += dir_entry->d_reclen) { dir_entry = (struct linux_dirent64 *) (dir_buf + pos); s = dir_entry->d_name; if (*s < '0' || *s > '9') continue; /* atoi is not guaranteed to be async-signal-safe. */ for (x = 0; *s >= '0' && *s <= '9'; s++) x = x * 10 + (*s - '0'); if (!*s && x > fds && x != dir_fd) fds = x; } } close (dir_fd); } if (fds != -1) { fds++; source = "/proc"; } } #endif /*USE_LINUX_GETDENTS*/ #ifdef RLIMIT_NOFILE if (fds == -1) { struct rlimit rl; rc = getrlimit (RLIMIT_NOFILE, &rl); if (rc == 0) { source = "RLIMIT_NOFILE"; fds = rl.rlim_max; } } #endif #ifdef RLIMIT_OFILE if (fds == -1) { struct rlimit rl; rc = getrlimit (RLIMIT_OFILE, &rl); if (rc == 0) { source = "RLIMIT_OFILE"; fds = rl.rlim_max; } } #endif #ifdef _SC_OPEN_MAX if (fds == -1) { long int scres; scres = sysconf (_SC_OPEN_MAX); if (scres >= 0) { source = "_SC_OPEN_MAX"; return scres; } } #endif #ifdef OPEN_MAX if (fds == -1) { source = "OPEN_MAX"; fds = OPEN_MAX; } #endif #if !defined(RLIMIT_NOFILE) && !defined(RLIMIT_OFILE) \ && !defined(_SC_OPEN_MAX) && !defined(OPEN_MAX) #warning "No known way to get the maximum number of file descriptors." #endif if (fds == -1) { source = "arbitrary"; /* Arbitrary limit. */ fds = 1024; } /* AIX returns INT32_MAX instead of a proper value. We assume that * this is always an error and use a more reasonable limit. */ #ifdef INT32_MAX if (fds == INT32_MAX) { source = "aix-fix"; fds = 1024; } #endif TRACE (DEBUG_SYSIO, "gpgme:max_fds", NULL, "max fds=%ld (%s)", fds, source); return fds; } int _gpgme_io_waitpid (int pid, int hang, int *r_status, int *r_signal) { int status; pid_t ret; *r_status = 0; *r_signal = 0; do ret = _gpgme_ath_waitpid (pid, &status, hang? 0 : WNOHANG); while (ret == (pid_t)(-1) && errno == EINTR); if (ret == pid) { if (WIFSIGNALED (status)) { *r_status = 4; /* Need some value here. */ *r_signal = WTERMSIG (status); } else if (WIFEXITED (status)) *r_status = WEXITSTATUS (status); else *r_status = 4; /* Oops. */ return 1; } return 0; } /* Returns 0 on success, -1 on error. */ int _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags, struct spawn_fd_item_s *fd_list, void (*atfork) (void *opaque, int reserved), void *atforkvalue, pid_t *r_pid) { pid_t pid; int i; int status; int signo; TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_spawn", NULL, "path=%s", path); i = 0; while (argv[i]) { TRACE_LOG ("argv[%2i] = %s", i, argv[i]); i++; } for (i = 0; fd_list[i].fd != -1; i++) { if (fd_list[i].dup_to == -1) TRACE_LOG ("fd[%i] = 0x%x", i, fd_list[i].fd); else TRACE_LOG ("fd[%i] = 0x%x -> 0x%x", i,fd_list[i].fd,fd_list[i].dup_to); } pid = fork (); if (pid == -1) return TRACE_SYSRES (-1); if (!pid) { /* Intermediate child to prevent zombie processes. */ if ((pid = fork ()) == 0) { /* Child. */ int max_fds = -1; int fd; int seen_stdin = 0; int seen_stdout = 0; int seen_stderr = 0; if (atfork) atfork (atforkvalue, 0); /* First close all fds which will not be inherited. If we * have closefrom(2) we first figure out the highest fd we * do not want to close, then call closefrom, and on success * use the regular code to close all fds up to the start * point of closefrom. Note that Solaris' and FreeBSD's closefrom do * not return errors. */ #ifdef HAVE_CLOSEFROM { fd = -1; for (i = 0; fd_list[i].fd != -1; i++) if (fd_list[i].fd > fd) fd = fd_list[i].fd; fd++; #if defined(__sun) || defined(__FreeBSD__) || defined(__GLIBC__) closefrom (fd); max_fds = fd; #else /*!__sun */ while ((i = closefrom (fd)) && errno == EINTR) ; if (!i || errno == EBADF) max_fds = fd; #endif /*!__sun*/ } #endif /*HAVE_CLOSEFROM*/ if (max_fds == -1) max_fds = get_max_fds (); for (fd = 0; fd < max_fds; fd++) { for (i = 0; fd_list[i].fd != -1; i++) if (fd_list[i].fd == fd) break; if (fd_list[i].fd == -1) close (fd); } /* And now dup and close those to be duplicated. */ for (i = 0; fd_list[i].fd != -1; i++) { int child_fd; int res; if (fd_list[i].dup_to != -1) child_fd = fd_list[i].dup_to; else child_fd = fd_list[i].fd; if (child_fd == 0) seen_stdin = 1; else if (child_fd == 1) seen_stdout = 1; else if (child_fd == 2) seen_stderr = 1; if (fd_list[i].dup_to == -1) continue; res = dup2 (fd_list[i].fd, fd_list[i].dup_to); if (res < 0) { #if 0 /* FIXME: The debug file descriptor is not dup'ed anyway, so we can't see this. */ TRACE_LOG ("dup2 failed in child: %s\n", strerror (errno)); #endif _exit (8); } close (fd_list[i].fd); } if (! seen_stdin || ! seen_stdout || !seen_stderr) { fd = open ("/dev/null", O_RDWR); if (fd == -1) { /* The debug file descriptor is not dup'ed, so we can't do a trace output. */ _exit (8); } /* Make sure that the process has connected stdin. */ if (! seen_stdin && fd != 0) { if (dup2 (fd, 0) == -1) _exit (8); } if (! seen_stdout && fd != 1) { if (dup2 (fd, 1) == -1) _exit (8); } if (! seen_stderr && fd != 2) { if (dup2 (fd, 2) == -1) _exit (8); } if (fd != 0 && fd != 1 && fd != 2) close (fd); } execv (path, (char *const *) argv); /* Hmm: in that case we could write a special status code to the status-pipe. */ _exit (8); /* End child. */ } if (pid == -1) _exit (1); else _exit (0); } TRACE_LOG ("waiting for child process pid=%i", pid); _gpgme_io_waitpid (pid, 1, &status, &signo); if (status) return TRACE_SYSRES (-1); for (i = 0; fd_list[i].fd != -1; i++) { if (! (flags & IOSPAWN_FLAG_NOCLOSE)) _gpgme_io_close (fd_list[i].fd); /* No handle translation. */ fd_list[i].peer_name = fd_list[i].fd; } if (r_pid) *r_pid = pid; return TRACE_SYSRES (0); } /* Select on the list of fds. Returns: -1 = error, 0 = timeout or nothing to select, > 0 = number of signaled fds. */ -int -_gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock) +#ifdef HAVE_POLL_H +static int +_gpgme_io_select_poll (struct io_select_fd_s *fds, size_t nfds, int nonblock) +{ + struct pollfd *poll_fds = NULL; + nfds_t poll_nfds; + /* Use a 1s timeout. */ + int timeout = 1000; + unsigned int i; + int any; + int count; + void *dbg_help = NULL; + TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_select", NULL, + "nfds=%zu, nonblock=%u", nfds, nonblock); + + + if (nonblock) + timeout = 0; + + poll_fds = malloc (sizeof (*poll_fds)*nfds); + if (!poll_fds) + return -1; + + poll_nfds = 0; + + TRACE_SEQ (dbg_help, "poll on [ "); + + any = 0; + for (i = 0; i < nfds; i++) + { + if (fds[i].fd == -1) + continue; + if (fds[i].for_read || fds[i].for_write) + { + poll_fds[poll_nfds].fd = fds[i].fd; + poll_fds[poll_nfds].events = 0; + poll_fds[poll_nfds].revents = 0; + if (fds[i].for_read) + { + poll_fds[poll_nfds].events |= POLLIN; + TRACE_ADD1 (dbg_help, "r=%d ", fds[i].fd); + } + if (fds[i].for_write) + { + poll_fds[poll_nfds].events |= POLLOUT; + TRACE_ADD1 (dbg_help, "w=%d ", fds[i].fd); + } + poll_nfds++; + any = 1; + } + fds[i].signaled = 0; + } + TRACE_END (dbg_help, "]"); + if (!any) + { + free (poll_fds); + return TRACE_SYSRES (0); + } + + do + count = poll (poll_fds, poll_nfds, timeout); + while (count < 0 && (errno == EINTR || errno == EAGAIN)); + if (count < 0) + { + int save_errno = errno; + free (poll_fds); + errno = save_errno; + return TRACE_SYSRES (-1); + } + + TRACE_SEQ (dbg_help, "poll OK [ "); + if (TRACE_ENABLED (dbg_help)) + { + poll_nfds = 0; + for (i = 0; i < nfds; i++) + { + if (fds[i].fd == -1) + continue; + if ((poll_fds[poll_nfds].revents & (POLLIN|POLLHUP))) + TRACE_ADD1 (dbg_help, "r=%d ", i); + if ((poll_fds[poll_nfds].revents & POLLOUT)) + TRACE_ADD1 (dbg_help, "w=%d ", i); + poll_nfds++; + } + TRACE_END (dbg_help, "]"); + } + + poll_nfds = 0; + for (i = 0; i < nfds; i++) + { + if (fds[i].fd == -1) + continue; + if (fds[i].for_read || fds[i].for_write) + { + short events_to_be_checked = 0; + + if (fds[i].for_read) + events_to_be_checked |= (POLLIN|POLLHUP); + if (fds[i].for_write) + events_to_be_checked |= POLLOUT; + if ((poll_fds[poll_nfds].revents & events_to_be_checked)) + fds[i].signaled = 1; + + poll_nfds++; + } + } + + free (poll_fds); + return TRACE_SYSRES (count); +} +#else +static int +_gpgme_io_select_select (struct io_select_fd_s *fds, size_t nfds, int nonblock) { fd_set readfds; fd_set writefds; unsigned int i; int any; int max_fd; int n; int count; /* Use a 1s timeout. */ struct timeval timeout = { 1, 0 }; void *dbg_help = NULL; TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_select", NULL, "nfds=%zu, nonblock=%u", nfds, nonblock); FD_ZERO (&readfds); FD_ZERO (&writefds); max_fd = 0; if (nonblock) timeout.tv_sec = 0; TRACE_SEQ (dbg_help, "select on [ "); any = 0; for (i = 0; i < nfds; i++) { if (fds[i].fd == -1) continue; if (fds[i].for_read) { if (fds[i].fd >= FD_SETSIZE) { TRACE_END (dbg_help, " -BAD- ]"); gpg_err_set_errno (EMFILE); return TRACE_SYSRES (-1); } assert (!FD_ISSET (fds[i].fd, &readfds)); FD_SET (fds[i].fd, &readfds); if (fds[i].fd > max_fd) max_fd = fds[i].fd; TRACE_ADD1 (dbg_help, "r=%d ", fds[i].fd); any = 1; } else if (fds[i].for_write) { if (fds[i].fd >= FD_SETSIZE) { TRACE_END (dbg_help, " -BAD- ]"); gpg_err_set_errno (EMFILE); return TRACE_SYSRES (-1); } assert (!FD_ISSET (fds[i].fd, &writefds)); FD_SET (fds[i].fd, &writefds); if (fds[i].fd > max_fd) max_fd = fds[i].fd; TRACE_ADD1 (dbg_help, "w=%d ", fds[i].fd); any = 1; } fds[i].signaled = 0; } TRACE_END (dbg_help, "]"); if (!any) return TRACE_SYSRES (0); do { count = _gpgme_ath_select (max_fd + 1, &readfds, &writefds, NULL, &timeout); } while (count < 0 && errno == EINTR); if (count < 0) return TRACE_SYSRES (-1); TRACE_SEQ (dbg_help, "select OK [ "); if (TRACE_ENABLED (dbg_help)) { for (i = 0; i <= max_fd; i++) { if (FD_ISSET (i, &readfds)) TRACE_ADD1 (dbg_help, "r=%d ", i); if (FD_ISSET (i, &writefds)) TRACE_ADD1 (dbg_help, "w=%d ", i); } TRACE_END (dbg_help, "]"); } /* The variable N is used to optimize it a little bit. */ for (n = count, i = 0; i < nfds && n; i++) { if (fds[i].fd == -1) ; else if (fds[i].for_read) { if (FD_ISSET (fds[i].fd, &readfds)) { fds[i].signaled = 1; n--; } } else if (fds[i].for_write) { if (FD_ISSET (fds[i].fd, &writefds)) { fds[i].signaled = 1; n--; } } } return TRACE_SYSRES (count); } +#endif +int +_gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock) +{ +#ifdef HAVE_POLL_H + return _gpgme_io_select_poll (fds, nfds, nonblock); +#else + return _gpgme_io_select_select (fds, nfds, nonblock); +#endif +} int _gpgme_io_recvmsg (int fd, struct msghdr *msg, int flags) { int nread; int saved_errno; struct iovec *iov; TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_recvmsg", NULL, "fd=%d msg=%p flags=%i", fd, msg, flags); nread = 0; iov = msg->msg_iov; while (iov < msg->msg_iov + msg->msg_iovlen) { nread += iov->iov_len; iov++; } TRACE_LOG ("about to receive %d bytes", nread); do { nread = _gpgme_ath_recvmsg (fd, msg, flags); } while (nread == -1 && errno == EINTR); saved_errno = errno; if (nread > 0) { int nr = nread; iov = msg->msg_iov; while (nr > 0) { int len = nr > iov->iov_len ? iov->iov_len : nr; TRACE_LOGBUFX (msg->msg_iov->iov_base, len); iov++; nr -= len; } } errno = saved_errno; return TRACE_SYSRES (nread); } int _gpgme_io_sendmsg (int fd, const struct msghdr *msg, int flags) { int nwritten; struct iovec *iov; TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_sendmsg", NULL, "fd=%d msg=%p flags=%i", fd, msg, flags); nwritten = 0; iov = msg->msg_iov; while (iov < msg->msg_iov + msg->msg_iovlen) { nwritten += iov->iov_len; iov++; } TRACE_LOG ("about to receive %d bytes", nwritten); iov = msg->msg_iov; while (nwritten > 0) { int len = nwritten > iov->iov_len ? iov->iov_len : nwritten; TRACE_LOGBUFX (msg->msg_iov->iov_base, len); iov++; nwritten -= len; } do { nwritten = _gpgme_ath_sendmsg (fd, msg, flags); } while (nwritten == -1 && errno == EINTR); return TRACE_SYSRES (nwritten); } int _gpgme_io_dup (int fd) { int new_fd; do new_fd = dup (fd); while (new_fd == -1 && errno == EINTR); TRACE (DEBUG_SYSIO, "_gpgme_io_dup", NULL, "fd=%d -> fd=%d", fd, new_fd); return new_fd; } int _gpgme_io_socket (int domain, int type, int proto) { int res; TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_socket", NULL, "domain=%d type=%i proto=%i", domain, type, proto); res = socket (domain, type, proto); return TRACE_SYSRES (res); } int _gpgme_io_connect (int fd, struct sockaddr *addr, int addrlen) { int res; TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_connect", NULL, "fd=%d addr=%p addrlen=%i", fd, addr, addrlen); do res = ath_connect (fd, addr, addrlen); while (res == -1 && errno == EINTR); return TRACE_SYSRES (res); } diff --git a/tests/gpg/t-cancel.c b/tests/gpg/t-cancel.c index 48158fbd..d5fb6664 100644 --- a/tests/gpg/t-cancel.c +++ b/tests/gpg/t-cancel.c @@ -1,272 +1,335 @@ /* t-thread-cancel.c - Regression test. * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001, 2003, 2004 g10 Code GmbH * * This file is part of GPGME. * * GPGME 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.1 of * the License, or (at your option) any later version. * * GPGME 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 program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ /* We need to include config.h so that we know whether we are building with large file system (LFS) support. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #ifdef HAVE_SYS_TIME_H # include #endif #include #include #include -#ifdef HAVE_SYS_SELECT_H -# include +#ifdef HAVE_POLL_H +# include +#else +# ifdef HAVE_SYS_SELECT_H +# include +# else +# ifdef HAVE_SYS_TIME_H +# include +# endif +# endif #endif #include #include "t-support.h" struct op_result { int done; gpgme_error_t err; }; static struct op_result op_result; struct one_fd { int fd; int dir; gpgme_io_cb_t fnc; void *fnc_data; }; #define FDLIST_MAX 32 static struct one_fd fdlist[FDLIST_MAX]; static pthread_mutex_t lock; static gpgme_error_t add_io_cb (void *data, int fd, int dir, gpgme_io_cb_t fnc, void *fnc_data, void **r_tag) { struct one_fd *fds = data; int i; pthread_mutex_lock (&lock); for (i = 0; i < FDLIST_MAX; i++) { if (fds[i].fd == -1) { fds[i].fd = fd; fds[i].dir = dir; fds[i].fnc = fnc; fds[i].fnc_data = fnc_data; break; } } pthread_mutex_unlock (&lock); if (i == FDLIST_MAX) return gpgme_err_make (GPG_ERR_SOURCE_USER_1, GPG_ERR_GENERAL); *r_tag = &fds[i]; return 0; } static void remove_io_cb (void *tag) { struct one_fd *fd = tag; pthread_mutex_lock (&lock); fd->fd = -1; pthread_mutex_unlock (&lock); } static void io_event (void *data, gpgme_event_io_t type, void *type_data) { struct op_result *result = data; if (type == GPGME_EVENT_DONE) { result->done = 1; result->err = * (gpgme_error_t *) type_data; } } +#ifdef HAVE_POLL_H +static int +do_select (void) +{ + struct pollfd poll_fds[FDLIST_MAX]; + nfds_t poll_nfds; + int i, n; + int any = 0; + + pthread_mutex_lock (&lock); + poll_nfds = 0; + for (i = 0; i < FDLIST_MAX; i++) + if (fdlist[i].fd != -1) + { + poll_fds[poll_nfds].fd = fdlist[i].fd; + poll_fds[poll_nfds].events = 0; + poll_fds[poll_nfds].revents = 0; + if (fdlist[i].dir) + poll_fds[poll_nfds].events |= POLLIN; + else + poll_fds[poll_nfds].events |= POLLOUT; + poll_nfds++; + } + pthread_mutex_unlock (&lock); + + do + { + n = poll (poll_fds, poll_nfds, 1000); + } + while (n < 0 && (errno == EINTR || errno == EAGAIN)); + + if (n < 0) + return n; /* Error or timeout. */ + + pthread_mutex_lock (&lock); + poll_nfds = 0; + for (i = 0; i < FDLIST_MAX && n; i++) + { + if (fdlist[i].fd != -1) + { + if ((poll_fds[poll_nfds++].revents + & (fdlist[i].dir ? (POLLIN|POLLHUP) : POLLOUT))) + { + assert (n); + n--; + any = 1; + (*fdlist[i].fnc) (fdlist[i].fnc_data, fdlist[i].fd); + } + } + } + pthread_mutex_unlock (&lock); + return any; +} +#else static int do_select (void) { fd_set rfds; fd_set wfds; int i, n; int any = 0; struct timeval tv; pthread_mutex_lock (&lock); FD_ZERO (&rfds); FD_ZERO (&wfds); for (i = 0; i < FDLIST_MAX; i++) if (fdlist[i].fd != -1) FD_SET (fdlist[i].fd, fdlist[i].dir ? &rfds : &wfds); pthread_mutex_unlock (&lock); tv.tv_sec = 0; tv.tv_usec = 1000; do { n = select (FD_SETSIZE, &rfds, &wfds, NULL, &tv); } while (n < 0 && errno == EINTR); if (n < 0) return n; /* Error or timeout. */ pthread_mutex_lock (&lock); for (i = 0; i < FDLIST_MAX && n; i++) { if (fdlist[i].fd != -1) { if (FD_ISSET (fdlist[i].fd, fdlist[i].dir ? &rfds : &wfds)) { assert (n); n--; any = 1; (*fdlist[i].fnc) (fdlist[i].fnc_data, fdlist[i].fd); } } } pthread_mutex_unlock (&lock); return any; } +#endif static int my_wait (void) { int n; do { n = do_select (); } while (n >= 0 && !op_result.done); return 0; } static struct gpgme_io_cbs io_cbs = { add_io_cb, fdlist, remove_io_cb, io_event, &op_result }; static void * thread_cancel (void *data) { gpgme_ctx_t ctx = data; gpgme_error_t err; usleep (100000); err = gpgme_cancel (ctx); fail_if_err (err); return NULL; } int main (void) { gpgme_ctx_t ctx; gpgme_error_t err; gpgme_engine_info_t info; int i; pthread_mutexattr_t attr; pthread_t tcancel; const char *parms = "\n" "Key-Type: RSA\n" "Key-Length: 2048\n" "Subkey-Type: RSA\n" "Subkey-Length: 2048\n" "Name-Real: Joe Tester\n" "Name-Comment: (pp=abc)\n" "Name-Email: joe@foo.bar\n" "Expire-Date: 0\n" "Passphrase: abc\n" "\n"; init_gpgme (GPGME_PROTOCOL_OpenPGP); err = gpgme_get_engine_info (&info); fail_if_err (err); /* The mutex must be recursive, since remove_io_cb (which acquires a lock) can be called while holding a lock acquired in do_select. */ pthread_mutexattr_init (&attr); pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init (&lock, &attr); pthread_mutexattr_destroy (&attr); for (i = 0; i < FDLIST_MAX; i++) fdlist[i].fd = -1; err = gpgme_new (&ctx); fail_if_err (err); gpgme_set_armor (ctx, 1); gpgme_set_io_cbs (ctx, &io_cbs); op_result.done = 0; pthread_create (&tcancel, NULL, thread_cancel, ctx); err = gpgme_op_genkey_start (ctx, parms, NULL, NULL); fail_if_err (err); my_wait (); pthread_join (tcancel, NULL); if (op_result.err) { if (gpgme_err_code (op_result.err) == GPG_ERR_CANCELED) fputs ("Successfully cancelled\n", stdout); else { fprintf (stderr, "%s:%i: Operation finished with unexpected error: %s\n", __FILE__, __LINE__, gpgme_strerror (op_result.err)); exit (1); } } else fputs ("Successfully finished before cancellation\n", stdout); gpgme_release (ctx); return 0; } diff --git a/tests/gpg/t-eventloop.c b/tests/gpg/t-eventloop.c index b31764ea..0106670b 100644 --- a/tests/gpg/t-eventloop.c +++ b/tests/gpg/t-eventloop.c @@ -1,229 +1,290 @@ /* t-eventloop.c - Regression test. * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH * * This file is part of GPGME. * * GPGME 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.1 of * the License, or (at your option) any later version. * * GPGME 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 program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ /* We need to include config.h so that we know whether we are building with large file system (LFS) support. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include -#include +#ifdef HAVE_POLL_H +# include +#else +# ifdef HAVE_SYS_SELECT_H +# include +# else +# ifdef HAVE_SYS_TIME_H +# include +# endif +# endif +#endif #include #include "t-support.h" /* Stripped down version of gpgme/wait.c. */ struct op_result { int done; gpgme_error_t err; }; struct op_result op_result; struct one_fd { int fd; int dir; gpgme_io_cb_t fnc; void *fnc_data; }; #define FDLIST_MAX 32 struct one_fd fdlist[FDLIST_MAX]; gpgme_error_t add_io_cb (void *data, int fd, int dir, gpgme_io_cb_t fnc, void *fnc_data, void **r_tag) { struct one_fd *fds = data; int i; for (i = 0; i < FDLIST_MAX; i++) { if (fds[i].fd == -1) { fds[i].fd = fd; fds[i].dir = dir; fds[i].fnc = fnc; fds[i].fnc_data = fnc_data; break; } } if (i == FDLIST_MAX) return gpgme_err_make (GPG_ERR_SOURCE_USER_1, GPG_ERR_GENERAL); *r_tag = &fds[i]; return 0; } void remove_io_cb (void *tag) { struct one_fd *fd = tag; fd->fd = -1; } void io_event (void *data, gpgme_event_io_t type, void *type_data) { struct op_result *result = data; if (type == GPGME_EVENT_DONE) { result->done = 1; result->err = * (gpgme_error_t *) type_data; } } +#ifdef HAVE_POLL_H +int +do_select (void) +{ + struct pollfd poll_fds[FDLIST_MAX]; + nfds_t poll_nfds; + int i, n; + int any = 0; + + poll_nfds = 0; + for (i = 0; i < FDLIST_MAX; i++) + if (fdlist[i].fd != -1) + { + poll_fds[poll_nfds].fd = fdlist[i].fd; + poll_fds[poll_nfds].events = 0; + poll_fds[poll_nfds].revents = 0; + if (fdlist[i].dir) + poll_fds[poll_nfds].events |= POLLIN; + else + poll_fds[poll_nfds].events |= POLLOUT; + poll_nfds++; + } + + do + { + n = poll (poll_fds, poll_nfds, 1000); + } + while (n < 0 && (errno == EINTR || errno == EAGAIN)); + + if (n < 0) + return n; /* Error or timeout. */ + + poll_nfds = 0; + for (i = 0; i < FDLIST_MAX && n; i++) + { + if (fdlist[i].fd != -1) + { + if ((poll_fds[poll_nfds++].revents + & (fdlist[i].dir ? (POLLIN|POLLHUP) : POLLOUT))) + { + assert (n); + n--; + any = 1; + (*fdlist[i].fnc) (fdlist[i].fnc_data, fdlist[i].fd); + } + } + } + return any; +} +#else int do_select (void) { fd_set rfds; fd_set wfds; int i, n; int any = 0; struct timeval tv; FD_ZERO (&rfds); FD_ZERO (&wfds); for (i = 0; i < FDLIST_MAX; i++) if (fdlist[i].fd != -1) FD_SET (fdlist[i].fd, fdlist[i].dir ? &rfds : &wfds); tv.tv_sec = 0; tv.tv_usec = 1000; do { n = select (FD_SETSIZE, &rfds, &wfds, NULL, &tv); } while (n < 0 && errno == EINTR); if (n < 0) return n; /* Error or timeout. */ for (i = 0; i < FDLIST_MAX && n; i++) { if (fdlist[i].fd != -1) { if (FD_ISSET (fdlist[i].fd, fdlist[i].dir ? &rfds : &wfds)) { assert (n); n--; any = 1; (*fdlist[i].fnc) (fdlist[i].fnc_data, fdlist[i].fd); } } } return any; } +#endif int my_wait (void) { int n; do { n = do_select (); } while (n >= 0 && !op_result.done); return 0; } struct gpgme_io_cbs io_cbs = { add_io_cb, fdlist, remove_io_cb, io_event, &op_result }; int main (void) { gpgme_ctx_t ctx; gpgme_error_t err; gpgme_data_t in, out; gpgme_key_t key[3] = { NULL, NULL, NULL }; int i; init_gpgme (GPGME_PROTOCOL_OpenPGP); for (i = 0; i < FDLIST_MAX; i++) fdlist[i].fd = -1; err = gpgme_engine_check_version (GPGME_PROTOCOL_OpenPGP); fail_if_err (err); err = gpgme_new (&ctx); fail_if_err (err); gpgme_set_armor (ctx, 1); gpgme_set_io_cbs (ctx, &io_cbs); op_result.done = 0; err = gpgme_data_new_from_mem (&in, "Hallo Leute\n", 12, 0); fail_if_err (err); err = gpgme_data_new (&out); fail_if_err (err); err = gpgme_get_key (ctx, "A0FF4590BB6122EDEF6E3C542D727CC768697734", &key[0], 0); fail_if_err (err); err = gpgme_get_key (ctx, "D695676BDCEDCC2CDD6152BCFE180B1DA9E3B0B2", &key[1], 0); fail_if_err (err); err = gpgme_op_encrypt_start (ctx, key, GPGME_ENCRYPT_ALWAYS_TRUST, in, out); fail_if_err (err); my_wait (); fail_if_err (op_result.err); fail_if_err (err); fflush (NULL); fputs ("Begin Result:\n", stdout); print_data (out); fputs ("End Result.\n", stdout); gpgme_key_unref (key[0]); gpgme_key_unref (key[1]); gpgme_data_release (in); gpgme_data_release (out); gpgme_release (ctx); return 0; }