Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package libHX for openSUSE:Factory checked 
in at 2021-10-11 15:30:41
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/libHX (Old)
 and      /work/SRC/openSUSE:Factory/.libHX.new.2443 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "libHX"

Mon Oct 11 15:30:41 2021 rev:62 rq:923056 version:4.0.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/libHX/libHX.changes      2021-08-05 
20:48:16.311920671 +0200
+++ /work/SRC/openSUSE:Factory/.libHX.new.2443/libHX.changes    2021-10-11 
15:31:16.090811389 +0200
@@ -1,0 +2,10 @@
+Sun Oct  3 00:28:41 UTC 2021 - Jan Engelhardt <jeng...@inai.de>
+
+- Update to release 4.0.1
+  * lib: add ``HX_slurp_fd``, ``HX_slurp_file``
+  * proc: add ``HXproc_switch_user``
+  * proc: add ``HXproc_top_fd``
+  * socket: add ``HX_socket_from_env``
+  * opt: add ``HXOPT_KEEP_ARGV`` flag
+
+-------------------------------------------------------------------

Old:
----
  libHX-3.26.tar.asc
  libHX-3.26.tar.xz

New:
----
  libHX-4.0.1.tar.asc
  libHX-4.0.1.tar.xz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ libHX.spec ++++++
--- /var/tmp/diff_new_pack.vjIcvr/_old  2021-10-11 15:31:16.694812357 +0200
+++ /var/tmp/diff_new_pack.vjIcvr/_new  2021-10-11 15:31:16.698812364 +0200
@@ -18,7 +18,7 @@
 
 Name:           libHX
 %define lname   libHX32
-Version:        3.26
+Version:        4.0.1
 Release:        0
 Summary:        Collection of routines for C and C++ programming
 License:        LGPL-2.1-or-later
@@ -70,7 +70,7 @@
 pushd obj/
 %define _configure ../configure
 %configure --includedir="%_includedir/%name" --docdir="%_docdir/%name"
-make %{?_smp_mflags}
+%make_build
 popd
 
 %install
@@ -82,7 +82,7 @@
 find "$b/%_libdir" -type f -name "*.la" -delete
 
 %check
-make -C obj check %{?_smp_mflags}
+%make_build -C obj check
 
 %post   -n %lname -p /sbin/ldconfig
 %postun -n %lname -p /sbin/ldconfig

++++++ libHX-3.26.tar.xz -> libHX-4.0.1.tar.xz ++++++
++++ 8862 lines of diff (skipped)
++++    retrying with extended exclude list
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libHX-3.26/Makefile.am new/libHX-4.0.1/Makefile.am
--- old/libHX-3.26/Makefile.am  2021-08-04 00:20:23.000000000 +0200
+++ new/libHX-4.0.1/Makefile.am 2021-10-03 01:05:06.000000000 +0200
@@ -15,8 +15,8 @@
 .PHONY: tarball
 tarball:
 # do not use mkdir_p here.
-       mkdir ${tmpdir};
-       pushd ${top_srcdir} && git archive 
--prefix=${PACKAGE_NAME}-${PACKAGE_VERSION}/ HEAD | tar -C ${tmpdir} -x && popd;
+       mkdir ${tmpdir}
+       pushd ${top_srcdir} && git archive 
--prefix=${PACKAGE_NAME}-${PACKAGE_VERSION}/ HEAD | tar -C ${tmpdir} -x && popd
        pushd ${tmpdir}/${PACKAGE_NAME}-${PACKAGE_VERSION} && ./autogen.sh && 
popd
-       tar --use=${packer} -C ${tmpdir} -cf 
${PACKAGE_NAME}-${PACKAGE_VERSION}${packext} --owner=root --group=root 
${PACKAGE_NAME}-${PACKAGE_VERSION}/;
-       rm -Rf ${tmpdir};
+       tar --use=${packer} -C ${tmpdir} -cf 
${PACKAGE_NAME}-${PACKAGE_VERSION}${packext} --owner=root --group=root 
${PACKAGE_NAME}-${PACKAGE_VERSION}/
+       rm -Rf ${tmpdir}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libHX-3.26/build-aux/ar-lib new/libHX-4.0.1/build-aux/ar-lib
--- old/libHX-3.26/build-aux/ar-lib     2021-08-04 00:20:39.121549456 +0200
+++ new/libHX-4.0.1/build-aux/ar-lib    2021-10-03 02:30:14.941163473 +0200
@@ -2,9 +2,9 @@
 # Wrapper for Microsoft lib.exe
 
 me=ar-lib
-scriptversion=2012-03-01.08; # UTC
+scriptversion=2019-07-04.01; # UTC
 
-# Copyright (C) 2010-2017 Free Software Foundation, Inc.
+# Copyright (C) 2010-2020 Free Software Foundation, Inc.
 # Written by Peter Rosin <p...@lysator.liu.se>.
 #
 # This program is free software; you can redistribute it and/or modify
@@ -18,7 +18,7 @@
 # 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 <http://www.gnu.org/licenses/>.
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 # As a special exception to the GNU General Public License, if you
 # distribute this file as part of a program that contains a
@@ -53,7 +53,7 @@
          MINGW*)
            file_conv=mingw
            ;;
-         CYGWIN*)
+         CYGWIN* | MSYS*)
            file_conv=cygwin
            ;;
          *)
@@ -65,7 +65,7 @@
        mingw)
          file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
          ;;
-       cygwin)
+       cygwin | msys)
          file=`cygpath -m "$file" || echo "$file"`
          ;;
        wine)
@@ -224,10 +224,11 @@
       esac
     done
   else
-    $AR -NOLOGO -LIST "$archive" | sed -e 's/\\/\\\\/g' | while read member
-    do
-      $AR -NOLOGO -EXTRACT:"$member" "$archive" || exit $?
-    done
+    $AR -NOLOGO -LIST "$archive" | tr -d '\r' | sed -e 's/\\/\\\\/g' \
+      | while read member
+        do
+          $AR -NOLOGO -EXTRACT:"$member" "$archive" || exit $?
+        done
   fi
 
 elif test -n "$quick$replace"; then
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libHX-3.26/build-aux/compile new/libHX-4.0.1/build-aux/compile
--- old/libHX-3.26/build-aux/compile    2021-08-04 00:20:39.129547843 +0200
+++ new/libHX-4.0.1/build-aux/compile   2021-10-03 02:30:14.941163473 +0200
@@ -1,9 +1,9 @@
 #! /bin/sh
 # Wrapper for compilers which do not understand '-c -o'.
 
-scriptversion=2016-01-11.22; # UTC
+scriptversion=2018-03-07.03; # UTC
 
-# Copyright (C) 1999-2017 Free Software Foundation, Inc.
+# Copyright (C) 1999-2020 Free Software Foundation, Inc.
 # Written by Tom Tromey <tro...@cygnus.com>.
 #
 # This program is free software; you can redistribute it and/or modify
