diff --git a/Makefile.am b/Makefile.am index 7af1ae7..a08b1d2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,143 +1,144 @@ # Copyright (C) 2010 Red Hat, Inc. # # Authors: Andrew Beekhof # Steven Dake # Angus Salkeld # # This file is part of libqb. # # libqb 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. # # libqb 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 libqb. If not, see . SPEC = $(PACKAGE_NAME).spec EXTRA_DIST = autogen.sh $(SPEC).in \ build-aux/git-version-gen \ build-aux/gitlog-to-changelog \ build-aux/release.mk \ .version AUTOMAKE_OPTIONS = foreign MAINTAINERCLEANFILES = Makefile.in aclocal.m4 configure depcomp \ config.guess config.sub missing install-sh \ autoheader automake autoconf libtool libtoolize \ ltmain.sh compile build-aux/test-driver ACLOCAL_AMFLAGS = -I m4 dist_doc_DATA = COPYING INSTALL README.markdown SUBDIRS = include lib doxygen2man docs tools tests examples dist-clean-local: rm -f .snapshot-version autoconf automake autoheader # this will also get rid of "libtoolized" m4 files maintainer-clean-local: rm -f .version .tarball-version rm -f $(patsubst $(top_srcdir)/m4/ax_%,,$(wildcard $(top_srcdir)/m4/*.m4)) clean-local: rm -rf $(SPEC) $(DIST_ARCHIVES) ## make rpm/srpm section. $(SPEC): $(SPEC).in @rm -f $@-t $@ @date="$(shell LC_ALL=C date "+%a %b %d %Y")" && \ if [ -s .tarball-version ] || [ -n "$$(echo '${VERSION}' | grep -v UNKNOWN)" ]; then \ ver="$$({ cat .tarball-version 2>/dev/null; echo '${VERSION}'; } | head -n1)" && \ gitver=$$(echo $$ver | sed \ -e 's|^\(v[0-9][0-9]*\.[0-9][0-9]*\)\([^.].*\)\?$$|\1.0\2|') && \ rpmver=$$(echo $$gitver | sed 's/-.*//') && \ alphatag="" && \ dirty=$$(echo $$gitver | sed -n 's/[^-][^-]*-//p') && \ numcomm="0"; \ else \ ver="$(shell git describe --abbrev=4 --match='v*' --tags HEAD 2>/dev/null)" && \ gitver=$$(echo $$ver | sed \ -e 's|^\(v[0-9][0-9]*\.[0-9][0-9]*\)\([^.].*\)\?$$|\1.0\2|') && \ rpmver=$$(echo $$gitver | sed -e "s/^v//" -e "s/-.*//g") && \ alphatag=$$(echo $$gitver | sed -e "s/.*-//" -e "s/^g//") && \ vtag=$$(echo $$ver | sed -e "s/-.*//g") && \ numcomm=$$(git rev-list $$vtag..HEAD | wc -l) && \ git update-index --refresh > /dev/null 2>&1 || true && \ dirty=$$(git diff-index --name-only HEAD 2>/dev/null | sed 's/..*/dirty/'); \ fi && \ if [ "$$numcomm" = "0" ]; then \ sed \ -e "s#@version@#$$rpmver#g" \ -e "s#%glo.*alpha.*##g" \ -e "s#%glo.*numcomm.*##g" \ -e "s#@dirty@#$$dirty#g" \ -e "s#@date@#$$date#g" \ $< > $@-t; \ else \ sed \ -e "s#@version@#$$rpmver#g" \ -e "s#@alphatag@#$$alphatag#g" \ -e "s#@numcomm@#$$numcomm#g" \ -e "s#@dirty@#$$dirty#g" \ -e "s#@date@#$$date#g" \ $< > $@-t; \ fi; \ if [ -z "$$dirty" ]; then sed -i -e "s#%glo.*dirty.*##g" $@-t; fi @chmod a-w $@-t $(AM_V_GEN)mv $@-t $@ RPMBUILDOPTS = --define "_sourcedir $(abs_builddir)" \ --define "_specdir $(abs_builddir)" \ --define "_builddir $(abs_builddir)" \ --define "_srcrpmdir $(abs_builddir)" \ --define "_rpmdir $(abs_builddir)" # generates both .tar.[gx]z (backward compatibility) tarball: dist srpm: clean autoreconf -if $(MAKE) $(SPEC) dist-xz rpmbuild $(RPMBUILDOPTS) --nodeps -bs $(SPEC) rpm: clean autoreconf -if $(MAKE) $(SPEC) dist-xz rpmbuild $(RPMBUILDOPTS) -ba $(SPEC) # release/versioning BUILT_SOURCES = .version .version: echo $(VERSION) > $@-t && mv $@-t $@ # untaint configure.ac when modified upon obtaining the "yanked" snapshot form dist-hook: gen-ChangeLog + echo $(SOURCE_EPOCH) > $(distdir)/source_epoch echo $(VERSION) | tee $(distdir)/.tarball-version | grep -Eqv '\-yank' \ || sed "s/\(.*git-version-gen[^']*[']\)[^']*/\1\$$Format:%h??%D\$$/" \ $(distdir)/configure.ac > $(builddir)/configure.ac-t if [ -f $(builddir)/configure.ac-t ]; then \ touch -r $(distdir)/configure.ac $(builddir)/configure.ac-t; \ chmod u+w $(builddir)/configure.ac-t; \ mv $(builddir)/configure.ac-t $(distdir)/configure.ac; \ fi gen_start_date = 2000-01-01 .PHONY: gen-ChangeLog gen-ChangeLog: if test -d .git; then \ $(top_srcdir)/build-aux/gitlog-to-changelog \ --since=$(gen_start_date) > $(distdir)/cl-t; \ rm -f $(distdir)/ChangeLog; \ mv $(distdir)/cl-t $(distdir)/ChangeLog; \ fi diff --git a/build-aux/update-copyright.sh b/build-aux/update-copyright.sh new file mode 100755 index 0000000..0146d1a --- /dev/null +++ b/build-aux/update-copyright.sh @@ -0,0 +1,29 @@ +#!/bin/sh +# +# Copyright (C) 2017-2019 Red Hat, Inc. All rights reserved. +# +# Author: Fabio M. Di Nitto +# +# This software licensed under GPL-2.0+ +# + +# script to update copyright dates across the tree + +enddate=$(date +%Y) + +input=$(grep -ril -e "Copyright.*Red Hat" |grep -v .swp |grep -v update-copyright |grep -v doxyxml.c) +for i in $input; do + startdate=$(git log --follow "" | grep ^Date: | tail -n 1 | awk '{print $6}') + if [ "$startdate" != "$enddate" ]; then + sed -i -e 's#Copyright (C).*Red Hat#Copyright (C) '$startdate'-'$enddate' Red Hat#g' $i + else + sed -i -e 's#Copyright (C).*Red Hat#Copyright (C) '$startdate' Red Hat#g' $i + fi +done + +input=$(find . -type f |grep -v ".git") +for i in $input; do + if [ -z "$(grep -i "Copyright" $i)" ]; then + echo "WARNING: $i appears to be missing Copyright information" + fi +done diff --git a/configure.ac b/configure.ac index 014f87f..c6ece26 100644 --- a/configure.ac +++ b/configure.ac @@ -1,851 +1,892 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.61]) dnl inject zero as a "patch" component of the version if missing in tag; dnl care to bump "X.Y.Z-yank" template below upon each release very desirable AC_INIT([libqb], m4_esyscmd([build-aux/git-version-gen $(echo '$Format:%h??%D$'\ | sed -ne 's/^[$]Format:[^$]*[$]$/.tarball-version/p;tend'\ -e 's/.*tag: v\([^, ][^, ]*\).*/\1-yank/;tv'\ -e 's/^\([[:xdigit:]][[:xdigit:]]*\).*/1.9.0-yank\1/'\ -e ':v' -e 'w .snapshot-version' -e 'i\ .snapshot-version' -e ':end' -e 'q')\ 's/^\(v[0-9][0-9]*\.[0-9][0-9]*\)\([^.].*\)\?$/\1.0\2/']), [developers@clusterlabs.org]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_SRCDIR([lib/ringbuffer.c]) AC_CONFIG_HEADERS([include/config.h include/qb/qbconfig.h]) AC_USE_SYSTEM_EXTENSIONS AM_INIT_AUTOMAKE([-Wno-portability dist-xz subdir-objects]) dnl automake >= 1.11 offers --enable-silent-rules for suppressing the output from dnl normal compilation. When a failure occurs, it will then display the full dnl command line dnl Wrap in m4_ifdef to avoid breaking on older platforms m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) LT_PREREQ([2.2.6]) # --enable-new-dtags: Use RUNPATH instead of RPATH. # It is necessary to have this done before libtool does linker detection. # See also: https://github.com/kronosnet/kronosnet/issues/107 AX_CHECK_LINK_FLAG([-Wl,--enable-new-dtags], [AM_LDFLAGS=-Wl,--enable-new-dtags], [AC_MSG_ERROR(["Linker support for --enable-new-dtags is required"])]) AC_SUBST([AM_LDFLAGS]) saved_LDFLAGS="$LDFLAGS" LDFLAGS="$AM_LDFLAGS $LDFLAGS" LT_INIT LDFLAGS="$saved_LDFLAGS" AC_CONFIG_MACRO_DIR([m4]) AC_CANONICAL_HOST AC_PROG_LIBTOOL AC_LANG([C]) dnl Fix default variables - "prefix" variable if not specified if test "$prefix" = "NONE"; then prefix="/usr" if test "$localstatedir" = "\${prefix}/var"; then localstatedir="/var" fi if test "$sysconfdir" = "\${prefix}/etc"; then sysconfdir="/etc" fi if test "$libdir" = "\${exec_prefix}/lib"; then if test -e /usr/lib64; then libdir="/usr/lib64" else libdir="/usr/lib" fi fi fi if test "$srcdir" = "."; then AC_MSG_NOTICE([building in place srcdir:$srcdir]) AC_DEFINE([BUILDING_IN_PLACE], 1, [building in place]) else AC_MSG_NOTICE([building out of tree srcdir:$srcdir]) fi # Checks for programs. # check stolen from gnulib/m4/gnu-make.m4 if ! ${MAKE-make} --version /cannot/make/this >/dev/null 2>&1; then AC_MSG_ERROR([you don't seem to have GNU make; it is required]) fi AC_PROG_CXX AM_CONDITIONAL(HAVE_GXX, [test "x$GXX" = xyes]) AC_PROG_AWK AC_PROG_CC AC_PROG_CPP AM_PROG_CC_C_O AC_PROG_CC_C99 if test "x$ac_cv_prog_cc_c99" = "xno"; then AC_MSG_ERROR(["C99 support is required"]) fi AC_PROG_INSTALL AC_PROG_LN_S AC_PROG_MAKE_SET AC_CHECK_PROGS([PKGCONFIG], [pkg-config]) AC_CHECK_PROGS([DOXYGEN], [doxygen]) AM_CONDITIONAL(HAVE_DOXYGEN, test -n "${DOXYGEN}") AC_CHECK_TOOLS([NM], [eu-nm nm], [:]) AC_CHECK_TOOLS([READELF], [eu-readelf readelf], [:]) AM_PATH_PYTHON([2.6],, [:]) AM_CONDITIONAL([HAVE_PYTHON], [test "$PYTHON" != :]) ## local helper functions # this function checks if CC support options passed as # args. Global CFLAGS are ignored during this test. cc_supports_flag() { BACKUP="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $@ -Werror" AC_MSG_CHECKING([whether $CC supports "$@"]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([])], [RC=0; AC_MSG_RESULT([yes])], [RC=1; AC_MSG_RESULT([no])]) CPPFLAGS="$BACKUP" return $RC } ## cleanup AC_MSG_NOTICE(Sanitizing prefix: ${prefix}) case $prefix in NONE) prefix=/usr/local;; esac AC_MSG_NOTICE(Sanitizing exec_prefix: ${exec_prefix}) case $exec_prefix in NONE) exec_prefix=$prefix;; prefix) exec_prefix=$prefix;; esac # Checks for libraries. AX_PTHREAD(,[AC_MSG_ERROR([POSIX threads support is required])]) AX_SAVE_FLAGS AC_SEARCH_LIBS([dlopen],[dl],,[AC_MSG_ERROR([cannot find dlopen() function])]) AC_SUBST([dlopen_LIBS],[$LIBS]) AX_RESTORE_FLAGS AX_SAVE_FLAGS AC_SEARCH_LIBS([socket], [socket],,[AC_MSG_ERROR([cannot find socket() function])]) AC_SUBST([socket_LIBS],[$LIBS]) AX_RESTORE_FLAGS AX_SAVE_FLAGS AC_SEARCH_LIBS([gethostbyname], [nsl],,[AC_MSG_ERROR([cannot find gethostbyname() function])]) AC_SUBST([nsl_LIBS],[$LIBS]) AX_RESTORE_FLAGS AX_SAVE_FLAGS AC_SEARCH_LIBS([clock_gettime], [rt],,[AC_MSG_ERROR([cannot find clock_gettime() function])]) AC_SUBST([rt_LIBS],[$LIBS]) AX_RESTORE_FLAGS USE_JOURNAL="no" PKG_CHECK_MODULES([SYSTEMD], [libsystemd], [have_systemd=yes],[have_systemd=no]) if test "x$enable_systemd_journal" = "xyes" ; then if test "x$have_systemd" = "xyes" ; then AC_DEFINE_UNQUOTED(USE_JOURNAL, 1, [Use systemd journal logging]) USE_JOURNAL="yes" else echo "systemd libraries not found, will just use syslog" fi fi # look for testing harness "check" PKG_CHECK_MODULES([CHECK], [check >= 0.9.4],[with_check=yes],[with_check=no]) AM_CONDITIONAL(HAVE_CHECK, test "${with_check}" = "yes") # look for GLIB (used for testing integration) PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.0, have_glib=yes, have_glib=no) AM_CONDITIONAL(HAVE_GLIB, test x$have_glib = xyes) AC_SUBST(GLIB_CFLAGS) AC_SUBST(GLIB_LIBS) if test x"$have_glib" = xyes; then AC_DEFINE_UNQUOTED([HAVE_GLIB], [1], [We have glib]) fi # For building doxygen2man and man pages PKG_CHECK_MODULES([libxml], [libxml-2.0]) # if we are not cross-compiling, we can use the locally built # version of doxygen2man, otherwise we can look for # a locally installed version. If neither are usable, then # don“t build the man pages if test "x$cross_compiling" = "xno"; then AM_CONDITIONAL([BUILD_MAN], [true]) DOXYGEN2MAN="\$(abs_builddir)/../doxygen2man/doxygen2man" else AC_CHECK_PROGS([DOXYGEN2MAN], [doxygen2man]) if test "x$DOXYGEN2MAN" = "x"; then AM_CONDITIONAL([BUILD_MAN], [false]) else AM_CONDITIONAL([BUILD_MAN], [true]) fi fi AC_SUBST(DOXYGEN2MAN) # Checks for header files. AC_HEADER_STDC AC_HEADER_SYS_WAIT AC_CHECK_HEADERS([arpa/inet.h link.h fcntl.h inttypes.h limits.h netinet/in.h \ stdint.h stddef.h stdlib.h string.h strings.h \ dlfcn.h time.h sys/time.h sys/types.h sys/stat.h \ sys/param.h sys/socket.h sys/time.h sys/poll.h sys/epoll.h \ sys/uio.h sys/event.h sys/sockio.h sys/un.h sys/resource.h \ syslog.h errno.h unistd.h sys/mman.h \ sys/sem.h sys/ipc.h sys/msg.h netdb.h]) # Checks for typedefs, structures, and compiler characteristics. AC_TYPE_UID_T AC_C_INLINE AC_TYPE_INT32_T AC_TYPE_INT64_T AC_TYPE_INT8_T AC_TYPE_MODE_T AC_TYPE_PID_T AC_TYPE_SIZE_T AC_TYPE_SSIZE_T AC_TYPE_UINT64_T AC_TYPE_UINT32_T AC_TYPE_UINT16_T AC_TYPE_UINT8_T AC_CHECK_MEMBER([struct sockaddr_un.sun_len], [AC_DEFINE([HAVE_STRUCT_SOCKADDR_UN_SUN_LEN], 1, [Define to 1 if struct sockaddr_un has a member sun_len])], [], [#include ]) AC_MSG_CHECKING(looking for union semun in sys/sem.h) AC_COMPILE_IFELSE([AC_LANG_PROGRAM( [[#include #include #include ]], [[union semun arg; semctl(0, 0, 0, arg);]])], [ AC_MSG_RESULT([yes]) AC_DEFINE_UNQUOTED([HAVE_SEMUN], 1, [Define to 1 if you have union semun.]) ], [ AC_MSG_RESULT([no]) ] ) AX_SAVE_FLAGS LIBS="$LIBS $rt_LIBS" AC_MSG_CHECKING(for a working clock_getres(CLOCK_MONOTONIC, &ts)) AC_COMPILE_IFELSE([AC_LANG_PROGRAM( [[#include ]], [[struct timespec ts; if(clock_getres(CLOCK_MONOTONIC, &ts)) return -1;]])], [ AC_MSG_RESULT([yes]) AC_DEFINE_UNQUOTED([HAVE_CLOCK_GETRES_MONOTONIC], 1, [Define to 1 if clock_getres(CLOCK_MONOTONIC, &ts) works]) ], [ AC_MSG_RESULT([no]) ] ) AC_CHECK_FUNCS([clock_gettime]) AX_RESTORE_FLAGS # Checks for library functions AC_FUNC_CHOWN AC_FUNC_FORK AC_FUNC_MMAP AC_FUNC_STRERROR_R AC_CHECK_FUNCS([alarm fsync fdatasync ftruncate posix_fallocate \ gettimeofday localtime localtime_r \ memset munmap socket \ strchr strrchr strdup strstr strcasecmp \ poll epoll_create epoll_create1 kqueue \ random rand getrlimit sysconf \ getpeerucred getpeereid \ openat unlinkat]) AX_SAVE_FLAGS CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LIBS="$LIBS $PTHREAD_LIBS" AC_CHECK_FUNCS([pthread_spin_lock pthread_setschedparam \ pthread_mutexattr_setpshared \ pthread_condattr_setpshared \ sem_timedwait semtimedop]) AX_RESTORE_FLAGS # Checks for defined macros AC_MSG_CHECKING(for MSG_NOSIGNAL) AC_TRY_COMPILE([#include ], [ int f = MSG_NOSIGNAL; ], [ AC_MSG_RESULT(yes) AC_DEFINE(HAVE_MSG_NOSIGNAL, 1, [Define this symbol if you have MSG_NOSIGNAL])], [ AC_MSG_RESULT(no)]) AC_MSG_CHECKING(for SO_NOSIGPIPE ) AC_TRY_COMPILE([#include ], [ int f = SO_NOSIGPIPE; ], [ AC_MSG_RESULT(yes) AC_DEFINE(HAVE_SO_NOSIGPIPE, 1, [Define this symbol if you have SO_NOSIGPIPE]) ], [ AC_MSG_RESULT(no)]) AC_MSG_CHECKING([for RTLD_NEXT]) AC_TRY_COMPILE([#ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include ], [ void *h = RTLD_NEXT; ], [ AC_MSG_RESULT([yes]) AC_DEFINE([HAVE_FAILURE_INJECTION], 1, [have failure injection]) have_RTLD_NEXT=yes ], [ AC_MSG_RESULT([no])]) AM_CONDITIONAL(HAVE_SEM_TIMEDWAIT, [test "x$ac_cv_func_sem_timedwait" = xyes]) AM_CONDITIONAL(HAVE_EPOLL, [test "x$ac_cv_func_epoll_create" = xyes]) AM_CONDITIONAL(HAVE_POLL, [test "x$ac_cv_func_poll" = xyes]) AM_CONDITIONAL(HAVE_KQUEUE, [test "x$ac_cv_func_kqueue" = xyes]) AM_CONDITIONAL(HAVE_FAILURE_INJECTION, [test "x$have_RTLD_NEXT" = xyes]) AC_CONFIG_LIBOBJ_DIR(lib) AC_REPLACE_FUNCS(strlcpy strlcat strchrnul) ## local defines PACKAGE_FEATURES="" if test x$ac_cv_func_epoll_create = xyes; then PACKAGE_FEATURES="$PACKAGE_FEATURES epoll" fi nongcc_memory_barrier_needed=no arch_force_shmlba=no file_sync=fdatasync AC_MSG_CHECKING([for architecture in ${host_cpu}]) case $host_cpu in sparc*) AC_MSG_RESULT([sparc]) AC_DEFINE_UNQUOTED([QB_ARCH_SPARC], [1], [sparc]) nongcc_memory_barrier_needed=yes arch_force_shmlba=yes ;; alpha*) AC_MSG_RESULT([alpha]) AC_DEFINE_UNQUOTED([QB_ARCH_ALPHA], [1], [alpha]) nongcc_memory_barrier_needed=yes ;; powerpc*) AC_MSG_RESULT([powerpc]) AC_DEFINE_UNQUOTED([QB_ARCH_POWERPC], [1], [powerpc]) nongcc_memory_barrier_needed=yes arch_force_shmlba=yes ;; ia64) AC_MSG_RESULT([ia64]) AC_DEFINE_UNQUOTED([QB_ARCH_IA64], [1], [ia64]) nongcc_memory_barrier_needed=yes ;; arm*) AC_MSG_RESULT([arm]) AC_DEFINE_UNQUOTED([QB_ARCH_ARM], [1], [arm]) arch_force_shmlba=yes ;; hppa*) AC_MSG_RESULT([hppa]) AC_DEFINE_UNQUOTED([QB_ARCH_HPPA], [1], [hppa]) ;; mips*) AC_MSG_RESULT([ia64]) AC_DEFINE_UNQUOTED([QB_ARCH_MIPS], [1], [mips]) arch_force_shmlba=yes ;; *) AC_MSG_RESULT([${host_cpu}]) ;; esac if test $arch_force_shmlba = yes; then AC_DEFINE_UNQUOTED([QB_FORCE_SHM_ALIGN], [1], [shared and fixed mmap must align on 16k]) fi # OS detection # THIS SECTION MUST DIE! CP=cp AC_MSG_CHECKING([for os in ${host_os}]) case "$host_os" in *linux*) AC_DEFINE_UNQUOTED([QB_LINUX], [1], [Compiling for Linux platform]) AC_MSG_RESULT([Linux]) ;; *cygwin*) AC_DEFINE_UNQUOTED([QB_CYGWIN], [1], [Compiling for Cygwin platform]) nongcc_memory_barrier_needed=yes gcc_has_builtin_sync_operations=no AC_MSG_RESULT([Cygwin]) ;; darwin*) AC_DEFINE_UNQUOTED([QB_DARWIN], [1], [Compiling for Darwin platform]) CP=rsync dnl Attribute section appears to work here but fails later with: dnl cc1: error in backend: Global variable 'descriptor.4902' dnl has an invalid section specifier '__verbose': mach-o dnl section specifier requires a segment and section dnl separated by a comma AC_DEFINE_UNQUOTED([DISABLE_POSIX_THREAD_PROCESS_SHARED], [1], [Disable _POSIX_THREAD_PROCESS_SHARED]) AC_MSG_RESULT([Darwin]) ;; *bsd*) AC_DEFINE_UNQUOTED([QB_BSD], [1], [Compiling for BSD platform]) case "$host_os" in *netbsd*) AC_DEFINE_UNQUOTED([UNIX_PATH_MAX], [103], [Unix path length]) ;; *openbsd*) AC_DEFINE_UNQUOTED([UNIX_PATH_MAX], [104], [Unix path length]) ;; esac AC_MSG_RESULT([BSD]) ;; *solaris*) AC_DEFINE_UNQUOTED(DISABLE_IPC_SHM, 1, [Disable shared mem ipc]) AC_DEFINE_UNQUOTED([QB_SOLARIS], [1], [Compiling for Solaris platform]) CP=rsync AC_MSG_RESULT([Solaris]) ;; *gnu*) AC_DEFINE_UNQUOTED([QB_GNU], [1], [Compiling for GNU/Hurd platform]) AC_MSG_RESULT([GNU]) ;; *) AC_MSG_ERROR([Unsupported OS? hmmmm]) ;; esac dnl break on first hit, fallthrough otherwise, until empty or unsupported dnl string reached; output QB_FILE_SYNC macro operates on a file descriptor while : ; do case ${file_sync} in fdatasync) test "x${ac_cv_func_fdatasync}" = "xno" || break file_sync=fsync;; fsync) test "x${ac_cv_func_fsync}" = "xno" || break file_sync=;; "") break;; *) AC_MSG_ERROR([Cannot select file sync method]);; esac done if test "x${file_sync}" != x; then AC_DEFINE_UNQUOTED([QB_FILE_SYNC(fd)], [${file_sync}(fd)], [File sync method]) else AC_MSG_WARN([No file sync method applicable!]) AC_DEFINE([QB_FILE_SYNC(x)], [], [File sync method]) fi AC_MSG_CHECKING([whether GCC supports builtin sync intrinsics]) if test -z "$gcc_has_builtin_sync_operations"; then gcc_has_builtin_sync_operations=no if test x"$GCC" = xyes && test x$have_mingw != xyes; then AC_TRY_LINK([], [int i; __sync_synchronize (); __sync_bool_compare_and_swap (&i, 0, 1); __sync_fetch_and_add (&i, 1); ], [gcc_has_builtin_sync_operations=yes], [gcc_has_builtin_sync_operations=no]) fi fi AC_MSG_RESULT($gcc_has_builtin_sync_operations) AM_CONDITIONAL(HAVE_GCC_BUILTINS_FOR_SYNC_OPERATIONS, [test "x$gcc_has_builtin_sync_operations" = xyes]) if test "x$gcc_has_builtin_sync_operations" = xyes; then AC_DEFINE_UNQUOTED(HAVE_GCC_BUILTINS_FOR_SYNC_OPERATIONS, 1, [have builtin sync operations]) fi # __atomic_XXX AC_MSG_CHECKING([whether GCC supports builtin atomic intrinsics]) if test -z "$gcc_has_builtin_atomic_operations"; then gcc_has_builtin_atomic_operations=no if test x"$GCC" = xyes && test x$have_mingw != xyes; then AC_TRY_LINK([], [int i; __atomic_load_n(&i, __ATOMIC_ACQUIRE); __atomic_exchange_n(&i, 0, __ATOMIC_RELEASE); ], [gcc_has_builtin_atomic_operations=yes], [gcc_has_builtin_atomic_operations=no]) fi fi AC_MSG_RESULT($gcc_has_builtin_atomic_operations) AM_CONDITIONAL(HAVE_GCC_BUILTINS_FOR_ATOMIC_OPERATIONS, [test "x$gcc_has_builtin_atomic_operations" = xyes]) if test "x$gcc_has_builtin_atomic_operations" = xyes; then AC_DEFINE_UNQUOTED(HAVE_GCC_BUILTINS_FOR_ATOMIC_OPERATIONS, 1, [have builtin atomic operations]) fi AC_MSG_CHECKING([whether atomics need memory barrier]) if test -n "$ac_cv_atomic_need_memory_barrier"; then memory_barrier_needed=$ac_cv_atomic_need_memory_barrier else if test x$gcc_has_builtin_sync_operations = xyes; then memory_barrier_needed=yes PACKAGE_FEATURES="$PACKAGE_FEATURES gcc__sync" else memory_barrier_needed=$nongcc_memory_barrier_needed AC_MSG_WARN([-----------------------------]) AC_MSG_WARN([You have gcc but not __sync_bool_compare_and_swap]) AC_MSG_WARN([try CFLAGS="-march= -mtune=native" ./configure]) AC_MSG_WARN([-----------------------------]) fi fi AC_MSG_RESULT($memory_barrier_needed) if test x"$memory_barrier_needed" != xno; then AC_DEFINE_UNQUOTED(QB_ATOMIC_OP_MEMORY_BARRIER_NEEDED, 1, [need atomic memory barrier]) fi LINT_FLAGS="-weak -D__gnuc_va_list=va_list -D__attribute\(x\)= \ -badflag -fcnuse -syntax -unrecog -sysunrecog -warnposix \ +ignoresigns +matchanyintegral +posixlib \ +showscan +showsummary" # local options AC_ARG_ENABLE([ansi], [AS_HELP_STRING([--enable-ansi],[force to build with ANSI standards])]) AC_ARG_ENABLE([fatal-warnings], [AS_HELP_STRING([--enable-fatal-warnings],[enable fatal warnings])]) AC_ARG_ENABLE([debug], [AS_HELP_STRING([--enable-debug],[enable debug build])]) AC_ARG_WITH([sanitizers], [AS_HELP_STRING([--with-sanitizers=...,...], [enable SANitizer build, do *NOT* use for production. Only ASAN/UBSAN/TSAN are currently supported])], [ SANITIZERS="$withval" ], [ SANITIZERS="" ]) AC_ARG_ENABLE([coverage], [AS_HELP_STRING([--enable-coverage],[coverage analysis of the codebase])]) AC_ARG_ENABLE([interlib-deps], [AS_HELP_STRING([--disable-interlib-deps], [disable inter-library dependencies (might break builds)])]) AC_ARG_ENABLE([slow-tests], [AS_HELP_STRING([--enable-slow-tests],[build and run slow tests])]) AC_ARG_WITH([socket-dir], [AS_HELP_STRING([--with-socket-dir=DIR],[socket directory @<:@LOCALSTATEDIR/run@:>@])], [ SOCKETDIR="$withval" ], [ SOCKETDIR="$localstatedir/run" ]) AC_ARG_ENABLE([systemd-journal], [AS_HELP_STRING([--enable-systemd-journal],[Allow use of systemd journal instead of syslog])]) AC_ARG_WITH([force-sockets-config-file], [AS_HELP_STRING([--with-force-sockets-config-file=FILE],[config file to force IPC to use filesystem sockets (Linux & Cygwin only) @<:@SYSCONFDIR/libqb/force-filesystem-sockets@:>@])], [ FORCESOCKETSFILE="$withval" ], [ FORCESOCKETSFILE="$sysconfdir/libqb/force-filesystem-sockets" ]) AC_ARG_ENABLE([install-tests], [AS_HELP_STRING([--enable-install-tests],[install tests])],, [ enable_install_tests="no" ]) AM_CONDITIONAL([INSTALL_TESTS], [test x$enable_install_tests = xyes]) AC_ARG_WITH([testdir], [AS_HELP_STRING([--with-testdir=DIR],[path to /usr/lib../libqb/tests/ dir where to install the test suite])], [ TESTDIR="$withval" ], [ TESTDIR="$libdir/libqb/tests" ]) AC_SUBST([TESTDIR]) AC_SUBST(CP) # *FLAGS handling goes here ENV_CFLAGS="$CFLAGS" ENV_CPPFLAGS="$CPPFLAGS" ENV_LDFLAGS="$LDFLAGS" # debug build stuff if test "x${enable_debug}" = xyes; then AC_DEFINE_UNQUOTED([DEBUG], [1], [Compiling Debugging code]) OPT_CFLAGS="-O0" if test "x${GCC}" = xyes; then GDB_FLAGS="-ggdb3" else GDB_FLAGS="-g" fi PACKAGE_FEATURES="$PACKAGE_FEATURES debug" fi # extra warnings EXTRA_WARNINGS="" WARNLIST=" all extra unused shadow missing-prototypes missing-declarations suggest-attribute=noreturn suggest-attribute=format property-attribute-mismatch strict-prototypes pointer-arith write-strings cast-align bad-function-cast missing-format-attribute float-equal format=2 format-signedness shift-overflow shift-overflow=2 overlength-strings redundent-decls init-self uninitialized unknown-pragmas no-unused-parameter unused-const-variable no-format-nonliteral no-format-truncation no-sign-compare " # at least no-sign-compare assumed temporary! for j in $WARNLIST; do if cc_supports_flag -W$j; then EXTRA_WARNINGS="$EXTRA_WARNINGS -W$j"; fi done # warnings suppression gcc_format_complaints=no if test x"$GCC" = xyes && cc_supports_flag -Wmissing-format-attribute; then gcc_format_complaints=yes AC_DEFINE([HAVE_GCC_MISSING_FORMAT_ATTRIBUTE], [], [gcc supports -Wmissing-format-attribute]) fi if test x"$GCC" = xyes && cc_supports_flag -Wsuggest-attribute=format; then gcc_format_complaints=yes AC_DEFINE([HAVE_GCC_SUGGEST_ATTRIBUTE_FORMAT], [], [gcc supports -Wsuggest-attribute=format]) fi dnl pretend GCC (<4.6) is not capable of format complaints when it does not dnl support diagnostic push/pop pragmas (cannot track state reliably, then) if test x"$gcc_format_complaints" = xyes; then backup_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -Werror" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #pragma GCC diagnostic push #pragma GCC diagnostic pop ]])], AC_DEFINE([HAVE_GCC_FORMAT_COMPLAINTS], [], [gcc can complain about missing format attribute])) CFLAGS="$backup_CFLAGS" fi # --- coverage --- if test "x${enable_coverage}" = xyes && \ cc_supports_flag -ftest-coverage && \ cc_supports_flag -fprofile-arcs ; then AC_MSG_NOTICE([Enabling Coverage (enable -O0 by default)]) OPT_CFLAGS="-O0" COVERAGE_CFLAGS="-ftest-coverage -fprofile-arcs" COVERAGE_LDFLAGS="-ftest-coverage -fprofile-arcs" PACKAGE_FEATURES="$PACKAGE_FEATURES coverage" else COVERAGE_CFLAGS="" COVERAGE_LDFLAGS="" fi # --- inter-library dependencies --- # because of debian/ubuntu swimming against the stream # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=702737, # override the libtool variable by force because the current # arrangement relies on transitive dependency propagation AC_MSG_NOTICE([Enabling inter-library dependencies: $enable_interlib_deps]) if test "x${enable_interlib_deps}" = xno; then link_all_deplibs=no else link_all_deplibs=yes fi # --- slow tests --- if test "x${enable_slow_tests}" = xyes ; then AC_DEFINE([HAVE_SLOW_TESTS], 1,[have slow tests]) AC_MSG_NOTICE([Enabling Slow tests]) fi AM_CONDITIONAL(HAVE_SLOW_TESTS, [test "x${enable_slow_tests}" = xyes]) AC_SUBST(HAVE_SLOW_TESTS) # --- ansi --- if test "x${enable_ansi}" = xyes && \ cc_supports_flag -std=iso9899:199409 ; then AC_MSG_NOTICE([Enabling ANSI Compatibility]) ANSI_CPPFLAGS="-ansi -D_GNU_SOURCE -DANSI_ONLY" PACKAGE_FEATURES="$PACKAGE_FEATURES ansi" else ANSI_CPPFLAGS="" fi # --- fatal warnings --- if test "x${enable_fatal_warnings}" = xyes && \ cc_supports_flag -Werror ; then AC_MSG_NOTICE([Enabling Fatal Warnings (-Werror)]) WERROR_CFLAGS="-Werror" PACKAGE_FEATURES="$PACKAGE_FEATURES fatal-warnings" else WERROR_CFLAGS="" fi # --- ASAN/UBSAN/TSAN (see man gcc) --- # when using SANitizers, we need to pass the -fsanitize.. # to both CFLAGS and LDFLAGS. The CFLAGS/LDFLAGS must be # specified as first in the list or there will be runtime # issues (for example user has to LD_PRELOAD asan for it to work # properly). if test -n "${SANITIZERS}"; then SANITIZERS=$(echo $SANITIZERS | sed -e 's/,/ /g') for SANITIZER in $SANITIZERS; do case $SANITIZER in asan|ASAN) SANITIZERS_CFLAGS="$SANITIZERS_CFLAGS -fsanitize=address" SANITIZERS_LDFLAGS="$SANITIZERS_LDFLAGS -fsanitize=address -lasan" AC_CHECK_LIB([asan],[main],,AC_MSG_ERROR([Unable to find libasan])) ;; ubsan|UBSAN) SANITIZERS_CFLAGS="$SANITIZERS_CFLAGS -fsanitize=undefined" SANITIZERS_LDFLAGS="$SANITIZERS_LDFLAGS -fsanitize=undefined -lubsan" AC_CHECK_LIB([ubsan],[main],,AC_MSG_ERROR([Unable to find libubsan])) ;; tsan|TSAN) SANITIZERS_CFLAGS="$SANITIZERS_CFLAGS -fsanitize=thread" SANITIZERS_LDFLAGS="$SANITIZERS_LDFLAGS -fsanitize=thread -ltsan" AC_CHECK_LIB([tsan],[main],,AC_MSG_ERROR([Unable to find libtsan])) ;; esac done fi # final build of *FLAGS CFLAGS="$SANITIZERS_CFLAGS $ENV_CFLAGS $OPT_CFLAGS $GDB_FLAGS \ $COVERAGE_CFLAGS $EXTRA_WARNINGS $WERROR_CFLAGS" CPPFLAGS="$ENV_CPPFLAGS $ANSI_CPPFLAGS" LDFLAGS="$SANITIZERS_LDFLAGS $ENV_LDFLAGS $COVERAGE_LDFLAGS" if test -f /usr/share/dict/words ; then HAVE_DICT_WORDS=yes AC_DEFINE([HAVE_DICT_WORDS], 1, "Have /usr/share/dict/words") fi AM_CONDITIONAL([HAVE_DICT_WORDS], [test "x$HAVE_DICT_WORDS" = xyes]) # substitute what we need: AC_SUBST([SOCKETDIR]) AC_SUBST([LINT_FLAGS]) AC_SUBST([FORCESOCKETSFILE]) AC_DEFINE_UNQUOTED([SOCKETDIR], "$(eval echo ${SOCKETDIR})", [Socket directory]) AC_DEFINE_UNQUOTED([LOCALSTATEDIR], "$(eval echo ${localstatedir})", [localstate directory]) AC_DEFINE_UNQUOTED([PACKAGE_FEATURES], "${PACKAGE_FEATURES}", [quarterback built-in features]) AC_DEFINE_UNQUOTED([FORCESOCKETSFILE], "$(eval echo ${FORCESOCKETSFILE})", [for sockets config file]) # version parsing (for qbconfig.h) AC_DEFINE_UNQUOTED([QB_VER_MAJOR], [$(echo "${VERSION}" \ | sed -e 's|^\([[0-9]][[0-9]]*\).*|\1|' \ -e t -e 's|.*|1|')], [libqb major version]) AC_DEFINE_UNQUOTED([QB_VER_MINOR], [$(echo "${VERSION}" \ | sed -e 's|^[[0-9]][[0-9]]*\.\([[0-9]][[0-9]]*\).*|\1|' \ -e t -e 's|.*|0|')], [libqb minor version]) AC_DEFINE_UNQUOTED([QB_VER_MICRO], [$(echo "${VERSION}" \ | sed -e 's|^[[0-9]][[0-9]]*\.[[0-9]][[0-9]]*\.\([[0-9]][[0-9]]*\).*|\1|' \ -e t -e 's|.*|0|')], [libqb patch version]) AC_DEFINE_UNQUOTED([QB_VER_REST], [$(echo "${VERSION}" \ | sed -e 's|^[[0-9]][[0-9]]*\(\.[[0-9]][[0-9]]*\)\{0,2\}\(.*\)|"\2"|' \ -e t -e 's|.*|""|')], [libqb patch version]) +AX_PROG_DATE +AS_IF([test "$ax_cv_prog_date_gnu_date:$ax_cv_prog_date_gnu_utc" = yes:yes], + [UTC_DATE_AT="date -u -d@"], + [AS_IF([test "x$ax_cv_prog_date_bsd_date" = xyes], + [UTC_DATE_AT="date -u -r"], + [AC_MSG_ERROR([date utility unable to convert epoch to UTC])])]) +AC_SUBST([UTC_DATE_AT]) + +AC_ARG_VAR([SOURCE_EPOCH],[last modification date of the source]) +AC_MSG_NOTICE([trying to determine source epoch]) +AC_MSG_CHECKING([for source epoch in \$SOURCE_EPOCH]) +AS_IF([test -n "$SOURCE_EPOCH"], + [AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no]) + AC_MSG_CHECKING([for source epoch in source_epoch file]) + AS_IF([test -e "$srcdir/source_epoch"], + [read SOURCE_EPOCH <"$srcdir/source_epoch" + AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no]) + AC_MSG_CHECKING([for source epoch baked in by gitattributes export-subst]) + SOURCE_EPOCH='$Format:%ct$' # template for rewriting by git-archive + AS_CASE([$SOURCE_EPOCH], + [?Format:*], # was not rewritten + [AC_MSG_RESULT([no]) + AC_MSG_CHECKING([for source epoch in \$SOURCE_DATE_EPOCH]) + AS_IF([test "x$SOURCE_DATE_EPOCH" != x], + [SOURCE_EPOCH="$SOURCE_DATE_EPOCH" + AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no]) + AC_MSG_CHECKING([whether git log can provide a source epoch]) + SOURCE_EPOCH=f${SOURCE_EPOCH#\$F} # convert into git log --pretty format + SOURCE_EPOCH=$(cd "$srcdir" && git log -1 --pretty=${SOURCE_EPOCH%$} 2>/dev/null) + AS_IF([test -n "$SOURCE_EPOCH"], + [AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no, using current time and breaking reproducibility]) + SOURCE_EPOCH=$(date +%s)])])], + [AC_MSG_RESULT([yes])] + )]) + ]) +AC_MSG_NOTICE([using source epoch $($UTC_DATE_AT$SOURCE_EPOCH +'%F %T %Z')]) + AC_CONFIG_FILES([Makefile include/Makefile include/qb/Makefile lib/Makefile lib/libqb.pc tools/Makefile tests/Makefile tests/test.conf examples/Makefile doxygen2man/Makefile docs/Makefile docs/man.dox lib/qblog_script.la:lib/qblog_script.la.in]) AC_OUTPUT AC_MSG_RESULT([]) AC_MSG_RESULT([$PACKAGE configuration:]) AC_MSG_RESULT([ Version = ${VERSION}]) AC_MSG_RESULT([ Prefix = ${prefix}]) AC_MSG_RESULT([ Executables = ${sbindir}]) AC_MSG_RESULT([ Man pages = ${mandir}]) AC_MSG_RESULT([ Doc dir = ${docdir}]) AC_MSG_RESULT([ Libraries = ${libdir}]) AC_MSG_RESULT([ Header files = ${includedir}]) AC_MSG_RESULT([ Arch-independent files = ${datadir}]) AC_MSG_RESULT([ State information = ${localstatedir}]) AC_MSG_RESULT([ System configuration = ${sysconfdir}]) AC_MSG_RESULT([ SOCKETDIR = ${SOCKETDIR}]) AC_MSG_RESULT([ Features = ${PACKAGE_FEATURES}]) AC_MSG_RESULT([ Use systemd journal = ${USE_JOURNAL}]) AC_MSG_RESULT([]) AC_MSG_RESULT([$PACKAGE build info:]) AC_MSG_RESULT([ Optimization = ${OPT_CFLAGS}]) AC_MSG_RESULT([ Debug options = ${GDB_CFLAGS}]) AC_MSG_RESULT([ Extra compiler warnings = ${EXTRA_WARNING}]) AC_MSG_RESULT([ Env. defined CFLAG = ${ENV_CFLAGS}]) AC_MSG_RESULT([ Env. defined CPPFLAGS = ${ENV_CPPFLAGS}]) AC_MSG_RESULT([ Env. defined LDFLAGS = ${ENV_LDFLAGS}]) AC_MSG_RESULT([ ANSI defined CPPFLAGS = ${ANSI_CPPFLAGS}]) AC_MSG_RESULT([ Coverage CFLAGS = ${COVERAGE_CFLAGS}]) AC_MSG_RESULT([ Coverage LDFLAGS = ${COVERAGE_LDFLAGS}]) AC_MSG_RESULT([ Fatal War. CFLAGS = ${WERROR_CFLAGS}]) AC_MSG_RESULT([ Final CFLAGS = ${CFLAGS}]) AC_MSG_RESULT([ Final CPPFLAGS = ${CPPFLAGS}]) AC_MSG_RESULT([ Final LDFLAGS = ${LDFLAGS}]) diff --git a/docs/Makefile.am b/docs/Makefile.am index c2235b6..f41e90c 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -1,258 +1,259 @@ # Copyright (C) 2010 Red Hat, Inc. # # Authors: Angus Salkeld # # This file is part of libqb. # # libqb 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. # # libqb 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 libqb. If not, see . MAINTAINERCLEANFILES = Makefile.in EXTRA_DIST = man.dox.in noinst_HEADERS = mainpage.h dist_man_MANS = man8/qb-blackbox.8 if HAVE_DOXYGEN if BUILD_MAN -doxygen2man_flags = -q -i qb/ -o man3/ -m -g -P -H "libqb Programmer's Manual" -p LIBQB +doxygen2man_flags = -q -i qb/ -o man3/ -m -g -P -H "libqb Programmer's Manual" $$($(UTC_DATE_AT)$(SOURCE_EPOCH) +"-D %F -Y %Y") \ + -p LIBQB -c -O ../include/qb -Y $(SOURCE_EPOCH) man3_MANS = \ man3/qb_array_create_2.3 \ man3/qb_array_create.3 \ man3/qb_array_elems_per_bin_get.3 \ man3/qb_array_free.3 \ man3/qb_array_grow.3 \ man3/qbarray.h.3 \ man3/qb_array_index.3 \ man3/qb_array_new_bin_cb_set.3 \ man3/qb_array_num_bins_get.3 \ man3/qbatomic.h.3 \ man3/qb_atomic_init.3 \ man3/qb_atomic_int_add.3 \ man3/qb_atomic_int_compare_and_exchange.3 \ man3/qb_atomic_int_exchange_and_add.3 \ man3/qb_atomic_int_get.3 \ man3/qb_atomic_int_set.3 \ man3/qb_atomic_pointer_compare_and_exchange.3 \ man3/qb_atomic_pointer_get.3 \ man3/qb_atomic_pointer_set.3 \ man3/qbdefs.h.3 \ man3/qb_hashtable_create.3 \ man3/qb_hdb_base_convert.3 \ man3/qb_hdb_create.3 \ man3/qb_hdb_destroy.3 \ man3/qbhdb.h.3 \ man3/qb_hdb_handle_create.3 \ man3/qb_hdb_handle_destroy.3 \ man3/qb_hdb_handle_get.3 \ man3/qb_hdb_handle_get_always.3 \ man3/qb_hdb_handle_put.3 \ man3/qb_hdb_handle_refcount_get.3 \ man3/qb_hdb_iterator_next.3 \ man3/qb_hdb_iterator_reset.3 \ man3/qb_hdb_nocheck_convert.3 \ man3/qb_ipcc_connect.3 \ man3/qb_ipcc_context_get.3 \ man3/qb_ipcc_context_set.3 \ man3/qb_ipcc_disconnect.3 \ man3/qb_ipcc_event_recv.3 \ man3/qb_ipcc_fc_enable_max_set.3 \ man3/qb_ipcc_fd_get.3 \ man3/qb_ipcc_get_buffer_size.3 \ man3/qbipcc.h.3 \ man3/qb_ipcc_is_connected.3 \ man3/qbipc_common.h.3 \ man3/qb_ipcc_recv.3 \ man3/qb_ipcc_send.3 \ man3/qb_ipcc_sendv.3 \ man3/qb_ipcc_sendv_recv.3 \ man3/qb_ipcc_verify_dgram_max_msg_size.3 \ man3/qb_ipcs_connection_auth_set.3 \ man3/qb_ipcs_connection_first_get.3 \ man3/qb_ipcs_connection_get_buffer_size.3 \ man3/qb_ipcs_connection_next_get.3 \ man3/qb_ipcs_connection_ref.3 \ man3/qb_ipcs_connection_service_context_get.3 \ man3/qb_ipcs_connection_stats_get_2.3 \ man3/qb_ipcs_connection_stats_get.3 \ man3/qb_ipcs_connection_unref.3 \ man3/qb_ipcs_context_get.3 \ man3/qb_ipcs_context_set.3 \ man3/qb_ipcs_create.3 \ man3/qb_ipcs_destroy.3 \ man3/qb_ipcs_disconnect.3 \ man3/qb_ipcs_enforce_buffer_size.3 \ man3/qb_ipcs_event_send.3 \ man3/qb_ipcs_event_sendv.3 \ man3/qbipcs.h.3 \ man3/qb_ipcs_poll_handlers_set.3 \ man3/qb_ipcs_ref.3 \ man3/qb_ipcs_request_rate_limit.3 \ man3/qb_ipcs_response_send.3 \ man3/qb_ipcs_response_sendv.3 \ man3/qb_ipcs_run.3 \ man3/qb_ipcs_service_context_get.3 \ man3/qb_ipcs_service_context_set.3 \ man3/qb_ipcs_service_id_get.3 \ man3/qb_ipcs_stats_get.3 \ man3/qb_ipcs_unref.3 \ man3/qb_list_add.3 \ man3/qb_list_add_tail.3 \ man3/qb_list_del.3 \ man3/qb_list_empty.3 \ man3/qblist.h.3 \ man3/qb_list_init.3 \ man3/qb_list_is_last.3 \ man3/qb_list_length.3 \ man3/qb_list_replace.3 \ man3/qb_list_splice.3 \ man3/qb_list_splice_tail.3 \ man3/qb_log_blackbox_print_from_file.3 \ man3/qb_log_blackbox_write_to_file.3 \ man3/qb_log_callsite_get.3 \ man3/qb_log_callsites_dump.3 \ man3/qb_log_callsites_register.3 \ man3/qb_log_ctl2.3 \ man3/qb_log_ctl.3 \ man3/qb_log_custom_close.3 \ man3/qb_log_custom_open.3 \ man3/qb_log_facility2int.3 \ man3/qb_log_facility2str.3 \ man3/qb_log_file_close.3 \ man3/qb_log_file_open.3 \ man3/qb_log_file_reopen.3 \ man3/qb_log_filter_ctl2.3 \ man3/qb_log_filter_ctl.3 \ man3/qb_log_filter_fn_set.3 \ man3/qb_log_fini.3 \ man3/qb_log_format_set.3 \ man3/qb_log_from_external_source.3 \ man3/qb_log_from_external_source_va.3 \ man3/qblog.h.3 \ man3/qb_log_init.3 \ man3/qb_log_real_.3 \ man3/qb_log_real_va_.3 \ man3/qb_log_tags_stringify_fn_set.3 \ man3/qb_log_target_format.3 \ man3/qb_log_target_user_data_get.3 \ man3/qb_log_target_user_data_set.3 \ man3/qb_log_thread_priority_set.3 \ man3/qb_log_thread_start.3 \ man3/qb_loop_create.3 \ man3/qb_loop_destroy.3 \ man3/qbloop.h.3 \ man3/qb_loop_job_add.3 \ man3/qb_loop_job_del.3 \ man3/qb_loop_poll_add.3 \ man3/qb_loop_poll_del.3 \ man3/qb_loop_poll_low_fds_event_set.3 \ man3/qb_loop_poll_mod.3 \ man3/qb_loop_run.3 \ man3/qb_loop_signal_add.3 \ man3/qb_loop_signal_del.3 \ man3/qb_loop_signal_mod.3 \ man3/qb_loop_stop.3 \ man3/qb_loop_timer_add.3 \ man3/qb_loop_timer_del.3 \ man3/qb_loop_timer_expire_time_get.3 \ man3/qb_loop_timer_expire_time_remaining.3 \ man3/qb_loop_timer_is_running.3 \ man3/qb_map_count_get.3 \ man3/qb_map_destroy.3 \ man3/qb_map_foreach.3 \ man3/qb_map_get.3 \ man3/qbmap.h.3 \ man3/qb_map_iter_create.3 \ man3/qb_map_iter_free.3 \ man3/qb_map_iter_next.3 \ man3/qb_map_notify_add.3 \ man3/qb_map_notify_del_2.3 \ man3/qb_map_notify_del.3 \ man3/qb_map_pref_iter_create.3 \ man3/qb_map_put.3 \ man3/qb_map_rm.3 \ man3/qb_rb_chmod.3 \ man3/qb_rb_chown.3 \ man3/qb_rb_chunk_alloc.3 \ man3/qb_rb_chunk_commit.3 \ man3/qb_rb_chunk_peek.3 \ man3/qb_rb_chunk_read.3 \ man3/qb_rb_chunk_reclaim.3 \ man3/qb_rb_chunks_used.3 \ man3/qb_rb_chunk_write.3 \ man3/qb_rb_close.3 \ man3/qb_rb_create_from_file.3 \ man3/qbrb.h.3 \ man3/qb_rb_name_get.3 \ man3/qb_rb_open.3 \ man3/qb_rb_refcount_get.3 \ man3/qb_rb_shared_user_data_get.3 \ man3/qb_rb_space_free.3 \ man3/qb_rb_space_used.3 \ man3/qb_rb_write_to_file.3 \ man3/qb_skiplist_create.3 \ man3/qb_strerror_r.3 \ man3/qb_thread_lock.3 \ man3/qb_thread_lock_create.3 \ man3/qb_thread_lock_destroy.3 \ man3/qb_thread_trylock.3 \ man3/qb_thread_unlock.3 \ man3/qb_timespec_add_ms.3 \ man3/qb_trie_create.3 \ man3/qb_trie_dump.3 \ man3/qbutil.h.3 \ man3/qb_util_nano_current_get.3 \ man3/qb_util_nano_from_epoch_get.3 \ man3/qb_util_nano_monotonic_hz.3 \ man3/qb_util_set_log_function.3 \ man3/qb_util_stopwatch_create.3 \ man3/qb_util_stopwatch_free.3 \ man3/qb_util_stopwatch_sec_elapsed_get.3 \ man3/qb_util_stopwatch_split.3 \ man3/qb_util_stopwatch_split_ctl.3 \ man3/qb_util_stopwatch_split_last.3 \ man3/qb_util_stopwatch_start.3 \ man3/qb_util_stopwatch_stop.3 \ man3/qb_util_stopwatch_time_split_get.3 \ man3/qb_util_stopwatch_us_elapsed_get.3 \ man3/qb_util_timespec_from_epoch_get.3 $(man3_MANS): txt-man txt-man: man.dox doxygen man.dox xml-man: txt-man mkdir -p man3 $(DOXYGEN2MAN) $(doxygen2man_flags) qbarray_8h.xml $(DOXYGEN2MAN) $(doxygen2man_flags) qbatomic_8h.xml $(DOXYGEN2MAN) $(doxygen2man_flags) qbdefs_8h.xml $(DOXYGEN2MAN) $(doxygen2man_flags) qbhdb_8h.xml $(DOXYGEN2MAN) $(doxygen2man_flags) qbipcc_8h.xml $(DOXYGEN2MAN) $(doxygen2man_flags) qbipc__common_8h.xml $(DOXYGEN2MAN) $(doxygen2man_flags) qbipcs_8h.xml $(DOXYGEN2MAN) $(doxygen2man_flags) qblist_8h.xml $(DOXYGEN2MAN) $(doxygen2man_flags) qblog_8h.xml $(DOXYGEN2MAN) $(doxygen2man_flags) qbloop_8h.xml $(DOXYGEN2MAN) $(doxygen2man_flags) qbmap_8h.xml $(DOXYGEN2MAN) $(doxygen2man_flags) qbrb_8h.xml $(DOXYGEN2MAN) $(doxygen2man_flags) qbutil_8h.xml all: $(man3_MANS) xml-man endif endif clean-local: rm -rf html man3 xml diff --git a/doxygen2man/doxygen2man.1 b/doxygen2man/doxygen2man.1 index 89ccab7..2fb8500 100644 --- a/doxygen2man/doxygen2man.1 +++ b/doxygen2man/doxygen2man.1 @@ -1,79 +1,90 @@ .\" .\" * Copyright (C) 2010-2020 Red Hat, Inc. .\" * .\" * All rights reserved. .\" * .\" * Author: Christine Caulfield .\" * -.TH "DOXYGEN2MAN" "8" "2020-03-09" "" "" +.TH "DOXYGEN2MAN" "8" "2020-09-09" "" "" .SH "NAME" doxygen2man \- A tool to generate man pages from Doxygen XML files .SH "SYNOPSIS" .B doxygen2man [OPTIONS] .SH "DESCRIPTION" .P This is a tool to generate API manpages from a doxygen-annotated header file First run doxygen on the file and then run this program against the main XML file it created and the directory containing the ancilliary files. It will then output a lot of *.3 man page files which you can then ship with your library. .P You will need to invoke this program once for each .h file in your library, using the name of the generated .xml file. This file will usually be called something like _8h.xml, eg qbipcs_8h.xml .P If you want HTML output then simpy use nroff on the generated files as you would do with any other man page. .SH "OPTIONS" .TP .B -a Print ASCII dump of man pages to stdout .TP .B -m Write man page files to .TP .B -P Print PARAMS section .TP .B -s Write man pages into section (default 3) .TP .B -p Use name. default .TP .B -H
Set header (default \"Programmer's Manual\") .TP .B -I Set the include filename (defaults to the one in the XML file or unknown.h) .TP .B -i Set the prefix for header files (eg qb/) if the are installed a subdir of /usr/include .TP .B -C Set the company name in the copyright (default Red Hat) .TP .B -D Date to print at top of man pages (format not checked, default: today) .TP .B -Y Year to print at end of copyright line (default: today's year) .TP .B -S Year to print at start of copyright line (default: 2010) .TP .B -o Write all man pages to (default .) .TP .B -d Directory for XML files (./xml/) .TP +.B -c +Use the Copyright line from the header file as the copyright line in the manpage. +This requires that doxygen2man has access to the original .h file (see option -O below) +and that the Copyright line has a defined format. It whould be within the first 10 lines +of the file and start with the text " * Copyright". If this line is not found then +the current year or that given by the -Y option will be used. +.TP +.B -O +Specifies the directory containing the original header files. This is (currently) only +used by the -c option above. +.TP .B -h Print usage text .SH "SEE ALSO" .BR doxygen (1) .SH "AUTHOR" Christine Caulfield .PP diff --git a/doxygen2man/doxygen2man.c b/doxygen2man/doxygen2man.c index 880734a..4253aee 100644 --- a/doxygen2man/doxygen2man.c +++ b/doxygen2man/doxygen2man.c @@ -1,1155 +1,1195 @@ /* * Copyright (C) 2018-2020 Red Hat, Inc. All rights reserved. * * Author: Christine Caulfield * * This software licensed under GPL-2.0+ */ /* * NOTE: this code is very rough, it does the bare minimum to parse the * XML out from doxygen and is probably very fragile to changes in that XML * schema. It probably leaks memory all over the place too. * * In its favour, it *does* generate nice man pages and should only be run very ocasionally */ #define _DEFAULT_SOURCE #define _BSD_SOURCE #define _XOPEN_SOURCE #define _XOPEN_SOURCE_EXTENDED #include #include #include #include #include #include #include #include #include #include #include #include #include /* * This isn't a maximum size, it just defines how long a parameter * type can get before we decide it's not worth lining everything up. * It's mainly to stop function pointer types (which can get VERY long because * of all *their* parameters) making everything else 'line-up' over separate lines */ #define LINE_LENGTH 80 static int print_ascii = 1; static int print_man = 0; static int print_params = 0; static int print_general = 0; static int num_functions = 0; -static int quiet=0; +static int quiet = 0; +static int use_header_copyright = 0; static const char *man_section="3"; static const char *package_name="Package"; static const char *header="Programmer's Manual"; static const char *company="Red Hat"; static const char *output_dir="./"; static const char *xml_dir = "./xml/"; static const char *xml_file; static const char *manpage_date = NULL; static const char *headerfile = NULL; static const char *header_prefix = ""; +static const char *header_src_dir = "./"; +static char header_copyright[256] = "\0"; static long manpage_year = LONG_MIN; static long start_year = 2010; static struct qb_list_head params_list; static struct qb_list_head retval_list; static qb_map_t *function_map; static qb_map_t *structures_map; static qb_map_t *used_structures_map; struct param_info { char *paramname; char *paramtype; char *paramdesc; struct param_info *next; struct qb_list_head list; }; struct struct_info { enum {STRUCTINFO_STRUCT, STRUCTINFO_ENUM} kind; char *structname; char *description; char *brief_description; struct qb_list_head params_list; /* our params */ struct qb_list_head list; }; static char *get_texttree(int *type, xmlNode *cur_node, char **returntext, char **notetext); static void traverse_node(xmlNode *parentnode, const char *leafname, void (do_members(xmlNode*, void*)), void *arg); static void free_paraminfo(struct param_info *pi) { free(pi->paramname); free(pi->paramtype); free(pi->paramdesc); free(pi); } static char *get_attr(xmlNode *node, const char *tag) { xmlAttr *this_attr; for (this_attr = node->properties; this_attr; this_attr = this_attr->next) { if (this_attr->type == XML_ATTRIBUTE_NODE && strcmp((char *)this_attr->name, tag) == 0) { return strdup((char *)this_attr->children->content); } } return NULL; } static char *get_child(xmlNode *node, const char *tag) { xmlNode *this_node; xmlNode *child; char buffer[1024] = {'\0'}; char *refid = NULL; char *declname = NULL; for (this_node = node->children; this_node; this_node = this_node->next) { if ((strcmp( (char*)this_node->name, "declname") == 0)) { declname = strdup((char*)this_node->children->content); } if ((this_node->type == XML_ELEMENT_NODE && this_node->children) && ((strcmp((char *)this_node->name, tag) == 0))) { refid = NULL; for (child = this_node->children; child; child = child->next) { if (child->content) { strncat(buffer, (char *)child->content, sizeof(buffer)-1); } if ((strcmp( (char*)child->name, "ref") == 0)) { if (child->children->content) { strncat(buffer,(char *)child->children->content, sizeof(buffer)-1); } refid = get_attr(child, "refid"); } } } if (declname && refid) { qb_map_put(used_structures_map, refid, declname); } } return strdup(buffer); } static struct param_info *find_param_by_name(struct qb_list_head *list, const char *name) { struct qb_list_head *iter; struct param_info *pi; qb_list_for_each(iter, list) { pi = qb_list_entry(iter, struct param_info, list); if (strcmp(pi->paramname, name) == 0) { return pi; } } return NULL; } static int not_all_whitespace(char *string) { unsigned int i; for (i=0; ichildren; this_tag; this_tag = this_tag->next) { for (sub_tag = this_tag->children; sub_tag; sub_tag = sub_tag->next) { if (sub_tag->type == XML_ELEMENT_NODE && strcmp((char *)sub_tag->name, "parameternamelist") == 0 && sub_tag->children->next->children) { paramname = (char*)sub_tag->children->next->children->content; } if (sub_tag->type == XML_ELEMENT_NODE && strcmp((char *)sub_tag->name, "parameterdescription") == 0 && paramname && sub_tag->children->next->children) { paramdesc = (char*)sub_tag->children->next->children->content; /* Add text to the param_map */ pi = find_param_by_name(list, paramname); if (pi) { pi->paramdesc = paramdesc; } else { pi = malloc(sizeof(struct param_info)); if (pi) { pi->paramname = paramname; pi->paramdesc = paramdesc; pi->paramtype = NULL; /* retval */ qb_list_add_tail(&pi->list, list); } } } } } } static char *get_text(xmlNode *cur_node, char **returntext, char **notetext) { xmlNode *this_tag; xmlNode *sub_tag; char *kind; char buffer[4096] = {'\0'}; for (this_tag = cur_node->children; this_tag; this_tag = this_tag->next) { if (this_tag->type == XML_TEXT_NODE && strcmp((char *)this_tag->name, "text") == 0) { if (not_all_whitespace((char*)this_tag->content)) { strncat(buffer, (char*)this_tag->content, sizeof(buffer)-1); } } if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "emphasis") == 0) { if (print_man) { strncat(buffer, "\\fB", sizeof(buffer)-1); } strncat(buffer, (char*)this_tag->children->content, sizeof(buffer)-1); if (print_man) { strncat(buffer, "\\fR", sizeof(buffer)-1); } } if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "ref") == 0) { if (print_man) { strncat(buffer, "\\fI", sizeof(buffer)-1); } strncat(buffer, (char*)this_tag->children->content, sizeof(buffer)-1); if (print_man) { strncat(buffer, "\\fR", sizeof(buffer)-1); } } if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "itemizedlist") == 0) { for (sub_tag = this_tag->children; sub_tag; sub_tag = sub_tag->next) { if (sub_tag->type == XML_ELEMENT_NODE && strcmp((char *)sub_tag->name, "listitem") == 0) { strncat(buffer, (char*)sub_tag->children->children->content, sizeof(buffer)-1); strncat(buffer, "\n", sizeof(buffer)-1); } } } /* Look for subsections - return value & params */ if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "simplesect") == 0) { char *tmp; kind = get_attr(this_tag, "kind"); tmp = get_text(this_tag->children, NULL, NULL); if (returntext && strcmp(kind, "return") == 0) { *returntext = tmp; } if (notetext && strcmp(kind, "note") == 0) { *notetext = tmp; } } if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "parameterlist") == 0) { kind = get_attr(this_tag, "kind"); if (strcmp(kind, "param") == 0) { get_param_info(this_tag, ¶ms_list); } if (strcmp(kind, "retval") == 0) { get_param_info(this_tag, &retval_list); } } } return strdup(buffer); } static void read_structname(xmlNode *cur_node, void *arg) { struct struct_info *si=arg; xmlNode *this_tag; for (this_tag = cur_node->children; this_tag; this_tag = this_tag->next) { if (strcmp((char*)this_tag->name, "compoundname") == 0) { si->structname = strdup((char*)this_tag->children->content); } } } static void read_structdesc(xmlNode *cur_node, void *arg) { struct struct_info *si=arg; xmlNode *this_tag; for (this_tag = cur_node->children; this_tag; this_tag = this_tag->next) { if (strcmp((char*)this_tag->name, "detaileddescription") == 0) { char *desc = get_texttree(NULL, this_tag, NULL, NULL); if (desc) { si->description = strdup((char*)desc); } } if (strcmp((char*)this_tag->name, "briefdescription") == 0) { char *brief = get_texttree(NULL, this_tag, NULL, NULL); if (brief) { si->brief_description = brief; } } } } static void read_headername(xmlNode *cur_node, void *arg) { char **h_file = arg; xmlNode *this_tag; for (this_tag = cur_node->children; this_tag; this_tag = this_tag->next) { if (strcmp((char*)this_tag->name, "compoundname") == 0) { *h_file = strdup((char*)this_tag->children->content); } } } /* Called from traverse_node() */ static void read_struct(xmlNode *cur_node, void *arg) { xmlNode *this_tag; struct struct_info *si=arg; struct param_info *pi; char fullname[1024]; char *type = NULL; char *name = NULL; const char *args=""; for (this_tag = cur_node->children; this_tag; this_tag = this_tag->next) { if (strcmp((char*)this_tag->name, "type") == 0) { type = (char*)this_tag->children->content; /* If type is NULL then look for a ref - it's probably an external struct or typedef */ if (type == NULL) { type = get_child(this_tag, "ref"); } } if (strcmp((char*)this_tag->name, "name") == 0) { name = (char*)this_tag->children->content; } if (this_tag->children && strcmp((char*)this_tag->name, "argsstring") == 0) { args = (char*)this_tag->children->content; } } if (name) { pi = malloc(sizeof(struct param_info)); if (pi) { snprintf(fullname, sizeof(fullname), "%s%s", name, args); pi->paramtype = type?strdup(type):strdup(""); pi->paramname = strdup(fullname); pi->paramdesc = NULL; qb_list_add_tail(&pi->list, &si->params_list); } } } static int read_structure_from_xml(const char *refid, const char *name) { char fname[PATH_MAX]; xmlNode *rootdoc; xmlDocPtr doc; struct struct_info *si; struct stat st; int ret = -1; snprintf(fname, sizeof(fname), "%s/%s.xml", xml_dir, refid); /* Don't call into libxml if the file does not exist - saves unwanted error messages */ if (stat(fname, &st) == -1) { return -1; } doc = xmlParseFile(fname); if (doc == NULL) { fprintf(stderr, "Error: unable to open xml file for %s\n", refid); return -1; } rootdoc = xmlDocGetRootElement(doc); if (!rootdoc) { fprintf(stderr, "Can't find \"document root\"\n"); return -1; } si = malloc(sizeof(struct struct_info)); if (si) { memset(si, 0, sizeof(*si)); si->kind = STRUCTINFO_STRUCT; qb_list_init(&si->params_list); traverse_node(rootdoc, "memberdef", read_struct, si); traverse_node(rootdoc, "compounddef", read_structdesc, si); traverse_node(rootdoc, "compounddef", read_structname, si); ret = 0; qb_map_put(structures_map, refid, si); } xmlFreeDoc(doc); return ret; } static char *allcaps(const char *name) { static char buffer[1024] = {'\0'}; size_t i; for (i=0; i< strlen(name); i++) { buffer[i] = toupper(name[i]); } buffer[strlen(name)] = '\0'; return buffer; } static void print_param(FILE *manfile, struct param_info *pi, int field_width, int bold, const char *delimiter) { const char *asterisks = " "; char *type = pi->paramtype; int typelength = strlen(type); /* Reformat pointer params so they look nicer */ if (typelength > 0 && pi->paramtype[typelength-1] == '*') { asterisks=" *"; type = strdup(pi->paramtype); type[typelength-1] = '\0'; /* Cope with double pointers */ if (typelength > 1 && pi->paramtype[typelength-2] == '*') { asterisks="**"; type[typelength-2] = '\0'; } /* Tidy function pointers */ if (typelength > 1 && pi->paramtype[typelength-2] == '(') { asterisks="(*"; type[typelength-2] = '\0'; } } fprintf(manfile, " %s%-*s%s%s\\fI%s\\fP%s\n", bold?"\\fB":"", field_width, type, asterisks, bold?"\\fP":"", pi->paramname, delimiter); if (type != pi->paramtype) { free(type); } } static void print_structure(FILE *manfile, struct struct_info *si) { struct param_info *pi; struct qb_list_head *iter; unsigned int max_param_length=0; fprintf(manfile, ".nf\n"); fprintf(manfile, "\\fB\n"); if (si->brief_description) { fprintf(manfile, "%s\n", si->brief_description); } if (si->description) { fprintf(manfile, "%s\n", si->description); } qb_list_for_each(iter, &si->params_list) { pi = qb_list_entry(iter, struct param_info, list); if (strlen(pi->paramtype) > max_param_length) { max_param_length = strlen(pi->paramtype); } } if (si->kind == STRUCTINFO_STRUCT) { fprintf(manfile, "struct %s {\n", si->structname); } else if (si->kind == STRUCTINFO_ENUM) { fprintf(manfile, "enum %s {\n", si->structname); } else { fprintf(manfile, "%s {\n", si->structname); } qb_list_for_each(iter, &si->params_list) { pi = qb_list_entry(iter, struct param_info, list); print_param(manfile, pi, max_param_length, 0,";"); } fprintf(manfile, "};\n"); fprintf(manfile, "\\fP\n"); fprintf(manfile, ".fi\n"); } char *get_texttree(int *type, xmlNode *cur_node, char **returntext, char **notetext) { xmlNode *this_tag; char *tmp = NULL; char buffer[4096] = {'\0'}; for (this_tag = cur_node->children; this_tag; this_tag = this_tag->next) { if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "para") == 0) { tmp = get_text(this_tag, returntext, notetext); strncat(buffer, tmp, sizeof(buffer)-1); strncat(buffer, "\n", sizeof(buffer)-1); free(tmp); } } if (buffer[0]) { return strdup(buffer); } else { return NULL; } } /* The text output is VERY basic and just a check that it's working really */ static void print_text(char *name, char *def, char *brief, char *args, char *detailed, struct qb_list_head *param_list, char *returntext, char *notetext) { printf(" ------------------ %s --------------------\n", name); printf("NAME\n"); if (brief) { printf(" %s - %s\n", name, brief); } else { printf(" %s\n", name); } printf("SYNOPSIS\n"); printf(" #include <%s%s>\n", header_prefix, headerfile); if (args) { printf(" %s %s\n\n", name, args); } if (detailed) { printf("DESCRIPTION\n"); printf(" %s\n", detailed); } if (returntext) { printf("RETURN VALUE\n"); printf(" %s\n", returntext); } if (notetext) { printf("NOTE\n"); printf(" %s\n", notetext); } } /* Print a long string with para marks in it. */ static void man_print_long_string(FILE *manfile, char *text) { char *next_nl; char *current = text; next_nl = strchr(text, '\n'); while (next_nl && *next_nl != '\0') { *next_nl = '\0'; if (strlen(current)) { fprintf(manfile, ".PP\n%s\n", current); } *next_nl = '\n'; current = next_nl+1; next_nl = strchr(current, '\n'); } /* The bit at the end */ if (strlen(current)) { fprintf(manfile, ".PP\n%s\n", current); } } static void print_manpage(char *name, char *def, char *brief, char *args, char *detailed, struct qb_list_head *param_map, char *returntext, char *notetext) { char manfilename[PATH_MAX]; char gendate[64]; const char *dateptr = gendate; FILE *manfile; time_t t; struct tm *tm; qb_map_iter_t *map_iter; struct qb_list_head *iter; struct qb_list_head *tmp; const char *p; void *data; unsigned int max_param_type_len; unsigned int max_param_name_len; unsigned int num_param_descs; int param_count = 0; int param_num = 0; struct param_info *pi; t = time(NULL); tm = localtime(&t); if (!tm) { perror("unable to get localtime"); exit(1); } strftime(gendate, sizeof(gendate), "%Y-%m-%d", tm); if (manpage_date) { dateptr = manpage_date; } if (manpage_year == LONG_MIN) { manpage_year = tm->tm_year+1900; } snprintf(manfilename, sizeof(manfilename), "%s/%s.%s", output_dir, name, man_section); manfile = fopen(manfilename, "w+"); if (!manfile) { perror("unable to open output file"); printf("%s", manfilename); exit(1); } /* Work out the length of the parameters, so we can line them up */ max_param_type_len = 0; max_param_name_len = 0; num_param_descs = 0; qb_list_for_each(iter, ¶ms_list) { pi = qb_list_entry(iter, struct param_info, list); /* It's mainly macros that break this, * macros need more work */ if (!pi->paramtype) { pi->paramtype = strdup(""); } if ((strlen(pi->paramtype) < LINE_LENGTH) && (strlen(pi->paramtype) > max_param_type_len)) { max_param_type_len = strlen(pi->paramtype); } if (strlen(pi->paramname) > max_param_name_len) { max_param_name_len = strlen(pi->paramname); } if (pi->paramdesc) { num_param_descs++; } param_count++; } /* Off we go */ fprintf(manfile, ".\\\" Automatically generated man page, do not edit\n"); fprintf(manfile, ".TH %s %s %s \"%s\" \"%s\"\n", allcaps(name), man_section, dateptr, package_name, header); fprintf(manfile, ".SH NAME\n"); if (brief) { fprintf(manfile, "%s \\- %s\n", name, brief); } else { fprintf(manfile, "%s\n", name); } fprintf(manfile, ".SH SYNOPSIS\n"); fprintf(manfile, ".nf\n"); fprintf(manfile, ".B #include <%s%s>\n", header_prefix, headerfile); if (def) { fprintf(manfile, ".sp\n"); fprintf(manfile, "\\fB%s\\fP(\n", def); qb_list_for_each(iter, ¶ms_list) { pi = qb_list_entry(iter, struct param_info, list); print_param(manfile, pi, max_param_type_len, 1, ++param_num < param_count?",":""); } fprintf(manfile, ");\n"); fprintf(manfile, ".fi\n"); } if (print_params && num_param_descs) { fprintf(manfile, ".SH PARAMS\n"); qb_list_for_each(iter, ¶ms_list) { pi = qb_list_entry(iter, struct param_info, list); fprintf(manfile, "\\fB%-*s \\fP\\fI%s\\fP\n", (int)max_param_name_len, pi->paramname, pi->paramdesc); fprintf(manfile, ".PP\n"); } } if (detailed) { fprintf(manfile, ".SH DESCRIPTION\n"); man_print_long_string(manfile, detailed); } if (qb_map_count_get(used_structures_map)) { int first_struct = 1; map_iter = qb_map_iter_create(used_structures_map); for (p = qb_map_iter_next(map_iter, &data); p; p = qb_map_iter_next(map_iter, &data)) { struct struct_info *si; const char *refid = p; char *refname = data; /* If it's not been read in - go and look for it */ si = qb_map_get(structures_map, refid); if (!si) { if (!read_structure_from_xml(refid, refname)) { si = qb_map_get(structures_map, refid); } } /* Only print header if the struct files exist - sometimes they don't */ if (si && first_struct) { fprintf(manfile, ".SH STRUCTURES\n"); first_struct = 0; } if (si) { print_structure(manfile, si); fprintf(manfile, ".PP\n"); } } qb_map_iter_free(map_iter); fprintf(manfile, ".RE\n"); } if (returntext) { fprintf(manfile, ".SH RETURN VALUE\n"); man_print_long_string(manfile, returntext); fprintf(manfile, ".PP\n"); } qb_list_for_each(iter, &retval_list) { pi = qb_list_entry(iter, struct param_info, list); fprintf(manfile, "\\fB%-*s \\fP%s\n", 10, pi->paramname, pi->paramdesc); fprintf(manfile, ".PP\n"); } if (notetext) { fprintf(manfile, ".SH NOTE\n"); man_print_long_string(manfile, notetext); } fprintf(manfile, ".SH SEE ALSO\n"); fprintf(manfile, ".PP\n"); fprintf(manfile, ".nh\n"); fprintf(manfile, ".ad l\n"); param_num = 0; map_iter = qb_map_iter_create(function_map); for (p = qb_map_iter_next(map_iter, &data); p; p = qb_map_iter_next(map_iter, &data)) { /* Exclude us! */ if (strcmp(data, name)) { fprintf(manfile, "\\fI%s\\fR(%s)%s", (char *)data, man_section, param_num < (num_functions - 1)?", ":""); } param_num++; } qb_map_iter_free(map_iter); fprintf(manfile, "\n"); fprintf(manfile, ".ad\n"); fprintf(manfile, ".hy\n"); fprintf(manfile, ".SH \"COPYRIGHT\"\n"); fprintf(manfile, ".PP\n"); - fprintf(manfile, "Copyright (C) %4ld-%4ld %s, Inc. All rights reserved.\n", start_year, manpage_year, company); + if (header_copyright[0] == 'C') { + fprintf(manfile, "%s", header_copyright); /* String already contains trailing NL */ + } else { + fprintf(manfile, "Copyright (C) %4ld-%4ld %s, Inc. All rights reserved.\n", start_year, manpage_year, company); + } fclose(manfile); /* Free the params & retval info */ qb_list_for_each_safe(iter, tmp, ¶ms_list) { pi = qb_list_entry(iter, struct param_info, list); qb_list_del(&pi->list); free_paraminfo(pi); } qb_list_for_each_safe(iter, tmp, &retval_list) { pi = qb_list_entry(iter, struct param_info, list); qb_list_del(&pi->list); free_paraminfo(pi); } /* Free used-structures map */ map_iter = qb_map_iter_create(used_structures_map); for (p = qb_map_iter_next(map_iter, &data); p; p = qb_map_iter_next(map_iter, &data)) { qb_map_rm(used_structures_map, p); free(data); } } /* Same as traverse_members, but to collect function names */ static void collect_functions(xmlNode *cur_node, void *arg) { xmlNode *this_tag; char *kind; char *name = NULL; if (cur_node->name && strcmp((char *)cur_node->name, "memberdef") == 0) { kind = get_attr(cur_node, "kind"); if (kind && strcmp(kind, "function") == 0) { for (this_tag = cur_node->children; this_tag; this_tag = this_tag->next) { if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "name") == 0) { name = strdup((char *)this_tag->children->content); } } if (name) { qb_map_put(function_map, name, name); num_functions++; } } } } /* Same as traverse_members, but to collect enums. The behave like structures for, but, for some reason, are in the main XML file rather than their own */ static void collect_enums(xmlNode *cur_node, void *arg) { xmlNode *this_tag; struct struct_info *si; char *kind; char *refid = NULL; char *name = NULL; if (cur_node->name && strcmp((char *)cur_node->name, "memberdef") == 0) { kind = get_attr(cur_node, "kind"); if (kind && strcmp(kind, "enum") == 0) { refid = get_attr(cur_node, "id"); for (this_tag = cur_node->children; this_tag; this_tag = this_tag->next) { if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "name") == 0) { name = strdup((char *)this_tag->children->content); } } if (name) { si = malloc(sizeof(struct struct_info)); if (si) { memset(si, 0, sizeof(*si)); si->kind = STRUCTINFO_ENUM; qb_list_init(&si->params_list); si->structname = strdup(name); traverse_node(cur_node, "enumvalue", read_struct, si); qb_map_put(structures_map, refid, si); } } } } } static void traverse_members(xmlNode *cur_node, void *arg) { xmlNode *this_tag; /* if arg == NULL then we're generating a page for the whole header file */ if ((cur_node->name && (strcmp((char *)cur_node->name, "memberdef") == 0)) || ((arg == NULL) && cur_node->name && strcmp((char *)cur_node->name, "compounddef")) == 0) { char *kind = NULL; char *def = NULL; char *args = NULL; char *name = NULL; char *brief = NULL; char *detailed = NULL; char *returntext = NULL; char *notetext = NULL; int type; kind=def=args=name=NULL; kind = get_attr(cur_node, "kind"); for (this_tag = cur_node->children; this_tag; this_tag = this_tag->next) { if (!this_tag->children || !this_tag->children->content) continue; if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "definition") == 0) def = strdup((char *)this_tag->children->content); if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "argsstring") == 0) args = strdup((char *)this_tag->children->content); if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "name") == 0) name = strdup((char *)this_tag->children->content); if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "briefdescription") == 0) { brief = get_texttree(&type, this_tag, &returntext, ¬etext); if (brief) { /* * apparently brief text contains extra trailing space and a \n. * remove them. */ brief[strlen(brief) - 2] = '\0'; } } if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "detaileddescription") == 0) { detailed = get_texttree(&type, this_tag, &returntext, ¬etext); } /* Get all the params */ if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "param") == 0) { char *param_type = get_child(this_tag, "type"); char *param_name = get_child(this_tag, "declname"); struct param_info *pi = malloc(sizeof(struct param_info)); if (pi) { pi->paramname = param_name; pi->paramtype = param_type; pi->paramdesc = NULL; qb_list_add_tail(&pi->list, ¶ms_list); } } } if (arg == headerfile) { /* Print header page */ name = (char*)headerfile; if (print_man) { if (!quiet) { printf("Printing header manpage for %s\n", name); } print_manpage(name, def, brief, args, detailed, ¶ms_list, returntext, notetext); } else { print_text(name, def, brief, args, detailed, ¶ms_list, returntext, notetext); } } if (kind && strcmp(kind, "function") == 0) { /* Make sure function has a doxygen description */ if (!detailed) { fprintf(stderr, "No detailed description for function '%s' - please fix this\n", name); } if (!name) { fprintf(stderr, "Internal error - no name found for function\n"); } else { if (print_man) { if (!quiet) { printf("Printing manpage for %s\n", name); } print_manpage(name, def, brief, args, detailed, ¶ms_list, returntext, notetext); } else { print_text(name, def, brief, args, detailed, ¶ms_list, returntext, notetext); } } } free(kind); free(def); free(args); free(name); } } static void traverse_node(xmlNode *parentnode, const char *leafname, void (do_members(xmlNode*, void*)), void *arg) { xmlNode *cur_node; for (cur_node = parentnode->children; cur_node; cur_node = cur_node->next) { if (cur_node->type == XML_ELEMENT_NODE && cur_node->name && strcmp((char*)cur_node->name, leafname)==0) { do_members(cur_node, arg); continue; } if (cur_node->type == XML_ELEMENT_NODE) { traverse_node(cur_node, leafname, do_members, arg); } } } static void usage(char *name) { printf("Usage:\n"); printf(" %s [OPTIONS] \n", name); printf("\n"); printf(" This is a tool to generate API manpages from a doxygen-annotated header file.\n"); printf(" First run doxygen on the file and then run this program against the main XML file\n"); printf(" it created and the directory containing the ancilliary files. It will then\n"); printf(" output a lot of *.3 man page files which you can then ship with your library.\n"); printf("\n"); printf(" You will need to invoke this program once for each .h file in your library,\n"); printf(" using the name of the generated .xml file. This file will usually be called\n"); printf(" something like _8h.xml, eg qbipcs_8h.xml\n"); printf("\n"); printf(" If you want HTML output then simpy use nroff on the generated files as you\n"); printf(" would do with any other man page.\n"); printf("\n"); printf(" -a Print ASCII dump of man pages to stdout\n"); printf(" -m Write man page files to \n"); printf(" -P Print PARAMS section\n"); printf(" -g Print general man page for the whole header file\n"); + printf(" -c Use the Copyright date from the header file (if one can be found)\n"); + printf(" -O Directory for the orignal header file. Often needed by -c above\n"); printf(" -s Write man pages into section Use name. default \n"); printf(" -H
Set header (default \"Programmer's Manual\"\n"); printf(" -I Set include filename (default taken from xml)\n"); printf(" -i Prefix for include files. eg qb/ (default \"\")\n"); printf(" -C Company name in copyright (defaults to Red Hat)\n"); printf(" -D Date to print at top of man pages (format not checked, default: today)\n"); printf(" -S Start year to print at end of copyright line (default: 2010)\n"); printf(" -Y Year to print at end of copyright line (default: today's year)\n"); printf(" -o Write all man pages to (default .)\n"); printf(" -d Directory for XML files (./xml/)\n"); printf(" -h Print this usage text\n"); } static long get_year(char *optionarg, char optionchar) { long year = strtol(optionarg, NULL, 10); /* * Don't make too many assumptions about the year. I was on call at the * 2000 rollover. #experience */ if (year == LONG_MIN || year == LONG_MAX || year < 1900) { fprintf(stderr, "Value passed to -%c is not a valid year number\n", optionchar); return 0; } return year; } int main(int argc, char *argv[]) { xmlNode *rootdoc; xmlDocPtr doc; int opt; char xml_filename[PATH_MAX]; - while ( (opt = getopt_long(argc, argv, "H:amqgPD:Y:s:S:d:o:p:f:I:i:C:h?", NULL, NULL)) != EOF) + while ( (opt = getopt_long(argc, argv, "H:amqgcPD:Y:s:S:d:o:p:f:I:i:C:O:h?", NULL, NULL)) != EOF) { switch(opt) { case 'a': print_ascii = 1; print_man = 0; break; case 'm': print_man = 1; print_ascii = 0; break; case 'P': print_params = 1; break; case 'g': print_general = 1; break; case 'q': quiet = 1; break; + case 'c': + use_header_copyright = 1; + break; case 'I': headerfile = optarg; break; case 'i': header_prefix = optarg; break; case 'C': company = optarg; break; case 's': man_section = optarg; break; case 'S': start_year = get_year(optarg, 'S'); if (start_year == 0) { return 1; } break; case 'd': xml_dir = optarg; break; case 'D': manpage_date = optarg; break; case 'Y': manpage_year = get_year(optarg, 'Y'); if (manpage_year == 0) { return 1; } break; case 'p': package_name = optarg; break; case 'H': header = optarg; break; case 'o': output_dir = optarg; break; + case 'O': + header_src_dir = optarg; + break; case '?': case 'h': usage(argv[0]); return 0; } } if (argv[optind]) { xml_file = argv[optind]; } if (!xml_file) { usage(argv[0]); exit(1); } if (!quiet) { printf("reading %s ... ", xml_file); } snprintf(xml_filename, sizeof(xml_filename), "%s/%s", xml_dir, xml_file); doc = xmlParseFile(xml_filename); if (doc == NULL) { fprintf(stderr, "Error: unable to read xml file %s\n", xml_filename); exit(1); } rootdoc = xmlDocGetRootElement(doc); if (!rootdoc) { fprintf(stderr, "Can't find \"document root\"\n"); exit(1); } if (!quiet) printf("done.\n"); /* Get our header file name */ if (!headerfile) { traverse_node(rootdoc, "compounddef", read_headername, &headerfile); + + if (use_header_copyright) { + /* And get the copyright line from this file if we can */ + char file_path[PATH_MAX]; + char file_line[256]; + FILE *hfile; + int lineno = 0; + + snprintf(file_path, sizeof(file_path), "%s/%s", header_src_dir, headerfile); + hfile = fopen(file_path, "r"); + if (hfile) { + /* Don't look too far, this should be at the top */ + while (!feof(hfile) && (lineno++ < 10)) { + if (fgets(file_line, sizeof(file_line)-1, hfile)) { + if (strncmp(file_line, " * Copyright", 12) == 0) { + /* Keep the NL at the end of the buffer, it save us printing one */ + strncpy(header_copyright, file_line+3, sizeof(header_copyright)-1); + break; + } + } + } + fclose(hfile); + } + } } + /* Default to *something* if it all goes wrong */ if (!headerfile) { headerfile = "unknown.h"; } qb_list_init(¶ms_list); qb_list_init(&retval_list); structures_map = qb_hashtable_create(10); function_map = qb_hashtable_create(10); used_structures_map = qb_hashtable_create(10); /* Collect functions */ traverse_node(rootdoc, "memberdef", collect_functions, NULL); /* Collect enums */ traverse_node(rootdoc, "memberdef", collect_enums, NULL); /* print pages */ traverse_node(rootdoc, "memberdef", traverse_members, NULL); if (print_general) { /* Generate and print a page for the headerfile itself */ traverse_node(rootdoc, "compounddef", traverse_members, (char *)headerfile); } return 0; } diff --git a/include/qb/qbarray.h b/include/qb/qbarray.h index 6c796fe..eca298b 100644 --- a/include/qb/qbarray.h +++ b/include/qb/qbarray.h @@ -1,139 +1,139 @@ /* - * Copyright (C) 2010 Red Hat, Inc. + * Copyright (C) 2010-2020 Red Hat, Inc. * * Author: Angus Salkeld * * This file is part of libqb. * * libqb 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. * * libqb 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 libqb. If not, see . */ #ifndef QB_ARRAY_H_DEFINED #define QB_ARRAY_H_DEFINED #include #ifndef S_SPLINT_S #include #endif /* S_SPLINT_S */ #include /* *INDENT-OFF* */ #ifdef __cplusplus extern "C" { #endif /* *INDENT-ON* */ /** * @file qbarray.h * This is a dynamic array (it can grow, but without moving memory). * * @code * arr = qb_array_create_2(64, sizeof(struct my_struct), 256); * ... * res = qb_array_index(arr, idx, (void**)&my_ptr); * if (res < 0) { * return res; * } * // use my_ptr, now even if there is a grow, this pointer will be valid. * @endcode * * Currently, this dynamic array abstract data type can accommodate only * 2^@c QB_ARRAY_MAX_INDEX_BITS elements, and with standard zero-based * indexing, this gives a valid index range [0, @c QB_ARRAY_MAX_ELEMENTS), * where the notation denotes the beginning of the interval is included and * the end is excluded. In other words, client space shall avoid a pitfall * of relying solely on the type of @c max_elements parameter to * @ref qb_array_create and/or of @c idx parameter to @ref qb_array_index * (these types conflict, anyway). */ #define QB_ARRAY_MAX_INDEX_BITS 16 #define QB_ARRAY_MAX_ELEMENTS (1 << QB_ARRAY_MAX_INDEX_BITS) struct qb_array; /** * This is an opaque data type representing an instance of an array. */ typedef struct qb_array qb_array_t; /** * Create an array with fixed sized elements. * * @param max_elements initial max elements * @param element_size size of each element * @return array instance */ qb_array_t* qb_array_create(size_t max_elements, size_t element_size); /** * Create an array with fixed sized elements. * * @param max_elements initial max elements * @param element_size size of each element * @param autogrow_elements the number of elements to grow automatically by. * @return array instance */ qb_array_t* qb_array_create_2(size_t max_elements, size_t element_size, size_t autogrow_elements); /** * Get an element at a particular index. * @param a array instance * @param idx the index, valid in [0, @c QB_ARRAY_MAX_ELEMENTS) range * @param element_out the pointer to the element data * @return (0 == success, else -errno) */ int32_t qb_array_index(qb_array_t* a, int32_t idx, void** element_out); /** * Grow the array. * * @param a array instance * @param max_elements the new maximum size of the array * @return (0 == success, else -errno) */ int32_t qb_array_grow(qb_array_t* a, size_t max_elements); /** * Get the number of bins used by the array. */ size_t qb_array_num_bins_get(qb_array_t* a); /** * Get the number of elements per bin. */ size_t qb_array_elems_per_bin_get(qb_array_t* a); typedef void (*qb_array_new_bin_cb_fn)(qb_array_t * a, uint32_t bin); /** * Get a callback when a new bin is allocated. */ int32_t qb_array_new_bin_cb_set(qb_array_t * a, qb_array_new_bin_cb_fn fn); /** * Free all the memory used by the array. * @param a array instance */ void qb_array_free(qb_array_t * a); /* *INDENT-OFF* */ #ifdef __cplusplus } #endif /* *INDENT-ON* */ #endif /* QB_ARRAY_H_DEFINED */ diff --git a/include/qb/qbconfig.h.in b/include/qb/qbconfig.h.in index 4a1508c..e027aee 100644 --- a/include/qb/qbconfig.h.in +++ b/include/qb/qbconfig.h.in @@ -1,49 +1,49 @@ /* - * Copyright (C) 2011 Red Hat, Inc. + * Copyright (C) 2010-2020 Red Hat, Inc. * * All rights reserved. * * Author: Angus Salkeld * * libqb 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. * * libqb 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 libqb. If not, see . */ #ifndef QB_CONFIG_H_DEFINED #define QB_CONFIG_H_DEFINED #include /* QB_PP_STRINGIFY */ /* need atomic memory barrier */ #undef QB_ATOMIC_OP_MEMORY_BARRIER_NEEDED /* versioning info: MAJOR, MINOR, MICRO, and REST components; note that static compile-time info is not that useful as consulting the respectively named members of qb_version struct constant under @c qb_ver identifier (or @c qb_ver_str equivalent of the local upper-cased value) directly from libqb in run-time (see qbutil.h), but that was only introduced after v1.0.2 */ #undef QB_VER_MAJOR #undef QB_VER_MINOR #undef QB_VER_MICRO #undef QB_VER_REST #define QB_VER_STR \ QB_PP_STRINGIFY(QB_VER_MAJOR) \ "." \ QB_PP_STRINGIFY(QB_VER_MINOR) \ "." \ QB_PP_STRINGIFY(QB_VER_MICRO) \ QB_VER_REST #endif /* QB_CONFIG_H_DEFINED */ diff --git a/include/qb/qbdefs.h b/include/qb/qbdefs.h index 67aa4bb..8b7fbe5 100644 --- a/include/qb/qbdefs.h +++ b/include/qb/qbdefs.h @@ -1,117 +1,117 @@ /* - * Copyright (C) 2010 Red Hat, Inc. + * Copyright (C) 2010-2020 Red Hat, Inc. * * Author: Angus Salkeld * * libqb 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. * * libqb 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 libqb. If not, see . */ #ifndef QB_DEFS_H_DEFINED #define QB_DEFS_H_DEFINED /* *INDENT-OFF* */ #ifdef __cplusplus extern "C" { #endif /* *INDENT-ON* */ /** * @file qbdefs.h * These are some convience macros and defines. * * @author Angus Salkeld */ /* * simple math macros */ #define QB_ROUNDUP(x, y) ((((x) + ((y) - 1)) / (y)) * (y)) #define QB_ABS(i) (((i) < 0) ? -(i) : (i)) #define QB_MAX(a, b) (((a) > (b)) ? (a) : (b)) #define QB_MIN(a, b) (((a) < (b)) ? (a) : (b)) /* * the usual boolean defines */ #define QB_FALSE 0 #define QB_TRUE (!QB_FALSE) /* * bit manipulation */ #define qb_bit_value(bit) (1U << (bit)) #define qb_bit_set(barray, bit) (barray |= qb_bit_value(bit)) #define qb_bit_clear(barray, bit) (barray &= ~(qb_bit_value(bit))) #define qb_bit_is_set(barray, bit) (barray & qb_bit_value(bit)) #define qb_bit_is_clear(barray, bit) (!(barray & qb_bit_value(bit))) /* * wrappers over preprocessor operators */ #define QB_PP_JOIN_(a, b) a##b #define QB_PP_JOIN(a, b) QB_PP_JOIN_(a, b) #define QB_PP_STRINGIFY_(arg) #arg #define QB_PP_STRINGIFY(arg) QB_PP_STRINGIFY_(arg) /* * handy time based converters. */ #ifndef HZ #define HZ 100 /* 10ms */ #endif #define QB_TIME_MS_IN_SEC 1000ULL #define QB_TIME_US_IN_SEC 1000000ULL #define QB_TIME_NS_IN_SEC 1000000000ULL #define QB_TIME_US_IN_MSEC 1000ULL #define QB_TIME_NS_IN_MSEC 1000000ULL #define QB_TIME_NS_IN_USEC 1000ULL #if defined (__GNUC__) && defined (__STRICT_ANSI__) #undef inline #define inline __inline__ #undef typeof #define typeof __typeof__ #endif /* ANSI */ #if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) #define QB_GNUC_DEPRECATED \ __attribute__((__deprecated__)) #else #define QB_GNUC_DEPRECATED #endif /* __GNUC__ */ #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) #define QB_GNUC_DEPRECATED_FOR(f) \ __attribute__((deprecated("Use " #f " instead"))) #else #define QB_GNUC_DEPRECATED_FOR(f) QB_GNUC_DEPRECATED #endif /* __GNUC__ */ #if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3) #define QB_GNUC_MAY_ALIAS __attribute__((may_alias)) #else #define QB_GNUC_MAY_ALIAS #endif /* *INDENT-OFF* */ #ifdef __cplusplus } #endif /* __cplusplus */ /* *INDENT-ON* */ #endif /* QB_DEFS_H_DEFINED */ diff --git a/include/qb/qbhdb.h b/include/qb/qbhdb.h index 87b2292..1926cc0 100644 --- a/include/qb/qbhdb.h +++ b/include/qb/qbhdb.h @@ -1,184 +1,184 @@ /* - * Copyright (C) 2006-2010 Red Hat, Inc. + * Copyright (C) 2010-2020 Red Hat, Inc. * * Author: Steven Dake * * This file is part of libqb. * * libqb 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. * * libqb 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 libqb. If not, see . */ #ifndef QB_HDB_H_DEFINED #define QB_HDB_H_DEFINED #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif /* *INDENT-OFF* */ #ifdef __cplusplus extern "C" { #endif /* *INDENT-ON* */ #include #include #include #include /** * @file qbhdb.h * The handle database is for reference counting objects. * * @note * Historically, handle database implementation also served internal needs * of libqb (e.g. for IPC services tracking), which was eventually replaced * with indirection-less reference counters and their direct modifications. */ /** * Generic handle type is 64 bits. */ typedef uint64_t qb_handle_t; /* * Formatting for string printing on 32/64 bit systems */ #define QB_HDB_D_FORMAT "%" PRIu64 #define QB_HDB_X_FORMAT "%" PRIx64 struct qb_hdb_handle { int32_t state; void *instance; int32_t check; int32_t ref_count; }; struct qb_hdb { uint32_t handle_count; qb_array_t *handles; uint32_t iterator; void (*destructor) (void *); uint32_t first_run; }; /** * Convience macro for declaring a file scoped handle database. * @code * QB_HDB_DECLARE(my_handle_database, NULL); * @endcode */ #define QB_HDB_DECLARE(database_name,destructor_function) \ static struct qb_hdb (database_name) = { \ .handle_count = 0, \ .handles = NULL, \ .iterator = 0, \ .destructor = destructor_function, \ .first_run = QB_TRUE \ }; \ /** * Create a new database. * @param hdb the database to init. */ void qb_hdb_create(struct qb_hdb *hdb); /** * Destroy a handle database. * @param hdb the database to destroy. */ void qb_hdb_destroy(struct qb_hdb *hdb); /** * Create a new handle. * @param hdb the database instance * @param instance_size size of the object to malloc * @param handle_id_out new handle * @return (0 == ok, -errno failure) */ int32_t qb_hdb_handle_create(struct qb_hdb *hdb, int32_t instance_size, qb_handle_t * handle_id_out); /** * Get the instance associated with this handle and increase it's refcount. * @param handle_in the handle * @param hdb the database instance * @param instance (out) pointer to the desired object. * @return (0 == ok, -errno failure) */ int32_t qb_hdb_handle_get(struct qb_hdb *hdb, qb_handle_t handle_in, void **instance); /** * Get the instance associated with this handle and increase it's refcount. * @param handle_in the handle * @param hdb the database instance * @param instance (out) pointer to the desired object. * @return (0 == ok, -errno failure) * @note This is currently an alias to @ref qb_hdb_handle_get. */ int32_t qb_hdb_handle_get_always(struct qb_hdb *hdb, qb_handle_t handle_in, void **instance); /** * Put the instance associated with this handle and decrease it's refcount. * @param handle_in the handle * @param hdb the database instance * @return (0 == ok, -errno failure) */ int32_t qb_hdb_handle_put(struct qb_hdb *hdb, qb_handle_t handle_in); /** * Request the destruction of the object. * * When the refcount is 0, it will be destroyed. * * @param handle_in the handle * @param hdb the database instance * @return (0 == ok, -errno failure) */ int32_t qb_hdb_handle_destroy(struct qb_hdb *hdb, qb_handle_t handle_in); /** * Get the current refcount. * @param handle_in the handle * @param hdb the database instance * @return (>= 0 is the refcount, -errno failure) */ int32_t qb_hdb_handle_refcount_get(struct qb_hdb *hdb, qb_handle_t handle_in); /** * Reset the iterator. * @param hdb the database instance */ void qb_hdb_iterator_reset(struct qb_hdb *hdb); /** * Get the next object and increament it's refcount. * * Remember to call qb_hdb_handle_put() * * @param hdb the database instance * @param handle (out) the handle * @param instance (out) pointer to the desired object. * @return (0 == ok, -errno failure) */ int32_t qb_hdb_iterator_next(struct qb_hdb *hdb, void **instance, qb_handle_t * handle); uint32_t qb_hdb_base_convert(qb_handle_t handle); uint64_t qb_hdb_nocheck_convert(uint32_t handle); /* *INDENT-OFF* */ #ifdef __cplusplus } #endif /* *INDENT-ON* */ #endif /* QB_HDB_H_DEFINED */ diff --git a/include/qb/qbipcc.h b/include/qb/qbipcc.h index 5f8bea9..5bf14cf 100644 --- a/include/qb/qbipcc.h +++ b/include/qb/qbipcc.h @@ -1,253 +1,253 @@ /* - * Copyright (C) 2006-2007, 2009 Red Hat, Inc. + * Copyright (C) 2010-2020 Red Hat, Inc. * * Author: Steven Dake * * This file is part of libqb. * * libqb 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. * * libqb 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 libqb. If not, see . */ #ifndef QB_IPCC_H_DEFINED #define QB_IPCC_H_DEFINED /* *INDENT-OFF* */ #ifdef __cplusplus extern "C" { #endif /* *INDENT-ON* */ #include /* size_t, ssize_t */ #include /* iovec */ #include /** * @file qbipcc.h * * Client IPC API. * * @par Lifecycle of an IPC connection. * An IPC connection is made to the server with qb_ipcc_connect(). This function * connects to the server and requests channels be created for communication. * To disconnect, the client either exits or executes the function qb_ipcc_disconnect(). * * @par Synchronous communication * The function qb_ipcc_sendv_recv() sends an iovector request and receives a response. * * @par Asynchronous requests from the client * The function qb_ipcc_sendv() sends an iovector request. * The function qb_ipcc_send() sends an message buffer request. * * @par Asynchronous events from the server * The qb_ipcc_event_recv() function receives an out-of-band asynchronous message. * The asynchronous messages are queued and can provide very high out-of-band performance. * To determine when to call qb_ipcc_event_recv() the qb_ipcc_fd_get() call is * used to obtain a file descriptor used in the poll() or select() system calls. * * @example ipcclient.c * This is an example of how to use the client. */ typedef struct qb_ipcc_connection qb_ipcc_connection_t; /** * Create a connection to an IPC service. * * @param name name of the service. * @param max_msg_size biggest msg size. * @return NULL (error: see errno) or a connection object. * * @note It is recommended to do a one time check on the * max_msg_size value using qb_ipcc_verify_dgram_max_msg_size * _BEFORE_ calling the connect function when IPC_SOCKET is in use. * Some distributions while allow large message buffers to be * set on the socket, but not actually honor them because of * kernel state values. The qb_ipcc_verify_dgram_max_msg_size * function both sets the socket buffer size and verifies it by * doing a send/recv. */ qb_ipcc_connection_t* qb_ipcc_connect(const char *name, size_t max_msg_size); /** * Test kernel dgram socket buffers to verify the largest size up * to the max_msg_size value a single msg can be. Rounds down to the * nearest 1k. * * @param max_msg_size biggest msg size. * @return -1 if max size can not be detected, positive value * representing the largest single msg up to max_msg_size * that can successfully be sent over a unix dgram socket. */ int32_t qb_ipcc_verify_dgram_max_msg_size(size_t max_msg_size); /** * Disconnect an IPC connection. * * @param c connection instance */ void qb_ipcc_disconnect(qb_ipcc_connection_t* c); /** * Get the file descriptor to poll. * * @param c connection instance * @param fd (out) file descriptor to poll */ int32_t qb_ipcc_fd_get(qb_ipcc_connection_t* c, int32_t * fd); /** * Set the maximum allowable flowcontrol value. * * @note the default is 1 * * @param c connection instance * @param max the max allowable flowcontrol value (1 or 2) */ int32_t qb_ipcc_fc_enable_max_set(qb_ipcc_connection_t * c, uint32_t max); /** * Send a message. * * @param c connection instance * @param msg_ptr pointer to a message to send * @param msg_len the size of the message * @return (size sent, -errno == error) * * @note the msg_ptr must include a qb_ipc_request_header at * the top of the message. The server will read the size field * to determine how much to recv. */ ssize_t qb_ipcc_send(qb_ipcc_connection_t* c, const void *msg_ptr, size_t msg_len); /** * Send a message (iovec). * * @param c connection instance * @param iov pointer to an iovec struct to send * @param iov_len the number of iovecs used * @return (size sent, -errno == error) * * @note the iov[0] must be a qb_ipc_request_header. The server will * read the size field to determine how much to recv. */ ssize_t qb_ipcc_sendv(qb_ipcc_connection_t* c, const struct iovec* iov, size_t iov_len); /** * Receive a response. * * @param c connection instance * @param msg_ptr pointer to a message buffer to receive into * @param msg_len the size of the buffer * @param ms_timeout max time to wait for a response * @return (size recv'ed, -errno == error) * * @note that msg_ptr will include a qb_ipc_response_header at * the top of the message. */ ssize_t qb_ipcc_recv(qb_ipcc_connection_t* c, void *msg_ptr, size_t msg_len, int32_t ms_timeout); /** * This is a convenience function that simply sends and then recvs. * * @param c connection instance * @param iov pointer to an iovec struct to send * @param iov_len the number of iovecs used * @param msg_ptr pointer to a message buffer to receive into * @param msg_len the size of the buffer * @param ms_timeout max time to wait for a response * * @note the iov[0] must include a qb_ipc_request_header at * the top of the message. The server will read the size field * to determine how much to recv. * @note that msg_ptr will include a qb_ipc_response_header at * the top of the message. * * @see qb_ipcc_sendv() qb_ipcc_recv() */ ssize_t qb_ipcc_sendv_recv(qb_ipcc_connection_t *c, const struct iovec *iov, uint32_t iov_len, void *msg_ptr, size_t msg_len, int32_t ms_timeout); /** * Receive an event. * * @param c connection instance * @param msg_ptr pointer to a message buffer to receive into * @param msg_len the size of the buffer * @param ms_timeout time in milliseconds to wait for a message * 0 == no wait, negative == block, positive == wait X ms. * @return size of the message or error (-errno) * * @note that msg_ptr will include a qb_ipc_response_header at * the top of the message. */ ssize_t qb_ipcc_event_recv(qb_ipcc_connection_t* c, void *msg_ptr, size_t msg_len, int32_t ms_timeout); /** * Associate a "user" pointer with this connection. * * @param context the point to associate with this connection. * @param c connection instance * @see qb_ipcc_context_get() */ void qb_ipcc_context_set(qb_ipcc_connection_t *c, void *context); /** * Get the context (set previously) * * @param c connection instance * @return the context * @see qb_ipcc_context_set() */ void *qb_ipcc_context_get(qb_ipcc_connection_t *c); /** * Is the connection connected? * * @param c connection instance * @retval QB_TRUE when connected * @retval QB_FALSE when not connected */ int32_t qb_ipcc_is_connected(qb_ipcc_connection_t *c); /** * What is the actual buffer size used after the connection. * * @note The buffer size is guaranteed to be at least the size * of the value given in qb_ipcc_connect, but it is possible * the server will enforce a larger size depending on the * implementation. If the server side is known to enforce * a buffer size, use this function after the client connection * is established to retrieve the buffer size in use. It is * important for the client side to know the buffer size in use * so the client can successfully retrieve large server events. * * @param c connection instance * @retval connection size in bytes or -error code */ int32_t qb_ipcc_get_buffer_size(qb_ipcc_connection_t * c); /* *INDENT-OFF* */ #ifdef __cplusplus } #endif /* *INDENT-ON* */ #endif /* QB_IPCC_H_DEFINED */ diff --git a/include/qb/qbipcs.h b/include/qb/qbipcs.h index 2ccb0c8..1a3af7a 100644 --- a/include/qb/qbipcs.h +++ b/include/qb/qbipcs.h @@ -1,500 +1,500 @@ /* - * Copyright (C) 2006-2009 Red Hat, Inc. + * Copyright (C) 2010-2020 Red Hat, Inc. * * Author: Steven Dake , * Angus Salkeld * * This file is part of libqb. * * libqb 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. * * libqb 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 libqb. If not, see . */ #ifndef QB_IPCS_H_DEFINED #define QB_IPCS_H_DEFINED /* *INDENT-OFF* */ #ifdef __cplusplus extern "C" { #endif /* *INDENT-ON* */ #include /* size_t, ssize_t */ #include /* iovec */ #include /* qb_ipc_type */ #include /* qb_loop_priority */ /** * @file qbipcs.h * * Server IPC API. * * @example ipcserver.c */ /** * Rates to be passed to #qb_ipcs_request_rate_limit. The exact interpretation * depends on how the event loop implementation understands the concept of * priorities, see the discussion at #qb_ipcs_poll_handlers structure -- an * integration point between IPC server instance and the underlying event loop. */ enum qb_ipcs_rate_limit { QB_IPCS_RATE_FAST, QB_IPCS_RATE_NORMAL, QB_IPCS_RATE_SLOW, QB_IPCS_RATE_OFF, QB_IPCS_RATE_OFF_2, }; struct qb_ipcs_connection; typedef struct qb_ipcs_connection qb_ipcs_connection_t; struct qb_ipcs_service; typedef struct qb_ipcs_service qb_ipcs_service_t; struct qb_ipcs_stats { uint32_t active_connections; uint32_t closed_connections; }; struct qb_ipcs_connection_stats { int32_t client_pid; uint64_t requests; uint64_t responses; uint64_t events; uint64_t send_retries; uint64_t recv_retries; int32_t flow_control_state; uint64_t flow_control_count; }; struct qb_ipcs_connection_stats_2 { int32_t client_pid; uint64_t requests; uint64_t responses; uint64_t events; uint64_t send_retries; uint64_t recv_retries; int32_t flow_control_state; uint64_t flow_control_count; uint32_t event_q_length; }; typedef int32_t (*qb_ipcs_dispatch_fn_t) (int32_t fd, int32_t revents, void *data); typedef int32_t (*qb_ipcs_dispatch_add_fn)(enum qb_loop_priority p, int32_t fd, int32_t events, void *data, qb_ipcs_dispatch_fn_t fn); typedef int32_t (*qb_ipcs_dispatch_mod_fn)(enum qb_loop_priority p, int32_t fd, int32_t events, void *data, qb_ipcs_dispatch_fn_t fn); typedef int32_t (*qb_ipcs_dispatch_del_fn)(int32_t fd); typedef int32_t (*qb_ipcs_job_add_fn)(enum qb_loop_priority p, void *data, qb_loop_job_dispatch_fn dispatch_fn); /* * A set of callbacks that need to be provided (only #job_add can be #NULL) * whenever the IPC server is to be run (by the means of #qb_ipcs_run). * It is possible to use accordingly named functions defined in qbloop.h module * or integrate with other existing (like GLib's event loop) or entirely new * code -- see the subtle distinction amongst the possible event loops pointed * out in the introductory comment at qbloop.h. * * At that occasion, please note the correlation of #QB_IPCS_RATE_FAST etc. * symbolic names with said advisory effect of the priorities in the native * implementation. This correspondence will not be this intuitively seemless * if some other event loop implementation is hooked in given that it abids * them strictly as mentioned (e.g. GLib's event loop over poll'able sources). * Differences between the two paradigms should also be accounted for when * the requirement to swap the event loop implementations arises. */ struct qb_ipcs_poll_handlers { qb_ipcs_job_add_fn job_add; qb_ipcs_dispatch_add_fn dispatch_add; qb_ipcs_dispatch_mod_fn dispatch_mod; qb_ipcs_dispatch_del_fn dispatch_del; }; /** * This callback is to check whether you want to accept a new connection. * * The type of checks you should do are authentication, service availability * or process resource constraints. * @return 0 to accept or -errno to indicate a failure (sent back to the client) * * @note If connection state data is allocated as a result of this callback * being invoked, that data must be freed in the destroy callback. Just because * the accept callback returns 0, that does not guarantee the * create and closed callback functions will follow. * @note you can call qb_ipcs_connection_auth_set() within this function. */ typedef int32_t (*qb_ipcs_connection_accept_fn) (qb_ipcs_connection_t *c, uid_t uid, gid_t gid); /** * This is called after a new connection has been created. * * @note A client connection is not considered connected until * this callback is invoked. */ typedef void (*qb_ipcs_connection_created_fn) (qb_ipcs_connection_t *c); /** * This is called after a connection has been disconnected. * * @note This callback will only be invoked if the connection is * successfully created. * @note if you return anything but 0 this function will be * repeatedly called (until 0 is returned). * * With SHM connections libqb will briefly trap SIGBUS during the * disconnect process to guard against server crashes if the mapped * file is truncated. The signal will be restored afterwards. */ typedef int32_t (*qb_ipcs_connection_closed_fn) (qb_ipcs_connection_t *c); /** * This is called just before a connection is freed. */ typedef void (*qb_ipcs_connection_destroyed_fn) (qb_ipcs_connection_t *c); /** * This is the message processing calback. * It is called with the message data. */ typedef int32_t (*qb_ipcs_msg_process_fn) (qb_ipcs_connection_t *c, void *data, size_t size); struct qb_ipcs_service_handlers { qb_ipcs_connection_accept_fn connection_accept; qb_ipcs_connection_created_fn connection_created; qb_ipcs_msg_process_fn msg_process; qb_ipcs_connection_closed_fn connection_closed; qb_ipcs_connection_destroyed_fn connection_destroyed; }; /** * Create a new IPC server. * * @param name for clients to connect to. * @param service_id an integer to associate with the service * @param type transport type. * @param handlers callbacks. * @return the new service instance. */ qb_ipcs_service_t* qb_ipcs_create(const char *name, int32_t service_id, enum qb_ipc_type type, struct qb_ipcs_service_handlers *handlers); /** * Increase the reference counter on the service object. * * @param s service instance */ void qb_ipcs_ref(qb_ipcs_service_t *s); /** * Decrease the reference counter on the service object. * * @param s service instance */ void qb_ipcs_unref(qb_ipcs_service_t *s); /** * Set your poll callbacks. * * @param s service instance * @param handlers the handlers that you want ipcs to use. */ void qb_ipcs_poll_handlers_set(qb_ipcs_service_t* s, struct qb_ipcs_poll_handlers *handlers); /** * Associate a "user" pointer with this service. * * @param s service instance * @param context the pointer to associate with this service. * @see qb_ipcs_service_context_get() */ void qb_ipcs_service_context_set(qb_ipcs_service_t* s, void *context); /** * Get the context (set previously) * * @param s service instance * @return the context * @see qb_ipcs_service_context_set() */ void *qb_ipcs_service_context_get(qb_ipcs_service_t* s); /** * run the new IPC server. * @param s service instance * @return 0 == ok; -errno to indicate a failure. Service is destroyed on failure. */ int32_t qb_ipcs_run(qb_ipcs_service_t* s); /** * Destroy the IPC server. * * @param s service instance to destroy */ void qb_ipcs_destroy(qb_ipcs_service_t* s); /** * Limit the incoming request rate. * @param s service instance * @param rl the new rate */ void qb_ipcs_request_rate_limit(qb_ipcs_service_t* s, enum qb_ipcs_rate_limit rl); /** * Send a response to a incoming request. * * @param c connection instance * @param data the message to send * @param size the size of the message * @return size sent or -errno for errors * * @note the data must include a qb_ipc_response_header at * the top of the message. The client will read the size field * to determine how much to recv. */ ssize_t qb_ipcs_response_send(qb_ipcs_connection_t *c, const void *data, size_t size); /** * Send a response to a incoming request. * * @param c connection instance * @param iov the iovec struct that points to the message to send * @param iov_len the number of iovecs. * @return size sent or -errno for errors * * @note the iov[0] must be a qb_ipc_response_header. The client will * read the size field to determine how much to recv. * * @note When send returns -EMSGSIZE, this means the msg is too * large and will never succeed. To determine the max msg size * a client can be sent, use qb_ipcs_connection_get_buffer_size() */ ssize_t qb_ipcs_response_sendv(qb_ipcs_connection_t *c, const struct iovec * iov, size_t iov_len); /** * Send an asynchronous event message to the client. * * @param c connection instance * @param data the message to send * @param size the size of the message * @return size sent or -errno for errors * * @note the data must include a qb_ipc_response_header at * the top of the message. The client will read the size field * to determine how much to recv. * * @note When send returns -EMSGSIZE, this means the msg is too * large and will never succeed. To determine the max msg size * a client can be sent, use qb_ipcs_connection_get_buffer_size() */ ssize_t qb_ipcs_event_send(qb_ipcs_connection_t *c, const void *data, size_t size); /** * Send an asynchronous event message to the client. * * @param c connection instance * @param iov the iovec struct that points to the message to send * @param iov_len the number of iovecs. * @return size sent or -errno for errors * * @note the iov[0] must be a qb_ipc_response_header. The client will * read the size field to determine how much to recv. * * @note When send returns -EMSGSIZE, this means the msg is too * large and will never succeed. To determine the max msg size * a client can be sent, use qb_ipcs_connection_get_buffer_size() */ ssize_t qb_ipcs_event_sendv(qb_ipcs_connection_t *c, const struct iovec * iov, size_t iov_len); /** * Increment the connection's reference counter. * * @param c connection instance */ void qb_ipcs_connection_ref(qb_ipcs_connection_t *c); /** * Decrement the connection's reference counter. * * @param c connection instance */ void qb_ipcs_connection_unref(qb_ipcs_connection_t *c); /** * Disconnect from this client. * * @param c connection instance */ void qb_ipcs_disconnect(qb_ipcs_connection_t *c); /** * Get the service id related to this connection's service. * (as passed into qb_ipcs_create() * * @return service id. */ int32_t qb_ipcs_service_id_get(qb_ipcs_connection_t *c); /** * Associate a "user" pointer with this connection. * * @param context the point to associate with this connection. * @param c connection instance * @see qb_ipcs_context_get() */ void qb_ipcs_context_set(qb_ipcs_connection_t *c, void *context); /** * Get the context (set previously) * * @param c connection instance * @return the context * @see qb_ipcs_context_set() */ void *qb_ipcs_context_get(qb_ipcs_connection_t *c); /** * Get the context previously set on the service backing this connection * * @param c connection instance * @return the context * @see qb_ipcs_service_context_set */ void *qb_ipcs_connection_service_context_get(qb_ipcs_connection_t *c); /** * Get the connection statistics. * * @deprecated from v0.13.0 onwards, use qb_ipcs_connection_stats_get_2 * @param stats (out) the statistics structure * @param clear_after_read clear stats after copying them into stats * @param c connection instance * @return 0 == ok; -errno to indicate a failure */ int32_t qb_ipcs_connection_stats_get(qb_ipcs_connection_t *c, struct qb_ipcs_connection_stats* stats, int32_t clear_after_read); /** * Get (and allocate) the connection statistics. * * @param clear_after_read clear stats after copying them into stats * @param c connection instance * @retval NULL if no memory or invalid connection * @retval allocated statistics structure (user must free it). */ struct qb_ipcs_connection_stats_2* qb_ipcs_connection_stats_get_2(qb_ipcs_connection_t *c, int32_t clear_after_read); /** * Get the service statistics. * * @param stats (out) the statistics structure * @param clear_after_read clear stats after copying them into stats * @param pt service instance * @return 0 == ok; -errno to indicate a failure */ int32_t qb_ipcs_stats_get(qb_ipcs_service_t* pt, struct qb_ipcs_stats* stats, int32_t clear_after_read); /** * Get the first connection. * * @note call qb_ipcs_connection_unref() after using the connection. * * @param pt service instance * @return first connection */ qb_ipcs_connection_t * qb_ipcs_connection_first_get(qb_ipcs_service_t* pt); /** * Get the next connection. * * @note call qb_ipcs_connection_unref() after using the connection. * * @param pt service instance * @param current current connection * @return next connection */ qb_ipcs_connection_t * qb_ipcs_connection_next_get(qb_ipcs_service_t* pt, qb_ipcs_connection_t *current); /** * Set the permissions on and shared memory files so that both processes can * read and write to them. * * @param conn connection instance * @param uid the user id to set. * @param gid the group id to set. * @param mode the mode to set. * * @see chmod() chown() * @note this must be called within the qb_ipcs_connection_accept_fn() * callback. */ void qb_ipcs_connection_auth_set(qb_ipcs_connection_t *conn, uid_t uid, gid_t gid, mode_t mode); /** * Retrieve the connection ipc buffer size. This reflects the * largest size msg that can be sent or received. * * @param conn connection instance * @return msg size in bytes, negative value on error. */ int32_t qb_ipcs_connection_get_buffer_size(qb_ipcs_connection_t *conn); /** * Enforce the max buffer size clients must use from the server side. * * @note Setting this will force client connections to use at least * max_buf_size bytes as their buffer size. If this value is not set * on the server, the clients enforce their own buffer sizes. * * @param s ipc server instance * @param max_buf_size represented in bytes */ void qb_ipcs_enforce_buffer_size(qb_ipcs_service_t *s, uint32_t max_buf_size); /* *INDENT-OFF* */ #ifdef __cplusplus } #endif /* *INDENT-ON* */ #endif /* QB_IPCS_H_DEFINED */ diff --git a/include/qb/qblist.h b/include/qb/qblist.h index 7e04efa..da1b760 100644 --- a/include/qb/qblist.h +++ b/include/qb/qblist.h @@ -1,315 +1,315 @@ /* - * Copyright (C) 2006-2010, 2009 Red Hat, Inc. + * Copyright (C) 2010-2020 Red Hat, Inc. * * Author: Steven Dake * * This file is part of libqb. * * libqb 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. * * libqb 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 libqb. If not, see . */ #ifndef QB_LIST_H_DEFINED #define QB_LIST_H_DEFINED /* *INDENT-OFF* */ #ifdef __cplusplus extern "C" { #endif /* *INDENT-ON* */ #include #include #include /** * @file qblist.h * This is a kernel style list implementation. * * @author Steven Dake */ struct qb_list_head { struct qb_list_head *next; struct qb_list_head *prev; }; /** * @def QB_LIST_DECLARE() * Declare and initialize a list head. */ #define QB_LIST_DECLARE(name) \ struct qb_list_head name = { &(name), &(name) } #define QB_INIT_LIST_HEAD(ptr) do { \ (ptr)->next = (ptr); (ptr)->prev = (ptr); \ } while (0) /** * Initialize the list entry. * * Points next and prev pointers to head. * @param head pointer to the list head */ static inline void qb_list_init(struct qb_list_head *head) { head->next = head; head->prev = head; } /** * Add this element to the list. * * @param element the new element to insert. * @param head pointer to the list head */ static inline void qb_list_add(struct qb_list_head *element, struct qb_list_head *head) { head->next->prev = element; element->next = head->next; element->prev = head; head->next = element; } /** * Add to the list (but at the end of the list). * * @param element pointer to the element to add * @param head pointer to the list head * @see qb_list_add() */ static inline void qb_list_add_tail(struct qb_list_head *element, struct qb_list_head *head) { head->prev->next = element; element->next = head; element->prev = head->prev; head->prev = element; } /** * Delete an entry from the list. * * @param _remove the list item to remove */ static inline void qb_list_del(struct qb_list_head *_remove) { _remove->next->prev = _remove->prev; _remove->prev->next = _remove->next; } /** * Replace old entry by new one * @param old_one: the element to be replaced * @param new_one: the new element to insert */ static inline void qb_list_replace(struct qb_list_head *old_one, struct qb_list_head *new_one) { new_one->next = old_one->next; new_one->next->prev = new_one; new_one->prev = old_one->prev; new_one->prev->next = new_one; } /** * Tests whether list is the last entry in list head * @param list: the entry to test * @param head: the head of the list * @return boolean true/false */ static inline int qb_list_is_last(const struct qb_list_head *list, const struct qb_list_head *head) { return list->next == head; } /** * A quick test to see if the list is empty (pointing to it's self). * @param head pointer to the list head * @return boolean true/false */ static inline int32_t qb_list_empty(const struct qb_list_head *head) { return head->next == head; } /** * Join two lists. * @param list the new list to add. * @param head the place to add it in the first list. * * @note The "list" is reinitialised */ static inline void qb_list_splice(struct qb_list_head *list, struct qb_list_head *head) { struct qb_list_head *first = list->next; struct qb_list_head *last = list->prev; struct qb_list_head *at = head->next; if (!qb_list_empty(list)) { first->prev = head; head->next = first; last->next = at; at->prev = last; } } /** * Join two lists, each list being a queue * @param list: the new list to add. * @param head: the place to add it in the first list. */ static inline void qb_list_splice_tail(struct qb_list_head *list, struct qb_list_head *head) { struct qb_list_head *first = list->next; struct qb_list_head *last = list->prev; struct qb_list_head *at = head; if (!qb_list_empty(list)) { first->prev = head->prev; head->prev->next = first; last->next = at; at->prev = last; } } /** * Get the struct for this entry * @param ptr: the &struct list_head pointer. * @param type: the type of the struct this is embedded in. * @param member: the name of the list_struct within the struct. */ #define qb_list_entry(ptr,type,member) ({ \ ((type *)((char*)ptr - offsetof(type, member))); }) /** * Get the first element from a list * @param ptr: the &struct list_head pointer. * @param type: the type of the struct this is embedded in. * @param member: the name of the list_struct within the struct. */ #define qb_list_first_entry(ptr, type, member) \ qb_list_entry((ptr)->next, type, member) /** * Iterate over a list * @param pos: the &struct list_head to use as a loop counter. * @param head: the head for your list. */ #define qb_list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); pos = pos->next) /** * Iterate over a list backwards * @param pos: the &struct list_head to use as a loop counter. * @param head: the head for your list. */ #define qb_list_for_each_reverse(pos, head) \ for (pos = (head)->prev; pos != (head); pos = pos->prev) /** * Iterate over a list safe against removal of list entry * @param pos: the &struct list_head to use as a loop counter. * @param n: another &struct list_head to use as temporary storage * @param head: the head for your list. */ #define qb_list_for_each_safe(pos, n, head) \ for (pos = (head)->next, n = pos->next; pos != (head); \ pos = n, n = pos->next) /** * Iterate over list of given type * @param pos: the type * to use as a loop counter. * @param head: the head for your list. * @param member: the name of the list_struct within the struct. */ #define qb_list_for_each_entry(pos, head, member) \ for (pos = qb_list_entry((head)->next, typeof(*pos), member); \ &pos->member != (head); \ pos = qb_list_entry(pos->member.next, typeof(*pos), member)) /** * Iterate backwards over list of given type. * @param pos: the type to use as a loop counter. * @param head: the head for your list. * @param member: the name of the list_struct within the struct. */ #define qb_list_for_each_entry_reverse(pos, head, member) \ for (pos = qb_list_entry((head)->prev, typeof(*pos), member); \ &pos->member != (head); \ pos = qb_list_entry(pos->member.prev, typeof(*pos), member)) /** * Iterate over list of given type safe against removal of list entry * @param pos: the type * to use as a loop cursor. * @param n: another type * to use as temporary storage * @param head: the head for your list. * @param member: the name of the list_struct within the struct. */ #define qb_list_for_each_entry_safe(pos, n, head, member) \ for (pos = qb_list_entry((head)->next, typeof(*pos), member), \ n = qb_list_entry(pos->member.next, typeof(*pos), member); \ &pos->member != (head); \ pos = n, n = qb_list_entry(n->member.next, typeof(*n), member)) /** * Iterate backwards over list safe against removal * @param pos: the type * to use as a loop cursor. * @param n: another type * to use as temporary storage * @param head: the head for your list. * @param member: the name of the list_struct within the struct. */ #define qb_list_for_each_entry_safe_reverse(pos, n, head, member) \ for (pos = qb_list_entry((head)->prev, typeof(*pos), member), \ n = qb_list_entry(pos->member.prev, typeof(*pos), member); \ &pos->member != (head); \ pos = n, n = qb_list_entry(n->member.prev, typeof(*n), member)) /** * Iterate over list of given type from the current point * @param pos: the type * to use as a loop cursor. * @param head: the head for your list. * @param member: the name of the list_struct within the struct. */ #define qb_list_for_each_entry_from(pos, head, member) \ for (; &pos->member != (head); \ pos = qb_list_entry(pos->member.next, typeof(*pos), member)) /** * Count the number of items in the list. * @param head: the head for your list. * @return length of the list. */ static inline int32_t qb_list_length(struct qb_list_head *head) { struct qb_list_head *item; int32_t length = 0; qb_list_for_each(item, head) length++; return length; } /* *INDENT-OFF* */ #ifdef __cplusplus } #endif /* __cplusplus */ /* *INDENT-ON* */ #endif /* QB_LIST_H_DEFINED */ diff --git a/include/qb/qbloop.h b/include/qb/qbloop.h index c9b1993..73d17fd 100644 --- a/include/qb/qbloop.h +++ b/include/qb/qbloop.h @@ -1,321 +1,321 @@ /* - * Copyright (C) 2010 Red Hat, Inc. + * Copyright (C) 2010-2020 Red Hat, Inc. * * Author: Angus Salkeld * * This file is part of libqb. * * libqb 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. * * libqb 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 libqb. If not, see . */ #ifndef QB_LOOP_H_DEFINED #define QB_LOOP_H_DEFINED /* *INDENT-OFF* */ #ifdef __cplusplus extern "C" { #endif /* *INDENT-ON* */ #include #include #include /* make POLLIN etc. readily available */ /** * @file qbloop.h * * Main loop manages timers, jobs and polling sockets. * * Only a weaker sense of priorities is implemented, alluding to distinct * set of pros and cons compared to the stronger, strict approach to them * as widely applied in this problem space (since the latter gives the * application more control as the effect of the former can still be * achieved with some reductions, whereas it is not straightforward the * other way around; cf. static priority task scheduling vs. relative * fine-tuning within a single priority domain with nice(2)): * * + implicit mitigation for deadlock-prone priority arrangements * * - less predictable (proportional probability based, we can talk * about an advisory effect of the priorities) responses to the arrival * of the high-ranked events (i.e. in the process of the picking the next * event to handle from the priority queue when at least two different * priorities are eligible at the moment) * * One practical application for this module of libqb is in combination with * IPC servers based on qbipcs.h published one (the #qb_ipcs_poll_handlers * structure maps fittingly to the control functions published here). * * @example tcpserver.c */ /** * Priorites for jobs, timers & poll */ enum qb_loop_priority { QB_LOOP_LOW = 0, QB_LOOP_MED = 1, QB_LOOP_HIGH = 2, }; /** * An opaque data type representing the main loop. */ typedef struct qb_loop qb_loop_t; typedef uint64_t qb_loop_timer_handle; typedef void *qb_loop_signal_handle; typedef int32_t (*qb_loop_poll_dispatch_fn) (int32_t fd, int32_t revents, void *data); typedef void (*qb_loop_job_dispatch_fn)(void *data); typedef void (*qb_loop_timer_dispatch_fn)(void *data); typedef int32_t (*qb_loop_signal_dispatch_fn)(int32_t rsignal, void *data); typedef void (*qb_loop_poll_low_fds_event_fn) (int32_t not_enough, int32_t fds_available); /** * Create a new main loop. * * @return loop instance. */ qb_loop_t * qb_loop_create(void); /** * */ void qb_loop_destroy(struct qb_loop * l); /** * Stop the main loop. * @param l pointer to the loop instance */ void qb_loop_stop(qb_loop_t *l); /** * Run the main loop. * * @param l pointer to the loop instance */ void qb_loop_run(qb_loop_t *l); /** * Add a job to the mainloop. * * This is run in the next cycle of the loop. * @note it is a one-shot job. * * @param l pointer to the loop instance * @param p the priority * @param data user data passed into the dispatch function * @param dispatch_fn callback function * @return status (0 == ok, -errno == failure) */ int32_t qb_loop_job_add(qb_loop_t *l, enum qb_loop_priority p, void *data, qb_loop_job_dispatch_fn dispatch_fn); /** * Delete a job from the mainloop. * * This will try to delete the job if it hasn't run yet. * * @note this will remove the first job that matches the * parameters (priority, data, dispatch_fn). * * @param l pointer to the loop instance * @param p the priority * @param data user data passed into the dispatch function * @param dispatch_fn callback function * @return status (0 == ok, -errno == failure) */ int32_t qb_loop_job_del(struct qb_loop *l, enum qb_loop_priority p, void *data, qb_loop_job_dispatch_fn dispatch_fn); /** * Add a timer to the mainloop. * @note it is a one-shot job. * * @param l pointer to the loop instance * @param p the priority * @param nsec_duration nano-secs in the future to run the dispatch. * @param data user data passed into the dispatch function * @param dispatch_fn callback function * @param timer_handle_out handle to delete the timer if needed. * @return status (0 == ok, -errno == failure) */ int32_t qb_loop_timer_add(qb_loop_t *l, enum qb_loop_priority p, uint64_t nsec_duration, void *data, qb_loop_timer_dispatch_fn dispatch_fn, qb_loop_timer_handle * timer_handle_out); /** * Delete a timer that is still outstanding. * * @param l pointer to the loop instance * @param th handle to delete the timer if needed. * @return status (0 == ok, -errno == failure) */ int32_t qb_loop_timer_del(qb_loop_t *l, qb_loop_timer_handle th); /** * Check to see if a timer that is still outstanding. * * @param l pointer to the loop instance * @param th handle to delete the timer if needed. * @retval QB_TRUE yes this timer is outstanding * @retval QB_FALSE this timer does not exist or has expired */ int32_t qb_loop_timer_is_running(qb_loop_t *l, qb_loop_timer_handle th); /** * Get the expiration time of the timer, as set when the timer was created * * @note if the timer has already expired it will return 0 * * @param l pointer to the loop instance * @param th timer handle. * @return nano seconds at which the timer will expire */ uint64_t qb_loop_timer_expire_time_get(struct qb_loop *l, qb_loop_timer_handle th); /** * Get the time remaining before the timer expires * * @note if the timer has already expired it will return 0 * * @param l pointer to the loop instance * @param th timer handle. * @return nano seconds remaining until the timer expires */ uint64_t qb_loop_timer_expire_time_remaining(struct qb_loop *l, qb_loop_timer_handle th); /** * Set a callback to receive events on file descriptors * getting low. * @param l pointer to the loop instance * @param fn callback function. * @return status (0 == ok, -errno == failure) */ int32_t qb_loop_poll_low_fds_event_set(qb_loop_t *l, qb_loop_poll_low_fds_event_fn fn); /** * Add a poll job to the mainloop. * @note it is a re-occurring job. * * @param l pointer to the loop instance * @param p the priority * @param fd file descriptor. * @param events (POLLIN|POLLOUT) etc .... * @param data user data passed into the dispatch function * @param dispatch_fn callback function * @return status (0 == ok, -errno == failure) */ int32_t qb_loop_poll_add(qb_loop_t *l, enum qb_loop_priority p, int32_t fd, int32_t events, void *data, qb_loop_poll_dispatch_fn dispatch_fn); /** * Modify a poll job. * * @param l pointer to the loop instance * @param p the priority * @param fd file descriptor. * @param events (POLLIN|POLLOUT) etc .... * @param data user data passed into the dispatch function * @param dispatch_fn callback function * @return status (0 == ok, -errno == failure) */ int32_t qb_loop_poll_mod(qb_loop_t *l, enum qb_loop_priority p, int32_t fd, int32_t events, void *data, qb_loop_poll_dispatch_fn dispatch_fn); /** * Delete a poll job. * * @param l pointer to the loop instance * @param fd file descriptor. * @return status (0 == ok, -errno == failure) */ int32_t qb_loop_poll_del(qb_loop_t *l, int32_t fd); /** * Add a signal job. * * Get a callback on this signal (not in the context of the signal). * * @param l pointer to the loop instance * @param p the priority * @param sig (SIGHUP or SIGINT) etc .... * @param data user data passed into the dispatch function * @param dispatch_fn callback function * @param handle (out) a reference to the signal job * @return status (0 == ok, -errno == failure) */ int32_t qb_loop_signal_add(qb_loop_t *l, enum qb_loop_priority p, int32_t sig, void *data, qb_loop_signal_dispatch_fn dispatch_fn, qb_loop_signal_handle *handle); /** * Modify the signal job * * @param l pointer to the loop instance * @param p the priority * @param sig (SIGHUP or SIGINT) etc .... * @param data user data passed into the dispatch function * @param dispatch_fn callback function * @param handle (in) a reference to the signal job * @return status (0 == ok, -errno == failure) */ int32_t qb_loop_signal_mod(qb_loop_t *l, enum qb_loop_priority p, int32_t sig, void *data, qb_loop_signal_dispatch_fn dispatch_fn, qb_loop_signal_handle handle); /** * Delete the signal job. * * @param l pointer to the loop instance * @param handle (in) a reference to the signal job * @return status (0 == ok, -errno == failure) */ int32_t qb_loop_signal_del(qb_loop_t *l, qb_loop_signal_handle handle); /* *INDENT-OFF* */ #ifdef __cplusplus } #endif /* __cplusplus */ /* *INDENT-ON* */ #endif /* QB_LOOP_H_DEFINED */ diff --git a/include/qb/qbmap.h b/include/qb/qbmap.h index 172c37d..f3a88cd 100644 --- a/include/qb/qbmap.h +++ b/include/qb/qbmap.h @@ -1,284 +1,284 @@ /* - * Copyright (C) 2011 Red Hat, Inc. + * Copyright (C) 2010-2020 Red Hat, Inc. * * Author: Angus Salkeld * * This file is part of libqb. * * libqb 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. * * libqb 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 libqb. If not, see . */ #ifndef QB_MAP_H_DEFINED #define QB_MAP_H_DEFINED #include #ifndef S_SPLINT_S #include #endif /* S_SPLINT_S */ /* *INDENT-OFF* */ #ifdef __cplusplus extern "C" { #endif /* *INDENT-ON* */ /** * @file qbmap.h * This provides a map interface to a Patricia trie, hashtable or skiplist. * * @par Ordering * The hashtable is NOT ordered, but ptrie and skiplist are. * * @par Iterating * Below is a simple example of how to iterate over a map. * @code * const char *p; * void *data; * qb_map_iter_t *it = qb_map_iter_create(m); * for (p = qb_map_iter_next(it, &data); p; p = qb_map_iter_next(it, &data)) { * printf("%s > %s\n", p, (char*) data); * } * qb_map_iter_free(it); * @endcode * * Deletion of items within the iterator is supported. But note do not * free the item memory in the iterator. If you need to free the data * items then register for a notifier and free the memory there. This * is required as the items are reference counted. * @code * qb_map_notify_add(m, NULL, my_map_free_handler, * QB_MAP_NOTIFY_FREE, NULL); * @endcode * * @par Notifications * These allow you to get callbacks when values are inserted/removed or * replaced. * @note hashtable only supports deletion and replacement notificatins. * There is also a special global callback for freeing deleted and replaced * values (QB_MAP_NOTIFY_FREE). * @see qb_map_notify_add() qb_map_notify_del_2() * * @par Prefix matching * The ptrie supports prefixes in the iterator: * * @code * it = qb_map_pref_iter_create(m, "aa"); * while ((p = qb_map_iter_next(it, &data)) != NULL) { * printf("%s > %s\n", p, (char*)data); * } * qb_map_iter_free(it); * @endcode * * The ptrie also supports prefixes in notifications: * (remember to pass QB_MAP_NOTIFY_RECURSIVE into the notify_add. * @code * qb_map_notify_add(m, "root", my_map_notification, * (QB_MAP_NOTIFY_INSERTED| * QB_MAP_NOTIFY_DELETED| * QB_MAP_NOTIFY_REPLACED| * QB_MAP_NOTIFY_RECURSIVE), * NULL); * * @endcode */ /** * This is an opaque data type representing an instance of a map. */ typedef struct qb_map qb_map_t; /** * This is an opaque data type representing an iterator instance. */ typedef struct qb_map_iter qb_map_iter_t; #define QB_MAP_NOTIFY_DELETED 1 #define QB_MAP_NOTIFY_REPLACED 2 #define QB_MAP_NOTIFY_INSERTED 4 #define QB_MAP_NOTIFY_RECURSIVE 8 #define QB_MAP_NOTIFY_FREE 16 typedef void (*qb_map_notify_fn)(uint32_t event, char* key, void* old_value, void* value, void* user_data); typedef int32_t (*qb_map_transverse_fn)(const char* key, void* value, void* user_data); /** * Create an unsorted map based on a hashtable. * * @param max_size maximum size of the hashtable * * @return the map instance */ qb_map_t* qb_hashtable_create(size_t max_size); /** * Create a sorted map using a skiplist. * * @return the map instance */ qb_map_t* qb_skiplist_create(void); /** * Create a sorted map using a Patricia trie or "Radix tree". * * @htmlonly * See the wikipedia Radix_tree * and Trie pages. * @endhtmlonly */ qb_map_t* qb_trie_create(void); /** * print out the nodes in the trie * * (for debug purposes) */ void qb_trie_dump(qb_map_t* m); /** * Add a notifier to the map. * * @param m the map instance * @param key the key (or prefix) to attach the notification to. * @param fn the callback * @param events the type of events to register for. * @param user_data a pointer to be passed into the callback * * @note QB_MAP_NOTIFY_INSERTED is only valid on tries. * @note you can use key prefixes with trie maps. * * @retval 0 success * @retval -errno failure */ int32_t qb_map_notify_add(qb_map_t* m, const char* key, qb_map_notify_fn fn, int32_t events, void *user_data); /** * Delete a notifier from the map. * * @note the key,fn and events must match those you added. * * @param m the map instance * @param key the key (or prefix) to attach the notification to. * @param fn the callback * @param events the type of events to register for. * * @retval 0 success * @retval -errno failure */ int32_t qb_map_notify_del(qb_map_t* m, const char* key, qb_map_notify_fn fn, int32_t events); /** * Delete a notifier from the map (including the userdata). * * @note the key, fn, events and userdata must match those you added. * * @param m the map instance * @param key the key (or prefix) to attach the notification to. * @param fn the callback * @param events the type of events to register for. * @param user_data a pointer to be passed into the callback * * @retval 0 success * @retval -errno failure */ int32_t qb_map_notify_del_2(qb_map_t* m, const char* key, qb_map_notify_fn fn, int32_t events, void *user_data); /** * Inserts a new key and value into a qb_map_t. * * If the key already exists in the qb_map_t, it gets replaced by the new key. */ void qb_map_put(qb_map_t *map, const char* key, const void* value); /** * Gets the value corresponding to the given key. * * @retval NULL (if the key does not exist) * @retval a pointer to the value */ void* qb_map_get(qb_map_t *map, const char* key); /** * Removes a key/value pair from a map. */ int32_t qb_map_rm(qb_map_t *map, const char* key); /** * Get the number of items in the map. */ size_t qb_map_count_get(qb_map_t *map); /** * Calls the given function for each of the key/value pairs in the map. * * The function is passed the key and value of each pair, and the given data * parameter. The map is traversed in sorted order. */ void qb_map_foreach(qb_map_t *map, qb_map_transverse_fn func, void* user_data); /** * Create an iterator */ qb_map_iter_t* qb_map_iter_create(qb_map_t *map); /** * Create a prefix iterator. * * This will iterate over all items with the given * prefix. * @note this is only supported by the trie. */ qb_map_iter_t* qb_map_pref_iter_create(qb_map_t *map, const char* prefix); /** * Get the next item * * @param i the iterator * @param value (out) the next item's value * * @retval the next key * @retval NULL - the end of the iteration */ const char* qb_map_iter_next(qb_map_iter_t* i, void** value); /** * free the iterator * * @param i the iterator */ void qb_map_iter_free(qb_map_iter_t* i); /** * Destroy the map, removes all the items from the map. */ void qb_map_destroy(qb_map_t *map); /* *INDENT-OFF* */ #ifdef __cplusplus } #endif /* *INDENT-ON* */ #endif /* QB_MAP_H_DEFINED */ diff --git a/include/qb/qbrb.h b/include/qb/qbrb.h index cde793d..b2915b6 100644 --- a/include/qb/qbrb.h +++ b/include/qb/qbrb.h @@ -1,304 +1,304 @@ /* - * Copyright (C) 2010 Red Hat, Inc. + * Copyright (C) 2010-2020 Red Hat, Inc. * * Author: Angus Salkeld * * This file is part of libqb. * * libqb 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. * * libqb 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 libqb. If not, see . */ #ifndef QB_RB_H_DEFINED #define QB_RB_H_DEFINED /* *INDENT-OFF* */ #ifdef __cplusplus extern "C" { #endif /* *INDENT-ON* */ #include #include /** * @file qbrb.h * This implements a ring buffer that works in "chunks", not bytes. * So you write/read a complete chunk or not at all. * There are two types of ring buffer: normal and overwrite. * Overwrite will reclaim the oldest chunks inorder to make way for new ones, * the normal version will refuse to write a new chunk if the ring buffer * is full. * * This implementation is capable of working across processes, but one process * must only write and the other process read. * * The read process will do the following: * @code * rb = qb_rb_open("test2", 2000, QB_RB_FLAG_SHARED_PROCESS|QB_RB_FLAG_CREATE); * for (i = 0; i < 200; i++) { * try_read_again: * l = qb_rb_chunk_read(rb, (void *)out, 32, 1000); * if (l < 0) { * goto try_read_again; * } * } * ... * qb_rb_close(rb); * * @endcode * * The write process will do the following: * @code * rb = qb_rb_open("test2", 2000, QB_RB_FLAG_SHARED_PROCESS); * for (i = 0; i < 200; i++) { * try_write_again: * l = qb_rb_chunk_write(rb, &v, sizeof(v)); * if (l < sizeof(v)) { * goto try_write_again; * } * } * ... * qb_rb_close(rb); * @endcode * * @author Angus Salkeld */ /** * Create a ring buffer (rather than open and existing one). * @see qb_rb_open() */ #define QB_RB_FLAG_CREATE 0x01 /** * New calls to qb_rb_chunk_write() will call qb_rb_chunk_reclaim() * if there is not enough space. * If this is not set then new writes will be refused. * @see qb_rb_open() */ #define QB_RB_FLAG_OVERWRITE 0x02 /** * The ringbuffer will be shared between pthreads not processes. * This effects the type of locks/semaphores that are used. * @see qb_rb_open() */ #define QB_RB_FLAG_SHARED_THREAD 0x04 /** * The ringbuffer will be shared between processes. * This effects the type of locks/semaphores that are used. * @see qb_rb_open() */ #define QB_RB_FLAG_SHARED_PROCESS 0x08 /** * Don't use semaphores, only atomic ops. * This mean that the timeout passed into qb_rb_chunk_read() * will be ignored. */ #define QB_RB_FLAG_NO_SEMAPHORE 0x10 struct qb_ringbuffer_s; typedef struct qb_ringbuffer_s qb_ringbuffer_t; /** * Create the ring buffer with the given type. * * This creates allocates a ring buffer in shared memory. * * @param name the unique name of this ringbuffer. * @param size the requested size. * @param flags or'ed flags * @param shared_user_data_size size for a shared data area. * @note the actual size will be rounded up to the next page size. * @return a new ring buffer or NULL if there was a problem. * @see QB_RB_FLAG_CREATE, QB_RB_FLAG_OVERWRITE, QB_RB_FLAG_SHARED_THREAD, QB_RB_FLAG_SHARED_PROCESS */ qb_ringbuffer_t *qb_rb_open(const char *name, size_t size, uint32_t flags, size_t shared_user_data_size); /** * Dereference the ringbuffer and, if we are the last user, destroy it. * * All files, mmaped memory, semaphores and locks will be destroyed. * * @param rb ringbuffer instance */ void qb_rb_close(qb_ringbuffer_t * rb); /** * Get the name of the ringbuffer. * @param rb ringbuffer instance * @return name. */ char *qb_rb_name_get(qb_ringbuffer_t * rb); /** * Get a point to user shared data area. * * @note this is of size "shared_user_data_size" passed into qb_rb_open() * * @param rb ringbuffer instance * @return pointer to shared data. */ void *qb_rb_shared_user_data_get(qb_ringbuffer_t * rb); /** * Write a chunk to the ring buffer. * * This simply calls qb_rb_chunk_alloc() and then * qb_rb_chunk_commit(). * * @param rb ringbuffer instance * @param data (in) the data to write * @param len (in) the size of the chunk. * @return the amount of bytes actually buffered (either len or -1). * * @see qb_rb_chunk_alloc() * @see qb_rb_chunk_commit() */ ssize_t qb_rb_chunk_write(qb_ringbuffer_t * rb, const void *data, size_t len); /** * Allocate space for a chunk of the given size. * * If type == QB_RB_FLAG_OVERWRITE and NULL is returned, memory corruption of * the memory file has occurred. The ringbuffer should be destroyed. * If type == QB_RB_NORMAL then when there is not enough space it will * return NULL. * * @param rb ringbuffer instance * @param len (in) the size to allocate. * @return pointer to chunk to write to, or NULL (if no space). * * @see qb_rb_chunk_alloc() */ void *qb_rb_chunk_alloc(qb_ringbuffer_t * rb, size_t len); /** * Finalize the chunk. * @param rb ringbuffer instance * @param len (in) the size of the chunk. */ int32_t qb_rb_chunk_commit(qb_ringbuffer_t * rb, size_t len); /** * Read (without reclaiming) the last chunk. * * This function is a way of accessing the next chunk without a memcpy(). * You can read the chunk data in place. * * @note This function will not "pop" the chunk, you will need to call * qb_rb_chunk_reclaim(). * @param rb ringbuffer instance * @param data_out (out) a pointer to the next chunk to read (not copied). * @param ms_timeout (in) time to wait for new data. * * @return the size of the chunk (0 if buffer empty). */ ssize_t qb_rb_chunk_peek(qb_ringbuffer_t * rb, void **data_out, int32_t ms_timeout); /** * Reclaim the oldest chunk. * You will need to call this if using qb_rb_chunk_peek(). * @param rb ringbuffer instance */ void qb_rb_chunk_reclaim(qb_ringbuffer_t * rb); /** * Read the oldest chunk into data_out. * * This is the same as qb_rb_chunk_peek() memcpy() and qb_rb_chunk_reclaim(). * * @param rb ringbuffer instance * @param data_out (in/out) the chunk will be memcpy'ed into this. * @param len (in) the size of data_out. * @param ms_timeout the amount od time to wait for new data. * @return the size of the chunk, or error. */ ssize_t qb_rb_chunk_read(qb_ringbuffer_t * rb, void *data_out, size_t len, int32_t ms_timeout); /** * Get the reference count. * * @param rb ringbuffer instance * @return the number of references */ int32_t qb_rb_refcount_get(qb_ringbuffer_t * rb); /** * The amount of free space in the ring buffer. * * @note Some of this space will be consumed by the chunk headers. * @param rb ringbuffer instance */ ssize_t qb_rb_space_free(qb_ringbuffer_t * rb); /** * The total amount of data in the buffer. * * @note This includes the chunk headers (8 bytes per chunk). * @param rb ringbuffer instance */ ssize_t qb_rb_space_used(qb_ringbuffer_t * rb); /** * The total number of chunks in the buffer. * * @param rb ringbuffer instance */ ssize_t qb_rb_chunks_used(qb_ringbuffer_t * rb); /** * Write the contents of the Ring Buffer to file. * @param fd open file to write the ringbuffer data to. * @param rb ringbuffer instance * @see qb_rb_create_from_file() */ ssize_t qb_rb_write_to_file(qb_ringbuffer_t * rb, int32_t fd); /** * Load the saved ring buffer from file into tempory memory. * @param fd file with saved ringbuffer data. * @param flags same flags as passed into qb_rb_open() * @return new ringbuffer instance * @see qb_rb_write_to_file() */ qb_ringbuffer_t *qb_rb_create_from_file(int32_t fd, uint32_t flags); /** * Like 'chown', it changes the owner and group of the ringbuffer's * resources. * @param owner uid of the owner to change to * @param group gid of the group to change to * @param rb ringbuffer instance * @return status (0 = ok, -errno for error) */ int32_t qb_rb_chown(qb_ringbuffer_t * rb, uid_t owner, gid_t group); /** * Like 'chmod', it changes the mode of the ringbuffer's resources. * @param mode mode to change to * @param rb ringbuffer instance * @retval 0 == ok * @retval -errno for error */ int32_t qb_rb_chmod(qb_ringbuffer_t * rb, mode_t mode); /* *INDENT-OFF* */ #ifdef __cplusplus } #endif /* __cplusplus */ /* *INDENT-ON* */ #endif /* QB_RB_H_DEFINED */ diff --git a/include/qb/qbutil.h b/include/qb/qbutil.h index b02ce8d..59bf145 100644 --- a/include/qb/qbutil.h +++ b/include/qb/qbutil.h @@ -1,309 +1,309 @@ /* - * Copyright (C) 2010 Red Hat, Inc. + * Copyright (C) 2010-2020 Red Hat, Inc. * * Author: Angus Salkeld * * libqb 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. * * libqb 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 libqb. If not, see . */ #ifndef QB_UTIL_H_DEFINED #define QB_UTIL_H_DEFINED /* *INDENT-OFF* */ #ifdef __cplusplus extern "C" { #endif /* *INDENT-ON* */ #include #include #ifndef S_SPLINT_S #include #endif /* S_SPLINT_S */ #include /** * @file qbutil.h * These are some convience functions used throughout libqb. * * @author Angus Salkeld * * @par Locking * - qb_thread_lock_create() * - qb_thread_lock() * - qb_thread_trylock() * - qb_thread_unlock() * - qb_thread_lock_destroy() * * @par Time functions * - qb_timespec_add_ms() * - qb_util_nano_current_get() * - qb_util_nano_monotonic_hz() * - qb_util_nano_from_epoch_get() * - qb_util_timespec_from_epoch_get() * * @par Basic Stopwatch * @code * uint64_t elapsed1; * uint64_t elapsed2; * qb_util_stopwatch_t *sw = qb_util_stopwatch_create(); * * qb_util_stopwatch_start(sw); * * usleep(sometime); * qb_util_stopwatch_stop(sw); * elapsed1 = qb_util_stopwatch_us_elapsed_get(sw); * * usleep(somemoretime); * qb_util_stopwatch_stop(sw); * elapsed2 = qb_util_stopwatch_us_elapsed_get(sw); * * qb_util_stopwatch_free(sw); * @endcode * * @par Stopwatch with splits * Setup a stopwatch with space for 3 splits. * * @code * uint64_t split; * qb_util_stopwatch_t *sw = qb_util_stopwatch_create(); * * qb_util_stopwatch_split_ctl(sw, 3, 0); * qb_util_stopwatch_start(sw); * * usleep(sometime); * qb_util_stopwatch_split(sw); * * usleep(somemoretime); * qb_util_stopwatch_split(sw); * * usleep(somemoretime); * qb_util_stopwatch_split(sw); * * idx = qb_util_stopwatch_split_last(sw); * do { * split = qb_util_stopwatch_time_split_get(sw, idx, idx); * qb_log(LOG_INFO, "split %d is %"PRIu64"", last, split); * idx--; * } while (split > 0); * * split = qb_util_stopwatch_time_split_get(sw, 2, 1); * qb_log(LOG_INFO, "time between second and third split is %"PRIu64"", split); * * qb_util_stopwatch_free(sw); * @endcode * */ /** * @typedef qb_thread_lock_type_t * QB_THREAD_LOCK_SHORT is a short term lock (spinlock if available on your system) * QB_THREAD_LOCK_LONG is a mutex */ typedef enum { QB_THREAD_LOCK_SHORT, QB_THREAD_LOCK_LONG, } qb_thread_lock_type_t; struct qb_thread_lock_s; typedef struct qb_thread_lock_s qb_thread_lock_t; /** * Create a new lock of the given type. * @param type QB_THREAD_LOCK_SHORT == spinlock (where available, else mutex) * QB_THREAD_LOCK_LONG == mutex * @return pointer to qb_thread_lock_type_t or NULL on error. */ qb_thread_lock_t *qb_thread_lock_create(qb_thread_lock_type_t type); /** * Calls either pthread_mutex_lock() or pthread_spin_lock(). */ int32_t qb_thread_lock(qb_thread_lock_t * tl); /** * Calls either pthread_mutex_trylock() or pthread_spin_trylock(). */ int32_t qb_thread_trylock(qb_thread_lock_t * tl); /** * Calls either pthread_mutex_unlock() or pthread_spin_unlock. */ int32_t qb_thread_unlock(qb_thread_lock_t * tl); /** * Calls either pthread_mutex_destro() or pthread_spin_destroy(). */ int32_t qb_thread_lock_destroy(qb_thread_lock_t * tl); typedef void (*qb_util_log_fn_t) (const char *file_name, int32_t file_line, int32_t severity, const char *msg); /** * Use this function to output libqb internal log message as you wish. */ void qb_util_set_log_function(qb_util_log_fn_t fn) QB_GNUC_DEPRECATED; /** * Add milliseconds onto the timespec. * @param ts the ts to add to * @param ms the amount of milliseconds to increment ts */ void qb_timespec_add_ms(struct timespec *ts, int32_t ms); /** * Get the current number of nano secounds produced * by the systems incrementing clock (CLOCK_MONOTOMIC * if available). */ uint64_t qb_util_nano_current_get(void); /** * Get the frequence of the clock used in * qb_util_nano_current_get(). */ uint64_t qb_util_nano_monotonic_hz(void); /** * Get the time in nano seconds since epoch. */ uint64_t qb_util_nano_from_epoch_get(void); /** * Get the time in timespec since epoch. * @param ts (out) the timespec * @return status (0 == ok, -errno on error) */ void qb_util_timespec_from_epoch_get(struct timespec *ts); /** * strerror_r replacement. */ char *qb_strerror_r(int errnum, char *buf, size_t buflen); typedef struct qb_util_stopwatch qb_util_stopwatch_t; #define QB_UTIL_SW_OVERWRITE 0x01 /** * Create a Stopwatch (to time operations) */ qb_util_stopwatch_t * qb_util_stopwatch_create(void); /** * Free the stopwatch */ void qb_util_stopwatch_free(qb_util_stopwatch_t *sw); /** * Start the stopwatch * * This also acts as a reset. Essentially it sets the * starting time and clears the splits. */ void qb_util_stopwatch_start(qb_util_stopwatch_t *sw); /** * Stop the stopwatch * * This just allows you to get the elapsed time. So * you can call this multiple times. Do not call qb_util_stopwatch_start() * unless you want to reset the stopwatch. */ void qb_util_stopwatch_stop(qb_util_stopwatch_t *sw); /** * Get the elapsed time in micro seconds. * * (it must have been started and stopped). */ uint64_t qb_util_stopwatch_us_elapsed_get(qb_util_stopwatch_t *sw); /** * Get the elapsed time in seconds. * * (it must have been started and stopped). */ float qb_util_stopwatch_sec_elapsed_get(qb_util_stopwatch_t *sw); /** * * @param sw the stopwatch * @param max_splits maximum number of time splits * @param options (0 or QB_UTIL_SW_OVERWRITE ) * @retval 0 on success * @retval -errno on failure */ int32_t qb_util_stopwatch_split_ctl(qb_util_stopwatch_t *sw, uint32_t max_splits, uint32_t options); /** * Create a new time split (or lap time) * * @param sw the stopwatch * @retval the relative split time in micro seconds * @retval 0 if no more splits available */ uint64_t qb_util_stopwatch_split(qb_util_stopwatch_t *sw); /** * Get the last split index to be used by * qb_util_stopwatch_time_split_get() * * @note this is zero based * * @param sw the stopwatch * @return the last entry index */ uint32_t qb_util_stopwatch_split_last(qb_util_stopwatch_t *sw); /** * Read the time split (in us) from "receint" to "older". * * If older == receint then the cumulated split will be * returned (from the stopwatch start). * * @param sw the stopwatch * @param receint split * @param older split * @retval the split time in micro seconds * @retval 0 if not a valid split */ uint64_t qb_util_stopwatch_time_split_get(qb_util_stopwatch_t *sw, uint32_t receint, uint32_t older); /** Structured library versioning info */ extern const struct qb_version { uint8_t major; /**< Major component */ uint8_t minor; /**< Minor component */ uint8_t micro; /**< Micro component */ const char *rest; /**< Rest (pertaining the mid-release-point) */ } qb_ver; /** Complete library versioning info as a string */ extern const char *const qb_ver_str; /* *INDENT-OFF* */ #ifdef __cplusplus } #endif /* __cplusplus */ /* *INDENT-ON* */ #endif /* QB_UTIL_H_DEFINED */ diff --git a/m4/ax_prog_date.m4 b/m4/ax_prog_date.m4 new file mode 100644 index 0000000..c85f0f2 --- /dev/null +++ b/m4/ax_prog_date.m4 @@ -0,0 +1,137 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_prog_date.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PROG_DATE() +# +# DESCRIPTION +# +# This macro tries to determine the type of the date (1) command and some +# of its non-standard capabilities. +# +# The type is determined as follow: +# +# * If the version string contains "GNU", then: +# - The variable ax_cv_prog_date_gnu is set to "yes". +# - The variable ax_cv_prog_date_type is set to "gnu". +# +# * If date supports the "-v 1d" option, then: +# - The variable ax_cv_prog_date_bsd is set to "yes". +# - The variable ax_cv_prog_date_type is set to "bsd". +# +# * If both previous checks fail, then: +# - The variable ax_cv_prog_date_type is set to "unknown". +# +# The following capabilities of GNU date are checked: +# +# * If date supports the --date arg option, then: +# - The variable ax_cv_prog_date_gnu_date is set to "yes". +# +# * If date supports the --utc arg option, then: +# - The variable ax_cv_prog_date_gnu_utc is set to "yes". +# +# The following capabilities of BSD date are checked: +# +# * If date supports the -v 1d option, then: +# - The variable ax_cv_prog_date_bsd_adjust is set to "yes". +# +# * If date supports the -r arg option, then: +# - The variable ax_cv_prog_date_bsd_date is set to "yes". +# +# All the aforementioned variables are set to "no" before a check is +# performed. +# +# LICENSE +# +# Copyright (c) 2017 Enrico M. Crisostomo +# +# 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 . +# +# 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 3 + +AC_DEFUN([AX_PROG_DATE], [dnl + AC_CACHE_CHECK([for GNU date], [ax_cv_prog_date_gnu], [ + ax_cv_prog_date_gnu=no + if date --version 2>/dev/null | head -1 | grep -q GNU + then + ax_cv_prog_date_gnu=yes + fi + ]) + AC_CACHE_CHECK([for BSD date], [ax_cv_prog_date_bsd], [ + ax_cv_prog_date_bsd=no + if date -v 1d > /dev/null 2>&1 + then + ax_cv_prog_date_bsd=yes + fi + ]) + AC_CACHE_CHECK([for date type], [ax_cv_prog_date_type], [ + ax_cv_prog_date_type=unknown + if test "x${ax_cv_prog_date_gnu}" = "xyes" + then + ax_cv_prog_date_type=gnu + elif test "x${ax_cv_prog_date_bsd}" = "xyes" + then + ax_cv_prog_date_type=bsd + fi + ]) + AS_VAR_IF([ax_cv_prog_date_gnu], [yes], [ + AC_CACHE_CHECK([whether GNU date supports --date], [ax_cv_prog_date_gnu_date], [ + ax_cv_prog_date_gnu_date=no + if date --date=@1512031231 > /dev/null 2>&1 + then + ax_cv_prog_date_gnu_date=yes + fi + ]) + AC_CACHE_CHECK([whether GNU date supports --utc], [ax_cv_prog_date_gnu_utc], [ + ax_cv_prog_date_gnu_utc=no + if date --utc > /dev/null 2>&1 + then + ax_cv_prog_date_gnu_utc=yes + fi + ]) + ]) + AS_VAR_IF([ax_cv_prog_date_bsd], [yes], [ + AC_CACHE_CHECK([whether BSD date supports -r], [ax_cv_prog_date_bsd_date], [ + ax_cv_prog_date_bsd_date=no + if date -r 1512031231 > /dev/null 2>&1 + then + ax_cv_prog_date_bsd_date=yes + fi + ]) + ]) + AS_VAR_IF([ax_cv_prog_date_bsd], [yes], [ + AC_CACHE_CHECK([whether BSD date supports -v], [ax_cv_prog_date_bsd_adjust], [ + ax_cv_prog_date_bsd_adjust=no + if date -v 1d > /dev/null 2>&1 + then + ax_cv_prog_date_bsd_adjust=yes + fi + ]) + ]) +])dnl AX_PROG_DATE