# libdaemon 0.14 (oe-core recipes-support) -- Lennart Poettering's helper library # for writing UNIX daemons. Six modules: dfork (daemonise + report status back # through a pipe), dlog (syslog/stderr), dsignal (a self-pipe signal queue), dpid # (PID-file ownership), dexec (run a child and collect its status), dnonblock. # # WHAT THIS SUITE CAN AND CANNOT SEE, stated up front so no reader over-reads it. # libdaemon ships no CLI, nothing on this image links it (its consumers -- avahi, # radvd, ifplugd, libteam -- are all unported; this port is the enabling step for # them), and the image carries no compiler, so there is no way to execute library # code from a shell suite. Everything below therefore reads the SHIPPED PAYLOAD: # the header tree, the archive's symbol index, the pkg-config contract and the # packaging policy. It is a contract test, not a functional one, and it does not # claim otherwise. # # The functional question was answered separately and does not live here: at # BRINTOS-214 an exerciser was cross-linked against this very archive and run on # this image. Five of the six modules work -- dlog, dnonblock, dsignal's setup, # dexec (/bin/true -> 0, /bin/false -> 1, a real fork+execv+reap through the # library) and daemon_fork() with its retval pipe (the child's value arrives # intact in the parent). dpid does not: daemon_pid_file_create() takes an # fcntl(F_SETLKW) advisory lock and those fail EINVAL platform-wide, because # glibc's F_SETLK is the LFS number 13 on this arch while the wasm32 syscall table # wires only sys_fcntl, whose do_fcntl() knows 5/6/7. That is the platform's, not # the port's, and it is the same defect dpkg carries dpkg-wasm-nolock.patch for. # # WHY THE WEIGHT IS ON THE SYMBOL INDEX, unlike 76-libatomic-ops.test next door. # There the library was effectively header-only and the archive a 15 KB residue, # so the headers carried the evidence. Here it is the reverse: every one of the 33 # public entry points is out-of-line in a 129 KB archive across six objects, and # nothing inlines. So the index IS the API, and a build that dropped a module # would show up here and nowhere else. . "$BST_LIB" # ONE root variable, and every path below derives from it -- including the # negative/policy checks. That is deliberate: a check that hardcodes an absolute # path inside `sh -c` cannot be exercised by a host dry run against the real # payload, so it ships never having been validated (76-libatomic-ops.test shipped # two such checks and only caught them because a dry run went looking). On target # BST_ROOT is unset and this is exactly /usr/... root=${BST_ROOT:-} inc=$root/usr/include/libdaemon libdir=$root/usr/lib lib=$libdir/libdaemon.a pc=$libdir/pkgconfig/libdaemon.pc # The whole suite is about the -dev/-staticdev payload, so gate on the archive # rather than on a tool: absent payload is a coverage prompt, not a failure. if [ ! -f "$lib" ]; then bst_skip libdaemon/present "no $lib -- libdaemon-staticdev not installed" return 0 2>/dev/null || exit 0 fi bst_ok libdaemon/present # ------------------------------------------------------------- headers --- # Seven headers, all under the libdaemon/ subdirectory. That placement is a # contract, not a detail: libdaemon.pc says plain -I${includedir}, so a consumer # writes #include and the subdirectory is load-bearing. for h in daemon.h dfork.h dlog.h dsignal.h dpid.h dexec.h dnonblock.h; do check "libdaemon/hdr-$h" test -f "$inc/$h" done check_eq libdaemon/hdr-count 7 "$(ls "$inc" 2>/dev/null | wc -l | tr -d ' ')" # Nothing at the top level of /usr/include: an install that flattened the tree # would still pass every per-header check above, so assert the negative too. check libdaemon/hdr-not-flattened sh -c "! test -e $root/usr/include/dfork.h" check libdaemon/hdr-not-flattened-daemon sh -c "! test -e $root/usr/include/daemon.h" # daemon.h is the umbrella header and pulls all six module headers, so # `#include ` alone is the whole API. Asserting each edge # rather than the file's existence is what would catch a partial install. for m in dfork dlog dpid dsignal dexec dnonblock; do check "libdaemon/umbrella-$m" grep -q "include " "$inc/daemon.h" done check_eq libdaemon/umbrella-count 6 \ "$(grep -c 'include /dev/null | tr -d ' ')" # Each header's public declarations. These are what a consumer compiles against, # so a truncated or wrong-version install shows up as a specific missing name. check libdaemon/decl-fork grep -q 'pid_t daemon_fork(void);' "$inc/dfork.h" check libdaemon/decl-retval-init grep -q 'int daemon_retval_init(void);' "$inc/dfork.h" check libdaemon/decl-retval-wait grep -q 'int daemon_retval_wait(int timeout);' "$inc/dfork.h" check libdaemon/decl-retval-send grep -q 'int daemon_retval_send(int s);' "$inc/dfork.h" check libdaemon/decl-close-all grep -q 'int daemon_close_all(int except_fd, ...);' "$inc/dfork.h" check libdaemon/decl-log grep -q 'void daemon_log(int prio, const char\* t, ...)' "$inc/dlog.h" check libdaemon/decl-logv grep -q 'void daemon_logv(int prio, const char\* t, va_list ap);' "$inc/dlog.h" check libdaemon/decl-log-use grep -q 'extern enum daemon_log_flags daemon_log_use;' "$inc/dlog.h" check libdaemon/decl-ident grep -q 'char \*daemon_ident_from_argv0(char \*argv0);' "$inc/dlog.h" check libdaemon/decl-verbosity grep -q 'void daemon_set_verbosity(int verbosity_prio);' "$inc/dlog.h" check libdaemon/decl-sig-init grep -q 'int daemon_signal_init(int s, ...);' "$inc/dsignal.h" check libdaemon/decl-sig-fd grep -q 'int daemon_signal_fd(void);' "$inc/dsignal.h" check libdaemon/decl-sig-next grep -q 'int daemon_signal_next(void);' "$inc/dsignal.h" check libdaemon/decl-pid-create grep -q 'int daemon_pid_file_create(void);' "$inc/dpid.h" check libdaemon/decl-pid-running grep -q 'pid_t daemon_pid_file_is_running(void);' "$inc/dpid.h" check libdaemon/decl-pid-proc-t grep -q 'typedef const char\* (\*daemon_pid_file_proc_t)(void);' "$inc/dpid.h" check libdaemon/decl-exec grep -q 'int daemon_exec(const char \*dir, int \*ret, const char \*prog, ...)' "$inc/dexec.h" check libdaemon/decl-nonblock grep -q 'int daemon_nonblock(int fd, int b);' "$inc/dnonblock.h" # The DAEMON_*_AVAILABLE macros are upstream's own feature-test vocabulary -- # a consumer #ifdefs on them to stay portable across libdaemon releases. All # seven are the 0.14 set; a downgrade would silently drop some. for m in DAEMON_LOGV_AVAILABLE DAEMON_SET_VERBOSITY_AVAILABLE \ DAEMON_CLOSE_ALL_AVAILABLE DAEMON_UNBLOCK_SIGS_AVAILABLE \ DAEMON_RESET_SIGS_AVAILABLE DAEMON_EXEC_AVAILABLE \ DAEMON_EXECV_AVAILABLE DAEMON_PID_FILE_KILL_WAIT_AVAILABLE; do check "libdaemon/feature-$m" grep -rq "define $m 1" "$inc/" done # daemon_log_use's flag values. They are an enum, not #defines, and a consumer # ORs them (the default is STDERR|AUTO), so the numeric values are ABI. check libdaemon/logflag-syslog grep -q 'DAEMON_LOG_SYSLOG = 1' "$inc/dlog.h" check libdaemon/logflag-stderr grep -q 'DAEMON_LOG_STDERR = 2' "$inc/dlog.h" check libdaemon/logflag-stdout grep -q 'DAEMON_LOG_STDOUT = 4' "$inc/dlog.h" check libdaemon/logflag-auto grep -q 'DAEMON_LOG_AUTO = 8' "$inc/dlog.h" # C++ consumers: every API header carries the extern "C" guard. for h in dfork.h dlog.h dsignal.h dpid.h dexec.h dnonblock.h; do check "libdaemon/cxx-guard-$h" grep -q 'extern "C"' "$inc/$h" done # --------------------------------------------------------- the archive --- # Six objects, one per source module. Read the ar SYMBOL INDEX exactly rather # than scanning a byte window: the index is member "/", its size is in its own # member header, and a window would also sweep up UNDEFINED references from the # object bodies -- which makes an absence assertion unsound (the trap # 76-libatomic-ops.test recorded after a native control run exposed it). arindex() { hdr=$(dd if="$1" bs=1 skip=8 count=60 2>/dev/null | tr -c '\40-\176' ' ') case "$(printf '%s' "$hdr" | cut -c1-16 | sed 's/ *$//')" in /) ;; *) return 1 ;; esac sz=$(printf '%s' "$hdr" | cut -c49-58 | tr -d ' ') [ -n "$sz" ] || return 1 dd if="$1" bs=1 skip=68 count="$sz" 2>/dev/null | tr -c '\40-\176\n' ' ' } idx=${TMPDIR:-/tmp}/bst-libdaemon-idx.$$ arindex "$lib" > "$idx" 2>/dev/null check libdaemon/ar-index-read test -s "$idx" # The 33 exported entry points, grouped by the object each comes from. This is # the suite's core: it is the whole public API, and it is the only place a # module dropped at link time would be visible. for s in daemon_log daemon_logv daemon_log_ident daemon_log_use \ daemon_ident_from_argv0 daemon_set_verbosity; do check "libdaemon/sym-dlog-$s" grep -qw "$s" "$idx" done for s in daemon_fork daemon_retval_init daemon_retval_done daemon_retval_send \ daemon_retval_wait daemon_close_all daemon_close_allv \ daemon_reset_sigs daemon_reset_sigsv daemon_unblock_sigs \ daemon_unblock_sigsv; do check "libdaemon/sym-dfork-$s" grep -qw "$s" "$idx" done for s in daemon_signal_init daemon_signal_done daemon_signal_fd \ daemon_signal_next daemon_signal_install; do check "libdaemon/sym-dsignal-$s" grep -qw "$s" "$idx" done for s in daemon_pid_file_create daemon_pid_file_remove \ daemon_pid_file_is_running daemon_pid_file_kill \ daemon_pid_file_kill_wait daemon_pid_file_ident \ daemon_pid_file_proc daemon_pid_file_proc_default; do check "libdaemon/sym-dpid-$s" grep -qw "$s" "$idx" done for s in daemon_exec daemon_execv; do check "libdaemon/sym-dexec-$s" grep -qw "$s" "$idx" done check libdaemon/sym-dnonblock-daemon_nonblock grep -qw daemon_nonblock "$idx" # Every declared entry point is exported and nothing else is: 33 defined symbols, # all of them daemon_*. A count check is what catches an upstream bump adding or # removing API, which the per-name checks above cannot see. check_eq libdaemon/sym-count 33 "$(tr ' ' '\n' < "$idx" | grep -c '^daemon_' | tr -d ' ')" # lock_file() and _sigfunc() are static helpers; if either ever appears in the # index the build lost its internal linkage, which is a real regression. check libdaemon/sym-no-lock_file sh -c "! grep -qw lock_file '$idx'" check libdaemon/sym-no-sigfunc sh -c "! grep -qw _sigfunc '$idx'" rm -f "$idx" # Six members, one per .c. Walk them rather than trusting arithmetic: skip the # index "/" and a long-name string table "//" if present, but NOT a "/" # member, which is a real object whose name lives in that table. armembers() { f=$1; off=8; n=0; c=0 while [ $n -lt 40 ]; do hdr=$(dd if="$f" bs=1 skip="$off" count=60 2>/dev/null | tr -c '\40-\176' ' ') nm=$(printf '%s' "$hdr" | cut -c1-16 | sed 's/ *$//') sz=$(printf '%s' "$hdr" | cut -c49-58 | tr -d ' ') [ -n "$sz" ] || break case "$nm" in /|//) ;; *) c=$((c + 1)) ;; esac off=$((off + 60 + sz)); [ $((off % 2)) -eq 1 ] && off=$((off + 1)) n=$((n + 1)) done echo "$c" } check_eq libdaemon/ar-member-count 6 "$(armembers "$lib")" # ------------------------------------------------------- the arm witness --- # The members are LLVM IR bitcode (BC C0 DE), not native objects and not finished # wasm modules -- this toolchain defers codegen to link time. It is the property a # NATIVE libdaemon.a would fail, so it is what establishes that the shipped # archive is wasm32 build output rather than a host artifact that wandered in. armagic() { f=$1; off=8; n=0 while [ $n -lt 6 ]; do hdr=$(dd if="$f" bs=1 skip="$off" count=60 2>/dev/null | tr -c '\40-\176' ' ') nm=$(printf '%s' "$hdr" | cut -c1-16 | sed 's/ *$//') sz=$(printf '%s' "$hdr" | cut -c49-58 | tr -d ' ') [ -n "$sz" ] || { echo "BADHDR"; return 1; } case "$nm" in /|//) ;; *) od -An -tx1 -j $((off + 60)) -N 4 "$f" 2>/dev/null | tr -d ' \n'; return 0 ;; esac off=$((off + 60 + sz)); [ $((off % 2)) -eq 1 ] && off=$((off + 1)) n=$((n + 1)) done echo "NOMEMBER"; return 1 } m=$(armagic "$lib") check_eq libdaemon/ar-bitcode 4243c0de "$m" # The two negatives turn the magic number above into a statement about the arm. if [ "$m" = "7f454c46" ]; then bst_notok libdaemon/ar-not-elf "members are ELF -- this is a NATIVE build, not wasm32" else bst_ok libdaemon/ar-not-elf; fi if [ "$m" = "0061736d" ]; then bst_notok libdaemon/ar-not-wasm-module "members are finished wasm modules, not bitcode" else bst_ok libdaemon/ar-not-wasm-module; fi check libdaemon/ar-magic grep -q '!' "$lib" # A size window, not an exact size: bitcode size moves with the toolchain, but an # archive that lost five of its six objects would fall straight through the floor. sz=$(wc -c < "$lib" | tr -d ' ') if [ "$sz" -ge 60000 ] && [ "$sz" -le 400000 ]; then bst_ok libdaemon/ar-size-window else bst_notok libdaemon/ar-size-window "libdaemon.a is $sz bytes, want 60000..400000"; fi # ------------------------------------------------------------ pkg-config --- check libdaemon/pc-present test -f "$pc" check_eq libdaemon/pc-version 0.14 "$(sed -n 's/^Version: *//p' "$pc" 2>/dev/null)" check_eq libdaemon/pc-name libdaemon "$(sed -n 's/^Name: *//p' "$pc" 2>/dev/null)" check_eq libdaemon/pc-prefix /usr "$(sed -n 's/^prefix=//p' "$pc" 2>/dev/null)" check_eq libdaemon/pc-libdir /usr/lib "$(sed -n 's/^libdir=//p' "$pc" 2>/dev/null)" check_eq libdaemon/pc-includedir '${prefix}/include' "$(sed -n 's/^includedir=//p' "$pc" 2>/dev/null)" check libdaemon/pc-libs grep -q '^Libs: -L${libdir} -ldaemon$' "$pc" check libdaemon/pc-cflags grep -q '^Cflags: -D_REENTRANT -I${includedir}$' "$pc" # No unsubstituted autoconf placeholder survived into the shipped file. check libdaemon/pc-no-at-vars sh -c "! grep -q '@[A-Za-z_]*@' '$pc'" # The -uninstalled variant is a build-tree artifact and must not ship. check libdaemon/pc-no-uninstalled sh -c "! test -e $libdir/pkgconfig/libdaemon-uninstalled.pc" # The queries a consumer's PKG_CHECK_MODULES actually issues, against our real # .pc. This is the one register in which a packaging mistake here would bite a # future avahi/radvd port, and it is a real program parsing our real artifact. if command -v pkgconf >/dev/null 2>&1; then check_eq libdaemon/pkgconf-exists 0 "$(pkgconf --exists libdaemon >/dev/null 2>&1; echo $?)" check_eq libdaemon/pkgconf-modversion 0.14 "$(pkgconf --modversion libdaemon 2>/dev/null)" check_eq libdaemon/pkgconf-libdir /usr/lib "$(pkgconf --variable=libdir libdaemon 2>/dev/null)" check_eq libdaemon/pkgconf-includedir /usr/include "$(pkgconf --variable=includedir libdaemon 2>/dev/null)" # --libs drops the system -L and keeps the link flag. check_eq libdaemon/pkgconf-libs "-ldaemon" "$(pkgconf --libs libdaemon 2>/dev/null)" # --cflags is NOT empty here, and that is the point. pkgconf is built with # --with-system-includedir=/usr/include so it filters -I/usr/include, but # -D_REENTRANT is not a system directory and survives. 76-libatomic-ops.test's # neighbouring assertion is the opposite (empty is correct there, because its # .pc carries only the -I); copying either one across would be wrong. check_eq libdaemon/pkgconf-cflags "-D_REENTRANT" "$(pkgconf --cflags libdaemon 2>/dev/null)" # With filtering off, the system paths reappear -- which is what makes "empty" # distinguishable from "found nothing" in the assertions above. check_out libdaemon/pkgconf-keep-cflags "-I/usr/include" \ pkgconf --keep-system-cflags --cflags libdaemon check_out libdaemon/pkgconf-keep-libs "-L/usr/lib" \ pkgconf --keep-system-libs --libs libdaemon check_eq libdaemon/pkgconf-atleast 0 "$(pkgconf --atleast-version=0.14 libdaemon >/dev/null 2>&1; echo $?)" check_eq libdaemon/pkgconf-max 1 "$(pkgconf --max-version=0.13 libdaemon >/dev/null 2>&1; echo $?)" else bst_skip libdaemon/pkgconf "pkgconf not installed" fi # ---------------------------------------------------------------- policy --- # Static-only: the distro builds every library with --disable-shared, so a .so of # any flavour appearing here means the arm's link policy changed under us. check libdaemon/policy-no-so sh -c "! ls $libdir/libdaemon.so* >/dev/null 2>&1" check libdaemon/policy-no-la sh -c "! test -e $libdir/libdaemon.la" # libtool's .lai/.la bookkeeping and the doc package are deliberately not shipped. check libdaemon/policy-no-doc sh -c "! test -e $root/usr/share/doc/libdaemon/README.html" # The empty main package emits no .deb at all (no ALLOW_EMPTY:${PN} in this # recipe, unlike libatomic-ops), so nothing outside -dev/-staticdev arrives and # there is no stray runtime directory. check libdaemon/policy-no-bin sh -c "! test -e $root/usr/bin/testd" check libdaemon/policy-no-libdir-subdir sh -c "! test -d $libdir/libdaemon"