@@ -17,7 +17,7 @@
 # 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 <http://www.gnu.org/licenses/>.
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 # As a special exception to the GNU General Public License, if you
 # distribute this file as part of a program that contains a
@@ -53,7 +53,7 @@
          MINGW*)
            file_conv=mingw
            ;;
-         CYGWIN*)
+         CYGWIN* | MSYS*)
            file_conv=cygwin
            ;;
          *)
@@ -67,7 +67,7 @@
        mingw/*)
          file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
          ;;
-       cygwin/*)
+       cygwin/* | msys/*)
          file=`cygpath -m "$file" || echo "$file"`
          ;;
        wine/*)
@@ -340,7 +340,7 @@
 # Local Variables:
 # mode: shell-script
 # sh-indentation: 2
-# eval: (add-hook 'write-file-hooks 'time-stamp)
+# eval: (add-hook 'before-save-hook 'time-stamp)
 # time-stamp-start: "scriptversion="
 # time-stamp-format: "%:y-%02m-%02d.%02H"
 # time-stamp-time-zone: "UTC0"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libHX-3.26/build-aux/missing new/libHX-4.0.1/build-aux/missing
--- old/libHX-3.26/build-aux/missing    2021-08-04 00:20:39.145544615 +0200
+++ new/libHX-4.0.1/build-aux/missing   2021-10-03 02:30:14.953163507 +0200
@@ -1,9 +1,9 @@
 #! /bin/sh
 # Common wrapper for a few potentially missing GNU programs.
 
-scriptversion=2016-01-11.22; # UTC
+scriptversion=2018-03-07.03; # UTC
 
-# Copyright (C) 1996-2017 Free Software Foundation, Inc.
+# Copyright (C) 1996-2020 Free Software Foundation, Inc.
 # Originally written by Fran,cois Pinard <pin...@iro.umontreal.ca>, 1996.
 
 # This program is free software; you can redistribute it and/or modify
@@ -17,7 +17,7 @@
 # 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 <http://www.gnu.org/licenses/>.
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 # As a special exception to the GNU General Public License, if you
 # distribute this file as part of a program that contains a
@@ -101,9 +101,9 @@
   exit $st
 fi
 
-perl_URL=http://www.perl.org/
-flex_URL=http://flex.sourceforge.net/
-gnu_software_URL=http://www.gnu.org/software
+perl_URL=https://www.perl.org/
+flex_URL=https://github.com/westes/flex
+gnu_software_URL=https://www.gnu.org/software
 
 program_details ()
 {
@@ -207,7 +207,7 @@
 exit $st
 
 # Local variables:
-# eval: (add-hook 'write-file-hooks 'time-stamp)
+# eval: (add-hook 'before-save-hook 'time-stamp)
 # time-stamp-start: "scriptversion="
 # time-stamp-format: "%:y-%02m-%02d.%02H"
 # time-stamp-time-zone: "UTC0"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libHX-3.26/build-aux/test-driver new/libHX-4.0.1/build-aux/test-driver
--- old/libHX-3.26/build-aux/test-driver        2021-08-04 00:20:39.281517180 
+0200
+++ new/libHX-4.0.1/build-aux/test-driver       2021-10-03 02:30:15.089163901 
+0200
@@ -1,9 +1,9 @@
 #! /bin/sh
 # test-driver - basic testsuite driver script.
 
-scriptversion=2016-01-11.22; # UTC
+scriptversion=2018-03-07.03; # UTC
 
-# Copyright (C) 2011-2017 Free Software Foundation, Inc.
+# Copyright (C) 2011-2020 Free Software Foundation, Inc.
 #
 # 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
@@ -16,7 +16,7 @@
 # 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 <http://www.gnu.org/licenses/>.
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 # As a special exception to the GNU General Public License, if you
 # distribute this file as part of a program that contains a
@@ -42,11 +42,13 @@
 {
   cat <<END
 Usage:
-  test-driver --test-name=NAME --log-file=PATH --trs-file=PATH
-              [--expect-failure={yes|no}] [--color-tests={yes|no}]
-              [--enable-hard-errors={yes|no}] [--]
+  test-driver --test-name NAME --log-file PATH --trs-file PATH
+              [--expect-failure {yes|no}] [--color-tests {yes|no}]
+              [--enable-hard-errors {yes|no}] [--]
               TEST-SCRIPT [TEST-SCRIPT-ARGUMENTS]
+
 The '--test-name', '--log-file' and '--trs-file' options are mandatory.
+See the GNU Automake documentation for information.
 END
 }
 
@@ -59,7 +61,7 @@
 while test $# -gt 1; do
  arg=${1%=*}
  val=${1#*=}
- if [ $arg == $val ]; then
+ if [ $arg = $val ]; then
    val=$2
    shift
  fi
@@ -75,7 +77,7 @@
   --) break;;
   -*) usage_error "invalid option: '$1'";;
   esac
-  [[ $arg != $val ]] && shift
+  [ $arg != $val ] && shift
 done
 
 missing_opts=
@@ -145,7 +147,7 @@
 # Local Variables:
 # mode: shell-script
 # sh-indentation: 2
-# eval: (add-hook 'write-file-hooks 'time-stamp)
+# eval: (add-hook 'before-save-hook 'time-stamp)
 # time-stamp-start: "scriptversion="
 # time-stamp-format: "%:y-%02m-%02d.%02H"
 # time-stamp-time-zone: "UTC0"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libHX-3.26/config.h.in new/libHX-4.0.1/config.h.in
--- old/libHX-3.26/config.h.in  2021-08-04 00:20:38.513672104 +0200
+++ new/libHX-4.0.1/config.h.in 2021-10-03 02:30:14.377161841 +0200
@@ -24,6 +24,9 @@
 /* Define to 1 if you have the `getppid' function. */
 #undef HAVE_GETPPID
 
+/* Define to 1 if you have the `initgroups' function. */
+#undef HAVE_INITGROUPS
+
 /* Define to 1 if you have the <inttypes.h> header file. */
 #undef HAVE_INTTYPES_H
 
@@ -33,6 +36,9 @@
 /* Define to 1 if you have the `pipe' function. */
 #undef HAVE_PIPE
 
+/* Define to 1 if you have the `setgid' function. */
+#undef HAVE_SETGID
+
 /* Define to 1 if you have the <stdint.h> header file. */
 #undef HAVE_STDINT_H
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libHX-3.26/configure.ac new/libHX-4.0.1/configure.ac
--- old/libHX-3.26/configure.ac 2021-08-04 00:20:23.000000000 +0200
+++ new/libHX-4.0.1/configure.ac        2021-10-03 01:05:06.000000000 +0200
@@ -1,4 +1,4 @@
-AC_INIT([libHX], [3.26])
+AC_INIT([libHX], [4.0.1])
 AC_CONFIG_AUX_DIR([build-aux])
 AC_CONFIG_HEADERS([config.h])
 AC_CONFIG_MACRO_DIR([m4])
@@ -103,10 +103,9 @@
        #include <sys/time.h>
        #include <time.h>
        ])
