chiark / gitweb /
Prep v233.3: Add all possible coverage tests for elogind
authorSven Eden <yamakuzure@gmx.net>
Wed, 19 Jul 2017 09:55:18 +0000 (11:55 +0200)
committerSven Eden <yamakuzure@gmx.net>
Thu, 20 Jul 2017 06:29:14 +0000 (08:29 +0200)
55 files changed:
Makefile-man.am
Makefile.am
cb/.gitignore [new file with mode: 0644]
cb/elogind.cbp
configure.ac
po/.gitignore
src/.gitignore
src/libelogind/sd-bus/test-bus-error.c [new file with mode: 0644]
src/libelogind/sd-bus/test-bus-introspect.c [new file with mode: 0644]
src/libelogind/sd-bus/test-bus-match.c [new file with mode: 0644]
src/libelogind/sd-bus/test-bus-server.c [new file with mode: 0644]
src/libelogind/sd-bus/test-bus-signature.c [new file with mode: 0644]
src/libelogind/sd-event/test-event.c [new file with mode: 0644]
src/test/.gitignore [new file with mode: 0644]
src/test/Makefile [new symlink]
src/test/test-alloc-util.c [new file with mode: 0644]
src/test/test-cgroup.c [new file with mode: 0644]
src/test/test-conf-files.c [new file with mode: 0644]
src/test/test-conf-parser.c [new file with mode: 0644]
src/test/test-copy.c [new file with mode: 0644]
src/test/test-ellipsize.c [new file with mode: 0644]
src/test/test-escape.c [new file with mode: 0644]
src/test/test-exec-util.c [new file with mode: 0644]
src/test/test-extract-word.c [new file with mode: 0644]
src/test/test-fd-util.c [new file with mode: 0644]
src/test/test-fs-util.c [new file with mode: 0644]
src/test/test-hash.c [new file with mode: 0644]
src/test/test-hashmap-plain.c [new file with mode: 0644]
src/test/test-hashmap.c [new file with mode: 0644]
src/test/test-helper.h [new file with mode: 0644]
src/test/test-hexdecoct.c [new file with mode: 0644]
src/test/test-id128.c [new file with mode: 0644]
src/test/test-io-util.c [new file with mode: 0644]
src/test/test-ipcrm.c [new file with mode: 0644]
src/test/test-list.c [new file with mode: 0644]
src/test/test-locale-util.c [new file with mode: 0644]
src/test/test-log.c [new file with mode: 0644]
src/test/test-parse-util.c [new file with mode: 0644]
src/test/test-path-util.c [new file with mode: 0644]
src/test/test-prioq.c [new file with mode: 0644]
src/test/test-proc-cmdline.c [new file with mode: 0644]
src/test/test-process-util.c [new file with mode: 0644]
src/test/test-selinux.c [new file with mode: 0644]
src/test/test-set.c [new file with mode: 0644]
src/test/test-signal-util.c [new file with mode: 0644]
src/test/test-siphash24.c [new file with mode: 0644]
src/test/test-sizeof.c [new file with mode: 0644]
src/test/test-stat-util.c [new file with mode: 0644]
src/test/test-string-util.c [new file with mode: 0644]
src/test/test-strip-tab-ansi.c [new file with mode: 0644]
src/test/test-unaligned.c [new file with mode: 0644]
src/test/test-user-util.c [new file with mode: 0644]
src/test/test-utf8.c [new file with mode: 0644]
src/test/test-util.c [new file with mode: 0644]
src/test/test-verbs.c [new file with mode: 0644]

index be18d79..f43808b 100644 (file)
@@ -12,6 +12,7 @@ MANPAGES += \
        man/loginctl.1 \
        man/logind.conf.5 \
        man/sd-event.3 \
+       man/sd_booted.3 \
        man/sd_event_add_io.3 \
        man/sd_event_exit.3 \
        man/sd_event_now.3 \
@@ -311,6 +312,7 @@ EXTRA_DIST += \
        man/logind.conf.xml \
        man/pam_elogind.xml \
        man/sd-event.xml \
+       man/sd_booted.xml \
        man/sd_event_add_io.xml \
        man/sd_event_exit.xml \
        man/sd_event_now.xml \
index 50d5f24..aae170c 100644 (file)
@@ -101,9 +101,14 @@ dist_rootlibexec_DATA =
 rootlib_LTLIBRARIES =
 tests=
 manual_tests =
+TEST_DATA_FILES =
 if ENABLE_TESTS
-noinst_PROGRAMS = $(manual_tests) $(tests)
+noinst_PROGRAMS = $(manual_tests) $(tests) $(unsafe_tests)
 TESTS = $(tests)
+if ENABLE_UNSAFE_TESTS
+TESTS += \
+       $(unsafe_tests)
+endif
 else
 noinst_PROGRAMS =
 TESTS =
@@ -135,6 +140,7 @@ AM_CPPFLAGS = \
        -DSYSTEMD_CGROUP_CONTROLLER_LEGACY=\"name=$(CGROUP_CONTROLLER)\" \
        -DSYSTEMD_CGROUP_CONTROLLER_HYBRID=\"name=$(CGROUP_CONTROLLER)\" \
        -DSYSTEMD_CGROUP_AGENT_PATH=\"$(rootlibexecdir)/elogind-cgroups-agent\" \
+       -DSYSTEMD_BINARY_PATH=\"$(rootlibexecdir)/elogind\" \
        -DUDEVLIBEXECDIR=\"$(udevlibexecdir)\" \
        -DPOLKIT_AGENT_BINARY_PATH=\"$(PKTTYAGENT)\" \
        -DSYSTEM_SLEEP_PATH=\"$(systemsleepdir)\" \
@@ -145,6 +151,7 @@ AM_CPPFLAGS = \
        -DLIBDIR=\"$(libdir)\" \
        -DROOTLIBDIR=\"$(rootlibdir)\" \
        -DROOTLIBEXECDIR=\"$(rootlibexecdir)\" \
+       -DTEST_DIR=\"$(abs_top_srcdir)/test\" \
        -I $(top_srcdir)/src \
        -I $(top_builddir)/src/basic \
        -I $(top_srcdir)/src/basic \
@@ -197,6 +204,10 @@ AM_V_RM_0 = @echo "  RM      " $@;
 
 # ------------------------------------------------------------------------------
 rootbin_PROGRAMS =
+rootlibexec_PROGRAMS = \
+       elogind \
+       elogind-cgroups-agent
+
 pkglibexec_PROGRAMS =
 
 dist_doc_DATA = \
@@ -459,6 +470,8 @@ libshared_la_SOURCES = \
        src/shared/sleep-config.h \
        src/shared/spawn-polkit-agent.c \
        src/shared/spawn-polkit-agent.h \
+       src/shared/tests.h \
+       src/shared/tests.c \
        src/shared/nsflags.h \
        src/shared/nsflags.c
 
@@ -549,6 +562,334 @@ src/basic/errno-to-name.h: src/basic/errno-list.txt
        $(AM_V_at)$(MKDIR_P) $(dir $@)
        $(AM_V_GEN)$(AWK) 'BEGIN{ print "static const char* const errno_names[] = { "} !/EDEADLOCK/ && !/EWOULDBLOCK/ && !/ENOTSUP/ { printf "[%s] = \"%s\",\n", $$1, $$1 } END{print "};"}' <$< >$@
 
+# ------------------------------------------------------------------------------
+
+manual_tests += \
+       test-cgroup
+
+unsafe_tests = \
+       test-ipcrm
+
+tests += \
+       test-log \
+       test-path-util \
+       test-siphash24 \
+       test-utf8 \
+       test-ellipsize \
+       test-util \
+       test-exec-util \
+       test-hexdecoct \
+       test-escape \
+       test-alloc-util \
+       test-proc-cmdline \
+       test-io-util \
+       test-fs-util \
+       test-stat-util \
+       test-fd-util \
+       test-string-util \
+       test-extract-word \
+       test-parse-util \
+       test-user-util \
+       test-process-util \
+       test-strip-tab-ansi \
+       test-prioq \
+       test-hashmap \
+       test-set \
+       test-list \
+       test-unaligned \
+       test-conf-files \
+       test-conf-parser \
+       test-locale-util \
+       test-copy \
+       test-verbs \
+       test-signal-util \
+       test-selinux \
+       test-sizeof
+
+TEST_DATA_FILES += \
+       test/bus-policy/hello.conf \
+       test/bus-policy/methods.conf \
+       test/bus-policy/ownerships.conf \
+       test/bus-policy/signals.conf \
+       test/bus-policy/check-own-rules.conf \
+       test/bus-policy/many-rules.conf \
+       test/bus-policy/test.conf
+
+
+EXTRA_DIST += \
+       src/test/test-helper.h
+
+test_utf8_SOURCES = \
+       src/test/test-utf8.c
+
+test_utf8_LDADD = \
+       libelogind-shared.la
+
+test_locale_util_SOURCES = \
+       src/test/test-locale-util.c
+
+test_locale_util_LDADD = \
+       libelogind-shared.la
+
+test_copy_SOURCES = \
+       src/test/test-copy.c
+
+# Link statically to ensure file is large
+test_copy_LDADD = \
+       libshared.la
+
+test_util_SOURCES = \
+       src/test/test-util.c
+
+test_util_LDADD = \
+       libelogind-shared.la
+
+test_exec_util_SOURCES = \
+       src/test/test-exec-util.c
+
+test_exec_util_LDADD = \
+       libelogind-shared.la
+
+test_hexdecoct_SOURCES = \
+       src/test/test-hexdecoct.c
+
+test_hexdecoct_LDADD = \
+       libelogind-shared.la
+
+test_alloc_util_SOURCES = \
+       src/test/test-alloc-util.c
+
+test_alloc_util_LDADD = \
+       libelogind-shared.la
+
+test_io_util_SOURCES = \
+       src/test/test-io-util.c
+
+test_io_util_LDADD = \
+       libelogind-shared.la
+
+test_fs_util_SOURCES = \
+       src/test/test-fs-util.c
+
+test_fs_util_LDADD = \
+       libelogind-shared.la
+
+test_proc_cmdline_SOURCES = \
+       src/test/test-proc-cmdline.c
+
+test_proc_cmdline_LDADD = \
+       libelogind-shared.la
+
+test_fd_util_SOURCES = \
+       src/test/test-fd-util.c
+
+test_fd_util_LDADD = \
+       libelogind-shared.la
+
+test_stat_util_SOURCES = \
+       src/test/test-stat-util.c
+
+test_stat_util_LDADD = \
+       libelogind-shared.la
+
+test_escape_SOURCES = \
+       src/test/test-escape.c
+
+test_escape_LDADD = \
+       libelogind-shared.la
+
+test_string_util_SOURCES = \
+       src/test/test-string-util.c
+
+test_string_util_LDADD = \
+       libelogind-shared.la
+
+test_extract_word_SOURCES = \
+       src/test/test-extract-word.c
+
+test_extract_word_LDADD = \
+       libelogind-shared.la
+
+test_parse_util_SOURCES = \
+       src/test/test-parse-util.c
+
+test_parse_util_LDADD = \
+       libelogind-shared.la
+
+test_user_util_SOURCES = \
+       src/test/test-user-util.c
+
+test_user_util_LDADD = \
+       libelogind-shared.la
+
+test_process_util_SOURCES = \
+       src/test/test-process-util.c
+
+test_process_util_LDADD = \
+       libelogind-shared.la
+
+test_verbs_SOURCES = \
+       src/test/test-verbs.c
+
+test_verbs_LDADD = \
+       libelogind-shared.la
+
+test_signal_util_SOURCES = \
+       src/test/test-signal-util.c
+
+test_signal_util_LDADD = \
+       libelogind-shared.la
+
+test_selinux_SOURCES = \
+       src/test/test-selinux.c
+
+test_selinux_LDADD = \
+       libelogind-shared.la
+
+test_sizeof_SOURCES = \
+       src/test/test-sizeof.c
+
+test_sizeof_LDADD = \
+       libelogind-shared.la
+
+BUILT_SOURCES += \
+       src/test/test-hashmap-ordered.c
+
+src/test/test-hashmap-ordered.c: src/test/test-hashmap-plain.c
+       $(AM_V_at)$(MKDIR_P) $(dir $@)
+       $(AM_V_GEN)$(AWK) 'BEGIN { print "/* GENERATED FILE */\n#define ORDERED" } \
+                          { if (!match($$0, "^#include"))          \
+                                gsub(/hashmap/, "ordered_hashmap"); \
+                            gsub(/HASHMAP/, "ORDERED_HASHMAP");     \
+                            gsub(/Hashmap/, "OrderedHashmap");      \
+                            print }' <$< >$@
+
+nodist_test_hashmap_SOURCES = \
+       src/test/test-hashmap-ordered.c
+
+test_hashmap_SOURCES = \
+       src/test/test-hashmap.c \
+       src/test/test-hashmap-plain.c
+
+test_hashmap_LDADD = \
+       libelogind-shared.la
+
+test_set_SOURCES = \
+       src/test/test-set.c
+
+test_set_LDADD = \
+       libelogind-shared.la
+
+test_list_SOURCES = \
+       src/test/test-list.c
+
+test_list_LDADD = \
+       libelogind-shared.la
+
+test_unaligned_LDADD = \
+       libelogind-shared.la
+
+test_unaligned_SOURCES = \
+       src/test/test-unaligned.c
+
+test_prioq_SOURCES = \
+       src/test/test-prioq.c
+
+test_prioq_LDADD = \
+       libelogind-shared.la
+
+test_log_SOURCES = \
+       src/test/test-log.c
+
+test_log_LDADD = \
+       libelogind-shared.la
+
+test_ipcrm_SOURCES = \
+       src/test/test-ipcrm.c
+
+test_ipcrm_LDADD = \
+       libelogind-shared.la
+
+test_ellipsize_SOURCES = \
+       src/test/test-ellipsize.c
+
+test_ellipsize_LDADD = \
+       libelogind-shared.la
+
+test_strip_tab_ansi_SOURCES = \
+       src/test/test-strip-tab-ansi.c
+
+test_strip_tab_ansi_LDADD = \
+       libelogind-shared.la
+
+test_cgroup_SOURCES = \
+       src/test/test-cgroup.c
+
+test_cgroup_LDADD = \
+       libelogind-shared.la
+
+test_path_util_SOURCES = \
+       src/test/test-path-util.c
+
+test_path_util_LDADD = \
+       libelogind-shared.la
+
+test_siphash24_SOURCES = \
+       src/test/test-siphash24.c
+
+test_siphash24_LDADD = \
+       libelogind-shared.la
+
+test_conf_files_SOURCES = \
+       src/test/test-conf-files.c
+
+test_conf_files_LDADD = \
+       libelogind-shared.la
+
+test_conf_parser_SOURCES = \
+       src/test/test-conf-parser.c
+
+test_conf_parser_LDADD = \
+       libelogind-shared.la
+
+# ------------------------------------------------------------------------------
+## .PHONY so it always rebuilds it
+.PHONY: coverage lcov-run lcov-report coverage-sync
+
+# run lcov from scratch, always
+coverage: all
+       $(MAKE) lcov-run
+       $(MAKE) lcov-report
+
+coverage_dir = coverage
+coverage_opts = --base-directory $(srcdir) --directory $(builddir) --rc 'geninfo_adjust_src_path=$(abspath $(srcdir))=>$(abspath $(builddir))'
+
+if ENABLE_COVERAGE
+# reset run coverage tests
+lcov-run:
+       @rm -rf $(coverage_dir)
+       lcov $(coverage_opts) --zerocounters
+       -$(MAKE) check
+
+# generate report based on current coverage data
+lcov-report:
+       $(MKDIR_P) $(coverage_dir)
+       lcov $(coverage_opts) --compat-libtool --capture --no-external \
+               | sed 's|$(abspath $(builddir))|$(abspath $(srcdir))|' > $(coverage_dir)/.lcov.info
+       lcov --remove $(coverage_dir)/.lcov.info --output-file $(coverage_dir)/.lcov-clean.info 'test-*'
+       genhtml -t "systemd test coverage" -o $(coverage_dir) $(coverage_dir)/.lcov-clean.info
+       @echo "Coverage report generated in $(abs_builddir)/$(coverage_dir)/index.html"
+
+# lcov doesn't work properly with vpath builds, make sure that bad
+# output is not uploaded by mistake.
+coverage-sync: coverage
+       test "$(builddir)" = "$(srcdir)"
+       rsync -rlv --delete --omit-dir-times coverage/ $(www_target)/coverage
+
+else
+lcov-run lcov-report:
+       echo "Need to reconfigure with --enable-coverage"
+endif
 
 # ------------------------------------------------------------------------------
 
@@ -669,6 +1010,71 @@ UNINSTALL_EXEC_HOOKS += header-uninstall-hook
 rootlib_LTLIBRARIES += \
        libelogind.la
 
+tests += \
+       test-bus-signature \
+       test-bus-server \
+       test-bus-match \
+       test-bus-introspect \
+       test-bus-error \
+       test-event
+
+test_bus_signature_SOURCES = \
+       src/libelogind/sd-bus/test-bus-signature.c
+
+test_bus_signature_LDADD = \
+       libelogind-shared.la
+
+test_bus_server_SOURCES = \
+       src/libelogind/sd-bus/test-bus-server.c
+
+test_bus_server_LDADD = \
+       libelogind-shared.la
+
+test_bus_error_SOURCES = \
+       src/libelogind/sd-bus/test-bus-error.c
+
+# Link statically because this test uses BUS_ERROR_MAP_ELF_REGISTER
+test_bus_error_LDADD = \
+       libshared.la
+
+test_bus_match_SOURCES = \
+       src/libelogind/sd-bus/test-bus-match.c
+
+test_bus_match_LDADD = \
+       libelogind-shared.la
+
+test_bus_introspect_SOURCES = \
+       src/libelogind/sd-bus/test-bus-introspect.c
+
+test_bus_introspect_LDADD = \
+       libelogind-shared.la
+
+test_event_SOURCES = \
+       src/libelogind/sd-event/test-event.c
+
+test_event_LDADD = \
+       libelogind-shared.la
+
+# ------------------------------------------------------------------------------
+test_id128_SOURCES = \
+       src/test/test-id128.c
+
+test_id128_LDADD = \
+       libelogind-shared.la
+
+tests += \
+       test-id128
+
+# ------------------------------------------------------------------------------
+test_hash_SOURCES = \
+       src/test/test-hash.c
+
+test_hash_LDADD = \
+       libelogind-shared.la
+
+tests += \
+       test-hash
+
 # ------------------------------------------------------------------------------
 elogind_SOURCES = \
        src/login/logind.c \
@@ -1099,10 +1505,10 @@ install-tree: all
        tree $(abs_srcdir)/install-tree
 
 # Let's run all tests of the test suite, but under valgrind. Let's
-# exclude the one perl script we have in there
+# exclude perl/python/shell scripts we have in there
 .PHONY: valgrind-tests
 valgrind-tests: $(TESTS)
-       $(AM_V_GEN)for f in $(filter-out %.pl, $^); do \
+       $(AM_V_GEN)for f in $(filter-out %.pl %.py, $^); do \
                if $(LIBTOOL) --mode=execute file $$f | grep -q shell; then \
                echo -e "$${x}Skipping non-binary $$f"; else \
                echo -e "$${x}Running $$f"; \
@@ -1181,9 +1587,38 @@ BUILT_SOURCES += \
 tests += \
        test-libelogind-sym
 