-AC_CHECK_FUNCS([fork execv execvp pipe], [b_proc="1"])
+AC_CHECK_FUNCS([fork execv execvp pipe])
 AC_CHECK_FUNCS([getegid geteuid getpid getppid])
-AM_CONDITIONAL([B_PROC], [test "$b_proc" = 1])
-
+AC_CHECK_FUNCS([initgroups setgid])
 AC_CHECK_PROGS([LYX], [lyx])
 AM_CONDITIONAL([BUILD_DOCS], [test -n "$LYX"])
 
@@ -119,6 +118,5 @@
 AC_SUBST([regular_CPPFLAGS])
 AC_SUBST([regular_CFLAGS])
 AC_SUBST([regular_CXXFLAGS])
-AC_CONFIG_FILES([Makefile assorted/Makefile src/Makefile
-       include/Makefile libHX.pc])
+AC_CONFIG_FILES([Makefile src/Makefile include/Makefile libHX.pc])
 AC_OUTPUT
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libHX-3.26/doc/api.rst new/libHX-4.0.1/doc/api.rst
--- old/libHX-3.26/doc/api.rst  2021-08-04 00:20:23.000000000 +0200
+++ new/libHX-4.0.1/doc/api.rst 2021-10-03 01:05:06.000000000 +0200
@@ -9,6 +9,18 @@
 ======  ======  ======  ========================================
 RMV     MinVer  FirstA  Name
 ======  ======  ======  ========================================
+3.27    3.27    3.27    HXOPT_KEEP_ARGV
+3.27    3.27    3.27    HXproc_top_fd
+3.27    3.27    3.27    HXproc_switch_user
+3.27    3.27    3.27    HXPROC_SU_SUCCESS
+3.27    3.27    3.27    HXPROC_SU_NOOP
+3.27    3.27    3.27    HXPROC_USER_NOT_FOUND
+3.27    3.27    3.27    HXPROC_GROUP_NOT_FOUND
+3.27    3.27    3.27    HXPROC_SETUID_FAILED
+3.27    3.27    3.27    HXPROC_SETGID_FAILED
+3.27    3.27    3.27    HXPROC_INITGROUPS_FAILED
+3.27    3.27    3.27    HX_slurp_fd
+3.27    3.27    3.27    HX_slurp_file
 3.25    3.25    3.25    HX_split_fixed
 3.25    3.25    3.25    HX_split_inplace
 3.22    3.22    3.22    HXQUOTE_SQLBQUOTE
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libHX-3.26/doc/changelog.rst new/libHX-4.0.1/doc/changelog.rst
--- old/libHX-3.26/doc/changelog.rst    2021-08-04 00:20:23.000000000 +0200
+++ new/libHX-4.0.1/doc/changelog.rst   2021-10-03 01:05:06.000000000 +0200
@@ -1,3 +1,19 @@
+v4.0 (2021-10-03)
+=================
+
+Enhancements:
+
+* lib: add ``HX_slurp_fd``, ``HX_slurp_file``
+* proc: add ``HXproc_switch_user``
+* proc: add ``HXproc_top_fd``
+* socket: add ``HX_socket_from_env``
+* opt: add ``HXOPT_KEEP_ARGV`` flag
+
+Fixes:
+
+* proc: re-close pipes when ``HXproc_build_pipes`` failed
+
+
 v3.26 (2021-08-03)
 ==================
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libHX-3.26/doc/files_and_dirs.rst new/libHX-4.0.1/doc/files_and_dirs.rst
--- old/libHX-3.26/doc/files_and_dirs.rst       2021-08-04 00:20:23.000000000 
+0200
+++ new/libHX-4.0.1/doc/files_and_dirs.rst      2021-10-03 01:05:06.000000000 
+0200
@@ -121,10 +121,24 @@
 
        #include <libHX/io.h>
 
+       #define HXF_KEEP ...
+       #define HXF_UID ...
+       #define HXF_GID ...
+
        int HX_copy_file(const char *src, const char *dest, unsigned int flags, 
...);
        int HX_copy_dir(const char *src, const char *dest, unsigned int flags, 
...);
+       char *HX_slurp_fd(int fd, size_t *outsize);
+       char *HX_slurp_file(const char *file, size_t *outsize);
 
-Possible flags that can be used with the functions:
+``HX_copy_file``
+       Copies one named file to a new location. Possible ``flags`` are
+       ``HXF_KEEP``, ``HXF_UID`` and ``HXF_GID``. Error checking by
+       ``HX_copy_file`` is flakey. ``HX_copy_file`` will return >0 on success,
+       or ``-errno`` on failure. Errors can arise from the use of the syscalls
+       ``open``, ``read`` and ``write``. The return value of ``fchmod``, which
+       is used to set the UID and GID, is actually ignored, which means
+       verifying that the owner has been set cannot be detected with
+       ``HX_copy_file`` alone (historic negligience?).
 
 ``HXF_KEEP``
        Do not overwrite existing files.
@@ -137,13 +151,22 @@
        Change the new file's group owner to the GID given in the varargs
        section. This is processed after ``HXF_UID``.
 
-Error checking is flakey.
-
-``HX_copy_file`` will return >0 on success, or ``-errno`` on failure. Errors
-can arise from the use of the syscalls ``open``, ``read`` and ``write``. The
-return value of ``fchmod``, which is used to set the UID and GID, is actually
-ignored, which means verifying that the owner has been set cannot be detected
-with ``HX_copy_file`` alone (historic negligience?).
+``HX_copy_dir``
+       Copies one named directory to a new location, recursively.
+       (Uses ``HX_copy_file`` and ``HX_copy_dir``.) Error checking by
+       ``HX_copy_dir`` is flakey.
+
+``HX_slurp_fd``
+       Reads all remaining bytes from the given filedescriptor ``fd`` and
+       returns a pointer to a newly-allocated content buffer. If ``outsize``
+       is not ``NULL``, the size of the buffer will be written to it. The
+       buffer is always terminated by a gratuitious NUL (not counted in
+       ``outsize``). Once no longer needed, the buffer should be released with
+       ``free``.
+
+``HX_slurp_file``
+       Reads all bytes from the given filename and returns a pointer to the
+       content buffer. Inherits all the characteristics from ``HX_slurp_fd``.
 
 
 Filedescriptor helpers
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libHX-3.26/doc/option_parsing.rst new/libHX-4.0.1/doc/option_parsing.rst
--- old/libHX-3.26/doc/option_parsing.rst       2021-08-04 00:20:23.000000000 
+0200
+++ new/libHX-4.0.1/doc/option_parsing.rst      2021-10-03 01:05:06.000000000 
+0200
@@ -15,12 +15,9 @@
 
 .. [#f4] popt failed to do this for a long time.
 
-A table-based approach allows for the parser to run as one atomic block of code
-(callbacks are, by definition, ???special??? exceptions), making it more opaque
-than an open-coded ``getopt``(3) loop. You give it your argument vector and the
-table, snip the finger (call the parser function once), and it is done. In
-getopt on the other hand, the getopt function returns for every argument it
-parsed and needs to be called repeatedly.
+A table-based approach allows for the parser to run as one unit, quite unlike
+the open-coded ``getopt``(3) loop where the function returns for every argument
+it parsed and needs to be called repeatedly.
 
 
 Synopsis
@@ -30,7 +27,7 @@
 
        #include <libHX/option.h>
 
-       struct HXoption {struct HXoption
+       struct HXoption {
                const char *ln;
                char sh;
                unsigned int type;
@@ -278,10 +275,11 @@
 
 ``HX_getopt`` is the actual parsing function. It takes the option table, and a
 pointer to your argc and argv variables that you get from the main function.
-The parser will, unlike GNU getopt, literally ???eat??? all options and their
-arguments, leaving only non-options in ``argv``, and ``argc`` updated, when
-finished. This is similar to how Perl's ``Getopt::Long`` module works.
-Additional flags can control the exact behavior of ``HX_getopt``:
+The parser will, by default, consume all options and their arguments, similar
+to Perl's ``Getopt::Long`` module. ``*argv`` is then updated to point to a new
+array of strings (to be deallocated with ``HX_zvecfree``) and ``*argc`` is
+updated accordingly. Additional flags can control the exact behavior of
+``HX_getopt``:
 
 ``HXOPT_PTHRU``
        ???Passthrough mode???. Any unknown options are not ???eaten??? and are 
instead
@@ -303,6 +301,9 @@
        non-option argument in argv is encountered. This behavior is also
        implicit when the environment variable ``POSIXLY_CORRECT`` is set.
 
+``HXOPT_KEEP_ARGV``
+       Do not modify ``argc`` and ``argv`` at all.
+
 The return value can be one of the following:
 
 ``HXOPT_ERR_SUCCESS``
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libHX-3.26/doc/process_management.rst 
new/libHX-4.0.1/doc/process_management.rst
--- old/libHX-3.26/doc/process_management.rst   2021-08-04 00:20:23.000000000 
+0200
+++ new/libHX-4.0.1/doc/process_management.rst  2021-10-03 01:05:06.000000000 
+0200
@@ -193,3 +193,41 @@
 
 ``EINVAL``
        Flags were not accepted.
+
+
+User identity control
+=====================
+
+.. code-block: c
+
+       #include <libHX/proc.h>
+
+       int HXproc_switch_user(const char *user, const char *group);
+
+``HXproc_switch_user`` is a wrapper for changing process identity to an
+unprivileged user. This utilizes ``setuid``, and possibly ``setgid`` plus
+``initgroups``.
+
+``user`` can either be a username or a numeric UID in string form, the latter
+of which will be parsed with strtoul in automatic base. If ``user`` is ``NULL``
+or the empty string, no change of process user identity occurs.
+
+``group`` can likewise be a name or GID. When ``group`` is ``NULL``, the
+process group(s) will change to the the user's group(s)????? both primary and
+secondary????? provided a user was specified (see above). When ``gruop`` is the
+empty string, no change of process group identity occurs.
+
+
+Process information
+===================
+
+.. code-block:: c
+
+       #include <libHX/proc.h>
+
+       int HXproc_top_fd(void);
+
+``HXproc_top_fd``
+       This function gives a heuristic for the highest fd in the process.
+       The returned number may be higher than the highest live fd actually.
+       On error, negative errno is returned.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libHX-3.26/doc/slurp.c new/libHX-4.0.1/doc/slurp.c
--- old/libHX-3.26/doc/slurp.c  2021-08-04 00:20:23.000000000 +0200
+++ new/libHX-4.0.1/doc/slurp.c 1970-01-01 01:00:00.000000000 +0100
@@ -1,38 +0,0 @@
-static void *p_slurp(const char *file, size_t *outsize)
-{
-       struct stat sb;
-       int ret = 0, fd = open(file, O_RDONLY | O_BINARY);
-       void *buf = NULL;
-       ssize_t rdret;
-
-       if (fd < 0) {
-               fprintf(stderr, "ERROR: Slurping %s failed: %s\n",
-                       file, strerror(errno));
-               return NULL;
-       }
-       if (fstat(fd, &buf) < 0) {
-               ret = errno;
-               perror("fstat");
-               goto out;
-       }
-       *outsize = sb.st_size; /* truncate if need be */
-       buf = malloc(*outsize);
-       if (buf == NULL) {
-               ret = errno;
-               perror("malloc");
-               goto out;
-       }
-       rdret = read(fd, buf, *outsize);
-       if (rdret < 0) {
-               ret = errno;
-               perror("read");
-               free(buf);
-       } else {
-               *outsize = rdret;
-       }
- out:
-       close(fd);
-       errno = ret;
-       return buf;
-}
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libHX-3.26/doc/socket_functions.rst new/libHX-4.0.1/doc/socket_functions.rst
--- old/libHX-3.26/doc/socket_functions.rst     1970-01-01 01:00:00.000000000 
+0100
+++ new/libHX-4.0.1/doc/socket_functions.rst    2021-10-03 01:05:06.000000000 
+0200
@@ -0,0 +1,16 @@
+================
+Socket functions
+================
+
+.. code-block:: c
+
+       #include <libHX/socket.h>
+
+       int HX_socket_from_env(const struct addrinfo *ai, const char *intf);
+
+``HX_socket_from_env``
+       The function looks up the current process's file descriptors for a
+       socket that is listening and which matches the given addrinfo and
+       (optionally) intf if the latter is not NULL``. Upon success, the fd
+       number is returned, or -1 if no file descriptor matched. No errors are
+       signalled.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libHX-3.26/include/Makefile.am new/libHX-4.0.1/include/Makefile.am
--- old/libHX-3.26/include/Makefile.am  2021-08-04 00:20:23.000000000 +0200
+++ new/libHX-4.0.1/include/Makefile.am 2021-10-03 01:05:06.000000000 +0200
@@ -2,7 +2,6 @@
 
 nobase_include_HEADERS = libHX.h \
        libHX/ctype_helper.h libHX/defs.h libHX/deque.h libHX/init.h \
-       libHX/io.h libHX/list.h \
-       libHX/map.h libHX/misc.h libHX/option.h libHX/proc.h libHX/string.h \
+       libHX/intdiff.hpp libHX/io.h libHX/list.h \
+       libHX/map.h libHX/misc.h libHX/option.h libHX/proc.h libHX/socket.h 
libHX/string.h \
        libHX/libxml_helper.h libHX/wx_helper.hpp
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libHX-3.26/include/libHX/io.h new/libHX-4.0.1/include/libHX/io.h
--- old/libHX-3.26/include/libHX/io.h   2021-08-04 00:20:23.000000000 +0200
+++ new/libHX-4.0.1/include/libHX/io.h  2021-10-03 01:05:06.000000000 +0200
@@ -35,6 +35,8 @@
 extern int HX_readlink(hxmc_t **, const char *);
 extern int HX_realpath(hxmc_t **, const char *, unsigned int);
 extern int HX_rrmdir(const char *);
+extern char *HX_slurp_fd(int fd, size_t *outsize);
+extern char *HX_slurp_file(const char *file, size_t *outsize);
 
 extern ssize_t HXio_fullread(int, void *, size_t);
 extern ssize_t HXio_fullwrite(int, const void *, size_t);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libHX-3.26/include/libHX/option.h new/libHX-4.0.1/include/libHX/option.h
--- old/libHX-3.26/include/libHX/option.h       2021-08-04 00:20:23.000000000 
+0200
+++ new/libHX-4.0.1/include/libHX/option.h      2021-10-03 01:05:06.000000000 
+0200
@@ -144,6 +144,7 @@
  * %HXOPT_USAGEONERR:  print out short usage when a parsing error occurs
  * %HXOPT_RQ_ORDER:    require option order/POSIX mode:
  *                     first non-option terminates option processing
+ * %HXOPT_KEEP_ARGV:   do not replace argc/argv at all
  */
 enum {
        HXOPT_PTHRU       = 1 << 0,
@@ -152,6 +153,7 @@
        HXOPT_HELPONERR   = 1 << 3,
        HXOPT_USAGEONERR  = 1 << 4,
        HXOPT_RQ_ORDER    = 1 << 5,
+       HXOPT_KEEP_ARGV   = 1 << 6,
 };
 
 /**
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libHX-3.26/include/libHX/proc.h new/libHX-4.0.1/include/libHX/proc.h
--- old/libHX-3.26/include/libHX/proc.h 2021-08-04 00:20:23.000000000 +0200
+++ new/libHX-4.0.1/include/libHX/proc.h        2021-10-03 01:05:06.000000000 
+0200
@@ -21,6 +21,16 @@
        HXPROC_NULL_STDERR = 1 << 8,
 };
 
+enum HXproc_su_status {
+       HXPROC_INITGROUPS_FAILED = -5,
+       HXPROC_SETGID_FAILED = -4,
+       HXPROC_SETUID_FAILED = -3,
+       HXPROC_GROUP_NOT_FOUND = -2,
+       HXPROC_USER_NOT_FOUND = -1,
+       HXPROC_SU_NOOP = 0,
+       HXPROC_SU_SUCCESS = 1,
+};
+
 struct HXproc_ops {
        void (*p_prefork)(void *);
        void (*p_postfork)(void *);
@@ -41,6 +51,8 @@
 extern int HXproc_run_async(const char *const *, struct HXproc *);
 extern int HXproc_run_sync(const char *const *, unsigned int);
 extern int HXproc_wait(struct HXproc *);
+extern enum HXproc_su_status HXproc_switch_user(const char *user, const char 
*group);
+extern int HXproc_top_fd(void);
 
 #ifdef __cplusplus
 } /* extern "C" */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libHX-3.26/include/libHX/socket.h new/libHX-4.0.1/include/libHX/socket.h
--- old/libHX-3.26/include/libHX/socket.h       1970-01-01 01:00:00.000000000 
+0100
+++ new/libHX-4.0.1/include/libHX/socket.h      2021-10-03 01:05:06.000000000 
+0200
@@ -0,0 +1,20 @@
+#ifndef _LIBHX_SOCKET_H
+#define _LIBHX_SOCKET_H 1
+
+#ifdef _WIN32
+#      include <ws2tcpip.h>
+#else
+#      include <netdb.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int HX_socket_from_env(const struct addrinfo *, const char *intf);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* _LIBHX_SOCKET_H */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libHX-3.26/src/.gitignore new/libHX-4.0.1/src/.gitignore
--- old/libHX-3.26/src/.gitignore       2021-08-04 00:20:23.000000000 +0200
+++ new/libHX-4.0.1/src/.gitignore      2021-10-03 01:05:06.000000000 +0200
@@ -5,6 +5,7 @@
 /t?-dir
 /t?-format
 /t?-intdiff
+/t?-io
 /t?-link
 /t?-list
 /t?-list2
@@ -21,6 +22,7 @@
 /t?-strchr2
 /t?-string
 /t?-strquote
+/t?-switchuser
 /t?-time
 
 # automake tests
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libHX-3.26/src/Makefile.am new/libHX-4.0.1/src/Makefile.am
--- old/libHX-3.26/src/Makefile.am      2021-08-04 00:20:23.000000000 +0200
+++ new/libHX-4.0.1/src/Makefile.am     2021-10-03 01:05:06.000000000 +0200
@@ -10,10 +10,10 @@
 endif
 
 libHX_la_SOURCES = deque.c dl.c format.c io.c map.c \
-                   mc.c misc.c opt.c \
-                   rand.c string.c time.c
+                   mc.c misc.c opt.c proc.c \
+                   rand.c socket.c string.c time.c
 libHX_la_LIBADD  = ${libdl_LIBS} ${libpthread_LIBS} ${librt_LIBS}
-libHX_la_LDFLAGS = -no-undefined -version-info 32:0:0
+libHX_la_LDFLAGS = -no-undefined -version-info 33:0:1
 if WITH_GNU_LD
 libHX_la_LDFLAGS += -Wl,--version-script=${srcdir}/libHX.map
 endif
@@ -22,9 +22,6 @@
 if MINGW32
 libHX_la_SOURCES += ux-file.c ux-mmap.c
 endif
-if B_PROC
-libHX_la_SOURCES += proc.c
-endif
 
 libHX_rtcheck_la_SOURCES = rtcheck.c
 libHX_rtcheck_la_LIBADD  = ${libdl_LIBS}
@@ -35,16 +32,18 @@
 
 EXTRA_DIST = internal.h map_int.h libHX.map
 
-check_PROGRAMS     = tc-compile tc-cast tc-deque tc-dir tc-format tc-link \
+check_PROGRAMS     = tc-compile tc-cast tc-deque tc-dir tc-format tc-io 
tc-link \
                      tc-list tc-list2 tc-map tc-memmem tc-misc tc-netio \
                      tc-option tc-proc tc-rand tc-realpath \
-                     tc-shconfig tc-strchr2 tc-string tc-strquote tc-time
+                     tc-shconfig tc-strchr2 tc-string tc-strquote \
+                     tc-switchuser tc-time
 TESTS              = tc-strchr2 tc-strquote
 tc_cast_CFLAGS     = ${AM_CFLAGS} -std=gnu99
 tc_cast_LDADD      = libHX.la -lm
 tc_compile_LDADD   = libHX.la
 tc_dir_LDADD       = libHX.la
 tc_format_LDADD    = libHX.la
+tc_io_LDADD        = libHX.la
 tc_link_LDADD      = libHX.la
 tc_list_LDADD      = libHX.la
 tc_list2_LDADD     = libHX.la
@@ -61,6 +60,7 @@
 tc_strchr2_LDADD   = libHX.la
 tc_string_LDADD    = libHX.la
 tc_strquote_LDADD  = libHX.la
+tc_switchuser_LDADD = libHX.la
 tc_time_LDADD      = libHX.la
 
 if HAVE_CXX
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libHX-3.26/src/internal.h new/libHX-4.0.1/src/internal.h
--- old/libHX-3.26/src/internal.h       2021-08-04 00:20:23.000000000 +0200
+++ new/libHX-4.0.1/src/internal.h      2021-10-03 01:05:06.000000000 +0200
@@ -42,6 +42,7 @@
 #define MAXLNLEN 1024 /* max length for usual line */
 
 #define HXMC_IDENT 0x200571AF
+#define nullptr NULL
 
 struct memcont {
        size_t alloc, length;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libHX-3.26/src/io.c new/libHX-4.0.1/src/io.c
--- old/libHX-3.26/src/io.c     2021-08-04 00:20:23.000000000 +0200
+++ new/libHX-4.0.1/src/io.c    2021-10-03 01:05:06.000000000 +0200
@@ -14,6 +14,7 @@
 #include <stdarg.h>
 #include <stdbool.h>
 #include <stddef.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -300,27 +301,38 @@
 /* Readlink - with a trailing zero (provided by HXmc) */
 EXPORT_SYMBOL int HX_readlink(hxmc_t **target, const char *path)
 {
-       bool dnull = *target == NULL;
-       char *tb;
-       int ret;
+       bool allocate = *target == NULL;
+       size_t linkbuf_size;
 
-       if (dnull) {
-               *target = HXmc_meminit(NULL, PATH_MAX);
+       if (allocate) {
+               linkbuf_size = 32;
+               *target = HXmc_meminit(NULL, 32);
                if (*target == NULL)
                        return -errno;
+       } else {
+               linkbuf_size = HXmc_length(*target);
        }
-       tb  = *target;
-       ret = readlink(path, tb, PATH_MAX);
-       if (ret < 0) {
-               ret = -errno;
-               if (!dnull) {
-                       HXmc_free(*target);
-                       *target = NULL;
+       while (true) {
+               ssize_t ret = readlink(path, *target, linkbuf_size);
+               if (ret < 0) {
+                       int saved_errno = errno;
+                       if (allocate)
+                               HXmc_free(*target);
+                       return -(errno = saved_errno);
+               }
+               if (static_cast(size_t, ret) < linkbuf_size) {
+                       HXmc_setlen(target, ret);
+                       return ret;
+               }
+               linkbuf_size *= 2;
+               if (HXmc_setlen(target, linkbuf_size) == NULL) {
+                       int saved_errno = errno;
+                       if (allocate)
+                               HXmc_free(*target);
+                       return -(errno = saved_errno);
                }
-               return ret;
        }
-       HXmc_setlen(target, ret);
-       return ret;
+       return 0;
 }
 
 /**
@@ -567,3 +579,46 @@
        }
        return done;
 }
+
+EXPORT_SYMBOL char *HX_slurp_fd(int fd, size_t *outsize)
+{
+       struct stat sb;
+       if (fstat(fd, &sb) < 0)
+               return NULL;
+       size_t fsize = sb.st_size; /* may truncate from loff_t to size_t */
+       if (fsize == SIZE_MAX)
+               --fsize;
+       char *buf = malloc(fsize + 1);
+       if (buf == NULL)
+               return NULL;
+       ssize_t rdret = HXio_fullread(fd, buf, fsize);
+       if (rdret < 0) {
+               int se = errno;
+               free(buf);
+               errno = se;
+               return NULL;
+       }
+       buf[rdret] = '\0';
+       if (outsize != NULL)
+               *outsize = rdret;
+       return buf;
+}
+
+EXPORT_SYMBOL char *HX_slurp_file(const char *file, size_t *outsize)
+{
+       int fd = open(file, O_RDONLY | O_BINARY | O_CLOEXEC);
+       if (fd < 0)
+               return NULL;
+       size_t tmpsize;
+       if (outsize == NULL)
+               outsize = &tmpsize;
+       char *buf = HX_slurp_fd(fd, outsize);
+       if (buf == NULL) {
+               int se = errno;
+               close(fd);
+               errno = se;
+               return NULL;
+       }
+       close(fd);
+       return buf;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libHX-3.26/src/libHX.map new/libHX-4.0.1/src/libHX.map
--- old/libHX-3.26/src/libHX.map        2021-08-04 00:20:23.000000000 +0200
+++ new/libHX-4.0.1/src/libHX.map       2021-10-03 01:05:06.000000000 +0200
@@ -126,3 +126,12 @@
 local:
        *;
 };
+
+LIBHX_3.27 {
+global:
+       HX_socket_from_env;
+       HX_slurp_fd;
+       HX_slurp_file;
+       HXproc_switch_user;
+       HXproc_top_fd;
+} LIBHX_3.25;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libHX-3.26/src/opt.c new/libHX-4.0.1/src/opt.c
--- old/libHX-3.26/src/opt.c    2021-08-04 00:20:23.000000000 +0200
+++ new/libHX-4.0.1/src/opt.c   2021-10-03 01:05:06.000000000 +0200
@@ -758,6 +758,7 @@
 
  out:
        if (ret == HXOPT_ERR_SUCCESS) {
+               if (!(ps.flags & HXOPT_KEEP_ARGV)) {
                const char **nvec = reinterpret_cast(const char **,
                                    HXdeque_to_vec(ps.remaining, &argk));
                if (nvec == NULL)
@@ -776,6 +777,7 @@
                *argv = nvec;
                if (argc != NULL)
                        *argc = argk;
+               }
        } else if (ret < 0) {
                if (!(ps.flags & HXOPT_QUIET))
                        fprintf(stderr, "%s: %s\n", __func__, strerror(errno));
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libHX-3.26/src/proc.c new/libHX-4.0.1/src/proc.c
--- old/libHX-3.26/src/proc.c   2021-08-04 00:20:23.000000000 +0200
+++ new/libHX-4.0.1/src/proc.c  2021-10-03 01:05:06.000000000 +0200
@@ -7,42 +7,115 @@
  *     General Public License as published by the Free Software Foundation;
  *     either version 2.1 or (at your option) any later version.
  */
-#include "config.h"
+#ifdef HAVE_CONFIG_H
+#      include "config.h"
+#endif
 #include "internal.h"
-
-#if !defined(HAVE_FORK) || !defined(HAVE_PIPE) || !defined(HAVE_EXECV) || \
-    !defined(HAVE_EXECVP)
 #include <errno.h>
+#include <limits.h>
+#include <unistd.h>
+#ifdef HAVE_SYS_RESOURCE_H
+#      include <sys/resource.h>
+#endif
 #include <libHX/proc.h>
 
-struct HXproc;
+#if defined(HAVE_INITGROUPS) && defined(HAVE_SETGID)
+#include <grp.h>
+#include <pwd.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
 
-EXPORT_SYMBOL int HXproc_run_async(const char *const *argv, struct HXproc *p)
+static int HXproc_switch_gid(const struct passwd *pw, gid_t gr_gid)
 {
-       return -ENOSYS;
+       bool do_setgid = getgid() != gr_gid || getegid() != gr_gid;
+       if (do_setgid && setgid(gr_gid) != 0) {
+               if (errno == 0)
+                       errno = -EINVAL;
+               return HXPROC_SETGID_FAILED;
+       }
+       if (pw == NULL)
+               return do_setgid ? HXPROC_SU_SUCCESS : HXPROC_SU_NOOP;
+
+       /* pw!=NULL: user switch requested, do initgroups now. */
+
+       if (geteuid() == pw->pw_uid) {
+               /*
+                * Target identity (usually unprivileged) already reached.
+                * initgroups is unlikely to succeed.
+                */
+               if (initgroups(pw->pw_name, gr_gid) < 0)
+                       /* ignore */;
+               return do_setgid ? HXPROC_SU_SUCCESS : HXPROC_SU_NOOP;
+       }
+       if (initgroups(pw->pw_name, gr_gid) != 0)
+               return HXPROC_INITGROUPS_FAILED;
+       return do_setgid ? HXPROC_SU_SUCCESS : HXPROC_SU_NOOP;
 }
 
-EXPORT_SYMBOL int HXproc_run_sync(const char *const *argv, unsigned int flags)
+static int HXproc_switch_group(const struct passwd *pw, const char *group)
 {
-       /* Might use system() here... */
-       return -ENOSYS;
+       char *end;
+       unsigned long gid = strtoul(group, &end, 10);
+       const struct group *gr = *end == '\0' ? getgrgid(gid) : getgrnam(group);
+       if (gr == NULL) {
+               if (errno == 0)
+                       errno = ENOENT;
+               return HXPROC_GROUP_NOT_FOUND;
+       }
+       return HXproc_switch_gid(pw, gr->gr_gid);
 }
 
-EXPORT_SYMBOL int HXproc_wait(struct HXproc *p)
+EXPORT_SYMBOL int HXproc_switch_user(const char *user, const char *group)
+{
+       const struct passwd *pw = NULL;
+       if (user != NULL && *user != '\0') {
+               char *end;
+               unsigned long uid = strtoul(user, &end, 10);
+               pw = *end == '\0' ? getpwuid(uid) : getpwnam(user);
+               if (pw == NULL) {
+                       if (errno == 0)
+                               errno = ENOENT;
+                       return HXPROC_USER_NOT_FOUND;
+               }
+       }
+       int ret = HXPROC_SU_NOOP;
+       if (group != NULL && *group != '\0') {
+               ret = HXproc_switch_group(pw, group);
+               if (ret < 0)
+                       return ret;
+       } else if (group == NULL && pw != NULL) {
+               ret = HXproc_switch_gid(pw, pw->pw_gid);
+               if (ret < 0)
+                       return ret;
+       }
+       bool do_setuid = pw != NULL && (getuid() != pw->pw_uid || geteuid() != 
pw->pw_uid);
+       if (do_setuid && setuid(pw->pw_uid) != 0) {
+               if (errno == 0)
+                       errno = -EINVAL;
+               return HXPROC_SETUID_FAILED;
+       }
+       return do_setuid ? HXPROC_SU_SUCCESS : ret;
+}
+
+#else
+
+EXPORT_SYMBOL int HXproc_switch_user(const char *user, const char *group)
 {
        return -ENOSYS;
 }
 
-#else /* HAVE_FORK, HAVE_PIPE, HAVE_EXECVE */
+#endif /* HAVE_lots */
 
+#if defined(HAVE_FORK) && defined(HAVE_PIPE) && defined(HAVE_EXECV) && \
+    defined(HAVE_EXECVP)
 #include <sys/wait.h>
 #include <fcntl.h>
-#include <errno.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
 #include <libHX/defs.h>
-#include <libHX/proc.h>
 #include "internal.h"
 
 #ifdef _WIN32
@@ -88,18 +161,20 @@
  */
 static void HXproc_close_pipes(int (*p)[2])
 {
+#define xc(fd) do { close(fd); (fd) = -1; } while (false)
        if (p[0][0] >= 0)
-               close(p[0][0]);
+               xc(p[0][0]);
        if (p[0][1] >= 0)
-               close(p[0][1]);
+               xc(p[0][1]);
        if (p[1][0] >= 0)
-               close(p[1][0]);
+               xc(p[1][0]);
        if (p[1][1] >= 0)
-               close(p[1][1]);
+               xc(p[1][1]);
        if (p[2][0] >= 0)
-               close(p[2][0]);
+               xc(p[2][0]);
        if (p[2][1] >= 0)
-               close(p[2][1]);
+               xc(p[2][1]);
+#undef xc
 }
 
 /**
@@ -136,6 +211,7 @@
        }
        if ((ret = HXproc_build_pipes(proc, pipes)) <= 0) {
                saved_errno = errno;
+               HXproc_close_pipes(pipes);
                if (nullfd >= 0)
                        close(nullfd);
                errno = saved_errno;
@@ -266,4 +342,41 @@
        return static_cast(unsigned char, proc->p_status);
 }
 
+#else
+
+EXPORT_SYMBOL int HXproc_run_async(const char *const *argv, struct HXproc *p)
+{
+       return -ENOSYS;
+}
+
+EXPORT_SYMBOL int HXproc_run_sync(const char *const *argv, unsigned int flags)
+{
+       /* Might use system() here... */
+       return -ENOSYS;
+}
+
+EXPORT_SYMBOL int HXproc_wait(struct HXproc *p)
+{
+       return -ENOSYS;
+}
+
 #endif /* HAVE_lots */
+
+EXPORT_SYMBOL int HXproc_top_fd(void)
+{
+#ifndef _WIN32
+       struct rlimit r;
+       if (getrlimit(RLIMIT_NOFILE, &r) == 0) {
+               if (r.rlim_max > INT_MAX)
+                       r.rlim_max = INT_MAX;
+               return r.rlim_max;
+       }
+       long v = sysconf(_SC_OPEN_MAX);
+       if (v >= 0) {
+               if (v > INT_MAX)
+                       v = INT_MAX;
+               return v;
+       }
+#endif
+       return FD_SETSIZE;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libHX-3.26/src/socket.c new/libHX-4.0.1/src/socket.c
--- old/libHX-3.26/src/socket.c 1970-01-01 01:00:00.000000000 +0100
+++ new/libHX-4.0.1/src/socket.c        2021-10-03 01:05:06.000000000 +0200
@@ -0,0 +1,86 @@
+/*
+ *     Socket-related functions
+ *     Copyright Jan Engelhardt, 2021
+ *
+ *     This file is part of libHX. libHX 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 or (at your option) any later version.
+ */
+#ifdef HAVE_CONFIG_H
+#      include "config.h"
+#endif
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef _WIN32
+#      include <ws2tcpip.h>
+#else
+#      include <sys/socket.h>
+#      include <netdb.h>
+#endif
+#include <libHX/proc.h>
+#include <libHX/socket.h>
+#include "internal.h"
+
+static int try_sk_from_env(int fd, const struct addrinfo *ai, const char *intf)
+{
+       int value = 0;
+       socklen_t optlen = sizeof(value);
+       int ret = getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &value, &optlen);
+       if (ret < 0 || value == 0)
+               return -1;
+       optlen = sizeof(value);
+       ret = getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &value, &optlen);
+       if (ret < 0 || value != ai->ai_family)
+               return -1;
+       optlen = sizeof(value);
+       ret = getsockopt(fd, SOL_SOCKET, SO_TYPE, &value, &optlen);
+       if (ret < 0 || value != ai->ai_socktype)
+               return -1;
+       optlen = sizeof(value);
+       ret = getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &value, &optlen);
+       if (ret < 0 || value != ai->ai_protocol)
+               return -1;
+       struct sockaddr_storage addr;
+       memset(&addr, 0, sizeof(addr));
+       optlen = sizeof(addr);
+       ret = getsockname(fd, (struct sockaddr *)&addr, &optlen);
+       if (ret < 0)
+               return -1;
+       if (sizeof(addr) < optlen)
+               optlen = sizeof(addr);
+       if (optlen != ai->ai_addrlen || memcmp(&addr, ai->ai_addr, optlen) != 0)
+               return -1;
+       if (intf == nullptr)
+               return fd;
+       char ifname[32];
+       optlen = sizeof(ifname);
+       ret = getsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, &optlen);
+       if (ret < 0)
+               return -1;
+       else if (optlen < sizeof(ifname))
+               ifname[optlen] = '\0';
+       else
+               ifname[sizeof(ifname)-1] = '\0';
+       if (strcmp(intf, ifname) != 0)
+               return -1;
+       return fd;
+}
+
+EXPORT_SYMBOL int HX_socket_from_env(const struct addrinfo *ai, const char 
*intf)
+{
+       int top_fd;
+       const char *env_limit = getenv("LISTEN_FDS");
+       if (env_limit != nullptr) {
+               top_fd = 3 + strtol(env_limit, nullptr, 0);
+       } else {
+               env_limit = getenv("HX_LISTEN_TOP_FD");
+               top_fd = env_limit != nullptr ? strtol(env_limit, nullptr, 0) : 
HXproc_top_fd();
+       }
+       for (int fd = 3; fd < top_fd; ++fd)
+               if (try_sk_from_env(fd, ai, intf) == fd)
+                       return fd;
+       errno = ENOENT;
+       return -1;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libHX-3.26/src/tc-io.c new/libHX-4.0.1/src/tc-io.c
--- old/libHX-3.26/src/tc-io.c  1970-01-01 01:00:00.000000000 +0100
+++ new/libHX-4.0.1/src/tc-io.c 2021-10-03 01:05:06.000000000 +0200
@@ -0,0 +1,11 @@
+#include <stdio.h>
+#include <libHX/io.h>
+
+int main(void)
+{
+       size_t z;
+       char *s = HX_slurp_file("tc-io.c", &z);
+       printf("%s\n", s);
+       printf("Dumped %zu bytes\n", z);
+       return 0;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libHX-3.26/src/tc-proc.c new/libHX-4.0.1/src/tc-proc.c
--- old/libHX-3.26/src/tc-proc.c        2021-08-04 00:20:23.000000000 +0200
+++ new/libHX-4.0.1/src/tc-proc.c       2021-10-03 01:05:06.000000000 +0200
@@ -7,10 +7,12 @@
  */
 #ifdef __cplusplus
 #      include <cerrno>
+#      include <cstdio>
 #      include <cstdlib>
 #      include <cstring>
 #else
 #      include <errno.h>
+#      include <stdio.h>
 #      include <stdlib.h>
 #      include <string.h>
 #endif
@@ -56,6 +58,7 @@
 {
        if (HX_init() <= 0)
                abort();
+       printf("top fd: %d\n", HXproc_top_fd());
 
        /* let it fail - test verbosity */
        HXproc_run_sync(t_args1 + 2, HXPROC_VERBOSE);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libHX-3.26/src/tc-switchuser.c new/libHX-4.0.1/src/tc-switchuser.c
--- old/libHX-3.26/src/tc-switchuser.c  1970-01-01 01:00:00.000000000 +0100
+++ new/libHX-4.0.1/src/tc-switchuser.c 2021-10-03 01:05:06.000000000 +0200
@@ -0,0 +1,73 @@
+/*
+ *     Copyright Jan Engelhardt
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the WTF Public License version 2 or
+ *     (at your option) any later version.
+ */
+#ifdef HAVE_CONFIG_H
+#      include "config.h"
+#endif
+#if defined(HAVE_INITGROUPS)
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <libHX/defs.h>
+#include <libHX/option.h>
+#include <libHX/proc.h>
+
+static char *user_name, *group_name;
+static const struct HXoption options_table[] = {
+       {.sh = 'u', .type = HXTYPE_STRING, .ptr = &user_name},
+       {.sh = 'g', .type = HXTYPE_STRING, .ptr=  &group_name},
+       HXOPT_TABLEEND,
+};
+
+int main(int argc, const char **argv)
+{
+       HX_getopt(options_table, &argc, &argv, HXOPT_USAGEONERR);
+       const char *user = user_name != NULL ? user_name : "-";
+       const char *group = group_name != NULL ? group_name : "-";
+       switch (HXproc_switch_user(user_name, group_name)) {
+       case HXPROC_USER_NOT_FOUND:
+               if (user_name == NULL)
+                       abort(); /* impossible outcomes */
+               printf("No such user \"%s\": %s\n", user_name, strerror(errno));
+               break;
+       case HXPROC_GROUP_NOT_FOUND:
+               if (group_name == NULL || *group_name == '\0')
+                       abort(); /* impossible outcome */
+               printf("No such group \"%s\": %s\n", group_name, 
strerror(errno));
+               break;
+       case HXPROC_SETUID_FAILED:
+               printf("setuid %s: %s\n", user, strerror(errno));
+               break;
+       case HXPROC_SETGID_FAILED:
+               printf("setgid %s: %s\n", group, strerror(errno));
+               break;
+       case HXPROC_INITGROUPS_FAILED:
+               printf("initgroups for %s: %s\n", user, strerror(errno));
+               break;
+       case HXPROC_SU_NOOP:
+               printf("No action was performed./User identity already 
reached.\n");
+               /* fallthrough */
+       case HXPROC_SU_SUCCESS: {
+               gid_t list[64] = {-1};
+               int numgroups = getgroups(ARRAY_SIZE(list), list);
+               printf("Identity now: uid %lu euid %lu gid %lu egid %lu\n",
+                      static_cast(unsigned long, getuid()),
+                      static_cast(unsigned long, geteuid()),
+                      static_cast(unsigned long, getgid()),
+                      static_cast(unsigned long, getegid()));
+               printf("Secondary groups:");
+               for (int i = 0; i < numgroups; ++i)
+                       printf(" %lu", static_cast(unsigned long, list[i]));
+               printf("\n");
+               break;
+       }
+       }
+       return EXIT_SUCCESS;
+}
+#endif

Reply via email to