+.PHONY: install-tests
+install-tests: $(tests) $(TEST_DATA_FILES)
+       for f in $(tests); do \
+           if [ -x $(top_builddir)/.libs/$$f ]; then \
+               install -D -m 755 $(top_builddir)/.libs/$$f $(DESTDIR)/$(testsdir)/$$f; \
+           else \
+               install -D -m 755 $(top_builddir)/$$f $(DESTDIR)/$(testsdir)/$$f; \
+           fi; \
+       done
+       for f in $(TEST_DATA_FILES); do \
+           install -D -m 644 $(top_srcdir)/$$f $(DESTDIR)/$(testsdir)/testdata/$${f#test/}; \
+       done
+
 .PHONY: cppcheck
 cppcheck:
-       cppcheck --enable=all -q $(top_srcdir)
+       cppcheck --enable=all -q \
+       -I $(top_srcdir)/src \
+       -I $(top_builddir)/src/basic \
+       -I $(top_srcdir)/src/basic \
+       -I $(top_srcdir)/src/core \
+       -I $(top_srcdir)/src/shared \
+       -I $(top_builddir)/src/shared \
+       -I $(top_srcdir)/src/login \
+       -I $(top_srcdir)/src/systemd \
+       -I $(top_srcdir)/src/libelogind/sd-bus \
+       -I $(top_srcdir)/src/libelogind/sd-event \
+       -I $(top_srcdir)/src/libelogind/sd-login \
+       -I $(top_srcdir)/src/libelogind/sd-id128 \
+       -I $(top_srcdir)/src/update-utmp \
+       -I $(top_srcdir)/src/sleep \
+       --force \
+       $(top_srcdir)
 
 # Used to extract compile flags for YCM.
 print-%:
diff --git a/cb/.gitignore b/cb/.gitignore
new file mode 100644 (file)
index 0000000..2d6f0ae
--- /dev/null
@@ -0,0 +1 @@
+.cccc
index 9705684..f92a5fe 100644 (file)
                <Unit filename="../src/libelogind/sd-bus/sd-bus.c">
                        <Option compilerVar="CC" />
                </Unit>
+               <Unit filename="../src/libelogind/sd-bus/test-bus-error.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/libelogind/sd-bus/test-bus-introspect.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/libelogind/sd-bus/test-bus-match.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/libelogind/sd-bus/test-bus-server.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/libelogind/sd-bus/test-bus-signature.c">
+                       <Option compilerVar="CC" />
+               </Unit>
                <Unit filename="../src/libelogind/sd-daemon/sd-daemon.c">
                        <Option compilerVar="CC" />
                </Unit>
                <Unit filename="../src/libelogind/sd-event/sd-event.c">
                        <Option compilerVar="CC" />
                </Unit>
+               <Unit filename="../src/libelogind/sd-event/test-event.c">
+                       <Option compilerVar="CC" />
+               </Unit>
                <Unit filename="../src/libelogind/sd-id128/id128-util.c">
                        <Option compilerVar="CC" />
                </Unit>
                </Unit>
                <Unit filename="../src/shared/spawn-polkit-agent.h" />
                <Unit filename="../src/shared/test-tables.h" />
+               <Unit filename="../src/shared/tests.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/shared/tests.h" />
                <Unit filename="../src/shared/udev-util.h" />
                <Unit filename="../src/shared/utmp-wtmp.c">
                        <Option compilerVar="CC" />
                <Unit filename="../src/systemd/sd-id128.h" />
                <Unit filename="../src/systemd/sd-login.h" />
                <Unit filename="../src/systemd/sd-messages.h" />
+               <Unit filename="../src/test/test-alloc-util.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/test/test-cgroup.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/test/test-conf-files.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/test/test-conf-parser.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/test/test-copy.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/test/test-ellipsize.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/test/test-escape.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/test/test-exec-util.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/test/test-extract-word.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/test/test-fd-util.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/test/test-fs-util.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/test/test-hash.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/test/test-hashmap-ordered.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/test/test-hashmap-plain.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/test/test-hashmap.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/test/test-helper.h" />
+               <Unit filename="../src/test/test-hexdecoct.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/test/test-id128.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/test/test-io-util.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/test/test-ipcrm.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/test/test-list.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/test/test-locale-util.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/test/test-log.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/test/test-parse-util.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/test/test-path-util.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/test/test-prioq.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/test/test-proc-cmdline.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/test/test-process-util.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/test/test-selinux.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/test/test-set.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/test/test-signal-util.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/test/test-siphash24.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/test/test-sizeof.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/test/test-stat-util.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/test/test-string-util.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/test/test-strip-tab-ansi.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/test/test-unaligned.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/test/test-user-util.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/test/test-utf8.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/test/test-util.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="../src/test/test-verbs.c">
+                       <Option compilerVar="CC" />
+               </Unit>
                <Unit filename="../src/update-utmp/update-utmp.c">
                        <Option compilerVar="CC" />
                </Unit>
index 281570e..aa3cf86 100644 (file)
@@ -764,9 +764,10 @@ AS_IF([test "x$0" != "x./configure"], [
 ])
 
 AC_ARG_ENABLE(tests,
-        [AS_HELP_STRING([--disable-tests], [disable tests, or enable extra tests with =unsafe])],
+        [AC_HELP_STRING([--disable-tests], [disable tests, or enable extra tests with =unsafe])],
         enable_tests=$enableval, enable_tests=yes)
-AM_CONDITIONAL(ENABLE_TESTS, [test x$enable_tests = xyes])
+AM_CONDITIONAL(ENABLE_TESTS, [test x$enable_tests = xyes -o x$enable_tests = xunsafe])
+AM_CONDITIONAL(ENABLE_UNSAFE_TESTS, [test x$enable_tests = xunsafe])
 
 AC_ARG_ENABLE(debug,
         [AC_HELP_STRING([--enable-debug@<:@=LIST@:>@], [enable extra debugging (elogind,hashmap,mmap-cache)])],
index 0d1d4b0..1cdd4b6 100644 (file)
@@ -4,3 +4,6 @@ Makefile.in.in
 Makefile
 systemd.pot
 /*.gmo
+
+# elogind specific ignores
+elogind.pot
index bea7268..ab44b03 100644 (file)
@@ -30,7 +30,6 @@ org.freedesktop.systemd1.policy
 /gpt-auto-generator
 /journal
 /socket-proxy
-/test
 /cgtop
 /locale
 /cryptsetup
diff --git a/src/libelogind/sd-bus/test-bus-error.c b/src/libelogind/sd-bus/test-bus-error.c
new file mode 100644 (file)
index 0000000..f7c889b
--- /dev/null
@@ -0,0 +1,234 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2013 Lennart Poettering
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "sd-bus.h"
+
+#include "bus-common-errors.h"
+#include "bus-error.h"
+#include "bus-util.h"
+#include "errno-list.h"
+
+static void test_error(void) {
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL, second = SD_BUS_ERROR_NULL;
+        const sd_bus_error const_error = SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FILE_EXISTS, "const error");
+        const sd_bus_error temporarily_const_error = {
+                .name = SD_BUS_ERROR_ACCESS_DENIED,
+                .message = "oh! no",
+                ._need_free = -1
+        };
+
+        assert_se(!sd_bus_error_is_set(&error));
+        assert_se(sd_bus_error_set(&error, SD_BUS_ERROR_NOT_SUPPORTED, "xxx") == -EOPNOTSUPP);
+        assert_se(streq(error.name, SD_BUS_ERROR_NOT_SUPPORTED));
+        assert_se(streq(error.message, "xxx"));
+        assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_NOT_SUPPORTED));
+        assert_se(sd_bus_error_get_errno(&error) == EOPNOTSUPP);
+        assert_se(sd_bus_error_is_set(&error));
+        sd_bus_error_free(&error);
+
+        /* Check with no error */
+        assert_se(!sd_bus_error_is_set(&error));
+        assert_se(sd_bus_error_setf(&error, NULL, "yyy %i", -1) == 0);
+        assert_se(error.name == NULL);
+        assert_se(error.message == NULL);
+        assert_se(!sd_bus_error_has_name(&error, SD_BUS_ERROR_FILE_NOT_FOUND));
+        assert_se(sd_bus_error_get_errno(&error) == 0);
+        assert_se(!sd_bus_error_is_set(&error));
+
+        assert_se(sd_bus_error_setf(&error, SD_BUS_ERROR_FILE_NOT_FOUND, "yyy %i", -1) == -ENOENT);
+        assert_se(streq(error.name, SD_BUS_ERROR_FILE_NOT_FOUND));
+        assert_se(streq(error.message, "yyy -1"));
+        assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_FILE_NOT_FOUND));
+        assert_se(sd_bus_error_get_errno(&error) == ENOENT);
+        assert_se(sd_bus_error_is_set(&error));
+
+        assert_se(!sd_bus_error_is_set(&second));
+        assert_se(second._need_free == 0);
+        assert_se(error._need_free > 0);
+        assert_se(sd_bus_error_copy(&second, &error) == -ENOENT);
+        assert_se(second._need_free > 0);
+        assert_se(streq(error.name, second.name));
+        assert_se(streq(error.message, second.message));
+        assert_se(sd_bus_error_get_errno(&second) == ENOENT);
+        assert_se(sd_bus_error_has_name(&second, SD_BUS_ERROR_FILE_NOT_FOUND));
+        assert_se(sd_bus_error_is_set(&second));
+
+        sd_bus_error_free(&error);
+        sd_bus_error_free(&second);
+
+        assert_se(!sd_bus_error_is_set(&second));
+        assert_se(const_error._need_free == 0);
+        assert_se(sd_bus_error_copy(&second, &const_error) == -EEXIST);
+        assert_se(second._need_free == 0);
+        assert_se(streq(const_error.name, second.name));
+        assert_se(streq(const_error.message, second.message));
+        assert_se(sd_bus_error_get_errno(&second) == EEXIST);
+        assert_se(sd_bus_error_has_name(&second, SD_BUS_ERROR_FILE_EXISTS));
+        assert_se(sd_bus_error_is_set(&second));
+        sd_bus_error_free(&second);
+
+        assert_se(!sd_bus_error_is_set(&second));
+        assert_se(temporarily_const_error._need_free < 0);
+        assert_se(sd_bus_error_copy(&second, &temporarily_const_error) == -EACCES);
+        assert_se(second._need_free > 0);
+        assert_se(streq(temporarily_const_error.name, second.name));
+        assert_se(streq(temporarily_const_error.message, second.message));
+        assert_se(sd_bus_error_get_errno(&second) == EACCES);
+        assert_se(sd_bus_error_has_name(&second, SD_BUS_ERROR_ACCESS_DENIED));
+        assert_se(sd_bus_error_is_set(&second));
+
+        assert_se(!sd_bus_error_is_set(&error));
+        assert_se(sd_bus_error_set_const(&error, "System.Error.EUCLEAN", "Hallo") == -EUCLEAN);
+        assert_se(streq(error.name, "System.Error.EUCLEAN"));
+        assert_se(streq(error.message, "Hallo"));
+        assert_se(sd_bus_error_has_name(&error, "System.Error.EUCLEAN"));
+        assert_se(sd_bus_error_get_errno(&error) == EUCLEAN);
+        assert_se(sd_bus_error_is_set(&error));
+        sd_bus_error_free(&error);
+
+        assert_se(!sd_bus_error_is_set(&error));
+        assert_se(sd_bus_error_set_errno(&error, EBUSY) == -EBUSY);
+        assert_se(streq(error.name, "System.Error.EBUSY"));
+        assert_se(streq(error.message, strerror(EBUSY)));
+        assert_se(sd_bus_error_has_name(&error, "System.Error.EBUSY"));
+        assert_se(sd_bus_error_get_errno(&error) == EBUSY);
+        assert_se(sd_bus_error_is_set(&error));
+        sd_bus_error_free(&error);
+
+        assert_se(!sd_bus_error_is_set(&error));
+        assert_se(sd_bus_error_set_errnof(&error, EIO, "Waldi %c", 'X') == -EIO);
+        assert_se(streq(error.name, SD_BUS_ERROR_IO_ERROR));
+        assert_se(streq(error.message, "Waldi X"));
+        assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_IO_ERROR));
+        assert_se(sd_bus_error_get_errno(&error) == EIO);
+        assert_se(sd_bus_error_is_set(&error));
+        sd_bus_error_free(&error);
+
+        /* Check with no error */
+        assert_se(!sd_bus_error_is_set(&error));
+        assert_se(sd_bus_error_set_errnof(&error, 0, "Waldi %c", 'X') == 0);
+        assert_se(error.name == NULL);
+        assert_se(error.message == NULL);
+        assert_se(!sd_bus_error_has_name(&error, SD_BUS_ERROR_IO_ERROR));
+        assert_se(sd_bus_error_get_errno(&error) == 0);
+        assert_se(!sd_bus_error_is_set(&error));
+}
+
+extern const sd_bus_error_map __start_BUS_ERROR_MAP[];
+extern const sd_bus_error_map __stop_BUS_ERROR_MAP[];
+
+static void dump_mapping_table(void) {
+        const sd_bus_error_map *m;
+
+        printf("----- errno mappings ------\n");
+        m = __start_BUS_ERROR_MAP;
+        while (m < __stop_BUS_ERROR_MAP) {
+
+                if (m->code == BUS_ERROR_MAP_END_MARKER) {
+                        m = ALIGN8_PTR(m+1);
+                        continue;
+                }
+
+                printf("%s -> %i/%s\n", strna(m->name), m->code, strna(errno_to_name(m->code)));
+                m++;
+        }
+        printf("---------------------------\n");
+}
+
+static void test_errno_mapping_standard(void) {
+        assert_se(sd_bus_error_set(NULL, "System.Error.EUCLEAN", NULL) == -EUCLEAN);
+        assert_se(sd_bus_error_set(NULL, "System.Error.EBUSY", NULL) == -EBUSY);
+        assert_se(sd_bus_error_set(NULL, "System.Error.EINVAL", NULL) == -EINVAL);
+        assert_se(sd_bus_error_set(NULL, "System.Error.WHATSIT", NULL) == -EIO);
+}
+
+BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map test_errors[] = {
+        SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error", 5),
+        SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-2", 52),
+        SD_BUS_ERROR_MAP_END
+};
+
+BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map test_errors2[] = {
+        SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-3", 33),
+        SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-4", 44),
+        SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-33", 333),
+        SD_BUS_ERROR_MAP_END
+};
+
+static const sd_bus_error_map test_errors3[] = {
+        SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-88", 888),
+        SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-99", 999),
+        SD_BUS_ERROR_MAP_END
+};
+
+static const sd_bus_error_map test_errors4[] = {
+        SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-77", 777),
+        SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-78", 778),
+        SD_BUS_ERROR_MAP_END
+};
+
+static const sd_bus_error_map test_errors_bad1[] = {
+        SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-1", 0),
+        SD_BUS_ERROR_MAP_END
+};
+
+static const sd_bus_error_map test_errors_bad2[] = {
+        SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-1", -1),
+        SD_BUS_ERROR_MAP_END
+};
+
+static void test_errno_mapping_custom(void) {
+        assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error", NULL) == -5);
+        assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-2", NULL) == -52);
+        assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-x", NULL) == -EIO);
+        assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-33", NULL) == -333);
+
+        assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-88", NULL) == -EIO);
+        assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-99", NULL) == -EIO);
+        assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-77", NULL) == -EIO);
+
+        assert_se(sd_bus_error_add_map(test_errors3) > 0);
+        assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-88", NULL) == -888);
+        assert_se(sd_bus_error_add_map(test_errors4) > 0);
+        assert_se(sd_bus_error_add_map(test_errors4) == 0);
+        assert_se(sd_bus_error_add_map(test_errors3) == 0);
+
+        assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-99", NULL) == -999);
+        assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-77", NULL) == -777);
+        assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-78", NULL) == -778);
+        assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-2", NULL) == -52);
+        assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-y", NULL) == -EIO);
+
+#if 0 /// UNNEEDED by elogind
+        assert_se(sd_bus_error_set(NULL, BUS_ERROR_NO_SUCH_UNIT, NULL) == -ENOENT);
+#endif // 0
+
+        assert_se(sd_bus_error_add_map(test_errors_bad1) == -EINVAL);
+        assert_se(sd_bus_error_add_map(test_errors_bad2) == -EINVAL);
+}
+
+int main(int argc, char *argv[]) {
+        dump_mapping_table();
+
+        test_error();
+        test_errno_mapping_standard();
+        test_errno_mapping_custom();
+
+        return 0;
+}
diff --git a/src/libelogind/sd-bus/test-bus-introspect.c b/src/libelogind/sd-bus/test-bus-introspect.c
new file mode 100644 (file)
index 0000000..4425cfa
--- /dev/null
@@ -0,0 +1,63 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2013 Lennart Poettering
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "bus-introspect.h"
+#include "log.h"
+
+static int prop_get(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error) {
+        return -EINVAL;
+}
+
+static int prop_set(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error) {
+        return -EINVAL;
+}
+
+static const sd_bus_vtable vtable[] = {
+        SD_BUS_VTABLE_START(0),
+        SD_BUS_METHOD("Hello", "ssas", "a(uu)", NULL, 0),
+        SD_BUS_METHOD("DeprecatedHello", "", "", NULL, SD_BUS_VTABLE_DEPRECATED),
+        SD_BUS_METHOD("DeprecatedHelloNoReply", "", "", NULL, SD_BUS_VTABLE_DEPRECATED|SD_BUS_VTABLE_METHOD_NO_REPLY),
+        SD_BUS_SIGNAL("Wowza", "sss", 0),
+        SD_BUS_SIGNAL("DeprecatedWowza", "ut", SD_BUS_VTABLE_DEPRECATED),
+        SD_BUS_WRITABLE_PROPERTY("AProperty", "s", prop_get, prop_set, 0, 0),
+        SD_BUS_PROPERTY("AReadOnlyDeprecatedProperty", "(ut)", prop_get, 0, SD_BUS_VTABLE_DEPRECATED),
+        SD_BUS_PROPERTY("ChangingProperty", "t", prop_get, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("Invalidating", "t", prop_get, 0, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
+        SD_BUS_PROPERTY("Constant", "t", prop_get, 0, SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EXPLICIT),
+        SD_BUS_VTABLE_END
+};
+
+int main(int argc, char *argv[]) {
+        struct introspect intro;
+
+        log_set_max_level(LOG_DEBUG);
+
+        assert_se(introspect_begin(&intro, false) >= 0);
+
+        fprintf(intro.f, " <interface name=\"org.foo\">\n");
+        assert_se(introspect_write_interface(&intro, vtable) >= 0);
+        fputs(" </interface>\n", intro.f);
+
+        fflush(intro.f);
+        fputs(intro.introspection, stdout);
+
+        introspect_free(&intro);
+
+        return 0;
+}
diff --git a/src/libelogind/sd-bus/test-bus-match.c b/src/libelogind/sd-bus/test-bus-match.c
new file mode 100644 (file)
index 0000000..29c4529
--- /dev/null
@@ -0,0 +1,159 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2013 Lennart Poettering
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "bus-match.h"
+#include "bus-message.h"
+#include "bus-slot.h"
+#include "bus-util.h"
+#include "log.h"
+#include "macro.h"
+
+static bool mask[32];
+
+static int filter(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
+        log_info("Ran %u", PTR_TO_UINT(userdata));
+        assert_se(PTR_TO_UINT(userdata) < ELEMENTSOF(mask));
+        mask[PTR_TO_UINT(userdata)] = true;
+        return 0;
+}
+
+static bool mask_contains(unsigned a[], unsigned n) {
+        unsigned i, j;
+
+        for (i = 0; i < ELEMENTSOF(mask); i++) {
+                bool found = false;
+
+                for (j = 0; j < n; j++)
+                        if (a[j] == i) {
+                                found = true;
+                                break;
+                        }
+
+                if (found != mask[i])
+                        return false;
+        }
+
+        return true;
+}
+
+static int match_add(sd_bus_slot *slots, struct bus_match_node *root, const char *match, int value) {
+        struct bus_match_component *components = NULL;
+        unsigned n_components = 0;
+        sd_bus_slot *s;
+        int r;
+
+        s = slots + value;
+        zero(*s);
+
+        r = bus_match_parse(match, &components, &n_components);
+        if (r < 0)
+                return r;
+
+        s->userdata = INT_TO_PTR(value);
+        s->match_callback.callback = filter;
+
+        r = bus_match_add(root, components, n_components, &s->match_callback);
+        bus_match_parse_free(components, n_components);
+
+        return r;
+}
+
+static void test_match_scope(const char *match, enum bus_match_scope scope) {
+        struct bus_match_component *components = NULL;
+        unsigned n_components = 0;
+
+        assert_se(bus_match_parse(match, &components, &n_components) >= 0);
+        assert_se(bus_match_get_scope(components, n_components) == scope);
+        bus_match_parse_free(components, n_components);
+}
+
+int main(int argc, char *argv[]) {
+        struct bus_match_node root = {
+                .type = BUS_MATCH_ROOT,
+        };
+
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+        enum bus_match_node_type i;
+        sd_bus_slot slots[19];
+        int r;
+
+        r = sd_bus_open_system(&bus);
+        if (r < 0)
+                return EXIT_TEST_SKIP;
+
+        assert_se(match_add(slots, &root, "arg2='wal\\'do',sender='foo',type='signal',interface='bar.x',", 1) >= 0);
+        assert_se(match_add(slots, &root, "arg2='wal\\'do2',sender='foo',type='signal',interface='bar.x',", 2) >= 0);
+        assert_se(match_add(slots, &root, "arg3='test',sender='foo',type='signal',interface='bar.x',", 3) >= 0);
+        assert_se(match_add(slots, &root, "arg3='test',sender='foo',type='method_call',interface='bar.x',", 4) >= 0);
+        assert_se(match_add(slots, &root, "", 5) >= 0);
+        assert_se(match_add(slots, &root, "interface='quux.x'", 6) >= 0);
+        assert_se(match_add(slots, &root, "interface='bar.x'", 7) >= 0);
+        assert_se(match_add(slots, &root, "member='waldo',path='/foo/bar'", 8) >= 0);
+        assert_se(match_add(slots, &root, "path='/foo/bar'", 9) >= 0);
+        assert_se(match_add(slots, &root, "path_namespace='/foo'", 10) >= 0);
+        assert_se(match_add(slots, &root, "path_namespace='/foo/quux'", 11) >= 0);
+        assert_se(match_add(slots, &root, "arg1='two'", 12) >= 0);
+        assert_se(match_add(slots, &root, "member='waldo',arg2path='/prefix/'", 13) >= 0);
+        assert_se(match_add(slots, &root, "member=waldo,path='/foo/bar',arg3namespace='prefix'", 14) >= 0);
+        assert_se(match_add(slots, &root, "arg4has='pi'", 15) >= 0);
+        assert_se(match_add(slots, &root, "arg4has='pa'", 16) >= 0);
+        assert_se(match_add(slots, &root, "arg4has='po'", 17) >= 0);
+        assert_se(match_add(slots, &root, "arg4='pi'", 18) >= 0);
+
+        bus_match_dump(&root, 0);
+
+        assert_se(sd_bus_message_new_signal(bus, &m, "/foo/bar", "bar.x", "waldo") >= 0);
+        assert_se(sd_bus_message_append(m, "ssssas", "one", "two", "/prefix/three", "prefix.four", 3, "pi", "pa", "po") >= 0);
+        assert_se(bus_message_seal(m, 1, 0) >= 0);
+
+        zero(mask);
+        assert_se(bus_match_run(NULL, &root, m) == 0);
+        assert_se(mask_contains((unsigned[]) { 9, 8, 7, 5, 10, 12, 13, 14, 15, 16, 17 }, 11));
+
+        assert_se(bus_match_remove(&root, &slots[8].match_callback) >= 0);
+        assert_se(bus_match_remove(&root, &slots[13].match_callback) >= 0);
+
+        bus_match_dump(&root, 0);
+
+        zero(mask);
+        assert_se(bus_match_run(NULL, &root, m) == 0);
+        assert_se(mask_contains((unsigned[]) { 9, 5, 10, 12, 14, 7, 15, 16, 17 }, 9));
+
+        for (i = 0; i < _BUS_MATCH_NODE_TYPE_MAX; i++) {
+                char buf[32];
+                const char *x;
+
+                assert_se(x = bus_match_node_type_to_string(i, buf, sizeof(buf)));
+
+                if (i >= BUS_MATCH_MESSAGE_TYPE)
+                        assert_se(bus_match_node_type_from_string(x, strlen(x)) == i);
+        }
+
+        bus_match_free(&root);
+
+        test_match_scope("interface='foobar'", BUS_MATCH_GENERIC);
+        test_match_scope("", BUS_MATCH_GENERIC);
+        test_match_scope("interface='org.freedesktop.DBus.Local'", BUS_MATCH_LOCAL);
+        test_match_scope("sender='org.freedesktop.DBus.Local'", BUS_MATCH_LOCAL);
+        test_match_scope("member='gurke',path='/org/freedesktop/DBus/Local'", BUS_MATCH_LOCAL);
+        test_match_scope("arg2='piep',sender='org.freedesktop.DBus',member='waldo'", BUS_MATCH_DRIVER);
+
+        return 0;
+}
diff --git a/src/libelogind/sd-bus/test-bus-server.c b/src/libelogind/sd-bus/test-bus-server.c
new file mode 100644 (file)
index 0000000..b6272ef
--- /dev/null
@@ -0,0 +1,216 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2013 Lennart Poettering
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pthread.h>
+#include <stdlib.h>
+
+#include "sd-bus.h"
+
+#include "bus-internal.h"
+#include "bus-util.h"
+#include "log.h"
+#include "macro.h"
+#include "util.h"
+
+struct context {
+        int fds[2];
+
+        bool client_negotiate_unix_fds;
+        bool server_negotiate_unix_fds;
+
+        bool client_anonymous_auth;
+        bool server_anonymous_auth;
+};
+
+static void *server(void *p) {
+        struct context *c = p;
+        sd_bus *bus = NULL;
+        sd_id128_t id;
+        bool quit = false;
+        int r;
+
+        assert_se(sd_id128_randomize(&id) >= 0);
+
+        assert_se(sd_bus_new(&bus) >= 0);
+        assert_se(sd_bus_set_fd(bus, c->fds[0], c->fds[0]) >= 0);
+        assert_se(sd_bus_set_server(bus, 1, id) >= 0);
+        assert_se(sd_bus_set_anonymous(bus, c->server_anonymous_auth) >= 0);
+        assert_se(sd_bus_negotiate_fds(bus, c->server_negotiate_unix_fds) >= 0);
+        assert_se(sd_bus_start(bus) >= 0);
+
+        while (!quit) {
+                _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
+
+                r = sd_bus_process(bus, &m);
+                if (r < 0) {
+                        log_error_errno(r, "Failed to process requests: %m");
+                        goto fail;
+                }
+
+                if (r == 0) {
+                        r = sd_bus_wait(bus, (uint64_t) -1);
+                        if (r < 0) {
+                                log_error_errno(r, "Failed to wait: %m");
+                                goto fail;
+                        }
+
+                        continue;
+                }
+
+                if (!m)
+                        continue;
+
+                log_info("Got message! member=%s", strna(sd_bus_message_get_member(m)));
+
+                if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "Exit")) {
+
+                        assert_se((sd_bus_can_send(bus, 'h') >= 1) == (c->server_negotiate_unix_fds && c->client_negotiate_unix_fds));
+
+                        r = sd_bus_message_new_method_return(m, &reply);
+                        if (r < 0) {
+                                log_error_errno(r, "Failed to allocate return: %m");
+                                goto fail;
+                        }
+
+                        quit = true;
+
+                } else if (sd_bus_message_is_method_call(m, NULL, NULL)) {
+                        r = sd_bus_message_new_method_error(
+                                        m,
+                                        &reply,
+                                        &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_UNKNOWN_METHOD, "Unknown method."));
+                        if (r < 0) {
+                                log_error_errno(r, "Failed to allocate return: %m");
+                                goto fail;
+                        }
+                }
+
+                if (reply) {
+                        r = sd_bus_send(bus, reply, NULL);
+                        if (r < 0) {
+                                log_error_errno(r, "Failed to send reply: %m");
+                                goto fail;
+                        }
+                }
+        }
+
+        r = 0;
+
+fail:
+        if (bus) {
+                sd_bus_flush(bus);
+                sd_bus_unref(bus);
+        }
+
+        return INT_TO_PTR(r);
+}
+
+static int client(struct context *c) {
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
+        _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
+        sd_bus_error error = SD_BUS_ERROR_NULL;
+        int r;
+
+        assert_se(sd_bus_new(&bus) >= 0);
+        assert_se(sd_bus_set_fd(bus, c->fds[1], c->fds[1]) >= 0);
+        assert_se(sd_bus_negotiate_fds(bus, c->client_negotiate_unix_fds) >= 0);
+        assert_se(sd_bus_set_anonymous(bus, c->client_anonymous_auth) >= 0);
+        assert_se(sd_bus_start(bus) >= 0);
+
+        r = sd_bus_message_new_method_call(
+                        bus,
+                        &m,
+                        "org.freedesktop.systemd.test",
+                        "/",
+                        "org.freedesktop.systemd.test",
+                        "Exit");
+        if (r < 0)
+                return log_error_errno(r, "Failed to allocate method call: %m");
+
+        r = sd_bus_call(bus, m, 0, &error, &reply);
+        if (r < 0) {
+                log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
+                return r;
+        }
+
+        return 0;
+}
+
+static int test_one(bool client_negotiate_unix_fds, bool server_negotiate_unix_fds,
+                    bool client_anonymous_auth, bool server_anonymous_auth) {
+
+        struct context c;
+        pthread_t s;
+        void *p;
+        int r, q;
+
+        zero(c);
+
+        assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, c.fds) >= 0);
+
+        c.client_negotiate_unix_fds = client_negotiate_unix_fds;
+        c.server_negotiate_unix_fds = server_negotiate_unix_fds;
+        c.client_anonymous_auth = client_anonymous_auth;
+        c.server_anonymous_auth = server_anonymous_auth;
+
+        r = pthread_create(&s, NULL, server, &c);
+        if (r != 0)
+                return -r;
+
+        r = client(&c);
+
+        q = pthread_join(s, &p);
+        if (q != 0)
+                return -q;
+
+        if (r < 0)
+                return r;
+
+        if (PTR_TO_INT(p) < 0)
+                return PTR_TO_INT(p);
+
+        return 0;
+}
+
+int main(int argc, char *argv[]) {
+        int r;
+
+        r = test_one(true, true, false, false);
+        assert_se(r >= 0);
+
+        r = test_one(true, false, false, false);
+        assert_se(r >= 0);
+
+        r = test_one(false, true, false, false);
+        assert_se(r >= 0);
+
+        r = test_one(false, false, false, false);
+        assert_se(r >= 0);
+
+        r = test_one(true, true, true, true);
+        assert_se(r >= 0);
+
+        r = test_one(true, true, false, true);
+        assert_se(r >= 0);
+
+        r = test_one(true, true, true, false);
+        assert_se(r == -EPERM);
+
+        return EXIT_SUCCESS;
+}
diff --git a/src/libelogind/sd-bus/test-bus-signature.c b/src/libelogind/sd-bus/test-bus-signature.c
new file mode 100644 (file)
index 0000000..4f4fd09
--- /dev/null
@@ -0,0 +1,164 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2013 Lennart Poettering
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "bus-internal.h"
+#include "bus-signature.h"
+#include "log.h"
+#include "string-util.h"
+
+int main(int argc, char *argv[]) {
+        char prefix[256];
+        int r;
+
+        assert_se(signature_is_single("y", false));
+        assert_se(signature_is_single("u", false));
+        assert_se(signature_is_single("v", false));
+        assert_se(signature_is_single("as", false));
+        assert_se(signature_is_single("(ss)", false));
+        assert_se(signature_is_single("()", false));
+        assert_se(signature_is_single("(()()()()())", false));
+        assert_se(signature_is_single("(((())))", false));
+        assert_se(signature_is_single("((((s))))", false));
+        assert_se(signature_is_single("{ss}", true));
+        assert_se(signature_is_single("a{ss}", false));
+        assert_se(!signature_is_single("uu", false));
+        assert_se(!signature_is_single("", false));
+        assert_se(!signature_is_single("(", false));
+        assert_se(!signature_is_single(")", false));
+        assert_se(!signature_is_single("())", false));
+        assert_se(!signature_is_single("((())", false));
+        assert_se(!signature_is_single("{)", false));
+        assert_se(!signature_is_single("{}", true));
+        assert_se(!signature_is_single("{sss}", true));
+        assert_se(!signature_is_single("{s}", true));
+        assert_se(!signature_is_single("{ss}", false));
+        assert_se(!signature_is_single("{ass}", true));
+        assert_se(!signature_is_single("a}", true));
+
+        assert_se(signature_is_pair("yy"));
+        assert_se(signature_is_pair("ss"));
+        assert_se(signature_is_pair("sas"));
+        assert_se(signature_is_pair("sv"));
+        assert_se(signature_is_pair("sa(vs)"));
+        assert_se(!signature_is_pair(""));
+        assert_se(!signature_is_pair("va"));
+        assert_se(!signature_is_pair("sss"));
+        assert_se(!signature_is_pair("{s}ss"));
+
+        assert_se(signature_is_valid("ssa{ss}sssub", true));
+        assert_se(signature_is_valid("ssa{ss}sssub", false));
+        assert_se(signature_is_valid("{ss}", true));
+        assert_se(!signature_is_valid("{ss}", false));
+        assert_se(signature_is_valid("", true));
+        assert_se(signature_is_valid("", false));
+
+        assert_se(signature_is_valid("sssusa(uuubbba(uu)uuuu)a{u(uuuvas)}", false));
+
+        assert_se(!signature_is_valid("a", false));
+        assert_se(signature_is_valid("as", false));
+        assert_se(signature_is_valid("aas", false));
+        assert_se(signature_is_valid("aaas", false));
+        assert_se(signature_is_valid("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaad", false));
+        assert_se(signature_is_valid("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaas", false));
+        assert_se(!signature_is_valid("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaau", false));
+
+        assert_se(signature_is_valid("(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))", false));
+        assert_se(!signature_is_valid("((((((((((((((((((((((((((((((((()))))))))))))))))))))))))))))))))", false));
+
+        assert_se(namespace_complex_pattern("", ""));
+        assert_se(namespace_complex_pattern("foobar", "foobar"));
+        assert_se(namespace_complex_pattern("foobar.waldo", "foobar.waldo"));
+        assert_se(namespace_complex_pattern("foobar.", "foobar.waldo"));
+        assert_se(namespace_complex_pattern("foobar.waldo", "foobar."));
+        assert_se(!namespace_complex_pattern("foobar.waldo", "foobar"));
+        assert_se(!namespace_complex_pattern("foobar", "foobar.waldo"));
+        assert_se(!namespace_complex_pattern("", "foo"));
+        assert_se(!namespace_complex_pattern("foo", ""));
+        assert_se(!namespace_complex_pattern("foo.", ""));
+
+        assert_se(path_complex_pattern("", ""));
+        assert_se(!path_complex_pattern("", "/"));
+        assert_se(!path_complex_pattern("/", ""));
+        assert_se(path_complex_pattern("/", "/"));
+        assert_se(path_complex_pattern("/foobar/", "/"));
+        assert_se(!path_complex_pattern("/foobar/", "/foobar"));
+        assert_se(path_complex_pattern("/foobar", "/foobar"));
+        assert_se(!path_complex_pattern("/foobar", "/foobar/"));
+        assert_se(!path_complex_pattern("/foobar", "/foobar/waldo"));
+        assert_se(path_complex_pattern("/foobar/", "/foobar/waldo"));
+        assert_se(path_complex_pattern("/foobar/waldo", "/foobar/"));
+
+        assert_se(path_simple_pattern("/foo/", "/foo/bar/waldo"));
+
+        assert_se(namespace_simple_pattern("", ""));
+        assert_se(namespace_simple_pattern("", ".foobar"));
+        assert_se(namespace_simple_pattern("foobar", "foobar"));
+        assert_se(namespace_simple_pattern("foobar.waldo", "foobar.waldo"));
+        assert_se(namespace_simple_pattern("foobar", "foobar.waldo"));
+        assert_se(!namespace_simple_pattern("foobar.waldo", "foobar"));
+        assert_se(!namespace_simple_pattern("", "foo"));
+        assert_se(!namespace_simple_pattern("foo", ""));
+        assert_se(namespace_simple_pattern("foo.", "foo.bar.waldo"));
+
+        assert_se(streq(object_path_startswith("/foo/bar", "/foo"), "bar"));
+        assert_se(streq(object_path_startswith("/foo", "/foo"), ""));
+        assert_se(streq(object_path_startswith("/foo", "/"), "foo"));
+        assert_se(streq(object_path_startswith("/", "/"), ""));
+        assert_se(!object_path_startswith("/foo", "/bar"));
+        assert_se(!object_path_startswith("/", "/bar"));
+        assert_se(!object_path_startswith("/foo", ""));
+
+        assert_se(object_path_is_valid("/foo/bar"));
+        assert_se(object_path_is_valid("/foo"));
+        assert_se(object_path_is_valid("/"));
+        assert_se(object_path_is_valid("/foo5"));
+        assert_se(object_path_is_valid("/foo_5"));
+        assert_se(!object_path_is_valid(""));
+        assert_se(!object_path_is_valid("/foo/"));
+        assert_se(!object_path_is_valid("//"));
+        assert_se(!object_path_is_valid("//foo"));
+        assert_se(!object_path_is_valid("/foo//bar"));
+        assert_se(!object_path_is_valid("/foo/aaaäöä"));
+
+        OBJECT_PATH_FOREACH_PREFIX(prefix, "/") {
+                log_info("<%s>", prefix);
+                assert_not_reached("???");
+        }
+
+        r = 0;
+        OBJECT_PATH_FOREACH_PREFIX(prefix, "/xxx") {
+                log_info("<%s>", prefix);
+                assert_se(streq(prefix, "/"));
+                assert_se(r == 0);
+                r++;
+        }
+        assert_se(r == 1);
+
+        r = 0;
+        OBJECT_PATH_FOREACH_PREFIX(prefix, "/xxx/yyy/zzz") {
+                log_info("<%s>", prefix);
+                assert_se(r != 0 || streq(prefix, "/xxx/yyy"));
+                assert_se(r != 1 || streq(prefix, "/xxx"));
+                assert_se(r != 2 || streq(prefix, "/"));
+                r++;
+        }
+        assert_se(r == 3);
+
+        return 0;
+}
diff --git a/src/libelogind/sd-event/test-event.c b/src/libelogind/sd-event/test-event.c
new file mode 100644 (file)
index 0000000..c0e5e06
--- /dev/null
@@ -0,0 +1,364 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2013 Lennart Poettering
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "sd-event.h"
+
+#include "fd-util.h"
+#include "log.h"
+#include "macro.h"
+#include "signal-util.h"
+#include "util.h"
+
+static int prepare_handler(sd_event_source *s, void *userdata) {
+        log_info("preparing %c", PTR_TO_INT(userdata));
+        return 1;
+}
+
+static bool got_a, got_b, got_c, got_unref;
+static unsigned got_d;
+
+static int unref_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+        sd_event_source_unref(s);
+        got_unref = true;
+        return 0;
+}
+
+static int io_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+
+        log_info("got IO on %c", PTR_TO_INT(userdata));
+
+        if (userdata == INT_TO_PTR('a')) {
+                assert_se(sd_event_source_set_enabled(s, SD_EVENT_OFF) >= 0);
+                assert_se(!got_a);
+                got_a = true;
+        } else if (userdata == INT_TO_PTR('b')) {
+                assert_se(!got_b);
+                got_b = true;
+        } else if (userdata == INT_TO_PTR('d')) {
+                got_d++;
+                if (got_d < 2)
+                        assert_se(sd_event_source_set_enabled(s, SD_EVENT_ONESHOT) >= 0);
+                else
+                        assert_se(sd_event_source_set_enabled(s, SD_EVENT_OFF) >= 0);
+        } else
+                assert_not_reached("Yuck!");
+
+        return 1;
+}
+
+static int child_handler(sd_event_source *s, const siginfo_t *si, void *userdata) {
+
+        assert_se(s);
+        assert_se(si);
+
+        log_info("got child on %c", PTR_TO_INT(userdata));
+
+        assert_se(userdata == INT_TO_PTR('f'));
+
+        assert_se(sd_event_exit(sd_event_source_get_event(s), 0) >= 0);
+        sd_event_source_unref(s);
+
+        return 1;
+}
+
+static int signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
+        sd_event_source *p = NULL;
+        pid_t pid;
+
+        assert_se(s);
+        assert_se(si);
+
+        log_info("got signal on %c", PTR_TO_INT(userdata));
+
+        assert_se(userdata == INT_TO_PTR('e'));
+
+        assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0);
+
+        pid = fork();
+        assert_se(pid >= 0);
+
+        if (pid == 0)
+                _exit(0);
+
+        assert_se(sd_event_add_child(sd_event_source_get_event(s), &p, pid, WEXITED, child_handler, INT_TO_PTR('f')) >= 0);
+        assert_se(sd_event_source_set_enabled(p, SD_EVENT_ONESHOT) >= 0);
+
+        sd_event_source_unref(s);
+
+        return 1;
+}
+
+static int defer_handler(sd_event_source *s, void *userdata) {
+        sd_event_source *p = NULL;
+
+        assert_se(s);
+
+        log_info("got defer on %c", PTR_TO_INT(userdata));
+
+        assert_se(userdata == INT_TO_PTR('d'));
+
+        assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGUSR1, -1) >= 0);
+
+        assert_se(sd_event_add_signal(sd_event_source_get_event(s), &p, SIGUSR1, signal_handler, INT_TO_PTR('e')) >= 0);
+        assert_se(sd_event_source_set_enabled(p, SD_EVENT_ONESHOT) >= 0);
+        raise(SIGUSR1);
+
+        sd_event_source_unref(s);
+
+        return 1;
+}
+
+static bool do_quit = false;
+
+static int time_handler(sd_event_source *s, uint64_t usec, void *userdata) {
+        log_info("got timer on %c", PTR_TO_INT(userdata));
+
+        if (userdata == INT_TO_PTR('c')) {
+
+                if (do_quit) {
+                        sd_event_source *p;
+
+                        assert_se(sd_event_add_defer(sd_event_source_get_event(s), &p, defer_handler, INT_TO_PTR('d')) >= 0);
+                        assert_se(sd_event_source_set_enabled(p, SD_EVENT_ONESHOT) >= 0);
+                } else {
+                        assert_se(!got_c);
+                        got_c = true;
+                }
+        } else
+                assert_not_reached("Huh?");
+
+        return 2;
+}
+
+static bool got_exit = false;
+
+static int exit_handler(sd_event_source *s, void *userdata) {
+        log_info("got quit handler on %c", PTR_TO_INT(userdata));
+
+        got_exit = true;
+
+        return 3;
+}
+
+static bool got_post = false;
+
+static int post_handler(sd_event_source *s, void *userdata) {
+        log_info("got post handler");
+
+        got_post = true;
+
+        return 2;
+}
+
+static void test_basic(void) {
+        sd_event *e = NULL;
+        sd_event_source *w = NULL, *x = NULL, *y = NULL, *z = NULL, *q = NULL, *t = NULL;
+        static const char ch = 'x';
+        int a[2] = { -1, -1 }, b[2] = { -1, -1}, d[2] = { -1, -1}, k[2] = { -1, -1 };
+        uint64_t event_now;
+        int64_t priority;
+
+        assert_se(pipe(a) >= 0);
+        assert_se(pipe(b) >= 0);
+        assert_se(pipe(d) >= 0);
+        assert_se(pipe(k) >= 0);
+
+        assert_se(sd_event_default(&e) >= 0);
+        assert_se(sd_event_now(e, CLOCK_MONOTONIC, &event_now) > 0);
+
+        assert_se(sd_event_set_watchdog(e, true) >= 0);
+
+        /* Test whether we cleanly can destroy an io event source from its own handler */
+        got_unref = false;
+        assert_se(sd_event_add_io(e, &t, k[0], EPOLLIN, unref_handler, NULL) >= 0);
+        assert_se(write(k[1], &ch, 1) == 1);
+        assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
+        assert_se(got_unref);
+
+        got_a = false, got_b = false, got_c = false, got_d = 0;
+
+        /* Add a oneshot handler, trigger it, re-enable it, and trigger
+         * it again. */
+        assert_se(sd_event_add_io(e, &w, d[0], EPOLLIN, io_handler, INT_TO_PTR('d')) >= 0);
+        assert_se(sd_event_source_set_enabled(w, SD_EVENT_ONESHOT) >= 0);
+        assert_se(write(d[1], &ch, 1) >= 0);
+        assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
+        assert_se(got_d == 1);
+        assert_se(write(d[1], &ch, 1) >= 0);
+        assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
+        assert_se(got_d == 2);
+
+        assert_se(sd_event_add_io(e, &x, a[0], EPOLLIN, io_handler, INT_TO_PTR('a')) >= 0);
+        assert_se(sd_event_add_io(e, &y, b[0], EPOLLIN, io_handler, INT_TO_PTR('b')) >= 0);
+        assert_se(sd_event_add_time(e, &z, CLOCK_MONOTONIC, 0, 0, time_handler, INT_TO_PTR('c')) >= 0);
+        assert_se(sd_event_add_exit(e, &q, exit_handler, INT_TO_PTR('g')) >= 0);
+
+        assert_se(sd_event_source_set_priority(x, 99) >= 0);
+        assert_se(sd_event_source_get_priority(x, &priority) >= 0);
+        assert_se(priority == 99);
+        assert_se(sd_event_source_set_enabled(y, SD_EVENT_ONESHOT) >= 0);
+        assert_se(sd_event_source_set_prepare(x, prepare_handler) >= 0);
+        assert_se(sd_event_source_set_priority(z, 50) >= 0);
+        assert_se(sd_event_source_set_enabled(z, SD_EVENT_ONESHOT) >= 0);
+        assert_se(sd_event_source_set_prepare(z, prepare_handler) >= 0);
+
+        /* Test for floating event sources */
+        assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGRTMIN+1, -1) >= 0);
+        assert_se(sd_event_add_signal(e, NULL, SIGRTMIN+1, NULL, NULL) >= 0);
+
+        assert_se(write(a[1], &ch, 1) >= 0);
+        assert_se(write(b[1], &ch, 1) >= 0);
+
+        assert_se(!got_a && !got_b && !got_c);
+
+        assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
+
+        assert_se(!got_a && got_b && !got_c);
+
+        assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
+
+        assert_se(!got_a && got_b && got_c);
+
+        assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
+
+        assert_se(got_a && got_b && got_c);
+
+        sd_event_source_unref(x);
+        sd_event_source_unref(y);
+
+        do_quit = true;
+        assert_se(sd_event_add_post(e, NULL, post_handler, NULL) >= 0);
+        assert_se(sd_event_now(e, CLOCK_MONOTONIC, &event_now) == 0);
+        assert_se(sd_event_source_set_time(z, event_now + 200 * USEC_PER_MSEC) >= 0);
+        assert_se(sd_event_source_set_enabled(z, SD_EVENT_ONESHOT) >= 0);
+
+        assert_se(sd_event_loop(e) >= 0);
+        assert_se(got_post);
+        assert_se(got_exit);
+
+        sd_event_source_unref(z);
+        sd_event_source_unref(q);
+
+        sd_event_source_unref(w);
+
+        sd_event_unref(e);
+
+        safe_close_pair(a);
+        safe_close_pair(b);
+        safe_close_pair(d);
+        safe_close_pair(k);
+}
+
+static void test_sd_event_now(void) {
+        _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+        uint64_t event_now;
+
+        assert_se(sd_event_new(&e) >= 0);
+        assert_se(sd_event_now(e, CLOCK_MONOTONIC, &event_now) > 0);
+        assert_se(sd_event_now(e, CLOCK_REALTIME, &event_now) > 0);
+        assert_se(sd_event_now(e, CLOCK_REALTIME_ALARM, &event_now) > 0);
+        if (clock_boottime_supported()) {
+                assert_se(sd_event_now(e, CLOCK_BOOTTIME, &event_now) > 0);
+                assert_se(sd_event_now(e, CLOCK_BOOTTIME_ALARM, &event_now) > 0);
+        }
+        assert_se(sd_event_now(e, -1, &event_now) == -EOPNOTSUPP);
+        assert_se(sd_event_now(e, 900 /* arbitrary big number */, &event_now) == -EOPNOTSUPP);
+
+        assert_se(sd_event_run(e, 0) == 0);
+
+        assert_se(sd_event_now(e, CLOCK_MONOTONIC, &event_now) == 0);
+        assert_se(sd_event_now(e, CLOCK_REALTIME, &event_now) == 0);
+        assert_se(sd_event_now(e, CLOCK_REALTIME_ALARM, &event_now) == 0);
+        if (clock_boottime_supported()) {
+                assert_se(sd_event_now(e, CLOCK_BOOTTIME, &event_now) == 0);
+                assert_se(sd_event_now(e, CLOCK_BOOTTIME_ALARM, &event_now) == 0);
+        }
+        assert_se(sd_event_now(e, -1, &event_now) == -EOPNOTSUPP);
+        assert_se(sd_event_now(e, 900 /* arbitrary big number */, &event_now) == -EOPNOTSUPP);
+}
+
+static int last_rtqueue_sigval = 0;
+static int n_rtqueue = 0;
+
+static int rtqueue_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
+        last_rtqueue_sigval = si->ssi_int;
+        n_rtqueue++;
+        return 0;
+}
+
+static void test_rtqueue(void) {
+        sd_event_source *u = NULL, *v = NULL, *s = NULL;
+        sd_event *e = NULL;
+
+        assert_se(sd_event_default(&e) >= 0);
+
+        assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGRTMIN+2, SIGRTMIN+3, SIGUSR2, -1) >= 0);
+        assert_se(sd_event_add_signal(e, &u, SIGRTMIN+2, rtqueue_handler, NULL) >= 0);
+        assert_se(sd_event_add_signal(e, &v, SIGRTMIN+3, rtqueue_handler, NULL) >= 0);
+        assert_se(sd_event_add_signal(e, &s, SIGUSR2, rtqueue_handler, NULL) >= 0);
+
+        assert_se(sd_event_source_set_priority(v, -10) >= 0);
+
+        assert(sigqueue(getpid(), SIGRTMIN+2, (union sigval) { .sival_int = 1 }) >= 0);
+        assert(sigqueue(getpid(), SIGRTMIN+3, (union sigval) { .sival_int = 2 }) >= 0);
+        assert(sigqueue(getpid(), SIGUSR2, (union sigval) { .sival_int = 3 }) >= 0);
+        assert(sigqueue(getpid(), SIGRTMIN+3, (union sigval) { .sival_int = 4 }) >= 0);
+        assert(sigqueue(getpid(), SIGUSR2, (union sigval) { .sival_int = 5 }) >= 0);
+
+        assert_se(n_rtqueue == 0);
+        assert_se(last_rtqueue_sigval == 0);
+
+        assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
+        assert_se(n_rtqueue == 1);
+        assert_se(last_rtqueue_sigval == 2); /* first SIGRTMIN+3 */
+
+        assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
+        assert_se(n_rtqueue == 2);
+        assert_se(last_rtqueue_sigval == 4); /* second SIGRTMIN+3 */
+
+        assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
+        assert_se(n_rtqueue == 3);
+        assert_se(last_rtqueue_sigval == 3); /* first SIGUSR2 */
+
+        assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
+        assert_se(n_rtqueue == 4);
+        assert_se(last_rtqueue_sigval == 1); /* SIGRTMIN+2 */
+
+        assert_se(sd_event_run(e, 0) == 0); /* the other SIGUSR2 is dropped, because the first one was still queued */
+        assert_se(n_rtqueue == 4);
+        assert_se(last_rtqueue_sigval == 1);
+
+        sd_event_source_unref(u);
+        sd_event_source_unref(v);
+        sd_event_source_unref(s);
+
+        sd_event_unref(e);
+}
+
+int main(int argc, char *argv[]) {
+
+        log_set_max_level(LOG_DEBUG);
+        log_parse_environment();
+
+        test_basic();
+        test_sd_event_now();
+        test_rtqueue();
+
+        return 0;
+}
diff --git a/src/test/.gitignore b/src/test/.gitignore
new file mode 100644 (file)
index 0000000..e4c198a
--- /dev/null
@@ -0,0 +1 @@
+test-hashmap-ordered.c
diff --git a/src/test/Makefile b/src/test/Makefile
new file mode 120000 (symlink)
index 0000000..d0b0e8e
--- /dev/null
@@ -0,0 +1 @@
+../Makefile
\ No newline at end of file
diff --git a/src/test/test-alloc-util.c b/src/test/test-alloc-util.c
new file mode 100644 (file)
index 0000000..cc4821e
--- /dev/null
@@ -0,0 +1,55 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "alloc-util.h"
+#include "macro.h"
+#include "util.h"
+
+static void test_alloca(void) {
+        static const uint8_t zero[997] = { };
+        char *t;
+
+        t = alloca_align(17, 512);
+        assert_se(!((uintptr_t)t & 0xff));
+        memzero(t, 17);
+
+        t = alloca0_align(997, 1024);
+        assert_se(!((uintptr_t)t & 0x1ff));
+        assert_se(!memcmp(t, zero, 997));
+}
+
+static void test_memdup_multiply(void) {
+        int org[] = {1, 2, 3};
+        int *dup;
+
+        dup = (int*)memdup_multiply(org, sizeof(int), 3);
+
+        assert_se(dup);
+        assert_se(dup[0] == 1);
+        assert_se(dup[1] == 2);
+        assert_se(dup[2] == 3);
+        free(dup);
+}
+
+int main(int argc, char *argv[]) {
+        test_alloca();
+        test_memdup_multiply();
+
+        return 0;
+}
diff --git a/src/test/test-cgroup.c b/src/test/test-cgroup.c
new file mode 100644 (file)
index 0000000..5336c19
--- /dev/null
@@ -0,0 +1,103 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <string.h>
+#include <unistd.h>
+
+#include "cgroup-util.h"
+#include "path-util.h"
+#include "string-util.h"
+#include "util.h"
+
+int main(int argc, char*argv[]) {
+        char *path;
+        char *c, *p;
+
+        assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, "/test-a") == 0);
+        assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, "/test-a") == 0);
+        assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, "/test-b") == 0);
+        assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, "/test-b/test-c") == 0);
+        assert_se(cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, "/test-b", 0) == 0);
+
+        assert_se(cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, getpid(), &path) == 0);
+        assert_se(streq(path, "/test-b"));
+        free(path);
+
+        assert_se(cg_attach(SYSTEMD_CGROUP_CONTROLLER, "/test-a", 0) == 0);
+
+        assert_se(cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, getpid(), &path) == 0);
+        assert_se(path_equal(path, "/test-a"));
+        free(path);
+
+        assert_se(cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, "/test-b/test-d", 0) == 0);
+
+        assert_se(cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, getpid(), &path) == 0);
+        assert_se(path_equal(path, "/test-b/test-d"));
+        free(path);
+
+        assert_se(cg_get_path(SYSTEMD_CGROUP_CONTROLLER, "/test-b/test-d", NULL, &path) == 0);
+        assert_se(path_equal(path, "/sys/fs/cgroup/systemd/test-b/test-d"));
+        free(path);
+
+        assert_se(cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, "/test-a") > 0);
+        assert_se(cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, "/test-b") > 0);
+        assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a") > 0);
+        assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b") == 0);
+
+        assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a", 0, 0, NULL, NULL, NULL) == 0);
+        assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", 0, 0, NULL, NULL, NULL) > 0);
+
+        assert_se(cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", SYSTEMD_CGROUP_CONTROLLER, "/test-a", 0) > 0);
+
+        assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a") == 0);
+        assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b") > 0);
+
+        assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a", 0, 0, NULL, NULL, NULL) > 0);
+        assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", 0, 0, NULL, NULL, NULL) == 0);
+
+        cg_trim(SYSTEMD_CGROUP_CONTROLLER, "/", false);
+
+        assert_se(cg_rmdir(SYSTEMD_CGROUP_CONTROLLER, "/test-b") < 0);
+        assert_se(cg_rmdir(SYSTEMD_CGROUP_CONTROLLER, "/test-a") >= 0);
+
+        assert_se(cg_split_spec("foobar:/", &c, &p) == 0);
+        assert_se(streq(c, "foobar"));
+        assert_se(streq(p, "/"));
+        free(c);
+        free(p);
+
+        assert_se(cg_split_spec("foobar:", &c, &p) < 0);
+        assert_se(cg_split_spec("foobar:asdfd", &c, &p) < 0);
+        assert_se(cg_split_spec(":///", &c, &p) < 0);
+        assert_se(cg_split_spec(":", &c, &p) < 0);
+        assert_se(cg_split_spec("", &c, &p) < 0);
+        assert_se(cg_split_spec("fo/obar:/", &c, &p) < 0);
+
+        assert_se(cg_split_spec("/", &c, &p) >= 0);
+        assert_se(c == NULL);
+        assert_se(streq(p, "/"));
+        free(p);
+
+        assert_se(cg_split_spec("foo", &c, &p) >= 0);
+        assert_se(streq(c, "foo"));
+        assert_se(p == NULL);
+        free(c);
+
+        return 0;
+}
diff --git a/src/test/test-conf-files.c b/src/test/test-conf-files.c
new file mode 100644 (file)
index 0000000..22b7c61
--- /dev/null
@@ -0,0 +1,107 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2014 Michael Marineau
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "alloc-util.h"
+#include "conf-files.h"
+#include "fs-util.h"
+#include "macro.h"
+#include "parse-util.h"
+#include "rm-rf.h"
+#include "string-util.h"
+#include "strv.h"
+#include "user-util.h"
+#include "util.h"
+
+static void setup_test_dir(char *tmp_dir, const char *files, ...) {
+        va_list ap;
+
+        assert_se(mkdtemp(tmp_dir) != NULL);
+
+        va_start(ap, files);
+        while (files != NULL) {
+                _cleanup_free_ char *path = strappend(tmp_dir, files);
+                assert_se(touch_file(path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID) == 0);
+                files = va_arg(ap, const char *);
+        }
+        va_end(ap);
+}
+
+static void test_conf_files_list(bool use_root) {
+        char tmp_dir[] = "/tmp/test-conf-files-XXXXXX";
+        _cleanup_strv_free_ char **found_files = NULL, **found_files2 = NULL;
+        const char *root_dir, *search_1, *search_2, *expect_a, *expect_b, *expect_c;
+
+        log_debug("/* %s */", __func__);
+
+        setup_test_dir(tmp_dir,
+                       "/dir1/a.conf",
+                       "/dir2/a.conf",
+                       "/dir2/b.conf",
+                       "/dir2/c.foo",
+                       NULL);
+
+        if (use_root) {
+                root_dir = tmp_dir;
+                search_1 = "/dir1";
+                search_2 = "/dir2";
+        } else {
+                root_dir = NULL;
+                search_1 = strjoina(tmp_dir, "/dir1");
+                search_2 = strjoina(tmp_dir, "/dir2");
+        }
+
+        expect_a = strjoina(tmp_dir, "/dir1/a.conf");
+        expect_b = strjoina(tmp_dir, "/dir2/b.conf");
+        expect_c = strjoina(tmp_dir, "/dir2/c.foo");
+
+        log_debug("/* Check when filtered by suffix */");
+
+        assert_se(conf_files_list(&found_files, ".conf", root_dir, search_1, search_2, NULL) == 0);
+        strv_print(found_files);
+
+        assert_se(found_files);
+        assert_se(streq_ptr(found_files[0], expect_a));
+        assert_se(streq_ptr(found_files[1], expect_b));
+        assert_se(found_files[2] == NULL);
+
+        log_debug("/* Check when unfiltered */");
+        assert_se(conf_files_list(&found_files2, NULL, root_dir, search_1, search_2, NULL) == 0);
+        strv_print(found_files2);
+
+        assert_se(found_files2);
+        assert_se(streq_ptr(found_files2[0], expect_a));
+        assert_se(streq_ptr(found_files2[1], expect_b));
+        assert_se(streq_ptr(found_files2[2], expect_c));
+        assert_se(found_files2[3] == NULL);
+
+        assert_se(rm_rf(tmp_dir, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
+}
+
+int main(int argc, char **argv) {
+        log_set_max_level(LOG_DEBUG);
+        log_parse_environment();
+        log_open();
+
+        test_conf_files_list(false);
+        test_conf_files_list(true);
+        return 0;
+}
diff --git a/src/test/test-conf-parser.c b/src/test/test-conf-parser.c
new file mode 100644 (file)
index 0000000..1d74e1b
--- /dev/null
@@ -0,0 +1,262 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2015 Ronny Chevalier
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "conf-parser.h"
+#include "log.h"
+#include "macro.h"
+#include "string-util.h"
+#include "strv.h"
+#include "util.h"
+
+static void test_config_parse_path_one(const char *rvalue, const char *expected) {
+        char *path = NULL;
+
+        assert_se(config_parse_path("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &path, NULL) >= 0);
+        assert_se(streq_ptr(expected, path));
+
+        free(path);
+}
+
+static void test_config_parse_log_level_one(const char *rvalue, int expected) {
+        int log_level = 0;
+
+        assert_se(config_parse_log_level("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &log_level, NULL) >= 0);
+        assert_se(expected == log_level);
+}
+
+#if 0 /// UNNEEDED by elogind
+static void test_config_parse_log_facility_one(const char *rvalue, int expected) {
+        int log_facility = 0;
+
+        assert_se(config_parse_log_facility("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &log_facility, NULL) >= 0);
+        assert_se(expected == log_facility);
+}
+#endif // 0
+
+static void test_config_parse_iec_size_one(const char *rvalue, size_t expected) {
+        size_t iec_size = 0;
+
+        assert_se(config_parse_iec_size("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &iec_size, NULL) >= 0);
+        assert_se(expected == iec_size);
+}
+
+#if 0 /// UNNEEDED by elogind
+static void test_config_parse_si_size_one(const char *rvalue, size_t expected) {
+        size_t si_size = 0;
+
+        assert_se(config_parse_si_size("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &si_size, NULL) >= 0);
+        assert_se(expected == si_size);
+}
+#endif // 0
+
+static void test_config_parse_int_one(const char *rvalue, int expected) {
+        int v = -1;
+
+        assert_se(config_parse_int("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &v, NULL) >= 0);
+        assert_se(expected == v);
+}
+
+static void test_config_parse_unsigned_one(const char *rvalue, unsigned expected) {
+        unsigned v = 0;
+
+        assert_se(config_parse_unsigned("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &v, NULL) >= 0);
+        assert_se(expected == v);
+}
+
+static void test_config_parse_strv_one(const char *rvalue, char **expected) {
+        char **strv = 0;
+
+        assert_se(config_parse_strv("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &strv, NULL) >= 0);
+        assert_se(strv_equal(expected, strv));
+
+        strv_free(strv);
+}
+
+static void test_config_parse_mode_one(const char *rvalue, mode_t expected) {
+        mode_t v = 0;
+
+        assert_se(config_parse_mode("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &v, NULL) >= 0);
+        assert_se(expected == v);
+}
+
+static void test_config_parse_sec_one(const char *rvalue, usec_t expected) {
+        usec_t v = 0;
+
+        assert_se(config_parse_sec("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &v, NULL) >= 0);
+        assert_se(expected == v);
+}
+
+#if 0 /// UNNEEDED by elogind
+static void test_config_parse_nsec_one(const char *rvalue, nsec_t expected) {
+        nsec_t v = 0;
+
+        assert_se(config_parse_nsec("unit", "filename", 1, "nsection", 1, "lvalue", 0, rvalue, &v, NULL) >= 0);
+        assert_se(expected == v);
+}
+#endif // 0
+
+static void test_config_parse_path(void) {
+        test_config_parse_path_one("/path", "/path");
+        test_config_parse_path_one("/path//////////", "/path");
+        test_config_parse_path_one("///path/foo///bar////bar//", "/path/foo/bar/bar");
+
+        test_config_parse_path_one("not_absolute/path", NULL);
+}
+
+static void test_config_parse_log_level(void) {
+        test_config_parse_log_level_one("debug", LOG_DEBUG);
+        test_config_parse_log_level_one("info", LOG_INFO);
+
+        test_config_parse_log_level_one("garbage", 0);
+}
+
+#if 0 /// UNNEEDED by elogind
+static void test_config_parse_log_facility(void) {
+        test_config_parse_log_facility_one("mail", LOG_MAIL);
+        test_config_parse_log_facility_one("user", LOG_USER);
+
+        test_config_parse_log_facility_one("garbage", 0);
+}
+#endif // 0
+
+static void test_config_parse_iec_size(void) {
+        test_config_parse_iec_size_one("1024", 1024);
+        test_config_parse_iec_size_one("2K", 2048);
+        test_config_parse_iec_size_one("10M", 10 * 1024 * 1024);
+        test_config_parse_iec_size_one("1G", 1 * 1024 * 1024 * 1024);
+        test_config_parse_iec_size_one("0G", 0);
+        test_config_parse_iec_size_one("0", 0);
+
+        test_config_parse_iec_size_one("-982", 0);
+        test_config_parse_iec_size_one("49874444198739873000000G", 0);
+        test_config_parse_iec_size_one("garbage", 0);
+}
+
+#if 0 /// UNNEEDED by elogind
+static void test_config_parse_si_size(void) {
+        test_config_parse_si_size_one("1024", 1024);
+        test_config_parse_si_size_one("2K", 2000);
+        test_config_parse_si_size_one("10M", 10 * 1000 * 1000);
+        test_config_parse_si_size_one("1G", 1 * 1000 * 1000 * 1000);
+        test_config_parse_si_size_one("0G", 0);
+        test_config_parse_si_size_one("0", 0);
+
+        test_config_parse_si_size_one("-982", 0);
+        test_config_parse_si_size_one("49874444198739873000000G", 0);
+        test_config_parse_si_size_one("garbage", 0);
+}
+#endif // 0
+
+static void test_config_parse_int(void) {
+        test_config_parse_int_one("1024", 1024);
+        test_config_parse_int_one("-1024", -1024);
+        test_config_parse_int_one("0", 0);
+
+        test_config_parse_int_one("99999999999999999999999999999999999999999999999999999999", -1);
+        test_config_parse_int_one("-99999999999999999999999999999999999999999999999999999999", -1);
+        test_config_parse_int_one("1G", -1);
+        test_config_parse_int_one("garbage", -1);
+}
+
+static void test_config_parse_unsigned(void) {
+        test_config_parse_unsigned_one("10241024", 10241024);
+        test_config_parse_unsigned_one("1024", 1024);
+        test_config_parse_unsigned_one("0", 0);
+
+        test_config_parse_unsigned_one("99999999999999999999999999999999999999999999999999999999", 0);
+        test_config_parse_unsigned_one("1G", 0);
+        test_config_parse_unsigned_one("garbage", 0);
+        test_config_parse_unsigned_one("1000garbage", 0);
+}
+
+static void test_config_parse_strv(void) {
+        test_config_parse_strv_one("", STRV_MAKE_EMPTY);
+        test_config_parse_strv_one("foo", STRV_MAKE("foo"));
+        test_config_parse_strv_one("foo bar foo", STRV_MAKE("foo", "bar", "foo"));
+        test_config_parse_strv_one("\"foo bar\" foo", STRV_MAKE("foo bar", "foo"));
+}
+
+static void test_config_parse_mode(void) {
+        test_config_parse_mode_one("777", 0777);
+        test_config_parse_mode_one("644", 0644);
+
+        test_config_parse_mode_one("-777", 0);
+        test_config_parse_mode_one("999", 0);
+        test_config_parse_mode_one("garbage", 0);
+        test_config_parse_mode_one("777garbage", 0);
+        test_config_parse_mode_one("777 garbage", 0);
+}
+
+static void test_config_parse_sec(void) {
+        test_config_parse_sec_one("1", 1 * USEC_PER_SEC);
+        test_config_parse_sec_one("1s", 1 * USEC_PER_SEC);
+        test_config_parse_sec_one("100ms", 100 * USEC_PER_MSEC);
+        test_config_parse_sec_one("5min 20s", 5 * 60 * USEC_PER_SEC + 20 * USEC_PER_SEC);
+
+        test_config_parse_sec_one("-1", 0);
+        test_config_parse_sec_one("10foo", 0);
+        test_config_parse_sec_one("garbage", 0);
+}
+
+#if 0 /// UNNEEDED by elogind
+static void test_config_parse_nsec(void) {
+        test_config_parse_nsec_one("1", 1);
+        test_config_parse_nsec_one("1s", 1 * NSEC_PER_SEC);
+        test_config_parse_nsec_one("100ms", 100 * NSEC_PER_MSEC);
+        test_config_parse_nsec_one("5min 20s", 5 * 60 * NSEC_PER_SEC + 20 * NSEC_PER_SEC);
+
+        test_config_parse_nsec_one("-1", 0);
+        test_config_parse_nsec_one("10foo", 0);
+        test_config_parse_nsec_one("garbage", 0);
+}
+
+static void test_config_parse_iec_uint64(void) {
+        uint64_t offset = 0;
+        assert_se(config_parse_iec_uint64(NULL, "/this/file", 11, "Section", 22, "Size", 0, "4M", &offset, NULL) == 0);
+        assert_se(offset == 4 * 1024 * 1024);
+
+        assert_se(config_parse_iec_uint64(NULL, "/this/file", 11, "Section", 22, "Size", 0, "4.5M", &offset, NULL) == 0);
+}
+#endif // 0
+
+int main(int argc, char **argv) {
+        log_parse_environment();
+        log_open();
+
+        test_config_parse_path();
+        test_config_parse_log_level();
+#if 0 /// UNNEEDED by elogind
+        test_config_parse_log_facility();
+#endif // 0
+        test_config_parse_iec_size();
+#if 0 /// UNNEEDED by elogind
+        test_config_parse_si_size();
+#endif // 0
+        test_config_parse_int();
+        test_config_parse_unsigned();
+        test_config_parse_strv();
+        test_config_parse_mode();
+        test_config_parse_sec();
+#if 0 /// UNNEEDED by elogind
+        test_config_parse_nsec();
+        test_config_parse_iec_uint64();
+#endif // 0
+
+        return 0;
+}
diff --git a/src/test/test-copy.c b/src/test/test-copy.c
new file mode 100644 (file)
index 0000000..0b91951
--- /dev/null
@@ -0,0 +1,264 @@
+/***
+  This file is part of systemd
+
+  Copyright 2014 Ronny Chevalier
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <unistd.h>
+
+#include "alloc-util.h"
+#include "copy.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "log.h"
+#include "macro.h"
+#include "mkdir.h"
+#include "path-util.h"
+#include "rm-rf.h"
+#include "string-util.h"
+#include "strv.h"
+#include "user-util.h"
+#include "util.h"
+
+#if 0 /// UNNEEDED by elogind
+static void test_copy_file(void) {
+        _cleanup_free_ char *buf = NULL;
+        char fn[] = "/tmp/test-copy_file.XXXXXX";
+        char fn_copy[] = "/tmp/test-copy_file.XXXXXX";
+        size_t sz = 0;
+        int fd;
+
+        log_info("%s", __func__);
+
+        fd = mkostemp_safe(fn);
+        assert_se(fd >= 0);
+        close(fd);
+
+        fd = mkostemp_safe(fn_copy);
+        assert_se(fd >= 0);
+        close(fd);
+
+        assert_se(write_string_file(fn, "foo bar bar bar foo", WRITE_STRING_FILE_CREATE) == 0);
+
+        assert_se(copy_file(fn, fn_copy, 0, 0644, 0, COPY_REFLINK) == 0);
+
+        assert_se(read_full_file(fn_copy, &buf, &sz) == 0);
+        assert_se(streq(buf, "foo bar bar bar foo\n"));
+        assert_se(sz == 20);
+
+        unlink(fn);
+        unlink(fn_copy);
+}
+
+static void test_copy_file_fd(void) {
+        char in_fn[] = "/tmp/test-copy-file-fd-XXXXXX";
+        char out_fn[] = "/tmp/test-copy-file-fd-XXXXXX";
+        _cleanup_close_ int in_fd = -1, out_fd = -1;
+        char text[] = "boohoo\nfoo\n\tbar\n";
+        char buf[64] = {0};
+
+        log_info("%s", __func__);
+
+        in_fd = mkostemp_safe(in_fn);
+        assert_se(in_fd >= 0);
+        out_fd = mkostemp_safe(out_fn);
+        assert_se(out_fd >= 0);
+
+        assert_se(write_string_file(in_fn, text, WRITE_STRING_FILE_CREATE) == 0);
+        assert_se(copy_file_fd("/a/file/which/does/not/exist/i/guess", out_fd, COPY_REFLINK) < 0);
+        assert_se(copy_file_fd(in_fn, out_fd, COPY_REFLINK) >= 0);
+        assert_se(lseek(out_fd, SEEK_SET, 0) == 0);
+
+        assert_se(read(out_fd, buf, sizeof(buf)) == sizeof(text) - 1);
+        assert_se(streq(buf, text));
+
+        unlink(in_fn);
+        unlink(out_fn);
+}
+
+static void test_copy_tree(void) {
+        char original_dir[] = "/tmp/test-copy_tree/";
+        char copy_dir[] = "/tmp/test-copy_tree-copy/";
+        char **files = STRV_MAKE("file", "dir1/file", "dir1/dir2/file", "dir1/dir2/dir3/dir4/dir5/file");
+        char **links = STRV_MAKE("link", "file",
+                                 "link2", "dir1/file");
+        char **p, **link;
+        const char *unixsockp;
+        struct stat st;
+
+        log_info("%s", __func__);
+
+        (void) rm_rf(copy_dir, REMOVE_ROOT|REMOVE_PHYSICAL);
+        (void) rm_rf(original_dir, REMOVE_ROOT|REMOVE_PHYSICAL);
+
+        STRV_FOREACH(p, files) {
+                _cleanup_free_ char *f;
+
+                assert_se((f = strappend(original_dir, *p)));
+
+                assert_se(mkdir_parents(f, 0755) >= 0);
+                assert_se(write_string_file(f, "file", WRITE_STRING_FILE_CREATE) == 0);
+        }
+
+        STRV_FOREACH_PAIR(link, p, links) {
+                _cleanup_free_ char *f, *l;
+
+                assert_se((f = strappend(original_dir, *p)));
+                assert_se((l = strappend(original_dir, *link)));
+
+                assert_se(mkdir_parents(l, 0755) >= 0);
+                assert_se(symlink(f, l) == 0);
+        }
+
+        unixsockp = strjoina(original_dir, "unixsock");
+        assert_se(mknod(unixsockp, S_IFSOCK|0644, 0) >= 0);
+
+        assert_se(copy_tree(original_dir, copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_MERGE) == 0);
+
+        STRV_FOREACH(p, files) {
+                _cleanup_free_ char *buf = NULL, *f;
+                size_t sz = 0;
+
+                assert_se((f = strappend(copy_dir, *p)));
+
+                assert_se(access(f, F_OK) == 0);
+                assert_se(read_full_file(f, &buf, &sz) == 0);
+                assert_se(streq(buf, "file\n"));
+        }
+
+        STRV_FOREACH_PAIR(link, p, links) {
+                _cleanup_free_ char *target = NULL, *f, *l;
+
+                assert_se((f = strjoin(original_dir, *p)));
+                assert_se((l = strjoin(copy_dir, *link)));
+
+                assert_se(readlink_and_canonicalize(l, NULL, &target) == 0);
+                assert_se(path_equal(f, target));
+        }
+
+        unixsockp = strjoina(copy_dir, "unixsock");
+        assert_se(stat(unixsockp, &st) >= 0);
+        assert_se(S_ISSOCK(st.st_mode));
+
+        assert_se(copy_tree(original_dir, copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK) < 0);
+        assert_se(copy_tree("/tmp/inexistent/foo/bar/fsdoi", copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK) < 0);
+
+        (void) rm_rf(copy_dir, REMOVE_ROOT|REMOVE_PHYSICAL);
+        (void) rm_rf(original_dir, REMOVE_ROOT|REMOVE_PHYSICAL);
+}
+#endif // 0
+
+static void test_copy_bytes(void) {
+        _cleanup_close_pair_ int pipefd[2] = {-1, -1};
+        _cleanup_close_ int infd = -1;
+        int r, r2;
+        char buf[1024], buf2[1024];
+
+        infd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC);
+        if (infd < 0)
+                infd = open("/etc/os-release", O_RDONLY|O_CLOEXEC);
+        assert_se(infd >= 0);
+
+        assert_se(pipe2(pipefd, O_CLOEXEC) == 0);
+
+        r = copy_bytes(infd, pipefd[1], (uint64_t) -1, 0);
+        assert_se(r == 0);
+
+        r = read(pipefd[0], buf, sizeof(buf));
+        assert_se(r >= 0);
+
+        assert_se(lseek(infd, 0, SEEK_SET) == 0);
+        r2 = read(infd, buf2, sizeof(buf2));
+        assert_se(r == r2);
+
+        assert_se(strneq(buf, buf2, r));
+
+        /* test copy_bytes with invalid descriptors */
+        r = copy_bytes(pipefd[0], pipefd[0], 1, 0);
+        assert_se(r == -EBADF);
+
+        r = copy_bytes(pipefd[1], pipefd[1], 1, 0);
+        assert_se(r == -EBADF);
+
+        r = copy_bytes(pipefd[1], infd, 1, 0);
+        assert_se(r == -EBADF);
+}
+
+static void test_copy_bytes_regular_file(const char *src, bool try_reflink, uint64_t max_bytes) {
+        char fn2[] = "/tmp/test-copy-file-XXXXXX";
+        char fn3[] = "/tmp/test-copy-file-XXXXXX";
+        _cleanup_close_ int fd = -1, fd2 = -1, fd3 = -1;
+        int r;
+        struct stat buf, buf2, buf3;
+
+        log_info("%s try_reflink=%s max_bytes=%" PRIu64, __func__, yes_no(try_reflink), max_bytes);
+
+        fd = open(src, O_RDONLY | O_CLOEXEC | O_NOCTTY);
+        assert_se(fd >= 0);
+
+        fd2 = mkostemp_safe(fn2);
+        assert_se(fd2 >= 0);
+
+        fd3 = mkostemp_safe(fn3);
+        assert_se(fd3 >= 0);
+
+        r = copy_bytes(fd, fd2, max_bytes, try_reflink ? COPY_REFLINK : 0);
+        if (max_bytes == (uint64_t) -1)
+                assert_se(r == 0);
+        else
+                assert_se(IN_SET(r, 0, 1));
+
+        assert_se(lseek(fd2, 0, SEEK_SET) == 0);
+
+        r = copy_bytes(fd2, fd3, max_bytes, try_reflink ? COPY_REFLINK : 0);
+        if (max_bytes == (uint64_t) -1)
+                assert_se(r == 0);
+        else
+                /* We cannot distinguish between the input being exactly max_bytes
+                 * or longer than max_bytes (without trying to read one more byte,
+                 * or calling stat, or FION_READ, etc, and we don't want to do any
+                 * of that). So we expect "truncation" since we know that file we
+                 * are copying is exactly max_bytes bytes. */
+                assert_se(r == 1);
+
+        assert_se(fstat(fd, &buf) == 0);
+        assert_se(fstat(fd2, &buf2) == 0);
+        assert_se(fstat(fd3, &buf3) == 0);
+
+        assert_se((uint64_t) buf2.st_size == MIN((uint64_t) buf.st_size, max_bytes));
+        assert_se(buf3.st_size == buf2.st_size);
+
+        unlink(fn2);
+        unlink(fn3);
+}
+
+int main(int argc, char *argv[]) {
+#if 0 /// UNNEEDED by elogind
+        test_copy_file();
+        test_copy_file_fd();
+        test_copy_tree();
+#endif // 0
+        test_copy_bytes();
+        test_copy_bytes_regular_file(argv[0], false, (uint64_t) -1);
+        test_copy_bytes_regular_file(argv[0], true, (uint64_t) -1);
+        test_copy_bytes_regular_file(argv[0], false, 1000); /* smaller than copy buffer size */
+        test_copy_bytes_regular_file(argv[0], true, 1000);
+        test_copy_bytes_regular_file(argv[0], false, 32000); /* larger than copy buffer size */
+        test_copy_bytes_regular_file(argv[0], true, 32000);
+
+        return 0;
+}
diff --git a/src/test/test-ellipsize.c b/src/test/test-ellipsize.c
new file mode 100644 (file)
index 0000000..d4f09b0
--- /dev/null
@@ -0,0 +1,45 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2013 Shawn Landden
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdio.h>
+
+#include "alloc-util.h"
+#include "def.h"
+#include "string-util.h"
+#include "terminal-util.h"
+#include "util.h"
+
+static void test_one(const char *p) {
+        _cleanup_free_ char *t;
+        t = ellipsize(p, columns(), 70);
+        puts(t);
+}
+
+int main(int argc, char *argv[]) {
+        test_one(DIGITS LETTERS DIGITS LETTERS);
+        test_one("한국어한국어한국어한국어한국어한국어한국어한국어한국어한국어한국어한국어한국어한국어한국어한국어한국어한국어");
+        test_one("-日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国日本国");
+        test_one("中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国-中国中国中国中国中国中国中国中国中国中国中国中国中国");
+        test_one("sÿstëmd sÿstëmd sÿstëmd sÿstëmd sÿstëmd sÿstëmd sÿstëmd sÿstëmd sÿstëmd sÿstëmd sÿstëmd sÿstëmd sÿstëmd");
+        test_one("🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮🐮");
+        test_one("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.");
+        test_one("shórt");
+
+        return 0;
+}
diff --git a/src/test/test-escape.c b/src/test/test-escape.c
new file mode 100644 (file)
index 0000000..2c2270b
--- /dev/null
@@ -0,0 +1,118 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "alloc-util.h"
+#include "escape.h"
+#include "macro.h"
+
+static void test_cescape(void) {
+        _cleanup_free_ char *escaped;
+
+        assert_se(escaped = cescape("abc\\\"\b\f\n\r\t\v\a\003\177\234\313"));
+        assert_se(streq(escaped, "abc\\\\\\\"\\b\\f\\n\\r\\t\\v\\a\\003\\177\\234\\313"));
+}
+
+static void test_cunescape(void) {
+        _cleanup_free_ char *unescaped;
+
+        assert_se(cunescape("abc\\\\\\\"\\b\\f\\a\\n\\r\\t\\v\\003\\177\\234\\313\\000\\x00", 0, &unescaped) < 0);
+        assert_se(cunescape("abc\\\\\\\"\\b\\f\\a\\n\\r\\t\\v\\003\\177\\234\\313\\000\\x00", UNESCAPE_RELAX, &unescaped) >= 0);
+        assert_se(streq_ptr(unescaped, "abc\\\"\b\f\a\n\r\t\v\003\177\234\313\\000\\x00"));
+        unescaped = mfree(unescaped);
+
+        /* incomplete sequences */
+        assert_se(cunescape("\\x0", 0, &unescaped) < 0);
+        assert_se(cunescape("\\x0", UNESCAPE_RELAX, &unescaped) >= 0);
+        assert_se(streq_ptr(unescaped, "\\x0"));
+        unescaped = mfree(unescaped);
+
+        assert_se(cunescape("\\x", 0, &unescaped) < 0);
+        assert_se(cunescape("\\x", UNESCAPE_RELAX, &unescaped) >= 0);
+        assert_se(streq_ptr(unescaped, "\\x"));
+        unescaped = mfree(unescaped);
+
+        assert_se(cunescape("\\", 0, &unescaped) < 0);
+        assert_se(cunescape("\\", UNESCAPE_RELAX, &unescaped) >= 0);
+        assert_se(streq_ptr(unescaped, "\\"));
+        unescaped = mfree(unescaped);
+
+        assert_se(cunescape("\\11", 0, &unescaped) < 0);
+        assert_se(cunescape("\\11", UNESCAPE_RELAX, &unescaped) >= 0);
+        assert_se(streq_ptr(unescaped, "\\11"));
+        unescaped = mfree(unescaped);
+
+        assert_se(cunescape("\\1", 0, &unescaped) < 0);
+        assert_se(cunescape("\\1", UNESCAPE_RELAX, &unescaped) >= 0);
+        assert_se(streq_ptr(unescaped, "\\1"));
+        unescaped = mfree(unescaped);
+
+        assert_se(cunescape("\\u0000", 0, &unescaped) < 0);
+        assert_se(cunescape("\\u00DF\\U000000df\\u03a0\\U00000041", UNESCAPE_RELAX, &unescaped) >= 0);
+        assert_se(streq_ptr(unescaped, "ßßΠA"));
+        unescaped = mfree(unescaped);
+
+        assert_se(cunescape("\\073", 0, &unescaped) >= 0);
+        assert_se(streq_ptr(unescaped, ";"));
+}
+
+#if 0 /// UNNEEDED by elogind
+static void test_shell_escape_one(const char *s, const char *bad, const char *expected) {
+        _cleanup_free_ char *r;
+
+        assert_se(r = shell_escape(s, bad));
+        assert_se(streq_ptr(r, expected));
+}
+
+static void test_shell_escape(void) {
+        test_shell_escape_one("", "", "");
+        test_shell_escape_one("\\", "", "\\\\");
+        test_shell_escape_one("foobar", "", "foobar");
+        test_shell_escape_one("foobar", "o", "f\\o\\obar");
+        test_shell_escape_one("foo:bar,baz", ",:", "foo\\:bar\\,baz");
+}
+
+static void test_shell_maybe_quote_one(const char *s, const char *expected) {
+        _cleanup_free_ char *r;
+
+        assert_se(r = shell_maybe_quote(s));
+        assert_se(streq(r, expected));
+}
+
+static void test_shell_maybe_quote(void) {
+
+        test_shell_maybe_quote_one("", "");
+        test_shell_maybe_quote_one("\\", "\"\\\\\"");
+        test_shell_maybe_quote_one("\"", "\"\\\"\"");
+        test_shell_maybe_quote_one("foobar", "foobar");
+        test_shell_maybe_quote_one("foo bar", "\"foo bar\"");
+        test_shell_maybe_quote_one("foo \"bar\" waldo", "\"foo \\\"bar\\\" waldo\"");
+        test_shell_maybe_quote_one("foo$bar", "\"foo\\$bar\"");
+}
+#endif // 0
+
+int main(int argc, char *argv[]) {
+        test_cescape();
+        test_cunescape();
+#if 0 /// UNNEEDED by elogind
+        test_shell_escape();
+        test_shell_maybe_quote();
+#endif // 0
+
+        return 0;
+}
diff --git a/src/test/test-exec-util.c b/src/test/test-exec-util.c
new file mode 100644 (file)
index 0000000..1520dd1
--- /dev/null
@@ -0,0 +1,352 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+  Copyright 2013 Thomas H.P. Andersen
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "alloc-util.h"
+#include "copy.h"
+#include "def.h"
+#include "env-util.h"
+#include "exec-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "log.h"
+#include "macro.h"
+#include "rm-rf.h"
+#include "string-util.h"
+#include "strv.h"
+
+static int here = 0, here2 = 0, here3 = 0;
+void *ignore_stdout_args[] = {&here, &here2, &here3};
+
+/* noop handlers, just check that arguments are passed correctly */
+static int ignore_stdout_func(int fd, void *arg) {
+        assert(fd >= 0);
+        assert(arg == &here);
+        safe_close(fd);
+
+        return 0;
+}
+static int ignore_stdout_func2(int fd, void *arg) {
+        assert(fd >= 0);
+        assert(arg == &here2);
+        safe_close(fd);
+
+        return 0;
+}
+static int ignore_stdout_func3(int fd, void *arg) {
+        assert(fd >= 0);
+        assert(arg == &here3);
+        safe_close(fd);
+
+        return 0;
+}
+
+static const gather_stdout_callback_t ignore_stdout[] = {
+        ignore_stdout_func,
+        ignore_stdout_func2,
+        ignore_stdout_func3,
+};
+
+static void test_execute_directory(bool gather_stdout) {
+        char template_lo[] = "/tmp/test-exec-util.XXXXXXX";
+        char template_hi[] = "/tmp/test-exec-util.XXXXXXX";
+        const char * dirs[] = {template_hi, template_lo, NULL};
+        const char *name, *name2, *name3, *overridden, *override, *masked, *mask;
+
+        log_info("/* %s (%s) */", __func__, gather_stdout ? "gathering stdout" : "asynchronous");
+
+        assert_se(mkdtemp(template_lo));
+        assert_se(mkdtemp(template_hi));
+
+        name = strjoina(template_lo, "/script");
+        name2 = strjoina(template_hi, "/script2");
+        name3 = strjoina(template_lo, "/useless");
+        overridden = strjoina(template_lo, "/overridden");
+        override = strjoina(template_hi, "/overridden");
+        masked = strjoina(template_lo, "/masked");
+        mask = strjoina(template_hi, "/masked");
+
+        assert_se(write_string_file(name,
+                                    "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/it_works",
+                                    WRITE_STRING_FILE_CREATE) == 0);
+        assert_se(write_string_file(name2,
+                                    "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/it_works2",
+                                    WRITE_STRING_FILE_CREATE) == 0);
+        assert_se(write_string_file(overridden,
+                                    "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/failed",
+                                    WRITE_STRING_FILE_CREATE) == 0);
+        assert_se(write_string_file(override,
+                                    "#!/bin/sh\necho 'Executing '$0",
+                                    WRITE_STRING_FILE_CREATE) == 0);
+        assert_se(write_string_file(masked,
+                                    "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/failed",
+                                    WRITE_STRING_FILE_CREATE) == 0);
+        assert_se(symlink("/dev/null", mask) == 0);
+        assert_se(touch(name3) >= 0);
+
+        assert_se(chmod(name, 0755) == 0);
+        assert_se(chmod(name2, 0755) == 0);
+        assert_se(chmod(overridden, 0755) == 0);
+        assert_se(chmod(override, 0755) == 0);
+        assert_se(chmod(masked, 0755) == 0);
+
+        if (gather_stdout)
+                execute_directories(dirs, DEFAULT_TIMEOUT_USEC, ignore_stdout, ignore_stdout_args, NULL);
+        else
+                execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, NULL);
+
+        assert_se(chdir(template_lo) == 0);
+        assert_se(access("it_works", F_OK) >= 0);
+        assert_se(access("failed", F_OK) < 0);
+
+        assert_se(chdir(template_hi) == 0);
+        assert_se(access("it_works2", F_OK) >= 0);
+        assert_se(access("failed", F_OK) < 0);
+
+        (void) rm_rf(template_lo, REMOVE_ROOT|REMOVE_PHYSICAL);
+        (void) rm_rf(template_hi, REMOVE_ROOT|REMOVE_PHYSICAL);
+}
+
+static void test_execution_order(void) {
+        char template_lo[] = "/tmp/test-exec-util-lo.XXXXXXX";
+        char template_hi[] = "/tmp/test-exec-util-hi.XXXXXXX";
+        const char *dirs[] = {template_hi, template_lo, NULL};
+        const char *name, *name2, *name3, *overridden, *override, *masked, *mask;
+        const char *output, *t;
+        _cleanup_free_ char *contents = NULL;
+
+        assert_se(mkdtemp(template_lo));
+        assert_se(mkdtemp(template_hi));
+
+        output = strjoina(template_hi, "/output");
+
+        log_info("/* %s >>%s */", __func__, output);
+
+        /* write files in "random" order */
+        name2 = strjoina(template_lo, "/90-bar");
+        name = strjoina(template_hi, "/80-foo");
+        name3 = strjoina(template_lo, "/last");
+        overridden = strjoina(template_lo, "/30-override");
+        override = strjoina(template_hi, "/30-override");
+        masked = strjoina(template_lo, "/10-masked");
+        mask = strjoina(template_hi, "/10-masked");
+
+        t = strjoina("#!/bin/sh\necho $(basename $0) >>", output);
+        assert_se(write_string_file(name, t, WRITE_STRING_FILE_CREATE) == 0);
+
+        t = strjoina("#!/bin/sh\necho $(basename $0) >>", output);
+        assert_se(write_string_file(name2, t, WRITE_STRING_FILE_CREATE) == 0);
+
+        t = strjoina("#!/bin/sh\necho $(basename $0) >>", output);
+        assert_se(write_string_file(name3, t, WRITE_STRING_FILE_CREATE) == 0);
+
+        t = strjoina("#!/bin/sh\necho OVERRIDDEN >>", output);
+        assert_se(write_string_file(overridden, t, WRITE_STRING_FILE_CREATE) == 0);
+
+        t = strjoina("#!/bin/sh\necho $(basename $0) >>", output);
+        assert_se(write_string_file(override, t, WRITE_STRING_FILE_CREATE) == 0);
+
+        t = strjoina("#!/bin/sh\necho MASKED >>", output);
+        assert_se(write_string_file(masked, t, WRITE_STRING_FILE_CREATE) == 0);
+
+        assert_se(symlink("/dev/null", mask) == 0);
+
+        assert_se(chmod(name, 0755) == 0);
+        assert_se(chmod(name2, 0755) == 0);
+        assert_se(chmod(name3, 0755) == 0);
+        assert_se(chmod(overridden, 0755) == 0);
+        assert_se(chmod(override, 0755) == 0);
+        assert_se(chmod(masked, 0755) == 0);
+
+        execute_directories(dirs, DEFAULT_TIMEOUT_USEC, ignore_stdout, ignore_stdout_args, NULL);
+
+        assert_se(read_full_file(output, &contents, NULL) >= 0);
+        assert_se(streq(contents, "30-override\n80-foo\n90-bar\nlast\n"));
+
+        (void) rm_rf(template_lo, REMOVE_ROOT|REMOVE_PHYSICAL);
+        (void) rm_rf(template_hi, REMOVE_ROOT|REMOVE_PHYSICAL);
+}
+
+static int gather_stdout_one(int fd, void *arg) {
+        char ***s = arg, *t;
+        char buf[128] = {};
+
+        assert_se(s);
+        assert_se(read(fd, buf, sizeof buf) >= 0);
+        safe_close(fd);
+
+        assert_se(t = strndup(buf, sizeof buf));
+        assert_se(strv_push(s, t) >= 0);
+
+        return 0;
+}
+static int gather_stdout_two(int fd, void *arg) {
+        char ***s = arg, **t;
+
+        STRV_FOREACH(t, *s)
+                assert_se(write(fd, *t, strlen(*t)) == (ssize_t) strlen(*t));
+        safe_close(fd);
+
+        return 0;
+}
+static int gather_stdout_three(int fd, void *arg) {
+        char **s = arg;
+        char buf[128] = {};
+
+        assert_se(read(fd, buf, sizeof buf - 1) > 0);
+        safe_close(fd);
+        assert_se(*s = strndup(buf, sizeof buf));
+
+        return 0;
+}
+
+const gather_stdout_callback_t const gather_stdout[] = {
+        gather_stdout_one,
+        gather_stdout_two,
+        gather_stdout_three,
+};
+
+
+static void test_stdout_gathering(void) {
+        char template[] = "/tmp/test-exec-util.XXXXXXX";
+        const char *dirs[] = {template, NULL};
+        const char *name, *name2, *name3;
+        int r;
+
+        char **tmp = NULL; /* this is only used in the forked process, no cleanup here */
+        _cleanup_free_ char *output = NULL;
+
+        void* args[] = {&tmp, &tmp, &output};
+
+        assert_se(mkdtemp(template));
+
+        log_info("/* %s */", __func__);
+
+        /* write files */
+        name = strjoina(template, "/10-foo");
+        name2 = strjoina(template, "/20-bar");
+        name3 = strjoina(template, "/30-last");
+
+        assert_se(write_string_file(name,
+                                    "#!/bin/sh\necho a\necho b\necho c\n",
+                                    WRITE_STRING_FILE_CREATE) == 0);
+        assert_se(write_string_file(name2,
+                                    "#!/bin/sh\necho d\n",
+                                    WRITE_STRING_FILE_CREATE) == 0);
+        assert_se(write_string_file(name3,
+                                    "#!/bin/sh\nsleep 1",
+                                    WRITE_STRING_FILE_CREATE) == 0);
+
+        assert_se(chmod(name, 0755) == 0);
+        assert_se(chmod(name2, 0755) == 0);
+        assert_se(chmod(name3, 0755) == 0);
+
+        r = execute_directories(dirs, DEFAULT_TIMEOUT_USEC, gather_stdout, args, NULL);
+        assert_se(r >= 0);
+
+        log_info("got: %s", output);
+
+        assert_se(streq(output, "a\nb\nc\nd\n"));
+}
+
+#if 0 /// UNNEEDED by elogind
+static void test_environment_gathering(void) {
+        char template[] = "/tmp/test-exec-util.XXXXXXX", **p;
+        const char *dirs[] = {template, NULL};
+        const char *name, *name2, *name3;
+        int r;
+
+        char **tmp = NULL; /* this is only used in the forked process, no cleanup here */
+        _cleanup_strv_free_ char **env = NULL;
+
+        void* const args[] = { &tmp, &tmp, &env };
+
+        assert_se(mkdtemp(template));
+
+        log_info("/* %s */", __func__);
+
+        /* write files */
+        name = strjoina(template, "/10-foo");
+        name2 = strjoina(template, "/20-bar");
+        name3 = strjoina(template, "/30-last");
+
+        assert_se(write_string_file(name,
+                                    "#!/bin/sh\n"
+                                    "echo A=23\n",
+                                    WRITE_STRING_FILE_CREATE) == 0);
+        assert_se(write_string_file(name2,
+                                    "#!/bin/sh\n"
+                                    "echo A=22:$A\n\n\n",            /* substitution from previous generator */
+                                    WRITE_STRING_FILE_CREATE) == 0);
+        assert_se(write_string_file(name3,
+                                    "#!/bin/sh\n"
+                                    "echo A=$A:24\n"
+                                    "echo B=12\n"
+                                    "echo C=000\n"
+                                    "echo C=001\n"                    /* variable overwriting */
+                                     /* various invalid entries */
+                                    "echo unset A\n"
+                                    "echo unset A=\n"
+                                    "echo unset A=B\n"
+                                    "echo unset \n"
+                                    "echo A B=C\n"
+                                    "echo A\n"
+                                    /* test variable assignment without newline */
+                                    "echo PATH=$PATH:/no/such/file",   /* no newline */
+                                    WRITE_STRING_FILE_CREATE) == 0);
+
+        assert_se(chmod(name, 0755) == 0);
+        assert_se(chmod(name2, 0755) == 0);
+        assert_se(chmod(name3, 0755) == 0);
+
+        r = execute_directories(dirs, DEFAULT_TIMEOUT_USEC, gather_environment, args, NULL);
+        assert_se(r >= 0);
+
+        STRV_FOREACH(p, env)
+                log_info("got env: \"%s\"", *p);
+
+        assert_se(streq(strv_env_get(env, "A"), "22:23:24"));
+        assert_se(streq(strv_env_get(env, "B"), "12"));
+        assert_se(streq(strv_env_get(env, "C"), "001"));
+        assert_se(endswith(strv_env_get(env, "PATH"), ":/no/such/file"));
+}
+#endif // 0
+
+int main(int argc, char *argv[]) {
+        log_set_max_level(LOG_DEBUG);
+        log_parse_environment();
+        log_open();
+
+        test_execute_directory(true);
+        test_execute_directory(false);
+        test_execution_order();
+        test_stdout_gathering();
+#if 0 /// UNNEEDED by elogind
+        test_environment_gathering();
+#endif // 0
+
+        return 0;
+}
diff --git a/src/test/test-extract-word.c b/src/test/test-extract-word.c
new file mode 100644 (file)
index 0000000..8b75ebd
--- /dev/null
@@ -0,0 +1,560 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+  Copyright 2013 Thomas H.P. Andersen
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "extract-word.h"
+#include "log.h"
+#include "string-util.h"
+
+static void test_extract_first_word(void) {
+        const char *p, *original;
+        char *t;
+
+        p = original = "foobar waldo";
+        assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
+        assert_se(streq(t, "foobar"));
+        free(t);
+        assert_se(p == original + 7);
+
+        assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
+        assert_se(streq(t, "waldo"));
+        free(t);
+        assert_se(isempty(p));
+
+        assert_se(extract_first_word(&p, &t, NULL, 0) == 0);
+        assert_se(!t);
+        assert_se(isempty(p));
+
+        p = original = "\"foobar\" \'waldo\'";
+        assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
+        assert_se(streq(t, "\"foobar\""));
+        free(t);
+        assert_se(p == original + 9);
+
+        assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
+        assert_se(streq(t, "\'waldo\'"));
+        free(t);
+        assert_se(isempty(p));
+
+        assert_se(extract_first_word(&p, &t, NULL, 0) == 0);
+        assert_se(!t);
+        assert_se(isempty(p));
+
+        p = original = "\"foobar\" \'waldo\'";
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES) > 0);
+        assert_se(streq(t, "foobar"));
+        free(t);
+        assert_se(p == original + 9);
+
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES) > 0);
+        assert_se(streq(t, "waldo"));
+        free(t);
+        assert_se(isempty(p));
+
+        assert_se(extract_first_word(&p, &t, NULL, 0) == 0);
+        assert_se(!t);
+        assert_se(isempty(p));
+
+        p = original = "\"";
+        assert_se(extract_first_word(&p, &t, NULL, 0) == 1);
+        assert_se(streq(t, "\""));
+        free(t);
+        assert_se(isempty(p));
+
+        p = original = "\"";
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES) == -EINVAL);
+        assert_se(p == original + 1);
+
+        p = original = "\'";
+        assert_se(extract_first_word(&p, &t, NULL, 0) == 1);
+        assert_se(streq(t, "\'"));
+        free(t);
+        assert_se(isempty(p));
+
+        p = original = "\'";
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES) == -EINVAL);
+        assert_se(p == original + 1);
+
+        p = original = "\'fooo";
+        assert_se(extract_first_word(&p, &t, NULL, 0) == 1);
+        assert_se(streq(t, "\'fooo"));
+        free(t);
+        assert_se(isempty(p));
+
+        p = original = "\'fooo";
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES) == -EINVAL);
+        assert_se(p == original + 5);
+
+        p = original = "\'fooo";
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_RELAX) > 0);
+        assert_se(streq(t, "fooo"));
+        free(t);
+        assert_se(isempty(p));
+
+        p = original = "\"fooo";
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_RELAX) > 0);
+        assert_se(streq(t, "fooo"));
+        free(t);
+        assert_se(isempty(p));
+
+        p = original = "yay\'foo\'bar";
+        assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
+        assert_se(streq(t, "yay\'foo\'bar"));
+        free(t);
+        assert_se(isempty(p));
+
+        p = original = "yay\'foo\'bar";
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES) > 0);
+        assert_se(streq(t, "yayfoobar"));
+        free(t);
+        assert_se(isempty(p));
+
+        p = original = "   foobar   ";
+        assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
+        assert_se(streq(t, "foobar"));
+        free(t);
+        assert_se(isempty(p));
+
+        p = original = " foo\\ba\\x6ar ";
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE) > 0);
+        assert_se(streq(t, "foo\ba\x6ar"));
+        free(t);
+        assert_se(isempty(p));
+
+        p = original = " foo\\ba\\x6ar ";
+        assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
+        assert_se(streq(t, "foobax6ar"));
+        free(t);
+        assert_se(isempty(p));
+
+        p = original = "    f\\u00f6o \"pi\\U0001F4A9le\"   ";
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE) > 0);
+        assert_se(streq(t, "föo"));
+        free(t);
+        assert_se(p == original + 13);
+
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE) > 0);
+        assert_se(streq(t, "pi\360\237\222\251le"));
+        free(t);
+        assert_se(isempty(p));
+
+        p = original = "fooo\\";
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_RELAX) > 0);
+        assert_se(streq(t, "fooo"));
+        free(t);
+        assert_se(isempty(p));
+
+        p = original = "fooo\\";
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE_RELAX) > 0);
+        assert_se(streq(t, "fooo\\"));
+        free(t);
+        assert_se(isempty(p));
+
+        p = original = "fooo\\";
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE_RELAX|EXTRACT_RELAX) > 0);
+        assert_se(streq(t, "fooo\\"));
+        free(t);
+        assert_se(isempty(p));
+
+        p = original = "fooo\\";
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX) > 0);
+        assert_se(streq(t, "fooo\\"));
+        free(t);
+        assert_se(isempty(p));
+
+        p = original = "\"foo\\";
+        assert_se(extract_first_word(&p, &t, NULL, 0) == -EINVAL);
+        assert_se(p == original + 5);
+
+        p = original = "\"foo\\";
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_RELAX) > 0);
+        assert_se(streq(t, "foo"));
+        free(t);
+        assert_se(isempty(p));
+
+        p = original = "foo::bar";
+        assert_se(extract_first_word(&p, &t, ":", 0) == 1);
+        assert_se(streq(t, "foo"));
+        free(t);
+        assert_se(p == original + 5);
+
+        assert_se(extract_first_word(&p, &t, ":", 0) == 1);
+        assert_se(streq(t, "bar"));
+        free(t);
+        assert_se(isempty(p));
+
+        assert_se(extract_first_word(&p, &t, ":", 0) == 0);
+        assert_se(!t);
+        assert_se(isempty(p));
+
+        p = original = "foo\\:bar::waldo";
+        assert_se(extract_first_word(&p, &t, ":", 0) == 1);
+        assert_se(streq(t, "foo:bar"));
+        free(t);
+        assert_se(p == original + 10);
+
+        assert_se(extract_first_word(&p, &t, ":", 0) == 1);
+        assert_se(streq(t, "waldo"));
+        free(t);
+        assert_se(isempty(p));
+
+        assert_se(extract_first_word(&p, &t, ":", 0) == 0);
+        assert_se(!t);
+        assert_se(isempty(p));
+
+        p = original = "\"foo\\";
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE_RELAX) == -EINVAL);
+        assert_se(p == original + 5);
+
+        p = original = "\"foo\\";
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE_RELAX|EXTRACT_RELAX) > 0);
+        assert_se(streq(t, "foo\\"));
+        free(t);
+        assert_se(isempty(p));
+
+        p = original = "\"foo\\";
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX|EXTRACT_RELAX) > 0);
+        assert_se(streq(t, "foo\\"));
+        free(t);
+        assert_se(isempty(p));
+
+        p = original = "fooo\\ bar quux";
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_RELAX) > 0);
+        assert_se(streq(t, "fooo bar"));
+        free(t);
+        assert_se(p == original + 10);
+
+        p = original = "fooo\\ bar quux";
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE_RELAX) > 0);
+        assert_se(streq(t, "fooo bar"));
+        free(t);
+        assert_se(p == original + 10);
+
+        p = original = "fooo\\ bar quux";
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE_RELAX|EXTRACT_RELAX) > 0);
+        assert_se(streq(t, "fooo bar"));
+        free(t);
+        assert_se(p == original + 10);
+
+        p = original = "fooo\\ bar quux";
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE) == -EINVAL);
+        assert_se(p == original + 5);
+
+        p = original = "fooo\\ bar quux";
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX) > 0);
+        assert_se(streq(t, "fooo\\ bar"));
+        free(t);
+        assert_se(p == original + 10);
+
+        p = original = "\\w+@\\K[\\d.]+";
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE) == -EINVAL);
+        assert_se(p == original + 1);
+
+        p = original = "\\w+@\\K[\\d.]+";
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX) > 0);
+        assert_se(streq(t, "\\w+@\\K[\\d.]+"));
+        free(t);
+        assert_se(isempty(p));
+
+        p = original = "\\w+\\b";
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX) > 0);
+        assert_se(streq(t, "\\w+\b"));
+        free(t);
+        assert_se(isempty(p));
+
+        p = original = "-N ''";
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES) > 0);
+        assert_se(streq(t, "-N"));
+        free(t);
+        assert_se(p == original + 3);
+
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES) > 0);
+        assert_se(streq(t, ""));
+        free(t);
+        assert_se(isempty(p));
+
+        p = original = ":foo\\:bar::waldo:";
+        assert_se(extract_first_word(&p, &t, ":", EXTRACT_DONT_COALESCE_SEPARATORS) == 1);
+        assert_se(t);
+        assert_se(streq(t, ""));
+        free(t);
+        assert_se(p == original + 1);
+
+        assert_se(extract_first_word(&p, &t, ":", EXTRACT_DONT_COALESCE_SEPARATORS) == 1);
+        assert_se(streq(t, "foo:bar"));
+        free(t);
+        assert_se(p == original + 10);
+
+        assert_se(extract_first_word(&p, &t, ":", EXTRACT_DONT_COALESCE_SEPARATORS) == 1);
+        assert_se(t);
+        assert_se(streq(t, ""));
+        free(t);
+        assert_se(p == original + 11);
+
+        assert_se(extract_first_word(&p, &t, ":", EXTRACT_DONT_COALESCE_SEPARATORS) == 1);
+        assert_se(streq(t, "waldo"));
+        free(t);
+        assert_se(p == original + 17);
+
+        assert_se(extract_first_word(&p, &t, ":", EXTRACT_DONT_COALESCE_SEPARATORS) == 1);
+        assert_se(streq(t, ""));
+        free(t);
+        assert_se(p == NULL);
+
+        assert_se(extract_first_word(&p, &t, ":", EXTRACT_DONT_COALESCE_SEPARATORS) == 0);
+        assert_se(!t);
+        assert_se(!p);
+
+        p = "foo\\xbar";
+        assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
+        assert_se(streq(t, "fooxbar"));
+        free(t);
+        assert_se(p == NULL);
+
+        p = "foo\\xbar";
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_RETAIN_ESCAPE) > 0);
+        assert_se(streq(t, "foo\\xbar"));
+        free(t);
+        assert_se(p == NULL);
+}
+
+#if 0 /// UNNEEDED by elogind
+static void test_extract_first_word_and_warn(void) {
+        const char *p, *original;
+        char *t;
+
+        p = original = "foobar waldo";
+        assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) > 0);
+        assert_se(streq(t, "foobar"));
+        free(t);
+        assert_se(p == original + 7);
+
+        assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) > 0);
+        assert_se(streq(t, "waldo"));
+        free(t);
+        assert_se(isempty(p));
+
+        assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) == 0);
+        assert_se(!t);
+        assert_se(isempty(p));
+
+        p = original = "\"foobar\" \'waldo\'";
+        assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES, NULL, "fake", 1, original) > 0);
+        assert_se(streq(t, "foobar"));
+        free(t);
+        assert_se(p == original + 9);
+
+        assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES, NULL, "fake", 1, original) > 0);
+        assert_se(streq(t, "waldo"));
+        free(t);
+        assert_se(isempty(p));
+
+        assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) == 0);
+        assert_se(!t);
+        assert_se(isempty(p));
+
+        p = original = "\"";
+        assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES, NULL, "fake", 1, original) == -EINVAL);
+        assert_se(p == original + 1);
+
+        p = original = "\'";
+        assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES, NULL, "fake", 1, original) == -EINVAL);
+        assert_se(p == original + 1);
+
+        p = original = "\'fooo";
+        assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES, NULL, "fake", 1, original) == -EINVAL);
+        assert_se(p == original + 5);
+
+        p = original = "\'fooo";
+        assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_RELAX, NULL, "fake", 1, original) > 0);
+        assert_se(streq(t, "fooo"));
+        free(t);
+        assert_se(isempty(p));
+
+        p = original = " foo\\ba\\x6ar ";
+        assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
+        assert_se(streq(t, "foo\ba\x6ar"));
+        free(t);
+        assert_se(isempty(p));
+
+        p = original = " foo\\ba\\x6ar ";
+        assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) > 0);
+        assert_se(streq(t, "foobax6ar"));
+        free(t);
+        assert_se(isempty(p));
+
+        p = original = "    f\\u00f6o \"pi\\U0001F4A9le\"   ";
+        assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
+        assert_se(streq(t, "föo"));
+        free(t);
+        assert_se(p == original + 13);
+
+        assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
+        assert_se(streq(t, "pi\360\237\222\251le"));
+        free(t);
+        assert_se(isempty(p));
+
+        p = original = "fooo\\";
+        assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_RELAX, NULL, "fake", 1, original) > 0);
+        assert_se(streq(t, "fooo"));
+        free(t);
+        assert_se(isempty(p));
+
+        p = original = "fooo\\";
+        assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) > 0);
+        assert_se(streq(t, "fooo\\"));
+        free(t);
+        assert_se(isempty(p));
+
+        p = original = "fooo\\";
+        assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
+        assert_se(streq(t, "fooo\\"));
+        free(t);
+        assert_se(isempty(p));
+
+        p = original = "\"foo\\";
+        assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES, NULL, "fake", 1, original) == -EINVAL);
+        assert_se(p == original + 5);
+
+        p = original = "\"foo\\";
+        assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_RELAX, NULL, "fake", 1, original) > 0);
+        assert_se(streq(t, "foo"));
+        free(t);
+        assert_se(isempty(p));
+
+        p = original = "\"foo\\";
+        assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE, NULL, "fake", 1, original) == -EINVAL);
+        assert_se(p == original + 5);
+
+        p = original = "\"foo\\";
+        assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE|EXTRACT_RELAX, NULL, "fake", 1, original) > 0);
+        assert_se(streq(t, "foo"));
+        free(t);
+        assert_se(isempty(p));
+
+        p = original = "fooo\\ bar quux";
+        assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_RELAX, NULL, "fake", 1, original) > 0);
+        assert_se(streq(t, "fooo bar"));
+        free(t);
+        assert_se(p == original + 10);
+
+        p = original = "fooo\\ bar quux";
+        assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) > 0);
+        assert_se(streq(t, "fooo bar"));
+        free(t);
+        assert_se(p == original + 10);
+
+        p = original = "fooo\\ bar quux";
+        assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
+        assert_se(streq(t, "fooo\\ bar"));
+        free(t);
+        assert_se(p == original + 10);
+
+        p = original = "\\w+@\\K[\\d.]+";
+        assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
+        assert_se(streq(t, "\\w+@\\K[\\d.]+"));
+        free(t);
+        assert_se(isempty(p));
+
+        p = original = "\\w+\\b";
+        assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
+        assert_se(streq(t, "\\w+\b"));
+        free(t);
+        assert_se(isempty(p));
+}
+
+static void test_extract_many_words(void) {
+        const char *p, *original;
+        char *a, *b, *c;
+
+        p = original = "foobar waldi piep";
+        assert_se(extract_many_words(&p, NULL, 0, &a, &b, &c, NULL) == 3);
+        assert_se(isempty(p));
+        assert_se(streq_ptr(a, "foobar"));
+        assert_se(streq_ptr(b, "waldi"));
+        assert_se(streq_ptr(c, "piep"));
+        free(a);
+        free(b);
+        free(c);
+
+        p = original = "'foobar' wa\"ld\"i   ";
+        assert_se(extract_many_words(&p, NULL, 0, &a, &b, &c, NULL) == 2);
+        assert_se(isempty(p));
+        assert_se(streq_ptr(a, "'foobar'"));
+        assert_se(streq_ptr(b, "wa\"ld\"i"));
+        assert_se(streq_ptr(c, NULL));
+        free(a);
+        free(b);
+
+        p = original = "'foobar' wa\"ld\"i   ";
+        assert_se(extract_many_words(&p, NULL, EXTRACT_QUOTES, &a, &b, &c, NULL) == 2);
+        assert_se(isempty(p));
+        assert_se(streq_ptr(a, "foobar"));
+        assert_se(streq_ptr(b, "waldi"));
+        assert_se(streq_ptr(c, NULL));
+        free(a);
+        free(b);
+
+        p = original = "";
+        assert_se(extract_many_words(&p, NULL, 0, &a, &b, &c, NULL) == 0);
+        assert_se(isempty(p));
+        assert_se(streq_ptr(a, NULL));
+        assert_se(streq_ptr(b, NULL));
+        assert_se(streq_ptr(c, NULL));
+
+        p = original = "  ";
+        assert_se(extract_many_words(&p, NULL, 0, &a, &b, &c, NULL) == 0);
+        assert_se(isempty(p));
+        assert_se(streq_ptr(a, NULL));
+        assert_se(streq_ptr(b, NULL));
+        assert_se(streq_ptr(c, NULL));
+
+        p = original = "foobar";
+        assert_se(extract_many_words(&p, NULL, 0, NULL) == 0);
+        assert_se(p == original);
+
+        p = original = "foobar waldi";
+        assert_se(extract_many_words(&p, NULL, 0, &a, NULL) == 1);
+        assert_se(p == original+7);
+        assert_se(streq_ptr(a, "foobar"));
+        free(a);
+
+        p = original = "     foobar    ";
+        assert_se(extract_many_words(&p, NULL, 0, &a, NULL) == 1);
+        assert_se(isempty(p));
+        assert_se(streq_ptr(a, "foobar"));
+        free(a);
+}
+#endif // 0
+
+int main(int argc, char *argv[]) {
+        log_parse_environment();
+        log_open();
+
+        test_extract_first_word();
+#if 0 /// UNNEEDED by elogind
+        test_extract_first_word_and_warn();
+        test_extract_many_words();
+#endif // 0
+
+        return 0;
+}
diff --git a/src/test/test-fd-util.c b/src/test/test-fd-util.c
new file mode 100644 (file)
index 0000000..d1d9e1d
--- /dev/null
@@ -0,0 +1,117 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "macro.h"
+
+static void test_close_many(void) {
+        int fds[3];
+        char name0[] = "/tmp/test-close-many.XXXXXX";
+        char name1[] = "/tmp/test-close-many.XXXXXX";
+        char name2[] = "/tmp/test-close-many.XXXXXX";
+
+        fds[0] = mkostemp_safe(name0);
+        fds[1] = mkostemp_safe(name1);
+        fds[2] = mkostemp_safe(name2);
+
+        close_many(fds, 2);
+
+        assert_se(fcntl(fds[0], F_GETFD) == -1);
+        assert_se(fcntl(fds[1], F_GETFD) == -1);
+        assert_se(fcntl(fds[2], F_GETFD) >= 0);
+
+        safe_close(fds[2]);
+
+        unlink(name0);
+        unlink(name1);
+        unlink(name2);
+}
+
+static void test_close_nointr(void) {
+        char name[] = "/tmp/test-test-close_nointr.XXXXXX";
+        int fd;
+
+        fd = mkostemp_safe(name);
+        assert_se(fd >= 0);
+        assert_se(close_nointr(fd) >= 0);
+        assert_se(close_nointr(fd) < 0);
+
+        unlink(name);
+}
+
+#if 0 /// UNNEEDED by elogind
+static void test_same_fd(void) {
+        _cleanup_close_pair_ int p[2] = { -1, -1 };
+        _cleanup_close_ int a = -1, b = -1, c = -1;
+
+        assert_se(pipe2(p, O_CLOEXEC) >= 0);
+        assert_se((a = dup(p[0])) >= 0);
+        assert_se((b = open("/dev/null", O_RDONLY|O_CLOEXEC)) >= 0);
+        assert_se((c = dup(a)) >= 0);
+
+        assert_se(same_fd(p[0], p[0]) > 0);
+        assert_se(same_fd(p[1], p[1]) > 0);
+        assert_se(same_fd(a, a) > 0);
+        assert_se(same_fd(b, b) > 0);
+
+        assert_se(same_fd(a, p[0]) > 0);
+        assert_se(same_fd(p[0], a) > 0);
+        assert_se(same_fd(c, p[0]) > 0);
+        assert_se(same_fd(p[0], c) > 0);
+        assert_se(same_fd(a, c) > 0);
+        assert_se(same_fd(c, a) > 0);
+
+        assert_se(same_fd(p[0], p[1]) == 0);
+        assert_se(same_fd(p[1], p[0]) == 0);
+        assert_se(same_fd(p[0], b) == 0);
+        assert_se(same_fd(b, p[0]) == 0);
+        assert_se(same_fd(p[1], a) == 0);
+        assert_se(same_fd(a, p[1]) == 0);
+        assert_se(same_fd(p[1], b) == 0);
+        assert_se(same_fd(b, p[1]) == 0);
+
+        assert_se(same_fd(a, b) == 0);
+        assert_se(same_fd(b, a) == 0);
+}
+#endif // 0
+
+static void test_open_serialization_fd(void) {
+        _cleanup_close_ int fd = -1;
+
+        fd = open_serialization_fd("test");
+        assert_se(fd >= 0);
+
+        write(fd, "test\n", 5);
+}
+
+int main(int argc, char *argv[]) {
+        test_close_many();
+        test_close_nointr();
+#if 0 /// UNNEEDED by elogind
+        test_same_fd();
+#endif // 0
+        test_open_serialization_fd();
+
+        return 0;
+}
diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c
new file mode 100644 (file)
index 0000000..0125b38
--- /dev/null
@@ -0,0 +1,331 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <unistd.h>
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "macro.h"
+#include "mkdir.h"
+#include "path-util.h"
+#include "rm-rf.h"
+#include "string-util.h"
+#include "strv.h"
+#include "util.h"
+
+static void test_chase_symlinks(void) {
+        _cleanup_free_ char *result = NULL;
+        char temp[] = "/tmp/test-chase.XXXXXX";
+        const char *top, *p, *q;
+        int r;
+
+        assert_se(mkdtemp(temp));
+
+        top = strjoina(temp, "/top");
+        assert_se(mkdir(top, 0700) >= 0);
+
+        p = strjoina(top, "/dot");
+        assert_se(symlink(".", p) >= 0);
+
+        p = strjoina(top, "/dotdot");
+        assert_se(symlink("..", p) >= 0);
+
+        p = strjoina(top, "/dotdota");
+        assert_se(symlink("../a", p) >= 0);
+
+        p = strjoina(temp, "/a");
+        assert_se(symlink("b", p) >= 0);
+
+        p = strjoina(temp, "/b");
+        assert_se(symlink("/usr", p) >= 0);
+
+        p = strjoina(temp, "/start");
+        assert_se(symlink("top/dot/dotdota", p) >= 0);
+
+        /* Paths that use symlinks underneath the "root" */
+
+        r = chase_symlinks(p, NULL, 0, &result);
+        assert_se(r > 0);
+        assert_se(path_equal(result, "/usr"));
+
+        result = mfree(result);
+        r = chase_symlinks(p, temp, 0, &result);
+        assert_se(r == -ENOENT);
+
+        q = strjoina(temp, "/usr");
+
+        r = chase_symlinks(p, temp, CHASE_NONEXISTENT, &result);
+        assert_se(r == 0);
+        assert_se(path_equal(result, q));
+
+        assert_se(mkdir(q, 0700) >= 0);
+
+        result = mfree(result);
+        r = chase_symlinks(p, temp, 0, &result);
+        assert_se(r > 0);
+        assert_se(path_equal(result, q));
+
+        p = strjoina(temp, "/slash");
+        assert_se(symlink("/", p) >= 0);
+
+        result = mfree(result);
+        r = chase_symlinks(p, NULL, 0, &result);
+        assert_se(r > 0);
+        assert_se(path_equal(result, "/"));
+
+        result = mfree(result);
+        r = chase_symlinks(p, temp, 0, &result);
+        assert_se(r > 0);
+        assert_se(path_equal(result, temp));
+
+        /* Paths that would "escape" outside of the "root" */
+
+        p = strjoina(temp, "/6dots");
+        assert_se(symlink("../../..", p) >= 0);
+
+        result = mfree(result);
+        r = chase_symlinks(p, temp, 0, &result);
+        assert_se(r > 0 && path_equal(result, temp));
+
+        p = strjoina(temp, "/6dotsusr");
+        assert_se(symlink("../../../usr", p) >= 0);
+
+        result = mfree(result);
+        r = chase_symlinks(p, temp, 0, &result);
+        assert_se(r > 0 && path_equal(result, q));
+
+        p = strjoina(temp, "/top/8dotsusr");
+        assert_se(symlink("../../../../usr", p) >= 0);
+
+        result = mfree(result);
+        r = chase_symlinks(p, temp, 0, &result);
+        assert_se(r > 0 && path_equal(result, q));
+
+        /* Paths that contain repeated slashes */
+
+        p = strjoina(temp, "/slashslash");
+        assert_se(symlink("///usr///", p) >= 0);
+
+        result = mfree(result);
+        r = chase_symlinks(p, NULL, 0, &result);
+        assert_se(r > 0);
+        assert_se(path_equal(result, "/usr"));
+
+        result = mfree(result);
+        r = chase_symlinks(p, temp, 0, &result);
+        assert_se(r > 0);
+        assert_se(path_equal(result, q));
+
+        /* Paths using . */
+
+        result = mfree(result);
+        r = chase_symlinks("/etc/./.././", NULL, 0, &result);
+        assert_se(r > 0);
+        assert_se(path_equal(result, "/"));
+
+        result = mfree(result);
+        r = chase_symlinks("/etc/./.././", "/etc", 0, &result);
+        assert_se(r > 0 && path_equal(result, "/etc"));
+
+        result = mfree(result);
+        r = chase_symlinks("/etc/machine-id/foo", NULL, 0, &result);
+        assert_se(r == -ENOTDIR);
+
+        /* Path that loops back to self */
+
+        result = mfree(result);
+        p = strjoina(temp, "/recursive-symlink");
+        assert_se(symlink("recursive-symlink", p) >= 0);
+        r = chase_symlinks(p, NULL, 0, &result);
+        assert_se(r == -ELOOP);
+
+        /* Path which doesn't exist */
+
+        p = strjoina(temp, "/idontexist");
+        r = chase_symlinks(p, NULL, 0, &result);
+        assert_se(r == -ENOENT);
+
+        r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result);
+        assert_se(r == 0);
+        assert_se(path_equal(result, p));
+        result = mfree(result);
+
+        p = strjoina(temp, "/idontexist/meneither");
+        r = chase_symlinks(p, NULL, 0, &result);
+        assert_se(r == -ENOENT);
+
+        r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result);
+        assert_se(r == 0);
+        assert_se(path_equal(result, p));
+        result = mfree(result);
+
+        /* Path which doesn't exist, but contains weird stuff */
+
+        p = strjoina(temp, "/idontexist/..");
+        r = chase_symlinks(p, NULL, 0, &result);
+        assert_se(r == -ENOENT);
+
+        r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result);
+        assert_se(r == -ENOENT);
+
+        p = strjoina(temp, "/target");
+        q = strjoina(temp, "/top");
+        assert_se(symlink(q, p) >= 0);
+        p = strjoina(temp, "/target/idontexist");
+        r = chase_symlinks(p, NULL, 0, &result);
+        assert_se(r == -ENOENT);
+
+        assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
+}
+
+static void test_unlink_noerrno(void) {
+        char name[] = "/tmp/test-close_nointr.XXXXXX";
+        int fd;
+
+        fd = mkostemp_safe(name);
+        assert_se(fd >= 0);
+        assert_se(close_nointr(fd) >= 0);
+
+        {
+                PROTECT_ERRNO;
+                errno = -42;
+                assert_se(unlink_noerrno(name) >= 0);
+                assert_se(errno == -42);
+                assert_se(unlink_noerrno(name) < 0);
+                assert_se(errno == -42);
+        }
+}
+
+static void test_readlink_and_make_absolute(void) {
+        char tempdir[] = "/tmp/test-readlink_and_make_absolute";
+        char name[] = "/tmp/test-readlink_and_make_absolute/original";
+        char name2[] = "test-readlink_and_make_absolute/original";
+        char name_alias[] = "/tmp/test-readlink_and_make_absolute-alias";
+        char *r = NULL;
+
+        assert_se(mkdir_safe(tempdir, 0755, getuid(), getgid()) >= 0);
+        assert_se(touch(name) >= 0);
+
+        assert_se(symlink(name, name_alias) >= 0);
+        assert_se(readlink_and_make_absolute(name_alias, &r) >= 0);
+        assert_se(streq(r, name));
+        free(r);
+        assert_se(unlink(name_alias) >= 0);
+
+        assert_se(chdir(tempdir) >= 0);
+        assert_se(symlink(name2, name_alias) >= 0);
+        assert_se(readlink_and_make_absolute(name_alias, &r) >= 0);
+        assert_se(streq(r, name));
+        free(r);
+        assert_se(unlink(name_alias) >= 0);
+
+        assert_se(rm_rf(tempdir, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
+}
+
+static void test_get_files_in_directory(void) {
+        _cleanup_strv_free_ char **l = NULL, **t = NULL;
+
+        assert_se(get_files_in_directory("/tmp", &l) >= 0);
+        assert_se(get_files_in_directory(".", &t) >= 0);
+        assert_se(get_files_in_directory(".", NULL) >= 0);
+}
+
+#if 0 /// UNNEEDED by elogind
+static void test_var_tmp(void) {
+        _cleanup_free_ char *tmpdir_backup = NULL, *temp_backup = NULL, *tmp_backup = NULL;
+        const char *tmp_dir = NULL, *t;
+
+        t = getenv("TMPDIR");
+        if (t) {
+                tmpdir_backup = strdup(t);
+                assert_se(tmpdir_backup);
+        }
+
+        t = getenv("TEMP");
+        if (t) {
+                temp_backup = strdup(t);
+                assert_se(temp_backup);
+        }
+
+        t = getenv("TMP");
+        if (t) {
+                tmp_backup = strdup(t);
+                assert_se(tmp_backup);
+        }
+
+        assert(unsetenv("TMPDIR") >= 0);
+        assert(unsetenv("TEMP") >= 0);
+        assert(unsetenv("TMP") >= 0);
+
+        assert_se(var_tmp_dir(&tmp_dir) >= 0);
+        assert_se(streq(tmp_dir, "/var/tmp"));
+
+        assert_se(setenv("TMPDIR", "/tmp", true) >= 0);
+        assert_se(streq(getenv("TMPDIR"), "/tmp"));
+
+        assert_se(var_tmp_dir(&tmp_dir) >= 0);
+        assert_se(streq(tmp_dir, "/tmp"));
+
+        assert_se(setenv("TMPDIR", "/88_does_not_exist_88", true) >= 0);
+        assert_se(streq(getenv("TMPDIR"), "/88_does_not_exist_88"));
+
+        assert_se(var_tmp_dir(&tmp_dir) >= 0);
+        assert_se(streq(tmp_dir, "/var/tmp"));
+
+        if (tmpdir_backup)  {
+                assert_se(setenv("TMPDIR", tmpdir_backup, true) >= 0);
+                assert_se(streq(getenv("TMPDIR"), tmpdir_backup));
+        }
+
+        if (temp_backup)  {
+                assert_se(setenv("TEMP", temp_backup, true) >= 0);
+                assert_se(streq(getenv("TEMP"), temp_backup));
+        }
+
+        if (tmp_backup)  {
+                assert_se(setenv("TMP", tmp_backup, true) >= 0);
+                assert_se(streq(getenv("TMP"), tmp_backup));
+        }
+}
+#endif // 0
+
+static void test_dot_or_dot_dot(void) {
+        assert_se(!dot_or_dot_dot(NULL));
+        assert_se(!dot_or_dot_dot(""));
+        assert_se(!dot_or_dot_dot("xxx"));
+        assert_se(dot_or_dot_dot("."));
+        assert_se(dot_or_dot_dot(".."));
+        assert_se(!dot_or_dot_dot(".foo"));
+        assert_se(!dot_or_dot_dot("..foo"));
+}
+
+int main(int argc, char *argv[]) {
+        test_unlink_noerrno();
+        test_readlink_and_make_absolute();
+        test_get_files_in_directory();
+#if 0 /// UNNEEDED by elogind
+        test_var_tmp();
+#endif // 0
+        test_chase_symlinks();
+        test_dot_or_dot_dot();
+
+        return 0;
+}
diff --git a/src/test/test-hash.c b/src/test/test-hash.c
new file mode 100644 (file)
index 0000000..1972b94
--- /dev/null
@@ -0,0 +1,82 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2016 Lennart Poettering
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "alloc-util.h"
+#include "log.h"
+#include "string-util.h"
+#include "khash.h"
+
+int main(int argc, char *argv[]) {
+        _cleanup_(khash_unrefp) khash *h = NULL, *copy = NULL;
+        _cleanup_free_ char *s = NULL;
+
+        log_set_max_level(LOG_DEBUG);
+
+        assert_se(khash_new(&h, NULL) == -EINVAL);
+        assert_se(khash_new(&h, "") == -EINVAL);
+        assert_se(khash_new(&h, "foobar") == -EOPNOTSUPP);
+
+        assert_se(khash_new(&h, "sha256") >= 0);
+        assert_se(khash_get_size(h) == 32);
+        assert_se(streq(khash_get_algorithm(h), "sha256"));
+
+        assert_se(khash_digest_string(h, &s) >= 0);
+        assert_se(streq(s, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"));
+        s = mfree(s);
+
+        assert_se(khash_put(h, "foobar", 6) >= 0);
+        assert_se(khash_digest_string(h, &s) >= 0);
+        assert_se(streq(s, "c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2"));
+        s = mfree(s);
+
+        assert_se(khash_put(h, "piep", 4) >= 0);
+        assert_se(khash_digest_string(h, &s) >= 0);
+        assert_se(streq(s, "f114d872b5ea075d3be9040d0b7a429514b3f9324a8e8e3dc3fb24c34ee56bea"));
+        s = mfree(s);
+
+        assert_se(khash_put(h, "foo", 3) >= 0);
+        assert_se(khash_dup(h, &copy) >= 0);
+
+        assert_se(khash_put(h, "bar", 3) >= 0);
+        assert_se(khash_put(copy, "bar", 3) >= 0);
+
+        assert_se(khash_digest_string(h, &s) >= 0);
+        assert_se(streq(s, "c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2"));
+        s = mfree(s);
+
+        assert_se(khash_digest_string(copy, &s) >= 0);
+        assert_se(streq(s, "c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2"));
+        s = mfree(s);
+
+        h = khash_unref(h);
+
+        assert_se(khash_new_with_key(&h, "hmac(sha256)", "quux", 4) >= 0);
+        assert_se(khash_get_size(h) == 32);
+        assert_se(streq(khash_get_algorithm(h), "hmac(sha256)"));
+
+        assert_se(khash_digest_string(h, &s) >= 0);
+        assert_se(streq(s, "abed9f8218ab473f77218a6a7d39abf1d21fa46d0700c4898e330ba88309d5ae"));
+        s = mfree(s);
+
+        assert_se(khash_put(h, "foobar", 6) >= 0);
+        assert_se(khash_digest_string(h, &s) >= 0);
+        assert_se(streq(s, "33f6c70a60db66007d5325d5d1dea37c371354e5b83347a59ad339ce9f4ba3dc"));
+
+        return 0;
+}
diff --git a/src/test/test-hashmap-plain.c b/src/test/test-hashmap-plain.c
new file mode 100644 (file)
index 0000000..1bd5c02
--- /dev/null
@@ -0,0 +1,875 @@
+/***
+  This file is part of systemd
+
+  Copyright 2013 Daniel Buch
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "alloc-util.h"
+#include "hashmap.h"
+#include "string-util.h"
+#include "strv.h"
+#include "util.h"
+
+void test_hashmap_funcs(void);
+
+static void test_hashmap_replace(void) {
+        Hashmap *m;
+        char *val1, *val2, *val3, *val4, *val5, *r;
+
+        m = hashmap_new(&string_hash_ops);
+
+        val1 = strdup("val1");
+        assert_se(val1);
+        val2 = strdup("val2");
+        assert_se(val2);
+        val3 = strdup("val3");
+        assert_se(val3);
+        val4 = strdup("val4");
+        assert_se(val4);
+        val5 = strdup("val5");
+        assert_se(val5);
+
+        hashmap_put(m, "key 1", val1);
+        hashmap_put(m, "key 2", val2);
+        hashmap_put(m, "key 3", val3);
+        hashmap_put(m, "key 4", val4);
+
+        hashmap_replace(m, "key 3", val1);
+        r = hashmap_get(m, "key 3");
+        assert_se(streq(r, "val1"));
+
+        hashmap_replace(m, "key 5", val5);
+        r = hashmap_get(m, "key 5");
+        assert_se(streq(r, "val5"));
+
+        free(val1);
+        free(val2);
+        free(val3);
+        free(val4);
+        free(val5);
+        hashmap_free(m);
+}
+
+static void test_hashmap_copy(void) {
+        Hashmap *m, *copy;
+        char *val1, *val2, *val3, *val4, *r;
+
+        val1 = strdup("val1");
+        assert_se(val1);
+        val2 = strdup("val2");
+        assert_se(val2);
+        val3 = strdup("val3");
+        assert_se(val3);
+        val4 = strdup("val4");
+        assert_se(val4);
+
+        m = hashmap_new(&string_hash_ops);
+
+        hashmap_put(m, "key 1", val1);
+        hashmap_put(m, "key 2", val2);
+        hashmap_put(m, "key 3", val3);
+        hashmap_put(m, "key 4", val4);
+
+        copy = hashmap_copy(m);
+
+        r = hashmap_get(copy, "key 1");
+        assert_se(streq(r, "val1"));
+        r = hashmap_get(copy, "key 2");
+        assert_se(streq(r, "val2"));
+        r = hashmap_get(copy, "key 3");
+        assert_se(streq(r, "val3"));
+        r = hashmap_get(copy, "key 4");
+        assert_se(streq(r, "val4"));
+
+        hashmap_free_free(copy);
+        hashmap_free(m);
+}
+
+static void test_hashmap_get_strv(void) {
+        Hashmap *m;
+        char **strv;
+        char *val1, *val2, *val3, *val4;
+
+        val1 = strdup("val1");
+        assert_se(val1);
+        val2 = strdup("val2");
+        assert_se(val2);
+        val3 = strdup("val3");
+        assert_se(val3);
+        val4 = strdup("val4");
+        assert_se(val4);
+
+        m = hashmap_new(&string_hash_ops);
+
+        hashmap_put(m, "key 1", val1);
+        hashmap_put(m, "key 2", val2);
+        hashmap_put(m, "key 3", val3);
+        hashmap_put(m, "key 4", val4);
+
+        strv = hashmap_get_strv(m);
+
+#ifndef ORDERED
+        strv = strv_sort(strv);
+#endif
+
+        assert_se(streq(strv[0], "val1"));
+        assert_se(streq(strv[1], "val2"));
+        assert_se(streq(strv[2], "val3"));
+        assert_se(streq(strv[3], "val4"));
+
+        strv_free(strv);
+
+        hashmap_free(m);
+}
+
+static void test_hashmap_move_one(void) {
+        Hashmap *m, *n;
+        char *val1, *val2, *val3, *val4, *r;
+
+        val1 = strdup("val1");
+        assert_se(val1);
+        val2 = strdup("val2");
+        assert_se(val2);
+        val3 = strdup("val3");
+        assert_se(val3);
+        val4 = strdup("val4");
+        assert_se(val4);
+
+        m = hashmap_new(&string_hash_ops);
+        n = hashmap_new(&string_hash_ops);
+
+        hashmap_put(m, "key 1", val1);
+        hashmap_put(m, "key 2", val2);
+        hashmap_put(m, "key 3", val3);
+        hashmap_put(m, "key 4", val4);
+
+        assert_se(hashmap_move_one(n, NULL, "key 3") == -ENOENT);
+        assert_se(hashmap_move_one(n, m, "key 5") == -ENOENT);
+        assert_se(hashmap_move_one(n, m, "key 3") == 0);
+        assert_se(hashmap_move_one(n, m, "key 4") == 0);
+
+        r = hashmap_get(n, "key 3");
+        assert_se(r && streq(r, "val3"));
+        r = hashmap_get(n, "key 4");
+        assert_se(r && streq(r, "val4"));
+        r = hashmap_get(m, "key 3");
+        assert_se(!r);
+
+        assert_se(hashmap_move_one(n, m, "key 3") == -EEXIST);
+
+        hashmap_free_free(m);
+        hashmap_free_free(n);
+}
+
+static void test_hashmap_move(void) {
+        Hashmap *m, *n;
+        char *val1, *val2, *val3, *val4, *r;
+
+        val1 = strdup("val1");
+        assert_se(val1);
+        val2 = strdup("val2");
+        assert_se(val2);
+        val3 = strdup("val3");
+        assert_se(val3);
+        val4 = strdup("val4");
+        assert_se(val4);
+
+        m = hashmap_new(&string_hash_ops);
+        n = hashmap_new(&string_hash_ops);
+
+        hashmap_put(n, "key 1", strdup(val1));
+        hashmap_put(m, "key 1", val1);
+        hashmap_put(m, "key 2", val2);
+        hashmap_put(m, "key 3", val3);
+        hashmap_put(m, "key 4", val4);
+
+        assert_se(hashmap_move(n, NULL) == 0);
+        assert_se(hashmap_move(n, m) == 0);
+
+        assert_se(hashmap_size(m) == 1);
+        r = hashmap_get(m, "key 1");
+        assert_se(r && streq(r, "val1"));
+
+        r = hashmap_get(n, "key 1");
+        assert_se(r && streq(r, "val1"));
+        r = hashmap_get(n, "key 2");
+        assert_se(r && streq(r, "val2"));
+        r = hashmap_get(n, "key 3");
+        assert_se(r && streq(r, "val3"));
+        r = hashmap_get(n, "key 4");
+        assert_se(r && streq(r, "val4"));
+
+        hashmap_free_free(m);
+        hashmap_free_free(n);
+}
+
+static void test_hashmap_update(void) {
+        Hashmap *m;
+        char *val1, *val2, *r;
+
+        m = hashmap_new(&string_hash_ops);
+        val1 = strdup("old_value");
+        assert_se(val1);
+        val2 = strdup("new_value");
+        assert_se(val2);
+
+        hashmap_put(m, "key 1", val1);
+        r = hashmap_get(m, "key 1");
+        assert_se(streq(r, "old_value"));
+
+        assert_se(hashmap_update(m, "key 2", val2) == -ENOENT);
+        r = hashmap_get(m, "key 1");
+        assert_se(streq(r, "old_value"));
+
+        assert_se(hashmap_update(m, "key 1", val2) == 0);
+        r = hashmap_get(m, "key 1");
+        assert_se(streq(r, "new_value"));
+
+        free(val1);
+        free(val2);
+        hashmap_free(m);
+}
+
+static void test_hashmap_put(void) {
+        Hashmap *m = NULL;
+        int valid_hashmap_put;
+        void *val1 = (void*) "val 1";
+        void *val2 = (void*) "val 2";
+        _cleanup_free_ char* key1 = NULL;
+
+        assert_se(hashmap_ensure_allocated(&m, &string_hash_ops) >= 0);
+        assert_se(m);
+
+        valid_hashmap_put = hashmap_put(m, "key 1", val1);
+        assert_se(valid_hashmap_put == 1);
+        assert_se(hashmap_put(m, "key 1", val1) == 0);
+        assert_se(hashmap_put(m, "key 1", val2) == -EEXIST);
+        key1 = strdup("key 1");
+        assert_se(hashmap_put(m, key1, val1) == 0);
+        assert_se(hashmap_put(m, key1, val2) == -EEXIST);
+
+        hashmap_free(m);
+}
+
+static void test_hashmap_remove(void) {
+        _cleanup_hashmap_free_ Hashmap *m = NULL;
+        char *r;
+
+        r = hashmap_remove(NULL, "key 1");
+        assert_se(r == NULL);
+
+        m = hashmap_new(&string_hash_ops);
+        assert_se(m);
+
+        r = hashmap_remove(m, "no such key");
+        assert_se(r == NULL);
+
+        hashmap_put(m, "key 1", (void*) "val 1");
+        hashmap_put(m, "key 2", (void*) "val 2");
+
+        r = hashmap_remove(m, "key 1");
+        assert_se(streq(r, "val 1"));
+
+        r = hashmap_get(m, "key 2");
+        assert_se(streq(r, "val 2"));
+        assert_se(!hashmap_get(m, "key 1"));
+}
+
+static void test_hashmap_remove2(void) {
+        _cleanup_hashmap_free_free_free_ Hashmap *m = NULL;
+        char key1[] = "key 1";
+        char key2[] = "key 2";
+        char val1[] = "val 1";
+        char val2[] = "val 2";
+        void *r, *r2;
+
+        r = hashmap_remove2(NULL, "key 1", &r2);
+        assert_se(r == NULL);
+
+        m = hashmap_new(&string_hash_ops);
+        assert_se(m);
+
+        r = hashmap_remove2(m, "no such key", &r2);
+        assert_se(r == NULL);
+
+        hashmap_put(m, strdup(key1), strdup(val1));
+        hashmap_put(m, strdup(key2), strdup(val2));
+
+        r = hashmap_remove2(m, key1, &r2);
+        assert_se(streq(r, val1));
+        assert_se(streq(r2, key1));
+        free(r);
+        free(r2);
+
+        r = hashmap_get(m, key2);
+        assert_se(streq(r, val2));
+        assert_se(!hashmap_get(m, key1));
+}
+
+static void test_hashmap_remove_value(void) {
+        _cleanup_hashmap_free_ Hashmap *m = NULL;
+        char *r;
+
+        char val1[] = "val 1";
+        char val2[] = "val 2";
+
+        r = hashmap_remove_value(NULL, "key 1", val1);
+        assert_se(r == NULL);
+
+        m = hashmap_new(&string_hash_ops);
+        assert_se(m);
+
+        r = hashmap_remove_value(m, "key 1", val1);
+        assert_se(r == NULL);
+
+        hashmap_put(m, "key 1", val1);
+        hashmap_put(m, "key 2", val2);
+
+        r = hashmap_remove_value(m, "key 1", val1);
+        assert_se(streq(r, "val 1"));
+
+        r = hashmap_get(m, "key 2");
+        assert_se(streq(r, "val 2"));
+        assert_se(!hashmap_get(m, "key 1"));
+
+        r = hashmap_remove_value(m, "key 2", val1);
+        assert_se(r == NULL);
+
+        r = hashmap_get(m, "key 2");
+        assert_se(streq(r, "val 2"));
+        assert_se(!hashmap_get(m, "key 1"));
+}
+
+static void test_hashmap_remove_and_put(void) {
+        _cleanup_hashmap_free_ Hashmap *m = NULL;
+        int valid;
+        char *r;
+
+        m = hashmap_new(&string_hash_ops);
+        assert_se(m);
+
+        valid = hashmap_remove_and_put(m, "invalid key", "new key", NULL);
+        assert_se(valid == -ENOENT);
+
+        valid = hashmap_put(m, "key 1", (void*) (const char *) "val 1");
+        assert_se(valid == 1);
+
+        valid = hashmap_remove_and_put(NULL, "key 1", "key 2", (void*) (const char *) "val 2");
+        assert_se(valid == -ENOENT);
+
+        valid = hashmap_remove_and_put(m, "key 1", "key 2", (void*) (const char *) "val 2");
+        assert_se(valid == 0);
+
+        r = hashmap_get(m, "key 2");
+        assert_se(streq(r, "val 2"));
+        assert_se(!hashmap_get(m, "key 1"));
+
+        valid = hashmap_put(m, "key 3", (void*) (const char *) "val 3");
+        assert_se(valid == 1);
+        valid = hashmap_remove_and_put(m, "key 3", "key 2", (void*) (const char *) "val 2");
+        assert_se(valid == -EEXIST);
+}
+
+static void test_hashmap_remove_and_replace(void) {
+        _cleanup_hashmap_free_ Hashmap *m = NULL;
+        int valid;
+        void *key1 = UINT_TO_PTR(1);
+        void *key2 = UINT_TO_PTR(2);
+        void *key3 = UINT_TO_PTR(3);
+        void *r;
+        int i, j;
+
+        m = hashmap_new(&trivial_hash_ops);
+        assert_se(m);
+
+        valid = hashmap_remove_and_replace(m, key1, key2, NULL);
+        assert_se(valid == -ENOENT);
+
+        valid = hashmap_put(m, key1, key1);
+        assert_se(valid == 1);
+
+        valid = hashmap_remove_and_replace(NULL, key1, key2, key2);
+        assert_se(valid == -ENOENT);
+
+        valid = hashmap_remove_and_replace(m, key1, key2, key2);
+        assert_se(valid == 0);
+
+        r = hashmap_get(m, key2);
+        assert_se(r == key2);
+        assert_se(!hashmap_get(m, key1));
+
+        valid = hashmap_put(m, key3, key3);
+        assert_se(valid == 1);
+        valid = hashmap_remove_and_replace(m, key3, key2, key2);
+        assert_se(valid == 0);
+        r = hashmap_get(m, key2);
+        assert_se(r == key2);
+        assert_se(!hashmap_get(m, key3));
+
+        /* Repeat this test several times to increase the chance of hitting
+         * the less likely case in hashmap_remove_and_replace where it
+         * compensates for the backward shift. */
+        for (i = 0; i < 20; i++) {
+                hashmap_clear(m);
+
+                for (j = 1; j < 7; j++)
+                        hashmap_put(m, UINT_TO_PTR(10*i + j), UINT_TO_PTR(10*i + j));
+                valid = hashmap_remove_and_replace(m, UINT_TO_PTR(10*i + 1),
+                                                   UINT_TO_PTR(10*i + 2),
+                                                   UINT_TO_PTR(10*i + 2));
+                assert_se(valid == 0);
+                assert_se(!hashmap_get(m, UINT_TO_PTR(10*i + 1)));
+                for (j = 2; j < 7; j++) {
+                        r = hashmap_get(m, UINT_TO_PTR(10*i + j));
+                        assert_se(r == UINT_TO_PTR(10*i + j));
+                }
+        }
+}
+
+static void test_hashmap_ensure_allocated(void) {
+        Hashmap *m;
+        int valid_hashmap;
+
+        m = hashmap_new(&string_hash_ops);
+
+        valid_hashmap = hashmap_ensure_allocated(&m, &string_hash_ops);
+        assert_se(valid_hashmap == 0);
+
+        assert_se(m);
+        hashmap_free(m);
+}
+
+static void test_hashmap_foreach_key(void) {
+        Hashmap *m;
+        Iterator i;
+        bool key_found[] = { false, false, false, false };
+        const char *s;
+        const char *key;
+        static const char key_table[] =
+                "key 1\0"
+                "key 2\0"
+                "key 3\0"
+                "key 4\0";
+
+        m = hashmap_new(&string_hash_ops);
+
+        NULSTR_FOREACH(key, key_table)
+                hashmap_put(m, key, (void*) (const char*) "my dummy val");
+
+        HASHMAP_FOREACH_KEY(s, key, m, i) {
+                assert(s);
+                if (!key_found[0] && streq(key, "key 1"))
+                        key_found[0] = true;
+                else if (!key_found[1] && streq(key, "key 2"))
+                        key_found[1] = true;
+                else if (!key_found[2] && streq(key, "key 3"))
+                        key_found[2] = true;
+                else if (!key_found[3] && streq(key, "fail"))
+                        key_found[3] = true;
+        }
+
+        assert_se(m);
+        assert_se(key_found[0] && key_found[1] && key_found[2] && !key_found[3]);
+
+        hashmap_free(m);
+}
+
+static void test_hashmap_foreach(void) {
+        Hashmap *m;
+        Iterator i;
+        bool value_found[] = { false, false, false, false };
+        char *val1, *val2, *val3, *val4, *s;
+        unsigned count;
+
+        val1 = strdup("my val1");
+        assert_se(val1);
+        val2 = strdup("my val2");
+        assert_se(val2);
+        val3 = strdup("my val3");
+        assert_se(val3);
+        val4 = strdup("my val4");
+        assert_se(val4);
+
+        m = NULL;
+
+        count = 0;
+        HASHMAP_FOREACH(s, m, i)
+                count++;
+        assert_se(count == 0);
+
+        m = hashmap_new(&string_hash_ops);
+
+        count = 0;
+        HASHMAP_FOREACH(s, m, i)
+                count++;
+        assert_se(count == 0);
+
+        hashmap_put(m, "Key 1", val1);
+        hashmap_put(m, "Key 2", val2);
+        hashmap_put(m, "Key 3", val3);
+        hashmap_put(m, "Key 4", val4);
+
+        HASHMAP_FOREACH(s, m, i) {
+                if (!value_found[0] && streq(s, val1))
+                        value_found[0] = true;
+                else if (!value_found[1] && streq(s, val2))
+                        value_found[1] = true;
+                else if (!value_found[2] && streq(s, val3))
+                        value_found[2] = true;
+                else if (!value_found[3] && streq(s, val4))
+                        value_found[3] = true;
+        }
+
+        assert_se(m);
+        assert_se(value_found[0] && value_found[1] && value_found[2] && value_found[3]);
+
+        hashmap_free_free(m);
+}
+
+static void test_hashmap_merge(void) {
+        Hashmap *m;
+        Hashmap *n;
+        char *val1, *val2, *val3, *val4, *r;
+
+        val1 = strdup("my val1");
+        assert_se(val1);
+        val2 = strdup("my val2");
+        assert_se(val2);
+        val3 = strdup("my val3");
+        assert_se(val3);
+        val4 = strdup("my val4");
+        assert_se(val4);
+
+        n = hashmap_new(&string_hash_ops);
+        m = hashmap_new(&string_hash_ops);
+
+        hashmap_put(m, "Key 1", val1);
+        hashmap_put(m, "Key 2", val2);
+        hashmap_put(n, "Key 3", val3);
+        hashmap_put(n, "Key 4", val4);
+
+        assert_se(hashmap_merge(m, n) == 0);
+        r = hashmap_get(m, "Key 3");
+        assert_se(r && streq(r, "my val3"));
+        r = hashmap_get(m, "Key 4");
+        assert_se(r && streq(r, "my val4"));
+
+        assert_se(n);
+        assert_se(m);
+        hashmap_free(n);
+        hashmap_free_free(m);
+}
+
+static void test_hashmap_contains(void) {
+        Hashmap *m;
+        char *val1;
+
+        val1 = strdup("my val");
+        assert_se(val1);
+
+        m = hashmap_new(&string_hash_ops);
+
+        assert_se(!hashmap_contains(m, "Key 1"));
+        hashmap_put(m, "Key 1", val1);
+        assert_se(hashmap_contains(m, "Key 1"));
+        assert_se(!hashmap_contains(m, "Key 2"));
+
+        assert_se(!hashmap_contains(NULL, "Key 1"));
+
+        assert_se(m);
+        hashmap_free_free(m);
+}
+
+static void test_hashmap_isempty(void) {
+        Hashmap *m;
+        char *val1;
+
+        val1 = strdup("my val");
+        assert_se(val1);
+
+        m = hashmap_new(&string_hash_ops);
+
+        assert_se(hashmap_isempty(m));
+        hashmap_put(m, "Key 1", val1);
+        assert_se(!hashmap_isempty(m));
+
+        assert_se(m);
+        hashmap_free_free(m);
+}
+
+static void test_hashmap_size(void) {
+        Hashmap *m;
+        char *val1, *val2, *val3, *val4;
+
+        val1 = strdup("my val");
+        assert_se(val1);
+        val2 = strdup("my val");
+        assert_se(val2);
+        val3 = strdup("my val");
+        assert_se(val3);
+        val4 = strdup("my val");
+        assert_se(val4);
+
+        assert_se(hashmap_size(NULL) == 0);
+        assert_se(hashmap_buckets(NULL) == 0);
+
+        m = hashmap_new(&string_hash_ops);
+
+        hashmap_put(m, "Key 1", val1);
+        hashmap_put(m, "Key 2", val2);
+        hashmap_put(m, "Key 3", val3);
+        hashmap_put(m, "Key 4", val4);
+
+        assert_se(m);
+        assert_se(hashmap_size(m) == 4);
+        assert_se(hashmap_buckets(m) >= 4);
+        hashmap_free_free(m);
+}
+
+static void test_hashmap_get(void) {
+        Hashmap *m;
+        char *r;
+        char *val;
+
+        val = strdup("my val");
+        assert_se(val);
+
+        r = hashmap_get(NULL, "Key 1");
+        assert_se(r == NULL);
+
+        m = hashmap_new(&string_hash_ops);
+
+        hashmap_put(m, "Key 1", val);
+
+        r = hashmap_get(m, "Key 1");
+        assert_se(streq(r, val));
+
+        r = hashmap_get(m, "no such key");
+        assert_se(r == NULL);
+
+        assert_se(m);
+        hashmap_free_free(m);
+}
+
+static void test_hashmap_get2(void) {
+        Hashmap *m;
+        char *r;
+        char *val;
+        char key_orig[] = "Key 1";
+        void *key_copy;
+
+        val = strdup("my val");
+        assert_se(val);
+
+        key_copy = strdup(key_orig);
+        assert_se(key_copy);
+
+        r = hashmap_get2(NULL, key_orig, &key_copy);
+        assert_se(r == NULL);
+
+        m = hashmap_new(&string_hash_ops);
+
+        hashmap_put(m, key_copy, val);
+        key_copy = NULL;
+
+        r = hashmap_get2(m, key_orig, &key_copy);
+        assert_se(streq(r, val));
+        assert_se(key_orig != key_copy);
+        assert_se(streq(key_orig, key_copy));
+
+        r = hashmap_get2(m, "no such key", NULL);
+        assert_se(r == NULL);
+
+        assert_se(m);
+        hashmap_free_free_free(m);
+}
+
+static void crippled_hashmap_func(const void *p, struct siphash *state) {
+        return trivial_hash_func(INT_TO_PTR(PTR_TO_INT(p) & 0xff), state);
+}
+
+static const struct hash_ops crippled_hashmap_ops = {
+        .hash = crippled_hashmap_func,
+        .compare = trivial_compare_func,
+};
+
+static void test_hashmap_many(void) {
+        Hashmap *h;
+        unsigned i, j;
+        void *v, *k;
+        static const struct {
+                const struct hash_ops *ops;
+                unsigned n_entries;
+        } tests[] = {
+                { .ops = NULL,                  .n_entries = 1 << 20 },
+                { .ops = &crippled_hashmap_ops, .n_entries = 1 << 14 },
+        };
+
+
+        for (j = 0; j < ELEMENTSOF(tests); j++) {
+                assert_se(h = hashmap_new(tests[j].ops));
+
+                for (i = 1; i < tests[j].n_entries*3; i+=3) {
+                        assert_se(hashmap_put(h, UINT_TO_PTR(i), UINT_TO_PTR(i)) >= 0);
+                        assert_se(PTR_TO_UINT(hashmap_get(h, UINT_TO_PTR(i))) == i);
+                }
+
+                for (i = 1; i < tests[j].n_entries*3; i++)
+                        assert_se(hashmap_contains(h, UINT_TO_PTR(i)) == (i % 3 == 1));
+
+                log_info("%u <= %u * 0.8 = %g", hashmap_size(h), hashmap_buckets(h), hashmap_buckets(h) * 0.8);
+
+                assert_se(hashmap_size(h) <= hashmap_buckets(h) * 0.8);
+                assert_se(hashmap_size(h) == tests[j].n_entries);
+
+                while (!hashmap_isempty(h)) {
+                        k = hashmap_first_key(h);
+                        v = hashmap_remove(h, k);
+                        assert_se(v == k);
+                }
+
+                hashmap_free(h);
+        }
+}
+
+static void test_hashmap_first(void) {
+        _cleanup_hashmap_free_ Hashmap *m = NULL;
+
+        m = hashmap_new(&string_hash_ops);
+        assert_se(m);
+
+        assert_se(!hashmap_first(m));
+        assert_se(hashmap_put(m, "key 1", (void*) "val 1") == 1);
+        assert_se(streq(hashmap_first(m), "val 1"));
+        assert_se(hashmap_put(m, "key 2", (void*) "val 2") == 1);
+#ifdef ORDERED
+        assert_se(streq(hashmap_first(m), "val 1"));
+        assert_se(hashmap_remove(m, "key 1"));
+        assert_se(streq(hashmap_first(m), "val 2"));
+#endif
+}
+
+static void test_hashmap_first_key(void) {
+        _cleanup_hashmap_free_ Hashmap *m = NULL;
+
+        m = hashmap_new(&string_hash_ops);
+        assert_se(m);
+
+        assert_se(!hashmap_first_key(m));
+        assert_se(hashmap_put(m, "key 1", NULL) == 1);
+        assert_se(streq(hashmap_first_key(m), "key 1"));
+        assert_se(hashmap_put(m, "key 2", NULL) == 1);
+#ifdef ORDERED
+        assert_se(streq(hashmap_first_key(m), "key 1"));
+        assert_se(hashmap_remove(m, "key 1") == NULL);
+        assert_se(streq(hashmap_first_key(m), "key 2"));
+#endif
+}
+
+static void test_hashmap_steal_first_key(void) {
+        _cleanup_hashmap_free_ Hashmap *m = NULL;
+
+        m = hashmap_new(&string_hash_ops);
+        assert_se(m);
+
+        assert_se(!hashmap_steal_first_key(m));
+        assert_se(hashmap_put(m, "key 1", NULL) == 1);
+        assert_se(streq(hashmap_steal_first_key(m), "key 1"));
+
+        assert_se(hashmap_isempty(m));
+}
+
+static void test_hashmap_steal_first(void) {
+        _cleanup_hashmap_free_ Hashmap *m = NULL;
+        int seen[3] = {};
+        char *val;
+
+        m = hashmap_new(&string_hash_ops);
+        assert_se(m);
+
+        assert_se(hashmap_put(m, "key 1", (void*) "1") == 1);
+        assert_se(hashmap_put(m, "key 2", (void*) "22") == 1);
+        assert_se(hashmap_put(m, "key 3", (void*) "333") == 1);
+
+        while ((val = hashmap_steal_first(m)))
+                seen[strlen(val) - 1]++;
+
+        assert_se(seen[0] == 1 && seen[1] == 1 && seen[2] == 1);
+
+        assert_se(hashmap_isempty(m));
+}
+
+static void test_hashmap_clear_free_free(void) {
+        _cleanup_hashmap_free_ Hashmap *m = NULL;
+
+        m = hashmap_new(&string_hash_ops);
+        assert_se(m);
+
+        assert_se(hashmap_put(m, strdup("key 1"), NULL) == 1);
+        assert_se(hashmap_put(m, strdup("key 2"), NULL) == 1);
+        assert_se(hashmap_put(m, strdup("key 3"), NULL) == 1);
+
+        hashmap_clear_free_free(m);
+        assert_se(hashmap_isempty(m));
+}
+
+static void test_hashmap_reserve(void) {
+        _cleanup_hashmap_free_ Hashmap *m = NULL;
+
+        m = hashmap_new(&string_hash_ops);
+
+        assert_se(hashmap_reserve(m, 1) == 0);
+        assert_se(hashmap_buckets(m) < 1000);
+        assert_se(hashmap_reserve(m, 1000) == 0);
+        assert_se(hashmap_buckets(m) >= 1000);
+        assert_se(hashmap_isempty(m));
+
+        assert_se(hashmap_put(m, "key 1", (void*) "val 1") == 1);
+
+        assert_se(hashmap_reserve(m, UINT_MAX) == -ENOMEM);
+        assert_se(hashmap_reserve(m, UINT_MAX - 1) == -ENOMEM);
+}
+
+void test_hashmap_funcs(void) {
+        test_hashmap_copy();
+        test_hashmap_get_strv();
+        test_hashmap_move_one();
+        test_hashmap_move();
+        test_hashmap_replace();
+        test_hashmap_update();
+        test_hashmap_put();
+        test_hashmap_remove();
+        test_hashmap_remove2();
+        test_hashmap_remove_value();
+        test_hashmap_remove_and_put();
+        test_hashmap_remove_and_replace();
+        test_hashmap_ensure_allocated();
+        test_hashmap_foreach();
+        test_hashmap_foreach_key();
+        test_hashmap_contains();
+        test_hashmap_merge();
+        test_hashmap_isempty();
+        test_hashmap_get();
+        test_hashmap_get2();
+        test_hashmap_size();
+        test_hashmap_many();
+        test_hashmap_first();
+        test_hashmap_first_key();
+        test_hashmap_steal_first_key();
+        test_hashmap_steal_first();
+        test_hashmap_clear_free_free();
+        test_hashmap_reserve();
+}
diff --git a/src/test/test-hashmap.c b/src/test/test-hashmap.c
new file mode 100644 (file)
index 0000000..83cea36
--- /dev/null
@@ -0,0 +1,67 @@
+/***
+  This file is part of systemd
+
+  Copyright 2013 Daniel Buch
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "hashmap.h"
+#include "util.h"
+
+void test_hashmap_funcs(void);
+void test_ordered_hashmap_funcs(void);
+
+static void test_ordered_hashmap_next(void) {
+        _cleanup_ordered_hashmap_free_ OrderedHashmap *m = NULL;
+        int i;
+
+        assert_se(m = ordered_hashmap_new(NULL));
+        for (i = -2; i <= 2; i++)
+                assert_se(ordered_hashmap_put(m, INT_TO_PTR(i), INT_TO_PTR(i+10)) == 1);
+        for (i = -2; i <= 1; i++)
+                assert_se(ordered_hashmap_next(m, INT_TO_PTR(i)) == INT_TO_PTR(i+11));
+        assert_se(!ordered_hashmap_next(m, INT_TO_PTR(2)));
+        assert_se(!ordered_hashmap_next(NULL, INT_TO_PTR(1)));
+        assert_se(!ordered_hashmap_next(m, INT_TO_PTR(3)));
+}
+
+static void test_uint64_compare_func(void) {
+        const uint64_t a = 0x100, b = 0x101;
+
+        assert_se(uint64_compare_func(&a, &a) == 0);
+        assert_se(uint64_compare_func(&a, &b) == -1);
+        assert_se(uint64_compare_func(&b, &a) == 1);
+}
+
+static void test_trivial_compare_func(void) {
+        assert_se(trivial_compare_func(INT_TO_PTR('a'), INT_TO_PTR('a')) == 0);
+        assert_se(trivial_compare_func(INT_TO_PTR('a'), INT_TO_PTR('b')) == -1);
+        assert_se(trivial_compare_func(INT_TO_PTR('b'), INT_TO_PTR('a')) == 1);
+}
+
+static void test_string_compare_func(void) {
+        assert_se(string_compare_func("fred", "wilma") != 0);
+        assert_se(string_compare_func("fred", "fred") == 0);
+}
+
+int main(int argc, const char *argv[]) {
+        test_hashmap_funcs();
+        test_ordered_hashmap_funcs();
+
+        test_ordered_hashmap_next();
+        test_uint64_compare_func();
+        test_trivial_compare_func();
+        test_string_compare_func();
+}
diff --git a/src/test/test-helper.h b/src/test/test-helper.h
new file mode 100644 (file)
index 0000000..ddb10f8
--- /dev/null
@@ -0,0 +1,41 @@
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2013 Holger Hans Peter Freyther
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "sd-daemon.h"
+
+#include "macro.h"
+
+#define TEST_REQ_RUNNING_SYSTEMD(x)                                 \
+        if (sd_booted() > 0) {                                      \
+                x;                                                  \
+        } else {                                                    \
+                printf("systemd not booted skipping '%s'\n", #x);   \
+        }
+
+#define MANAGER_SKIP_TEST(r)                                    \
+        IN_SET(r,                                               \
+               -EPERM,                                          \
+               -EACCES,                                         \
+               -EADDRINUSE,                                     \
+               -EHOSTDOWN,                                      \
+               -ENOENT,                                         \
+               -ENOMEDIUM /* cannot determine cgroup */         \
+               )
diff --git a/src/test/test-hexdecoct.c b/src/test/test-hexdecoct.c
new file mode 100644 (file)
index 0000000..fcae427
--- /dev/null
@@ -0,0 +1,379 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "alloc-util.h"
+#include "hexdecoct.h"
+#include "macro.h"
+#include "string-util.h"
+
+static void test_hexchar(void) {
+        assert_se(hexchar(0xa) == 'a');
+        assert_se(hexchar(0x0) == '0');
+}
+
+static void test_unhexchar(void) {
+        assert_se(unhexchar('a') == 0xA);
+        assert_se(unhexchar('A') == 0xA);
+        assert_se(unhexchar('0') == 0x0);
+}
+
+static void test_base32hexchar(void) {
+        assert_se(base32hexchar(0) == '0');
+        assert_se(base32hexchar(9) == '9');
+        assert_se(base32hexchar(10) == 'A');
+        assert_se(base32hexchar(31) == 'V');
+}
+
+static void test_unbase32hexchar(void) {
+        assert_se(unbase32hexchar('0') == 0);
+        assert_se(unbase32hexchar('9') == 9);
+        assert_se(unbase32hexchar('A') == 10);
+        assert_se(unbase32hexchar('V') == 31);
+        assert_se(unbase32hexchar('=') == -EINVAL);
+}
+
+static void test_base64char(void) {
+        assert_se(base64char(0) == 'A');
+        assert_se(base64char(26) == 'a');
+        assert_se(base64char(63) == '/');
+}
+
+static void test_unbase64char(void) {
+        assert_se(unbase64char('A') == 0);
+        assert_se(unbase64char('Z') == 25);
+        assert_se(unbase64char('a') == 26);
+        assert_se(unbase64char('z') == 51);
+        assert_se(unbase64char('0') == 52);
+        assert_se(unbase64char('9') == 61);
+        assert_se(unbase64char('+') == 62);
+        assert_se(unbase64char('/') == 63);
+        assert_se(unbase64char('=') == -EINVAL);
+}
+
+static void test_octchar(void) {
+        assert_se(octchar(00) == '0');
+        assert_se(octchar(07) == '7');
+}
+
+static void test_unoctchar(void) {
+        assert_se(unoctchar('0') == 00);
+        assert_se(unoctchar('7') == 07);
+}
+
+static void test_decchar(void) {
+        assert_se(decchar(0) == '0');
+        assert_se(decchar(9) == '9');
+}
+
+static void test_undecchar(void) {
+        assert_se(undecchar('0') == 0);
+        assert_se(undecchar('9') == 9);
+}
+
+static void test_unhexmem(void) {
+        const char *hex = "efa2149213";
+        const char *hex_invalid = "efa214921o";
+        _cleanup_free_ char *hex2 = NULL;
+        _cleanup_free_ void *mem = NULL;
+        size_t len;
+
+        assert_se(unhexmem(hex_invalid, strlen(hex_invalid), &mem, &len) == -EINVAL);
+        assert_se(unhexmem(hex, strlen(hex) + 1, &mem, &len) == -EINVAL);
+        assert_se(unhexmem(hex, strlen(hex) - 1, &mem, &len) == -EINVAL);
+        assert_se(unhexmem(hex, strlen(hex), &mem, &len) == 0);
+
+        assert_se((hex2 = hexmem(mem, len)));
+        assert_se(streq(hex, hex2));
+}
+
+/* https://tools.ietf.org/html/rfc4648#section-10 */
+static void test_base32hexmem(void) {
+        char *b32;
+
+        b32 = base32hexmem("", strlen(""), true);
+        assert_se(b32);
+        assert_se(streq(b32, ""));
+        free(b32);
+
+        b32 = base32hexmem("f", strlen("f"), true);
+        assert_se(b32);
+        assert_se(streq(b32, "CO======"));
+        free(b32);
+
+        b32 = base32hexmem("fo", strlen("fo"), true);
+        assert_se(b32);
+        assert_se(streq(b32, "CPNG===="));
+        free(b32);
+
+        b32 = base32hexmem("foo", strlen("foo"), true);
+        assert_se(b32);
+        assert_se(streq(b32, "CPNMU==="));
+        free(b32);
+
+        b32 = base32hexmem("foob", strlen("foob"), true);
+        assert_se(b32);
+        assert_se(streq(b32, "CPNMUOG="));
+        free(b32);
+
+        b32 = base32hexmem("fooba", strlen("fooba"), true);
+        assert_se(b32);
+        assert_se(streq(b32, "CPNMUOJ1"));
+        free(b32);
+
+        b32 = base32hexmem("foobar", strlen("foobar"), true);
+        assert_se(b32);
+        assert_se(streq(b32, "CPNMUOJ1E8======"));
+        free(b32);
+
+        b32 = base32hexmem("", strlen(""), false);
+        assert_se(b32);
+        assert_se(streq(b32, ""));
+        free(b32);
+
+        b32 = base32hexmem("f", strlen("f"), false);
+        assert_se(b32);
+        assert_se(streq(b32, "CO"));
+        free(b32);
+
+        b32 = base32hexmem("fo", strlen("fo"), false);
+        assert_se(b32);
+        assert_se(streq(b32, "CPNG"));
+        free(b32);
+
+        b32 = base32hexmem("foo", strlen("foo"), false);
+        assert_se(b32);
+        assert_se(streq(b32, "CPNMU"));
+        free(b32);
+
+        b32 = base32hexmem("foob", strlen("foob"), false);
+        assert_se(b32);
+        assert_se(streq(b32, "CPNMUOG"));
+        free(b32);
+
+        b32 = base32hexmem("fooba", strlen("fooba"), false);
+        assert_se(b32);
+        assert_se(streq(b32, "CPNMUOJ1"));
+        free(b32);
+
+        b32 = base32hexmem("foobar", strlen("foobar"), false);
+        assert_se(b32);
+        assert_se(streq(b32, "CPNMUOJ1E8"));
+        free(b32);
+}
+
+static void test_unbase32hexmem(void) {
+        void *mem;
+        size_t len;
+
+        assert_se(unbase32hexmem("", strlen(""), true, &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), ""));
+        free(mem);
+
+        assert_se(unbase32hexmem("CO======", strlen("CO======"), true, &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), "f"));
+        free(mem);
+
+        assert_se(unbase32hexmem("CPNG====", strlen("CPNG===="), true, &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), "fo"));
+        free(mem);
+
+        assert_se(unbase32hexmem("CPNMU===", strlen("CPNMU==="), true, &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), "foo"));
+        free(mem);
+
+        assert_se(unbase32hexmem("CPNMUOG=", strlen("CPNMUOG="), true, &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), "foob"));
+        free(mem);
+
+        assert_se(unbase32hexmem("CPNMUOJ1", strlen("CPNMUOJ1"), true, &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), "fooba"));
+        free(mem);
+
+        assert_se(unbase32hexmem("CPNMUOJ1E8======", strlen("CPNMUOJ1E8======"), true, &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), "foobar"));
+        free(mem);
+
+        assert_se(unbase32hexmem("A", strlen("A"), true, &mem, &len) == -EINVAL);
+        assert_se(unbase32hexmem("A=======", strlen("A======="), true, &mem, &len) == -EINVAL);
+        assert_se(unbase32hexmem("AAA=====", strlen("AAA====="), true, &mem, &len) == -EINVAL);
+        assert_se(unbase32hexmem("AAAAAA==", strlen("AAAAAA=="), true, &mem, &len) == -EINVAL);
+        assert_se(unbase32hexmem("AB======", strlen("AB======"), true, &mem, &len) == -EINVAL);
+        assert_se(unbase32hexmem("AAAB====", strlen("AAAB===="), true, &mem, &len) == -EINVAL);
+        assert_se(unbase32hexmem("AAAAB===", strlen("AAAAB==="), true, &mem, &len) == -EINVAL);
+        assert_se(unbase32hexmem("AAAAAAB=", strlen("AAAAAAB="), true, &mem, &len) == -EINVAL);
+
+        assert_se(unbase32hexmem("XPNMUOJ1", strlen("CPNMUOJ1"), true, &mem, &len) == -EINVAL);
+        assert_se(unbase32hexmem("CXNMUOJ1", strlen("CPNMUOJ1"), true, &mem, &len) == -EINVAL);
+        assert_se(unbase32hexmem("CPXMUOJ1", strlen("CPNMUOJ1"), true, &mem, &len) == -EINVAL);
+        assert_se(unbase32hexmem("CPNXUOJ1", strlen("CPNMUOJ1"), true, &mem, &len) == -EINVAL);
+        assert_se(unbase32hexmem("CPNMXOJ1", strlen("CPNMUOJ1"), true, &mem, &len) == -EINVAL);
+        assert_se(unbase32hexmem("CPNMUXJ1", strlen("CPNMUOJ1"), true, &mem, &len) == -EINVAL);
+        assert_se(unbase32hexmem("CPNMUOX1", strlen("CPNMUOJ1"), true, &mem, &len) == -EINVAL);
+        assert_se(unbase32hexmem("CPNMUOJX", strlen("CPNMUOJ1"), true, &mem, &len) == -EINVAL);
+
+        assert_se(unbase32hexmem("", strlen(""), false, &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), ""));
+        free(mem);
+
+        assert_se(unbase32hexmem("CO", strlen("CO"), false, &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), "f"));
+        free(mem);
+
+        assert_se(unbase32hexmem("CPNG", strlen("CPNG"), false, &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), "fo"));
+        free(mem);
+
+        assert_se(unbase32hexmem("CPNMU", strlen("CPNMU"), false, &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), "foo"));
+        free(mem);
+
+        assert_se(unbase32hexmem("CPNMUOG", strlen("CPNMUOG"), false, &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), "foob"));
+        free(mem);
+
+        assert_se(unbase32hexmem("CPNMUOJ1", strlen("CPNMUOJ1"), false, &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), "fooba"));
+        free(mem);
+
+        assert_se(unbase32hexmem("CPNMUOJ1E8", strlen("CPNMUOJ1E8"), false, &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), "foobar"));
+        free(mem);
+
+        assert_se(unbase32hexmem("CPNMUOG=", strlen("CPNMUOG="), false, &mem, &len) == -EINVAL);
+        assert_se(unbase32hexmem("CPNMUOJ1E8======", strlen("CPNMUOJ1E8======"), false, &mem, &len) == -EINVAL);
+        assert_se(unbase32hexmem("A", strlen("A"), false, &mem, &len) == -EINVAL);
+        assert_se(unbase32hexmem("A", strlen("A"), false, &mem, &len) == -EINVAL);
+        assert_se(unbase32hexmem("AAA", strlen("AAA"), false, &mem, &len) == -EINVAL);
+        assert_se(unbase32hexmem("AAAAAA", strlen("AAAAAA"), false, &mem, &len) == -EINVAL);
+        assert_se(unbase32hexmem("AB", strlen("AB"), false, &mem, &len) == -EINVAL);
+        assert_se(unbase32hexmem("AAAB", strlen("AAAB"), false, &mem, &len) == -EINVAL);
+        assert_se(unbase32hexmem("AAAAB", strlen("AAAAB"), false, &mem, &len) == -EINVAL);
+        assert_se(unbase32hexmem("AAAAAAB", strlen("AAAAAAB"), false, &mem, &len) == -EINVAL);
+}
+
+/* https://tools.ietf.org/html/rfc4648#section-10 */
+static void test_base64mem(void) {
+        char *b64;
+
+        assert_se(base64mem("", strlen(""), &b64) == 0);
+        assert_se(streq(b64, ""));
+        free(b64);
+
+        assert_se(base64mem("f", strlen("f"), &b64) == 4);
+        assert_se(streq(b64, "Zg=="));
+        free(b64);
+
+        assert_se(base64mem("fo", strlen("fo"), &b64) == 4);
+        assert_se(streq(b64, "Zm8="));
+        free(b64);
+
+        assert_se(base64mem("foo", strlen("foo"), &b64) == 4);
+        assert_se(streq(b64, "Zm9v"));
+        free(b64);
+
+        assert_se(base64mem("foob", strlen("foob"), &b64) == 8);
+        assert_se(streq(b64, "Zm9vYg=="));
+        free(b64);
+
+        assert_se(base64mem("fooba", strlen("fooba"), &b64) == 8);
+        assert_se(streq(b64, "Zm9vYmE="));
+        free(b64);
+
+        assert_se(base64mem("foobar", strlen("foobar"), &b64) == 8);
+        assert_se(streq(b64, "Zm9vYmFy"));
+        free(b64);
+}
+
+static void test_unbase64mem(void) {
+        void *mem;
+        size_t len;
+
+        assert_se(unbase64mem("", strlen(""), &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), ""));
+        free(mem);
+
+        assert_se(unbase64mem("Zg==", strlen("Zg=="), &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), "f"));
+        free(mem);
+
+        assert_se(unbase64mem("Zm8=", strlen("Zm8="), &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), "fo"));
+        free(mem);
+
+        assert_se(unbase64mem("Zm9v", strlen("Zm9v"), &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), "foo"));
+        free(mem);
+
+        assert_se(unbase64mem("Zm9vYg==", strlen("Zm9vYg=="), &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), "foob"));
+        free(mem);
+
+        assert_se(unbase64mem("Zm9vYmE=", strlen("Zm9vYmE="), &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), "fooba"));
+        free(mem);
+
+        assert_se(unbase64mem("Zm9vYmFy", strlen("Zm9vYmFy"), &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), "foobar"));
+        free(mem);
+
+        assert_se(unbase64mem("A", strlen("A"), &mem, &len) == -EINVAL);
+        assert_se(unbase64mem("A====", strlen("A===="), &mem, &len) == -EINVAL);
+        assert_se(unbase64mem("AAB==", strlen("AAB=="), &mem, &len) == -EINVAL);
+        assert_se(unbase64mem("AAAB=", strlen("AAAB="), &mem, &len) == -EINVAL);
+}
+
+static void test_hexdump(void) {
+        uint8_t data[146];
+        unsigned i;
+
+        hexdump(stdout, NULL, 0);
+        hexdump(stdout, "", 0);
+        hexdump(stdout, "", 1);
+        hexdump(stdout, "x", 1);
+        hexdump(stdout, "x", 2);
+        hexdump(stdout, "foobar", 7);
+        hexdump(stdout, "f\nobar", 7);
+        hexdump(stdout, "xxxxxxxxxxxxxxxxxxxxyz", 23);
+
+        for (i = 0; i < ELEMENTSOF(data); i++)
+                data[i] = i*2;
+
+        hexdump(stdout, data, sizeof(data));
+}
+
+int main(int argc, char *argv[]) {
+        test_hexchar();
+        test_unhexchar();
+        test_base32hexchar();
+        test_unbase32hexchar();
+        test_base64char();
+        test_unbase64char();
+        test_octchar();
+        test_unoctchar();
+        test_decchar();
+        test_undecchar();
+        test_unhexmem();
+        test_base32hexmem();
+        test_unbase32hexmem();
+        test_base64mem();
+        test_unbase64mem();
+        test_hexdump();
+
+        return 0;
+}
diff --git a/src/test/test-id128.c b/src/test/test-id128.c
new file mode 100644 (file)
index 0000000..e8c4c3e
--- /dev/null
@@ -0,0 +1,171 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2011 Lennart Poettering
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <string.h>
+
+#include "sd-daemon.h"
+#include "sd-id128.h"
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "id128-util.h"
+#include "macro.h"
+#include "string-util.h"
+#include "util.h"
+
+#define ID128_WALDI SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10)
+#define STR_WALDI "0102030405060708090a0b0c0d0e0f10"
+#define UUID_WALDI "01020304-0506-0708-090a-0b0c0d0e0f10"
+
+int main(int argc, char *argv[]) {
+        sd_id128_t id, id2;
+        char t[33], q[37];
+        _cleanup_free_ char *b = NULL;
+        _cleanup_close_ int fd = -1;
+        int r;
+
+        assert_se(sd_id128_randomize(&id) == 0);
+        printf("random: %s\n", sd_id128_to_string(id, t));
+
+        assert_se(sd_id128_from_string(t, &id2) == 0);
+        assert_se(sd_id128_equal(id, id2));
+
+        if (sd_booted() > 0) {
+                assert_se(sd_id128_get_machine(&id) == 0);
+                printf("machine: %s\n", sd_id128_to_string(id, t));
+
+                assert_se(sd_id128_get_boot(&id) == 0);
+                printf("boot: %s\n", sd_id128_to_string(id, t));
+        }
+
+        printf("waldi: %s\n", sd_id128_to_string(ID128_WALDI, t));
+        assert_se(streq(t, STR_WALDI));
+
+        assert_se(asprintf(&b, SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(ID128_WALDI)) == 32);
+        printf("waldi2: %s\n", b);
+        assert_se(streq(t, b));
+
+        printf("waldi3: %s\n", id128_to_uuid_string(ID128_WALDI, q));
+        assert_se(streq(q, UUID_WALDI));
+
+        b = mfree(b);
+        assert_se(asprintf(&b, ID128_UUID_FORMAT_STR, SD_ID128_FORMAT_VAL(ID128_WALDI)) == 36);
+        printf("waldi4: %s\n", b);
+        assert_se(streq(q, b));
+
+        assert_se(sd_id128_from_string(STR_WALDI, &id) >= 0);
+        assert_se(sd_id128_equal(id, ID128_WALDI));
+
+        assert_se(sd_id128_from_string(UUID_WALDI, &id) >= 0);
+        assert_se(sd_id128_equal(id, ID128_WALDI));
+
+        assert_se(sd_id128_from_string("", &id) < 0);
+        assert_se(sd_id128_from_string("01020304-0506-0708-090a-0b0c0d0e0f101", &id) < 0);
+        assert_se(sd_id128_from_string("01020304-0506-0708-090a-0b0c0d0e0f10-", &id) < 0);
+        assert_se(sd_id128_from_string("01020304-0506-0708-090a0b0c0d0e0f10", &id) < 0);
+        assert_se(sd_id128_from_string("010203040506-0708-090a-0b0c0d0e0f10", &id) < 0);
+
+        assert_se(id128_is_valid(STR_WALDI));
+        assert_se(id128_is_valid(UUID_WALDI));
+        assert_se(!id128_is_valid(""));
+        assert_se(!id128_is_valid("01020304-0506-0708-090a-0b0c0d0e0f101"));
+        assert_se(!id128_is_valid("01020304-0506-0708-090a-0b0c0d0e0f10-"));
+        assert_se(!id128_is_valid("01020304-0506-0708-090a0b0c0d0e0f10"));
+        assert_se(!id128_is_valid("010203040506-0708-090a-0b0c0d0e0f10"));
+
+        fd = open_tmpfile_unlinkable(NULL, O_RDWR|O_CLOEXEC);
+        assert_se(fd >= 0);
+
+        /* First, write as UUID */
+        assert_se(sd_id128_randomize(&id) >= 0);
+        assert_se(id128_write_fd(fd, ID128_UUID, id, false) >= 0);
+
+        assert_se(lseek(fd, 0, SEEK_SET) == 0);
+        assert_se(id128_read_fd(fd, ID128_PLAIN, &id2) == -EINVAL);
+
+        assert_se(lseek(fd, 0, SEEK_SET) == 0);
+        assert_se(id128_read_fd(fd, ID128_UUID, &id2) >= 0);
+        assert_se(sd_id128_equal(id, id2));
+
+        assert_se(lseek(fd, 0, SEEK_SET) == 0);
+        assert_se(id128_read_fd(fd, ID128_ANY, &id2) >= 0);
+        assert_se(sd_id128_equal(id, id2));
+
+        /* Second, write as plain */
+        assert_se(lseek(fd, 0, SEEK_SET) == 0);
+        assert_se(ftruncate(fd, 0) >= 0);
+
+        assert_se(sd_id128_randomize(&id) >= 0);
+        assert_se(id128_write_fd(fd, ID128_PLAIN, id, false) >= 0);
+
+        assert_se(lseek(fd, 0, SEEK_SET) == 0);
+        assert_se(id128_read_fd(fd, ID128_UUID, &id2) == -EINVAL);
+
+        assert_se(lseek(fd, 0, SEEK_SET) == 0);
+        assert_se(id128_read_fd(fd, ID128_PLAIN, &id2) >= 0);
+        assert_se(sd_id128_equal(id, id2));
+
+        assert_se(lseek(fd, 0, SEEK_SET) == 0);
+        assert_se(id128_read_fd(fd, ID128_ANY, &id2) >= 0);
+        assert_se(sd_id128_equal(id, id2));
+
+        /* Third, write plain without trailing newline */
+        assert_se(lseek(fd, 0, SEEK_SET) == 0);
+        assert_se(ftruncate(fd, 0) >= 0);
+
+        assert_se(sd_id128_randomize(&id) >= 0);
+        assert_se(write(fd, sd_id128_to_string(id, t), 32) == 32);
+
+        assert_se(lseek(fd, 0, SEEK_SET) == 0);
+        assert_se(id128_read_fd(fd, ID128_UUID, &id2) == -EINVAL);
+
+        assert_se(lseek(fd, 0, SEEK_SET) == 0);
+        assert_se(id128_read_fd(fd, ID128_PLAIN, &id2) >= 0);
+        assert_se(sd_id128_equal(id, id2));
+
+        /* Third, write UUID without trailing newline */
+        assert_se(lseek(fd, 0, SEEK_SET) == 0);
+        assert_se(ftruncate(fd, 0) >= 0);
+
+        assert_se(sd_id128_randomize(&id) >= 0);
+        assert_se(write(fd, id128_to_uuid_string(id, q), 36) == 36);
+
+        assert_se(lseek(fd, 0, SEEK_SET) == 0);
+        assert_se(id128_read_fd(fd, ID128_PLAIN, &id2) == -EINVAL);
+
+        assert_se(lseek(fd, 0, SEEK_SET) == 0);
+        assert_se(id128_read_fd(fd, ID128_UUID, &id2) >= 0);
+        assert_se(sd_id128_equal(id, id2));
+
+        assert_se(sd_id128_get_machine_app_specific(SD_ID128_MAKE(f0,3d,aa,eb,1c,33,4b,43,a7,32,17,29,44,bf,77,2e), &id) >= 0);
+        assert_se(sd_id128_get_machine_app_specific(SD_ID128_MAKE(f0,3d,aa,eb,1c,33,4b,43,a7,32,17,29,44,bf,77,2e), &id2) >= 0);
+        assert_se(sd_id128_equal(id, id2));
+        assert_se(sd_id128_get_machine_app_specific(SD_ID128_MAKE(51,df,0b,4b,c3,b0,4c,97,80,e2,99,b9,8c,a3,73,b8), &id2) >= 0);
+        assert_se(!sd_id128_equal(id, id2));
+
+        /* Query the invocation ID */
+        r = sd_id128_get_invocation(&id);
+        if (r < 0)
+                log_warning_errno(r, "Failed to get invocation ID, ignoring: %m");
+        else
+                log_info("Invocation ID: " SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(id));
+
+        return 0;
+}
diff --git a/src/test/test-io-util.c b/src/test/test-io-util.c
new file mode 100644 (file)
index 0000000..10bd383
--- /dev/null
@@ -0,0 +1,69 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "io-util.h"
+#include "macro.h"
+
+static void test_sparse_write_one(int fd, const char *buffer, size_t n) {
+        char check[n];
+
+        assert_se(lseek(fd, 0, SEEK_SET) == 0);
+        assert_se(ftruncate(fd, 0) >= 0);
+        assert_se(sparse_write(fd, buffer, n, 4) == (ssize_t) n);
+
+        assert_se(lseek(fd, 0, SEEK_CUR) == (off_t) n);
+        assert_se(ftruncate(fd, n) >= 0);
+
+        assert_se(lseek(fd, 0, SEEK_SET) == 0);
+        assert_se(read(fd, check, n) == (ssize_t) n);
+
+        assert_se(memcmp(buffer, check, n) == 0);
+}
+
+static void test_sparse_write(void) {
+        const char test_a[] = "test";
+        const char test_b[] = "\0\0\0\0test\0\0\0\0";
+        const char test_c[] = "\0\0test\0\0\0\0";
+        const char test_d[] = "\0\0test\0\0\0test\0\0\0\0test\0\0\0\0\0test\0\0\0test\0\0\0\0test\0\0\0\0\0\0\0\0";
+        const char test_e[] = "test\0\0\0\0test";
+        _cleanup_close_ int fd = -1;
+        char fn[] = "/tmp/sparseXXXXXX";
+
+        fd = mkostemp(fn, O_CLOEXEC);
+        assert_se(fd >= 0);
+        unlink(fn);
+
+        test_sparse_write_one(fd, test_a, sizeof(test_a));
+        test_sparse_write_one(fd, test_b, sizeof(test_b));
+        test_sparse_write_one(fd, test_c, sizeof(test_c));
+        test_sparse_write_one(fd, test_d, sizeof(test_d));
+        test_sparse_write_one(fd, test_e, sizeof(test_e));
+}
+
+int main(void) {
+        test_sparse_write();
+
+        return 0;
+}
diff --git a/src/test/test-ipcrm.c b/src/test/test-ipcrm.c
new file mode 100644 (file)
index 0000000..2a3852b
--- /dev/null
@@ -0,0 +1,42 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2014 Lennart Poettering
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "clean-ipc.h"
+#include "user-util.h"
+#include "util.h"
+
+int main(int argc, char *argv[]) {
+        uid_t uid;
+        int r;
+#if 0 /// not configurable in elogind
+        const char* name = argv[1] ?: NOBODY_USER_NAME;
+#else
+        const char* name = argv[1] ?: "nobody";
+#endif // 0
+
+        r = get_user_creds(&name, &uid, NULL, NULL, NULL);
+        if (r < 0) {
+                log_full_errno(r == -ESRCH ? LOG_NOTICE : LOG_ERR,
+                               r, "Failed to resolve \"%s\": %m", name);
+                return r == -ESRCH ? EXIT_TEST_SKIP : EXIT_FAILURE;
+        }
+
+        r = clean_ipc_by_uid(uid);
+        return  r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/src/test/test-list.c b/src/test/test-list.c
new file mode 100644 (file)
index 0000000..0ccd745
--- /dev/null
@@ -0,0 +1,221 @@
+/***
+  This file is part of systemd
+
+  Copyright 2013 Jan Janssen
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "list.h"
+#include "util.h"
+
+int main(int argc, const char *argv[]) {
+        size_t i;
+        typedef struct list_item {
+                LIST_FIELDS(struct list_item, item);
+        } list_item;
+        LIST_HEAD(list_item, head);
+        list_item items[4];
+        list_item *cursor;
+
+        LIST_HEAD_INIT(head);
+        assert_se(head == NULL);
+
+        for (i = 0; i < ELEMENTSOF(items); i++) {
+                LIST_INIT(item, &items[i]);
+                assert_se(LIST_JUST_US(item, &items[i]));
+                LIST_PREPEND(item, head, &items[i]);
+        }
+
+        i = 0;
+        LIST_FOREACH_OTHERS(item, cursor, &items[2]) {
+                i++;
+                assert_se(cursor != &items[2]);
+        }
+        assert_se(i == ELEMENTSOF(items)-1);
+
+        i = 0;
+        LIST_FOREACH_OTHERS(item, cursor, &items[0]) {
+                i++;
+                assert_se(cursor != &items[0]);
+        }
+        assert_se(i == ELEMENTSOF(items)-1);
+
+        i = 0;
+        LIST_FOREACH_OTHERS(item, cursor, &items[3]) {
+                i++;
+                assert_se(cursor != &items[3]);
+        }
+        assert_se(i == ELEMENTSOF(items)-1);
+
+        assert_se(!LIST_JUST_US(item, head));
+
+        assert_se(items[0].item_next == NULL);
+        assert_se(items[1].item_next == &items[0]);
+        assert_se(items[2].item_next == &items[1]);
+        assert_se(items[3].item_next == &items[2]);
+
+        assert_se(items[0].item_prev == &items[1]);
+        assert_se(items[1].item_prev == &items[2]);
+        assert_se(items[2].item_prev == &items[3]);
+        assert_se(items[3].item_prev == NULL);
+
+        LIST_FIND_HEAD(item, &items[0], cursor);
+        assert_se(cursor == &items[3]);
+
+        LIST_FIND_TAIL(item, &items[3], cursor);
+        assert_se(cursor == &items[0]);
+
+        LIST_REMOVE(item, head, &items[1]);
+        assert_se(LIST_JUST_US(item, &items[1]));
+
+        assert_se(items[0].item_next == NULL);
+        assert_se(items[2].item_next == &items[0]);
+        assert_se(items[3].item_next == &items[2]);
+
+        assert_se(items[0].item_prev == &items[2]);
+        assert_se(items[2].item_prev == &items[3]);
+        assert_se(items[3].item_prev == NULL);
+
+        LIST_INSERT_AFTER(item, head, &items[3], &items[1]);
+        assert_se(items[0].item_next == NULL);
+        assert_se(items[2].item_next == &items[0]);
+        assert_se(items[1].item_next == &items[2]);
+        assert_se(items[3].item_next == &items[1]);
+
+        assert_se(items[0].item_prev == &items[2]);
+        assert_se(items[2].item_prev == &items[1]);
+        assert_se(items[1].item_prev == &items[3]);
+        assert_se(items[3].item_prev == NULL);
+
+        LIST_REMOVE(item, head, &items[1]);
+        assert_se(LIST_JUST_US(item, &items[1]));
+
+        assert_se(items[0].item_next == NULL);
+        assert_se(items[2].item_next == &items[0]);
+        assert_se(items[3].item_next == &items[2]);
+
+        assert_se(items[0].item_prev == &items[2]);
+        assert_se(items[2].item_prev == &items[3]);
+        assert_se(items[3].item_prev == NULL);
+
+        LIST_INSERT_BEFORE(item, head, &items[2], &items[1]);
+        assert_se(items[0].item_next == NULL);
+        assert_se(items[2].item_next == &items[0]);
+        assert_se(items[1].item_next == &items[2]);
+        assert_se(items[3].item_next == &items[1]);
+
+        assert_se(items[0].item_prev == &items[2]);
+        assert_se(items[2].item_prev == &items[1]);
+        assert_se(items[1].item_prev == &items[3]);
+        assert_se(items[3].item_prev == NULL);
+
+        LIST_REMOVE(item, head, &items[0]);
+        assert_se(LIST_JUST_US(item, &items[0]));
+
+        assert_se(items[2].item_next == NULL);
+        assert_se(items[1].item_next == &items[2]);
+        assert_se(items[3].item_next == &items[1]);
+
+        assert_se(items[2].item_prev == &items[1]);
+        assert_se(items[1].item_prev == &items[3]);
+        assert_se(items[3].item_prev == NULL);
+
+        LIST_INSERT_BEFORE(item, head, &items[3], &items[0]);
+        assert_se(items[2].item_next == NULL);
+        assert_se(items[1].item_next == &items[2]);
+        assert_se(items[3].item_next == &items[1]);
+        assert_se(items[0].item_next == &items[3]);
+
+        assert_se(items[2].item_prev == &items[1]);
+        assert_se(items[1].item_prev == &items[3]);
+        assert_se(items[3].item_prev == &items[0]);
+        assert_se(items[0].item_prev == NULL);
+        assert_se(head == &items[0]);
+
+        LIST_REMOVE(item, head, &items[0]);
+        assert_se(LIST_JUST_US(item, &items[0]));
+
+        assert_se(items[2].item_next == NULL);
+        assert_se(items[1].item_next == &items[2]);
+        assert_se(items[3].item_next == &items[1]);
+
+        assert_se(items[2].item_prev == &items[1]);
+        assert_se(items[1].item_prev == &items[3]);
+        assert_se(items[3].item_prev == NULL);
+
+        LIST_INSERT_BEFORE(item, head, NULL, &items[0]);
+        assert_se(items[0].item_next == NULL);
+        assert_se(items[2].item_next == &items[0]);
+        assert_se(items[1].item_next == &items[2]);
+        assert_se(items[3].item_next == &items[1]);
+
+        assert_se(items[0].item_prev == &items[2]);
+        assert_se(items[2].item_prev == &items[1]);
+        assert_se(items[1].item_prev == &items[3]);
+        assert_se(items[3].item_prev == NULL);
+
+        LIST_REMOVE(item, head, &items[0]);
+        assert_se(LIST_JUST_US(item, &items[0]));
+
+        assert_se(items[2].item_next == NULL);
+        assert_se(items[1].item_next == &items[2]);
+        assert_se(items[3].item_next == &items[1]);
+
+        assert_se(items[2].item_prev == &items[1]);
+        assert_se(items[1].item_prev == &items[3]);
+        assert_se(items[3].item_prev == NULL);
+
+        LIST_REMOVE(item, head, &items[1]);
+        assert_se(LIST_JUST_US(item, &items[1]));
+
+        assert_se(items[2].item_next == NULL);
+        assert_se(items[3].item_next == &items[2]);
+
+        assert_se(items[2].item_prev == &items[3]);
+        assert_se(items[3].item_prev == NULL);
+
+        LIST_REMOVE(item, head, &items[2]);
+        assert_se(LIST_JUST_US(item, &items[2]));
+        assert_se(LIST_JUST_US(item, head));
+
+        LIST_REMOVE(item, head, &items[3]);
+        assert_se(LIST_JUST_US(item, &items[3]));
+
+        assert_se(head == NULL);
+
+        for (i = 0; i < ELEMENTSOF(items); i++) {
+                assert_se(LIST_JUST_US(item, &items[i]));
+                LIST_APPEND(item, head, &items[i]);
+        }
+
+        assert_se(!LIST_JUST_US(item, head));
+
+        assert_se(items[0].item_next == &items[1]);
+        assert_se(items[1].item_next == &items[2]);
+        assert_se(items[2].item_next == &items[3]);
+        assert_se(items[3].item_next == NULL);
+
+        assert_se(items[0].item_prev == NULL);
+        assert_se(items[1].item_prev == &items[0]);
+        assert_se(items[2].item_prev == &items[1]);
+        assert_se(items[3].item_prev == &items[2]);
+
+        for (i = 0; i < ELEMENTSOF(items); i++)
+                LIST_REMOVE(item, head, &items[i]);
+
+        assert_se(head == NULL);
+
+        return 0;
+}
diff --git a/src/test/test-locale-util.c b/src/test/test-locale-util.c
new file mode 100644 (file)
index 0000000..427c698
--- /dev/null
@@ -0,0 +1,58 @@
+/***
+  This file is part of systemd
+
+  Copyright 2014 Ronny Chevalier
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+
+#include "locale-util.h"
+#include "macro.h"
+#include "strv.h"
+
+static void test_get_locales(void) {
+        _cleanup_strv_free_ char **locales = NULL;
+        char **p;
+        int r;
+
+        r = get_locales(&locales);
+        assert_se(r >= 0);
+        assert_se(locales);
+
+        STRV_FOREACH(p, locales) {
+                puts(*p);
+                assert_se(locale_is_valid(*p));
+        }
+}
+
+static void test_locale_is_valid(void) {
+        assert_se(locale_is_valid("en_EN.utf8"));
+        assert_se(locale_is_valid("fr_FR.utf8"));
+        assert_se(locale_is_valid("fr_FR@euro"));
+        assert_se(locale_is_valid("fi_FI"));
+        assert_se(locale_is_valid("POSIX"));
+        assert_se(locale_is_valid("C"));
+
+        assert_se(!locale_is_valid(""));
+        assert_se(!locale_is_valid("/usr/bin/foo"));
+        assert_se(!locale_is_valid("\x01gar\x02 bage\x03"));
+}
+
+int main(int argc, char *argv[]) {
+        test_get_locales();
+        test_locale_is_valid();
+
+        return 0;
+}
diff --git a/src/test/test-log.c b/src/test/test-log.c
new file mode 100644 (file)
index 0000000..ae9e113
--- /dev/null
@@ -0,0 +1,53 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2012 Lennart Poettering
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stddef.h>
+#include <unistd.h>
+
+#include "format-util.h"
+#include "log.h"
+#include "util.h"
+
+int main(int argc, char* argv[]) {
+
+        log_set_target(LOG_TARGET_CONSOLE);
+        log_open();
+
+        log_struct(LOG_INFO,
+                   "MESSAGE=Waldo PID="PID_FMT, getpid(),
+                   "SERVICE=piepapo",
+                   NULL);
+
+        log_set_target(LOG_TARGET_JOURNAL);
+        log_open();
+
+        log_struct(LOG_INFO,
+                   "MESSAGE=Foobar PID="PID_FMT, getpid(),
+                   "SERVICE=foobar",
+                   NULL);
+
+        log_struct(LOG_INFO,
+                   "MESSAGE=Foobar PID="PID_FMT, getpid(),
+                   "FORMAT_STR_TEST=1=%i A=%c 2=%hi 3=%li 4=%lli 1=%p foo=%s 2.5=%g 3.5=%g 4.5=%Lg",
+                   (int) 1, 'A', (short) 2, (long int) 3, (long long int) 4, (void*) 1, "foo", (float) 2.5f, (double) 3.5, (long double) 4.5,
+                   "SUFFIX=GOT IT",
+                   NULL);
+
+        return 0;
+}
diff --git a/src/test/test-parse-util.c b/src/test/test-parse-util.c
new file mode 100644 (file)
index 0000000..981370a
--- /dev/null
@@ -0,0 +1,555 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+  Copyright 2013 Thomas H.P. Andersen
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <locale.h>
+#include <math.h>
+
+#include "log.h"
+#include "parse-util.h"
+
+static void test_parse_boolean(void) {
+        assert_se(parse_boolean("1") == 1);
+        assert_se(parse_boolean("y") == 1);
+        assert_se(parse_boolean("Y") == 1);
+        assert_se(parse_boolean("yes") == 1);
+        assert_se(parse_boolean("YES") == 1);
+        assert_se(parse_boolean("true") == 1);
+        assert_se(parse_boolean("TRUE") == 1);
+        assert_se(parse_boolean("on") == 1);
+        assert_se(parse_boolean("ON") == 1);
+
+        assert_se(parse_boolean("0") == 0);
+        assert_se(parse_boolean("n") == 0);
+        assert_se(parse_boolean("N") == 0);
+        assert_se(parse_boolean("no") == 0);
+        assert_se(parse_boolean("NO") == 0);
+        assert_se(parse_boolean("false") == 0);
+        assert_se(parse_boolean("FALSE") == 0);
+        assert_se(parse_boolean("off") == 0);
+        assert_se(parse_boolean("OFF") == 0);
+
+        assert_se(parse_boolean("garbage") < 0);
+        assert_se(parse_boolean("") < 0);
+        assert_se(parse_boolean("full") < 0);
+}
+
+static void test_parse_pid(void) {
+        int r;
+        pid_t pid;
+
+        r = parse_pid("100", &pid);
+        assert_se(r == 0);
+        assert_se(pid == 100);
+
+        r = parse_pid("0x7FFFFFFF", &pid);
+        assert_se(r == 0);
+        assert_se(pid == 2147483647);
+
+        pid = 65; /* pid is left unchanged on ERANGE. Set to known arbitrary value. */
+        r = parse_pid("0", &pid);
+        assert_se(r == -ERANGE);
+        assert_se(pid == 65);
+
+        pid = 65; /* pid is left unchanged on ERANGE. Set to known arbitrary value. */
+        r = parse_pid("-100", &pid);
+        assert_se(r == -ERANGE);
+        assert_se(pid == 65);
+
+        pid = 65; /* pid is left unchanged on ERANGE. Set to known arbitrary value. */
+        r = parse_pid("0xFFFFFFFFFFFFFFFFF", &pid);
+        assert_se(r == -ERANGE);
+        assert_se(pid == 65);
+
+        r = parse_pid("junk", &pid);
+        assert_se(r == -EINVAL);
+}
+
+static void test_parse_mode(void) {
+        mode_t m;
+
+        assert_se(parse_mode("-1", &m) < 0);
+        assert_se(parse_mode("", &m) < 0);
+        assert_se(parse_mode("888", &m) < 0);
+        assert_se(parse_mode("77777", &m) < 0);
+
+        assert_se(parse_mode("544", &m) >= 0 && m == 0544);
+        assert_se(parse_mode("777", &m) >= 0 && m == 0777);
+        assert_se(parse_mode("7777", &m) >= 0 && m == 07777);
+        assert_se(parse_mode("0", &m) >= 0 && m == 0);
+}
+
+static void test_parse_size(void) {
+        uint64_t bytes;
+
+        assert_se(parse_size("111", 1024, &bytes) == 0);
+        assert_se(bytes == 111);
+
+        assert_se(parse_size("111.4", 1024, &bytes) == 0);
+        assert_se(bytes == 111);
+
+        assert_se(parse_size(" 112 B", 1024, &bytes) == 0);
+        assert_se(bytes == 112);
+
+        assert_se(parse_size(" 112.6 B", 1024, &bytes) == 0);
+        assert_se(bytes == 112);
+
+        assert_se(parse_size("3.5 K", 1024, &bytes) == 0);
+        assert_se(bytes == 3*1024 + 512);
+
+        assert_se(parse_size("3. K", 1024, &bytes) == 0);
+        assert_se(bytes == 3*1024);
+
+        assert_se(parse_size("3.0 K", 1024, &bytes) == 0);
+        assert_se(bytes == 3*1024);
+
+        assert_se(parse_size("3. 0 K", 1024, &bytes) == -EINVAL);
+
+        assert_se(parse_size(" 4 M 11.5K", 1024, &bytes) == 0);
+        assert_se(bytes == 4*1024*1024 + 11 * 1024 + 512);
+
+        assert_se(parse_size("3B3.5G", 1024, &bytes) == -EINVAL);
+
+        assert_se(parse_size("3.5G3B", 1024, &bytes) == 0);
+        assert_se(bytes == 3ULL*1024*1024*1024 + 512*1024*1024 + 3);
+
+        assert_se(parse_size("3.5G 4B", 1024, &bytes) == 0);
+        assert_se(bytes == 3ULL*1024*1024*1024 + 512*1024*1024 + 4);
+
+        assert_se(parse_size("3B3G4T", 1024, &bytes) == -EINVAL);
+
+        assert_se(parse_size("4T3G3B", 1024, &bytes) == 0);
+        assert_se(bytes == (4ULL*1024 + 3)*1024*1024*1024 + 3);
+
+        assert_se(parse_size(" 4 T 3 G 3 B", 1024, &bytes) == 0);
+        assert_se(bytes == (4ULL*1024 + 3)*1024*1024*1024 + 3);
+
+        assert_se(parse_size("12P", 1024, &bytes) == 0);
+        assert_se(bytes == 12ULL * 1024*1024*1024*1024*1024);
+
+        assert_se(parse_size("12P12P", 1024, &bytes) == -EINVAL);
+
+        assert_se(parse_size("3E 2P", 1024, &bytes) == 0);
+        assert_se(bytes == (3 * 1024 + 2ULL) * 1024*1024*1024*1024*1024);
+
+        assert_se(parse_size("12X", 1024, &bytes) == -EINVAL);
+
+        assert_se(parse_size("12.5X", 1024, &bytes) == -EINVAL);
+
+        assert_se(parse_size("12.5e3", 1024, &bytes) == -EINVAL);
+
+        assert_se(parse_size("1024E", 1024, &bytes) == -ERANGE);
+        assert_se(parse_size("-1", 1024, &bytes) == -ERANGE);
+        assert_se(parse_size("-1024E", 1024, &bytes) == -ERANGE);
+
+        assert_se(parse_size("-1024P", 1024, &bytes) == -ERANGE);
+
+        assert_se(parse_size("-10B 20K", 1024, &bytes) == -ERANGE);
+}
+
+#if 0 /// UNNEEDED by elogind
+static void test_parse_range(void) {
+        unsigned lower, upper;
+
+        /* Successful cases */
+        assert_se(parse_range("111", &lower, &upper) == 0);
+        assert_se(lower == 111);
+        assert_se(upper == 111);
+
+        assert_se(parse_range("111-123", &lower, &upper) == 0);
+        assert_se(lower == 111);
+        assert_se(upper == 123);
+
+        assert_se(parse_range("123-111", &lower, &upper) == 0);
+        assert_se(lower == 123);
+        assert_se(upper == 111);
+
+        assert_se(parse_range("123-123", &lower, &upper) == 0);
+        assert_se(lower == 123);
+        assert_se(upper == 123);
+
+        assert_se(parse_range("0", &lower, &upper) == 0);
+        assert_se(lower == 0);
+        assert_se(upper == 0);
+
+        assert_se(parse_range("0-15", &lower, &upper) == 0);
+        assert_se(lower == 0);
+        assert_se(upper == 15);
+
+        assert_se(parse_range("15-0", &lower, &upper) == 0);
+        assert_se(lower == 15);
+        assert_se(upper == 0);
+
+        assert_se(parse_range("128-65535", &lower, &upper) == 0);
+        assert_se(lower == 128);
+        assert_se(upper == 65535);
+
+        assert_se(parse_range("1024-4294967295", &lower, &upper) == 0);
+        assert_se(lower == 1024);
+        assert_se(upper == 4294967295);
+
+        /* Leading whitespace is acceptable */
+        assert_se(parse_range(" 111", &lower, &upper) == 0);
+        assert_se(lower == 111);
+        assert_se(upper == 111);
+
+        assert_se(parse_range(" 111-123", &lower, &upper) == 0);
+        assert_se(lower == 111);
+        assert_se(upper == 123);
+
+        assert_se(parse_range("111- 123", &lower, &upper) == 0);
+        assert_se(lower == 111);
+        assert_se(upper == 123);
+
+        assert_se(parse_range("\t111-\t123", &lower, &upper) == 0);
+        assert_se(lower == 111);
+        assert_se(upper == 123);
+
+        assert_se(parse_range(" \t 111- \t 123", &lower, &upper) == 0);
+        assert_se(lower == 111);
+        assert_se(upper == 123);
+
+        /* Error cases, make sure they fail as expected */
+        lower = upper = 9999;
+        assert_se(parse_range("111garbage", &lower, &upper) == -EINVAL);
+        assert_se(lower == 9999);
+        assert_se(upper == 9999);
+
+        assert_se(parse_range("garbage111", &lower, &upper) == -EINVAL);
+        assert_se(lower == 9999);
+        assert_se(upper == 9999);
+
+        assert_se(parse_range("garbage", &lower, &upper) == -EINVAL);
+        assert_se(lower == 9999);
+        assert_se(upper == 9999);
+
+        assert_se(parse_range("111-123garbage", &lower, &upper) == -EINVAL);
+        assert_se(lower == 9999);
+        assert_se(upper == 9999);
+
+        assert_se(parse_range("111garbage-123", &lower, &upper) == -EINVAL);
+        assert_se(lower == 9999);
+        assert_se(upper == 9999);
+
+        /* Empty string */
+        lower = upper = 9999;
+        assert_se(parse_range("", &lower, &upper) == -EINVAL);
+        assert_se(lower == 9999);
+        assert_se(upper == 9999);
+
+        /* 111--123 will pass -123 to safe_atou which returns -ERANGE for negative */
+        assert_se(parse_range("111--123", &lower, &upper) == -ERANGE);
+        assert_se(lower == 9999);
+        assert_se(upper == 9999);
+
+        assert_se(parse_range("-111-123", &lower, &upper) == -EINVAL);
+        assert_se(lower == 9999);
+        assert_se(upper == 9999);
+
+        assert_se(parse_range("111-123-", &lower, &upper) == -EINVAL);
+        assert_se(lower == 9999);
+        assert_se(upper == 9999);
+
+        assert_se(parse_range("111.4-123", &lower, &upper) == -EINVAL);
+        assert_se(lower == 9999);
+        assert_se(upper == 9999);
+
+        assert_se(parse_range("111-123.4", &lower, &upper) == -EINVAL);
+        assert_se(lower == 9999);
+        assert_se(upper == 9999);
+
+        assert_se(parse_range("111,4-123", &lower, &upper) == -EINVAL);
+        assert_se(lower == 9999);
+        assert_se(upper == 9999);
+
+        assert_se(parse_range("111-123,4", &lower, &upper) == -EINVAL);
+        assert_se(lower == 9999);
+        assert_se(upper == 9999);
+
+        /* Error on trailing dash */
+        assert_se(parse_range("111-", &lower, &upper) == -EINVAL);
+        assert_se(lower == 9999);
+        assert_se(upper == 9999);
+
+        assert_se(parse_range("111-123-", &lower, &upper) == -EINVAL);
+        assert_se(lower == 9999);
+        assert_se(upper == 9999);
+
+        assert_se(parse_range("111--", &lower, &upper) == -EINVAL);
+        assert_se(lower == 9999);
+        assert_se(upper == 9999);
+
+        assert_se(parse_range("111- ", &lower, &upper) == -EINVAL);
+        assert_se(lower == 9999);
+        assert_se(upper == 9999);
+
+        /* Whitespace is not a separator */
+        assert_se(parse_range("111 123", &lower, &upper) == -EINVAL);
+        assert_se(lower == 9999);
+        assert_se(upper == 9999);
+
+        assert_se(parse_range("111\t123", &lower, &upper) == -EINVAL);
+        assert_se(lower == 9999);
+        assert_se(upper == 9999);
+
+        assert_se(parse_range("111 \t 123", &lower, &upper) == -EINVAL);
+        assert_se(lower == 9999);
+        assert_se(upper == 9999);
+
+        /* Trailing whitespace is invalid (from safe_atou) */
+        assert_se(parse_range("111 ", &lower, &upper) == -EINVAL);
+        assert_se(lower == 9999);
+        assert_se(upper == 9999);
+
+        assert_se(parse_range("111-123 ", &lower, &upper) == -EINVAL);
+        assert_se(lower == 9999);
+        assert_se(upper == 9999);
+
+        assert_se(parse_range("111 -123", &lower, &upper) == -EINVAL);
+        assert_se(lower == 9999);
+        assert_se(upper == 9999);
+
+        assert_se(parse_range("111 -123 ", &lower, &upper) == -EINVAL);
+        assert_se(lower == 9999);
+        assert_se(upper == 9999);
+
+        assert_se(parse_range("111\t-123\t", &lower, &upper) == -EINVAL);
+        assert_se(lower == 9999);
+        assert_se(upper == 9999);
+
+        assert_se(parse_range("111 \t -123 \t ", &lower, &upper) == -EINVAL);
+        assert_se(lower == 9999);
+        assert_se(upper == 9999);
+
+        /* Out of the "unsigned" range, this is 1<<64 */
+        assert_se(parse_range("0-18446744073709551616", &lower, &upper) == -ERANGE);
+        assert_se(lower == 9999);
+        assert_se(upper == 9999);
+}
+#endif // 0
+
+static void test_safe_atolli(void) {
+        int r;
+        long long l;
+
+        r = safe_atolli("12345", &l);
+        assert_se(r == 0);
+        assert_se(l == 12345);
+
+        r = safe_atolli("  12345", &l);
+        assert_se(r == 0);
+        assert_se(l == 12345);
+
+        r = safe_atolli("-12345", &l);
+        assert_se(r == 0);
+        assert_se(l == -12345);
+
+        r = safe_atolli("  -12345", &l);
+        assert_se(r == 0);
+        assert_se(l == -12345);
+
+        r = safe_atolli("12345678901234567890", &l);
+        assert_se(r == -ERANGE);
+
+        r = safe_atolli("-12345678901234567890", &l);
+        assert_se(r == -ERANGE);
+
+        r = safe_atolli("junk", &l);
+        assert_se(r == -EINVAL);
+}
+
+static void test_safe_atou16(void) {
+        int r;
+        uint16_t l;
+
+        r = safe_atou16("12345", &l);
+        assert_se(r == 0);
+        assert_se(l == 12345);
+
+        r = safe_atou16("  12345", &l);
+        assert_se(r == 0);
+        assert_se(l == 12345);
+
+        r = safe_atou16("123456", &l);
+        assert_se(r == -ERANGE);
+
+        r = safe_atou16("-1", &l);
+        assert_se(r == -ERANGE);
+
+        r = safe_atou16("  -1", &l);
+        assert_se(r == -ERANGE);
+
+        r = safe_atou16("junk", &l);
+        assert_se(r == -EINVAL);
+}
+
+static void test_safe_atoi16(void) {
+        int r;
+        int16_t l;
+
+        r = safe_atoi16("-12345", &l);
+        assert_se(r == 0);
+        assert_se(l == -12345);
+
+        r = safe_atoi16("  -12345", &l);
+        assert_se(r == 0);
+        assert_se(l == -12345);
+
+        r = safe_atoi16("32767", &l);
+        assert_se(r == 0);
+        assert_se(l == 32767);
+
+        r = safe_atoi16("  32767", &l);
+        assert_se(r == 0);
+        assert_se(l == 32767);
+
+        r = safe_atoi16("36536", &l);
+        assert_se(r == -ERANGE);
+
+        r = safe_atoi16("-32769", &l);
+        assert_se(r == -ERANGE);
+
+        r = safe_atoi16("junk", &l);
+        assert_se(r == -EINVAL);
+}
+
+static void test_safe_atod(void) {
+        int r;
+        double d;
+        char *e;
+
+        r = safe_atod("junk", &d);
+        assert_se(r == -EINVAL);
+
+        r = safe_atod("0.2244", &d);
+        assert_se(r == 0);
+        assert_se(fabs(d - 0.2244) < 0.000001);
+
+        r = safe_atod("0,5", &d);
+        assert_se(r == -EINVAL);
+
+        errno = 0;
+        strtod("0,5", &e);
+        assert_se(*e == ',');
+
+        /* Check if this really is locale independent */
+        if (setlocale(LC_NUMERIC, "de_DE.utf8")) {
+
+                r = safe_atod("0.2244", &d);
+                assert_se(r == 0);
+                assert_se(fabs(d - 0.2244) < 0.000001);
+
+                r = safe_atod("0,5", &d);
+                assert_se(r == -EINVAL);
+
+                errno = 0;
+                assert_se(fabs(strtod("0,5", &e) - 0.5) < 0.00001);
+        }
+
+        /* And check again, reset */
+        assert_se(setlocale(LC_NUMERIC, "C"));
+
+        r = safe_atod("0.2244", &d);
+        assert_se(r == 0);
+        assert_se(fabs(d - 0.2244) < 0.000001);
+
+        r = safe_atod("0,5", &d);
+        assert_se(r == -EINVAL);
+
+        errno = 0;
+        strtod("0,5", &e);
+        assert_se(*e == ',');
+}
+
+static void test_parse_percent(void) {
+        assert_se(parse_percent("") == -EINVAL);
+        assert_se(parse_percent("foo") == -EINVAL);
+        assert_se(parse_percent("0") == -EINVAL);
+        assert_se(parse_percent("50") == -EINVAL);
+        assert_se(parse_percent("100") == -EINVAL);
+        assert_se(parse_percent("-1") == -EINVAL);
+        assert_se(parse_percent("0%") == 0);
+        assert_se(parse_percent("55%") == 55);
+        assert_se(parse_percent("100%") == 100);
+        assert_se(parse_percent("-7%") == -ERANGE);
+        assert_se(parse_percent("107%") == -ERANGE);
+        assert_se(parse_percent("%") == -EINVAL);
+        assert_se(parse_percent("%%") == -EINVAL);
+        assert_se(parse_percent("%1") == -EINVAL);
+        assert_se(parse_percent("1%%") == -EINVAL);
+}
+
+static void test_parse_percent_unbounded(void) {
+        assert_se(parse_percent_unbounded("101%") == 101);
+        assert_se(parse_percent_unbounded("400%") == 400);
+}
+
+#if 0 /// UNNEEDED by elogind
+static void test_parse_nice(void) {
+        int n;
+
+        assert_se(parse_nice("0", &n) >= 0 && n == 0);
+        assert_se(parse_nice("+0", &n) >= 0 && n == 0);
+        assert_se(parse_nice("-1", &n) >= 0 && n == -1);
+        assert_se(parse_nice("-2", &n) >= 0 && n == -2);
+        assert_se(parse_nice("1", &n) >= 0 && n == 1);
+        assert_se(parse_nice("2", &n) >= 0 && n == 2);
+        assert_se(parse_nice("+1", &n) >= 0 && n == 1);
+        assert_se(parse_nice("+2", &n) >= 0 && n == 2);
+        assert_se(parse_nice("-20", &n) >= 0 && n == -20);
+        assert_se(parse_nice("19", &n) >= 0 && n == 19);
+        assert_se(parse_nice("+19", &n) >= 0 && n == 19);
+
+
+        assert_se(parse_nice("", &n) == -EINVAL);
+        assert_se(parse_nice("-", &n) == -EINVAL);
+        assert_se(parse_nice("+", &n) == -EINVAL);
+        assert_se(parse_nice("xx", &n) == -EINVAL);
+        assert_se(parse_nice("-50", &n) == -ERANGE);
+        assert_se(parse_nice("50", &n) == -ERANGE);
+        assert_se(parse_nice("+50", &n) == -ERANGE);
+        assert_se(parse_nice("-21", &n) == -ERANGE);
+        assert_se(parse_nice("20", &n) == -ERANGE);
+        assert_se(parse_nice("+20", &n) == -ERANGE);
+}
+#endif // 0
+
+int main(int argc, char *argv[]) {
+        log_parse_environment();
+        log_open();
+
+        test_parse_boolean();
+        test_parse_pid();
+        test_parse_mode();
+        test_parse_size();
+#if 0 /// UNNEEDED by elogind
+        test_parse_range();
+#endif // 0
+        test_safe_atolli();
+        test_safe_atou16();
+        test_safe_atoi16();
+        test_safe_atod();
+        test_parse_percent();
+        test_parse_percent_unbounded();
+#if 0 /// UNNEEDED by elogind
+        test_parse_nice();
+#endif // 0
+
+        return 0;
+}
diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c
new file mode 100644 (file)
index 0000000..4d957cd
--- /dev/null
@@ -0,0 +1,578 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2013 Zbigniew Jędrzejewski-Szmek
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdio.h>
+#include <sys/mount.h>
+#include <unistd.h>
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "macro.h"
+#include "mount-util.h"
+#include "path-util.h"
+#include "rm-rf.h"
+#include "string-util.h"
+#include "strv.h"
+#include "util.h"
+
+#define test_path_compare(a, b, result) {                 \
+                assert_se(path_compare(a, b) == result);  \
+                assert_se(path_compare(b, a) == -result); \
+                assert_se(path_equal(a, b) == !result);   \
+                assert_se(path_equal(b, a) == !result);   \
+        }
+
+static void test_path(void) {
+        _cleanup_close_ int fd = -1;
+
+        test_path_compare("/goo", "/goo", 0);
+        test_path_compare("/goo", "/goo", 0);
+        test_path_compare("//goo", "/goo", 0);
+        test_path_compare("//goo/////", "/goo", 0);
+        test_path_compare("goo/////", "goo", 0);
+
+        test_path_compare("/goo/boo", "/goo//boo", 0);
+        test_path_compare("//goo/boo", "/goo/boo//", 0);
+
+        test_path_compare("/", "///", 0);
+
+        test_path_compare("/x", "x/", 1);
+        test_path_compare("x/", "/", -1);
+
+        test_path_compare("/x/./y", "x/y", 1);
+        test_path_compare("x/.y", "x/y", -1);
+
+        test_path_compare("foo", "/foo", -1);
+        test_path_compare("/foo", "/foo/bar", -1);
+        test_path_compare("/foo/aaa", "/foo/b", -1);
+        test_path_compare("/foo/aaa", "/foo/b/a", -1);
+        test_path_compare("/foo/a", "/foo/aaa", -1);
+        test_path_compare("/foo/a/b", "/foo/aaa", -1);
+
+        assert_se(path_is_absolute("/"));
+        assert_se(!path_is_absolute("./"));
+
+        assert_se(is_path("/dir"));
+        assert_se(is_path("a/b"));
+        assert_se(!is_path("."));
+
+        assert_se(streq(basename("./aa/bb/../file.da."), "file.da."));
+        assert_se(streq(basename("/aa///.file"), ".file"));
+        assert_se(streq(basename("/aa///file..."), "file..."));
+        assert_se(streq(basename("file.../"), ""));
+
+        fd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY);
+        assert_se(fd >= 0);
+        assert_se(fd_is_mount_point(fd, "/", 0) > 0);
+
+        {
+                char p1[] = "aaa/bbb////ccc";
+                char p2[] = "//aaa/.////ccc";
+                char p3[] = "/./";
+
+                assert_se(path_equal(path_kill_slashes(p1), "aaa/bbb/ccc"));
+                assert_se(path_equal(path_kill_slashes(p2), "/aaa/./ccc"));
+                assert_se(path_equal(path_kill_slashes(p3), "/./"));
+        }
+
+        assert_se(PATH_IN_SET("/bin", "/", "/bin", "/foo"));
+        assert_se(PATH_IN_SET("/bin", "/bin"));
+        assert_se(PATH_IN_SET("/bin", "/foo/bar", "/bin"));
+        assert_se(PATH_IN_SET("/", "/", "/", "/foo/bar"));
+        assert_se(!PATH_IN_SET("/", "/abc", "/def"));
+
+        assert_se(path_equal_ptr(NULL, NULL));
+        assert_se(path_equal_ptr("/a", "/a"));
+        assert_se(!path_equal_ptr("/a", "/b"));
+        assert_se(!path_equal_ptr("/a", NULL));
+        assert_se(!path_equal_ptr(NULL, "/a"));
+}
+
+static void test_find_binary(const char *self) {
+        char *p;
+
+        assert_se(find_binary("/bin/sh", &p) == 0);
+        puts(p);
+        assert_se(path_equal(p, "/bin/sh"));
+        free(p);
+
+        assert_se(find_binary(self, &p) == 0);
+        puts(p);
+        /* libtool might prefix the binary name with "lt-" */
+        assert_se(endswith(p, "/lt-test-path-util") || endswith(p, "/test-path-util"));
+        assert_se(path_is_absolute(p));
+        free(p);
+
+        assert_se(find_binary("sh", &p) == 0);
+        puts(p);
+        assert_se(endswith(p, "/sh"));
+        assert_se(path_is_absolute(p));
+        free(p);
+
+        assert_se(find_binary("xxxx-xxxx", &p) == -ENOENT);
+        assert_se(find_binary("/some/dir/xxxx-xxxx", &p) == -ENOENT);
+}
+
+static void test_prefixes(void) {
+        static const char* values[] = { "/a/b/c/d", "/a/b/c", "/a/b", "/a", "", NULL};
+        unsigned i;
+        char s[PATH_MAX];
+        bool b;
+
+        i = 0;
+        PATH_FOREACH_PREFIX_MORE(s, "/a/b/c/d") {
+                log_error("---%s---", s);
+                assert_se(streq(s, values[i++]));
+        }
+        assert_se(values[i] == NULL);
+
+        i = 1;
+        PATH_FOREACH_PREFIX(s, "/a/b/c/d") {
+                log_error("---%s---", s);
+                assert_se(streq(s, values[i++]));
+        }
+        assert_se(values[i] == NULL);
+
+        i = 0;
+        PATH_FOREACH_PREFIX_MORE(s, "////a////b////c///d///////")
+                assert_se(streq(s, values[i++]));
+        assert_se(values[i] == NULL);
+
+        i = 1;
+        PATH_FOREACH_PREFIX(s, "////a////b////c///d///////")
+                assert_se(streq(s, values[i++]));
+        assert_se(values[i] == NULL);
+
+        PATH_FOREACH_PREFIX(s, "////")
+                assert_not_reached("Wut?");
+
+        b = false;
+        PATH_FOREACH_PREFIX_MORE(s, "////") {
+                assert_se(!b);
+                assert_se(streq(s, ""));
+                b = true;
+        }
+        assert_se(b);
+
+        PATH_FOREACH_PREFIX(s, "")
+                assert_not_reached("wut?");
+
+        b = false;
+        PATH_FOREACH_PREFIX_MORE(s, "") {
+                assert_se(!b);
+                assert_se(streq(s, ""));
+                b = true;
+        }
+}
+
+static void test_path_join(void) {
+
+#define test_join(root, path, rest, expected) {  \
+                _cleanup_free_ char *z = NULL;   \
+                z = path_join(root, path, rest); \
+                assert_se(streq(z, expected));   \
+        }
+
+        test_join("/root", "/a/b", "/c", "/root/a/b/c");
+        test_join("/root", "a/b", "c", "/root/a/b/c");
+        test_join("/root", "/a/b", "c", "/root/a/b/c");
+        test_join("/root", "/", "c", "/root/c");
+        test_join("/root", "/", NULL, "/root/");
+
+        test_join(NULL, "/a/b", "/c", "/a/b/c");
+        test_join(NULL, "a/b", "c", "a/b/c");
+        test_join(NULL, "/a/b", "c", "/a/b/c");
+        test_join(NULL, "/", "c", "/c");
+        test_join(NULL, "/", NULL, "/");
+}
+
+#if 0 /// UNNEEDED by elogind
+static void test_fsck_exists(void) {
+        /* Ensure we use a sane default for PATH. */
+        unsetenv("PATH");
+
+        /* fsck.minix is provided by util-linux and will probably exist. */
+        assert_se(fsck_exists("minix") == 1);
+
+        assert_se(fsck_exists("AbCdE") == 0);
+        assert_se(fsck_exists("/../bin/") == 0);
+}
+
+static void test_make_relative(void) {
+        char *result;
+
+        assert_se(path_make_relative("some/relative/path", "/some/path", &result) < 0);
+        assert_se(path_make_relative("/some/path", "some/relative/path", &result) < 0);
+
+#define test(from_dir, to_path, expected) {                \
+                _cleanup_free_ char *z = NULL;             \
+                path_make_relative(from_dir, to_path, &z); \
+                assert_se(streq(z, expected));             \
+        }
+
+        test("/", "/", ".");
+        test("/", "/some/path", "some/path");
+        test("/some/path", "/some/path", ".");
+        test("/some/path", "/some/path/in/subdir", "in/subdir");
+        test("/some/path", "/", "../..");
+        test("/some/path", "/some/other/path", "../other/path");
+        test("//extra/////slashes///won't////fool///anybody//", "////extra///slashes////are/just///fine///", "../../../are/just/fine");
+}
+#endif // 0
+
+static void test_strv_resolve(void) {
+        char tmp_dir[] = "/tmp/test-path-util-XXXXXX";
+        _cleanup_strv_free_ char **search_dirs = NULL;
+        _cleanup_strv_free_ char **absolute_dirs = NULL;
+        char **d;
+
+        assert_se(mkdtemp(tmp_dir) != NULL);
+
+        search_dirs = strv_new("/dir1", "/dir2", "/dir3", NULL);
+        assert_se(search_dirs);
+        STRV_FOREACH(d, search_dirs) {
+                char *p = strappend(tmp_dir, *d);
+                assert_se(p);
+                assert_se(strv_push(&absolute_dirs, p) == 0);
+        }
+
+        assert_se(mkdir(absolute_dirs[0], 0700) == 0);
+        assert_se(mkdir(absolute_dirs[1], 0700) == 0);
+        assert_se(symlink("dir2", absolute_dirs[2]) == 0);
+
+        path_strv_resolve(search_dirs, tmp_dir);
+        assert_se(streq(search_dirs[0], "/dir1"));
+        assert_se(streq(search_dirs[1], "/dir2"));
+        assert_se(streq(search_dirs[2], "/dir2"));
+
+        assert_se(rm_rf(tmp_dir, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
+}
+
+static void test_path_startswith(void) {
+        const char *p;
+
+        p = path_startswith("/foo/bar/barfoo/", "/foo");
+        assert_se(streq_ptr(p, "bar/barfoo/"));
+
+        p = path_startswith("/foo/bar/barfoo/", "/foo/");
+        assert_se(streq_ptr(p, "bar/barfoo/"));
+
+        p = path_startswith("/foo/bar/barfoo/", "/");
+        assert_se(streq_ptr(p, "foo/bar/barfoo/"));
+
+        p = path_startswith("/foo/bar/barfoo/", "////");
+        assert_se(streq_ptr(p, "foo/bar/barfoo/"));
+
+        p = path_startswith("/foo/bar/barfoo/", "/foo//bar/////barfoo///");
+        assert_se(streq_ptr(p, ""));
+
+        p = path_startswith("/foo/bar/barfoo/", "/foo/bar/barfoo////");
+        assert_se(streq_ptr(p, ""));
+
+        p = path_startswith("/foo/bar/barfoo/", "/foo/bar///barfoo/");
+        assert_se(streq_ptr(p, ""));
+
+        p = path_startswith("/foo/bar/barfoo/", "/foo////bar/barfoo/");
+        assert_se(streq_ptr(p, ""));
+
+        p = path_startswith("/foo/bar/barfoo/", "////foo/bar/barfoo/");
+        assert_se(streq_ptr(p, ""));
+
+        p = path_startswith("/foo/bar/barfoo/", "/foo/bar/barfoo");
+        assert_se(streq_ptr(p, ""));
+
+        assert_se(!path_startswith("/foo/bar/barfoo/", "/foo/bar/barfooa/"));
+        assert_se(!path_startswith("/foo/bar/barfoo/", "/foo/bar/barfooa"));
+        assert_se(!path_startswith("/foo/bar/barfoo/", ""));
+        assert_se(!path_startswith("/foo/bar/barfoo/", "/bar/foo"));
+        assert_se(!path_startswith("/foo/bar/barfoo/", "/f/b/b/"));
+}
+
+static void test_prefix_root_one(const char *r, const char *p, const char *expected) {
+        _cleanup_free_ char *s = NULL;
+        const char *t;
+
+        assert_se(s = prefix_root(r, p));
+        assert_se(streq_ptr(s, expected));
+
+        t = prefix_roota(r, p);
+        assert_se(t);
+        assert_se(streq_ptr(t, expected));
+}
+
+static void test_prefix_root(void) {
+        test_prefix_root_one("/", "/foo", "/foo");
+        test_prefix_root_one(NULL, "/foo", "/foo");
+        test_prefix_root_one("", "/foo", "/foo");
+        test_prefix_root_one("///", "/foo", "/foo");
+        test_prefix_root_one("/", "////foo", "/foo");
+        test_prefix_root_one(NULL, "////foo", "/foo");
+
+        test_prefix_root_one("/foo", "/bar", "/foo/bar");
+        test_prefix_root_one("/foo", "bar", "/foo/bar");
+        test_prefix_root_one("foo", "bar", "foo/bar");
+        test_prefix_root_one("/foo/", "/bar", "/foo/bar");
+        test_prefix_root_one("/foo/", "//bar", "/foo/bar");
+        test_prefix_root_one("/foo///", "//bar", "/foo/bar");
+}
+
+static void test_path_is_mount_point(void) {
+        int fd;
+        char tmp_dir[] = "/tmp/test-path-is-mount-point-XXXXXX";
+        _cleanup_free_ char *file1 = NULL, *file2 = NULL, *link1 = NULL, *link2 = NULL;
+        _cleanup_free_ char *dir1 = NULL, *dir1file = NULL, *dirlink1 = NULL, *dirlink1file = NULL;
+        _cleanup_free_ char *dir2 = NULL, *dir2file = NULL;
+
+        assert_se(path_is_mount_point("/", NULL, AT_SYMLINK_FOLLOW) > 0);
+        assert_se(path_is_mount_point("/", NULL, 0) > 0);
+
+        assert_se(path_is_mount_point("/proc", NULL, AT_SYMLINK_FOLLOW) > 0);
+        assert_se(path_is_mount_point("/proc", NULL, 0) > 0);
+
+        assert_se(path_is_mount_point("/proc/1", NULL, AT_SYMLINK_FOLLOW) == 0);
+        assert_se(path_is_mount_point("/proc/1", NULL, 0) == 0);
+
+        assert_se(path_is_mount_point("/sys", NULL, AT_SYMLINK_FOLLOW) > 0);
+        assert_se(path_is_mount_point("/sys", NULL, 0) > 0);
+
+        /* we'll create a hierarchy of different kinds of dir/file/link
+         * layouts:
+         *
+         * <tmp>/file1, <tmp>/file2
+         * <tmp>/link1 -> file1, <tmp>/link2 -> file2
+         * <tmp>/dir1/
+         * <tmp>/dir1/file
+         * <tmp>/dirlink1 -> dir1
+         * <tmp>/dirlink1file -> dirlink1/file
+         * <tmp>/dir2/
+         * <tmp>/dir2/file
+         */
+
+        /* file mountpoints */
+        assert_se(mkdtemp(tmp_dir) != NULL);
+        file1 = path_join(NULL, tmp_dir, "file1");
+        assert_se(file1);
+        file2 = path_join(NULL, tmp_dir, "file2");
+        assert_se(file2);
+        fd = open(file1, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
+        assert_se(fd > 0);
+        close(fd);
+        fd = open(file2, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
+        assert_se(fd > 0);
+        close(fd);
+        link1 = path_join(NULL, tmp_dir, "link1");
+        assert_se(link1);
+        assert_se(symlink("file1", link1) == 0);
+        link2 = path_join(NULL, tmp_dir, "link2");
+        assert_se(link1);
+        assert_se(symlink("file2", link2) == 0);
+
+        assert_se(path_is_mount_point(file1, NULL, AT_SYMLINK_FOLLOW) == 0);
+        assert_se(path_is_mount_point(file1, NULL, 0) == 0);
+        assert_se(path_is_mount_point(link1, NULL, AT_SYMLINK_FOLLOW) == 0);
+        assert_se(path_is_mount_point(link1, NULL, 0) == 0);
+
+        /* directory mountpoints */
+        dir1 = path_join(NULL, tmp_dir, "dir1");
+        assert_se(dir1);
+        assert_se(mkdir(dir1, 0755) == 0);
+        dirlink1 = path_join(NULL, tmp_dir, "dirlink1");
+        assert_se(dirlink1);
+        assert_se(symlink("dir1", dirlink1) == 0);
+        dirlink1file = path_join(NULL, tmp_dir, "dirlink1file");
+        assert_se(dirlink1file);
+        assert_se(symlink("dirlink1/file", dirlink1file) == 0);
+        dir2 = path_join(NULL, tmp_dir, "dir2");
+        assert_se(dir2);
+        assert_se(mkdir(dir2, 0755) == 0);
+
+        assert_se(path_is_mount_point(dir1, NULL, AT_SYMLINK_FOLLOW) == 0);
+        assert_se(path_is_mount_point(dir1, NULL, 0) == 0);
+        assert_se(path_is_mount_point(dirlink1, NULL, AT_SYMLINK_FOLLOW) == 0);
+        assert_se(path_is_mount_point(dirlink1, NULL, 0) == 0);
+
+        /* file in subdirectory mountpoints */
+        dir1file = path_join(NULL, dir1, "file");
+        assert_se(dir1file);
+        fd = open(dir1file, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
+        assert_se(fd > 0);
+        close(fd);
+
+        assert_se(path_is_mount_point(dir1file, NULL, AT_SYMLINK_FOLLOW) == 0);
+        assert_se(path_is_mount_point(dir1file, NULL, 0) == 0);
+        assert_se(path_is_mount_point(dirlink1file, NULL, AT_SYMLINK_FOLLOW) == 0);
+        assert_se(path_is_mount_point(dirlink1file, NULL, 0) == 0);
+
+        /* these tests will only work as root */
+        if (mount(file1, file2, NULL, MS_BIND, NULL) >= 0) {
+                int rt, rf, rlt, rlf, rl1t, rl1f;
+
+                /* files */
+                /* capture results in vars, to avoid dangling mounts on failure */
+                rf = path_is_mount_point(file2, NULL, 0);
+                rt = path_is_mount_point(file2, NULL, AT_SYMLINK_FOLLOW);
+                rlf = path_is_mount_point(link2, NULL, 0);
+                rlt = path_is_mount_point(link2, NULL, AT_SYMLINK_FOLLOW);
+
+                assert_se(umount(file2) == 0);
+
+                assert_se(rf == 1);
+                assert_se(rt == 1);
+                assert_se(rlf == 0);
+                assert_se(rlt == 1);
+
+                /* dirs */
+                dir2file = path_join(NULL, dir2, "file");
+                assert_se(dir2file);
+                fd = open(dir2file, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
+                assert_se(fd > 0);
+                close(fd);
+
+                assert_se(mount(dir2, dir1, NULL, MS_BIND, NULL) >= 0);
+
+                rf = path_is_mount_point(dir1, NULL, 0);
+                rt = path_is_mount_point(dir1, NULL, AT_SYMLINK_FOLLOW);
+                rlf = path_is_mount_point(dirlink1, NULL, 0);
+                rlt = path_is_mount_point(dirlink1, NULL, AT_SYMLINK_FOLLOW);
+                /* its parent is a mount point, but not /file itself */
+                rl1f = path_is_mount_point(dirlink1file, NULL, 0);
+                rl1t = path_is_mount_point(dirlink1file, NULL, AT_SYMLINK_FOLLOW);
+
+                assert_se(umount(dir1) == 0);
+
+                assert_se(rf == 1);
+                assert_se(rt == 1);
+                assert_se(rlf == 0);
+                assert_se(rlt == 1);
+                assert_se(rl1f == 0);
+                assert_se(rl1t == 0);
+
+        } else
+                printf("Skipping bind mount file test: %m\n");
+
+        assert_se(rm_rf(tmp_dir, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
+}
+
+static void test_file_in_same_dir(void) {
+        char *t;
+
+        t = file_in_same_dir("/", "a");
+        assert_se(streq(t, "/a"));
+        free(t);
+
+        t = file_in_same_dir("/", "/a");
+        assert_se(streq(t, "/a"));
+        free(t);
+
+        t = file_in_same_dir("", "a");
+        assert_se(streq(t, "a"));
+        free(t);
+
+        t = file_in_same_dir("a/", "a");
+        assert_se(streq(t, "a/a"));
+        free(t);
+
+        t = file_in_same_dir("bar/foo", "bar");
+        assert_se(streq(t, "bar/bar"));
+        free(t);
+}
+
+static void test_filename_is_valid(void) {
+        char foo[FILENAME_MAX+2];
+        int i;
+
+        assert_se(!filename_is_valid(""));
+        assert_se(!filename_is_valid("/bar/foo"));
+        assert_se(!filename_is_valid("/"));
+        assert_se(!filename_is_valid("."));
+        assert_se(!filename_is_valid(".."));
+
+        for (i=0; i<FILENAME_MAX+1; i++)
+                foo[i] = 'a';
+        foo[FILENAME_MAX+1] = '\0';
+
+        assert_se(!filename_is_valid(foo));
+
+        assert_se(filename_is_valid("foo_bar-333"));
+        assert_se(filename_is_valid("o.o"));
+}
+
+static void test_hidden_or_backup_file(void) {
+        assert_se(hidden_or_backup_file(".hidden"));
+        assert_se(hidden_or_backup_file("..hidden"));
+        assert_se(!hidden_or_backup_file("hidden."));
+
+        assert_se(hidden_or_backup_file("backup~"));
+        assert_se(hidden_or_backup_file(".backup~"));
+
+        assert_se(hidden_or_backup_file("lost+found"));
+        assert_se(hidden_or_backup_file("aquota.user"));
+        assert_se(hidden_or_backup_file("aquota.group"));
+
+        assert_se(hidden_or_backup_file("test.rpmnew"));
+        assert_se(hidden_or_backup_file("test.dpkg-old"));
+        assert_se(hidden_or_backup_file("test.dpkg-remove"));
+        assert_se(hidden_or_backup_file("test.swp"));
+
+        assert_se(!hidden_or_backup_file("test.rpmnew."));
+        assert_se(!hidden_or_backup_file("test.dpkg-old.foo"));
+}
+
+#if 0 /// UNNEEDED by elogind
+static void test_systemd_installation_has_version(const char *path) {
+        int r;
+        const unsigned versions[] = {0, 231, atoi(PACKAGE_VERSION), 999};
+        unsigned i;
+
+        for (i = 0; i < ELEMENTSOF(versions); i++) {
+                r = systemd_installation_has_version(path, versions[i]);
+                assert_se(r >= 0);
+                log_info("%s has systemd >= %u: %s",
+                         path ?: "Current installation", versions[i], yes_no(r));
+        }
+}
+#endif // 0
+
+int main(int argc, char **argv) {
+        log_set_max_level(LOG_DEBUG);
+        log_parse_environment();
+        log_open();
+
+        test_path();
+        test_find_binary(argv[0]);
+        test_prefixes();
+        test_path_join();
+#if 0 /// UNNEEDED by elogind
+        test_fsck_exists();
+        test_make_relative();
+#endif // 0
+        test_strv_resolve();
+        test_path_startswith();
+        test_prefix_root();
+        test_path_is_mount_point();
+        test_file_in_same_dir();
+        test_filename_is_valid();
+        test_hidden_or_backup_file();
+
+#if 0 /// UNNEEDED by elogind
+        test_systemd_installation_has_version(argv[1]); /* NULL is OK */
+#endif // 0
+
+        return 0;
+}
diff --git a/src/test/test-prioq.c b/src/test/test-prioq.c
new file mode 100644 (file)
index 0000000..d81880a
--- /dev/null
@@ -0,0 +1,171 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2013 Lennart Poettering
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdlib.h>
+
+#include "alloc-util.h"
+#include "prioq.h"
+#include "set.h"
+#include "siphash24.h"
+#include "util.h"
+
+#define SET_SIZE 1024*4
+
+static int unsigned_compare(const void *a, const void *b) {
+        const unsigned *x = a, *y = b;
+
+        if (*x < *y)
+                return -1;
+
+        if (*x > *y)
+                return 1;
+
+        return 0;
+}
+
+static void test_unsigned(void) {
+        unsigned buffer[SET_SIZE], i;
+        Prioq *q;
+
+        srand(0);
+
+        q = prioq_new(trivial_compare_func);
+        assert_se(q);
+
+        for (i = 0; i < ELEMENTSOF(buffer); i++) {
+                unsigned u;
+
+                u = (unsigned) rand();
+                buffer[i] = u;
+                assert_se(prioq_put(q, UINT_TO_PTR(u), NULL) >= 0);
+        }
+
+        qsort(buffer, ELEMENTSOF(buffer), sizeof(buffer[0]), unsigned_compare);
+
+        for (i = 0; i < ELEMENTSOF(buffer); i++) {
+                unsigned u;
+
+                assert_se(prioq_size(q) == ELEMENTSOF(buffer) - i);
+
+                u = PTR_TO_UINT(prioq_pop(q));
+                assert_se(buffer[i] == u);
+        }
+
+        assert_se(prioq_isempty(q));
+        prioq_free(q);
+}
+
+struct test {
+        unsigned value;
+        unsigned idx;
+};
+
+static int test_compare(const void *a, const void *b) {
+        const struct test *x = a, *y = b;
+
+        if (x->value < y->value)
+                return -1;
+
+        if (x->value > y->value)
+                return 1;
+
+        return 0;
+}
+
+static void test_hash(const void *a, struct siphash *state) {
+        const struct test *x = a;
+
+        siphash24_compress(&x->value, sizeof(x->value), state);
+}
+
+static const struct hash_ops test_hash_ops = {
+        .hash = test_hash,
+        .compare = test_compare
+};
+
+static void test_struct(void) {
+        Prioq *q;
+        Set *s;
+        unsigned previous = 0, i;
+        int r;
+
+        srand(0);
+
+        q = prioq_new(test_compare);
+        assert_se(q);
+
+        s = set_new(&test_hash_ops);
+        assert_se(s);
+
+        for (i = 0; i < SET_SIZE; i++) {
+                struct test *t;
+
+                t = new0(struct test, 1);
+                assert_se(t);
+                t->value = (unsigned) rand();
+
+                r = prioq_put(q, t, &t->idx);
+                assert_se(r >= 0);
+
+                if (i % 4 == 0) {
+                        r = set_consume(s, t);
+                        assert_se(r >= 0);
+                }
+        }
+
+        for (;;) {
+                struct test *t;
+
+                t = set_steal_first(s);
+                if (!t)
+                        break;
+
+                r = prioq_remove(q, t, &t->idx);
+                assert_se(r > 0);
+
+                free(t);
+        }
+
+        for (i = 0; i < SET_SIZE * 3 / 4; i++) {
+                struct test *t;
+
+                assert_se(prioq_size(q) == (SET_SIZE * 3 / 4) - i);
+
+                t = prioq_pop(q);
+                assert_se(t);
+
+                assert_se(previous <= t->value);
+                previous = t->value;
+                free(t);
+        }
+
+        assert_se(prioq_isempty(q));
+        prioq_free(q);
+
+        assert_se(set_isempty(s));
+        set_free(s);
+}
+
+int main(int argc, char* argv[]) {
+
+        test_unsigned();
+        test_struct();
+
+        return 0;
+}
diff --git a/src/test/test-proc-cmdline.c b/src/test/test-proc-cmdline.c
new file mode 100644 (file)
index 0000000..59dcf40
--- /dev/null
@@ -0,0 +1,162 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "alloc-util.h"
+#include "log.h"
+#include "macro.h"
+#include "proc-cmdline.h"
+//#include "special.h"
+#include "string-util.h"
+#include "util.h"
+
+static int obj;
+
+static int parse_item(const char *key, const char *value, void *data) {
+        assert_se(key);
+        assert_se(data == &obj);
+
+        log_info("kernel cmdline option <%s> = <%s>", key, strna(value));
+        return 0;
+}
+
+static void test_proc_cmdline_parse(void) {
+        assert_se(proc_cmdline_parse(parse_item, &obj, true) >= 0);
+}
+
+#if 0 /// UNNEEDED by elogind
+static void test_runlevel_to_target(void) {
+        in_initrd_force(false);
+        assert_se(streq_ptr(runlevel_to_target(NULL), NULL));
+        assert_se(streq_ptr(runlevel_to_target("unknown-runlevel"), NULL));
+        assert_se(streq_ptr(runlevel_to_target("rd.unknown-runlevel"), NULL));
+        assert_se(streq_ptr(runlevel_to_target("3"), SPECIAL_MULTI_USER_TARGET));
+        assert_se(streq_ptr(runlevel_to_target("rd.rescue"), NULL));
+
+        in_initrd_force(true);
+        assert_se(streq_ptr(runlevel_to_target(NULL), NULL));
+        assert_se(streq_ptr(runlevel_to_target("unknown-runlevel"), NULL));
+        assert_se(streq_ptr(runlevel_to_target("rd.unknown-runlevel"), NULL));
+        assert_se(streq_ptr(runlevel_to_target("3"), NULL));
+        assert_se(streq_ptr(runlevel_to_target("rd.rescue"), SPECIAL_RESCUE_TARGET));
+}
+
+static void test_proc_cmdline_get_key(void) {
+        _cleanup_free_ char *value = NULL;
+
+        putenv((char*) "SYSTEMD_PROC_CMDLINE=foo_bar=quux wuff-piep=tuet zumm");
+
+        assert_se(proc_cmdline_get_key("", 0, &value) == -EINVAL);
+        assert_se(proc_cmdline_get_key("abc", 0, NULL) == 0);
+        assert_se(proc_cmdline_get_key("abc", 0, &value) == 0 && value == NULL);
+        assert_se(proc_cmdline_get_key("abc", PROC_CMDLINE_VALUE_OPTIONAL, &value) == 0 && value == NULL);
+
+        assert_se(proc_cmdline_get_key("foo_bar", 0, &value) > 0 && streq_ptr(value, "quux"));
+        value = mfree(value);
+        assert_se(proc_cmdline_get_key("foo_bar", PROC_CMDLINE_VALUE_OPTIONAL, &value) > 0 && streq_ptr(value, "quux"));
+        value = mfree(value);
+        assert_se(proc_cmdline_get_key("foo-bar", 0, &value) > 0 && streq_ptr(value, "quux"));
+        value = mfree(value);
+        assert_se(proc_cmdline_get_key("foo-bar", PROC_CMDLINE_VALUE_OPTIONAL, &value) > 0 && streq_ptr(value, "quux"));
+        value = mfree(value);
+        assert_se(proc_cmdline_get_key("foo-bar", 0, NULL) == 0);
+        assert_se(proc_cmdline_get_key("foo-bar", PROC_CMDLINE_VALUE_OPTIONAL, NULL) == -EINVAL);
+
+        assert_se(proc_cmdline_get_key("wuff-piep", 0, &value) > 0 && streq_ptr(value, "tuet"));
+        value = mfree(value);
+        assert_se(proc_cmdline_get_key("wuff-piep", PROC_CMDLINE_VALUE_OPTIONAL, &value) > 0 && streq_ptr(value, "tuet"));
+        value = mfree(value);
+        assert_se(proc_cmdline_get_key("wuff_piep", 0, &value) > 0 && streq_ptr(value, "tuet"));
+        value = mfree(value);
+        assert_se(proc_cmdline_get_key("wuff_piep", PROC_CMDLINE_VALUE_OPTIONAL, &value) > 0 && streq_ptr(value, "tuet"));
+        value = mfree(value);
+        assert_se(proc_cmdline_get_key("wuff_piep", 0, NULL) == 0);
+        assert_se(proc_cmdline_get_key("wuff_piep", PROC_CMDLINE_VALUE_OPTIONAL, NULL) == -EINVAL);
+
+        assert_se(proc_cmdline_get_key("zumm", 0, &value) == 0 && value == NULL);
+        assert_se(proc_cmdline_get_key("zumm", PROC_CMDLINE_VALUE_OPTIONAL, &value) > 0 && value == NULL);
+        assert_se(proc_cmdline_get_key("zumm", 0, NULL) > 0);
+}
+
+static void test_proc_cmdline_get_bool(void) {
+        bool value = false;
+
+        putenv((char*) "SYSTEMD_PROC_CMDLINE=foo_bar bar-waldo=1 x_y-z=0 quux=miep");
+
+        assert_se(proc_cmdline_get_bool("", &value) == -EINVAL);
+        assert_se(proc_cmdline_get_bool("abc", &value) == 0 && value == false);
+        assert_se(proc_cmdline_get_bool("foo_bar", &value) > 0 && value == true);
+        assert_se(proc_cmdline_get_bool("foo-bar", &value) > 0 && value == true);
+        assert_se(proc_cmdline_get_bool("bar-waldo", &value) > 0 && value == true);
+        assert_se(proc_cmdline_get_bool("bar_waldo", &value) > 0 && value == true);
+        assert_se(proc_cmdline_get_bool("x_y-z", &value) > 0 && value == false);
+        assert_se(proc_cmdline_get_bool("x-y-z", &value) > 0 && value == false);
+        assert_se(proc_cmdline_get_bool("x-y_z", &value) > 0 && value == false);
+        assert_se(proc_cmdline_get_bool("x_y_z", &value) > 0 && value == false);
+        assert_se(proc_cmdline_get_bool("quux", &value) == -EINVAL && value == false);
+}
+#endif // 0
+
+static void test_proc_cmdline_key_streq(void) {
+
+        assert_se(proc_cmdline_key_streq("", ""));
+        assert_se(proc_cmdline_key_streq("a", "a"));
+        assert_se(!proc_cmdline_key_streq("", "a"));
+        assert_se(!proc_cmdline_key_streq("a", ""));
+        assert_se(proc_cmdline_key_streq("a", "a"));
+        assert_se(!proc_cmdline_key_streq("a", "b"));
+        assert_se(proc_cmdline_key_streq("x-y-z", "x-y-z"));
+        assert_se(proc_cmdline_key_streq("x-y-z", "x_y_z"));
+        assert_se(proc_cmdline_key_streq("x-y-z", "x-y_z"));
+        assert_se(proc_cmdline_key_streq("x-y-z", "x_y-z"));
+        assert_se(proc_cmdline_key_streq("x_y-z", "x-y_z"));
+        assert_se(!proc_cmdline_key_streq("x_y-z", "x-z_z"));
+}
+
+#if 0 /// UNNEEDED by elogind
+static void test_proc_cmdline_key_startswith(void) {
+
+        assert_se(proc_cmdline_key_startswith("", ""));
+        assert_se(proc_cmdline_key_startswith("x", ""));
+        assert_se(!proc_cmdline_key_startswith("", "x"));
+        assert_se(proc_cmdline_key_startswith("x", "x"));
+        assert_se(!proc_cmdline_key_startswith("x", "y"));
+        assert_se(!proc_cmdline_key_startswith("foo-bar", "quux"));
+        assert_se(proc_cmdline_key_startswith("foo-bar", "foo"));
+        assert_se(proc_cmdline_key_startswith("foo-bar", "foo-bar"));
+        assert_se(proc_cmdline_key_startswith("foo-bar", "foo_bar"));
+        assert_se(proc_cmdline_key_startswith("foo-bar", "foo_"));
+        assert_se(!proc_cmdline_key_startswith("foo-bar", "foo_xx"));
+}
+#endif // 0
+
+int main(void) {
+        log_parse_environment();
+        log_open();
+
+        test_proc_cmdline_parse();
+        test_proc_cmdline_key_streq();
+#if 0 /// UNNEEDED by elogind
+        test_proc_cmdline_key_startswith();
+        test_proc_cmdline_get_key();
+        test_proc_cmdline_get_bool();
+        test_runlevel_to_target();
+#endif // 0
+
+        return 0;
+}
diff --git a/src/test/test-process-util.c b/src/test/test-process-util.c
new file mode 100644 (file)
index 0000000..07c0704
--- /dev/null
@@ -0,0 +1,453 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+  Copyright 2013 Thomas H.P. Andersen
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sched.h>
+#include <sys/mount.h>
+#include <sys/personality.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#ifdef HAVE_VALGRIND_VALGRIND_H
+#include <valgrind/valgrind.h>
+#endif
+
+#include "alloc-util.h"
+//#include "architecture.h"
+#include "fd-util.h"
+#include "log.h"
+#include "macro.h"
+#include "parse-util.h"
+#include "process-util.h"
+#include "stdio-util.h"
+#include "string-util.h"
+#include "terminal-util.h"
+#include "test-helper.h"
+#include "util.h"
+#include "virt.h"
+
+static void test_get_process_comm(pid_t pid) {
+        struct stat st;
+        _cleanup_free_ char *a = NULL, *c = NULL, *d = NULL, *f = NULL, *i = NULL;
+        _cleanup_free_ char *env = NULL;
+        char path[strlen("/proc//comm") + DECIMAL_STR_MAX(pid_t)];
+#if 0 /// UNNEEDED by elogind
+        pid_t e;
+        uid_t u;
+        gid_t g;
+#endif // 0
+        dev_t h;
+        int r;
+
+        xsprintf(path, "/proc/"PID_FMT"/comm", pid);
+
+        if (stat(path, &st) == 0) {
+                assert_se(get_process_comm(pid, &a) >= 0);
+                log_info("PID"PID_FMT" comm: '%s'", pid, a);
+        } else
+                log_warning("%s not exist.", path);
+
+        assert_se(get_process_cmdline(pid, 0, true, &c) >= 0);
+        log_info("PID"PID_FMT" cmdline: '%s'", pid, c);
+
+        assert_se(get_process_cmdline(pid, 8, false, &d) >= 0);
+        log_info("PID"PID_FMT" cmdline truncated to 8: '%s'", pid, d);
+
+        free(d);
+        assert_se(get_process_cmdline(pid, 1, false, &d) >= 0);
+        log_info("PID"PID_FMT" cmdline truncated to 1: '%s'", pid, d);
+
+#if 0 /// UNNEEDED by elogind
+        assert_se(get_process_ppid(pid, &e) >= 0);
+        log_info("PID"PID_FMT" PPID: "PID_FMT, pid, e);
+        assert_se(pid == 1 ? e == 0 : e > 0);
+#endif // 0
+
+        assert_se(is_kernel_thread(pid) == 0 || pid != 1);
+
+        r = get_process_exe(pid, &f);
+        assert_se(r >= 0 || r == -EACCES);
+        log_info("PID"PID_FMT" exe: '%s'", pid, strna(f));
+
+#if 0 /// UNNEEDED by elogind
+        assert_se(get_process_uid(pid, &u) == 0);
+        log_info("PID"PID_FMT" UID: "UID_FMT, pid, u);
+        assert_se(u == 0 || pid != 1);
+
+        assert_se(get_process_gid(pid, &g) == 0);
+        log_info("PID"PID_FMT" GID: "GID_FMT, pid, g);
+        assert_se(g == 0 || pid != 1);
+
+        r = get_process_environ(pid, &env);
+        assert_se(r >= 0 || r == -EACCES);
+        log_info("PID"PID_FMT" strlen(environ): %zi", pid, env ? (ssize_t)strlen(env) : (ssize_t)-errno);
+#endif // 0
+
+        if (!detect_container())
+                assert_se(get_ctty_devnr(pid, &h) == -ENXIO || pid != 1);
+
+        getenv_for_pid(pid, "PATH", &i);
+        log_info("PID"PID_FMT" $PATH: '%s'", pid, strna(i));
+}
+
+static void test_pid_is_unwaited(void) {
+        pid_t pid;
+
+        pid = fork();
+        assert_se(pid >= 0);
+        if (pid == 0) {
+                _exit(EXIT_SUCCESS);
+        } else {
+                int status;
+
+                waitpid(pid, &status, 0);
+                assert_se(!pid_is_unwaited(pid));
+        }
+        assert_se(pid_is_unwaited(getpid()));
+        assert_se(!pid_is_unwaited(-1));
+}
+
+static void test_pid_is_alive(void) {
+        pid_t pid;
+
+        pid = fork();
+        assert_se(pid >= 0);
+        if (pid == 0) {
+                _exit(EXIT_SUCCESS);
+        } else {
+                int status;
+
+                waitpid(pid, &status, 0);
+                assert_se(!pid_is_alive(pid));
+        }
+        assert_se(pid_is_alive(getpid()));
+        assert_se(!pid_is_alive(-1));
+}
+
+#if 0 /// UNNEEDED by elogind
+static void test_personality(void) {
+
+        assert_se(personality_to_string(PER_LINUX));
+        assert_se(!personality_to_string(PERSONALITY_INVALID));
+
+        assert_se(streq(personality_to_string(PER_LINUX), architecture_to_string(native_architecture())));
+
+        assert_se(personality_from_string(personality_to_string(PER_LINUX)) == PER_LINUX);
+        assert_se(personality_from_string(architecture_to_string(native_architecture())) == PER_LINUX);
+
+#ifdef __x86_64__
+        assert_se(streq_ptr(personality_to_string(PER_LINUX), "x86-64"));
+        assert_se(streq_ptr(personality_to_string(PER_LINUX32), "x86"));
+
+        assert_se(personality_from_string("x86-64") == PER_LINUX);
+        assert_se(personality_from_string("x86") == PER_LINUX32);
+        assert_se(personality_from_string("ia64") == PERSONALITY_INVALID);
+        assert_se(personality_from_string(NULL) == PERSONALITY_INVALID);
+
+        assert_se(personality_from_string(personality_to_string(PER_LINUX32)) == PER_LINUX32);
+#endif
+}
+#endif // 0
+
+static void test_get_process_cmdline_harder(void) {
+        char path[] = "/tmp/test-cmdlineXXXXXX";
+        _cleanup_close_ int fd = -1;
+        _cleanup_free_ char *line = NULL;
+        pid_t pid;
+
+        if (geteuid() != 0)
+                return;
+
+#ifdef HAVE_VALGRIND_VALGRIND_H
+        /* valgrind patches open(/proc//cmdline)
+         * so, test_get_process_cmdline_harder fails always
+         * See https://github.com/systemd/systemd/pull/3555#issuecomment-226564908 */
+        if (RUNNING_ON_VALGRIND)
+                return;
+#endif
+
+        pid = fork();
+        if (pid > 0) {
+                siginfo_t si;
+
+                (void) wait_for_terminate(pid, &si);
+
+                assert_se(si.si_code == CLD_EXITED);
+                assert_se(si.si_status == 0);
+
+                return;
+        }
+
+        assert_se(pid == 0);
+        assert_se(unshare(CLONE_NEWNS) >= 0);
+
+        fd = mkostemp(path, O_CLOEXEC);
+        assert_se(fd >= 0);
+
+        if (mount(path, "/proc/self/cmdline", "bind", MS_BIND, NULL) < 0) {
+                /* This happens under selinux… Abort the test in this case. */
+                log_warning_errno(errno, "mount(..., \"/proc/self/cmdline\", \"bind\", ...) failed: %m");
+                assert(errno == EACCES);
+                return;
+        }
+
+        assert_se(unlink(path) >= 0);
+
+        assert_se(prctl(PR_SET_NAME, "testa") >= 0);
+
+        assert_se(get_process_cmdline(getpid(), 0, false, &line) == -ENOENT);
+
+        assert_se(get_process_cmdline(getpid(), 0, true, &line) >= 0);
+        assert_se(streq(line, "[testa]"));
+        line = mfree(line);
+
+        assert_se(get_process_cmdline(getpid(), 1, true, &line) >= 0);
+        assert_se(streq(line, ""));
+        line = mfree(line);
+
+        assert_se(get_process_cmdline(getpid(), 2, true, &line) >= 0);
+        assert_se(streq(line, "["));
+        line = mfree(line);
+
+        assert_se(get_process_cmdline(getpid(), 3, true, &line) >= 0);
+        assert_se(streq(line, "[."));
+        line = mfree(line);
+
+        assert_se(get_process_cmdline(getpid(), 4, true, &line) >= 0);
+        assert_se(streq(line, "[.."));
+        line = mfree(line);
+
+        assert_se(get_process_cmdline(getpid(), 5, true, &line) >= 0);
+        assert_se(streq(line, "[..."));
+        line = mfree(line);
+
+        assert_se(get_process_cmdline(getpid(), 6, true, &line) >= 0);
+        assert_se(streq(line, "[...]"));
+        line = mfree(line);
+
+        assert_se(get_process_cmdline(getpid(), 7, true, &line) >= 0);
+        assert_se(streq(line, "[t...]"));
+        line = mfree(line);
+
+        assert_se(get_process_cmdline(getpid(), 8, true, &line) >= 0);
+        assert_se(streq(line, "[testa]"));
+        line = mfree(line);
+
+        assert_se(write(fd, "\0\0\0\0\0\0\0\0\0", 10) == 10);
+
+        assert_se(get_process_cmdline(getpid(), 0, false, &line) == -ENOENT);
+
+        assert_se(get_process_cmdline(getpid(), 0, true, &line) >= 0);
+        assert_se(streq(line, "[testa]"));
+        line = mfree(line);
+
+        assert_se(write(fd, "foo\0bar\0\0\0\0\0", 10) == 10);
+
+        assert_se(get_process_cmdline(getpid(), 0, false, &line) >= 0);
+        assert_se(streq(line, "foo bar"));
+        line = mfree(line);
+
+        assert_se(get_process_cmdline(getpid(), 0, true, &line) >= 0);
+        assert_se(streq(line, "foo bar"));
+        line = mfree(line);
+
+        assert_se(write(fd, "quux", 4) == 4);
+        assert_se(get_process_cmdline(getpid(), 0, false, &line) >= 0);
+        assert_se(streq(line, "foo bar quux"));
+        line = mfree(line);
+
+        assert_se(get_process_cmdline(getpid(), 0, true, &line) >= 0);
+        assert_se(streq(line, "foo bar quux"));
+        line = mfree(line);
+
+        assert_se(get_process_cmdline(getpid(), 1, true, &line) >= 0);
+        assert_se(streq(line, ""));
+        line = mfree(line);
+
+        assert_se(get_process_cmdline(getpid(), 2, true, &line) >= 0);
+        assert_se(streq(line, "."));
+        line = mfree(line);
+
+        assert_se(get_process_cmdline(getpid(), 3, true, &line) >= 0);
+        assert_se(streq(line, ".."));
+        line = mfree(line);
+
+        assert_se(get_process_cmdline(getpid(), 4, true, &line) >= 0);
+        assert_se(streq(line, "..."));
+        line = mfree(line);
+
+        assert_se(get_process_cmdline(getpid(), 5, true, &line) >= 0);
+        assert_se(streq(line, "f..."));
+        line = mfree(line);
+
+        assert_se(get_process_cmdline(getpid(), 6, true, &line) >= 0);
+        assert_se(streq(line, "fo..."));
+        line = mfree(line);
+
+        assert_se(get_process_cmdline(getpid(), 7, true, &line) >= 0);
+        assert_se(streq(line, "foo..."));
+        line = mfree(line);
+
+        assert_se(get_process_cmdline(getpid(), 8, true, &line) >= 0);
+        assert_se(streq(line, "foo..."));
+        line = mfree(line);
+
+        assert_se(get_process_cmdline(getpid(), 9, true, &line) >= 0);
+        assert_se(streq(line, "foo b..."));
+        line = mfree(line);
+
+        assert_se(get_process_cmdline(getpid(), 10, true, &line) >= 0);
+        assert_se(streq(line, "foo ba..."));
+        line = mfree(line);
+
+        assert_se(get_process_cmdline(getpid(), 11, true, &line) >= 0);
+        assert_se(streq(line, "foo bar..."));
+        line = mfree(line);
+
+        assert_se(get_process_cmdline(getpid(), 12, true, &line) >= 0);
+        assert_se(streq(line, "foo bar..."));
+        line = mfree(line);
+
+        assert_se(get_process_cmdline(getpid(), 13, true, &line) >= 0);
+        assert_se(streq(line, "foo bar quux"));
+        line = mfree(line);
+
+        assert_se(get_process_cmdline(getpid(), 14, true, &line) >= 0);
+        assert_se(streq(line, "foo bar quux"));
+        line = mfree(line);
+
+        assert_se(get_process_cmdline(getpid(), 1000, true, &line) >= 0);
+        assert_se(streq(line, "foo bar quux"));
+        line = mfree(line);
+
+        assert_se(ftruncate(fd, 0) >= 0);
+        assert_se(prctl(PR_SET_NAME, "aaaa bbbb cccc") >= 0);
+
+        assert_se(get_process_cmdline(getpid(), 0, false, &line) == -ENOENT);
+
+        assert_se(get_process_cmdline(getpid(), 0, true, &line) >= 0);
+        assert_se(streq(line, "[aaaa bbbb cccc]"));
+        line = mfree(line);
+
+        assert_se(get_process_cmdline(getpid(), 10, true, &line) >= 0);
+        assert_se(streq(line, "[aaaa...]"));
+        line = mfree(line);
+
+        assert_se(get_process_cmdline(getpid(), 11, true, &line) >= 0);
+        assert_se(streq(line, "[aaaa...]"));
+        line = mfree(line);
+
+        assert_se(get_process_cmdline(getpid(), 12, true, &line) >= 0);
+        assert_se(streq(line, "[aaaa b...]"));
+        line = mfree(line);
+
+        safe_close(fd);
+        _exit(0);
+}
+
+#if 0 /// UNNEEDED by elogind
+static void test_rename_process_one(const char *p, int ret) {
+        _cleanup_free_ char *comm = NULL, *cmdline = NULL;
+        pid_t pid;
+        int r;
+
+        pid = fork();
+        assert_se(pid >= 0);
+
+        if (pid > 0) {
+                siginfo_t si;
+
+                assert_se(wait_for_terminate(pid, &si) >= 0);
+                assert_se(si.si_code == CLD_EXITED);
+                assert_se(si.si_status == EXIT_SUCCESS);
+
+                return;
+        }
+
+        /* child */
+        r = rename_process(p);
+
+        assert_se(r == ret ||
+                  (ret == 0 && r >= 0) ||
+                  (ret > 0 && r > 0));
+
+        if (r < 0)
+                goto finish;
+
+#ifdef HAVE_VALGRIND_VALGRIND_H
+        /* see above, valgrind is weird, we can't verify what we are doing here */
+        if (RUNNING_ON_VALGRIND)
+                goto finish;
+#endif
+
+        assert_se(get_process_comm(0, &comm) >= 0);
+        log_info("comm = <%s>", comm);
+        assert_se(strneq(comm, p, 15));
+
+        assert_se(get_process_cmdline(0, 0, false, &cmdline) >= 0);
+        log_info("cmdline = <%s>", cmdline);
+        assert_se(strneq(p, cmdline, strlen("test-process-util")));
+        assert_se(startswith(p, cmdline));
+
+finish:
+        _exit(EXIT_SUCCESS);
+}
+
+static void test_rename_process(void) {
+        test_rename_process_one(NULL, -EINVAL);
+        test_rename_process_one("", -EINVAL);
+        test_rename_process_one("foo", 1); /* should always fit */
+        test_rename_process_one("this is a really really long process name, followed by some more words", 0); /* unlikely to fit */
+        test_rename_process_one("1234567", 1); /* should always fit */
+}
+#endif // 0
+
+int main(int argc, char *argv[]) {
+
+        log_set_max_level(LOG_DEBUG);
+        log_parse_environment();
+        log_open();
+
+        saved_argc = argc;
+        saved_argv = argv;
+
+        if (argc > 1) {
+                pid_t pid = 0;
+
+                (void) parse_pid(argv[1], &pid);
+                test_get_process_comm(pid);
+        } else {
+                TEST_REQ_RUNNING_SYSTEMD(test_get_process_comm(1));
+                test_get_process_comm(getpid());
+        }
+
+        test_pid_is_unwaited();
+        test_pid_is_alive();
+#if 0 /// UNNEEDED by elogind
+        test_personality();
+#endif // 0
+        test_get_process_cmdline_harder();
+#if 0 /// UNNEEDED by elogind
+        test_rename_process();
+#endif // 0
+
+        return 0;
+}
diff --git a/src/test/test-selinux.c b/src/test/test-selinux.c
new file mode 100644 (file)
index 0000000..5687e1e
--- /dev/null
@@ -0,0 +1,126 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2016 Zbigniew Jędrzejewski-Szmek
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/stat.h>
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "log.h"
+#include "selinux-util.h"
+#include "string-util.h"
+#include "time-util.h"
+#include "util.h"
+
+static void test_testing(void) {
+        bool b;
+
+        log_info("============ %s ==========", __func__);
+
+        b = mac_selinux_use();
+        log_info("mac_selinux_use → %s", yes_no(b));
+
+        b = mac_selinux_have();
+        log_info("mac_selinux_have → %s", yes_no(b));
+
+        mac_selinux_retest();
+
+        b = mac_selinux_use();
+        log_info("mac_selinux_use → %s", yes_no(b));
+
+        b = mac_selinux_have();
+        log_info("mac_selinux_have → %s", yes_no(b));
+}
+
+static void test_loading(void) {
+        usec_t n1, n2;
+        int r;
+
+        log_info("============ %s ==========", __func__);
+
+        n1 = now(CLOCK_MONOTONIC);
+        r = mac_selinux_init();
+        n2 = now(CLOCK_MONOTONIC);
+        log_info_errno(r, "mac_selinux_init → %d %.2fs (%m)", r, (n2 - n1)/1e6);
+}
+
+static void test_cleanup(void) {
+        usec_t n1, n2;
+
+        log_info("============ %s ==========", __func__);
+
+        n1 = now(CLOCK_MONOTONIC);
+        mac_selinux_finish();
+        n2 = now(CLOCK_MONOTONIC);
+        log_info("mac_selinux_finish → %.2fs", (n2 - n1)/1e6);
+}
+
+#if 0 /// UNNEEDED by elogind
+static void test_misc(const char* fname) {
+        _cleanup_(mac_selinux_freep) char *label = NULL, *label2 = NULL, *label3 = NULL;
+        int r;
+        _cleanup_close_ int fd = -1;
+
+        log_info("============ %s ==========", __func__);
+
+        r = mac_selinux_get_our_label(&label);
+        log_info_errno(r, "mac_selinux_get_our_label → %d, \"%s\" (%m)",
+                       r, strnull(label));
+
+        r = mac_selinux_get_create_label_from_exe(fname, &label2);
+        log_info_errno(r, "mac_selinux_create_label_from_exe → %d, \"%s\" (%m)",
+                       r, strnull(label2));
+
+        fd = socket(AF_INET, SOCK_DGRAM, 0);
+        assert_se(fd >= 0);
+
+        r = mac_selinux_get_child_mls_label(fd, fname, label2, &label3);
+        log_info_errno(r, "mac_selinux_get_child_mls_label → %d, \"%s\" (%m)",
+                       r, strnull(label3));
+}
+#endif // 0
+
+static void test_create_file_prepare(const char* fname) {
+        int r;
+
+        log_info("============ %s ==========", __func__);
+
+        r = mac_selinux_create_file_prepare(fname, S_IRWXU);
+        log_info_errno(r, "mac_selinux_create_file_prepare → %d (%m)", r);
+
+        mac_selinux_create_file_clear();
+}
+
+int main(int argc, char **argv) {
+        const char *path = SYSTEMD_BINARY_PATH;
+        if (argc >= 2)
+                path = argv[1];
+
+        log_set_max_level(LOG_DEBUG);
+        log_parse_environment();
+
+        test_testing();
+        test_loading();
+#if 0 /// UNNEEDED by elogind
+        test_misc(path);
+#endif // 0
+        test_create_file_prepare(path);
+        test_cleanup();
+
+        return 0;
+}
diff --git a/src/test/test-set.c b/src/test/test-set.c
new file mode 100644 (file)
index 0000000..0ee5ddc
--- /dev/null
@@ -0,0 +1,63 @@
+/***
+  This file is part of systemd
+
+  Copyright 2014 Zbigniew Jędrzejewski-Szmek
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "set.h"
+
+static void test_set_steal_first(void) {
+        _cleanup_set_free_ Set *m = NULL;
+        int seen[3] = {};
+        char *val;
+
+        m = set_new(&string_hash_ops);
+        assert_se(m);
+
+        assert_se(set_put(m, (void*) "1") == 1);
+        assert_se(set_put(m, (void*) "22") == 1);
+        assert_se(set_put(m, (void*) "333") == 1);
+
+        while ((val = set_steal_first(m)))
+                seen[strlen(val) - 1]++;
+
+        assert_se(seen[0] == 1 && seen[1] == 1 && seen[2] == 1);
+
+        assert_se(set_isempty(m));
+}
+
+static void test_set_put(void) {
+        _cleanup_set_free_ Set *m = NULL;
+
+        m = set_new(&string_hash_ops);
+        assert_se(m);
+
+        assert_se(set_put(m, (void*) "1") == 1);
+        assert_se(set_put(m, (void*) "22") == 1);
+        assert_se(set_put(m, (void*) "333") == 1);
+        assert_se(set_put(m, (void*) "333") == 0);
+        assert_se(set_remove(m, (void*) "333"));
+        assert_se(set_put(m, (void*) "333") == 1);
+        assert_se(set_put(m, (void*) "333") == 0);
+        assert_se(set_put(m, (void*) "22") == 0);
+}
+
+int main(int argc, const char *argv[]) {
+        test_set_steal_first();
+        test_set_put();
+
+        return 0;
+}
diff --git a/src/test/test-signal-util.c b/src/test/test-signal-util.c
new file mode 100644 (file)
index 0000000..671eb86
--- /dev/null
@@ -0,0 +1,67 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2016 Lennart Poettering
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <signal.h>
+#include <unistd.h>
+
+#include "macro.h"
+#include "signal-util.h"
+
+static void test_block_signals(void) {
+        sigset_t ss;
+
+        assert_se(sigprocmask(0, NULL, &ss) >= 0);
+
+        assert_se(sigismember(&ss, SIGUSR1) == 0);
+        assert_se(sigismember(&ss, SIGALRM) == 0);
+        assert_se(sigismember(&ss, SIGVTALRM) == 0);
+
+        {
+                BLOCK_SIGNALS(SIGUSR1, SIGVTALRM);
+
+                assert_se(sigprocmask(0, NULL, &ss) >= 0);
+                assert_se(sigismember(&ss, SIGUSR1) == 1);
+                assert_se(sigismember(&ss, SIGALRM) == 0);
+                assert_se(sigismember(&ss, SIGVTALRM) == 1);
+
+        }
+
+        assert_se(sigprocmask(0, NULL, &ss) >= 0);
+        assert_se(sigismember(&ss, SIGUSR1) == 0);
+        assert_se(sigismember(&ss, SIGALRM) == 0);
+        assert_se(sigismember(&ss, SIGVTALRM) == 0);
+}
+
+static void test_ignore_signals(void) {
+        assert_se(ignore_signals(SIGINT, -1) >= 0);
+        assert_se(kill(getpid(), SIGINT) >= 0);
+        assert_se(ignore_signals(SIGUSR1, SIGUSR2, SIGTERM, SIGPIPE, -1) >= 0);
+        assert_se(kill(getpid(), SIGUSR1) >= 0);
+        assert_se(kill(getpid(), SIGUSR2) >= 0);
+        assert_se(kill(getpid(), SIGTERM) >= 0);
+        assert_se(kill(getpid(), SIGPIPE) >= 0);
+        assert_se(default_signals(SIGINT, SIGUSR1, SIGUSR2, SIGTERM, SIGPIPE, -1) >= 0);
+}
+
+int main(int argc, char *argv[]) {
+        test_block_signals();
+        test_ignore_signals();
+
+        return 0;
+}
diff --git a/src/test/test-siphash24.c b/src/test/test-siphash24.c
new file mode 100644 (file)
index 0000000..b74b7ad
--- /dev/null
@@ -0,0 +1,124 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2015 Tom Gundersen
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "siphash24.h"
+#include "util.h"
+
+#define ITERATIONS 10000000ULL
+
+static void do_test(const uint8_t *in, size_t len, const uint8_t *key) {
+        struct siphash state = {};
+        uint64_t out;
+        unsigned i, j;
+
+        out = siphash24(in, len, key);
+        assert_se(out == 0xa129ca6149be45e5);
+
+        /* verify the internal state as given in the above paper */
+        siphash24_init(&state, key);
+        assert_se(state.v0 == 0x7469686173716475);
+        assert_se(state.v1 == 0x6b617f6d656e6665);
+        assert_se(state.v2 == 0x6b7f62616d677361);
+        assert_se(state.v3 == 0x7b6b696e727e6c7b);
+        siphash24_compress(in, len, &state);
+        assert_se(state.v0 == 0x4a017198de0a59e0);
+        assert_se(state.v1 == 0x0d52f6f62a4f59a4);
+        assert_se(state.v2 == 0x634cb3577b01fd3d);
+        assert_se(state.v3 == 0xa5224d6f55c7d9c8);
+        out = siphash24_finalize(&state);
+        assert_se(out == 0xa129ca6149be45e5);
+        assert_se(state.v0 == 0xf6bcd53893fecff1);
+        assert_se(state.v1 == 0x54b9964c7ea0d937);
+        assert_se(state.v2 == 0x1b38329c099bb55a);
+        assert_se(state.v3 == 0x1814bb89ad7be679);
+
+        /* verify that decomposing the input in three chunks gives the
+           same result */
+        for (i = 0; i < len; i++) {
+                for (j = i; j < len; j++) {
+                        siphash24_init(&state, key);
+                        siphash24_compress(in, i, &state);
+                        siphash24_compress(&in[i], j - i, &state);
+                        siphash24_compress(&in[j], len - j, &state);
+                        out = siphash24_finalize(&state);
+                        assert_se(out == 0xa129ca6149be45e5);
+                }
+        }
+}
+
+static void test_short_hashes(void) {
+        const uint8_t one[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+                                0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16 };
+        const uint8_t  key[16] = { 0x22, 0x24, 0x41, 0x22, 0x55, 0x77, 0x88, 0x07,
+                                   0x23, 0x09, 0x23, 0x14, 0x0c, 0x33, 0x0e, 0x0f};
+        uint8_t two[sizeof one] = {};
+
+        struct siphash state1 = {}, state2 = {};
+        unsigned i, j;
+
+        siphash24_init(&state1, key);
+        siphash24_init(&state2, key);
+
+        /* hashing 1, 2, 3, 4, 5, ..., 16 bytes, with the byte after the buffer different */
+        for (i = 1; i <= sizeof one; i++) {
+                siphash24_compress(one, i, &state1);
+
+                two[i-1] = one[i-1];
+                siphash24_compress(two, i, &state2);
+
+                assert_se(memcmp(&state1, &state2, sizeof state1) == 0);
+        }
+
+        /* hashing n and 1, n and 2, n and 3, ..., n-1 and 1, n-2 and 2, ... */
+        for (i = sizeof one; i > 0; i--) {
+                zero(two);
+
+                for (j = 1; j <= sizeof one; j++) {
+                        siphash24_compress(one, i, &state1);
+                        siphash24_compress(one, j, &state1);
+
+                        siphash24_compress(one, i, &state2);
+                        two[j-1] = one[j-1];
+                        siphash24_compress(two, j, &state2);
+
+                        assert_se(memcmp(&state1, &state2, sizeof state1) == 0);
+                }
+        }
+}
+
+/* see https://131002.net/siphash/siphash.pdf, Appendix A */
+int main(int argc, char *argv[]) {
+        const uint8_t in[15]  = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+                                  0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e };
+        const uint8_t key[16] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+                                  0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
+        uint8_t in_buf[20];
+
+        /* Test with same input but different alignments. */
+        memcpy(in_buf, in, sizeof(in));
+        do_test(in_buf, sizeof(in), key);
+        memcpy(in_buf + 1, in, sizeof(in));
+        do_test(in_buf + 1, sizeof(in), key);
+        memcpy(in_buf + 2, in, sizeof(in));
+        do_test(in_buf + 2, sizeof(in), key);
+        memcpy(in_buf + 4, in, sizeof(in));
+        do_test(in_buf + 4, sizeof(in), key);
+
+        test_short_hashes();
+}
diff --git a/src/test/test-sizeof.c b/src/test/test-sizeof.c
new file mode 100644 (file)
index 0000000..8f99a13
--- /dev/null
@@ -0,0 +1,53 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2016 Zbigniew Jędrzejewski-Szmek
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "log.h"
+#include "time-util.h"
+
+/* Print information about various types. Useful when diagnosing
+ * gcc diagnostics on an unfamiliar architecture. */
+
+#pragma GCC diagnostic ignored "-Wtype-limits"
+
+#define info(t)                                                 \
+        log_info("%s → %zu bits%s", STRINGIFY(t),               \
+                 sizeof(t)*CHAR_BIT,                            \
+                 strstr(STRINGIFY(t), "signed") ? "" :          \
+                 ((t)-1 < (t)0 ? ", signed" : ", unsigned"));
+
+int main(void) {
+        info(char);
+        info(signed char);
+        info(unsigned char);
+        info(short unsigned);
+        info(unsigned);
+        info(long unsigned);
+        info(long long unsigned);
+
+        info(float);
+        info(double);
+        info(long double);
+
+        info(size_t);
+        info(ssize_t);
+        info(time_t);
+        info(usec_t);
+
+        return 0;
+}
diff --git a/src/test/test-stat-util.c b/src/test/test-stat-util.c
new file mode 100644 (file)
index 0000000..93d025d
--- /dev/null
@@ -0,0 +1,104 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <fcntl.h>
+#include <linux/magic.h>
+#include <unistd.h>
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "macro.h"
+#include "missing.h"
+#include "mount-util.h"
+#include "stat-util.h"
+
+static void test_files_same(void) {
+        _cleanup_close_ int fd = -1;
+        char name[] = "/tmp/test-files_same.XXXXXX";
+        char name_alias[] = "/tmp/test-files_same.alias";
+
+        fd = mkostemp_safe(name);
+        assert_se(fd >= 0);
+        assert_se(symlink(name, name_alias) >= 0);
+
+        assert_se(files_same(name, name));
+        assert_se(files_same(name, name_alias));
+
+        unlink(name);
+        unlink(name_alias);
+}
+
+#if 0 /// UNNEEDED by elogind
+static void test_is_symlink(void) {
+        char name[] = "/tmp/test-is_symlink.XXXXXX";
+        char name_link[] = "/tmp/test-is_symlink.link";
+        _cleanup_close_ int fd = -1;
+
+        fd = mkostemp_safe(name);
+        assert_se(fd >= 0);
+        assert_se(symlink(name, name_link) >= 0);
+
+        assert_se(is_symlink(name) == 0);
+        assert_se(is_symlink(name_link) == 1);
+        assert_se(is_symlink("/a/file/which/does/not/exist/i/guess") < 0);
+
+
+        unlink(name);
+        unlink(name_link);
+}
+
+static void test_path_is_os_tree(void) {
+        assert_se(path_is_os_tree("/") > 0);
+        assert_se(path_is_os_tree("/etc") == 0);
+        assert_se(path_is_os_tree("/idontexist") == -ENOENT);
+}
+
+static void test_path_check_fstype(void) {
+        /* run might not be a mount point in build chroots */
+        if (path_is_mount_point("/run", NULL, AT_SYMLINK_FOLLOW) > 0) {
+                assert_se(path_check_fstype("/run", TMPFS_MAGIC) > 0);
+                assert_se(path_check_fstype("/run", BTRFS_SUPER_MAGIC) == 0);
+        }
+        assert_se(path_check_fstype("/proc", PROC_SUPER_MAGIC) > 0);
+        assert_se(path_check_fstype("/proc", BTRFS_SUPER_MAGIC) == 0);
+        assert_se(path_check_fstype("/proc", BTRFS_SUPER_MAGIC) == 0);
+        assert_se(path_check_fstype("/i-dont-exist", BTRFS_SUPER_MAGIC) == -ENOENT);
+}
+
+static void test_path_is_temporary_fs(void) {
+        /* run might not be a mount point in build chroots */
+        if (path_is_mount_point("/run", NULL, AT_SYMLINK_FOLLOW) > 0)
+                assert_se(path_is_temporary_fs("/run") > 0);
+        assert_se(path_is_temporary_fs("/proc") == 0);
+        assert_se(path_is_temporary_fs("/i-dont-exist") == -ENOENT);
+}
+#endif // 0
+
+int main(int argc, char *argv[]) {
+        test_files_same();
+#if 0 /// UNNEEDED by elogind
+        test_is_symlink();
+        test_path_is_os_tree();
+        test_path_check_fstype();
+        test_path_is_temporary_fs();
+#endif // 0
+
+        return 0;
+}
diff --git a/src/test/test-string-util.c b/src/test/test-string-util.c
new file mode 100644 (file)
index 0000000..54fe87d
--- /dev/null
@@ -0,0 +1,375 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2015 Lennart Poettering
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "alloc-util.h"
+#include "macro.h"
+#include "string-util.h"
+#include "strv.h"
+
+static void test_string_erase(void) {
+        char *x;
+
+        x = strdupa("");
+        assert_se(streq(string_erase(x), ""));
+
+        x = strdupa("1");
+        assert_se(streq(string_erase(x), ""));
+
+        x = strdupa("123456789");
+        assert_se(streq(string_erase(x), ""));
+
+        assert_se(x[1] == '\0');
+        assert_se(x[2] == '\0');
+        assert_se(x[3] == '\0');
+        assert_se(x[4] == '\0');
+        assert_se(x[5] == '\0');
+        assert_se(x[6] == '\0');
+        assert_se(x[7] == '\0');
+        assert_se(x[8] == '\0');
+        assert_se(x[9] == '\0');
+}
+
+#if 0 /// UNNEEDED by elogind
+static void test_ascii_strcasecmp_n(void) {
+
+        assert_se(ascii_strcasecmp_n("", "", 0) == 0);
+        assert_se(ascii_strcasecmp_n("", "", 1) == 0);
+        assert_se(ascii_strcasecmp_n("", "a", 1) < 0);
+&nbs