]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #1937 from evverx/fix-stdout-parsing
authorLennart Poettering <lennart@poettering.net>
Fri, 27 Nov 2015 13:03:49 +0000 (14:03 +0100)
committerLennart Poettering <lennart@poettering.net>
Fri, 27 Nov 2015 13:03:49 +0000 (14:03 +0100)
Fix stdout stream parsing

276 files changed:
.editorconfig [new file with mode: 0644]
.gitignore
.vimrc
Makefile-man.am
Makefile.am
NEWS
TODO
configure.ac
man/.gitignore
man/custom-html.xsl
man/resolved.conf.xml
man/sd-daemon.xml
man/sd-event.xml [new file with mode: 0644]
man/sd_event_add_child.xml
man/sd_event_add_defer.xml
man/sd_event_add_io.xml [new file with mode: 0644]
man/sd_event_add_signal.xml
man/sd_event_add_time.xml
man/sd_event_exit.xml [new file with mode: 0644]
man/sd_event_get_fd.xml
man/sd_event_new.xml
man/sd_event_now.xml [new file with mode: 0644]
man/sd_event_run.xml
man/sd_event_set_name.xml [deleted file]
man/sd_event_set_watchdog.xml [new file with mode: 0644]
man/sd_event_source_get_event.xml [new file with mode: 0644]
man/sd_event_source_get_pending.xml [new file with mode: 0644]
man/sd_event_source_set_description.xml [new file with mode: 0644]
man/sd_event_source_set_enabled.xml [new file with mode: 0644]
man/sd_event_source_set_prepare.xml [new file with mode: 0644]
man/sd_event_source_set_priority.xml [new file with mode: 0644]
man/sd_event_source_set_userdata.xml [new file with mode: 0644]
man/sd_event_source_unref.xml [new file with mode: 0644]
man/sd_event_wait.xml
man/sd_watchdog_enabled.xml
man/systemctl.xml
man/systemd-journald.service.xml
man/systemd-system.conf.xml
man/systemd.exec.xml
man/systemd.mount.xml
man/systemd.service.xml
man/systemd.socket.xml
man/systemd.time.xml
man/systemd.timer.xml
po/el.po
po/es.po
po/hu.po
po/it.po
po/pt_BR.po
po/ru.po
po/sv.po
po/uk.po
po/zh_TW.po
shell-completion/bash/loginctl
shell-completion/bash/machinectl
shell-completion/bash/systemctl.in
shell-completion/bash/systemd-nspawn
src/basic/audit-util.h
src/basic/bitmap.h
src/basic/calendarspec.c
src/basic/calendarspec.h
src/basic/cgroup-util.h
src/basic/escape.c
src/basic/escape.h
src/basic/fd-util.h
src/basic/fileio-label.h
src/basic/fs-util.h
src/basic/gunicode.h
src/basic/hostname-util.c
src/basic/in-addr-util.c
src/basic/in-addr-util.h
src/basic/ioprio.h
src/basic/json.h
src/basic/memfd-util.h
src/basic/ordered-set.h
src/basic/parse-util.c
src/basic/parse-util.h
src/basic/process-util.h
src/basic/selinux-util.h
src/basic/set.h
src/basic/socket-util.h
src/basic/stat-util.h
src/basic/terminal-util.h
src/basic/time-util.c
src/basic/user-util.h
src/basic/virt.c
src/bootchart/bootchart.h
src/bootchart/store.h
src/bus-proxyd/bus-xml-policy.h
src/bus-proxyd/driver.h
src/core/bus-endpoint.h
src/core/bus-policy.h
src/core/cgroup.h
src/core/dbus-kill.h
src/core/dbus-manager.c
src/core/dbus-scope.h
src/core/dbus-service.h
src/core/dbus-slice.h
src/core/dbus-socket.c
src/core/dbus-socket.h
src/core/dbus-swap.h
src/core/dbus-timer.c
src/core/dbus-timer.h
src/core/dbus-unit.c
src/core/dbus-unit.h
src/core/execute.c
src/core/execute.h
src/core/job.c
src/core/job.h
src/core/load-dropin.h
src/core/load-fragment-gperf.gperf.m4
src/core/load-fragment.c
src/core/load-fragment.h
src/core/macros.systemd.in
src/core/manager.h
src/core/mount.c
src/core/mount.h
src/core/selinux-access.h
src/core/service.h
src/core/socket.c
src/core/socket.h
src/core/swap.c
src/core/timer.c
src/core/timer.h
src/core/transaction.h
src/core/triggers.systemd.in
src/core/unit.c
src/core/unit.h
src/fstab-generator/fstab-generator.c
src/import/curl-util.h
src/import/export-raw.h
src/import/export-tar.h
src/import/import-compress.h
src/import/import-raw.h
src/import/import-tar.h
src/import/pull-common.h
src/import/pull-dkr.h
src/import/pull-job.h
src/import/pull-raw.h
src/import/pull-tar.h
src/journal-remote/journal-remote-parse.h
src/journal-remote/journal-remote.h
src/journal-remote/journal-upload.h
src/journal-remote/microhttpd-util.h
src/journal/catalog.h
src/journal/fsprg.h
src/journal/journal-file.h
src/journal/journal-internal.h
src/journal/journald-audit.h
src/libsystemd-network/arp-util.h
src/libsystemd-network/dhcp-internal.h
src/libsystemd-network/dhcp-lease-internal.h
src/libsystemd-network/dhcp-option.c
src/libsystemd-network/dhcp-protocol.h
src/libsystemd-network/dhcp-server-internal.h
src/libsystemd-network/dhcp6-lease-internal.h
src/libsystemd-network/dhcp6-option.c
src/libsystemd-network/icmp6-util.c
src/libsystemd-network/lldp-tlv.h
src/libsystemd-network/network-internal.h
src/libsystemd-network/sd-dhcp-client.c
src/libsystemd-network/sd-dhcp-lease.c
src/libsystemd-network/sd-dhcp-server.c
src/libsystemd-network/sd-dhcp6-client.c
src/libsystemd-network/sd-ndisc.c
src/libsystemd-network/test-dhcp-client.c
src/libsystemd-network/test-dhcp-option.c
src/libsystemd/sd-bus/bus-control.h
src/libsystemd/sd-bus/bus-dump.h
src/libsystemd/sd-bus/bus-error.h
src/libsystemd/sd-bus/bus-introspect.h
src/libsystemd/sd-bus/bus-slot.h
src/libsystemd/sd-event/sd-event.c
src/libsystemd/sd-event/test-event.c
src/libsystemd/sd-netlink/local-addresses.h
src/libudev/libudev-private.h
src/login/logind-acl.h
src/login/logind-action.h
src/login/logind-session.h
src/machine/machined.h
src/network/networkd-address-pool.h
src/network/networkd-address.h
src/network/networkd-dhcp4.c
src/network/networkd-fdb.h
src/network/networkd-link.c
src/network/networkd-link.h
src/network/networkd-netdev-vxlan.h
src/network/networkd-netdev.h
src/network/networkd-network.h
src/network/networkd-route.h
src/network/networkd.h
src/nspawn/nspawn-cgroup.h
src/nspawn/nspawn-expose-ports.h
src/nspawn/nspawn-network.h
src/nspawn/nspawn-settings.h
src/nss-mymachines/nss-mymachines.c
src/resolve-host/resolve-host.c
src/resolve/resolved-bus.c
src/resolve/resolved-conf.c
src/resolve/resolved-conf.h
src/resolve/resolved-def.h
src/resolve/resolved-dns-answer.c
src/resolve/resolved-dns-answer.h
src/resolve/resolved-dns-cache.c
src/resolve/resolved-dns-cache.h
src/resolve/resolved-dns-packet.c
src/resolve/resolved-dns-packet.h
src/resolve/resolved-dns-query.c
src/resolve/resolved-dns-query.h
src/resolve/resolved-dns-question.c
src/resolve/resolved-dns-question.h
src/resolve/resolved-dns-rr.c
src/resolve/resolved-dns-rr.h
src/resolve/resolved-dns-scope.c
src/resolve/resolved-dns-scope.h
src/resolve/resolved-dns-search-domain.c [new file with mode: 0644]
src/resolve/resolved-dns-search-domain.h [new file with mode: 0644]
src/resolve/resolved-dns-server.c
src/resolve/resolved-dns-server.h
src/resolve/resolved-dns-transaction.c
src/resolve/resolved-dns-transaction.h
src/resolve/resolved-dns-zone.c
src/resolve/resolved-dns-zone.h
src/resolve/resolved-gperf.gperf
src/resolve/resolved-link.c
src/resolve/resolved-link.h
src/resolve/resolved-manager.c
src/resolve/resolved-manager.h
src/resolve/resolved-resolv-conf.c [new file with mode: 0644]
src/resolve/resolved-resolv-conf.h [new file with mode: 0644]
src/resolve/resolved.c
src/resolve/resolved.conf.in
src/run/run.c
src/shared/acl-util.h
src/shared/bus-util.c
src/shared/cgroup-show.h
src/shared/conf-parser.h
src/shared/dns-domain.c
src/shared/dns-domain.h
src/shared/efivars.h
src/shared/logs-show.h
src/shared/machine-image.h
src/shared/nss-util.h
src/systemctl/systemctl.c
src/systemd/sd-bus.h
src/systemd/sd-daemon.h
src/systemd/sd-device.h
src/systemd/sd-dhcp-client.h
src/systemd/sd-dhcp-server.h
src/systemd/sd-dhcp6-client.h
src/systemd/sd-event.h
src/systemd/sd-ipv4acd.h
src/systemd/sd-ipv4ll.h
src/systemd/sd-journal.h
src/systemd/sd-lldp.h
src/systemd/sd-login.h
src/systemd/sd-messages.h
src/systemd/sd-ndisc.h
src/systemd/sd-netlink.h
src/systemd/sd-network.h
src/systemd/sd-resolve.h
src/test/test-calendarspec.c
src/test/test-dns-domain.c
src/test/test-siphash24.c
src/test/test-unit-file.c
src/timesync/timesyncd-conf.h
src/timesync/timesyncd-manager.h
src/timesync/timesyncd-server.h
sysctl.d/50-coredump.conf.in
test/README.testsuite
test/TEST-01-BASIC/test.sh
test/TEST-02-CRYPTSETUP/test.sh
test/TEST-03-JOBS/test.sh
test/test-functions
tools/make-directive-index.py
units/user@.service.m4.in

diff --git a/.editorconfig b/.editorconfig
new file mode 100644 (file)
index 0000000..70b6c0f
--- /dev/null
@@ -0,0 +1,17 @@
+# EditorConfig configuration for systemd
+# http://EditorConfig.org
+
+# top-most EditorConfig file
+root = true
+
+# Unix-style newlines with a newline ending every file, utf-8 charset
+[*]
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
+charset = utf-8
+
+# match config files, set indent to spaces with width of eight
+[*.{c,h}]
+indent_style = space
+indent_size = 8
index b0abc0d4e99e3bab18117e1286d73eccb594508a..98eb29d6573862b336126e27c8bc0e411d2c0ddc 100644 (file)
@@ -1,6 +1,5 @@
 *.a
 *.cache
-*.html
 *.la
 *.lo
 *.log
diff --git a/.vimrc b/.vimrc
index b802c908f268d686c31b7fc6f5e7d0375b27abfe..d8020f340469889cca4773ab97c7e1e009798453 100644 (file)
--- a/.vimrc
+++ b/.vimrc
@@ -1,4 +1,8 @@
 " 'set exrc' in ~/.vimrc will read .vimrc from the current directory
+" Warning: Enabling exrc is dangerous! You can do nearly everything from a
+" vimrc configuration file, including write operations and shell execution.
+" You should consider setting 'set secure' as well, which is highly
+" recommended!
 set tabstop=8
 set shiftwidth=8
 set expandtab
index c792c8932499feee2c6c5ecb1bb885abc8f73e53..db0416f9d12d7e780a9b529c5d26d3e3bad42ec5 100644 (file)
@@ -26,6 +26,7 @@ MANPAGES += \
        man/os-release.5 \
        man/sd-bus-errors.3 \
        man/sd-daemon.3 \
+       man/sd-event.3 \
        man/sd-id128.3 \
        man/sd-journal.3 \
        man/sd_booted.3 \
@@ -47,12 +48,23 @@ MANPAGES += \
        man/sd_bus_request_name.3 \
        man/sd_event_add_child.3 \
        man/sd_event_add_defer.3 \
+       man/sd_event_add_io.3 \
        man/sd_event_add_signal.3 \
        man/sd_event_add_time.3 \
+       man/sd_event_exit.3 \
        man/sd_event_get_fd.3 \
        man/sd_event_new.3 \
+       man/sd_event_now.3 \
        man/sd_event_run.3 \
-       man/sd_event_set_name.3 \
+       man/sd_event_set_watchdog.3 \
+       man/sd_event_source_get_event.3 \
+       man/sd_event_source_get_pending.3 \
+       man/sd_event_source_set_description.3 \
+       man/sd_event_source_set_enabled.3 \
+       man/sd_event_source_set_prepare.3 \
+       man/sd_event_source_set_priority.3 \
+       man/sd_event_source_set_userdata.3 \
+       man/sd_event_source_unref.3 \
        man/sd_event_wait.3 \
        man/sd_id128_get_machine.3 \
        man/sd_id128_randomize.3 \
@@ -199,6 +211,19 @@ MANPAGES_ALIAS += \
        man/SD_DEBUG.3 \
        man/SD_EMERG.3 \
        man/SD_ERR.3 \
+       man/SD_EVENT_ARMED.3 \
+       man/SD_EVENT_EXITING.3 \
+       man/SD_EVENT_FINISHED.3 \
+       man/SD_EVENT_INITIAL.3 \
+       man/SD_EVENT_OFF.3 \
+       man/SD_EVENT_ON.3 \
+       man/SD_EVENT_ONESHOT.3 \
+       man/SD_EVENT_PENDING.3 \
+       man/SD_EVENT_PREPARING.3 \
+       man/SD_EVENT_PRIORITY_IDLE.3 \
+       man/SD_EVENT_PRIORITY_IMPORTANT.3 \
+       man/SD_EVENT_PRIORITY_NORMAL.3 \
+       man/SD_EVENT_RUNNING.3 \
        man/SD_ID128_CONST_STR.3 \
        man/SD_ID128_FORMAT_STR.3 \
        man/SD_ID128_FORMAT_VAL.3 \
@@ -295,21 +320,41 @@ MANPAGES_ALIAS += \
        man/sd_bus_ref.3 \
        man/sd_bus_release_name.3 \
        man/sd_bus_unref.3 \
+       man/sd_event.3 \
        man/sd_event_add_exit.3 \
        man/sd_event_add_post.3 \
+       man/sd_event_child_handler_t.3 \
        man/sd_event_default.3 \
        man/sd_event_dispatch.3 \
-       man/sd_event_get_name.3 \
+       man/sd_event_get_exit_code.3 \
+       man/sd_event_get_state.3 \
+       man/sd_event_get_tid.3 \
+       man/sd_event_get_watchdog.3 \
+       man/sd_event_handler_t.3 \
+       man/sd_event_io_handler_t.3 \
        man/sd_event_loop.3 \
        man/sd_event_prepare.3 \
        man/sd_event_ref.3 \
+       man/sd_event_signal_handler_t.3 \
+       man/sd_event_source.3 \
        man/sd_event_source_get_child_pid.3 \
+       man/sd_event_source_get_description.3 \
+       man/sd_event_source_get_enabled.3 \
+       man/sd_event_source_get_io_events.3 \
+       man/sd_event_source_get_io_fd.3 \
+       man/sd_event_source_get_io_revents.3 \
+       man/sd_event_source_get_priority.3 \
        man/sd_event_source_get_signal.3 \
        man/sd_event_source_get_time.3 \
        man/sd_event_source_get_time_accuracy.3 \
        man/sd_event_source_get_time_clock.3 \
+       man/sd_event_source_get_userdata.3 \
+       man/sd_event_source_ref.3 \
+       man/sd_event_source_set_io_events.3 \
+       man/sd_event_source_set_io_fd.3 \
        man/sd_event_source_set_time.3 \
        man/sd_event_source_set_time_accuracy.3 \
+       man/sd_event_time_handler_t.3 \
        man/sd_event_unref.3 \
        man/sd_id128_equal.3 \
        man/sd_id128_from_string.3 \
@@ -489,6 +534,19 @@ man/SD_CRIT.3: man/sd-daemon.3
 man/SD_DEBUG.3: man/sd-daemon.3
 man/SD_EMERG.3: man/sd-daemon.3
 man/SD_ERR.3: man/sd-daemon.3
+man/SD_EVENT_ARMED.3: man/sd_event_wait.3
+man/SD_EVENT_EXITING.3: man/sd_event_wait.3
+man/SD_EVENT_FINISHED.3: man/sd_event_wait.3
+man/SD_EVENT_INITIAL.3: man/sd_event_wait.3
+man/SD_EVENT_OFF.3: man/sd_event_source_set_enabled.3
+man/SD_EVENT_ON.3: man/sd_event_source_set_enabled.3
+man/SD_EVENT_ONESHOT.3: man/sd_event_source_set_enabled.3
+man/SD_EVENT_PENDING.3: man/sd_event_wait.3
+man/SD_EVENT_PREPARING.3: man/sd_event_wait.3
+man/SD_EVENT_PRIORITY_IDLE.3: man/sd_event_source_set_priority.3
+man/SD_EVENT_PRIORITY_IMPORTANT.3: man/sd_event_source_set_priority.3
+man/SD_EVENT_PRIORITY_NORMAL.3: man/sd_event_source_set_priority.3
+man/SD_EVENT_RUNNING.3: man/sd_event_wait.3
 man/SD_ID128_CONST_STR.3: man/sd-id128.3
 man/SD_ID128_FORMAT_STR.3: man/sd-id128.3
 man/SD_ID128_FORMAT_VAL.3: man/sd-id128.3
@@ -585,21 +643,41 @@ man/sd_bus_path_encode_many.3: man/sd_bus_path_encode.3
 man/sd_bus_ref.3: man/sd_bus_new.3
 man/sd_bus_release_name.3: man/sd_bus_request_name.3
 man/sd_bus_unref.3: man/sd_bus_new.3
+man/sd_event.3: man/sd_event_new.3
 man/sd_event_add_exit.3: man/sd_event_add_defer.3
 man/sd_event_add_post.3: man/sd_event_add_defer.3
+man/sd_event_child_handler_t.3: man/sd_event_add_child.3
 man/sd_event_default.3: man/sd_event_new.3
 man/sd_event_dispatch.3: man/sd_event_wait.3
-man/sd_event_get_name.3: man/sd_event_set_name.3
+man/sd_event_get_exit_code.3: man/sd_event_exit.3
+man/sd_event_get_state.3: man/sd_event_wait.3
+man/sd_event_get_tid.3: man/sd_event_new.3
+man/sd_event_get_watchdog.3: man/sd_event_set_watchdog.3
+man/sd_event_handler_t.3: man/sd_event_add_defer.3
+man/sd_event_io_handler_t.3: man/sd_event_add_io.3
 man/sd_event_loop.3: man/sd_event_run.3
 man/sd_event_prepare.3: man/sd_event_wait.3
 man/sd_event_ref.3: man/sd_event_new.3
+man/sd_event_signal_handler_t.3: man/sd_event_add_signal.3
+man/sd_event_source.3: man/sd_event_add_io.3
 man/sd_event_source_get_child_pid.3: man/sd_event_add_child.3
+man/sd_event_source_get_description.3: man/sd_event_source_set_description.3
+man/sd_event_source_get_enabled.3: man/sd_event_source_set_enabled.3
+man/sd_event_source_get_io_events.3: man/sd_event_add_io.3
+man/sd_event_source_get_io_fd.3: man/sd_event_add_io.3
+man/sd_event_source_get_io_revents.3: man/sd_event_add_io.3
+man/sd_event_source_get_priority.3: man/sd_event_source_set_priority.3
 man/sd_event_source_get_signal.3: man/sd_event_add_signal.3
 man/sd_event_source_get_time.3: man/sd_event_add_time.3
 man/sd_event_source_get_time_accuracy.3: man/sd_event_add_time.3
 man/sd_event_source_get_time_clock.3: man/sd_event_add_time.3
+man/sd_event_source_get_userdata.3: man/sd_event_source_set_userdata.3
+man/sd_event_source_ref.3: man/sd_event_source_unref.3
+man/sd_event_source_set_io_events.3: man/sd_event_add_io.3
+man/sd_event_source_set_io_fd.3: man/sd_event_add_io.3
 man/sd_event_source_set_time.3: man/sd_event_add_time.3
 man/sd_event_source_set_time_accuracy.3: man/sd_event_add_time.3
+man/sd_event_time_handler_t.3: man/sd_event_add_time.3
 man/sd_event_unref.3: man/sd_event_new.3
 man/sd_id128_equal.3: man/sd-id128.3
 man/sd_id128_from_string.3: man/sd_id128_to_string.3
@@ -857,6 +935,45 @@ man/SD_EMERG.html: man/sd-daemon.html
 man/SD_ERR.html: man/sd-daemon.html
        $(html-alias)
 
+man/SD_EVENT_ARMED.html: man/sd_event_wait.html
+       $(html-alias)
+
+man/SD_EVENT_EXITING.html: man/sd_event_wait.html
+       $(html-alias)
+
+man/SD_EVENT_FINISHED.html: man/sd_event_wait.html
+       $(html-alias)
+
+man/SD_EVENT_INITIAL.html: man/sd_event_wait.html
+       $(html-alias)
+
+man/SD_EVENT_OFF.html: man/sd_event_source_set_enabled.html
+       $(html-alias)
+
+man/SD_EVENT_ON.html: man/sd_event_source_set_enabled.html
+       $(html-alias)
+
+man/SD_EVENT_ONESHOT.html: man/sd_event_source_set_enabled.html
+       $(html-alias)
+
+man/SD_EVENT_PENDING.html: man/sd_event_wait.html
+       $(html-alias)
+
+man/SD_EVENT_PREPARING.html: man/sd_event_wait.html
+       $(html-alias)
+
+man/SD_EVENT_PRIORITY_IDLE.html: man/sd_event_source_set_priority.html
+       $(html-alias)
+
+man/SD_EVENT_PRIORITY_IMPORTANT.html: man/sd_event_source_set_priority.html
+       $(html-alias)
+
+man/SD_EVENT_PRIORITY_NORMAL.html: man/sd_event_source_set_priority.html
+       $(html-alias)
+
+man/SD_EVENT_RUNNING.html: man/sd_event_wait.html
+       $(html-alias)
+
 man/SD_ID128_CONST_STR.html: man/sd-id128.html
        $(html-alias)
 
@@ -1145,19 +1262,40 @@ man/sd_bus_release_name.html: man/sd_bus_request_name.html
 man/sd_bus_unref.html: man/sd_bus_new.html
        $(html-alias)
 
+man/sd_event.html: man/sd_event_new.html
+       $(html-alias)
+
 man/sd_event_add_exit.html: man/sd_event_add_defer.html
        $(html-alias)
 
 man/sd_event_add_post.html: man/sd_event_add_defer.html
        $(html-alias)
 
+man/sd_event_child_handler_t.html: man/sd_event_add_child.html
+       $(html-alias)
+
 man/sd_event_default.html: man/sd_event_new.html
        $(html-alias)
 
 man/sd_event_dispatch.html: man/sd_event_wait.html
        $(html-alias)
 
-man/sd_event_get_name.html: man/sd_event_set_name.html
+man/sd_event_get_exit_code.html: man/sd_event_exit.html
+       $(html-alias)
+
+man/sd_event_get_state.html: man/sd_event_wait.html
+       $(html-alias)
+
+man/sd_event_get_tid.html: man/sd_event_new.html
+       $(html-alias)
+
+man/sd_event_get_watchdog.html: man/sd_event_set_watchdog.html
+       $(html-alias)
+
+man/sd_event_handler_t.html: man/sd_event_add_defer.html
+       $(html-alias)
+
+man/sd_event_io_handler_t.html: man/sd_event_add_io.html
        $(html-alias)
 
 man/sd_event_loop.html: man/sd_event_run.html
@@ -1169,9 +1307,33 @@ man/sd_event_prepare.html: man/sd_event_wait.html
 man/sd_event_ref.html: man/sd_event_new.html
        $(html-alias)
 
+man/sd_event_signal_handler_t.html: man/sd_event_add_signal.html
+       $(html-alias)
+
+man/sd_event_source.html: man/sd_event_add_io.html
+       $(html-alias)
+
 man/sd_event_source_get_child_pid.html: man/sd_event_add_child.html
        $(html-alias)
 
+man/sd_event_source_get_description.html: man/sd_event_source_set_description.html
+       $(html-alias)
+
+man/sd_event_source_get_enabled.html: man/sd_event_source_set_enabled.html
+       $(html-alias)
+
+man/sd_event_source_get_io_events.html: man/sd_event_add_io.html
+       $(html-alias)
+
+man/sd_event_source_get_io_fd.html: man/sd_event_add_io.html
+       $(html-alias)
+
+man/sd_event_source_get_io_revents.html: man/sd_event_add_io.html
+       $(html-alias)
+
+man/sd_event_source_get_priority.html: man/sd_event_source_set_priority.html
+       $(html-alias)
+
 man/sd_event_source_get_signal.html: man/sd_event_add_signal.html
        $(html-alias)
 
@@ -1184,12 +1346,27 @@ man/sd_event_source_get_time_accuracy.html: man/sd_event_add_time.html
 man/sd_event_source_get_time_clock.html: man/sd_event_add_time.html
        $(html-alias)
 
+man/sd_event_source_get_userdata.html: man/sd_event_source_set_userdata.html
+       $(html-alias)
+
+man/sd_event_source_ref.html: man/sd_event_source_unref.html
+       $(html-alias)
+
+man/sd_event_source_set_io_events.html: man/sd_event_add_io.html
+       $(html-alias)
+
+man/sd_event_source_set_io_fd.html: man/sd_event_add_io.html
+       $(html-alias)
+
 man/sd_event_source_set_time.html: man/sd_event_add_time.html
        $(html-alias)
 
 man/sd_event_source_set_time_accuracy.html: man/sd_event_add_time.html
        $(html-alias)
 
+man/sd_event_time_handler_t.html: man/sd_event_add_time.html
+       $(html-alias)
+
 man/sd_event_unref.html: man/sd_event_new.html
        $(html-alias)
 
@@ -2264,6 +2441,7 @@ EXTRA_DIST += \
        man/runlevel.xml \
        man/sd-bus-errors.xml \
        man/sd-daemon.xml \
+       man/sd-event.xml \
        man/sd-id128.xml \
        man/sd-journal.xml \
        man/sd-login.xml \
@@ -2286,12 +2464,23 @@ EXTRA_DIST += \
        man/sd_bus_request_name.xml \
        man/sd_event_add_child.xml \
        man/sd_event_add_defer.xml \
+       man/sd_event_add_io.xml \
        man/sd_event_add_signal.xml \
        man/sd_event_add_time.xml \
+       man/sd_event_exit.xml \
        man/sd_event_get_fd.xml \
        man/sd_event_new.xml \
+       man/sd_event_now.xml \
        man/sd_event_run.xml \
-       man/sd_event_set_name.xml \
+       man/sd_event_set_watchdog.xml \
+       man/sd_event_source_get_event.xml \
+       man/sd_event_source_get_pending.xml \
+       man/sd_event_source_set_description.xml \
+       man/sd_event_source_set_enabled.xml \
+       man/sd_event_source_set_prepare.xml \
+       man/sd_event_source_set_priority.xml \
+       man/sd_event_source_set_userdata.xml \
+       man/sd_event_source_unref.xml \
        man/sd_event_wait.xml \
        man/sd_get_seats.xml \
        man/sd_id128_get_machine.xml \
index 336c977e5eaea0623dce0dbdd38bc3d4645c9449..6597d305d13f7495f6b7028606e9ef6abb3abed9 100644 (file)
@@ -42,9 +42,9 @@ LIBUDEV_CURRENT=7
 LIBUDEV_REVISION=4
 LIBUDEV_AGE=6
 
-LIBSYSTEMD_CURRENT=12
+LIBSYSTEMD_CURRENT=13
 LIBSYSTEMD_REVISION=0
-LIBSYSTEMD_AGE=12
+LIBSYSTEMD_AGE=13
 
 # The following four libraries only exist for compatibility reasons,
 # their version info should not be bumped anymore
@@ -426,6 +426,7 @@ dist_bashcompletion_DATA = \
        shell-completion/bash/systemd-delta \
        shell-completion/bash/systemd-detect-virt \
        shell-completion/bash/systemd-nspawn \
+       shell-completion/bash/systemd-path \
        shell-completion/bash/systemd-run \
        shell-completion/bash/udevadm \
        shell-completion/bash/kernel-install
@@ -660,6 +661,7 @@ EXTRA_DIST += \
        README.md \
        autogen.sh \
        .dir-locals.el \
+       .editorconfig \
        .vimrc \
        .ycm_extra_conf.py \
        .travis.yml \
@@ -5138,6 +5140,8 @@ systemd_resolved_SOURCES = \
        src/resolve/resolved-manager.h \
        src/resolve/resolved-conf.c \
        src/resolve/resolved-conf.h \
+       src/resolve/resolved-resolv-conf.c \
+       src/resolve/resolved-resolv-conf.h \
        src/resolve/resolved-bus.c \
        src/resolve/resolved-bus.h \
        src/resolve/resolved-link.h \
@@ -5161,6 +5165,8 @@ systemd_resolved_SOURCES = \
        src/resolve/resolved-dns-scope.c \
        src/resolve/resolved-dns-server.h \
        src/resolve/resolved-dns-server.c \
+       src/resolve/resolved-dns-search-domain.h \
+       src/resolve/resolved-dns-search-domain.c \
        src/resolve/resolved-dns-cache.h \
        src/resolve/resolved-dns-cache.c \
        src/resolve/resolved-dns-zone.h \
@@ -5369,6 +5375,11 @@ networkctl_LDADD = \
        libshared.la \
        libsystemd-network.la
 
+if ENABLE_BASH_COMPLETION
+dist_bashcompletion_DATA += \
+       shell-completion/bash/networkctl
+endif
+
 test_network_SOURCES = \
        src/network/test-network.c
 
diff --git a/NEWS b/NEWS
index 49862b352da0a36916fa393016017272643fe8ee..539093616ee2520e18ffb9f0e58d10f24ac5d773 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -88,6 +88,14 @@ CHANGES WITH 228:
           from PID1's environment block into the environment block of
           the service.
 
+        * Timer units gained support for a new RemainAfterElapse=
+          setting which takes a boolean argument. It defaults on on,
+          exposing behaviour unchanged to previous releases. If set to
+          off, timer units are unloaded after they elapsed if they
+          cannot elapse again. This is particularly useful for
+          transient timer units, which shall not stay around longer
+          than until they first elapse.
+
         * systemd will now bump the net.unix.max_dgram_qlen to 512 by
           default now (the kernel default is 16). This is beneficial
           for avoiding blocking on AF_UNIX/SOCK_DGRAM sockets since it
@@ -188,7 +196,7 @@ CHANGES WITH 228:
         Tom Gundersen, Torstein Husebø, Vito Caputo, Zbigniew
         Jędrzejewski-Szmek
 
-        -- Berlin, 2015-11-XX
+        -- Berlin, 2015-11-18
 
 CHANGES WITH 227:
 
diff --git a/TODO b/TODO
index 10a20758bab4df800d751fe1e3da06fb656a1c52..a11ecdb277b94524867922a636183776ca32f2fb 100644 (file)
--- a/TODO
+++ b/TODO
@@ -71,8 +71,6 @@ Features:
 
 * install: include generator dirs in unit file search paths
 
-* invent a better systemd-run scheme for naming scopes, that works with remoting
-
 * rework C11 utf8.[ch] to use char32_t instead of uint32_t when referring
   to unicode chars, to make things more expressive.
 
@@ -103,7 +101,7 @@ Features:
 
 * Rework systemctl's GetAll property parsing to use the generic bus_map_all_properties() API
 
-* core/cgroup: support net_cls modules, and support automatically allocating class ids, then add support for making firewall changes depending on it, to implement a per-service firewall
+* implement a per-service firewall based on net_cls
 
 * Port various tools to make use of verbs.[ch], where applicable
 
@@ -155,8 +153,6 @@ Features:
 
 * maybe provide an API to allow migration of foreign PIDs into existing scopes.
 
-* maybe support a new very "soft" reboot mode, that simply kills all processes, disassembles everything, flushes /run and sysvipc, and then reexecs systemd again
-
 * man: maybe use the word "inspect" rather than "introspect"?
 
 * systemctl: if some operation fails, show log output?
@@ -187,19 +183,16 @@ Features:
     we always process them before we process client requests
   - DNSSEC
         - add display of private key types (http://tools.ietf.org/html/rfc4034#appendix-A.1.1)?
-  - DNS
-        - search paths
   - mDNS/DNS-SD
+        - mDNS RR resolving
+        - service registration
+        - service/domain/types browsing
         - avahi compat
   - DNS-SD service registration from socket units
-  - edns0
-  - dname: Not necessary for plain DNS as synthesized cname is handed out instead if we do not
-           announce dname support. However, for DNSSEC it is necessary as the synthesized cname
-           will not be signed.
-  - cname on PTR (?)
   - resolved should optionally register additional per-interface LLMNR
     names, so that for the container case we can establish the same name
     (maybe "host") for referencing the server, everywhere.
+  - add API so NM can push DNS server info into resolved
 
 * refcounting in sd-resolve is borked
 
@@ -213,8 +206,6 @@ Features:
 
 * generator that automatically discovers btrfs subvolumes, identifies their purpose based on some xattr on them.
 
-* timer units: actually add extra delays to timer units with high AccuracySec values, don't start them already when we are awake...
-
 * a way for container managers to turn off getty starting via $container_headless= or so...
 
 * figure out a nice way how we can let the admin know what child/sibling unit causes cgroup membership for a specific unit
@@ -736,7 +727,6 @@ Features:
   - Support --test based on current system state
   - If we show an error about a unit (such as not showing up) and it has no Description string, then show a description string generated form the reverse of unit_name_mangle().
   - after deserializing sockets in socket.c we should reapply sockopts and things
-  - make timer units go away after they elapsed
   - drop PID 1 reloading, only do reexecing (difficult: Reload()
     currently is properly synchronous, Reexec() is weird, because we
     cannot delay the response properly until we are back, so instead of
index c96b9fb1d9fe47b3a6926a282036b35d6b408de2..ec30ff12ae32a61327b8d92a9b18124c6fab86e9 100644 (file)
@@ -20,7 +20,7 @@
 AC_PREREQ([2.64])
 
 AC_INIT([systemd],
-        [227],
+        [228],
         [http://github.com/systemd/systemd/issues],
         [systemd],
         [http://www.freedesktop.org/wiki/Software/systemd])
index bf5eeab938eccf6b69180572ad89f747232bba3a..d928e5a83fd8248e078ee50bf18b30f02e9aad7f 100644 (file)
@@ -1,4 +1,5 @@
 /systemd.directives.xml
 /systemd.index.xml
 /*.[13578]
+/*.html
 /custom-entities.ent
index 84c23014e4bf255e045828a72ab933dd288687f1..e89d73e7f1dc9f126aba781bc5f11a2f4869876c 100644 (file)
@@ -37,7 +37,8 @@
 <xsl:template match="citerefentry[not(@project)]">
   <a>
     <xsl:attribute name="href">
-      <xsl:value-of select="refentrytitle"/><xsl:text>.html</xsl:text>
+      <xsl:value-of select="refentrytitle"/><xsl:text>.html#</xsl:text>
+      <xsl:value-of select="refentrytitle/@target"/>
     </xsl:attribute>
     <xsl:call-template name="inline.charseq"/>
   </a>
index 811e33f4fa4370dd2b242b09ca580cad02e9aa88..4680b6a4e5923b2d62a22f1b30c0a8f20ae5fd32 100644 (file)
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*-nxml-*-->
+<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
 <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
   "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
 
         sent to one of the listed DNS servers in parallel to any
         per-interface DNS servers acquired from
         <citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
-        For compatibility reasons, if set to the empty list, the DNS
-        servers listed in <filename>/etc/resolv.conf</filename> are
-        used, if any are configured there. This setting defaults to
-        the empty list.</para></listitem>
+        For compatibility reasons, if this setting is not specified,
+        the DNS servers listed in
+        <filename>/etc/resolv.conf</filename> are used instead, if
+        that file exists and any servers are configured in it. This
+        setting defaults to the empty list.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         instead.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>Domains=</varname></term>
+        <listitem><para>A space-separated list of search domains.  For
+        compatibility reasons, if this setting is not specified, the
+        search domains listed in <filename>/etc/resolv.conf</filename>
+        are used instead, if that file exists and any domains are
+        configured in it. This setting defaults to the empty
+        list.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>LLMNR=</varname></term>
         <listitem><para>Takes a boolean argument or
index b7ba363656606d9c6f049e3999e903677d5b66ea..b06d99f34623f2c7e438cf2dac084947025e6371 100644 (file)
   <refsect1>
     <title>Description</title>
 
-    <para><filename>sd-daemon.h</filename> provide APIs for new-style
+    <para><filename>sd-daemon.h</filename> provides APIs for new-style
     daemons, as implemented by the
     <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>
-    init system.</para>
+    service manager.</para>
 
     <para>See
     <citerefentry><refentrytitle>sd_listen_fds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
diff --git a/man/sd-event.xml b/man/sd-event.xml
new file mode 100644 (file)
index 0000000..47989f4
--- /dev/null
@@ -0,0 +1,187 @@
+<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<!--
+  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/>.
+-->
+
+<refentry id="sd-event" xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sd-event</title>
+    <productname>systemd</productname>
+
+    <authorgroup>
+      <author>
+        <contrib>Developer</contrib>
+        <firstname>Lennart</firstname>
+        <surname>Poettering</surname>
+        <email>lennart@poettering.net</email>
+      </author>
+    </authorgroup>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd-event</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd-event</refname>
+    <refpurpose>A generic event loop implementation</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-event.h&gt;</funcsynopsisinfo>
+    </funcsynopsis>
+
+    <cmdsynopsis>
+      <command>pkg-config --cflags --libs libsystemd</command>
+    </cmdsynopsis>
+
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><filename>sd-event.h</filename> provides a generic event
+    loop implementation, based on Linux <citerefentry
+    project='man-pages'><refentrytitle>epoll</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
+    </para>
+
+    <para>See
+    <citerefentry><refentrytitle>sd_event_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_event_run</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_event_source_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_event_source_set_priority</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_event_source_set_userdata</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_event_source_get_event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_event_source_get_pending</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_event_source_set_description</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_event_source_set_prepare</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_event_wait</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_event_get_fd</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_event_set_watchdog</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_event_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_event_now</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    for more information about the functions available.</para>
+
+    <para>The event loop design is targeted on running a separate
+    instance of the event loop in each thread; it has no concept of
+    distributing events from a single event loop instance onto
+    multiple worker threads. Dispatching events is strictly ordered
+    and subject to configurable priorities. In each event loop
+    iteration a single event source is dispatched. Each time an event
+    source is dispatched the kernel is polled for new events, before
+    the next event source is dispatched. The event loop is designed to
+    honour priorities and provide fairness within each priority. It is
+    not designed to provide optimal throughput, as this contradicts
+    these goals due the limitations of the underlying <citerefentry
+    project='man-pages'><refentrytitle>epoll</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+    primitives.</para>
+
+    <para>The event loop implementation provides the following features:</para>
+
+    <orderedlist>
+      <listitem><para>I/O event sources, based on <citerefentry
+      project='man-pages'><refentrytitle>epoll</refentrytitle><manvolnum>7</manvolnum></citerefentry>'s
+      file descriptor watching, including edge triggered events (<constant>EPOLLET</constant>). See <citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para></listitem>
+
+      <listitem><para>Timer event sources, based on <citerefentry
+      project='man-pages'><refentrytitle>timerfd_create</refentrytitle><manvolnum>2</manvolnum></citerefentry>,
+      supporting the <constant>CLOCK_MONOTONIC</constant>,
+      <constant>CLOCK_REALTIME</constant>,
+      <constant>CLOCK_BOOTIME</constant> clocks, as well as the
+      <constant>CLOCK_REALTIME_ALARM</constant> and
+      <constant>CLOCK_BOOTTIME_ALARM</constant> clocks that can resume
+      the system from suspend. When creating timer events a required
+      accuracy parameter may be specified which allows coalescing of
+      timer events to minimize power consumption. See <citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para></listitem>
+
+      <listitem><para>UNIX process signal events, based on
+      <citerefentry
+      project='man-pages'><refentrytitle>signalfd</refentrytitle><manvolnum>2</manvolnum></citerefentry>,
+      including full support for real-time signals, and queued parameters. See <citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para></listitem>
+
+      <listitem><para>Child process state change events, based on
+      <citerefentry project='man-pages'><refentrytitle>waitid</refentrytitle><manvolnum>2</manvolnum></citerefentry>. See <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para></listitem>
+
+      <listitem><para>Static event sources, of three types: defer,
+      post and exit, for invoking calls in each event loop, after
+      other event sources or at event loop termination. See
+      <citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para></listitem>
+
+      <listitem><para>Event sources may be assigned a 64bit priority
+      value, that controls the order in which event sources are
+      dispatched if multiple are pending simultanously. See
+      <citerefentry><refentrytitle>sd_event_source_set_priority</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para></listitem>
+
+      <listitem><para>The event loop may automatically send watchdog
+      notification messages to the service manager. See
+      <citerefentry><refentrytitle>sd_event_set_watchdog</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para></listitem>
+
+      <listitem><para>The event loop may be integrated into foreign
+      event loops, such as the GLib one. See
+      <citerefentry><refentrytitle>sd_event_get_fd</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+      for an example.</para></listitem>
+    </orderedlist>
+
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_run</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_source_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_source_set_priority</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_source_set_userdata</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_source_get_event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_source_get_pending</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_source_set_description</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_source_set_prepare</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_wait</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_get_fd</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_set_watchdog</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_now</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry project='man-pages'><refentrytitle>epoll</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
+      <citerefentry project='man-pages'><refentrytitle>timerfd_create</refentrytitle><manvolnum>2</manvolnum></citerefentry>,
+      <citerefentry project='man-pages'><refentrytitle>signalfd</refentrytitle><manvolnum>2</manvolnum></citerefentry>,
+      <citerefentry project='man-pages'><refentrytitle>waitid</refentrytitle><manvolnum>2</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
index 77bec4e706aa6da0931c1566be49c5a295acd7f2..d4b180cf03c591c49c1dcbffce92ce63abec6ae7 100644 (file)
@@ -21,7 +21,7 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 -->
 
-<refentry id="sd_event_add_child">
+<refentry id="sd_event_add_child" xmlns:xi="http://www.w3.org/2001/XInclude">
 
   <refentryinfo>
     <title>sd_event_add_child</title>
   <refnamediv>
     <refname>sd_event_add_child</refname>
     <refname>sd_event_source_get_child_pid</refname>
+    <refname>sd_event_child_handler_t</refname>
 
-    <refpurpose>Add a child state change event source to an event loop</refpurpose>
+    <refpurpose>Add a child process state change event source to an event loop</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
     <funcsynopsis>
-      <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+      <funcsynopsisinfo>#include &lt;systemd/sd-event.h&gt;</funcsynopsisinfo>
+
+      <funcsynopsisinfo><token>typedef</token> struct sd_event_source sd_event_source;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>typedef int (*<function>sd_event_child_handler_t</function>)</funcdef>
+        <paramdef>sd_event_source *<parameter>s</parameter></paramdef>
+        <paramdef>const siginfo_t *<parameter>si</parameter></paramdef>
+        <paramdef>void *<parameter>userdata</parameter></paramdef>
+      </funcprototype>
 
       <funcprototype>
         <funcdef>int <function>sd_event_add_child</function></funcdef>
         <paramdef>void *<parameter>userdata</parameter></paramdef>
       </funcprototype>
 
-      <funcprototype>
-        <funcdef>typedef int (*<function>sd_event_child_handler_t</function>)</funcdef>
-        <paramdef>sd_event_source *<parameter>s</parameter></paramdef>
-        <paramdef>const siginfo_t *<parameter>si</parameter></paramdef>
-        <paramdef>void *<parameter>userdata</parameter></paramdef>
-      </funcprototype>
-
       <funcprototype>
         <funcdef>int <function>sd_event_source_get_child_pid</function></funcdef>
         <paramdef>sd_event_source *<parameter>source</parameter></paramdef>
     <title>Description</title>
 
     <para><function>sd_event_add_child()</function> adds a new child
-    state change event source to an event loop object. The event loop
-    is specified in <parameter>event</parameter>, the event source is
-    returned in the <parameter>source</parameter> parameter. The
-    <parameter>pid</parameter> parameter specifies the process to
-    watch. The <parameter>handler</parameter> must reference a
-    function to call when the process changes state. The handler
-    function will be passed the <parameter>userdata</parameter>
-    pointer, which may be chosen freely by the caller. The handler
-    also receives a pointer to a <structname>const
-    siginfo_t</structname> structure containing the information about
-    the event. The <parameter>options</parameter> parameter determines
-    which state changes will be watched for. It must contain an OR-ed
-    mask of <constant>WEXITED</constant> (watch for the child
+    process state change event source to an event loop. The event loop
+    object is specified in the <parameter>event</parameter> parameter,
+    the event source object is returned in the
+    <parameter>source</parameter> parameter. The
+    <parameter>pid</parameter> parameter specifies the PID of the
+    process to watch. The <parameter>handler</parameter> must
+    reference a function to call when the process changes state. The
+    handler function will be passed the
+    <parameter>userdata</parameter> pointer, which may be chosen
+    freely by the caller. The handler also receives a pointer to a
+    <structname>siginfo_t</structname> structure containing
+    information about the child process event. The
+    <parameter>options</parameter> parameter determines which state
+    changes will be watched for. It must contain an OR-ed mask of
+    <constant>WEXITED</constant> (watch for the child process
     terminating), <constant>WSTOPPED</constant> (watch for the child
-    being stopped by a signal), and <constant>WCONTINUED</constant>
-    (watch for the child being resumed by a signal). See
-    <citerefentry><refentrytitle>waitid</refentrytitle><manvolnum>2</manvolnum></citerefentry>
+    process being stopped by a signal), and
+    <constant>WCONTINUED</constant> (watch for the child process being
+    resumed by a signal). See <citerefentry
+    project='man-pages'><refentrytitle>waitid</refentrytitle><manvolnum>2</manvolnum></citerefentry>
     for further information.</para>
 
     <para>Only a single handler may be installed for a specific
-    child. The handler is enabled
-    for a single event (<constant>SD_EVENT_ONESHOT</constant>),
-    but this may be
-    changed with
+    child process. The handler is enabled for a single event
+    (<constant>SD_EVENT_ONESHOT</constant>), but this may be changed
+    with
     <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
     If the handler function returns a negative error code, it will be
-    disabled after the invocation, even if
-    <constant>SD_EVENT_ON</constant> mode is set.
+    disabled after the invocation, even if the
+    <constant>SD_EVENT_ON</constant> mode was requested before.
     </para>
 
+    <para>To destroy an event source object use
+    <citerefentry><refentrytitle>sd_event_source_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    but note that the event source is only removed from the event loop
+    when all references to the event source are dropped. To make sure
+    an event source does not fire anymore, even when there's still a
+    reference to it kept, consider setting the event source to
+    <constant>SD_EVENT_OFF</constant> with
+    <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
+
+    <para>If the the second parameter of
+    <function>sd_event_add_child()</function> is passed as NULL no
+    reference to the event source object is returned. In this case the
+    event source is considered "floating", and will be destroyed
+    implicitly when the event loop itself is destroyed.</para>
+
+    <para>Note that the <parameter>handler</parameter> function is
+    invoked at a time where the child process is not reaped yet (and
+    thus still is exposed as a zombie process by the kernel). However,
+    the child will be reaped automatically after the function
+    returns. Child processes for which no child process state change
+    event sources are installed will not be reaped by the event loop
+    implementation.</para>
+
+    <para>If both a child process state change event source and a
+    <constant>SIGCHLD</constant> signal event source is installed in
+    the same event loop, the configured event source priorities decide
+    which event source is dispatched first. If the signal handler is
+    processed first, it should leave the child processes for which
+    child process state change event sources are installed unreaped.</para>
+
     <para><function>sd_event_source_get_child_pid()</function>
-    retrieves the configured <parameter>pid</parameter> of a child
-    state change event source created previously with
+    retrieves the configured PID of a child process state change event
+    source created previously with
     <function>sd_event_add_child()</function>. It takes the event
     source object as the <parameter>source</parameter> parameter and a
-    pointer to <type>pid_t</type> to return the result in.
+    pointer to a <type>pid_t</type> variable to return the process ID
+    in.
     </para>
   </refsect1>
 
         <term><constant>-EBUSY</constant></term>
 
         <listitem><para>A handler is already installed for this
-        child.</para></listitem>
+        child process.</para></listitem>
 
       </varlistentry>
 
 
       </varlistentry>
 
-    </variablelist>
-  </refsect1>
+      <varlistentry>
+        <term><constant>-EDOM</constant></term>
 
-  <refsect1>
-    <title>Notes</title>
+        <listitem><para>The passed event source is not a child process event source.</para></listitem>
+      </varlistentry>
 
-    <para><function>sd_event_add_child()</function> and the other functions
-    described here are available as a shared library, which can be
-    compiled and linked to with the
-    <constant>libsystemd</constant> <citerefentry project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry>
-    file.</para>
+    </variablelist>
   </refsect1>
 
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
   <refsect1>
     <title>See Also</title>
 
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_event_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_now</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+      <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_source_set_priority</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_source_set_userdata</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_source_set_description</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry project='man-pages'><refentrytitle>waitid</refentrytitle><manvolnum>2</manvolnum></citerefentry>
     </para>
   </refsect1>
 
index 826f2fd224a5654083b5ad3e19e329b23f363402..6a13ede76e69d4f08df019a7d7da58c17bb72712 100644 (file)
@@ -21,7 +21,7 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 -->
 
-<refentry id="sd_event_add_defer">
+<refentry id="sd_event_add_defer" xmlns:xi="http://www.w3.org/2001/XInclude">
 
   <refentryinfo>
     <title>sd_event_add_defer</title>
     <refname>sd_event_add_defer</refname>
     <refname>sd_event_add_post</refname>
     <refname>sd_event_add_exit</refname>
+    <refname>sd_event_handler_t</refname>
 
     <refpurpose>Add static event sources to an event loop</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
     <funcsynopsis>
-      <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+      <funcsynopsisinfo>#include &lt;systemd/sd-event.h&gt;</funcsynopsisinfo>
+
+      <funcsynopsisinfo><token>typedef</token> struct sd_event_source sd_event_source;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>typedef int (*<function>sd_event_handler_t</function>)</funcdef>
+        <paramdef>sd_event_source *<parameter>s</parameter></paramdef>
+        <paramdef>void *<parameter>userdata</parameter></paramdef>
+      </funcprototype>
 
       <funcprototype>
         <funcdef>int <function>sd_event_add_defer</function></funcdef>
         <paramdef>void *<parameter>userdata</parameter></paramdef>
       </funcprototype>
 
-      <funcprototype>
-        <funcdef>typedef int (*<function>sd_event_handler_t</function>)</funcdef>
-        <paramdef>sd_event_source *<parameter>s</parameter></paramdef>
-        <paramdef>void *<parameter>userdata</parameter></paramdef>
-      </funcprototype>
-
     </funcsynopsis>
   </refsynopsisdiv>
 
   <refsect1>
     <title>Description</title>
 
-    <para>These three functions add new event sources to an event loop
-    object. The event loop is specified in
-    <parameter>event</parameter>, the event source is returned in the
-    <parameter>source</parameter> parameter. The event sources are
-    enabled statically and will "fire" when the event loop is run and
-    the conditions described below are met. The handler function will
-    be passed the <parameter>userdata</parameter> pointer, which may
-    be chosen freely by the caller.</para>
+    <para>These three functions add new static event sources to an
+    event loop. The event loop object is specified in the
+    <parameter>event</parameter> parameter, the event source object is
+    returned in the <parameter>source</parameter> parameter. The event
+    sources are enabled statically and will "fire" when the event loop
+    is run and the conditions described below are met. The handler
+    function will be passed the <parameter>userdata</parameter>
+    pointer, which may be chosen freely by the caller.</para>
 
     <para><function>sd_event_add_defer()</function> adds a new event
-    source that will "fire" the next time the event loop is run. By
-    default, the handler will be called once
-    (<constant>SD_EVENT_ONESHOT</constant>).</para>
+    source that will be dispatched instantly, before the event loop
+    goes to sleep again and waits for new events. By default, the
+    handler will be called once
+    (<constant>SD_EVENT_ONESHOT</constant>). Note that if the event
+    source is set to <constant>SD_EVENT_ON</constant> the event loop
+    will never go to sleep again, but continously call the handler,
+    possibly interleaved with other event sources.</para>
 
     <para><function>sd_event_add_post()</function> adds a new event
-    source that will "fire" if any event handlers are invoked whenever
-    the event loop is run. By default, the source is enabled
-    permanently (<constant>SD_EVENT_ON</constant>).</para>
+    source that is run before the event loop will sleep and wait
+    for new events, but only after at least one other non-post event
+    source was dispatched. By default, the source is enabled
+    permanently (<constant>SD_EVENT_ON</constant>). Note that this
+    event source type will still allow the event loop to go to sleep
+    again, even if set to <constant>SD_EVENT_ON</constant>, as long as
+    no other event source is ever triggered.</para>
 
     <para><function>sd_event_add_exit()</function> adds a new event
-    source that will "fire" when the event loop is terminated
-    with <function>sd_event_exit()</function>.</para>
+    source that will be dispatched when the event loop is terminated
+    with <citerefentry><refentrytitle>sd_event_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
 
     <para>The
     <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     function may be used to enable the event source permanently
     (<constant>SD_EVENT_ON</constant>) or to make it fire just once
-    (<constant>SD_EVENT_ONESHOT</constant>). If the handler function
-    returns a negative error code, it will be disabled after the
-    invocation, even if <constant>SD_EVENT_ON</constant> mode is
-    set.</para>
+    (<constant>SD_EVENT_ONESHOT</constant>).</para>
+
+    <para>If the handler function returns a negative error code, it
+    will be disabled after the invocation, even if the
+    <constant>SD_EVENT_ON</constant> mode was requested before.</para>
+
+    <para>To destroy an event source object use
+    <citerefentry><refentrytitle>sd_event_source_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    but note that the event source is only removed from the event loop
+    when all references to the event source are dropped. To make sure
+    an event source does not fire anymore, even when there's still a
+    reference to it kept, consider setting the event source to
+    <constant>SD_EVENT_OFF</constant> with
+    <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
+
+    <para>If the second parameter of these functions is passed as
+    NULL no reference to the event source object is returned. In this
+    case the event source is considered "floating", and will be
+    destroyed implicitly when the event loop itself is
+    destroyed.</para>
   </refsect1>
 
   <refsect1>
     </variablelist>
   </refsect1>
 
-  <refsect1>
-    <title>Notes</title>
-
-    <para>Functions described here are available as a shared library,
-    which can be compiled and linked to with the
-    <constant>libsystemd</constant> <citerefentry
-    project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry>
-    file.</para>
-  </refsect1>
+  <xi:include href="libsystemd-pkgconfig.xml" />
 
   <refsect1>
     <title>See Also</title>
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_event_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_now</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+      <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_source_set_priority</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_source_set_userdata</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_source_set_description</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     </para>
   </refsect1>
 
diff --git a/man/sd_event_add_io.xml b/man/sd_event_add_io.xml
new file mode 100644 (file)
index 0000000..4cc0428
--- /dev/null
@@ -0,0 +1,300 @@
+<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<!--
+  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/>.
+-->
+
+<refentry id="sd_event_add_io" xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sd_event_add_io</title>
+    <productname>systemd</productname>
+
+    <authorgroup>
+      <author>
+        <contrib>Developer</contrib>
+        <firstname>Lennart</firstname>
+        <surname>Poettering</surname>
+        <email>lennart@poettering.net</email>
+      </author>
+    </authorgroup>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_event_add_io</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_event_add_io</refname>
+    <refname>sd_event_source_get_io_events</refname>
+    <refname>sd_event_source_set_io_events</refname>
+    <refname>sd_event_source_get_io_revents</refname>
+    <refname>sd_event_source_get_io_fd</refname>
+    <refname>sd_event_source_set_io_fd</refname>
+    <refname>sd_event_source</refname>
+    <refname>sd_event_io_handler_t</refname>
+
+    <refpurpose>Add an I/O event source to an event loop</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-event.h&gt;</funcsynopsisinfo>
+
+      <funcsynopsisinfo><token>typedef</token> struct sd_event_source sd_event_source;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>typedef int (*<function>sd_event_io_handler_t</function>)</funcdef>
+        <paramdef>sd_event_source *<parameter>s</parameter></paramdef>
+        <paramdef>int <parameter>fd</parameter></paramdef>
+        <paramdef>uint32_t <parameter>revents</parameter></paramdef>
+        <paramdef>void *<parameter>userdata</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_event_add_io</function></funcdef>
+        <paramdef>sd_event *<parameter>event</parameter></paramdef>
+        <paramdef>sd_event_source **<parameter>source</parameter></paramdef>
+        <paramdef>int <parameter>fd</parameter></paramdef>
+        <paramdef>uint32_t <parameter>events</parameter></paramdef>
+        <paramdef>sd_event_io_handler_t <parameter>handler</parameter></paramdef>
+        <paramdef>void *<parameter>userdata</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_event_source_get_io_events</function></funcdef>
+        <paramdef>sd_event_source *<parameter>source</parameter></paramdef>
+        <paramdef>uint32_t *<parameter>events</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_event_source_set_io_events</function></funcdef>
+        <paramdef>sd_event_source *<parameter>source</parameter></paramdef>
+        <paramdef>uint32_t <parameter>events</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_event_source_get_io_revents</function></funcdef>
+        <paramdef>sd_event_source *<parameter>source</parameter></paramdef>
+        <paramdef>uint32_t *<parameter>revents</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_event_source_get_io_fd</function></funcdef>
+        <paramdef>sd_event_source *<parameter>source</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_event_source_set_io_fd</function></funcdef>
+        <paramdef>sd_event_source *<parameter>source</parameter></paramdef>
+        <paramdef>int <parameter>fd</parameter></paramdef>
+      </funcprototype>
+
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_event_add_io()</function> adds a new I/O event
+    source to an event loop. The event loop object is specified in the
+    <parameter>event</parameter> parameter, the event source object is
+    returned in the <parameter>source</parameter> parameter. The
+    <parameter>fd</parameter> parameter takes the UNIX file descriptor
+    to watch, which may refer to a socket, a FIFO, a message queue, a
+    serial connection, a character device or any other file descriptor
+    compatible with Linux <citerefentry
+    project='man-pages'><refentrytitle>epoll</refentrytitle><manvolnum>7</manvolnum></citerefentry>. The
+    <parameter>events</parameter> parameter takes a bit mask of I/O
+    events to watch the file descriptor for, a combination of the
+    following event flags: <constant>EPOLLIN</constant>,
+    <constant>EPOLLOUT</constant>, <constant>EPOLLRDHUP</constant>,
+    <constant>EPOLLPRI</constant> and <constant>EPOLLET</constant>,
+    see <citerefentry
+    project='man-pages'><refentrytitle>epoll_ctl</refentrytitle><manvolnum>2</manvolnum></citerefentry>
+    for details. The <parameter>handler</parameter> shall reference a
+    function to call when the I/O event source is triggered. The
+    handler function will be passed the
+    <parameter>userdata</parameter> pointer, which may be chosen
+    freely by the caller. The handler will also be passed the file
+    descriptor the event was seen on as well as the actual event flags
+    seen. It's generally a subset of the events watched, however may
+    additionally have <constant>EPOLLERR</constant> and
+    <constant>EPOLLHUP</constant> set.</para>
+
+    <para>By default, the I/O event source will stay enabled
+    continously (<constant>SD_EVENT_ON</constant>), but this may be
+    changed with
+    <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+    If the handler function returns a negative error code, it will be
+    disabled after the invocation, even if the
+    <constant>SD_EVENT_ON</constant> mode was requested before. Note
+    that an I/O event source set to <constant>SD_EVENT_ON</constant> will
+    fire continously unless data is read or written to the file
+    descriptor in order to reset the mask of events seen.
+    </para>
+
+    <para>Setting the I/O event mask to watch for to 0 does not mean
+    that the event source won't be triggered anymore, as
+    <constant>EPOLLHUP</constant> and <constant>EPOLLERR</constant>
+    may be triggered even with a zero event mask. To temporarily
+    disable an I/O event source use
+    <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    with <constant>SD_EVENT_OFF</constant> instead.</para>
+
+    <para>To destroy an event source object use
+    <citerefentry><refentrytitle>sd_event_source_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    but note that the event source is only removed from the event loop
+    when all references to the event source are dropped. To make sure
+    an event source does not fire anymore, even when there's still a
+    reference to it kept, consider setting the event source to
+    <constant>SD_EVENT_OFF</constant> with
+    <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
+
+    <para>If the the second parameter of
+    <function>sd_event_add_io()</function> is passed as NULL no
+    reference to the event source object is returned. In this case the
+    event source is considered "floating", and will be destroyed
+    implicitly when the event loop itself is destroyed.</para>
+
+    <para>It is recommended to use
+    <function>sd_event_add_io()</function> only in conjunction with
+    file descriptors that have <constant>O_NONBLOCK</constant> set, to
+    ensure that all I/O operations from invoked handlers are properly
+    asynchronous and non-blocking. Using file descriptors without
+    <constant>O_NONBLOCK</constant> might result in unexpected
+    starving of other event sources. See <citerefentry
+    project='man-pages'><refentrytitle>fcntl</refentrytitle><manvolnum>2</manvolnum></citerefentry>
+    for details on enabling <constant>O_NONBLOCK</constant> mode.</para>
+
+    <para><function>sd_event_source_get_io_events()</function> retrieves
+    the configured I/O event mask to watch of an I/O event source created
+    previously with <function>sd_event_add_io()</function>. It takes
+    the event source object and a pointer to a variable to store the
+    event mask in.</para>
+
+    <para><function>sd_event_source_set_io_events()</function> changes the
+    configured I/O event mask to watch of an I/O event source created previously
+    with <function>sd_event_add_io()</function>. It takes the event
+    source object and the new event mask to set.</para>
+
+    <para><function>sd_event_source_get_io_revents()</function>
+    retrieves the I/O event mask of currently seen but undispatched
+    events from an I/O event source created previously with
+    <function>sd_event_add_io()</function>. It takes the event source
+    object and a pointer to a variable to store the event mask
+    in. When called from a handler function on the handler's event
+    source object this will return the same mask as passed to the
+    handler's <parameter>revents</parameter> parameter. This call is
+    primarily useful to check for undispatched events of an event
+    source from the handler of an unrelated (possibly higher priority)
+    event source. Note the relation between
+    <function>sd_event_source_get_pending()</function> and
+    <function>sd_event_source_get_io_revents()</function>: both
+    functions will report non-zero results when there's an event
+    pending for the event source, but the former applies to all event
+    source types, the latter only to I/O event sources.</para>
+
+    <para><function>sd_event_source_get_io_fd()</function> retrieves
+    the UNIX file descriptor of an I/O event source created previously
+    with <function>sd_event_add_io()</function>. It takes the event
+    source object and returns the positive file descriptor in the return
+    value, or a negative error number on error (see below).</para>
+
+    <para><function>sd_event_source_set_io_fd()</function>
+    changes the UNIX file descriptor of an I/O event source created
+    previously with <function>sd_event_add_io()</function>. It takes
+    the event source object and the new file descriptor to set.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success, these functions return 0 or a positive
+    integer. On failure, they return a negative errno-style error
+    code. </para>
+  </refsect1>
+
+  <refsect1>
+    <title>Errors</title>
+
+    <para>Returned errors may indicate the following problems:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><constant>-ENOMEM</constant></term>
+
+        <listitem><para>Not enough memory to allocate an object.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>-EINVAL</constant></term>
+
+        <listitem><para>An invalid argument has been passed.</para></listitem>
+
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>-ESTALE</constant></term>
+
+        <listitem><para>The event loop is already terminated.</para></listitem>
+
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>-ECHILD</constant></term>
+
+        <listitem><para>The event loop has been created in a different process.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>-EDOM</constant></term>
+
+        <listitem><para>The passed event source is not an I/O event source.</para></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_now</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_source_set_priority</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_source_set_userdata</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_source_set_description</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_source_get_pending</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry project='man-pages'><refentrytitle>epoll_ctl</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry project='man-pages'><refentrytitle>epoll</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
index 0923fe0ae7942b8b63cf6eff69b4d13179dcfb84..b5312735d27b8bf2359b06dc05e7b98752bc7655 100644 (file)
@@ -21,7 +21,7 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 -->
 
-<refentry id="sd_event_add_signal">
+<refentry id="sd_event_add_signal" xmlns:xi="http://www.w3.org/2001/XInclude">
 
   <refentryinfo>
     <title>sd_event_add_signal</title>
   <refnamediv>
     <refname>sd_event_add_signal</refname>
     <refname>sd_event_source_get_signal</refname>
+    <refname>sd_event_signal_handler_t</refname>
 
-    <refpurpose>Add a signal event source to an event loop</refpurpose>
+    <refpurpose>Add a UNIX process signal event source to an event
+    loop</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
     <funcsynopsis>
-      <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+      <funcsynopsisinfo>#include &lt;systemd/sd-event.h&gt;</funcsynopsisinfo>
+
+      <funcsynopsisinfo><token>typedef</token> struct sd_event_source sd_event_source;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>typedef int (*<function>sd_event_signal_handler_t</function>)</funcdef>
+        <paramdef>sd_event_source *<parameter>s</parameter></paramdef>
+        <paramdef>const struct signalfd_siginfo *<parameter>si</parameter></paramdef>
+        <paramdef>void *<parameter>userdata</parameter></paramdef>
+      </funcprototype>
 
       <funcprototype>
         <funcdef>int <function>sd_event_add_signal</function></funcdef>
         <paramdef>void *<parameter>userdata</parameter></paramdef>
       </funcprototype>
 
-      <funcprototype>
-        <funcdef>typedef int (*<function>sd_event_signal_handler_t</function>)</funcdef>
-        <paramdef>sd_event_source *<parameter>s</parameter></paramdef>
-        <paramdef>const struct signalfd_siginfo *<parameter>si</parameter></paramdef>
-        <paramdef>void *<parameter>userdata</parameter></paramdef>
-      </funcprototype>
-
       <funcprototype>
         <funcdef>int <function>sd_event_source_get_signal</function></funcdef>
         <paramdef>sd_event_source *<parameter>source</parameter></paramdef>
   <refsect1>
     <title>Description</title>
 
-    <para><function>sd_event_add_signal()</function> adds a new signal
-    event source to an event loop object. The event loop is specified
-    in <parameter>event</parameter>, and the event source is returned in
-    the <parameter>source</parameter> parameter. The
-    <parameter>signal</parameter> parameter specifies the signal to be handled
-    (see
-    <citerefentry project='man-pages'><refentrytitle>signal</refentrytitle><manvolnum>7</manvolnum></citerefentry>).
-    The <parameter>handler</parameter> must reference a function to
-    call when the signal is delivered or be <constant>NULL</constant>.
-    The handler function will be passed the
-    <parameter>userdata</parameter> pointer, which may be chosen
+    <para><function>sd_event_add_signal()</function> adds a new UNIX
+    process signal event source to an event loop. The event loop
+    object is specified in the <parameter>event</parameter> parameter,
+    and the event source object is returned in the
+    <parameter>source</parameter> parameter. The
+    <parameter>signal</parameter> parameter specifies the numeric
+    signal to be handled (see <citerefentry
+    project='man-pages'><refentrytitle>signal</refentrytitle><manvolnum>7</manvolnum></citerefentry>).
+    The <parameter>handler</parameter> parameter must reference a
+    function to call when the signal is received or be
+    <constant>NULL</constant>.  The handler function will be passed
+    the <parameter>userdata</parameter> pointer, which may be chosen
     freely by the caller. The handler also receives a pointer to a
-    <structname>const struct signalfd_siginfo</structname> containing
-    the information about the received signal. See
-    <citerefentry project='man-pages'><refentrytitle>signalfd</refentrytitle><manvolnum>2</manvolnum></citerefentry>
+    <structname>signalfd_siginfo</structname> structure containing
+    information about the received signal. See <citerefentry
+    project='man-pages'><refentrytitle>signalfd</refentrytitle><manvolnum>2</manvolnum></citerefentry>
     for further information.</para>
 
     <para>Only a single handler may be installed for a specific
-    signal. The signal will be unblocked, and must be
-    blocked when the function is called. If the handler is not
-    specified (<parameter>handler</parameter> is
+    signal. The signal will be unblocked by this call, and must be
+    blocked before this function is called in all threads (using
+    <citerefentry
+    project='man-pages'><refentrytitle>sigprocmask</refentrytitle><manvolnum>2</manvolnum></citerefentry>). If
+    the handler is not specified (<parameter>handler</parameter> is
     <constant>NULL</constant>), a default handler which causes the
-    program to exit will be used.  By default, the handler is enabled
-    permanently (<constant>SD_EVENT_ON</constant>), but this may be
-    changed with
+    program to exit cleanly will be used.</para>
+
+    <para>By default, the event source is enabled permanently
+    (<constant>SD_EVENT_ON</constant>), but this may be changed with
     <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
     If the handler function returns a negative error code, it will be
-    disabled after the invocation, even if
-    <constant>SD_EVENT_ON</constant> mode is set.
+    disabled after the invocation, even if the
+    <constant>SD_EVENT_ON</constant> mode was requested before.
     </para>
 
+    <para>To destroy an event source object use
+    <citerefentry><refentrytitle>sd_event_source_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    but note that the event source is only removed from the event loop
+    when all references to the event source are dropped. To make sure
+    an event source does not fire anymore, even when there's still a
+    reference to it kept, consider setting the event source to
+    <constant>SD_EVENT_OFF</constant> with
+    <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
+
+    <para>If the the second parameter of
+    <function>sd_event_add_signal()</function> is passed as NULL no
+    reference to the event source object is returned. In this case the
+    event source is considered "floating", and will be destroyed
+    implicitly when the event loop itself is destroyed.</para>
+
     <para><function>sd_event_source_get_signal()</function> retrieves
-    the configured signal number of a signal event source created
-    previously with <function>sd_event_add_signal()</function>. It
-    takes the event source object as the <parameter>source</parameter>
+    the configured UNIX process signal number of a signal event source
+    created previously with
+    <function>sd_event_add_signal()</function>. It takes the event
+    source object as the <parameter>source</parameter>
     parameter.</para>
 
   </refsect1>
 
       </varlistentry>
 
-    </variablelist>
-  </refsect1>
+      <varlistentry>
+        <term><constant>-EDOM</constant></term>
 
-  <refsect1>
-    <title>Notes</title>
+        <listitem><para>The passed event source is not a signal event source.</para></listitem>
+      </varlistentry>
 
-    <para><function>sd_event_add_signal()</function> and the other functions
-    described here are available as a shared library, which can be
-    compiled and linked to with the
-    <constant>libsystemd</constant> <citerefentry project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry>
-    file.</para>
+    </variablelist>
   </refsect1>
 
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
   <refsect1>
     <title>See Also</title>
 
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_event_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_now</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+      <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_source_set_description</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_source_set_userdata</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry project='man-pages'><refentrytitle>signal</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
+      <citerefentry project='man-pages'><refentrytitle>signalfd</refentrytitle><manvolnum>2</manvolnum></citerefentry>
     </para>
   </refsect1>
 
index c5f7aee19d728a1e4844499ed33ee1e2858f2f0d..df38f52fc9721a06d088917090c3d57505280054 100644 (file)
@@ -21,7 +21,7 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 -->
 
-<refentry id="sd_event_add_time">
+<refentry id="sd_event_add_time" xmlns:xi="http://www.w3.org/2001/XInclude">
 
   <refentryinfo>
     <title>sd_event_add_time</title>
     <refname>sd_event_source_get_time_accuracy</refname>
     <refname>sd_event_source_set_time_accuracy</refname>
     <refname>sd_event_source_get_time_clock</refname>
+    <refname>sd_event_time_handler_t</refname>
 
     <refpurpose>Add a timer event source to an event loop</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
     <funcsynopsis>
-      <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+      <funcsynopsisinfo>#include &lt;systemd/sd-event.h&gt;</funcsynopsisinfo>
+
+      <funcsynopsisinfo><token>typedef</token> struct sd_event_source sd_event_source;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>typedef int (*<function>sd_event_time_handler_t</function>)</funcdef>
+        <paramdef>sd_event_source *<parameter>s</parameter></paramdef>
+        <paramdef>uint64_t <parameter>usec</parameter></paramdef>
+        <paramdef>void *<parameter>userdata</parameter></paramdef>
+      </funcprototype>
 
       <funcprototype>
         <funcdef>int <function>sd_event_add_time</function></funcdef>
         <paramdef>void *<parameter>userdata</parameter></paramdef>
       </funcprototype>
 
-      <funcprototype>
-        <funcdef>typedef int (*<function>sd_event_time_handler_t</function>)</funcdef>
-        <paramdef>sd_event_source *<parameter>s</parameter></paramdef>
-        <paramdef>uint64_t <parameter>usec</parameter></paramdef>
-        <paramdef>void *<parameter>userdata</parameter></paramdef>
-      </funcprototype>
-
       <funcprototype>
         <funcdef>int <function>sd_event_source_get_time</function></funcdef>
         <paramdef>sd_event_source *<parameter>source</parameter></paramdef>
-        <paramdef>usec_t *<parameter>usec</parameter></paramdef>
+        <paramdef>uint64_t *<parameter>usec</parameter></paramdef>
       </funcprototype>
 
       <funcprototype>
         <funcdef>int <function>sd_event_source_set_time</function></funcdef>
         <paramdef>sd_event_source *<parameter>source</parameter></paramdef>
-        <paramdef>usec_t <parameter>usec</parameter></paramdef>
+        <paramdef>uint64_t <parameter>usec</parameter></paramdef>
       </funcprototype>
 
       <funcprototype>
         <funcdef>int <function>sd_event_source_get_time_accuracy</function></funcdef>
         <paramdef>sd_event_source *<parameter>source</parameter></paramdef>
-        <paramdef>usec_t *<parameter>usec</parameter></paramdef>
+        <paramdef>uint64_t *<parameter>usec</parameter></paramdef>
       </funcprototype>
 
       <funcprototype>
         <funcdef>int <function>sd_event_source_set_time_accuracy</function></funcdef>
         <paramdef>sd_event_source *<parameter>source</parameter></paramdef>
-        <paramdef>usec_t <parameter>usec</parameter></paramdef>
+        <paramdef>uint64_t <parameter>usec</parameter></paramdef>
       </funcprototype>
 
       <funcprototype>
     <title>Description</title>
 
     <para><function>sd_event_add_time()</function> adds a new timer
-    event source to an event loop object. The event loop is specified
-    in <parameter>event</parameter>, the event source is returned in
-    the <parameter>source</parameter> parameter. The
-    <parameter>clock</parameter> parameter takes a clock identifier,
-    one of <constant>CLOCK_REALTIME</constant>,
-    <constant>CLOCK_MONOTONIC</constant> and
+    event source to an event loop. The event loop object is specified
+    in the <parameter>event</parameter> parameter, the event source
+    object is returned in the <parameter>source</parameter>
+    parameter. The <parameter>clock</parameter> parameter takes a
+    clock identifier, one of <constant>CLOCK_REALTIME</constant>,
+    <constant>CLOCK_MONOTONIC</constant>,
+    <constant>CLOCK_BOOTTIME</constant>,
+    <constant>CLOCK_REALTIME_ALARM</constant> or
     <constant>CLOCK_BOOTTIME_ALARM</constant>. See
     <citerefentry><refentrytitle>timerfd_create</refentrytitle><manvolnum>2</manvolnum></citerefentry>
     for details regarding the various types of clocks. The
     <parameter>usec</parameter> parameter takes a time value in
-    microseconds, relative to the clock's epoch specifying when the
-    timer shall elapse the earliest. The
+    microseconds (µs), relative to the clock's epoch, specifying when
+    the timer shall elapse the earliest. If a time that already lies
+    in the past is specified (including 0), the timer source is
+    dispatched immediately in the next event loop iterations. The
     <parameter>accuracy</parameter> parameter takes an additional
-    accuracy value in microseconds specifying a time the timer event
-    may be delayed. Specify 0 for selecting the default accuracy
-    (250ms). Specify 1 for most accurate timers. Consider specifying
-    60000000 or larger (1h) for long-running events that may be
+    accuracy value in µs specifying a time the timer event may be
+    delayed. Specify 0 for selecting the default accuracy
+    (250ms). Specify 1µs for most accurate timers. Consider specifying
+    60000000µs or larger (1min) for long-running events that may be
     delayed substantially. Picking higher accuracy values allows the
     system to coalesce timer events more aggressively, thus improving
-    power efficiency. The <parameter>handler</parameter> shall
-    reference a function to call when the timer elapses. The handler
-    function will be passed the <parameter>userdata</parameter>
-    pointer, which may be chosen freely by the caller. The handler is
-    also passed the configured time it was triggered, however it might
-    actually have been called at a slightly later time, subject to the
-    specified accuracy value, the kernel timer slack (see
+    power efficiency. The <parameter>handler</parameter> parameter
+    shall reference a function to call when the timer elapses. The
+    handler function will be passed the
+    <parameter>userdata</parameter> pointer, which may be chosen
+    freely by the caller. The handler is also passed the configured
+    time it was triggered, however it might actually have been called
+    at a slightly later time, subject to the specified accuracy value,
+    the kernel timer slack (see
     <citerefentry><refentrytitle>prctl</refentrytitle><manvolnum>2</manvolnum></citerefentry>)
-    and additional scheduling latencies.</para>
+    and additional scheduling latencies. To query the actual time the
+    handler was called use
+    <citerefentry><refentrytitle>sd_event_now</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
 
     <para>By default, the timer will elapse once
     (<constant>SD_EVENT_ONESHOT</constant>), but this may be changed
     with
     <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
     If the handler function returns a negative error code, it will be
-    disabled after the invocation, even if
-    <constant>SD_EVENT_ON</constant> mode is set.
+    disabled after the invocation, even if the
+    <constant>SD_EVENT_ON</constant> mode was requested before. Note
+    that a timer event set to <constant>SD_EVENT_ON</constant> will
+    fire continously unless its configured time is updated using
+    <function>sd_event_source_set_time()</function>.
     </para>
 
+    <para>To destroy an event source object use
+    <citerefentry><refentrytitle>sd_event_source_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    but note that the event source is only removed from the event loop
+    when all references to the event source are dropped. To make sure
+    an event source does not fire anymore, even when there's still a
+    reference to it kept, consider setting the event source to
+    <constant>SD_EVENT_OFF</constant> with
+    <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
+
+    <para>If the the second parameter of
+    <function>sd_event_add_time()</function> is passed as NULL no
+    reference to the event source object is returned. In this case the
+    event source is considered "floating", and will be destroyed
+    implicitly when the event loop itself is destroyed.</para>
+
+    <para>If the <parameter>handler</parameter> to
+    <function>sd_event_add_time()</function> is passed as NULL, and
+    the event source fires, this will be considered a request to exit
+    the event loop. In this case, the <parameter>userdata</parameter>
+    parameter, cast to an integer is used for the exit code passed to
+    <citerefentry><refentrytitle>sd_event_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
+
+    <para>Use <constant>CLOCK_BOOTTIME_ALARM</constant> and
+    <constant>CLOCK_REALTIME_ALARM</constant> to define event sources
+    that may wake up the system from suspend.</para>
+
+    <para>In order to set up relative timers (that is, relative to the
+    current time), retrieve the current time via
+    <citerefentry><refentrytitle>sd_event_now</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    add the desired timespan to sleep to it, and pass the result as
+    the <parameter>usec</parameter> parameter to
+    <function>sd_event_add_time()</function>.</para>
+
+    <para>In order to set up repetitive timers (that is, timers that
+    are triggered in regular intervals), set up the timer normally,
+    for the first invocation. Each time the event handler is invoked,
+    update the timer's trigger time with
+    <citerefentry><refentrytitle>sd_event_source_set_time</refentrytitle><manvolnum>3</manvolnum></citerefentry> for the next timer
+    iteration, and reenable the timer using
+    <function>sd_event_source_set_enabled()</function>. To calculate
+    the next point in time to pass to
+    <function>sd_event_source_set_time()</function>, either use as
+    base the <parameter>usec</parameter> parameter passed to the timer
+    callback, or the timestamp returned by
+    <function>sd_event_now()</function>. In the former case timer
+    events will be regular, while in the latter case the scheduling
+    latency will keep accumulating on the timer.</para>
+
     <para><function>sd_event_source_get_time()</function> retrieves
     the configured time value of a timer event source created
     previously with <function>sd_event_add_time()</function>. It takes
     the event source object and a pointer to a variable to store the
-    time in microseconds in.</para>
+    time, relative to the selected clock's epoch, in µs in.</para>
 
     <para><function>sd_event_source_set_time()</function> changes the
     configured time value of a timer event source created previously
     with <function>sd_event_add_time()</function>. It takes the event
     source object and a time relative to the selected clock's
-    epoch, in microseconds.</para>
+    epoch, in µs.</para>
 
     <para><function>sd_event_source_get_time_accuracy()</function>
     retrieves the configured accuracy value of a timer event source
     created previously with <function>sd_event_add_time()</function>. It
     takes the event source object and a pointer to a variable to store
-    the accuracy in microseconds in.</para>
+    the accuracy in µs in.</para>
 
     <para><function>sd_event_source_set_time_accuracy()</function>
     changes the configured accuracy of a timer event source created
     previously with <function>sd_event_add_time()</function>. It takes
-    the event source object and an accuracy, in microseconds.</para>
+    the event source object and an accuracy, in µs.</para>
 
     <para><function>sd_event_source_get_time_clock()</function>
     retrieves the configured clock of a timer event source created
     previously with <function>sd_event_add_time()</function>. It takes
     the event source object and a pointer to a variable to store the
     clock identifier in.</para>
-
   </refsect1>
 
   <refsect1>
         <listitem><para>The selected clock is not supported by the event loop implementation.</para></listitem>
 
       </varlistentry>
-    </variablelist>
-  </refsect1>
 
-  <refsect1>
-    <title>Notes</title>
+      <varlistentry>
+        <term><constant>-EDOM</constant></term>
 
-    <para><function>sd_event_add_time()</function> and the other functions
-    described here are available as a shared library, which can be
-    compiled and linked to with the
-    <constant>libsystemd</constant> <citerefentry project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry>
-    file.</para>
+        <listitem><para>The passed event source is not a timer event source.</para></listitem>
+      </varlistentry>
+    </variablelist>
   </refsect1>
 
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
   <refsect1>
     <title>See Also</title>
 
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_event_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_now</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>clock_gettime</refentrytitle><manvolnum>2</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+      <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_source_set_priority</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_source_set_userdata</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_source_set_description</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry project='man-pages'><refentrytitle>clock_gettime</refentrytitle><manvolnum>2</manvolnum></citerefentry>,
+      <citerefentry project='man-pages'><refentrytitle>timerfd_create</refentrytitle><manvolnum>2</manvolnum></citerefentry>,
+      <citerefentry project='man-pages'><refentrytitle>prctl</refentrytitle><manvolnum>2</manvolnum></citerefentry>
     </para>
   </refsect1>
 
diff --git a/man/sd_event_exit.xml b/man/sd_event_exit.xml
new file mode 100644 (file)
index 0000000..4f34f3b
--- /dev/null
@@ -0,0 +1,163 @@
+<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<!--
+  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/>.
+-->
+
+<refentry id="sd_event_exit" xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sd_event_exit</title>
+    <productname>systemd</productname>
+
+    <authorgroup>
+      <author>
+        <contrib>Developer</contrib>
+        <firstname>Lennart</firstname>
+        <surname>Poettering</surname>
+        <email>lennart@poettering.net</email>
+      </author>
+    </authorgroup>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_event_exit</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_event_exit</refname>
+    <refname>sd_event_get_exit_code</refname>
+
+    <refpurpose>Ask the event loop to exit</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-event.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>int <function>sd_event_exit</function></funcdef>
+        <paramdef>sd_event *<parameter>event</parameter></paramdef>
+        <paramdef>int <parameter>code</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_event_get_exit_code</function></funcdef>
+        <paramdef>sd_event *<parameter>event</parameter></paramdef>
+        <paramdef>int *<parameter>code</parameter></paramdef>
+      </funcprototype>
+
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_event_exit()</function> requests the event loop
+    specified in the <parameter>event</parameter> event loop object to
+    exit. The <parameter>code</parameter> parameter may be any integer
+    value and is returned as-is by
+    <citerefentry><refentrytitle>sd_event_loop</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    after the last event loop iteration. It may also be be queried
+    using <function>sd_event_get_exit_code()</function>, see
+    below. </para>
+
+    <para>When exiting is requested the event loop will stop listening
+    for and dispatching regular event sources. Instead it will proceed
+    with executing only event sources registered with
+    <citerefentry><refentrytitle>sd_event_add_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    in the order defined by their priority. After all exit event
+    sources have been dispatched the event loop is terminated.</para>
+
+    <para>If <function>sd_event_exit()</function> is invoked a second
+    time while the event loop is still processing exit event sources,
+    the exit code stored in the event loop object is updated, but
+    otherwise no further operation is executed.</para>
+
+    <para><function>sd_event_get_exit_code()</function> may be used to
+    query the exit code passed into
+    <function>sd_event_exit()</function> earlier.</para>
+
+    <para>While the full positive and negative integer ranges may be used
+    for the exit code, care should be taken not pick exit codes that
+    conflict with regular exit codes returned by
+    <function>sd_event_loop()</function>, if these exit codes shall be
+    distinguishable.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success, <function>sd_event_exit()</function> and
+    <function>sd_event_get_exit_code()</function> return 0 or a positive
+    integer. On failure, they return a negative errno-style error
+    code.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Errors</title>
+
+    <para>Returned errors may indicate the following problems:</para>
+
+    <variablelist>
+
+      <varlistentry>
+        <term><constant>-EINVAL</constant></term>
+
+        <listitem><para>The event loop object or error code pointer are invalid.</para></listitem>
+
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>-ECHILD</constant></term>
+
+        <listitem><para>The event loop was created in a different process.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>-ESTALE</constant></term>
+
+        <listitem><para>The event loop has exited already and all exit handlers are already processed.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>-ENODATA</constant></term>
+
+        <listitem><para>The event loop has not been requested to exit yet.</para></listitem>
+      </varlistentry>
+
+    </variablelist>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
index ecdbe76ec411cfad61825c5303d07d10cb9d90d5..f68752dd0e8b5097df435c2cc70bb71052bd5d83 100644 (file)
@@ -21,8 +21,7 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 -->
 
-<refentry id="sd_event_get_fd"
-        xmlns:xi="http://www.w3.org/2001/XInclude">
+<refentry id="sd_event_get_fd" xmlns:xi="http://www.w3.org/2001/XInclude">
 
   <refentryinfo>
     <title>sd_event_get_fd</title>
 
   <refsynopsisdiv>
     <funcsynopsis>
-      <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+      <funcsynopsisinfo>#include &lt;systemd/sd-event.h&gt;</funcsynopsisinfo>
 
       <funcprototype>
         <funcdef>int <function>sd_event_get_fd</function></funcdef>
-        <paramdef>sd_bus *<parameter>event</parameter></paramdef>
+        <paramdef>sd_event *<parameter>event</parameter></paramdef>
       </funcprototype>
 
     </funcsynopsis>
     <title>Description</title>
 
     <para><function>sd_event_get_fd()</function> returns the file
-    descriptor that the event loop object returned by the
+    descriptor that an event loop object returned by the
     <citerefentry><refentrytitle>sd_event_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>
-    function uses to wait for events. This file descriptor can be
-    polled for events. This makes it possible to embed the
+    function uses to wait for events. This file descriptor may itself
+    be polled for
+    <constant>POLLIN</constant>/<constant>EPOLLIN</constant>
+    events. This makes it possible to embed an
     <citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>
-    event loop inside of another event loop.</para>
+    event loop into another, possibly foreign, event loop.</para>
+
+    <para>The returned file descriptor refers to an <citerefentry
+    project='man-pages'><refentrytitle>epoll</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+    object. It is recommended not to alter it by invoking
+    <citerefentry
+    project='man-pages'><refentrytitle>epoll_ctl</refentrytitle><manvolnum>2</manvolnum></citerefentry>
+    on it, in order to avoid interference with the event loop's inner
+    logic and assumptions.</para>
   </refsect1>
 
   <refsect1>
     <title>Return Value</title>
 
     <para>On success, <function>sd_event_get_fd()</function> returns a
-    non-negative integer. On failure, it returns a negative
+    non-negative file descriptor. On failure, it returns a negative
     errno-style error code.</para>
   </refsect1>
 
     <title>Examples</title>
 
     <example>
-      <title>Integration in glib event loop</title>
+      <title>Integration in the GLib event loop</title>
 
       <programlisting><xi:include href="glib-event-glue.c" parse="text" /></programlisting>
     </example>
   </refsect1>
 
-  <refsect1>
-    <title>Notes</title>
-
-    <para><function>sd_event_get_fd()</function> is available as a
-    shared library, which can be compiled and linked to with the
-    <constant>libsystemd</constant> <citerefentry
-    project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry>
-    file.</para>
-  </refsect1>
+  <xi:include href="libsystemd-pkgconfig.xml" />
 
   <refsect1>
     <title>See Also</title>
     <para>
       <citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_event_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_event_ref</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+      <citerefentry><refentrytitle>sd_event_wait</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry project='man-pages'><refentrytitle>epoll_ctl</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry project='man-pages'><refentrytitle>epoll</refentrytitle><manvolnum>7</manvolnum></citerefentry>
     </para>
   </refsect1>
 
index f6c5d3981485b9b9acba9e2f6449476b8eba4280..3356faa89943102b8777541820dedd09cc5c69b3 100644 (file)
@@ -21,7 +21,7 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 -->
 
-<refentry id="sd_event_new">
+<refentry id="sd_event_new" xmlns:xi="http://www.w3.org/2001/XInclude">
 
   <refentryinfo>
     <title>sd_event_new</title>
     <refname>sd_event_default</refname>
     <refname>sd_event_ref</refname>
     <refname>sd_event_unref</refname>
+    <refname>sd_event_get_tid</refname>
+    <refname>sd_event</refname>
 
     <refpurpose>Acquire and release an event loop object</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
     <funcsynopsis>
-      <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+      <funcsynopsisinfo>#include &lt;systemd/sd-event.h&gt;</funcsynopsisinfo>
+
+      <funcsynopsisinfo><token>typedef</token> struct sd_event sd_event;</funcsynopsisinfo>
 
       <funcprototype>
         <funcdef>int <function>sd_event_new</function></funcdef>
-        <paramdef>sd_bus **<parameter>event</parameter></paramdef>
+        <paramdef>sd_event **<parameter>event</parameter></paramdef>
       </funcprototype>
 
       <funcprototype>
         <funcdef>int <function>sd_event_default</function></funcdef>
-        <paramdef>sd_bus **<parameter>event</parameter></paramdef>
+        <paramdef>sd_event **<parameter>event</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>sd_event *<function>sd_event_ref</function></funcdef>
+        <paramdef>sd_event *<parameter>event</parameter></paramdef>
       </funcprototype>
 
       <funcprototype>
-        <funcdef>sd_bus *<function>sd_event_ref</function></funcdef>
-        <paramdef>sd_bus *<parameter>event</parameter></paramdef>
+        <funcdef>sd_event *<function>sd_event_unref</function></funcdef>
+        <paramdef>sd_event *<parameter>event</parameter></paramdef>
       </funcprototype>
 
       <funcprototype>
-        <funcdef>sd_bus *<function>sd_event_unref</function></funcdef>
-        <paramdef>sd_bus *<parameter>event</parameter></paramdef>
+        <funcdef>int <function>sd_event_get_tid</function></funcdef>
+        <paramdef>sd_event *<parameter>event</parameter></paramdef>
+        <paramdef>pid_t *<parameter>tid</parameter></paramdef>
       </funcprototype>
 
     </funcsynopsis>
     thread. All threads have exactly either zero or one default event loop
     objects associated, but never more.</para>
 
+    <para>After allocating an event loop object, add event sources to
+    it with
+    <citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    or
+    <citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    and then execute the event loop using
+    <citerefentry><refentrytitle>sd_event_run</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
+
     <para><function>sd_event_ref()</function> increases the reference
     count of the specified event loop object by one.</para>
 
     distinct objects. Note that, in order to free an event loop object,
     all remaining event sources of the event loop also need to be
     freed as each keeps a reference to it.</para>
+
+    <para>Both <function>sd_event_ref()</function> and
+    <function>sd_event_unref()</function> execute no operation if the
+    passed in event loop object is <constant>NULL</constant>.</para>
+
+    <para><function>sd_event_get_tid()</function> retrieves the thread
+    identifier ("TID") of the thread the specified event loop object
+    is associated with. This call is only supported for event loops
+    allocated with <function>sd_event_default()</function>, and
+    returns the identifier for the thread the event loop is the
+    default event loop of. See <citerefentry
+    project='man-pages'><refentrytitle>gettid</refentrytitle><manvolnum>2</manvolnum></citerefentry>
+    for more information on thread identifiers.</para>
   </refsect1>
 
   <refsect1>
         <listitem><para>The maximum number of event loops has been allocated.</para></listitem>
 
       </varlistentry>
-    </variablelist>
-  </refsect1>
 
-  <refsect1>
-    <title>Notes</title>
+      <varlistentry>
+        <term><constant>-ENXIO</constant></term>
 
-    <para><function>sd_event_new()</function> and the other functions
-    described here are available as a shared library, which can be
-    compiled and linked to with the
-    <constant>libsystemd</constant> <citerefentry project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry>
-    file.</para>
+        <listitem><para><function>sd_event_get_tid()</function> was
+        invoked on an event loop object that was not allocated with
+        <function>sd_event_default()</function>.</para></listitem>
+      </varlistentry>
+
+    </variablelist>
   </refsect1>
 
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
   <refsect1>
     <title>See Also</title>
 
       <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_event_add_post</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_event_add_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+      <citerefentry><refentrytitle>sd_event_add_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_run</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry project='man-pages'><refentrytitle>gettid</refentrytitle><manvolnum>2</manvolnum></citerefentry>
     </para>
   </refsect1>
 
diff --git a/man/sd_event_now.xml b/man/sd_event_now.xml
new file mode 100644 (file)
index 0000000..f577e44
--- /dev/null
@@ -0,0 +1,141 @@
+<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<!--
+  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/>.
+-->
+
+<refentry id="sd_event_now" xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sd_event_now</title>
+    <productname>systemd</productname>
+
+    <authorgroup>
+      <author>
+        <contrib>Developer</contrib>
+        <firstname>Lennart</firstname>
+        <surname>Poettering</surname>
+        <email>lennart@poettering.net</email>
+      </author>
+    </authorgroup>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_event_now</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_event_now</refname>
+
+    <refpurpose>Retrieve current event loop iteration timestamp</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-event.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>int <function>sd_event_now</function></funcdef>
+        <paramdef>sd_event *<parameter>event</parameter></paramdef>
+        <paramdef>clockid_t <parameter>clock</parameter></paramdef>
+        <paramdef>uint64_t *<parameter>usec</parameter></paramdef>
+      </funcprototype>
+
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_event_now()</function> returns the timestamp
+    the most recent event loop iteration began. This timestamp is
+    taken right after after returning from the event sleep, and before
+    dispatching any event sources. The <parameter>event</parameter>
+    parameter takes the even loop object to retrieve the timestamp
+    from. The <parameter>clock</parameter> parameter specifies the clock to
+    retrieve the timestamp for, and is one of
+    <constant>CLOCK_REALTIME</constant> (or its equivalent
+    <constant>CLOCK_REALTIME_ALARM</constant>),
+    <constant>CLOCK_MONOTONIC</constant> or
+    <constant>CLOCK_BOOTTIME</constant> (or its equivalent
+    <constant>CLOCK_BOOTTIME_ALARM</constant>), see <citerefentry
+    project='man-pages'><refentrytitle>clock_gettime</refentrytitle><manvolnum>2</manvolnum></citerefentry>
+    for more information on the various clocks. The retrieved
+    timestamp is stored in the <parameter>usec</parameter> parameter,
+    in µs since the clock's epoch. If this function is invoked before
+    the first event loop iteration the current time is returned, as
+    reported by <function>clock_gettime()</function>. To distinguish
+    this case from a regular invocation the return value will be
+    positive non-zero in this case, while it is zero when the returned
+    timestamp refers to the actual event loop iteration.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>If the first event loop iteration has not run yet
+    <function>sd_event_now()</function> returns the requested
+    timestamp in <parameter>usec</parameter> and returns a positive,
+    non-zero return value. Otherwise, on success it will return the
+    iteration's timestamp in <parameter>usec</parameter> and 0 as
+    return value. On failure, the call returns a negative errno-style
+    error code.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Errors</title>
+
+    <para>Returned errors may indicate the following problems:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><constant>-EINVAL</constant></term>
+
+        <listitem><para>An invalid parameter was
+        passed.</para></listitem>
+
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>-ECHILD</constant></term>
+
+        <listitem><para>The event loop object was created in a
+        different process.</para></listitem>
+      </varlistentry>
+
+    </variablelist>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry project='man-pages'><refentrytitle>clock_gettime</refentrytitle><manvolnum>2</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
index 06236fcd1ad25e69f6fc04fb7b63e2dc44bd9a65..5b68959165d9280de8121b4f5c6bc2fb88f05c67 100644 (file)
@@ -21,7 +21,7 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 -->
 
-<refentry id="sd_event_run">
+<refentry id="sd_event_run" xmlns:xi="http://www.w3.org/2001/XInclude">
 
   <refentryinfo>
     <title>sd_event_run</title>
@@ -46,7 +46,7 @@
     <refname>sd_event_run</refname>
     <refname>sd_event_loop</refname>
 
-    <refpurpose>Run the libsystemd event loop</refpurpose>
+    <refpurpose>Run an event loop</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
@@ -56,7 +56,7 @@
       <funcprototype>
         <funcdef>int <function>sd_event_run</function></funcdef>
         <paramdef>sd_event *<parameter>event</parameter></paramdef>
-        <paramdef>uint64_t <parameter>timeout</parameter></paramdef>
+        <paramdef>uint64_t <parameter>usec</parameter></paramdef>
       </funcprototype>
 
       <funcprototype>
   <refsect1>
     <title>Description</title>
 
-    <para><function>sd_event_run()</function> can be used to run one
-    iteration of the event loop of libsystemd. This function waits
-    until an event to process is available, and dispatches a handler
-    for it. The <parameter>timeout</parameter> parameter specifices the
-    maximum time (in microseconds) to wait. <constant>(uint64_t)
-    -1</constant> may be used to specify an infinite timeout.</para>
-
-    <para><function>sd_event_loop</function> runs
-    <function>sd_event_wait</function> in a loop with a timeout of
-    infinity. This makes it suitable for the main event loop of a
-    program.</para>
+    <para><function>sd_event_run()</function> may be used to run a single
+    iteration of the event loop specified in the
+    <parameter>event</parameter> parameter. The function waits until an event to
+    process is available, and dispatches the registered handler for
+    it. The <parameter>usec</parameter> parameter specifies the
+    maximum time (in microseconds) to wait for an event. Use
+    <constant>(uint64_t) -1</constant> to specify an infinite
+    timeout.</para>
+
+    <para><function>sd_event_loop()</function> invokes
+    <function>sd_event_run()</function> in a loop, thus implementing
+    the actual event loop. The call returns as soon as exiting was
+    requested using
+    <citerefentry><refentrytitle>sd_event_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
 
     <para>The event loop object <parameter>event</parameter> is
     created with
-    <function>sd_event_new</function>.
-    Events to wait for and their handlers can be registered with
-    <function>sd_event_add_time</function>,
-    <function>sd_event_add_child</function>,
-    <function>sd_event_add_signal</function>,
-    <function>sd_event_add_defer</function>,
-    <function>sd_event_add_exit</function>,
+    <citerefentry><refentrytitle>sd_event_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+    Events sources to wait for and their handlers may be registered
+    with
+    <citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_event_add_post</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     and
-    <function>sd_event_add_post</function>.
+    <citerefentry><refentrytitle>sd_event_add_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
     </para>
 
-    <para>For more fine-grained control,
-    <function>sd_event_prepare</function>,
-    <function>sd_event_wait</function>, and
-    <function>sd_event_dispatch</function> may be used. Along with
-    <function>sd_event_get_fd</function>, those functions make it
-    possible to integrate the libsystemd loop inside of another event
-    loop.</para>
+    <para>For low-level control of event loop execution, use
+    <citerefentry><refentrytitle>sd_event_prepare</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_event_wait</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    and
+    <citerefentry><refentrytitle>sd_event_dispatch</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    which are wrapped by <function>sd_event_run()</function>. Along
+    with
+    <citerefentry><refentrytitle>sd_event_get_fd</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    these functions allow integration of an
+    <citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    event loop into foreign event loop implementations.</para>
   </refsect1>
 
   <refsect1>
     <title>Return Value</title>
 
-    <para>On success, these functions return 0 or a positive integer.
-    On failure, they return a negative errno-style error code.
-    <function>sd_event_run</function> returns 0 if the event loop is
-    finished, and a positive value if it can be continued.</para>
+    <para>On failure, these functions return a negative errno-style
+    error code. <function>sd_event_run()</function> returns a
+    positive, non-zero integer if an event source was dispatched, and
+    zero when the specified timeout hit before an event source has
+    seen any event, and hence no event source was
+    dispatched. <function>sd_event_loop()</function> returns the exit
+    code specified when invoking
+    <function>sd_event_exit()</function>.</para>
   </refsect1>
 
   <refsect1>
         <term><constant>-EINVAL</constant></term>
 
         <listitem><para>The <parameter>event</parameter> parameter is
-        <constant>NULL</constant>.</para></listitem>
+        invalid or <constant>NULL</constant>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
     <para>Other errors are possible, too.</para>
   </refsect1>
 
-  <refsect1>
-    <title>Notes</title>
-
-    <para><function>sd_event_run()</function> and
-    <function>sd_event_loop()</function> are available
-    as a shared library, which can be compiled and linked to with the
-    <constant>libsystemd</constant> <citerefentry project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry>
-    file.</para>
-  </refsect1>
+  <xi:include href="libsystemd-pkgconfig.xml" />
 
   <refsect1>
     <title>See Also</title>
     <para>
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_event_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_event_wait</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_event_add_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_event_add_post</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_get_fd</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_wait</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <ulink url="https://developer.gnome.org/glib/unstable/glib-The-Main-Event-Loop.html">GLib Main Event Loop</ulink>.
     </para>
   </refsect1>
diff --git a/man/sd_event_set_name.xml b/man/sd_event_set_name.xml
deleted file mode 100644 (file)
index 1471e12..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
-<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
-"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
-
-<!--
-  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/>.
--->
-
-<refentry id="sd_event_set_name"
-        xmlns:xi="http://www.w3.org/2001/XInclude">
-
-  <refentryinfo>
-    <title>sd_event_set_name</title>
-    <productname>systemd</productname>
-
-    <authorgroup>
-      <author>
-        <contrib>More text</contrib>
-        <firstname>Zbigniew</firstname>
-        <surname>Jędrzejewski-Szmek</surname>
-        <email>zbyszek@in.waw.pl</email>
-      </author>
-    </authorgroup>
-  </refentryinfo>
-
-  <refmeta>
-    <refentrytitle>sd_event_set_name</refentrytitle>
-    <manvolnum>3</manvolnum>
-  </refmeta>
-
-  <refnamediv>
-    <refname>sd_event_set_name</refname>
-    <refname>sd_event_get_name</refname>
-
-    <refpurpose>Set human-readable names for event sources</refpurpose>
-  </refnamediv>
-
-  <refsynopsisdiv>
-    <funcsynopsis>
-      <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
-
-      <funcprototype>
-        <funcdef>int <function>sd_event_set_name</function></funcdef>
-        <paramdef>sd_event_source *<parameter>source</parameter></paramdef>
-        <paramdef>const char *<parameter>name</parameter></paramdef>
-      </funcprototype>
-
-      <funcprototype>
-        <funcdef>int <function>sd_event_get_name</function></funcdef>
-        <paramdef>sd_event_source *<parameter>source</parameter></paramdef>
-        <paramdef>const char **<parameter>name</parameter></paramdef>
-      </funcprototype>
-
-    </funcsynopsis>
-  </refsynopsisdiv>
-
-  <refsect1>
-    <title>Description</title>
-
-    <para><function>sd_event_set_name()</function> can be used to set
-    an arbitrary name for the event source
-    <parameter>source</parameter>. This name will be used in error
-    messages generated by
-    <citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>
-    for this source. The <parameter>name</parameter> must point
-    to a <constant>NUL</constant>-terminated string or be
-    <constant>NULL</constant>. In the latter case, the name will be
-    unset. The string is copied internally, so the
-    <parameter>name</parameter> argument is not referenced after the
-    function returns.</para>
-
-    <para><function>sd_event_set_name()</function> can be used to
-    query the current name assigned to source
-    <parameter>source</parameter>. It returns a pointer to the current
-    name (possibly <constant>NULL</constant>) in
-    <parameter>name</parameter>.</para>
-  </refsect1>
-
-  <refsect1>
-    <title>Return Value</title>
-
-    <para>On success, <function>sd_event_set_name()</function> and
-    <function>sd_event_get_name()</function> return a
-    non-negative integer. On failure, they return a negative
-    errno-style error code.</para>
-  </refsect1>
-
-  <refsect1>
-    <title>Errors</title>
-
-    <para>Returned errors may indicate the following problems:</para>
-
-    <variablelist>
-      <varlistentry>
-        <term><constant>-EINVAL</constant></term>
-
-        <listitem><para><parameter>source</parameter> is not a valid
-        pointer to an <structname>sd_event_source</structname>
-        structure or the <parameter>name</parameter> argument for
-        <function>sd_event_get_name()</function> is
-        <constant>NULL</constant>.</para></listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term><constant>-ENOMEM</constant></term>
-
-        <listitem><para>Not enough memory to copy the
-        name.</para></listitem>
-      </varlistentry>
-    </variablelist>
-  </refsect1>
-
-  <refsect1>
-    <title>Notes</title>
-
-    <para>The functions described here are available as a
-    shared library, which can be compiled and linked to with the
-    <constant>libsystemd</constant> <citerefentry
-    project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry>
-    file.</para>
-  </refsect1>
-
-  <refsect1>
-    <title>See Also</title>
-
-    <para>
-      <citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_event_run</refentrytitle><manvolnum>3</manvolnum></citerefentry>
-    </para>
-  </refsect1>
-
-</refentry>
diff --git a/man/sd_event_set_watchdog.xml b/man/sd_event_set_watchdog.xml
new file mode 100644 (file)
index 0000000..cbc5bc0
--- /dev/null
@@ -0,0 +1,177 @@
+<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<!--
+  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/>.
+-->
+
+<refentry id="sd_event_set_watchdog" xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sd_event_set_watchdog</title>
+    <productname>systemd</productname>
+
+    <authorgroup>
+      <author>
+        <contrib>Developer</contrib>
+        <firstname>Lennart</firstname>
+        <surname>Poettering</surname>
+        <email>lennart@poettering.net</email>
+      </author>
+    </authorgroup>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_event_set_watchdog</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_event_set_watchdog</refname>
+    <refname>sd_event_get_watchdog</refname>
+
+    <refpurpose>Enable event loop watchdog support</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-event.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>int <function>sd_event_set_watchdog</function></funcdef>
+        <paramdef>sd_event *<parameter>event</parameter></paramdef>
+        <paramdef>int b</paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_event_get_watchdog</function></funcdef>
+        <paramdef>sd_event *<parameter>event</parameter></paramdef>
+      </funcprototype>
+
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_event_set_watchdog()</function> may be used to
+    enable or disable automatic watchdog notification support in the
+    event loop object specified in the <parameter>event</parameter>
+    parameter. Specifically, depending on the <parameter>b</parameter>
+    boolean argument this will make sure the event loop wakes up in
+    regular intervals and sends watchdog notification messages to the
+    service manager, if this was requested by the service
+    manager. Watchdog support is determined with
+    <citerefentry><refentrytitle>sd_watchdog_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    and watchdog messages are sent with
+    <citerefentry><refentrytitle>sd_notify</refentrytitle><manvolnum>3</manvolnum></citerefentry>. See
+    the <varname>WatchdogSec=</varname> setting in
+    <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    for details on how to enable watchdog support for a service and
+    the protocol used. The wake-up interval is chosen as half the
+    watchdog timeout declared by the service manager via the
+    <varname>$WATCHDOG_USEC</varname> environment variable. If the
+    service manager did not request watchdog notifications, or if the
+    process was not invoked by the service manager this call with a
+    true <parameter>b</parameter> parameter executes no
+    operation. Passing a false <parameter>b</parameter> parameter will
+    disable the automatic sending of watchdog notification messages if
+    it was enabled before. Newly allocated event loop objects have
+    this feature disabled.</para>
+
+    <para>The first watchdog notification message is sent immediately
+    when <function>set_event_set_watchdog()</function> is invoked with
+    a true <parameter>b</parameter> parameter.</para>
+
+    <para>The watchdog logic is designed to allow the service manager
+    to automatically detect services that ceased processing of
+    incoming events, and thus appear "hung". Watchdog notifications
+    are sent out only at the beginning of each event loop
+    iteration. If an event source dispatch function blocks for an
+    excessively long time and does not return execution to the event
+    loop quickly, this might hence cause the notification message to
+    be delayed, and possibly result in abnormal program termination,
+    as configured in the service unit file.</para>
+
+    <para><function>sd_event_get_watchdog()</function> may be used to
+    determine whether watchdog support was previously requested by a
+    call to <function>sd_event_set_watchdog()</function> with a true
+    <parameter>b</parameter> parameter and successfully
+    enabled.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success, <function>sd_event_set_watchdog()</function> and
+    <function>sd_event_get_watchdog()</function> return a non-zero
+    positive integer if the service manager requested watchdog support
+    and watchdog support was successfully enabled. They return zero if
+    the service manager did not request watchdog support, or if
+    watchdog support was explicitly disabled with a false
+    <parameter>b</parameter> parameter. On failure, they return a
+    negative errno-style error
+    code.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Errors</title>
+
+    <para>Returned errors may indicate the following problems:</para>
+
+    <variablelist>
+
+      <varlistentry>
+        <term><constant>-ECHILD</constant></term>
+
+        <listitem><para>The event loop has been created in a different process.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>-EINVAL</constant></term>
+
+        <listitem><para>The passed event loop object was invalid.</para></listitem>
+      </varlistentry>
+
+    </variablelist>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_post</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_watchdog_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_notify</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
diff --git a/man/sd_event_source_get_event.xml b/man/sd_event_source_get_event.xml
new file mode 100644 (file)
index 0000000..2fdbd41
--- /dev/null
@@ -0,0 +1,100 @@
+<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<!--
+  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/>.
+-->
+
+<refentry id="sd_event_source_get_event" xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sd_event_source_get_event</title>
+    <productname>systemd</productname>
+
+    <authorgroup>
+      <author>
+        <contrib>Developer</contrib>
+        <firstname>Lennart</firstname>
+        <surname>Poettering</surname>
+        <email>lennart@poettering.net</email>
+      </author>
+    </authorgroup>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_event_source_get_event</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_event_source_get_event</refname>
+
+    <refpurpose>Retrieve the event loop of an event source</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-event.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>sd_event* <function>sd_event_source_get_event</function></funcdef>
+        <paramdef>sd_event_source *<parameter>source</parameter></paramdef>
+      </funcprototype>
+
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_event_source_get_event()</function> may be used
+    to retrieve the event loop object the event source object specified
+    as <parameter>source</parameter> is associated with. The event
+    loop object is specified when creating an event source object with
+    calls such as
+    <citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    or
+    <citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success, <function>sd_event_source_get_event()</function>
+    returns the associated event loop object. On failure, it returns
+    NULL.</para>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_source_set_userdata</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
diff --git a/man/sd_event_source_get_pending.xml b/man/sd_event_source_get_pending.xml
new file mode 100644 (file)
index 0000000..1c06e81
--- /dev/null
@@ -0,0 +1,167 @@
+<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<!--
+  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/>.
+-->
+
+<refentry id="sd_event_source_get_pending" xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sd_event_source_get_pending</title>
+    <productname>systemd</productname>
+
+    <authorgroup>
+      <author>
+        <contrib>Developer</contrib>
+        <firstname>Lennart</firstname>
+        <surname>Poettering</surname>
+        <email>lennart@poettering.net</email>
+      </author>
+    </authorgroup>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_event_source_get_pending</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_event_source_get_pending</refname>
+
+    <refpurpose>Determine pending state of event sources</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-event.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>int <function>sd_event_source_get_pending</function></funcdef>
+        <paramdef>sd_event_source *<parameter>source</parameter></paramdef>
+      </funcprototype>
+
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_event_source_get_pending()</function> may be
+    used to determine whether the event source object specified as
+    <parameter>source</parameter> has seen events but has not been
+    dispatched yet (and thus is marked "pending").</para>
+
+    <para>Event source objects initially are not marked pending, when
+    they are created with calls such as
+    <citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    with the exception of those created with
+    <citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    which are immediately marked pending, and
+    <citerefentry><refentrytitle>sd_event_add_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    for which the "pending" concept is not defined. For details see
+    the respective manual pages.</para>
+
+    <para>In each event loop iteration one event source of those
+    marked pending is dispatched, in the order defined by the event
+    source priority, as set with
+    <citerefentry><refentrytitle>sd_event_source_set_priority</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
+
+    <para>For I/O event sources, as created with
+    <citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    the call
+    <citerefentry><refentrytitle>sd_event_get_io_revents</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    may be used to query the type of event pending in more
+    detail.</para>
+
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success,
+    <function>sd_event_source_get_pending()</function> returns an
+    integer greater than zero when the event source is marked pending,
+    and zero when the event source is not marked pending. On failure,
+    it returns a negative errno-style error code.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Errors</title>
+
+    <para>Returned errors may indicate the following problems:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><constant>-EINVAL</constant></term>
+
+        <listitem><para><parameter>source</parameter> is not a valid
+        pointer to an <structname>sd_event_source</structname>
+        object.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>-EDOM</constant></term>
+
+        <listitem><para><parameter>source</parameter> refers to an
+        event source object created with
+        <citerefentry><refentrytitle>sd_event_add_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>-ENOMEM</constant></term>
+
+        <listitem><para>Not enough memory.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>-ESTALE</constant></term>
+
+        <listitem><para>The event loop is already terminated.</para></listitem>
+
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>-ECHILD</constant></term>
+
+        <listitem><para>The event loop has been created in a different process.</para></listitem>
+
+      </varlistentry>
+
+    </variablelist>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_source_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
diff --git a/man/sd_event_source_set_description.xml b/man/sd_event_source_set_description.xml
new file mode 100644 (file)
index 0000000..b9488a6
--- /dev/null
@@ -0,0 +1,170 @@
+<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<!--
+  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/>.
+-->
+
+<refentry id="sd_event_source_set_description" xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sd_event_source_set_description</title>
+    <productname>systemd</productname>
+
+    <authorgroup>
+      <author>
+        <contrib>More text</contrib>
+        <firstname>Zbigniew</firstname>
+        <surname>Jędrzejewski-Szmek</surname>
+        <email>zbyszek@in.waw.pl</email>
+      </author>
+    </authorgroup>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_event_source_set_description</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_event_source_set_description</refname>
+    <refname>sd_event_source_get_description</refname>
+
+    <refpurpose>Set or retrieve descriptive names of event sources</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-event.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>int <function>sd_event_source_set_description</function></funcdef>
+        <paramdef>sd_event_source *<parameter>source</parameter></paramdef>
+        <paramdef>const char *<parameter>description</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_event_source_get_description</function></funcdef>
+        <paramdef>sd_event_source *<parameter>source</parameter></paramdef>
+        <paramdef>const char **<parameter>description</parameter></paramdef>
+      </funcprototype>
+
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_event_source_set_description()</function> may
+    be used to set an arbitrary descriptive name for the event source
+    object specified as <parameter>source</parameter>. This name will
+    be used in debugging messages generated by
+    <citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    for this event source, and may be queried using
+    <function>sd_event_source_get_description()</function> for
+    debugging purposes. The <parameter>description</parameter> parameter shall
+    point to a <constant>NUL</constant>-terminated string or be
+    <constant>NULL</constant>. In the latter case, the descriptive
+    name will be unset. The string is copied internally, hence the
+    <parameter>description</parameter> argument is not referenced
+    after the function returns.</para>
+
+    <para><function>sd_event_source_get_description()</function> may
+    be used to query the current descriptive name assigned to the
+    event source object <parameter>source</parameter>. It returns a
+    pointer to the current name in <parameter>description</parameter>,
+    stored in memory internal to the event source. The memory is
+    invalidated when the event source is destroyed or the descriptive
+    name is changed.</para>
+
+    <para>Event source objects generally have no description set when
+    they are created, except for UNIX signal event sources created
+    with
+    <citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    whose descriptive name is initialized to the signal's C constant
+    name (e.g. <literal>SIGINT</literal> or
+    <literal>SIGTERM</literal>).</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success, <function>sd_event_source_set_description()</function> and
+    <function>sd_event_source_get_description()</function> return a
+    non-negative integer. On failure, they return a negative
+    errno-style error code.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Errors</title>
+
+    <para>Returned errors may indicate the following problems:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><constant>-EINVAL</constant></term>
+
+        <listitem><para><parameter>source</parameter> is not a valid
+        pointer to an <structname>sd_event_source</structname>
+        object or the <parameter>description</parameter> argument for
+        <function>sd_event_source_get_description()</function> is
+        <constant>NULL</constant>.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>-ENOMEM</constant></term>
+
+        <listitem><para>Not enough memory to copy the
+        name.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>-ECHILD</constant></term>
+
+        <listitem><para>The event loop has been created in a different process.</para></listitem>
+
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>-ENXIO</constant></term>
+
+        <listitem><para>No name was set for the event
+        source.</para></listitem>
+      </varlistentry>
+
+    </variablelist>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_source_set_userdata</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
diff --git a/man/sd_event_source_set_enabled.xml b/man/sd_event_source_set_enabled.xml
new file mode 100644 (file)
index 0000000..74c02e8
--- /dev/null
@@ -0,0 +1,179 @@
+<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<!--
+  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/>.
+-->
+
+<refentry id="sd_event_source_set_enabled" xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sd_event_source_set_enabled</title>
+    <productname>systemd</productname>
+
+    <authorgroup>
+      <author>
+        <contrib>Developer</contrib>
+        <firstname>Lennart</firstname>
+        <surname>Poettering</surname>
+        <email>lennart@poettering.net</email>
+      </author>
+    </authorgroup>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_event_source_set_enabled</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_event_source_set_enabled</refname>
+    <refname>sd_event_source_get_enabled</refname>
+    <refname>SD_EVENT_ON</refname>
+    <refname>SD_EVENT_OFF</refname>
+    <refname>SD_EVENT_ONESHOT</refname>
+
+    <refpurpose>Enable or disable event sources</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-event.h&gt;</funcsynopsisinfo>
+
+      <funcsynopsisinfo><token>enum</token> {
+        <constant>SD_EVENT_OFF</constant> = 0,
+        <constant>SD_EVENT_ON</constant> = 1,
+        <constant>SD_EVENT_ONESHOT</constant> = -1,
+};</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>int <function>sd_event_source_set_enabled</function></funcdef>
+        <paramdef>sd_event_source *<parameter>source</parameter></paramdef>
+        <paramdef>int <parameter>enabled</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_event_source_get_enabled</function></funcdef>
+        <paramdef>sd_event_source *<parameter>source</parameter></paramdef>
+        <paramdef>int *<parameter>enabled</parameter></paramdef>
+      </funcprototype>
+
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_event_source_set_enabled()</function> may be
+    used to enable or disable the event source object specified as
+    <parameter>source</parameter>. The <parameter>enabled</parameter>
+    parameter takes one of <constant>SD_EVENT_ON</constant> (to
+    enable), <constant>SD_EVENT_OFF</constant> (to disable) or
+    <constant>SD_EVENT_ONESHOT</constant>. If invoked with
+    <constant>SD_EVENT_ONESHOT</constant> the event source will be
+    enabled but automatically reset to
+    <constant>SD_EVENT_OFF</constant> after the event source was
+    dispatched once.</para>
+
+    <para>Event sources that are disabled will not result in event
+    loop wakeups and will not be dispatched, until they are enabled
+    again.</para>
+
+    <para><function>sd_event_source_get_enabled()</function> may be
+    used to query whether the event source object
+    <parameter>source</parameter> is currently enabled or not. It
+    returns the enablement state in
+    <parameter>enabled</parameter>.</para>
+
+    <para>Event source objects are enabled when they are first created
+    with calls such as
+    <citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>. However,
+    depending on the event source type they are enabled continously
+    (<constant>SD_EVENT_ON</constant>) or only for a single invocation
+    of the event source handler
+    (<constant>SD_EVENT_ONESHOT</constant>). For details see the
+    respective manual pages.</para>
+
+    <para>As event source objects stay active and may be dispatched as
+    long as there is at least one reference to them, in many cases it
+    is a good idea to combine a call to
+    <citerefentry><refentrytitle>sd_event_source_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    with a prior call to
+    <function>sd_event_source_set_enabled()</function> with
+    <constant>SD_EVENT_OFF</constant>, to ensure the event source is
+    not dispatched again until all other remaining references are dropped.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success, <function>sd_event_source_set_enabled()</function> and
+    <function>sd_event_source_get_enabled()</function> return a
+    non-negative integer. On failure, they return a negative
+    errno-style error code.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Errors</title>
+
+    <para>Returned errors may indicate the following problems:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><constant>-EINVAL</constant></term>
+
+        <listitem><para><parameter>source</parameter> is not a valid
+        pointer to an <structname>sd_event_source</structname>
+        object.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>-ENOMEM</constant></term>
+
+        <listitem><para>Not enough memory.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>-ECHILD</constant></term>
+
+        <listitem><para>The event loop has been created in a different process.</para></listitem>
+
+      </varlistentry>
+
+    </variablelist>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_source_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
diff --git a/man/sd_event_source_set_prepare.xml b/man/sd_event_source_set_prepare.xml
new file mode 100644 (file)
index 0000000..7066a55
--- /dev/null
@@ -0,0 +1,171 @@
+<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<!--
+  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/>.
+-->
+
+<refentry id="sd_event_source_set_prepare" xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sd_event_source_set_prepare</title>
+    <productname>systemd</productname>
+
+    <authorgroup>
+      <author>
+        <contrib>Developer</contrib>
+        <firstname>Lennart</firstname>
+        <surname>Poettering</surname>
+        <email>lennart@poettering.net</email>
+      </author>
+    </authorgroup>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_event_source_set_prepare</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_event_source_set_prepare</refname>
+
+    <refpurpose>Set a preparation callback for event sources</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-event.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>int <function>sd_event_source_set_prepare</function></funcdef>
+        <paramdef>sd_event_source *<parameter>source</parameter></paramdef>
+        <paramdef>sd_event_handler_t <parameter>callback</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>typedef int (*<function>sd_event_handler_t</function>)</funcdef>
+        <paramdef>sd_event_source *<parameter>s</parameter></paramdef>
+        <paramdef>void *<parameter>userdata</parameter></paramdef>
+      </funcprototype>
+
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_event_source_set_prepare()</function> may be
+    used to set a prepartion callback for the event source object
+    specified as <parameter>source</parameter>. The callback function
+    specified as <parameter>callback</parameter> will be invoked
+    immediately before the event loop goes to sleep to wait for
+    incoming events. It is invoked with the user data pointer passed
+    when the event source was created. The callback function may be
+    used to reconfigure the precise events to wait for. If the
+    <parameter>callback</parameter> parameter is passed as NULL the
+    callback function is reset. </para>
+
+    <para>Event source objects have no preparation callback associated
+    when they are first created with calls such as
+    <citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>. Preparation
+    callback functions are supported for all event source types with
+    the exception of those created with
+    <citerefentry><refentrytitle>sd_event_add_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>. Preparation
+    callback functions are dispatched in the order indicated by the
+    event source's priority field, as set with
+    <citerefentry><refentrytitle>sd_event_source_set_priority</refentrytitle><manvolnum>3</manvolnum></citerefentry>. Preparation
+    callbacks of disabled event sources (see
+    <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>)
+    are not invoked.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success,
+    <function>sd_event_source_set_prepare()</function> returns a
+    non-negative integer. On failure, it returns a negative
+    errno-style error code.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Errors</title>
+
+    <para>Returned errors may indicate the following problems:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><constant>-EINVAL</constant></term>
+
+        <listitem><para><parameter>source</parameter> is not a valid
+        pointer to an <structname>sd_event_source</structname>
+        object.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>-ESTALE</constant></term>
+
+        <listitem><para>The event loop is already terminated.</para></listitem>
+
+      </varlistentry>
+      <varlistentry>
+        <term><constant>-ENOMEM</constant></term>
+
+        <listitem><para>Not enough memory.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>-ECHILD</constant></term>
+
+        <listitem><para>The event loop has been created in a different process.</para></listitem>
+
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>-EDOM</constant></term>
+
+        <listitem><para>The specified event source has been created
+        with
+        <citerefentry><refentrytitle>sd_event_add_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para></listitem>
+
+      </varlistentry>
+
+    </variablelist>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_source_set_priority</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_source_set_userdata</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
diff --git a/man/sd_event_source_set_priority.xml b/man/sd_event_source_set_priority.xml
new file mode 100644 (file)
index 0000000..cc0f5a0
--- /dev/null
@@ -0,0 +1,189 @@
+<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<!--
+  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/>.
+-->
+
+<refentry id="sd_event_source_set_priority" xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sd_event_source_set_priority</title>
+    <productname>systemd</productname>
+
+    <authorgroup>
+      <author>
+        <contrib>Developer</contrib>
+        <firstname>Lennart</firstname>
+        <surname>Poettering</surname>
+        <email>lennart@poettering.net</email>
+      </author>
+    </authorgroup>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_event_source_set_priority</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_event_source_set_priority</refname>
+    <refname>sd_event_source_get_priority</refname>
+    <refname>SD_EVENT_PRIORITY_IMPORTANT</refname>
+    <refname>SD_EVENT_PRIORITY_NORMAL</refname>
+    <refname>SD_EVENT_PRIORITY_IDLE</refname>
+
+    <refpurpose>Set or retrieve the priority of event sources</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-event.h&gt;</funcsynopsisinfo>
+
+      <funcsynopsisinfo><token>enum</token> {
+        <constant>SD_EVENT_SOURCE_IMPORTANT</constant> = -100,
+        <constant>SD_EVENT_SOURCE_NORMAL</constant> = 0,
+        <constant>SD_EVENT_SOURCE_IDLE</constant> = 100,
+};</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>int <function>sd_event_source_set_priority</function></funcdef>
+        <paramdef>sd_event_source *<parameter>source</parameter></paramdef>
+        <paramdef>int64_t <parameter>priority</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_event_source_get_priority</function></funcdef>
+        <paramdef>sd_event_source *<parameter>source</parameter></paramdef>
+        <paramdef>int64_t *<parameter>priority</parameter></paramdef>
+      </funcprototype>
+
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_event_source_set_priority()</function> may be
+    used to set the priority for the event source object specified as
+    <parameter>source</parameter>. The priority is specified as an
+    arbitrary signed 64bit integer. The priority is initialized to
+    <constant>SD_EVENT_PRIORITY_NORMAL</constant> (0) when the event
+    source is allocated with a call such as
+    <citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    or
+    <citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    and may be changed with this call. If multiple event sources have seen events at the same time, they are dispatched in the order indicated by the
+    event sources' priorities. Event sources with smaller priority
+    values are dispatched first. As well-known points of reference,
+    the constants <constant>SD_EVENT_PRIORITY_IMPORTANT</constant>
+    (-100), <constant>SD_EVENT_PRIORITY_NORMAL</constant> (0) and
+    <constant>SD_EVENT_PRIORITY_IDLE</constant> (100) may be used to
+    indicate event sources that shall be dispatched early, normally or
+    late. It is recommended to specify priorities based on these
+    definitions, and relative to them -- however, the full 64bit
+    signed integer range is available for ordering event
+    sources.</para>
+
+    <para>Priorities define the order in which event sources that have
+    seen events are dispatched. Care should be taken to ensure that
+    high-priority event sources (those with negative priority values
+    assigned) do not cause starvation of low-priority event sources
+    (those with positive priority values assigned).</para>
+
+    <para>The order in which event sources with the same priority are
+    dispatched is undefined, but the event loop generally tries to
+    dispatch them in the order it learnt about events on them. As the
+    backing kernel primitives do not provide accurate information
+    about the order in which events occured this is not necessarily
+    reliable. However, it is guaranteed that if events are seen on
+    multiple same-priority event sources at the same time, each one is
+    not dispatched again until all others have been dispatched
+    once. This behaviour guarantees that within each priority
+    particular event sources do not starve or dominate the event
+    loop.</para>
+
+    <para><function>sd_event_source_get_priority()</function> may be
+    used to query the current priority assigned to the event source
+    object <parameter>source</parameter>.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success,
+    <function>sd_event_source_set_priority()</function> and
+    <function>sd_event_source_get_priority()</function> return a
+    non-negative integer. On failure, they return a negative
+    errno-style error code.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Errors</title>
+
+    <para>Returned errors may indicate the following problems:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><constant>-EINVAL</constant></term>
+
+        <listitem><para><parameter>source</parameter> is not a valid
+        pointer to an <structname>sd_event_source</structname>
+        object.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>-ENOMEM</constant></term>
+
+        <listitem><para>Not enough memory.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>-ESTALE</constant></term>
+
+        <listitem><para>The event loop is already terminated.</para></listitem>
+
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>-ECHILD</constant></term>
+
+        <listitem><para>The event loop has been created in a different process.</para></listitem>
+
+      </varlistentry>
+
+    </variablelist>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
diff --git a/man/sd_event_source_set_userdata.xml b/man/sd_event_source_set_userdata.xml
new file mode 100644 (file)
index 0000000..533d491
--- /dev/null
@@ -0,0 +1,119 @@
+<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<!--
+  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/>.
+-->
+
+<refentry id="sd_event_source_set_userdata" xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sd_event_source_set_userdata</title>
+    <productname>systemd</productname>
+
+    <authorgroup>
+      <author>
+        <contrib>Developer</contrib>
+        <firstname>Lennart</firstname>
+        <surname>Poettering</surname>
+        <email>lennart@poettering.net</email>
+      </author>
+    </authorgroup>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_event_source_set_userdata</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_event_source_set_userdata</refname>
+    <refname>sd_event_source_get_userdata</refname>
+
+    <refpurpose>Set or retrieve user data pointer of event sources</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-event.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>void* <function>sd_event_source_set_userdata</function></funcdef>
+        <paramdef>sd_event_source *<parameter>source</parameter></paramdef>
+        <paramdef>void *<parameter>userdata</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>void* <function>sd_event_source_get_userdata</function></funcdef>
+        <paramdef>sd_event_source *<parameter>source</parameter></paramdef>
+      </funcprototype>
+
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_event_source_set_userdata()</function> may be
+    used to set an arbitrary user data pointer for the event source
+    object specified as <parameter>source</parameter>. The user data
+    pointer is usually specified when creating an event source object
+    with calls such as
+    <citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    or
+    <citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    and may be updated with this call. The user data pointer is also
+    passed to all handler callback functions associated with the event
+    source. The <parameter>userdata</parameter> parameter specifies
+    the new user data pointer to set, the function returns the
+    previous user data pointer. Note that <constant>NULL</constant> is
+    a valid user data pointer.</para>
+
+    <para><function>sd_event_source_get_userdata()</function> may be
+    used to query the current user data pointer assigned to the event
+    source object <parameter>source</parameter>.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success,
+    <function>sd_event_source_set_userdata()</function> and
+    <function>sd_event_source_get_userdata()</function> return the
+    previously set user data pointer. On failure, they return
+    NULL.</para>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_source_set_description</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
diff --git a/man/sd_event_source_unref.xml b/man/sd_event_source_unref.xml
new file mode 100644 (file)
index 0000000..579ec47
--- /dev/null
@@ -0,0 +1,125 @@
+<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<!--
+  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/>.
+-->
+
+<refentry id="sd_event_source_unref" xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sd_event_source_unref</title>
+    <productname>systemd</productname>
+
+    <authorgroup>
+      <author>
+        <contrib>Developer</contrib>
+        <firstname>Lennart</firstname>
+        <surname>Poettering</surname>
+        <email>lennart@poettering.net</email>
+      </author>
+    </authorgroup>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_event_source_unref</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_event_source_unref</refname>
+    <refname>sd_event_source_ref</refname>
+
+    <refpurpose>Increase or decrease event source reference counters</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-event.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>sd_event_source* <function>sd_event_source_unref</function></funcdef>
+        <paramdef>sd_event_source *<parameter>source</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>sd_event_source* <function>sd_event_source_ref</function></funcdef>
+        <paramdef>sd_event_source *<parameter>source</parameter></paramdef>
+      </funcprototype>
+
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_event_source_unref()</function> may be used to
+    decrement by one the reference counter of the event source object
+    specified as <parameter>source</parameter>. The reference counter
+    is initially set to one, when the event source is created with calls
+    such as
+    <citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    or
+    <citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>. When
+    the reference counter reaches zero it is removed from its event loop
+    object and destroyed.</para>
+
+    <para><function>sd_event_source_ref()</function> may be used
+    to increase by one the reference counter of the event source object
+    specified as <parameter>source</parameter>.</para>
+
+    <para>Both functions execute no operation if the passed event
+    source object is <constant>NULL</constant>.</para>
+
+    <para>Note that event source objects stay alive and may be
+    dispatched as long as they have a reference counter greater than
+    zero. In order to drop a reference of an event source and make
+    sure the associated event source handler function is not called
+    anymore it is recommended to combine a call of
+    <function>sd_event_source_unref()</function> with a prior call to
+    <function>sd_event_source_set_enabled()</function> with
+    <constant>SD_EVENT_OFF</constant>.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para><function>sd_event_source_unref()</function> always returns
+    <constant>NULL</constant>.
+    <function>sd_event_source_ref()</function> always returns the
+    event source object passed in.</para>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
index 7ca50aedf9b05d859db401f1c36de91f7dff82f8..1eefa807003acad4d81b8eb7e5671bddffc7fb34 100644 (file)
@@ -21,7 +21,7 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 -->
 
-<refentry id="sd_event_wait">
+<refentry id="sd_event_wait" xmlns:xi="http://www.w3.org/2001/XInclude">
 
   <refentryinfo>
     <title>sd_event_wait</title>
     <refname>sd_event_wait</refname>
     <refname>sd_event_prepare</refname>
     <refname>sd_event_dispatch</refname>
-
-    <refpurpose>Run parts of the libsystemd event loop</refpurpose>
+    <refname>sd_event_get_state</refname>
+    <refname>SD_EVENT_INITIAL</refname>
+    <refname>SD_EVENT_PREPARING</refname>
+    <refname>SD_EVENT_ARMED</refname>
+    <refname>SD_EVENT_PENDING</refname>
+    <refname>SD_EVENT_RUNNING</refname>
+    <refname>SD_EVENT_EXITING</refname>
+    <refname>SD_EVENT_FINISHED</refname>
+
+    <refpurpose>Low-level event loop operations</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
     <funcsynopsis>
       <funcsynopsisinfo>#include &lt;systemd/sd-event.h&gt;</funcsynopsisinfo>
 
+      <funcsynopsisinfo><token>enum</token> {
+        <constant>SD_EVENT_INITIAL</constant>,
+        <constant>SD_EVENT_PREPARING</constant>,
+        <constant>SD_EVENT_ARMED</constant>,
+        <constant>SD_EVENT_PENDING</constant>,
+        <constant>SD_EVENT_RUNNING</constant>,
+        <constant>SD_EVENT_EXITING</constant>,
+        <constant>SD_EVENT_FINISHED</constant>,
+};</funcsynopsisinfo>
+
       <funcprototype>
         <funcdef>int <function>sd_event_prepare</function></funcdef>
         <paramdef>sd_event *<parameter>event</parameter></paramdef>
@@ -62,7 +80,7 @@
       <funcprototype>
         <funcdef>int <function>sd_event_wait</function></funcdef>
         <paramdef>sd_event *<parameter>event</parameter></paramdef>
-        <paramdef>uint64_t <parameter>timeout</parameter></paramdef>
+        <paramdef>uint64_t <parameter>usec</parameter></paramdef>
       </funcprototype>
 
       <funcprototype>
         <paramdef>sd_event *<parameter>event</parameter></paramdef>
       </funcprototype>
 
+      <funcprototype>
+        <funcdef>int <function>sd_event_get_state</function></funcdef>
+        <paramdef>sd_event *<parameter>event</parameter></paramdef>
+      </funcprototype>
+
     </funcsynopsis>
   </refsynopsisdiv>
 
   <refsect1>
     <title>Description</title>
 
-    <para>Functions described here form parts of an event loop.</para>
-
-    <para><function>sd_event_prepare</function> checks for pending
+    <para>The low-level <function>sd_event_prepare()</function>,
+    <function>sd_event_wait()</function> and
+    <function>sd_event_dispatch()</function> functions may be used to
+    execute specific phases of an event loop. See
+    <citerefentry><refentrytitle>sd_event_run</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    and
+    <citerefentry><refentrytitle>sd_event_loop</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    for higher-level functions that execute individual but complete
+    iterations of an event loop or run it continously.</para>
+
+    <para><function>sd_event_prepare()</function> checks for pending
     events and arms necessary timers. If any events are ready to be
-    processed, it returns a positive value, and the events should be
-    processed with <function>sd_event_dispatch</function>.
-    <function>sd_event_dispatch</function> runs a handler for one of
-    the events from the sources with the highest priority. On success,
-    <function>sd_event_dispatch</function> returns either 0, which
-    means that the loop is finished, or a positive value, which means
-    that the loop is again in the initial state and
-    <function>sd_event_prepare</function> should be called again.
-    </para>
+    processed ("pending"), it returns a positive, non-zero value, and the caller
+    should process these events with
+    <function>sd_event_dispatch()</function>.</para>
+
+    <para><function>sd_event_dispatch()</function> dispatches the
+    highest priority event source that has a pending event. On
+    success, <function>sd_event_dispatch()</function> returns either
+    zero, which indicates that no further event sources may be
+    dispatched and exiting of the event loop was requested via
+    <citerefentry><refentrytitle>sd_event_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>;
+    or a positive non-zero value, which means that an event source was
+    dispatched and the loop returned to its initial state, and the
+    caller should initiate the next event loop iteration by invoking
+    <function>sd_event_prepare()</function> again.</para>
+
+    <para>In case <function>sd_event_prepare()</function> returned
+    zero, <function>sd_event_wait()</function> should be called to
+    wait for further events or a timeout. If any events are ready to
+    be processed, it returns a positive, non-zero value, and the
+    events should be dispatched with
+    <function>sd_event_dispatch()</function>. Otherwise, the event
+    loop returned to its initial state and the next event loop
+    iteration should be initiated by invoking
+    <function>sd_event_prepare()</function> again.</para>
+
+    <para><function>sd_event_get_state()</function> may be used to
+    determine the state the event loop is currently in. It returns one
+    of the states described below.</para>
+
+    <para>All four functions take, as the first argument, the event
+    loop object <parameter>event</parameter> that has been created
+    with <function>sd_event_new()</function>. The timeout for
+    <function>sd_event_wait()</function> is specified in
+    <parameter>usec</parameter> in milliseconds.  <constant>(uint64_t)
+    -1</constant> may be used to specify an infinite timeout.</para>
+</refsect1>
+
+  <refsect1>
+    <title>State Machine</title>
+
+    <para>The event loop knows the following states, that may be
+    queried with <function>sd_event_get_state()</function>.</para>
 
-    <para>In case <function>sd_event_prepare</function> returned 0,
-    <function>sd_event_wait</function> should be called to wait for
-    events or a timeout. If any events are ready to be processed, it
-    returns a positive value, and the events should be processed with
-    <function>sd_event_dispatch</function>. Otherwise, the loop is
-    back in the initial state and <function>sd_event_prepare</function>
-    should be called again.</para>
+    <variablelist>
+      <varlistentry>
+        <term><constant>SD_EVENT_INITIAL</constant></term>
+
+        <listitem><para>The initial state the event loop is in,
+        before each event loop iteration. Use
+        <function>sd_event_prepare()</function> to transition the
+        event loop into the <constant>SD_EVENT_ARMED</constant> or
+        <constant>SD_EVENT_PENDING</constant> states.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>SD_EVENT_PREPARING</constant></term>
+
+        <listitem><para>An event source is currently being prepared,
+        i.e. the preparation handler is currently being excuted, as
+        set with
+        <citerefentry><refentrytitle>sd_event_set_prepare</refentrytitle><manvolnum>3</manvolnum></citerefentry>. This
+        state is only seen in the event source preparation handler
+        that is invoked from the
+        <function>sd_event_prepare()</function> call and is
+        immediately followed by <constant>SD_EVENT_ARMED</constant> or
+        <constant>SD_EVENT_PENDING</constant>.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>SD_EVENT_ARMED</constant></term>
+
+        <listitem><para><function>sd_event_prepare()</function> has
+        been called and no event sources were ready to be
+        dispatched. Use <function>sd_event_wait()</function> to wait
+        for new events, and transition into
+        <constant>SD_EVENT_PENDING</constant> or back into
+        <constant>SD_EVENT_INITIAL</constant>.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>SD_EVENT_PENDING</constant></term>
+
+        <listitem><para><function>sd_event_prepare()</function> or
+        <function>sd_event_wait()</function> have been called and
+        there were event sources with events pending. Use
+        <function>sd_event_dispatch()</function> to dispatch the
+        highest priority event source and transition back to
+        <constant>SD_EVENT_INITIAL</constant>, or
+        <constant>SD_EVENT_FINISHED</constant>.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>SD_EVENT_RUNNING</constant></term>
+
+        <listitem><para>A regular event source is currently being
+        dispatched. This state is only seen in the event source
+        handler that is invoked from the
+        <function>sd_event_dispatch()</function> call, and is
+        immediately followed by <constant>SD_EVENT_INITIAL</constant>
+        or <constant>SD_EVENT_FINISHED</constant> as soon the event
+        source handler returns. Note that during dispatching of exit
+        event sources the <constant>SD_EVENT_EXITING</constant> state
+        is seen instead.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>SD_EVENT_EXITING</constant></term>
+
+        <listitem><para>Similar to
+        <constant>SD_EVENT_RUNNING</constant> but is the state in
+        effect while dispatching exit event sources. It is followed by
+        <constant>SD_EVENT_INITIAL</constant> or
+        <constant>SD_EVENT_FINISHED</constant> as soon as the event
+        handler returns.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>SD_EVENT_FINISHED</constant></term>
+
+        <listitem><para>The event loop has exited. All exit event
+        sources have run. If the event loop is in this state it serves
+        no purpose anymore, and should be freed.</para></listitem>
+      </varlistentry>
+
+    </variablelist>
+
+    <para>A simplified flow chart of the states and the calls to
+    transition between them is shown below. Note that
+    <constant>SD_EVENT_PREPARING</constant>,
+    <constant>SD_EVENT_RUNNING</constant> and
+    <constant>SD_EVENT_EXITING</constant> are not shown here.</para>
 
     <programlisting>
-             ┌──────────┐
-             │ initial  ├──←←←←←←←←←←←←←←←←←←←─┐
-             └───┬──────┘                      ↑
-                 │                             ↑
-           sd_event_prepare   ┌─────────┐      ↑
-                 ├ 0 →→→→→→→──┤  armed  │      ↑
-                 1            └───┬─────┘      ↑
-                 ↓                │            ↑
-                 ↓           sd_event_wait     ↑
-                 ├───←←←←←←←─── 1 ┴─ 0 →→→→→→→─┘
-             ┌───┴──────┐                      ↑
-             │ pending  │                      ↑
-             └───┬──────┘                      ↑
-                 │                             ↑
-           sd_event_dispatch                   ↑
-                 ↓                             ↑
-                 ├ 1 ──────────→→→→→→→─────────┘
-                 0
-                 ↓
-             ┌───┴──────┐
-             │ finished │
-             └──────────┘
+          INITIAL -&lt;---&lt;---&lt;---&lt;---&lt;---&lt;---&lt;---&lt;---&lt;---&lt;---&lt;---&lt;---\
+             |                                                     |
+             |                                                     ^
+             |                                                     |
+             v                 ret == 0                            |
+      sd_event_prepare() &gt;---&gt;---&gt;---&gt;---&gt;- ARMED                  |
+             |                                |                    ^
+             | ret > 0                        |                    |
+             |                                |                    |
+             v                                v          ret == 0  |
+          PENDING &lt;---&lt;---&lt;---&lt;---&lt;---&lt; sd_event_wait() &gt;---&gt;---&gt;--+
+             |           ret > 0                                   ^
+             |                                                     |
+             |                                                     |
+             v                                                     |
+      sd_event_dispatch() &gt;---&gt;---&gt;---&gt;---&gt;---&gt;---&gt;---&gt;---&gt;---&gt;---&gt;/
+             |                             ret > 0
+             | ret == 0
+             |
+             v
+          FINISHED
     </programlisting>
-
-    <para>All three functions take, as the first argument, the event
-    loop object <parameter>event</parameter> that is created with
-    <function>sd_event_new</function>. The timeout for
-    <function>sd_event_wait</function> is specified with
-    <parameter>timeout</parameter> in milliseconds.
-    <constant>(uint64_t) -1</constant> may be used to specify an
-    infinite timeout.</para>
   </refsect1>
 
   <refsect1>
 
     <para>On success, these functions return 0 or a positive integer.
     On failure, they return a negative errno-style error code. In case
-    of <function>sd_event_prepare</function> and
-    <function>sd_event_wait</function>, a positive value means that
-    events are ready to be processed and 0 means that no events are
-    ready. In case of <function>sd_event_dispatch</function>, a
-    positive value means that the loop is again in the initial state
-    and 0 means the loop is finished. For any of these functions, a
-    negative return value means the loop must be aborted.</para>
+    of <function>sd_event_prepare()</function> and
+    <function>sd_event_wait()</function>, a positive, non-zero return
+    code indicates that events are ready to be processed and zero
+    indicates that no events are ready. In case of
+    <function>sd_event_dispatch()</function>, a positive, non-zero
+    return code indicates that the event loop returned to its initial
+    state and zero indicates the event loop has
+    exited. <function>sd_event_get_state()</function> returns a
+    positive or zero state on success.</para>
   </refsect1>
 
   <refsect1>
         <term><constant>-EINVAL</constant></term>
 
         <listitem><para>The <parameter>event</parameter> parameter is
-        <constant>NULL</constant>.</para></listitem>
+        invalid or <constant>NULL</constant>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
     <para>Other errors are possible, too.</para>
   </refsect1>
 
-  <refsect1>
-    <title>Notes</title>
-
-    <para>Functions described here are available
-    as a shared library, which can be compiled and linked to with the
-    <constant>libsystemd</constant> <citerefentry project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry>
-    file.</para>
-  </refsect1>
+  <xi:include href="libsystemd-pkgconfig.xml" />
 
   <refsect1>
     <title>See Also</title>
     <para>
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_event_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_event_run</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_event_add_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_event_add_post</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+      <citerefentry><refentrytitle>sd_event_add_post</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_run</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_get_fd</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_source_set_prepare</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     </para>
   </refsect1>
 
index 144ab1db61475be5729cf2ffb60a7c7ad96e9c98..6e27528a715fc9ff2f187baf2477dbc755388398 100644 (file)
     <varname>WatchdogSec=</varname> in service files. See
     <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>
     for details.</para>
+
+    <para>Use
+    <citerefentry><refentrytitle>sd_event_set_watchdog</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    to enable automatic watchdog support in
+    <citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>-based event loops.</para>
   </refsect1>
 
   <refsect1>
       <citerefentry><refentrytitle>sd-daemon</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>daemon</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_notify</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+      <citerefentry><refentrytitle>sd_notify</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_set_watchdog</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     </para>
   </refsect1>
 
index 755a74f9877b5a7509723d27ec4f77146419770e..1fb056874ce2650e4eaffeaee98b2513176127a5 100644 (file)
@@ -888,6 +888,11 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
 
             <para>Example: <command>systemctl set-property foobar.service CPUShares=777</command></para>
 
+            <para>If the specified unit appears to be inactive, the
+            changes will be only stored on disk as described
+            previously hence they will be effective when the unit will
+            be started.</para>
+
             <para>Note that this command allows changing multiple
             properties at the same time, which is preferable over
             setting them individually. Like unit file configuration
index f1054b03bb1255591de6b473d942c23e21ca2ffc..2810638bc26f46f58212412bf130e93678be49af 100644 (file)
     <programlisting>mkdir -p /var/log/journal
 systemd-tmpfiles --create --prefix /var/log/journal</programlisting>
 
-    <para><filename>systemd-journald</filename> will forward all
-    received log messages to the
-    <constant>AF_UNIX</constant>/<constant>SOCK_DGRAM</constant>
-    socket <filename>/run/systemd/journal/syslog</filename>, if it
-    exists, which may be used by Unix syslog daemons to process the
-    data further.</para>
-
     <para>See
     <citerefentry><refentrytitle>journald.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
     for information about the configuration of this service.</para>
index ead52951daace5a634872503f18e906479b8e13f..edc6df914aba2c1736a3c4f7594edd8cc0c6a949 100644 (file)
         <listitem><para>These settings control various default
         resource limits for units. See
         <citerefentry><refentrytitle>setrlimit</refentrytitle><manvolnum>2</manvolnum></citerefentry>
-        for details. Use the string <varname>infinity</varname> to
-        configure no limit on a specific resource. The multiplicative suffixes
-        K (=1024), M (=1024*1024) and so on for G, T, P and E may be used for
-        resource limits measured in bytes (e.g. DefaultLimitAS=16G). These
-        settings may be overridden in individual units using the corresponding
-        LimitXXX= directives. Note that these resource limits are only
-        defaults for units, they are not applied to PID 1
+        for details. The resource limit is possible to specify in two formats,
+        <option>value</option> to set soft and hard limits to the same value,
+        or <option>soft:hard</option> to set both limits individually (e.g. DefaultLimitAS=4G:16G).
+        Use the string <varname>infinity</varname> to
+        configure no limit on a specific resource. The multiplicative
+        suffixes K (=1024), M (=1024*1024) and so on for G, T, P and E
+        may be used for resource limits measured in bytes
+        (e.g. DefaultLimitAS=16G). For the limits referring to time values,
+        the usual time units ms, s, min, h and so on may be used (see
+        <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+        for details). Note that if no time unit is specified for
+        <varname>DefaultLimitCPU=</varname> the default unit of seconds is
+        implied, while for <varname>DefaultLimitRTTIME=</varname> the default
+        unit of microseconds is implied. Also, note that the effective
+        granularity of the limits might influence their
+        enforcement. For example, time limits specified for
+        <varname>DefaultLimitCPU=</varname> will be rounded up implicitly to
+        multiples of 1s. These  settings may be overridden in individual units
+        using the corresponding LimitXXX= directives. Note that these resource
+        limits are only defaults for units, they are not applied to PID 1
         itself.</para></listitem>
       </varlistentry>
     </variablelist>
index 6dda6c5e69fa316df09153c4d8201e401a4d2e5b..5f98ef163c04cfc855ad433d36a998ba96e8d4b7 100644 (file)
         <listitem><para>These settings set both soft and hard limits
         of various resources for executed processes. See
         <citerefentry><refentrytitle>setrlimit</refentrytitle><manvolnum>2</manvolnum></citerefentry>
-        for details. Use the string <varname>infinity</varname> to
+        for details. The resource limit is possible to specify in two formats,
+        <option>value</option> to set soft and hard limits to the same value,
+        or <option>soft:hard</option> to set both limits individually (e.g. LimitAS=4G:16G).
+        Use the string <varname>infinity</varname> to
         configure no limit on a specific resource. The multiplicative
         suffixes K (=1024), M (=1024*1024) and so on for G, T, P and E
         may be used for resource limits measured in bytes
       <varlistentry>
         <term><varname>$LISTEN_FDS</varname></term>
         <term><varname>$LISTEN_PID</varname></term>
+        <term><varname>$LISTEN_FDNAMES</varname></term>
 
         <listitem><para>Information about file descriptors passed to a
         service for socket activation. See
         </para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>$NOTIFY_SOCKET</varname></term>
+
+        <listitem><para>The socket
+        <function>sd_notify()</function> talks to. See
+        <citerefentry><refentrytitle>sd_notify</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>$WATCHDOG_PID</varname></term>
+        <term><varname>$WATCHDOG_USEC</varname></term>
+
+        <listitem><para>Information about watchdog keep-alive notifications. See
+        <citerefentry><refentrytitle>sd_watchdog_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+        </para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>$TERM</varname></term>
 
 
     <para>Additional variables may be configured by the following
     means: for processes spawned in specific units, use the
-    <varname>Environment=</varname> and
-    <varname>EnvironmentFile=</varname> options above; to specify
+    <varname>Environment=</varname>, <varname>EnvironmentFile=</varname>
+    and <varname>PassEnvironment=</varname> options above; to specify
     variables globally, use <varname>DefaultEnvironment=</varname>
     (see
     <citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>)
index a724d88584717d71b9ad4ce5df0bcf0dc2b0ad55..4a8d265fed72f140b2a3f34c4c530e5fbac72549 100644 (file)
@@ -88,7 +88,8 @@
     configured in a unit file <filename>home-lennart.mount</filename>.
     For details about the escaping logic used to convert a file system
     path to a unit name, see
-    <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
+    <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+    Note that mount units cannot be templated.</para>
 
     <para>Optionally, a mount unit may be accompanied by an automount
     unit, to allow on-demand or parallelized mounting. See
index 20a71afe59a991300d193b281c8918b1654049da..b998a1f81f8971cdf37309c7869498c363365309 100644 (file)
         larger than the configured time, then the service is placed in
         a failed state and it will be terminated with
         <constant>SIGABRT</constant>. By setting
-        <varname>Restart=</varname> to <option>on-failure</option> or
+        <varname>Restart=</varname> to <option>on-failure</option>,
+        <option>on-watchdog</option>, <option>on-abnormal</option> or
         <option>always</option>, the service will be automatically
         restarted. The time configured here will be passed to the
         executed service process in the
         check whether the service manager expects watchdog keep-alive
         notifications. See
         <citerefentry><refentrytitle>sd_watchdog_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>
-       for details.
+        for details.
+        <citerefentry><refentrytitle>sd_event_set_watchdog</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+        may be used to enable automatic watchdog notification support.
         </para></listitem>
       </varlistentry>
 
         <constant>SIGPIPE</constant>. Exit status definitions can
         either be numeric exit codes or termination signal names,
         separated by spaces. For example:
-        <programlisting>SuccessExitStatus=1 2 8
-        SIGKILL</programlisting> ensures that exit codes 1, 2, 8 and
+
+        <programlisting>SuccessExitStatus=1 2 8 SIGKILL</programlisting>
+
+        ensures that exit codes 1, 2, 8 and
         the termination signal <constant>SIGKILL</constant> are
         considered clean service terminations.
         </para>
         signal names, and are separated by spaces. Defaults to the
         empty list, so that, by default, no exit status is excluded
         from the configured restart logic. For example:
-        <programlisting>RestartPreventExitStatus=1 6
-        SIGABRT</programlisting> ensures that exit codes 1 and 6 and
-        the termination signal <constant>SIGABRT</constant> will not
-        result in automatic service restarting. This option may appear
-        more than once, in which case the list of restart-preventing
-        statuses is merged. If the empty string is assigned to this
-        option, the list is reset and all prior assignments of this
-        option will have no effect.</para></listitem>
+
+        <programlisting>RestartPreventExitStatus=1 6 SIGABRT</programlisting>
+
+        ensures that exit codes 1 and 6 and the termination signal
+        <constant>SIGABRT</constant> will not result in automatic
+        service restarting. This option may appear more than once, in
+        which case the list of restart-preventing statuses is
+        merged. If the empty string is assigned to this option, the
+        list is reset and all prior assignments of this option will
+        have no effect.</para></listitem>
       </varlistentry>
 
       <varlistentry>
index beac053bf0e61572d3b7d4e9d71814f3a2852dd7..43841c23996144b3de099b90518622fe743efc3e 100644 (file)
         </para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>SocketProtocol=</varname></term>
+        <listitem><para>Takes a one of <option>udplite</option>
+        or <option>sctp</option>. Specifies a socket protocol
+        (<constant>IPPROTO_UDPLITE</constant>) UDP-Lite
+        (<constant>IPPROTO_SCTP</constant>) SCTP socket respectively. </para>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>BindIPv6Only=</varname></term>
         <listitem><para>Takes a one of <option>default</option>,
index a6fcc95e4c345558307f9f5f45b52a9363babd94..ffcac82263093c999fb1d9a995dc3033af8942f1 100644 (file)
     the value and all values plus multiples of the repetition value
     are matched.</para>
 
+    <para>The seconds component may contain decimal fractions both in
+    the value and the repetition. All fractions are rounded to 6
+    decimal places.</para>
+
     <para>Either time or date specification may be omitted, in which
     case the current day and 00:00:00 is implied, respectively. If the
     second component is not specified, <literal>:00</literal> is
@@ -276,6 +280,7 @@ Wed-Sat,Tue 12-10-15 1:2:3 → Tue-Sat 2012-10-15 01:02:03
     Sat,Sun 12-05 08:05:40 → Sat,Sun *-12-05 08:05:40
           Sat,Sun 08:05:40 → Sat,Sun *-*-* 08:05:40
           2003-03-05 05:40 → 2003-03-05 05:40:00
+05:40:23.4200004/3.1700005 → 05:40:23.420000/3.170001
       2003-03-05 05:40 UTC → 2003-03-05 05:40:00 UTC
                 2003-03-05 → 2003-03-05 00:00:00
                      03-05 → *-03-05 00:00:00
index 8cf6c4683b3367606a9aca9f783cca2cf198bad4..cfa13015b035a46a26e5d644219e288365b02039 100644 (file)
         <varname>OnUnitInactiveSec=</varname> and ending the time
         configured with <varname>AccuracySec=</varname> later. Within
         this time window, the expiry time will be placed at a
-        host-specific, randomized but stable position that is
+        host-specific, randomized, but stable position that is
         synchronized between all local timer units. This is done in
-        order to distribute the wake-up time in networked
-        installations, as well as optimizing power consumption to
-        suppress unnecessary CPU wake-ups. To get best accuracy, set
-        this option to 1us. Note that the timer is still subject to
-        the timer slack configured via
+        order to optimize power consumption to suppress unnecessary
+        CPU wake-ups. To get best accuracy, set this option to
+        1us. Note that the timer is still subject to the timer slack
+        configured via
         <citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>'s
         <varname>TimerSlackNSec=</varname> setting. See
         <citerefentry><refentrytitle>prctl</refentrytitle><manvolnum>2</manvolnum></citerefentry>
         this value as high as possible and as low as
         necessary.</para></listitem>
       </varlistentry>
+
+      <varlistentry>
+        <term><varname>RandomizedDelaySec=</varname></term>
+
+        <listitem><para>Delay the timer by a randomly selected, evenly
+        distributed amount of time between 0 and the specified time
+        value. Defaults to 0, indicating that no randomized delay
+        shall be applied. Each timer unit will determine this delay
+        randomly each time it is started, and the delay will simply be
+        added on top of the next determined elapsing time. This is
+        useful to stretch dispatching of similarly configured timer
+        events over a certain amount time, to avoid that they all fire
+        at the same time, possibly resulting in resource
+        congestion. Note the relation to
+        <varname>AccuracySec=</varname> above: the latter allows the
+        service manager to coalesce timer events within a specified
+        time range in order to minimize wakeups, the former does the
+        opposite: it stretches timer events over a time range, to make
+        it unlikely that they fire simultaneously. If
+        <varname>RandomizedDelaySec=</varname> and
+        <varname>AccuracySec=</varname> are used in conjunction, first
+        the randomized delay is added, and then the result is
+        possibly further shifted to coalesce it with other timer
+        events happening on the system. As mentioned above
+        <varname>AccuracySec=</varname> defaults to 1min and
+        <varname>RandomizedDelaySec=</varname> to 0, thus encouraging
+        coalescing of timer events. In order to optimally stretch
+        timer events over a certain range of time, make sure to set
+        <varname>RandomizedDelaySec=</varname> to a higher value, and
+        <varname>AccuracySec=1us</varname>.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>Unit=</varname></term>
 
         again after any work that is to be done is finished. Defaults
         to <varname>false</varname>.</para></listitem>
       </varlistentry>
+
+      <varlistentry>
+        <term><varname>RemainAfterElapse=</varname></term>
+
+        <listitem><para>Takes a boolean argument. If true, an elapsed
+        timer will stay loaded, and its state remains queriable. If
+        false, an elapsed timer unit that cannot elapse anymore is
+        unloaded. Turning this off is particularly useful for
+        transient timer units that shall disappear after they first
+        elapse. Note that this setting has an effect on repeatedly
+        starting the a timer unit that only elapses once: if
+        <varname>RemainAfterElapse=</varname> is on, it will not be
+        started again, and is guaranteed to elapse only once. However,
+        if <varname>RemainAfterLeapse=</varname> is off, it might be
+        started again if it is already elapsed, and thus be triggered
+        multiple times. Defaults to
+        <varname>yes</varname>.</para></listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
index 95cde045b62c938413ac1bad361cb42659ebe25c..94c9986d60473708fd2788ea4f1f1abfbc7e7d90 100644 (file)
--- a/po/el.po
+++ b/po/el.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: systemd master\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-06-18 00:53+0200\n"
+"POT-Creation-Date: 2015-11-22 16:37+0100\n"
 "PO-Revision-Date: 2014-04-29 09:17+0300\n"
 "Last-Translator: Dimitris Spingos (Δημήτρης Σπίγγος) <dmtrs32@gmail.com>\n"
 "Language-Team: team@lists.gnome.gr\n"
@@ -451,6 +451,15 @@ msgid ""
 "interface."
 msgstr "Απαιτείται πιστοποίηση για να ορίσετε τοπικά όνομα οικοδεσπότη."
 
+#: ../src/login/org.freedesktop.login1.policy.in.h:55
+msgid "Set a wall message"
+msgstr ""
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:56
+#, fuzzy
+msgid "Authentication is required to set a wall message"
+msgstr "Απαιτείται πιστοποίηση για να ορίσετε τοπικά όνομα οικοδεσπότη."
+
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:1
 msgid "Log into a local container"
 msgstr ""
@@ -461,20 +470,68 @@ msgid "Authentication is required to log into a local container."
 msgstr "Απαιτείται πιστοποίηση για να ορίσετε τοπικά όνομα οικοδεσπότη."
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:3
-msgid "Manage local virtual machines and containers"
+msgid "Log into the local host"
 msgstr ""
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:4
 #, fuzzy
+msgid "Authentication is required to log into the local host."
+msgstr "Απαιτείται πιστοποίηση για να ορίσετε τοπικά όνομα οικοδεσπότη."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:5
+#, fuzzy
+msgid "Acquire a shell in a local container"
+msgstr "Απαιτείται πιστοποίηση για να ορίσετε τοπικά όνομα οικοδεσπότη."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:6
+#, fuzzy
+msgid "Authentication is required to acquire a shell in a local container."
+msgstr "Απαιτείται πιστοποίηση για να ορίσετε τοπικά όνομα οικοδεσπότη."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:7
+msgid "Acquire a shell on the local host"
+msgstr ""
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:8
+#, fuzzy
+msgid "Authentication is required to acquire a shell on the local host."
+msgstr "Απαιτείται πιστοποίηση για να ορίσετε τοπικά όνομα οικοδεσπότη."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:9
+#, fuzzy
+msgid "Acquire a pseudo TTY in a local container"
+msgstr "Απαιτείται πιστοποίηση για να ορίσετε τοπικά όνομα οικοδεσπότη."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:10
+#, fuzzy
+msgid ""
+"Authentication is required to acquire a pseudo TTY in a local container."
+msgstr "Απαιτείται πιστοποίηση για να ορίσετε τοπικά όνομα οικοδεσπότη."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:11
+msgid "Acquire a pseudo TTY on the local host"
+msgstr ""
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:12
+#, fuzzy
+msgid "Authentication is required to acquire a pseudo TTY on the local host."
+msgstr "Απαιτείται πιστοποίηση για να ορίσετε τοπικά όνομα οικοδεσπότη."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:13
+msgid "Manage local virtual machines and containers"
+msgstr ""
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:14
+#, fuzzy
 msgid ""
 "Authentication is required to manage local virtual machines and containers."
 msgstr "Απαιτείται πιστοποίηση για να ορίσετε πληροφορίες τοπικής μηχανής."
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:5
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:15
 msgid "Manage local virtual machine and container images"
 msgstr ""
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:6
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:16
 #, fuzzy
 msgid ""
 "Authentication is required to manage local virtual machine and container "
@@ -520,3 +577,38 @@ msgid ""
 msgstr ""
 "Απαιτείται πιστοποίηση για να ελέγξετε αν ο συγχρονισμός ώρας δικτύου θα "
 "ενεργοποιηθεί."
+
+#: ../src/core/dbus-unit.c:428
+#, fuzzy
+msgid "Authentication is required to start '$(unit)'."
+msgstr "Απαιτείται πιστοποίηση για να ορίσετε την ώρα του συστήματος."
+
+#: ../src/core/dbus-unit.c:429
+#, fuzzy
+msgid "Authentication is required to stop '$(unit)'."
+msgstr "Απαιτείται πιστοποίηση για να ορίσετε την ώρα του συστήματος."
+
+#: ../src/core/dbus-unit.c:430
+#, fuzzy
+msgid "Authentication is required to reload '$(unit)'."
+msgstr "Απαιτείται πιστοποίηση για να ορίσετε την ώρα του συστήματος."
+
+#: ../src/core/dbus-unit.c:431 ../src/core/dbus-unit.c:432
+#, fuzzy
+msgid "Authentication is required to restart '$(unit)'."
+msgstr "Απαιτείται πιστοποίηση για να ορίσετε την ώρα του συστήματος."
+
+#: ../src/core/dbus-unit.c:535
+#, fuzzy
+msgid "Authentication is required to kill '$(unit)'."
+msgstr "Απαιτείται πιστοποίηση για να ορίσετε τοπικά όνομα οικοδεσπότη."
+
+#: ../src/core/dbus-unit.c:565
+#, fuzzy
+msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
+msgstr "Απαιτείται πιστοποίηση για να ορίσετε τοπικά όνομα οικοδεσπότη."
+
+#: ../src/core/dbus-unit.c:597
+#, fuzzy
+msgid "Authentication is required to set properties on '$(unit)'."
+msgstr "Απαιτείται πιστοποίηση για να ορίσετε την ώρα του συστήματος."
index ceca42386a9be24acbbf7b2a5a26fe8c92e8da51..681b30d5b2f0eab0c1547a404597204199c77e8e 100644 (file)
--- a/po/es.po
+++ b/po/es.po
@@ -9,7 +9,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: systemd master\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-06-18 00:53+0200\n"
+"POT-Creation-Date: 2015-11-22 16:37+0100\n"
 "PO-Revision-Date: 2015-04-24 13:26+0200\n"
 "Last-Translator: Álex Puchades <alex94puchades@gmail.com>\n"
 "Language-Team: Español; Castellano <gnome-es-list@gnome.org>\n"
@@ -446,6 +446,15 @@ msgstr ""
 "Se requiere autenticación para indicar al firmware que arranque la interfaz "
 "de configuración."
 
+#: ../src/login/org.freedesktop.login1.policy.in.h:55
+msgid "Set a wall message"
+msgstr ""
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:56
+#, fuzzy
+msgid "Authentication is required to set a wall message"
+msgstr "Se requiere autenticación para establecer el nombre de equipo local."
+
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:1
 msgid "Log into a local container"
 msgstr "Conectarse a un contenedor local"
@@ -455,21 +464,70 @@ msgid "Authentication is required to log into a local container."
 msgstr "Se requiere autenticación para conectarse a un contenedor local."
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:3
+#, fuzzy
+msgid "Log into the local host"
+msgstr "Conectarse a un contenedor local"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:4
+#, fuzzy
+msgid "Authentication is required to log into the local host."
+msgstr "Se requiere autenticación para conectarse a un contenedor local."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:5
+#, fuzzy
+msgid "Acquire a shell in a local container"
+msgstr "Conectarse a un contenedor local"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:6
+#, fuzzy
+msgid "Authentication is required to acquire a shell in a local container."
+msgstr "Se requiere autenticación para conectarse a un contenedor local."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:7
+msgid "Acquire a shell on the local host"
+msgstr ""
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:8
+#, fuzzy
+msgid "Authentication is required to acquire a shell on the local host."
+msgstr "Se requiere autenticación para establecer el nombre de equipo local."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:9
+#, fuzzy
+msgid "Acquire a pseudo TTY in a local container"
+msgstr "Conectarse a un contenedor local"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:10
+#, fuzzy
+msgid ""
+"Authentication is required to acquire a pseudo TTY in a local container."
+msgstr "Se requiere autenticación para conectarse a un contenedor local."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:11
+msgid "Acquire a pseudo TTY on the local host"
+msgstr ""
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:12
+#, fuzzy
+msgid "Authentication is required to acquire a pseudo TTY on the local host."
+msgstr "Se requiere autenticación para establecer el nombre de equipo local."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:13
 msgid "Manage local virtual machines and containers"
 msgstr "Administrar máquinas virtuales y contenedores locales"
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:4
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:14
 msgid ""
 "Authentication is required to manage local virtual machines and containers."
 msgstr ""
 "Se requiere autenticación para administrar las máquinas virtuales y los "
 "contenedores locales."
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:5
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:15
 msgid "Manage local virtual machine and container images"
 msgstr "Administrar imágenes de máquina virtual y de contenedor locales"
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:6
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:16
 msgid ""
 "Authentication is required to manage local virtual machine and container "
 "images."
@@ -517,6 +575,41 @@ msgstr ""
 "Se requiere autenticación para activar/desactivar la sincronización de hora "
 "por red."
 
+#: ../src/core/dbus-unit.c:428
+#, fuzzy
+msgid "Authentication is required to start '$(unit)'."
+msgstr "Se requiere autenticación para establecer la fecha y hora del sistema."
+
+#: ../src/core/dbus-unit.c:429
+#, fuzzy
+msgid "Authentication is required to stop '$(unit)'."
+msgstr "Se requiere autenticación para establecer la fecha y hora del sistema."
+
+#: ../src/core/dbus-unit.c:430
+#, fuzzy
+msgid "Authentication is required to reload '$(unit)'."
+msgstr "Se requiere autenticación para recargar el estado de systemd."
+
+#: ../src/core/dbus-unit.c:431 ../src/core/dbus-unit.c:432
+#, fuzzy
+msgid "Authentication is required to restart '$(unit)'."
+msgstr "Se requiere autenticación para establecer la fecha y hora del sistema."
+
+#: ../src/core/dbus-unit.c:535
+#, fuzzy
+msgid "Authentication is required to kill '$(unit)'."
+msgstr "Se requiere autenticación para conectarse a un contenedor local."
+
+#: ../src/core/dbus-unit.c:565
+#, fuzzy
+msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
+msgstr "Se requiere autenticación para establecer el nombre de equipo local."
+
+#: ../src/core/dbus-unit.c:597
+#, fuzzy
+msgid "Authentication is required to set properties on '$(unit)'."
+msgstr "Se requiere autenticación para establecer la fecha y hora del sistema."
+
 #~ msgid "Press Ctrl+C to cancel all filesystem checks in progress"
 #~ msgstr ""
 #~ "Presione Ctrl+C para cancelar todas las comprobaciones del sistema de "
index c5dee25052039b928b00572b82af3d74e0df4a16..21050623c57d8a31482a53b2fd2d1b7596c6b9ba 100644 (file)
--- a/po/hu.po
+++ b/po/hu.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: systemd master\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-06-18 00:53+0200\n"
+"POT-Creation-Date: 2015-11-22 16:37+0100\n"
 "PO-Revision-Date: 2015-01-02 22:58+0100\n"
 "Last-Translator: Gabor Kelemen <kelemeng at ubuntu dot com>\n"
 "Language-Team: Hungarian <openscope at googlegroups dot com>\n"
@@ -439,6 +439,15 @@ msgid ""
 "interface."
 msgstr "Hitelesítés szükséges a helyi gépnév beállításához."
 
+#: ../src/login/org.freedesktop.login1.policy.in.h:55
+msgid "Set a wall message"
+msgstr ""
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:56
+#, fuzzy
+msgid "Authentication is required to set a wall message"
+msgstr "Hitelesítés szükséges a helyi gépnév beállításához."
+
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:1
 msgid "Log into a local container"
 msgstr "Bejelentkezés helyi konténerbe"
@@ -449,20 +458,69 @@ msgid "Authentication is required to log into a local container."
 msgstr "Hitelesítés szükséges a bejelentkezéshez egy helyi konténerbe."
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:3
+#, fuzzy
+msgid "Log into the local host"
+msgstr "Bejelentkezés helyi konténerbe"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:4
+#, fuzzy
+msgid "Authentication is required to log into the local host."
+msgstr "Hitelesítés szükséges a bejelentkezéshez egy helyi konténerbe."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:5
+#, fuzzy
+msgid "Acquire a shell in a local container"
+msgstr "Bejelentkezés helyi konténerbe"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:6
+#, fuzzy
+msgid "Authentication is required to acquire a shell in a local container."
+msgstr "Hitelesítés szükséges a bejelentkezéshez egy helyi konténerbe."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:7
+msgid "Acquire a shell on the local host"
+msgstr ""
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:8
+#, fuzzy
+msgid "Authentication is required to acquire a shell on the local host."
+msgstr "Hitelesítés szükséges a helyi gépnév beállításához."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:9
+#, fuzzy
+msgid "Acquire a pseudo TTY in a local container"
+msgstr "Bejelentkezés helyi konténerbe"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:10
+#, fuzzy
+msgid ""
+"Authentication is required to acquire a pseudo TTY in a local container."
+msgstr "Hitelesítés szükséges a bejelentkezéshez egy helyi konténerbe."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:11
+msgid "Acquire a pseudo TTY on the local host"
+msgstr ""
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:12
+#, fuzzy
+msgid "Authentication is required to acquire a pseudo TTY on the local host."
+msgstr "Hitelesítés szükséges a helyi gépnév beállításához."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:13
 msgid "Manage local virtual machines and containers"
 msgstr ""
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:4
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:14
 #, fuzzy
 msgid ""
 "Authentication is required to manage local virtual machines and containers."
 msgstr "Hitelesítés szükséges a helyi gép információinak beállításához."
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:5
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:15
 msgid "Manage local virtual machine and container images"
 msgstr ""
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:6
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:16
 #, fuzzy
 msgid ""
 "Authentication is required to manage local virtual machine and container "
@@ -506,3 +564,38 @@ msgid ""
 "Authentication is required to control whether network time synchronization "
 "shall be enabled."
 msgstr "Hitelesítés szükséges a hálózati időszinkronizáció engedélyezéséhez."
+
+#: ../src/core/dbus-unit.c:428
+#, fuzzy
+msgid "Authentication is required to start '$(unit)'."
+msgstr "Hitelesítés szükséges a rendszeridő beállításához."
+
+#: ../src/core/dbus-unit.c:429
+#, fuzzy
+msgid "Authentication is required to stop '$(unit)'."
+msgstr "Hitelesítés szükséges a rendszeridő beállításához."
+
+#: ../src/core/dbus-unit.c:430
+#, fuzzy
+msgid "Authentication is required to reload '$(unit)'."
+msgstr "Hitelesítés szükséges a systemd állapotának újratöltéséhez."
+
+#: ../src/core/dbus-unit.c:431 ../src/core/dbus-unit.c:432
+#, fuzzy
+msgid "Authentication is required to restart '$(unit)'."
+msgstr "Hitelesítés szükséges a rendszeridő beállításához."
+
+#: ../src/core/dbus-unit.c:535
+#, fuzzy
+msgid "Authentication is required to kill '$(unit)'."
+msgstr "Hitelesítés szükséges a bejelentkezéshez egy helyi konténerbe."
+
+#: ../src/core/dbus-unit.c:565
+#, fuzzy
+msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
+msgstr "Hitelesítés szükséges a helyi gépnév beállításához."
+
+#: ../src/core/dbus-unit.c:597
+#, fuzzy
+msgid "Authentication is required to set properties on '$(unit)'."
+msgstr "Hitelesítés szükséges a rendszeridő beállításához."
index e11e7e3faadbb1494e8061c855aef93f88a7ba8c..a8547b9395229ca575b296c56de81ba20ae9b5e3 100644 (file)
--- a/po/it.po
+++ b/po/it.po
@@ -7,8 +7,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: systemd\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-06-18 00:53+0200\n"
-"PO-Revision-Date: 2015-06-10 23:10+0100\n"
+"POT-Creation-Date: 2015-11-22 16:37+0100\n"
+"PO-Revision-Date: 2015-11-22 16:54+0100\n"
 "Last-Translator: Daniele Medri <dmedri@gmail.com>\n"
 "Language-Team: Italian\n"
 "Language: it\n"
@@ -16,7 +16,7 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
-"X-Generator: Poedit 1.7.6\n"
+"X-Generator: Poedit 1.8.5\n"
 
 #: ../src/core/org.freedesktop.systemd1.policy.in.in.h:1
 msgid "Send passphrase back to system"
@@ -443,6 +443,14 @@ msgstr ""
 "Autenticazione richiesta per indicare al firmware di avviare l'interfaccia "
 "di configurazione."
 
+#: ../src/login/org.freedesktop.login1.policy.in.h:55
+msgid "Set a wall message"
+msgstr "Configura un messaggio per gli utenti"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:56
+msgid "Authentication is required to set a wall message"
+msgstr "Autenticazione richiesta per configurare un messaggio per gli utenti"
+
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:1
 msgid "Log into a local container"
 msgstr "Accedi in un container locale"
@@ -452,20 +460,62 @@ msgid "Authentication is required to log into a local container."
 msgstr "Autenticazione richiesta per accedere in un container locale."
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:3
+msgid "Log into the local host"
+msgstr "Accedi in un host locale"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:4
+msgid "Authentication is required to log into the local host."
+msgstr "Autenticazione richiesta per accedere in un host locale."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:5
+msgid "Acquire a shell in a local container"
+msgstr "Apri una shell in un container locale"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:6
+msgid "Authentication is required to acquire a shell in a local container."
+msgstr "Autenticazione richiesta per aprire una shell in un container locale."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:7
+msgid "Acquire a shell on the local host"
+msgstr "Apri una shell in un host locale"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:8
+msgid "Authentication is required to acquire a shell on the local host."
+msgstr "Autenticazione richiesta per aprire una shell in un host locale."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:9
+msgid "Acquire a pseudo TTY in a local container"
+msgstr "Apri un pseudo TTY in un container locale"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:10
+msgid ""
+"Authentication is required to acquire a pseudo TTY in a local container."
+msgstr ""
+"Autenticazione richiesta per aprire un pseudo TTY in un container locale."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:11
+msgid "Acquire a pseudo TTY on the local host"
+msgstr "Apri un pseudo TTY in un host locale"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:12
+msgid "Authentication is required to acquire a pseudo TTY on the local host."
+msgstr "Autenticazione richiesta per aprire un pseudo TTY in un host locale."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:13
 msgid "Manage local virtual machines and containers"
 msgstr "Gestisci le virtual machine e i container locali"
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:4
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:14
 msgid ""
 "Authentication is required to manage local virtual machines and containers."
 msgstr ""
 "Autenticazione richiesta per gestire le virtual machine e i container locali."
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:5
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:15
 msgid "Manage local virtual machine and container images"
 msgstr "Gestisci le immagini locali delle virtual machine e dei container"
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:6
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:16
 msgid ""
 "Authentication is required to manage local virtual machine and container "
 "images."
@@ -514,3 +564,32 @@ msgid ""
 msgstr ""
 "Autenticazione richiesta per verificare se la sincronizzazione dell'orario "
 "in rete possa essere attivata."
+
+#: ../src/core/dbus-unit.c:428
+msgid "Authentication is required to start '$(unit)'."
+msgstr "Autenticazione richiesta per avviare '$(unit)'."
+
+#: ../src/core/dbus-unit.c:429
+msgid "Authentication is required to stop '$(unit)'."
+msgstr "Autenticazione richiesta per fermare '$(unit)'."
+
+#: ../src/core/dbus-unit.c:430
+msgid "Authentication is required to reload '$(unit)'."
+msgstr "Autenticazione richiesta per ricaricare '$(unit)'."
+
+#: ../src/core/dbus-unit.c:431 ../src/core/dbus-unit.c:432
+msgid "Authentication is required to restart '$(unit)'."
+msgstr "Autenticazione richiesta per riavviare '$(unit)'."
+
+#: ../src/core/dbus-unit.c:535
+msgid "Authentication is required to kill '$(unit)'."
+msgstr "Autenticazione richiesta per terminare '$(unit)'."
+
+#: ../src/core/dbus-unit.c:565
+msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
+msgstr ""
+"Autenticazione richiesta per riconfigurare lo stato \"fallito\" di '$(unit)'."
+
+#: ../src/core/dbus-unit.c:597
+msgid "Authentication is required to set properties on '$(unit)'."
+msgstr "Autenticazione richiesta per configurare le proprietà di '$(unit)'."
index 1dd5900e2f7bd482f31cce03b165b06095819c97..2a11371f97924d3a2ffeecac714c2f8a7b9fb105 100644 (file)
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: systemd master\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-06-18 00:53+0200\n"
+"POT-Creation-Date: 2015-11-22 16:37+0100\n"
 "PO-Revision-Date: 2015-01-10 12:23-0300\n"
 "Last-Translator: Rafael Ferreira <rafael.f.f1@gmail.com>\n"
 "Language-Team: Brazilian Portuguese <gnome-pt_br-list@gnome.org>\n"
@@ -441,6 +441,15 @@ msgid ""
 "interface."
 msgstr "É necessária autenticação para definir nome de máquina local."
 
+#: ../src/login/org.freedesktop.login1.policy.in.h:55
+msgid "Set a wall message"
+msgstr ""
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:56
+#, fuzzy
+msgid "Authentication is required to set a wall message"
+msgstr "É necessária autenticação para definir nome de máquina local."
+
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:1
 msgid "Log into a local container"
 msgstr "Conectar a um contêiner local"
@@ -451,20 +460,69 @@ msgid "Authentication is required to log into a local container."
 msgstr "É necessária autenticação para se conectar a um contêiner local."
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:3
+#, fuzzy
+msgid "Log into the local host"
+msgstr "Conectar a um contêiner local"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:4
+#, fuzzy
+msgid "Authentication is required to log into the local host."
+msgstr "É necessária autenticação para se conectar a um contêiner local."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:5
+#, fuzzy
+msgid "Acquire a shell in a local container"
+msgstr "Conectar a um contêiner local"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:6
+#, fuzzy
+msgid "Authentication is required to acquire a shell in a local container."
+msgstr "É necessária autenticação para se conectar a um contêiner local."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:7
+msgid "Acquire a shell on the local host"
+msgstr ""
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:8
+#, fuzzy
+msgid "Authentication is required to acquire a shell on the local host."
+msgstr "É necessária autenticação para definir nome de máquina local."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:9
+#, fuzzy
+msgid "Acquire a pseudo TTY in a local container"
+msgstr "Conectar a um contêiner local"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:10
+#, fuzzy
+msgid ""
+"Authentication is required to acquire a pseudo TTY in a local container."
+msgstr "É necessária autenticação para se conectar a um contêiner local."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:11
+msgid "Acquire a pseudo TTY on the local host"
+msgstr ""
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:12
+#, fuzzy
+msgid "Authentication is required to acquire a pseudo TTY on the local host."
+msgstr "É necessária autenticação para definir nome de máquina local."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:13
 msgid "Manage local virtual machines and containers"
 msgstr ""
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:4
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:14
 #, fuzzy
 msgid ""
 "Authentication is required to manage local virtual machines and containers."
 msgstr "É necessária autenticação para definir informações de máquina local."
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:5
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:15
 msgid "Manage local virtual machine and container images"
 msgstr ""
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:6
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:16
 #, fuzzy
 msgid ""
 "Authentication is required to manage local virtual machine and container "
@@ -510,3 +568,38 @@ msgid ""
 msgstr ""
 "É necessária autenticação para controlar se deve ser habilitada, ou não, a "
 "sincronização de horário através de rede."
+
+#: ../src/core/dbus-unit.c:428
+#, fuzzy
+msgid "Authentication is required to start '$(unit)'."
+msgstr "É necessária autenticação para definir o horário do sistema."
+
+#: ../src/core/dbus-unit.c:429
+#, fuzzy
+msgid "Authentication is required to stop '$(unit)'."
+msgstr "É necessária autenticação para definir o horário do sistema."
+
+#: ../src/core/dbus-unit.c:430
+#, fuzzy
+msgid "Authentication is required to reload '$(unit)'."
+msgstr "É necessária autenticação para recarregar o estado do sistema."
+
+#: ../src/core/dbus-unit.c:431 ../src/core/dbus-unit.c:432
+#, fuzzy
+msgid "Authentication is required to restart '$(unit)'."
+msgstr "É necessária autenticação para definir o horário do sistema."
+
+#: ../src/core/dbus-unit.c:535
+#, fuzzy
+msgid "Authentication is required to kill '$(unit)'."
+msgstr "É necessária autenticação para se conectar a um contêiner local."
+
+#: ../src/core/dbus-unit.c:565
+#, fuzzy
+msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
+msgstr "É necessária autenticação para definir nome de máquina local."
+
+#: ../src/core/dbus-unit.c:597
+#, fuzzy
+msgid "Authentication is required to set properties on '$(unit)'."
+msgstr "É necessária autenticação para definir o horário do sistema."
index efb6f7b414d71a92cc9dfcdeca0df3ac0009e55a..32427d095795a79fcdd6d9a5b509ab7341fec675 100644 (file)
--- a/po/ru.po
+++ b/po/ru.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: systemd\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-06-18 00:53+0200\n"
+"POT-Creation-Date: 2015-11-22 16:37+0100\n"
 "PO-Revision-Date: 2015-03-22 21:53+0300\n"
 "Last-Translator: Sergey Ptashnick <0comffdiz@inbox.ru>\n"
 "Language: ru\n"
@@ -465,6 +465,15 @@ msgid ""
 "interface."
 msgstr "Чтобы настроить имя компьютера, необходимо пройти аутентификацию."
 
+#: ../src/login/org.freedesktop.login1.policy.in.h:55
+msgid "Set a wall message"
+msgstr ""
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:56
+#, fuzzy
+msgid "Authentication is required to set a wall message"
+msgstr "Чтобы настроить имя компьютера, необходимо пройти аутентификацию."
+
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:1
 msgid "Log into a local container"
 msgstr "Зайти в локальный контейнер"
@@ -474,21 +483,70 @@ msgid "Authentication is required to log into a local container."
 msgstr "Чтобы зайти в локальный контейнер, необходимо пройти аутентификацию."
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:3
+#, fuzzy
+msgid "Log into the local host"
+msgstr "Зайти в локальный контейнер"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:4
+#, fuzzy
+msgid "Authentication is required to log into the local host."
+msgstr "Чтобы зайти в локальный контейнер, необходимо пройти аутентификацию."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:5
+#, fuzzy
+msgid "Acquire a shell in a local container"
+msgstr "Зайти в локальный контейнер"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:6
+#, fuzzy
+msgid "Authentication is required to acquire a shell in a local container."
+msgstr "Чтобы зайти в локальный контейнер, необходимо пройти аутентификацию."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:7
+msgid "Acquire a shell on the local host"
+msgstr ""
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:8
+#, fuzzy
+msgid "Authentication is required to acquire a shell on the local host."
+msgstr "Чтобы настроить имя компьютера, необходимо пройти аутентификацию."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:9
+#, fuzzy
+msgid "Acquire a pseudo TTY in a local container"
+msgstr "Зайти в локальный контейнер"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:10
+#, fuzzy
+msgid ""
+"Authentication is required to acquire a pseudo TTY in a local container."
+msgstr "Чтобы зайти в локальный контейнер, необходимо пройти аутентификацию."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:11
+msgid "Acquire a pseudo TTY on the local host"
+msgstr ""
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:12
+#, fuzzy
+msgid "Authentication is required to acquire a pseudo TTY on the local host."
+msgstr "Чтобы настроить имя компьютера, необходимо пройти аутентификацию."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:13
 msgid "Manage local virtual machines and containers"
 msgstr "Управление виртуальными машинами и контейнерами"
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:4
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:14
 msgid ""
 "Authentication is required to manage local virtual machines and containers."
 msgstr ""
 "Для управления виртуальными машинами и контейнерами, необходимо пройти "
 "аутентификацию."
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:5
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:15
 msgid "Manage local virtual machine and container images"
 msgstr "Управление образами виртуальных машин и контейнеров"
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:6
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:16
 msgid ""
 "Authentication is required to manage local virtual machine and container "
 "images."
@@ -536,6 +594,43 @@ msgstr ""
 "Чтобы включить или выключить синхронизацию времени по сети, необходимо "
 "пройти аутентификацию."
 
+#: ../src/core/dbus-unit.c:428
+#, fuzzy
+msgid "Authentication is required to start '$(unit)'."
+msgstr "Чтобы настроить системное время, необходимо пройти аутентификацию."
+
+#: ../src/core/dbus-unit.c:429
+#, fuzzy
+msgid "Authentication is required to stop '$(unit)'."
+msgstr "Чтобы настроить системное время, необходимо пройти аутентификацию."
+
+#: ../src/core/dbus-unit.c:430
+#, fuzzy
+msgid "Authentication is required to reload '$(unit)'."
+msgstr ""
+"Чтобы заставить systemd перечитать конфигурацию, необходимо пройти "
+"аутентификацию."
+
+#: ../src/core/dbus-unit.c:431 ../src/core/dbus-unit.c:432
+#, fuzzy
+msgid "Authentication is required to restart '$(unit)'."
+msgstr "Чтобы настроить системное время, необходимо пройти аутентификацию."
+
+#: ../src/core/dbus-unit.c:535
+#, fuzzy
+msgid "Authentication is required to kill '$(unit)'."
+msgstr "Чтобы зайти в локальный контейнер, необходимо пройти аутентификацию."
+
+#: ../src/core/dbus-unit.c:565
+#, fuzzy
+msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
+msgstr "Чтобы настроить имя компьютера, необходимо пройти аутентификацию."
+
+#: ../src/core/dbus-unit.c:597
+#, fuzzy
+msgid "Authentication is required to set properties on '$(unit)'."
+msgstr "Чтобы настроить системное время, необходимо пройти аутентификацию."
+
 #~ msgid "Press Ctrl+C to cancel all filesystem checks in progress"
 #~ msgstr ""
 #~ "Чтобы прервать все запущенные проверки файловых систем, нажмите Ctrl+C"
index af8f421abbf831b346325ced8f1553059d089fb8..28307413096b44c1913bad8eba1fbf1529cf3aa8 100644 (file)
--- a/po/sv.po
+++ b/po/sv.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: systemd master\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-06-18 00:53+0200\n"
+"POT-Creation-Date: 2015-11-22 16:37+0100\n"
 "PO-Revision-Date: 2015-03-14 11:09+0100\n"
 "Last-Translator: Sebastian Rasmussen <sebras@gmail.com>\n"
 "Language-Team: Swedish\n"
@@ -418,6 +418,15 @@ msgid ""
 "interface."
 msgstr "Autentisering krävs för att ange lokalt värdnamn."
 
+#: ../src/login/org.freedesktop.login1.policy.in.h:55
+msgid "Set a wall message"
+msgstr ""
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:56
+#, fuzzy
+msgid "Authentication is required to set a wall message"
+msgstr "Autentisering krävs för att ange lokalt värdnamn."
+
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:1
 msgid "Log into a local container"
 msgstr "Logga till en lokal behållare"
@@ -427,20 +436,69 @@ msgid "Authentication is required to log into a local container."
 msgstr "Autentisering krävs för att logga till en lokal behållare"
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:3
+#, fuzzy
+msgid "Log into the local host"
+msgstr "Logga till en lokal behållare"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:4
+#, fuzzy
+msgid "Authentication is required to log into the local host."
+msgstr "Autentisering krävs för att logga till en lokal behållare"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:5
+#, fuzzy
+msgid "Acquire a shell in a local container"
+msgstr "Logga till en lokal behållare"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:6
+#, fuzzy
+msgid "Authentication is required to acquire a shell in a local container."
+msgstr "Autentisering krävs för att logga till en lokal behållare"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:7
+msgid "Acquire a shell on the local host"
+msgstr ""
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:8
+#, fuzzy
+msgid "Authentication is required to acquire a shell on the local host."
+msgstr "Autentisering krävs för att ange lokalt värdnamn."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:9
+#, fuzzy
+msgid "Acquire a pseudo TTY in a local container"
+msgstr "Logga till en lokal behållare"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:10
+#, fuzzy
+msgid ""
+"Authentication is required to acquire a pseudo TTY in a local container."
+msgstr "Autentisering krävs för att logga till en lokal behållare"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:11
+msgid "Acquire a pseudo TTY on the local host"
+msgstr ""
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:12
+#, fuzzy
+msgid "Authentication is required to acquire a pseudo TTY on the local host."
+msgstr "Autentisering krävs för att ange lokalt värdnamn."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:13
 msgid "Manage local virtual machines and containers"
 msgstr "Hantera lokala virtuella maskiner och behållare"
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:4
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:14
 msgid ""
 "Authentication is required to manage local virtual machines and containers."
 msgstr ""
 "Autentisering krävs för att hantera lokala virtuella maskiner och behållare."
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:5
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:15
 msgid "Manage local virtual machine and container images"
 msgstr "Hantera lokala virtuella maskin- och behållaravbildningar"
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:6
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:16
 msgid ""
 "Authentication is required to manage local virtual machine and container "
 "images."
@@ -490,6 +548,41 @@ msgstr ""
 "Autentisering krävs för att kontrollera huruvida synkronisering av "
 "nätverkstid ska vara aktiverat."
 
+#: ../src/core/dbus-unit.c:428
+#, fuzzy
+msgid "Authentication is required to start '$(unit)'."
+msgstr "Autentisering krävs för ange systemtiden."
+
+#: ../src/core/dbus-unit.c:429
+#, fuzzy
+msgid "Authentication is required to stop '$(unit)'."
+msgstr "Autentisering krävs för ange systemtiden."
+
+#: ../src/core/dbus-unit.c:430
+#, fuzzy
+msgid "Authentication is required to reload '$(unit)'."
+msgstr "Autentisering krävs för att läsa om tillståndet för systemd."
+
+#: ../src/core/dbus-unit.c:431 ../src/core/dbus-unit.c:432
+#, fuzzy
+msgid "Authentication is required to restart '$(unit)'."
+msgstr "Autentisering krävs för ange systemtiden."
+
+#: ../src/core/dbus-unit.c:535
+#, fuzzy
+msgid "Authentication is required to kill '$(unit)'."
+msgstr "Autentisering krävs för att logga till en lokal behållare"
+
+#: ../src/core/dbus-unit.c:565
+#, fuzzy
+msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
+msgstr "Autentisering krävs för att ange lokalt värdnamn."
+
+#: ../src/core/dbus-unit.c:597
+#, fuzzy
+msgid "Authentication is required to set properties on '$(unit)'."
+msgstr "Autentisering krävs för ange systemtiden."
+
 #~ msgid "Press Ctrl+C to cancel all filesystem checks in progress"
 #~ msgstr "Tryck Ctrl+C för att avbryta alla pågående filsystemskontroller."
 
index 656d59c79ebf04b207ecb46ddd0df76a39041952..e93a8c0ecb02af6845b486a8d812984a6892462c 100644 (file)
--- a/po/uk.po
+++ b/po/uk.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: systemd master\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-06-18 00:53+0200\n"
+"POT-Creation-Date: 2015-11-22 16:37+0100\n"
 "PO-Revision-Date: 2014-07-16 19:13+0300\n"
 "Last-Translator: Daniel Korostil <ted.korostiled@gmail.com>\n"
 "Language-Team: linux.org.ua\n"
@@ -421,6 +421,15 @@ msgid ""
 "interface."
 msgstr "Засвідчення потрібне, щоб встановити назву локального вузла."
 
+#: ../src/login/org.freedesktop.login1.policy.in.h:55
+msgid "Set a wall message"
+msgstr ""
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:56
+#, fuzzy
+msgid "Authentication is required to set a wall message"
+msgstr "Засвідчення потрібне, щоб встановити назву локального вузла."
+
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:1
 msgid "Log into a local container"
 msgstr ""
@@ -431,20 +440,68 @@ msgid "Authentication is required to log into a local container."
 msgstr "Засвідчення потрібне, щоб встановити назву локального вузла."
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:3
-msgid "Manage local virtual machines and containers"
+msgid "Log into the local host"
 msgstr ""
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:4
 #, fuzzy
+msgid "Authentication is required to log into the local host."
+msgstr "Засвідчення потрібне, щоб встановити назву локального вузла."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:5
+#, fuzzy
+msgid "Acquire a shell in a local container"
+msgstr "Засвідчення потрібне, щоб встановити назву локального вузла."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:6
+#, fuzzy
+msgid "Authentication is required to acquire a shell in a local container."
+msgstr "Засвідчення потрібне, щоб встановити назву локального вузла."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:7
+msgid "Acquire a shell on the local host"
+msgstr ""
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:8
+#, fuzzy
+msgid "Authentication is required to acquire a shell on the local host."
+msgstr "Засвідчення потрібне, щоб встановити назву локального вузла."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:9
+#, fuzzy
+msgid "Acquire a pseudo TTY in a local container"
+msgstr "Засвідчення потрібне, щоб встановити назву локального вузла."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:10
+#, fuzzy
+msgid ""
+"Authentication is required to acquire a pseudo TTY in a local container."
+msgstr "Засвідчення потрібне, щоб встановити назву локального вузла."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:11
+msgid "Acquire a pseudo TTY on the local host"
+msgstr ""
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:12
+#, fuzzy
+msgid "Authentication is required to acquire a pseudo TTY on the local host."
+msgstr "Засвідчення потрібне, щоб встановити назву локального вузла."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:13
+msgid "Manage local virtual machines and containers"
+msgstr ""
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:14
+#, fuzzy
 msgid ""
 "Authentication is required to manage local virtual machines and containers."
 msgstr "Засвідчення потрібно, щоб вказати локальну інформацію про машини."
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:5
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:15
 msgid "Manage local virtual machine and container images"
 msgstr ""
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:6
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:16
 #, fuzzy
 msgid ""
 "Authentication is required to manage local virtual machine and container "
@@ -488,3 +545,38 @@ msgid ""
 msgstr ""
 "Засвідчення потрібно, щоб контролювати, чи синхронізування часу через мережу "
 "запущено."
+
+#: ../src/core/dbus-unit.c:428
+#, fuzzy
+msgid "Authentication is required to start '$(unit)'."
+msgstr "Засвідчення потрібно, щоб вказати системний час."
+
+#: ../src/core/dbus-unit.c:429
+#, fuzzy
+msgid "Authentication is required to stop '$(unit)'."
+msgstr "Засвідчення потрібно, щоб вказати системний час."
+
+#: ../src/core/dbus-unit.c:430
+#, fuzzy
+msgid "Authentication is required to reload '$(unit)'."
+msgstr "Засвідчення потрібно, щоб вказати системний час."
+
+#: ../src/core/dbus-unit.c:431 ../src/core/dbus-unit.c:432
+#, fuzzy
+msgid "Authentication is required to restart '$(unit)'."
+msgstr "Засвідчення потрібно, щоб вказати системний час."
+
+#: ../src/core/dbus-unit.c:535
+#, fuzzy
+msgid "Authentication is required to kill '$(unit)'."
+msgstr "Засвідчення потрібне, щоб встановити назву локального вузла."
+
+#: ../src/core/dbus-unit.c:565
+#, fuzzy
+msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
+msgstr "Засвідчення потрібне, щоб встановити назву локального вузла."
+
+#: ../src/core/dbus-unit.c:597
+#, fuzzy
+msgid "Authentication is required to set properties on '$(unit)'."
+msgstr "Засвідчення потрібно, щоб вказати системний час."
index fb276a1577ac2b1df0a733d17d6ecd2f2bb262d0..5a214a3c486661584b8d5af300fbdfce371f48a3 100644 (file)
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: \n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-06-18 00:53+0200\n"
+"POT-Creation-Date: 2015-11-22 16:37+0100\n"
 "PO-Revision-Date: 2015-06-11 12:44+0800\n"
 "Last-Translator: Jeff Huang <s8321414@gmail.com>\n"
 "Language-Team: chinese-l10n <chinese-l10n@googlegroups.com>\n"
@@ -373,6 +373,15 @@ msgid ""
 "interface."
 msgstr "對韌體的指示以開始設定介面需要驗證。"
 
+#: ../src/login/org.freedesktop.login1.policy.in.h:55
+msgid "Set a wall message"
+msgstr ""
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:56
+#, fuzzy
+msgid "Authentication is required to set a wall message"
+msgstr "設定主機名稱需要驗證。"
+
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:1
 msgid "Log into a local container"
 msgstr "登入到本機容器"
@@ -382,19 +391,68 @@ msgid "Authentication is required to log into a local container."
 msgstr "登入到本機容器需要驗證。"
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:3
+#, fuzzy
+msgid "Log into the local host"
+msgstr "登入到本機容器"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:4
+#, fuzzy
+msgid "Authentication is required to log into the local host."
+msgstr "登入到本機容器需要驗證。"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:5
+#, fuzzy
+msgid "Acquire a shell in a local container"
+msgstr "登入到本機容器"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:6
+#, fuzzy
+msgid "Authentication is required to acquire a shell in a local container."
+msgstr "登入到本機容器需要驗證。"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:7
+msgid "Acquire a shell on the local host"
+msgstr ""
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:8
+#, fuzzy
+msgid "Authentication is required to acquire a shell on the local host."
+msgstr "設定主機名稱需要驗證。"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:9
+#, fuzzy
+msgid "Acquire a pseudo TTY in a local container"
+msgstr "登入到本機容器"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:10
+#, fuzzy
+msgid ""
+"Authentication is required to acquire a pseudo TTY in a local container."
+msgstr "登入到本機容器需要驗證。"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:11
+msgid "Acquire a pseudo TTY on the local host"
+msgstr ""
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:12
+#, fuzzy
+msgid "Authentication is required to acquire a pseudo TTY on the local host."
+msgstr "設定主機名稱需要驗證。"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:13
 msgid "Manage local virtual machines and containers"
 msgstr "管理本機虛擬機器及容器"
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:4
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:14
 msgid ""
 "Authentication is required to manage local virtual machines and containers."
 msgstr "管理本機虛擬機器及容器需要驗證。"
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:5
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:15
 msgid "Manage local virtual machine and container images"
 msgstr "管理本機虛擬機器及容器映像"
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:6
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:16
 msgid ""
 "Authentication is required to manage local virtual machine and container "
 "images."
@@ -435,3 +493,38 @@ msgid ""
 "Authentication is required to control whether network time synchronization "
 "shall be enabled."
 msgstr "控制網路時間同步是否啟用需要驗證。"
+
+#: ../src/core/dbus-unit.c:428
+#, fuzzy
+msgid "Authentication is required to start '$(unit)'."
+msgstr "設定系統時間需要驗證。"
+
+#: ../src/core/dbus-unit.c:429
+#, fuzzy
+msgid "Authentication is required to stop '$(unit)'."
+msgstr "設定系統時間需要驗證。"
+
+#: ../src/core/dbus-unit.c:430
+#, fuzzy
+msgid "Authentication is required to reload '$(unit)'."
+msgstr "重新載入 systemd 狀態需要驗證。"
+
+#: ../src/core/dbus-unit.c:431 ../src/core/dbus-unit.c:432
+#, fuzzy
+msgid "Authentication is required to restart '$(unit)'."
+msgstr "設定系統時間需要驗證。"
+
+#: ../src/core/dbus-unit.c:535
+#, fuzzy
+msgid "Authentication is required to kill '$(unit)'."
+msgstr "登入到本機容器需要驗證。"
+
+#: ../src/core/dbus-unit.c:565
+#, fuzzy
+msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
+msgstr "設定主機名稱需要驗證。"
+
+#: ../src/core/dbus-unit.c:597
+#, fuzzy
+msgid "Authentication is required to set properties on '$(unit)'."
+msgstr "設定系統時間需要驗證。"
index 7a083d28756cde70594649ea676402b589bebdec..776eca4e626aeded37569c7e75f0c8d296287add 100644 (file)
@@ -41,7 +41,8 @@ _loginctl () {
         if __contains_word "$prev" ${OPTS[ARG]}; then
                 case $prev in
                         --signal|-s)
-                                comps=$(compgen -A signal)
+                                _signals
+                                return
                         ;;
                         --kill-who)
                                 comps='all leader'
index 140465d31685d3fffe9353f30a112919f0a3e179..61c540278607357df32051c270b559d7c4a33359 100644 (file)
@@ -57,7 +57,8 @@ _machinectl() {
         if __contains_word "$prev" ${OPTS[ARG]}; then
                 case $prev in
                         --signal|-s)
-                                comps=$(compgen -A signal)
+                                _signals
+                                return
                         ;;
                         --kill-who)
                                 comps='all leader'
index d80d8f02a810479bf736c39646690270eefd3038..6ffab33e4592234d492bebf0aad11cabf083ef9c 100644 (file)
@@ -115,7 +115,8 @@ _systemctl () {
         if __contains_word "$prev" ${OPTS[ARG]}; then
                 case $prev in
                         --signal|-s)
-                                comps=$(compgen -A signal)
+                                _signals
+                                return
                         ;;
                         --type|-t)
                                 comps=$(__systemctl $mode -t help)
index f9b740380c26546e294fa2bf4b8a97aaf20bbd39..429e712eb3f526fa63987ebbf08923951625fe05 100644 (file)
@@ -57,7 +57,7 @@ _systemd_nspawn() {
                       [ARG]='-D --directory -u --user --uuid --capability --drop-capability --link-journal --bind --bind-ro -M --machine
                              -S --slice --setenv -Z --selinux-context -L --selinux-apifs-context --register --network-interface --network-bridge
                              --personality -i --image --tmpfs --volatile
-                             --network-macvlan'
+                             --network-macvlan --kill-signal'
         )
 
         _init_completion || return
@@ -132,6 +132,10 @@ _systemd_nspawn() {
                                 compopt -o nospace
                                 comps=$( compgen -A file -- "$cur" )
                         ;;
+                        --kill-signal)
+                                _signals
+                                return
+                        ;;
                 esac
                 COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
                 return 0
index 6de331c73e5f8b4d341a4680be9c8a35c3c31a42..026d3cd9b1e2350e2206cad9f6435fd909a1d893 100644 (file)
@@ -21,8 +21,8 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <stdint.h>
 #include <stdbool.h>
+#include <stdint.h>
 #include <sys/types.h>
 
 #define AUDIT_SESSION_INVALID ((uint32_t) -1)
index 2874bc99f7b90fbfb000e449f2da22f009262904..9ce7b42d00b1c79d59a971d5c804922f222230e4 100644 (file)
@@ -21,8 +21,8 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include "macro.h"
 #include "hashmap.h"
+#include "macro.h"
 
 typedef struct Bitmap Bitmap;
 
index 157ae1fb74b327000f68c7d42357e1f5acdfad88..8f60561ede715138e370a0e89f1c2899df941978 100644 (file)
@@ -25,6 +25,7 @@
 #include "alloc-util.h"
 #include "calendarspec.h"
 #include "fileio.h"
+#include "parse-util.h"
 #include "string-util.h"
 
 #define BITS_WEEKDAYS   127
@@ -49,7 +50,7 @@ void calendar_spec_free(CalendarSpec *c) {
         free_chain(c->day);
         free_chain(c->hour);
         free_chain(c->minute);
-        free_chain(c->second);
+        free_chain(c->microsecond);
 
         free(c);
 }
@@ -135,7 +136,7 @@ int calendar_spec_normalize(CalendarSpec *c) {
         sort_chain(&c->day);
         sort_chain(&c->hour);
         sort_chain(&c->minute);
-        sort_chain(&c->second);
+        sort_chain(&c->microsecond);
 
         return 0;
 }
@@ -177,7 +178,7 @@ _pure_ bool calendar_spec_valid(CalendarSpec *c) {
         if (!chain_valid(c->minute, 0, 59))
                 return false;
 
-        if (!chain_valid(c->second, 0, 59))
+        if (!chain_valid(c->microsecond, 0, 60*USEC_PER_SEC-1))
                 return false;
 
         return true;
@@ -232,7 +233,7 @@ static void format_weekdays(FILE *f, const CalendarSpec *c) {
         }
 }
 
-static void format_chain(FILE *f, int space, const CalendarComponent *c) {
+static void format_chain(FILE *f, int space, const CalendarComponent *c, bool usec) {
         assert(f);
 
         if (!c) {
@@ -241,14 +242,25 @@ static void format_chain(FILE *f, int space, const CalendarComponent *c) {
         }
 
         assert(c->value >= 0);
-        fprintf(f, "%0*i", space, c->value);
-
-        if (c->repeat > 0)
-                fprintf(f, "/%i", c->repeat);
+        if (!usec)
+                fprintf(f, "%0*i", space, c->value);
+        else if (c->value % USEC_PER_SEC == 0)
+                fprintf(f, "%0*i", space, (int) (c->value / USEC_PER_SEC));
+        else
+                fprintf(f, "%0*i.%06i", space, (int) (c->value / USEC_PER_SEC), (int) (c->value % USEC_PER_SEC));
+
+        if (c->repeat > 0) {
+                if (!usec)
+                        fprintf(f, "/%i", c->repeat);
+                else if (c->repeat % USEC_PER_SEC == 0)
+                        fprintf(f, "/%i", (int) (c->repeat / USEC_PER_SEC));
+                else
+                        fprintf(f, "/%i.%06i", (int) (c->repeat / USEC_PER_SEC), (int) (c->repeat % USEC_PER_SEC));
+        }
 
         if (c->next) {
                 fputc(',', f);
-                format_chain(f, space, c->next);
+                format_chain(f, space, c->next, usec);
         }
 }
 
@@ -270,17 +282,17 @@ int calendar_spec_to_string(const CalendarSpec *c, char **p) {
                 fputc(' ', f);
         }
 
-        format_chain(f, 4, c->year);
+        format_chain(f, 4, c->year, false);
         fputc('-', f);
-        format_chain(f, 2, c->month);
+        format_chain(f, 2, c->month, false);
         fputc('-', f);
-        format_chain(f, 2, c->day);
+        format_chain(f, 2, c->day, false);
         fputc(' ', f);
-        format_chain(f, 2, c->hour);
+        format_chain(f, 2, c->hour, false);
         fputc(':', f);
-        format_chain(f, 2, c->minute);
+        format_chain(f, 2, c->minute, false);
         fputc(':', f);
-        format_chain(f, 2, c->second);
+        format_chain(f, 2, c->microsecond, true);
 
         if (c->utc)
                 fputs(" UTC", f);
@@ -391,35 +403,70 @@ static int parse_weekdays(const char **p, CalendarSpec *c) {
         }
 }
 
-static int prepend_component(const char **p, CalendarComponent **c) {
-        unsigned long value, repeat = 0;
-        char *e = NULL, *ee = NULL;
-        CalendarComponent *cc;
-
-        assert(p);
-        assert(c);
+static int parse_component_decimal(const char **p, bool usec, unsigned long *res) {
+        unsigned long value;
+        const char *e = NULL;
+        char *ee = NULL;
+        int r;
 
         errno = 0;
-        value = strtoul(*p, &e, 10);
+        value = strtoul(*p, &ee, 10);
         if (errno > 0)
                 return -errno;
-        if (e == *p)
+        if (ee == *p)
                 return -EINVAL;
         if ((unsigned long) (int) value != value)
                 return -ERANGE;
+        e = ee;
 
-        if (*e == '/') {
-                repeat = strtoul(e+1, &ee, 10);
-                if (errno > 0)
-                        return -errno;
-                if (ee == e+1)
-                        return -EINVAL;
-                if ((unsigned long) (int) repeat != repeat)
-                        return -ERANGE;
-                if (repeat <= 0)
+        if (usec) {
+                if (value * USEC_PER_SEC / USEC_PER_SEC != value)
                         return -ERANGE;
 
-                e = ee;
+                value *= USEC_PER_SEC;
+                if (*e == '.') {
+                        unsigned add;
+
+                        e++;
+                        r = parse_fractional_part_u(&e, 6, &add);
+                        if (r < 0)
+                                return r;
+
+                        if (add + value < value)
+                                return -ERANGE;
+                        value += add;
+                }
+        }
+
+        *p = e;
+        *res = value;
+
+        return 0;
+}
+
+static int prepend_component(const char **p, bool usec, CalendarComponent **c) {
+        unsigned long value, repeat = 0;
+        CalendarComponent *cc;
+        int r;
+        const char *e;
+
+        assert(p);
+        assert(c);
+
+        e = *p;
+
+        r = parse_component_decimal(&e, usec, &value);
+        if (r < 0)
+                return r;
+
+        if (*e == '/') {
+                e++;
+                r = parse_component_decimal(&e, usec, &repeat);
+                if (r < 0)
+                        return r;
+
+                if (repeat == 0)
+                        return -ERANGE;
         }
 
         if (*e != 0 && *e != ' ' && *e != ',' && *e != '-' && *e != ':')
@@ -438,13 +485,31 @@ static int prepend_component(const char **p, CalendarComponent **c) {
 
         if (*e ==',') {
                 *p += 1;
-                return prepend_component(p, c);
+                return prepend_component(p, usec, c);
         }
 
         return 0;
 }
 
-static int parse_chain(const char **p, CalendarComponent **c) {
+static int const_chain(int value, CalendarComponent **c) {
+        CalendarComponent *cc = NULL;
+
+        assert(c);
+
+        cc = new0(CalendarComponent, 1);
+        if (!cc)
+                return -ENOMEM;
+
+        cc->value = value;
+        cc->repeat = 0;
+        cc->next = *c;
+
+        *c = cc;
+
+        return 0;
+}
+
+static int parse_chain(const char **p, bool usec, CalendarComponent **c) {
         const char *t;
         CalendarComponent *cc = NULL;
         int r;
@@ -455,12 +520,19 @@ static int parse_chain(const char **p, CalendarComponent **c) {
         t = *p;
 
         if (t[0] == '*') {
+                if (usec) {
+                        r = const_chain(0, c);
+                        if (r < 0)
+                                return r;
+                        (*c)->repeat = USEC_PER_SEC;
+                } else
+                        *c = NULL;
+
                 *p = t + 1;
-                *c = NULL;
                 return 0;
         }
 
-        r = prepend_component(&t, &cc);
+        r = prepend_component(&t, usec, &cc);
         if (r < 0) {
                 free_chain(cc);
                 return r;
@@ -471,24 +543,6 @@ static int parse_chain(const char **p, CalendarComponent **c) {
         return 0;
 }
 
-static int const_chain(int value, CalendarComponent **c) {
-        CalendarComponent *cc = NULL;
-
-        assert(c);
-
-        cc = new0(CalendarComponent, 1);
-        if (!cc)
-                return -ENOMEM;
-
-        cc->value = value;
-        cc->repeat = 0;
-        cc->next = *c;
-
-        *c = cc;
-
-        return 0;
-}
-
 static int parse_date(const char **p, CalendarSpec *c) {
         const char *t;
         int r;
@@ -503,7 +557,7 @@ static int parse_date(const char **p, CalendarSpec *c) {
         if (*t == 0)
                 return 0;
 
-        r = parse_chain(&t, &first);
+        r = parse_chain(&t, false, &first);
         if (r < 0)
                 return r;
 
@@ -519,7 +573,7 @@ static int parse_date(const char **p, CalendarSpec *c) {
         }
 
         t++;
-        r = parse_chain(&t, &second);
+        r = parse_chain(&t, false, &second);
         if (r < 0) {
                 free_chain(first);
                 return r;
@@ -540,7 +594,7 @@ static int parse_date(const char **p, CalendarSpec *c) {
         }
 
         t++;
-        r = parse_chain(&t, &third);
+        r = parse_chain(&t, false, &third);
         if (r < 0) {
                 free_chain(first);
                 free_chain(second);
@@ -582,7 +636,7 @@ static int parse_calendar_time(const char **p, CalendarSpec *c) {
                 goto finish;
         }
 
-        r = parse_chain(&t, &h);
+        r = parse_chain(&t, false, &h);
         if (r < 0)
                 goto fail;
 
@@ -592,7 +646,7 @@ static int parse_calendar_time(const char **p, CalendarSpec *c) {
         }
 
         t++;
-        r = parse_chain(&t, &m);
+        r = parse_chain(&t, false, &m);
         if (r < 0)
                 goto fail;
 
@@ -610,7 +664,7 @@ static int parse_calendar_time(const char **p, CalendarSpec *c) {
         }
 
         t++;
-        r = parse_chain(&t, &s);
+        r = parse_chain(&t, true, &s);
         if (r < 0)
                 goto fail;
 
@@ -639,7 +693,8 @@ finish:
         *p = t;
         c->hour = h;
         c->minute = m;
-        c->second = s;
+        c->microsecond = s;
+
         return 0;
 
 fail:
@@ -671,7 +726,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
         }
 
         if (strcaseeq(p, "minutely")) {
-                r = const_chain(0, &c->second);
+                r = const_chain(0, &c->microsecond);
                 if (r < 0)
                         goto fail;
 
@@ -679,7 +734,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
                 r = const_chain(0, &c->minute);
                 if (r < 0)
                         goto fail;
-                r = const_chain(0, &c->second);
+                r = const_chain(0, &c->microsecond);
                 if (r < 0)
                         goto fail;
 
@@ -690,7 +745,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
                 r = const_chain(0, &c->minute);
                 if (r < 0)
                         goto fail;
-                r = const_chain(0, &c->second);
+                r = const_chain(0, &c->microsecond);
                 if (r < 0)
                         goto fail;
 
@@ -704,7 +759,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
                 r = const_chain(0, &c->minute);
                 if (r < 0)
                         goto fail;
-                r = const_chain(0, &c->second);
+                r = const_chain(0, &c->microsecond);
                 if (r < 0)
                         goto fail;
 
@@ -724,7 +779,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
                 r = const_chain(0, &c->minute);
                 if (r < 0)
                         goto fail;
-                r = const_chain(0, &c->second);
+                r = const_chain(0, &c->microsecond);
                 if (r < 0)
                         goto fail;
 
@@ -738,7 +793,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
                 r = const_chain(0, &c->minute);
                 if (r < 0)
                         goto fail;
-                r = const_chain(0, &c->second);
+                r = const_chain(0, &c->microsecond);
                 if (r < 0)
                         goto fail;
 
@@ -765,7 +820,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
                 r = const_chain(0, &c->minute);
                 if (r < 0)
                         goto fail;
-                r = const_chain(0, &c->second);
+                r = const_chain(0, &c->microsecond);
                 if (r < 0)
                         goto fail;
 
@@ -789,7 +844,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
                 r = const_chain(0, &c->minute);
                 if (r < 0)
                         goto fail;
-                r = const_chain(0, &c->second);
+                r = const_chain(0, &c->microsecond);
                 if (r < 0)
                         goto fail;
 
@@ -906,14 +961,16 @@ static bool matches_weekday(int weekdays_bits, const struct tm *tm, bool utc) {
         return (weekdays_bits & (1 << k));
 }
 
-static int find_next(const CalendarSpec *spec, struct tm *tm) {
+static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) {
         struct tm c;
+        int tm_usec;
         int r;
 
         assert(spec);
         assert(tm);
 
         c = *tm;
+        tm_usec = *usec;
 
         for (;;) {
                 /* Normalize the current date */
@@ -927,7 +984,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) {
                 if (r > 0) {
                         c.tm_mon = 0;
                         c.tm_mday = 1;
-                        c.tm_hour = c.tm_min = c.tm_sec = 0;
+                        c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
                 }
                 if (r < 0 || tm_out_of_bounds(&c, spec->utc))
                         return r;
@@ -938,29 +995,29 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) {
 
                 if (r > 0) {
                         c.tm_mday = 1;
-                        c.tm_hour = c.tm_min = c.tm_sec = 0;
+                        c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
                 }
                 if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
                         c.tm_year ++;
                         c.tm_mon = 0;
                         c.tm_mday = 1;
-                        c.tm_hour = c.tm_min = c.tm_sec = 0;
+                        c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
                         continue;
                 }
 
                 r = find_matching_component(spec->day, &c.tm_mday);
                 if (r > 0)
-                        c.tm_hour = c.tm_min = c.tm_sec = 0;
+                        c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
                 if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
                         c.tm_mon ++;
                         c.tm_mday = 1;
-                        c.tm_hour = c.tm_min = c.tm_sec = 0;
+                        c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
                         continue;
                 }
 
                 if (!matches_weekday(spec->weekdays_bits, &c, spec->utc)) {
                         c.tm_mday++;
-                        c.tm_hour = c.tm_min = c.tm_sec = 0;
+                        c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
                         continue;
                 }
 
@@ -969,7 +1026,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) {
                         c.tm_min = c.tm_sec = 0;
                 if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
                         c.tm_mday ++;
-                        c.tm_hour = c.tm_min = c.tm_sec = 0;
+                        c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
                         continue;
                 }
 
@@ -978,19 +1035,23 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) {
                         c.tm_sec = 0;
                 if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
                         c.tm_hour ++;
-                        c.tm_min = c.tm_sec = 0;
+                        c.tm_min = c.tm_sec = tm_usec = 0;
                         continue;
                 }
 
-                r = find_matching_component(spec->second, &c.tm_sec);
+                c.tm_sec = c.tm_sec * USEC_PER_SEC + tm_usec;
+                r = find_matching_component(spec->microsecond, &c.tm_sec);
+                tm_usec = c.tm_sec % USEC_PER_SEC;
+                c.tm_sec /= USEC_PER_SEC;
+
                 if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
                         c.tm_min ++;
-                        c.tm_sec = 0;
+                        c.tm_sec = tm_usec = 0;
                         continue;
                 }
 
-
                 *tm = c;
+                *usec = tm_usec;
                 return 0;
         }
 }
@@ -999,14 +1060,17 @@ int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next)
         struct tm tm;
         time_t t;
         int r;
+        usec_t tm_usec;
 
         assert(spec);
         assert(next);
 
-        t = (time_t) (usec / USEC_PER_SEC) + 1;
+        usec++;
+        t = (time_t) (usec / USEC_PER_SEC);
         assert_se(localtime_or_gmtime_r(&t, &tm, spec->utc));
+        tm_usec = usec % USEC_PER_SEC;
 
-        r = find_next(spec, &tm);
+        r = find_next(spec, &tm, &tm_usec);
         if (r < 0)
                 return r;
 
@@ -1014,6 +1078,6 @@ int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next)
         if (t == (time_t) -1)
                 return -EINVAL;
 
-        *next = (usec_t) t * USEC_PER_SEC;
+        *next = (usec_t) t * USEC_PER_SEC + tm_usec;
         return 0;
 }
index 56dc02f391bc12cd8cc00d73a66d80ed915d6089..75b699682a7ef85cce76c3d1535a320d255c62d6 100644 (file)
@@ -25,6 +25,7 @@
  * time, a la cron */
 
 #include <stdbool.h>
+
 #include "util.h"
 
 typedef struct CalendarComponent {
@@ -44,7 +45,7 @@ typedef struct CalendarSpec {
 
         CalendarComponent *hour;
         CalendarComponent *minute;
-        CalendarComponent *second;
+        CalendarComponent *microsecond;
 } CalendarSpec;
 
 void calendar_spec_free(CalendarSpec *c);
index 01359fa7cb40643892704a5500db3f95fac42a23..a80ee60bd3d7bab122a05553bac951f349764dc1 100644 (file)
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <sys/types.h>
-#include <stdio.h>
 #include <dirent.h>
+#include <stdio.h>
+#include <sys/types.h>
 
-#include "set.h"
 #include "def.h"
+#include "set.h"
 
 /* An enum of well known cgroup controllers */
 typedef enum CGroupController {
index 4815161b092afb5d1d04eef1856b8db7341d6ca9..42a84c93174e619736e33b7a395179c947deca9b 100644 (file)
@@ -89,20 +89,20 @@ size_t cescape_char(char c, char *buf) {
         return buf - buf_old;
 }
 
-char *cescape(const char *s) {
-        char *r, *t;
+char *cescape_length(const char *s, size_t n) {
         const char *f;
+        char *r, *t;
 
-        assert(s);
+        assert(s || n == 0);
 
         /* Does C style string escaping. May be reversed with
          * cunescape(). */
 
-        r = new(char, strlen(s)*4 + 1);
+        r = new(char, n*4 + 1);
         if (!r)
                 return NULL;
 
-        for (f = s, t = r; *f; f++)
+        for (f = s, t = r; f < s + n; f++)
                 t += cescape_char(*f, t);
 
         *t = 0;
@@ -110,6 +110,12 @@ char *cescape(const char *s) {
         return r;
 }
 
+char *cescape(const char *s) {
+        assert(s);
+
+        return cescape_length(s, strlen(s));
+}
+
 int cunescape_one(const char *p, size_t length, char *ret, uint32_t *ret_unicode) {
         int r = 1;
 
index 85ba9090818f3626d12787de0b4aee42f0db29f0..52ebf11c4a46d01b9d6ecd0519397d67ea3c5b5a 100644 (file)
@@ -21,8 +21,8 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <sys/types.h>
 #include <inttypes.h>
+#include <sys/types.h>
 
 /* What characters are special in the shell? */
 /* must be escaped outside and inside double-quotes */
@@ -35,6 +35,7 @@ typedef enum UnescapeFlags {
 } UnescapeFlags;
 
 char *cescape(const char *s);
+char *cescape_length(const char *s, size_t n);
 size_t cescape_char(char c, char *buf);
 
 int cunescape(const char *s, UnescapeFlags flags, char **ret);
index 0e9182d75be165fbc2a59892ac281ac6785f3fba..5ce1592eeb414afaf24caee3b48e104006bb18e5 100644 (file)
@@ -21,9 +21,9 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <stdio.h>
 #include <dirent.h>
 #include <stdbool.h>
+#include <stdio.h>
 #include <sys/socket.h>
 
 #include "macro.h"
index 25fa351be276eb26475cb9f9ce16910f555b9195..9feb3cccb52f50d84627871f45a3c9e2055f3a53 100644 (file)
@@ -23,6 +23,7 @@
 ***/
 
 #include <stdio.h>
+
 #include "fileio.h"
 
 int write_string_file_atomic_label(const char *fn, const char *line);
index 902c7e295b53502f7043a7d3096053e6df549a1d..5fbb7bc4c3e0f74c3c9616a590400060aca575e3 100644 (file)
 ***/
 
 #include <fcntl.h>
+#include <limits.h>
 #include <sys/inotify.h>
 #include <sys/types.h>
 #include <unistd.h>
-#include <limits.h>
 
 #include "time-util.h"
 
index e70818fdd74757d3bbbf2fa6ca1d94cf960df8f7..b03aa431603c64a834d596a4962b9c83bdbca81e 100644 (file)
@@ -6,8 +6,8 @@
 
 #pragma once
 
-#include <stdint.h>
 #include <stdbool.h>
+#include <stdint.h>
 #include <stdlib.h>
 
 char *utf8_prev_char (const char *p);
index ea0528c6fcbd19dafbfcdd7d1b687151366c2199..c57a3cbd60e4ea8a901e1f03ffd4edfa1416e3cf 100644 (file)
@@ -72,7 +72,7 @@ static bool hostname_valid_char(char c) {
  * allow_trailing_dot is true and at least two components are present
  * in the name. Note that due to the restricted charset and length
  * this call is substantially more conservative than
- * dns_domain_is_valid().
+ * dns_name_is_valid().
  */
 bool hostname_is_valid(const char *s, bool allow_trailing_dot) {
         unsigned n_dots = 0;
index f4e24121e7b80b8a0c49e1df2f81a2f45ea9dcc7..b75c39aac753c22b977f6c49b7d575286915dd7f 100644 (file)
@@ -44,7 +44,7 @@ int in_addr_is_link_local(int family, const union in_addr_union *u) {
         assert(u);
 
         if (family == AF_INET)
-                return (be32toh(u->in.s_addr) & 0xFFFF0000) == (169U << 24 | 254U << 16);
+                return (be32toh(u->in.s_addr) & UINT32_C(0xFFFF0000)) == (UINT32_C(169) << 24 | UINT32_C(254) << 16);
 
         if (family == AF_INET6)
                 return IN6_IS_ADDR_LINKLOCAL(&u->in6);
@@ -52,6 +52,19 @@ int in_addr_is_link_local(int family, const union in_addr_union *u) {
         return -EAFNOSUPPORT;
 }
 
+int in_addr_is_localhost(int family, const union in_addr_union *u) {
+        assert(u);
+
+        if (family == AF_INET)
+                /* All of 127.x.x.x is localhost. */
+                return (be32toh(u->in.s_addr) & UINT32_C(0xFF000000)) == UINT32_C(127) << 24;
+
+        if (family == AF_INET6)
+                return IN6_IS_ADDR_LOOPBACK(&u->in6);
+
+        return -EAFNOSUPPORT;
+}
+
 int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b) {
         assert(a);
         assert(b);
index 51af08868cda48af0db4681613a5be4b853234ac..58f55b341850c866d39f724e2c04d833f2301f8c 100644 (file)
@@ -33,6 +33,7 @@ union in_addr_union {
 
 int in_addr_is_null(int family, const union in_addr_union *u);
 int in_addr_is_link_local(int family, const union in_addr_union *u);
+int in_addr_is_localhost(int family, const union in_addr_union *u);
 int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b);
 int in_addr_prefix_intersect(int family, const union in_addr_union *a, unsigned aprefixlen, const union in_addr_union *b, unsigned bprefixlen);
 int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen);
index e5c71d0043249cff9bd8c8bdc95eba5758e0ccea..d8bb6eb4971b1332e2ba378e7beb20e21d475a05 100644 (file)
@@ -4,8 +4,8 @@
 /* This is minimal version of Linux' linux/ioprio.h header file, which
  * is licensed GPL2 */
 
-#include <unistd.h>
 #include <sys/syscall.h>
+#include <unistd.h>
 
 /*
  * Gives us 8 prio classes with 13-bits of data for each class
index e0b4d810b5b370fa21ada8d4e10e71868e3f6f10..8a7d79cb17e09ced60d332033221e75c52474fa1 100644 (file)
@@ -22,6 +22,7 @@
 ***/
 
 #include <stdbool.h>
+
 #include "util.h"
 
 enum {
index 2cb404ea811099f1cdc05b5d15be44a8032aecac..3e4de008a4ff3ffdeb4c857fa83b703fad8d9773 100644 (file)
@@ -21,8 +21,8 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <sys/types.h>
 #include <inttypes.h>
+#include <sys/types.h>
 
 int memfd_new(const char *name);
 int memfd_new_and_map(const char *name, size_t sz, void **p);
index 6c617ab3055eb02607a852c5f51e0a9b112ace64..da10e90ff2811cdb4979b81536c165c63a3bce9d 100644 (file)
@@ -29,6 +29,17 @@ static inline OrderedSet* ordered_set_new(const struct hash_ops *ops) {
         return (OrderedSet*) ordered_hashmap_new(ops);
 }
 
+static inline int ordered_set_ensure_allocated(OrderedSet **s, const struct hash_ops *ops) {
+        if (*s)
+                return 0;
+
+        *s = ordered_set_new(ops);
+        if (!*s)
+                return -ENOMEM;
+
+        return 0;
+}
+
 static inline OrderedSet* ordered_set_free(OrderedSet *s) {
         ordered_hashmap_free((OrderedHashmap*) s);
         return NULL;
index 151067e91604d3a15d22184850213363af7a157f..3ae99d93346de28829528e434f114089feb1fa25 100644 (file)
@@ -490,3 +490,39 @@ int safe_atod(const char *s, double *ret_d) {
         *ret_d = (double) d;
         return 0;
 }
+
+int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) {
+        size_t i;
+        unsigned val = 0;
+        const char *s;
+
+        s = *p;
+
+        /* accept any number of digits, strtoull is limted to 19 */
+        for(i=0; i < digits; i++,s++) {
+                if (*s < '0' || *s > '9') {
+                        if (i == 0)
+                                return -EINVAL;
+
+                        /* too few digits, pad with 0 */
+                        for (; i < digits; i++)
+                                val *= 10;
+
+                        break;
+                }
+
+                val *= 10;
+                val += *s - '0';
+        }
+
+        /* maybe round up */
+        if (*s >= '5' && *s <= '9')
+                val++;
+
+        s += strspn(s, DIGITS);
+
+        *p = s;
+        *res = val;
+
+        return 0;
+}
index 408690d0b3ac0bd2bbb6140c3ba00c9106a03f05..125de53d7afa06a59e8b4bde2a80820d54f89b26 100644 (file)
@@ -90,3 +90,5 @@ static inline int safe_atoli(const char *s, long int *ret_u) {
 #endif
 
 int safe_atod(const char *s, double *ret_d);
+
+int parse_fractional_part_u(const char **s, size_t digits, unsigned *res);
index 72633ebf70581ff29ec5d26e5aa3c115cdfb2c6d..fdc7e1bdef76d7c430e4ad140e709cacb15c8c2e 100644 (file)
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <stdbool.h>
-#include <sys/types.h>
 #include <alloca.h>
+#include <signal.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <string.h>
-#include <signal.h>
+#include <sys/types.h>
 
 #include "formats-util.h"
 #include "macro.h"
index 2afcaec183eceba147d0f4411aab2d4ebf27080c..d19984c5fe84fd13dcc0e5c915213d67f53558b7 100644 (file)
@@ -21,8 +21,8 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <sys/socket.h>
 #include <stdbool.h>
+#include <sys/socket.h>
 
 #include "macro.h"
 
index 4554ef2d49fae8b19191484437f578daa06c2824..5fd7de08f9ec371b418bb02b6ca20646f351e9fe 100644 (file)
@@ -27,7 +27,6 @@
 Set *internal_set_new(const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS);
 #define set_new(ops) internal_set_new(ops  HASHMAP_DEBUG_SRC_ARGS)
 
-
 static inline Set *set_free(Set *s) {
         internal_hashmap_free(HASHMAP_BASE(s));
         return NULL;
index c60f2556af840e86806adbea785ce7a86a30d239..129ffa811ccc405d6bd9036d9b1065f2c2f72278 100644 (file)
@@ -21,9 +21,9 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <sys/socket.h>
-#include <netinet/in.h>
 #include <netinet/ether.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
 #include <sys/un.h>
 #include <linux/netlink.h>
 #include <linux/if_packet.h>
index 909b220a245c9a446e851100e25b7ad1af0f2def..fb9246427464c375938f7751ae9a07d8b4f24cf0 100644 (file)
@@ -52,9 +52,8 @@ int path_is_os_tree(const char *path);
 int files_same(const char *filea, const char *fileb);
 
 /* The .f_type field of struct statfs is really weird defined on
- * different archs. Let's use our own type we know is sufficiently
- * larger to store the possible values. */
-typedef long statfs_f_type_t;
+ * different archs. Let's give its type a name. */
+typedef typeof(((struct statfs*)NULL)->f_type) statfs_f_type_t;
 
 bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) _pure_;
 int fd_check_fstype(int fd, statfs_f_type_t magic_value);
index f2185c1c117d876c7d3ff4287bae8928ef553b5c..b2c7a297ae816ecddda0c82d7f3919988f296afa 100644 (file)
@@ -19,8 +19,8 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <stdbool.h>
 #include <stdarg.h>
+#include <stdbool.h>
 #include <stdio.h>
 
 #include "macro.h"
index 647763a2309883517e5f56def3a21a457a13b73f..b9da6991da0192b9977cb3f91166c08ecafdac0a 100644 (file)
@@ -27,6 +27,7 @@
 #include "fd-util.h"
 #include "fileio.h"
 #include "fs-util.h"
+#include "parse-util.h"
 #include "path-util.h"
 #include "string-util.h"
 #include "strv.h"
@@ -658,29 +659,18 @@ int parse_timestamp(const char *t, usec_t *usec) {
 
 parse_usec:
         {
-                char *end;
-                unsigned long long val;
-                size_t l;
+                unsigned add;
 
                 k++;
-                if (*k < '0' || *k > '9')
+                r = parse_fractional_part_u(&k, 6, &add);
+                if (r < 0)
                         return -EINVAL;
 
-                /* base 10 instead of base 0, .09 is not base 8 */
-                errno = 0;
-                val = strtoull(k, &end, 10);
-                if (*end || errno)
+                if (*k)
                         return -EINVAL;
 
-                l = end-k;
-
-                /* val has l digits, make them 6 */
-                for (; l < 6; l++)
-                        val *= 10;
-                for (; l > 6; l--)
-                        val /= 10;
+                x_usec = add;
 
-                x_usec = val;
         }
 
 from_tm:
index 11ff6674cf5b7272989c9ce76b9223951e8c3691..6106e138bed0d9ff2c60218f72fcd031abdc1d39 100644 (file)
@@ -21,8 +21,8 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <sys/types.h>
 #include <stdbool.h>
+#include <sys/types.h>
 
 bool uid_is_valid(uid_t uid);
 
index 1e5d6eea6e8b9b340f7e03187526efc5df4b138f..b82680a54bfbebceea112d4136129726069a3a1e 100644 (file)
@@ -269,13 +269,20 @@ int detect_vm(void) {
         if (cached_found >= 0)
                 return cached_found;
 
-        r = detect_vm_cpuid();
+        /* We have to use the correct order here:
+         * Some virtualization technologies do use KVM hypervisor but are
+         * expected to be detected as something else. So detect DMI first.
+         *
+         * An example is Virtualbox since version 5.0, which uses KVM backend.
+         * Detection via DMI works corretly, the CPU ID would find KVM
+         * only. */
+        r = detect_vm_dmi();
         if (r < 0)
                 return r;
         if (r != VIRTUALIZATION_NONE)
                 goto finish;
 
-        r = detect_vm_dmi();
+        r = detect_vm_cpuid();
         if (r < 0)
                 return r;
         if (r != VIRTUALIZATION_NONE)
index bdb4b001991de011f08045d00fafb1e46191f913..8432a2a119422edfb70c7cf18d470f9774075630 100644 (file)
@@ -25,6 +25,7 @@
 ***/
 
 #include <stdbool.h>
+
 #include "list.h"
 
 #define MAXCPUS        16
index bbb4796efdd628a139c7fd15df3b205bf7dd6a0c..4d2e0d439f0b380e9efe26ef3271c2598660dc62 100644 (file)
@@ -25,6 +25,7 @@
 ***/
 
 #include <dirent.h>
+
 #include "bootchart.h"
 
 double gettime_ns(void);
index 8f0ab8f17ff08434f2dd30c8c012eef1b22e7119..8dde0cb86894a3dc28b024aceef07b3434a2e0ce 100644 (file)
@@ -23,8 +23,8 @@
 
 #include <pthread.h>
 
-#include "list.h"
 #include "hashmap.h"
+#include "list.h"
 
 typedef enum PolicyItemType {
         _POLICY_ITEM_TYPE_UNSET = 0,
index da3834f8b05a707f9359cdd25999c5a25c7b1e6f..9f689024412670f75c1720e321affd19d7730c42 100644 (file)
@@ -22,6 +22,7 @@
 ***/
 
 #include "sd-bus.h"
+
 #include "bus-xml-policy.h"
 #include "proxy.h"
 
index 4a31f4c4be956157f0e3dc7f183ab94bee4831c6..f6c5f7c5af97548fe761a7a399f5475ea24ad7d4 100644 (file)
@@ -24,8 +24,8 @@
 typedef struct BusEndpoint BusEndpoint;
 typedef struct BusEndpointPolicy BusEndpointPolicy;
 
-#include "hashmap.h"
 #include "bus-policy.h"
+#include "hashmap.h"
 
 struct BusEndpointPolicy {
         char *name;
index 3b04f5457a7179089fbce3f575604573920b484d..2f612891855b20e2729545036c2284b93b871d2a 100644 (file)
@@ -21,9 +21,9 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
+#include "kdbus.h"
 #include "list.h"
 #include "macro.h"
-#include "kdbus.h"
 
 typedef struct BusNamePolicy BusNamePolicy;
 
index 457544b49f30b326dd5d5f78845b07a7bd2eee77..1b18d06652ef0b6346cba5ebcaa26bd7fa60e40e 100644 (file)
@@ -112,8 +112,8 @@ struct CGroupContext {
         bool delegate;
 };
 
-#include "unit.h"
 #include "cgroup-util.h"
+#include "unit.h"
 
 void cgroup_context_init(CGroupContext *c);
 void cgroup_context_done(CGroupContext *c);
index 794c402048adf3c663cad1747e717908b40d48c0..1d32fca54728f2cbdc0dc63b6165614088b85b40 100644 (file)
@@ -23,8 +23,8 @@
 
 #include "sd-bus.h"
 
-#include "unit.h"
 #include "kill.h"
+#include "unit.h"
 
 extern const sd_bus_vtable bus_kill_vtable[];
 
index 67e4e8b218fe339fa671825b2a4391b2906d642a..2562396180934b1f6fb5990e631cf9b1de8ca670 100644 (file)
@@ -630,9 +630,13 @@ static int method_set_unit_properties(sd_bus_message *message, void *userdata, s
         if (r < 0)
                 return r;
 
-        u = manager_get_unit(m, name);
-        if (!u)
-                return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
+        r = manager_load_unit(m, name, NULL, error, &u);
+        if (r < 0)
+                return r;
+
+        r = bus_unit_check_load_state(u, error);
+        if (r < 0)
+                return r;
 
         return bus_unit_method_set_properties(message, u, error);
 }
@@ -644,6 +648,7 @@ static int transient_unit_from_message(
                 Unit **unit,
                 sd_bus_error *error) {
 
+        UnitType t;
         Unit *u;
         int r;
 
@@ -651,23 +656,18 @@ static int transient_unit_from_message(
         assert(message);
         assert(name);
 
+        t = unit_name_to_type(name);
+        if (t < 0)
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid unit name or type.");
+
+        if (!unit_vtable[t]->can_transient)
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unit type %s does not support transient units.", unit_type_to_string(t));
+
         r = manager_load_unit(m, name, NULL, error, &u);
         if (r < 0)
                 return r;
 
-        /* Check if the unit already exists or is already referenced,
-         * in a number of different ways. Note that to cater for unit
-         * types such as slice, we are generally fine with units that
-         * are marked UNIT_LOADED even even though nothing was
-         * actually loaded, as those unit types don't require a file
-         * on disk to validly load. */
-
-        if (!IN_SET(u->load_state, UNIT_NOT_FOUND, UNIT_LOADED) ||
-            u->fragment_path ||
-            u->source_path ||
-            !strv_isempty(u->dropin_paths) ||
-            u->refs ||
-            set_size(u->dependencies[UNIT_REFERENCED_BY]) > 0)
+        if (!unit_is_pristine(u))
                 return sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS, "Unit %s already exists.", name);
 
         /* OK, the unit failed to load and is unreferenced, now let's
@@ -681,6 +681,9 @@ static int transient_unit_from_message(
         if (r < 0)
                 return r;
 
+        /* Now load the missing bits of the unit we just created */
+        manager_dispatch_load_queue(m);
+
         *unit = u;
 
         return 0;
@@ -691,8 +694,6 @@ static int transient_aux_units_from_message(
                 sd_bus_message *message,
                 sd_bus_error *error) {
 
-        Unit *u;
-        char *name = NULL;
         int r;
 
         assert(m);
@@ -703,20 +704,17 @@ static int transient_aux_units_from_message(
                 return r;
 
         while ((r = sd_bus_message_enter_container(message, 'r', "sa(sv)")) > 0) {
+                const char *name = NULL;
+                Unit *u;
+
                 r = sd_bus_message_read(message, "s", &name);
                 if (r < 0)
                         return r;
 
                 r = transient_unit_from_message(m, message, name, &u, error);
-                if (r < 0 && r != -EEXIST)
+                if (r < 0)
                         return r;
 
-                if (r != -EEXIST) {
-                        r = unit_load(u);
-                        if (r < 0)
-                                return r;
-                }
-
                 r = sd_bus_message_exit_container(message);
                 if (r < 0)
                         return r;
@@ -735,7 +733,6 @@ static int method_start_transient_unit(sd_bus_message *message, void *userdata,
         const char *name, *smode;
         Manager *m = userdata;
         JobMode mode;
-        UnitType t;
         Unit *u;
         int r;
 
@@ -750,13 +747,6 @@ static int method_start_transient_unit(sd_bus_message *message, void *userdata,
         if (r < 0)
                 return r;
 
-        t = unit_name_to_type(name);
-        if (t < 0)
-                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid unit type.");
-
-        if (!unit_vtable[t]->can_transient)
-                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unit type %s does not support transient units.", unit_type_to_string(t));
-
         mode = job_mode_from_string(smode);
         if (mode < 0)
                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Job mode %s is invalid.", smode);
@@ -775,13 +765,6 @@ static int method_start_transient_unit(sd_bus_message *message, void *userdata,
         if (r < 0)
                 return r;
 
-        /* And load this stub fully */
-        r = unit_load(u);
-        if (r < 0)
-                return r;
-
-        manager_dispatch_load_queue(m);
-
         /* Finally, start it */
         return bus_unit_queue_job(message, u, JOB_START, mode, false, error);
 }
index 33beda47b77d46f7043230ecafa3325890b27063..4fb0b25e095be5efe168fc9253ece0bd3b11f844 100644 (file)
@@ -22,6 +22,7 @@
 ***/
 
 #include "sd-bus.h"
+
 #include "unit.h"
 
 extern const sd_bus_vtable bus_scope_vtable[];
index aab9f7aa264a74827c6cce5020cf530e1f74921a..a67b64ab5bb71e83177e2320e35fe759d8147693 100644 (file)
@@ -22,6 +22,7 @@
 ***/
 
 #include "sd-bus.h"
+
 #include "unit.h"
 
 extern const sd_bus_vtable bus_service_vtable[];
index eadc3b1a9cb2d3998682d5dfb70726d8981f530b..117d11471bab5740b9c2dbbed148ea3d621ded6c 100644 (file)
@@ -22,6 +22,7 @@
 ***/
 
 #include "sd-bus.h"
+
 #include "unit.h"
 
 extern const sd_bus_vtable bus_slice_vtable[];
index be5ef261a654283335f3192df513997cf138fb9a..895dd0775313b7f4029ec8ecfea20779495b437e 100644 (file)
@@ -150,6 +150,7 @@ const sd_bus_vtable bus_socket_vtable[] = {
         SD_BUS_PROPERTY("NConnections", "u", bus_property_get_unsigned, offsetof(Socket, n_connections), 0),
         SD_BUS_PROPERTY("NAccepted", "u", bus_property_get_unsigned, offsetof(Socket, n_accepted), 0),
         SD_BUS_PROPERTY("FileDescriptorName", "s", property_get_fdname, 0, 0),
+        SD_BUS_PROPERTY("SocketProtocol", "i", bus_property_get_int, offsetof(Socket, socket_protocol), SD_BUS_VTABLE_PROPERTY_CONST),
         BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPre", offsetof(Socket, exec_command[SOCKET_EXEC_START_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
         BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPost", offsetof(Socket, exec_command[SOCKET_EXEC_START_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
         BUS_EXEC_COMMAND_LIST_VTABLE("ExecStopPre", offsetof(Socket, exec_command[SOCKET_EXEC_STOP_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
index 17164d98713266d0e130c3f2a3743dd9cbc5c4b7..8dad6ea2e980b644213ce68336eb0aa304b16370 100644 (file)
@@ -22,6 +22,7 @@
 ***/
 
 #include "sd-bus.h"
+
 #include "unit.h"
 
 extern const sd_bus_vtable bus_socket_vtable[];
index 9469f68ab89486618618978ccdafa533b3604517..a414ca7f758bda300ac62aaaf9ce587644625572 100644 (file)
@@ -23,6 +23,7 @@
 ***/
 
 #include "sd-bus.h"
+
 #include "unit.h"
 
 extern const sd_bus_vtable bus_swap_vtable[];
index a8a280d961202c684d87615652213241d5dbb660..ec301df6d74108b46f7519772c63044a115403d6 100644 (file)
@@ -180,8 +180,10 @@ const sd_bus_vtable bus_timer_vtable[] = {
         BUS_PROPERTY_DUAL_TIMESTAMP("LastTriggerUSec", offsetof(Timer, last_trigger), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Timer, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("AccuracyUSec", "t", bus_property_get_usec, offsetof(Timer, accuracy_usec), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("RandomizedDelayUSec", "t", bus_property_get_usec, offsetof(Timer, random_usec), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("Persistent", "b", bus_property_get_bool, offsetof(Timer, persistent), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("WakeSystem", "b", bus_property_get_bool, offsetof(Timer, wake_system), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("RemainAfterElapse", "b", bus_property_get_bool, offsetof(Timer, remain_after_elapse), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_VTABLE_END
 };
 
@@ -282,8 +284,23 @@ static int bus_timer_set_transient_property(
 
                 return 1;
 
-        } else if (streq(name, "WakeSystem")) {
+        } else if (streq(name, "RandomizedDelayUSec")) {
+                usec_t u = 0;
+
+                r = sd_bus_message_read(message, "t", &u);
+                if (r < 0)
+                        return r;
+
+                if (mode != UNIT_CHECK) {
+                        char time[FORMAT_TIMESPAN_MAX];
+
+                        t->random_usec = u;
+                        unit_write_drop_in_private_format(UNIT(t), mode, name, "RandomizedDelaySec=%s\n", format_timespan(time, sizeof(time), u, USEC_PER_MSEC));
+                }
+
+                return 1;
 
+        } else if (streq(name, "WakeSystem")) {
                 int b;
 
                 r = sd_bus_message_read(message, "b", &b);
@@ -292,11 +309,24 @@ static int bus_timer_set_transient_property(
 
                 if (mode != UNIT_CHECK) {
                         t->wake_system = b;
-                        unit_write_drop_in_private_format(UNIT(t), mode, name, "%s=%s\n", name, yes_no(t->wake_system));
+                        unit_write_drop_in_private_format(UNIT(t), mode, name, "%s=%s\n", name, yes_no(b));
                 }
 
                 return 1;
 
+        } else if (streq(name, "RemainAfterElapse")) {
+                int b;
+
+                r = sd_bus_message_read(message, "b", &b);
+                if (r < 0)
+                        return r;
+
+                if (mode != UNIT_CHECK) {
+                        t->remain_after_elapse = b;
+                        unit_write_drop_in_private_format(UNIT(t), mode, name, "%s=%s\n", name, yes_no(b));
+                }
+
+                return 1;
         }
 
         return 0;
index 103172f05577edcb97bb19366a0edb927ee35774..ca35c4b8c1f2d2c3251ebe44226c1b71861c81aa 100644 (file)
@@ -22,6 +22,7 @@
 ***/
 
 #include "sd-bus.h"
+
 #include "unit.h"
 
 extern const sd_bus_vtable bus_timer_vtable[];
index d9b7382c82b082b1b8913079303a7260477d5072..66b465a0b7a00026bfd843c12e681ba195f93848 100644 (file)
@@ -1251,3 +1251,20 @@ int bus_unit_set_properties(
 
         return n;
 }
+
+int bus_unit_check_load_state(Unit *u, sd_bus_error *error) {
+
+        if (u->load_state == UNIT_LOADED)
+                return 0;
+
+        /* Give a better description of the unit error when
+         * possible. Note that in the case of UNIT_MASKED, load_error
+         * is not set. */
+        if (u->load_state == UNIT_MASKED)
+                return sd_bus_error_setf(error, BUS_ERROR_UNIT_MASKED, "Unit is masked.");
+
+        if (u->load_state == UNIT_NOT_FOUND)
+                return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Unit not found.");
+
+        return sd_bus_error_set_errnof(error, u->load_error, "Unit is not loaded properly: %m.");
+}
index b622e0ae8d02de9bfcbd1fc6e2810decda07c897..ac9ee2d6b870e18bbc5641ce84a9bdd8013b30d6 100644 (file)
@@ -22,6 +22,7 @@
 ***/
 
 #include "sd-bus.h"
+
 #include "unit.h"
 
 extern const sd_bus_vtable bus_unit_vtable[];
@@ -37,3 +38,5 @@ int bus_unit_method_reset_failed(sd_bus_message *message, void *userdata, sd_bus
 int bus_unit_queue_job(sd_bus_message *message, Unit *u, JobType type, JobMode mode, bool reload_if_possible, sd_bus_error *error);
 int bus_unit_set_properties(Unit *u, sd_bus_message *message, UnitSetPropertiesMode mode, bool commit, sd_bus_error *error);
 int bus_unit_method_set_properties(sd_bus_message *message, void *userdata, sd_bus_error *error);
+
+int bus_unit_check_load_state(Unit *u, sd_bus_error *error);
index 07979bf8b30934aa6cb245d2fbf0f93d0f162051..677480cbe1f787d1fd99e730d1e8cbf39bbe8f49 100644 (file)
@@ -2414,8 +2414,8 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
 
         for (i = 0; i < RLIM_NLIMITS; i++)
                 if (c->rlimit[i])
-                        fprintf(f, "%s%s: "RLIM_FMT"\n",
-                                prefix, rlimit_to_string(i), c->rlimit[i]->rlim_max);
+                        fprintf(f, "%s%s: " RLIM_FMT " " RLIM_FMT "\n",
+                                prefix, rlimit_to_string(i), c->rlimit[i]->rlim_cur, c->rlimit[i]->rlim_max);
 
         if (c->ioprio_set) {
                 _cleanup_free_ char *class_str = NULL;
index 1faff160cbea58d115938ba56ddf004872b85dea..be5be9f531769a8b86b5d7768e829309e510cf0e 100644 (file)
@@ -27,16 +27,16 @@ typedef struct ExecContext ExecContext;
 typedef struct ExecRuntime ExecRuntime;
 typedef struct ExecParameters ExecParameters;
 
-#include <sys/capability.h>
+#include <sched.h>
 #include <stdbool.h>
 #include <stdio.h>
-#include <sched.h>
+#include <sys/capability.h>
 
-#include "list.h"
+#include "bus-endpoint.h"
 #include "fdset.h"
+#include "list.h"
 #include "missing.h"
 #include "namespace.h"
-#include "bus-endpoint.h"
 
 typedef enum ExecUtmpMode {
         EXEC_UTMP_INIT,
@@ -204,8 +204,8 @@ struct ExecContext {
         BusEndpoint *bus_endpoint;
 };
 
-#include "cgroup.h"
 #include "cgroup-util.h"
+#include "cgroup.h"
 
 struct ExecParameters {
         char **argv;
index 53e094721514ae9f660598d2a738e7d39a3718e5..9654590635dfc05488267f6109a59e90d0103857 100644 (file)
@@ -500,17 +500,26 @@ static void job_change_type(Job *j, JobType newtype) {
 }
 
 static int job_perform_on_unit(Job **j) {
-        /* While we execute this operation the job might go away (for
-         * example: because it finishes immediately or is replaced by a new,
-         * conflicting job.) To make sure we don't access a freed job later on
-         * we store the id here, so that we can verify the job is still
-         * valid. */
-        Manager *m  = (*j)->manager;
-        Unit *u     = (*j)->unit;
-        JobType t   = (*j)->type;
-        uint32_t id = (*j)->id;
+        uint32_t id;
+        Manager *m;
+        JobType t;
+        Unit *u;
         int r;
 
+        /* While we execute this operation the job might go away (for
+         * example: because it finishes immediately or is replaced by
+         * a new, conflicting job.) To make sure we don't access a
+         * freed job later on we store the id here, so that we can
+         * verify the job is still valid. */
+
+        assert(j);
+        assert(*j);
+
+        m = (*j)->manager;
+        u = (*j)->unit;
+        t = (*j)->type;
+        id = (*j)->id;
+
         switch (t) {
                 case JOB_START:
                         r = unit_start(u);
@@ -518,6 +527,7 @@ static int job_perform_on_unit(Job **j) {
 
                 case JOB_RESTART:
                         t = JOB_STOP;
+                        /* fall through */
                 case JOB_STOP:
                         r = unit_stop(u);
                         break;
@@ -617,8 +627,7 @@ int job_run_and_invalidate(Job *j) {
 }
 
 _pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobResult result) {
-        const char *format;
-        const UnitStatusMessageFormats *format_table;
+
         static const char *const generic_finished_start_job[_JOB_RESULT_MAX] = {
                 [JOB_DONE]        = "Started %s.",
                 [JOB_TIMEOUT]     = "Timed out starting %s.",
@@ -644,11 +653,14 @@ _pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobR
                 [JOB_SKIPPED]     = "%s is not active.",
         };
 
+        const UnitStatusMessageFormats *format_table;
+        const char *format;
+
         assert(u);
         assert(t >= 0);
         assert(t < _JOB_TYPE_MAX);
 
-        if (t == JOB_START || t == JOB_STOP || t == JOB_RESTART) {
+        if (IN_SET(t, JOB_START, JOB_STOP, JOB_RESTART)) {
                 format_table = &UNIT_VTABLE(u)->status_message_formats;
                 if (format_table) {
                         format = t == JOB_START ? format_table->finished_start_job[result] :
@@ -672,7 +684,6 @@ _pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobR
 }
 
 static void job_print_status_message(Unit *u, JobType t, JobResult result) {
-        const char *format;
         static const char* const job_result_status_table[_JOB_RESULT_MAX] = {
                 [JOB_DONE]        = ANSI_GREEN            "  OK  " ANSI_NORMAL,
                 [JOB_TIMEOUT]     = ANSI_HIGHLIGHT_RED    " TIME " ANSI_NORMAL,
@@ -683,10 +694,16 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) {
                 [JOB_UNSUPPORTED] = ANSI_HIGHLIGHT_YELLOW "UNSUPP" ANSI_NORMAL,
         };
 
+        const char *format;
+
         assert(u);
         assert(t >= 0);
         assert(t < _JOB_TYPE_MAX);
 
+        /* Reload status messages have traditionally not been printed to console. */
+        if (t == JOB_RELOAD)
+                return;
+
         format = job_get_status_message_format(u, t, result);
         if (!format)
                 return;
@@ -699,10 +716,10 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) {
         REENABLE_WARNING;
 
         if (t == JOB_START && result == JOB_FAILED) {
-                _cleanup_free_ char *quoted = shell_maybe_quote(u->id);
+                _cleanup_free_ char *quoted;
 
-                manager_status_printf(u->manager, STATUS_TYPE_NORMAL, NULL,
-                                      "See 'systemctl status %s' for details.", strna(quoted));
+                quoted = shell_maybe_quote(u->id);
+                manager_status_printf(u->manager, STATUS_TYPE_NORMAL, NULL, "See 'systemctl status %s' for details.", strna(quoted));
         }
 }
 
@@ -740,13 +757,22 @@ static void job_log_status_message(Unit *u, JobType t, JobResult result) {
         snprintf(buf, sizeof(buf), format, unit_description(u));
         REENABLE_WARNING;
 
-        if (t == JOB_START)
+        switch (t) {
+
+        case JOB_START:
                 mid = result == JOB_DONE ? SD_MESSAGE_UNIT_STARTED : SD_MESSAGE_UNIT_FAILED;
-        else if (t == JOB_STOP || t == JOB_RESTART)
-                mid = SD_MESSAGE_UNIT_STOPPED;
-        else if (t == JOB_RELOAD)
+                break;
+
+        case JOB_RELOAD:
                 mid = SD_MESSAGE_UNIT_RELOADED;
-        else {
+                break;
+
+        case JOB_STOP:
+        case JOB_RESTART:
+                mid = SD_MESSAGE_UNIT_STOPPED;
+                break;
+
+        default:
                 log_struct(job_result_log_level[result],
                            LOG_UNIT_ID(u),
                            LOG_MESSAGE("%s", buf),
@@ -770,10 +796,7 @@ static void job_emit_status_message(Unit *u, JobType t, JobResult result) {
                 return;
 
         job_log_status_message(u, t, result);
-
-        /* Reload status messages have traditionally not been printed to console. */
-        if (t != JOB_RELOAD)
-                job_print_status_message(u, t, result);
+        job_print_status_message(u, t, result);
 }
 
 static void job_fail_dependencies(Unit *u, UnitDependency d) {
index 60d8bd4f3e5e09e0487fe41de1af8fccc3ad1847..118b24e5b712eccfa53afa3df7c24594c3beb94b 100644 (file)
@@ -26,6 +26,7 @@
 #include "sd-event.h"
 
 #include "list.h"
+#include "unit-name.h"
 
 typedef struct Job Job;
 typedef struct JobDependency JobDependency;
index 1e018c45253f6d03d0036527aa6698c614c8c157..93ffcc4a72b11e0b3d92cd725b69363c34943add 100644 (file)
@@ -21,8 +21,8 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include "unit.h"
 #include "dropin.h"
+#include "unit.h"
 
 /* Read service data supplementary drop-in directories */
 
index 799418033d8a24e5915ba397dd90a21d21135942..0408b9a82914b832bd8fc91bdb05d85533d63560 100644 (file)
@@ -249,6 +249,7 @@ Socket.ListenNetlink,            config_parse_socket_listen,         SOCKET_SOCK
 Socket.ListenSpecial,            config_parse_socket_listen,         SOCKET_SPECIAL,                0
 Socket.ListenMessageQueue,       config_parse_socket_listen,         SOCKET_MQUEUE,                 0
 Socket.ListenUSBFunction,        config_parse_socket_listen,         SOCKET_USB_FUNCTION,           0
+Socket.SocketProtocol,           config_parse_socket_protocol,       0,                             0
 Socket.BindIPv6Only,             config_parse_socket_bind,           0,                             0,
 Socket.Backlog,                  config_parse_unsigned,              0,                             offsetof(Socket, backlog)
 Socket.BindToDevice,             config_parse_socket_bindtodevice,   0,                             0
@@ -344,7 +345,9 @@ Timer.OnUnitActiveSec,           config_parse_timer,                 0,
 Timer.OnUnitInactiveSec,         config_parse_timer,                 0,                             0
 Timer.Persistent,                config_parse_bool,                  0,                             offsetof(Timer, persistent)
 Timer.WakeSystem,                config_parse_bool,                  0,                             offsetof(Timer, wake_system)
+Timer.RemainAfterElapse,         config_parse_bool,                  0,                             offsetof(Timer, remain_after_elapse)
 Timer.AccuracySec,               config_parse_sec,                   0,                             offsetof(Timer, accuracy_usec)
+Timer.RandomizedDelaySec,        config_parse_sec,                   0,                             offsetof(Timer, random_usec)
 Timer.Unit,                      config_parse_trigger_unit,          0,                             0
 m4_dnl
 Path.PathExists,                 config_parse_path_spec,             0,                             0
index dda79267f7083ea47b17ed40f9d96fb567c6a69a..3c124495b6c74efa461dbb50d4eee0fe6152fe14 100644 (file)
@@ -421,6 +421,37 @@ int config_parse_socket_listen(const char *unit,
         return 0;
 }
 
+int config_parse_socket_protocol(const char *unit,
+                                 const char *filename,
+                                 unsigned line,
+                                 const char *section,
+                                 unsigned section_line,
+                                 const char *lvalue,
+                                 int ltype,
+                                 const char *rvalue,
+                                 void *data,
+                                 void *userdata) {
+        Socket *s;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        s = SOCKET(data);
+
+        if (streq(rvalue, "udplite"))
+                s->socket_protocol = IPPROTO_UDPLITE;
+        else if (streq(rvalue, "sctp"))
+                s->socket_protocol = IPPROTO_SCTP;
+        else {
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Socket protocol not supported, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        return 0;
+}
+
 int config_parse_socket_bind(const char *unit,
                              const char *filename,
                              unsigned line,
@@ -1058,59 +1089,123 @@ int config_parse_bounding_set(
         return 0;
 }
 
-int config_parse_limit(
-                const char *unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        struct rlimit **rl = data;
-        rlim_t v;
-        int r;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
-
-        rl += ltype;
+static int rlim_parse_u64(const char *val, rlim_t *res) {
+        int r = 0;
 
-        if (streq(rvalue, "infinity"))
-                v = RLIM_INFINITY;
+        if (streq(val, "infinity"))
+                *res = RLIM_INFINITY;
         else {
                 uint64_t u;
 
                 /* setrlimit(2) suggests rlim_t is always 64bit on Linux. */
                 assert_cc(sizeof(rlim_t) == sizeof(uint64_t));
 
-                r = safe_atou64(rvalue, &u);
+                r = safe_atou64(val, &u);
                 if (r >= 0 && u >= (uint64_t) RLIM_INFINITY)
                         r = -ERANGE;
-                if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue);
-                        return 0;
-                }
+                if (r == 0)
+                        *res = (rlim_t) u;
+        }
+        return r;
+}
+
+static int rlim_parse_size(const char *val, rlim_t *res) {
+        int r = 0;
+
+        if (streq(val, "infinity"))
+                *res = RLIM_INFINITY;
+        else {
+                uint64_t u;
+
+                r = parse_size(val, 1024, &u);
+                if (r >= 0 && u >= (uint64_t) RLIM_INFINITY)
+                        r = -ERANGE;
+                if (r == 0)
+                        *res = (rlim_t) u;
+        }
+        return r;
+}
+
+static int rlim_parse_sec(const char *val, rlim_t *res) {
+        int r = 0;
+
+        if (streq(val, "infinity"))
+                *res = RLIM_INFINITY;
+        else {
+                usec_t t;
+
+                r = parse_sec(val, &t);
+                if (r < 0)
+                        return r;
+                if (t == USEC_INFINITY)
+                        *res = RLIM_INFINITY;
+                else
+                        *res = (rlim_t) (DIV_ROUND_UP(t, USEC_PER_SEC));
 
-                v = (rlim_t) u;
         }
+        return r;
+}
+
+static int rlim_parse_usec(const char *val, rlim_t *res) {
+        int r = 0;
+
+        if (streq(val, "infinity"))
+                *res = RLIM_INFINITY;
+        else {
+                usec_t t;
+
+                r = parse_time(val, &t, 1);
+                if (r < 0)
+                        return r;
+                if (t == USEC_INFINITY)
+                        *res = RLIM_INFINITY;
+                else
+                        *res = (rlim_t) t;
+        }
+        return r;
+}
+
+static int parse_rlimit_range(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *value,
+                struct rlimit **rl,
+                int (*rlim_parser)(const char *, rlim_t *)) {
+
+        const char *whole_value = value;
+        rlim_t soft, hard;
+        _cleanup_free_ char *sword = NULL, *hword = NULL;
+        int nwords, r;
+
+        assert(value);
+
+        /* <value> or <soft:hard> */
+        nwords = extract_many_words(&value, ":", EXTRACT_DONT_COALESCE_SEPARATORS, &sword, &hword, NULL);
+        r = nwords < 0 ? nwords : nwords == 0 ? -EINVAL : 0;
+
+        if (r == 0)
+                r = rlim_parser(sword, &soft);
+        if (r == 0 && nwords == 2)
+                r = rlim_parser(hword, &hard);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", whole_value);
+                return 0;
+        }
+        if (nwords == 2 && soft > hard)
+                return log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid resource value ("RLIM_FMT" > "RLIM_FMT"), ignoring: %s", soft, hard, whole_value);
 
         if (!*rl) {
                 *rl = new(struct rlimit, 1);
                 if (!*rl)
                         return log_oom();
         }
-
-        (*rl)->rlim_cur = (*rl)->rlim_max = v;
+        (*rl)->rlim_cur = soft;
+        (*rl)->rlim_max = nwords == 2 ? hard : soft;
         return 0;
 }
 
-int config_parse_bytes_limit(
+int config_parse_limit(
                 const char *unit,
                 const char *filename,
                 unsigned line,
@@ -1123,8 +1218,6 @@ int config_parse_bytes_limit(
                 void *userdata) {
 
         struct rlimit **rl = data;
-        rlim_t bytes;
-        int r;
 
         assert(filename);
         assert(lvalue);
@@ -1132,31 +1225,30 @@ int config_parse_bytes_limit(
         assert(data);
 
         rl += ltype;
+        return parse_rlimit_range(unit, filename, line, rvalue, rl, rlim_parse_u64);
+}
 
-        if (streq(rvalue, "infinity"))
-                bytes = RLIM_INFINITY;
-        else {
-                uint64_t u;
-
-                r = parse_size(rvalue, 1024, &u);
-                if (r >= 0 && u >= (uint64_t) RLIM_INFINITY)
-                        r = -ERANGE;
-                if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue);
-                        return 0;
-                }
+int config_parse_bytes_limit(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
 
-                bytes = (rlim_t) u;
-        }
+        struct rlimit **rl = data;
 
-        if (!*rl) {
-                *rl = new(struct rlimit, 1);
-                if (!*rl)
-                        return log_oom();
-        }
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
 
-        (*rl)->rlim_cur = (*rl)->rlim_max = bytes;
-        return 0;
+        rl += ltype;
+        return parse_rlimit_range(unit, filename, line, rvalue, rl, rlim_parse_size);
 }
 
 int config_parse_sec_limit(
@@ -1172,8 +1264,6 @@ int config_parse_sec_limit(
                 void *userdata) {
 
         struct rlimit **rl = data;
-        rlim_t seconds;
-        int r;
 
         assert(filename);
         assert(lvalue);
@@ -1181,35 +1271,9 @@ int config_parse_sec_limit(
         assert(data);
 
         rl += ltype;
-
-        if (streq(rvalue, "infinity"))
-                seconds = RLIM_INFINITY;
-        else {
-                usec_t t;
-
-                r = parse_sec(rvalue, &t);
-                if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue);
-                        return 0;
-                }
-
-                if (t == USEC_INFINITY)
-                        seconds = RLIM_INFINITY;
-                else
-                        seconds = (rlim_t) (DIV_ROUND_UP(t, USEC_PER_SEC));
-        }
-
-        if (!*rl) {
-                *rl = new(struct rlimit, 1);
-                if (!*rl)
-                        return log_oom();
-        }
-
-        (*rl)->rlim_cur = (*rl)->rlim_max = seconds;
-        return 0;
+        return parse_rlimit_range(unit, filename, line, rvalue, rl, rlim_parse_sec);
 }
 
-
 int config_parse_usec_limit(
                 const char *unit,
                 const char *filename,
@@ -1223,8 +1287,6 @@ int config_parse_usec_limit(
                 void *userdata) {
 
         struct rlimit **rl = data;
-        rlim_t useconds;
-        int r;
 
         assert(filename);
         assert(lvalue);
@@ -1232,33 +1294,10 @@ int config_parse_usec_limit(
         assert(data);
 
         rl += ltype;
+        return parse_rlimit_range(unit, filename, line, rvalue, rl, rlim_parse_usec);
+}
 
-        if (streq(rvalue, "infinity"))
-                useconds = RLIM_INFINITY;
-        else {
-                usec_t t;
-
-                r = parse_time(rvalue, &t, 1);
-                if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue);
-                        return 0;
-                }
-
-                if (t == USEC_INFINITY)
-                        useconds = RLIM_INFINITY;
-                else
-                        useconds = (rlim_t) t;
-        }
-
-        if (!*rl) {
-                *rl = new(struct rlimit, 1);
-                if (!*rl)
-                        return log_oom();
-        }
 
-        (*rl)->rlim_cur = (*rl)->rlim_max = useconds;
-        return 0;
-}
 
 #ifdef HAVE_SYSV_COMPAT
 int config_parse_sysv_priority(const char *unit,
@@ -1305,38 +1344,28 @@ int config_parse_exec_mount_flags(const char *unit,
                                   void *data,
                                   void *userdata) {
 
-        ExecContext *c = data;
-        const char *word, *state;
-        size_t l;
+
         unsigned long flags = 0;
+        ExecContext *c = data;
 
         assert(filename);
         assert(lvalue);
         assert(rvalue);
         assert(data);
 
-        FOREACH_WORD_SEPARATOR(word, l, rvalue, ", ", state) {
-                _cleanup_free_ char *t;
-
-                t = strndup(word, l);
-                if (!t)
-                        return log_oom();
-
-                if (streq(t, "shared"))
-                        flags = MS_SHARED;
-                else if (streq(t, "slave"))
-                        flags = MS_SLAVE;
-                else if (streq(t, "private"))
-                        flags = MS_PRIVATE;
-                else {
-                        log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse mount flag %s, ignoring: %s", t, rvalue);
-                        return 0;
-                }
+        if (streq(rvalue, "shared"))
+                flags = MS_SHARED;
+        else if (streq(rvalue, "slave"))
+                flags = MS_SLAVE;
+        else if (streq(rvalue, "private"))
+                flags = MS_PRIVATE;
+        else {
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse mount flag %s, ignoring.", rvalue);
+                return 0;
         }
-        if (!isempty(state))
-                log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
 
         c->mount_flags = flags;
+
         return 0;
 }
 
index 62300c10f9fb192abc6e78ae813cda1b6e67da39..a451fc164a737d6e229e332ef4ec3022b1a085b6 100644 (file)
@@ -38,6 +38,7 @@ int config_parse_unit_path_printf(const char *unit, const char *filename, unsign
 int config_parse_unit_path_strv_printf(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 int config_parse_documentation(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 int config_parse_socket_listen(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_socket_protocol(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 int config_parse_socket_bind(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 int config_parse_exec_nice(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 int config_parse_exec_oom_score_adjust(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
index 8a0e44b58c9c48b3e51a43ff5a3c11a690750816..2cace3d3ba5f6463fdc95bec07f50b7ab6dadc89 100644 (file)
@@ -43,7 +43,7 @@ if [ $1 -eq 1 ] ; then \
 fi \
 %{nil}
 
-%systemd_user_post() %systemd_post --user --global %{?*}
+%systemd_user_post() %{expand:%systemd_post \\--user \\--global %%{?*}}
 
 %systemd_preun() \
 if [ $1 -eq 0 ] ; then \
index b5b258f909105d38b914a9ca74505f34d982a98b..f6903a5c34ebc1debd14558a7fad66f353a7b571 100644 (file)
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
+#include <libmount.h>
 #include <stdbool.h>
 #include <stdio.h>
-#include <libmount.h>
 
 #include "sd-bus.h"
 #include "sd-event.h"
+
 #include "cgroup-util.h"
 #include "fdset.h"
 #include "hashmap.h"
index 9b44357e905f26895e571f90df5736ad2209630d..2ad4ad4f424e5dd956e11407a727476f461db8eb 100644 (file)
@@ -335,7 +335,7 @@ static int mount_add_device_links(Mount *m) {
         if (mount_is_auto(p) && UNIT(m)->manager->running_as == MANAGER_SYSTEM)
                 device_wants_mount = true;
 
-        r = unit_add_node_link(UNIT(m), p->what, device_wants_mount);
+        r = unit_add_node_link(UNIT(m), p->what, device_wants_mount, m->from_fragment ? UNIT_BINDS_TO : UNIT_REQUIRES);
         if (r < 0)
                 return r;
 
index 83d14ae71383c2c3e82748783bfd6587264cfef8..9f78aa90753237672d6ae2d9056fc7b7cfe6ca99 100644 (file)
@@ -23,8 +23,8 @@
 
 typedef struct Mount Mount;
 
-#include "kill.h"
 #include "execute.h"
+#include "kill.h"
 
 typedef enum MountExecCommand {
         MOUNT_EXEC_MOUNT,
index 30725521cb34d4a77a3def35410d2fb7aa1bd938..3566ba529f9c6798b0257af9b4f6473c96ff976e 100644 (file)
@@ -22,6 +22,7 @@
 ***/
 
 #include "sd-bus.h"
+
 #include "bus-util.h"
 #include "manager.h"
 
index e7656682476c803a198809a9df2179276ddc8bec..d0faad88e0affb6a9dc07d559d6b35543aba718f 100644 (file)
 typedef struct Service Service;
 typedef struct ServiceFDStore ServiceFDStore;
 
+#include "exit-status.h"
+#include "kill.h"
 #include "path.h"
 #include "ratelimit.h"
-#include "kill.h"
-#include "exit-status.h"
 
 typedef enum ServiceRestart {
         SERVICE_RESTART_NO,
index 5b9e32ce9dc98ab8875fa48c17b3363a31e5a989..860a1e3051ce01e8194e4e8eb16cad455a818483 100644 (file)
@@ -289,7 +289,7 @@ static int socket_add_device_link(Socket *s) {
                 return 0;
 
         t = strjoina("/sys/subsystem/net/devices/", s->bind_to_device);
-        return unit_add_node_link(UNIT(s), t, false);
+        return unit_add_node_link(UNIT(s), t, false, UNIT_BINDS_TO);
 }
 
 static int socket_add_default_dependencies(Socket *s) {
@@ -1266,6 +1266,19 @@ static int socket_open_fds(Socket *s) {
                                 know_label = true;
                         }
 
+                        /* Apply the socket protocol */
+                        switch(p->address.type) {
+                        case SOCK_STREAM:
+                        case SOCK_SEQPACKET:
+                                if (p->socket->socket_protocol == IPPROTO_SCTP)
+                                        p->address.protocol = p->socket->socket_protocol;
+                                break;
+                        case SOCK_DGRAM:
+                                if (p->socket->socket_protocol == IPPROTO_UDPLITE)
+                                        p->address.protocol = p->socket->socket_protocol;
+                                break;
+                        }
+
                         r = socket_address_listen(
                                         &p->address,
                                         SOCK_CLOEXEC|SOCK_NONBLOCK,
index 94cda8a90d1536638ab853fcf7c4f6f4730a8dd7..08033287a61c2e8d609821de4bad1735dae343df 100644 (file)
@@ -23,9 +23,9 @@
 
 typedef struct Socket Socket;
 
-#include "socket-util.h"
 #include "mount.h"
 #include "service.h"
+#include "socket-util.h"
 
 typedef enum SocketExecCommand {
         SOCKET_EXEC_START_PRE,
@@ -120,6 +120,8 @@ struct Socket {
         bool remove_on_stop;
         bool writable;
 
+        int socket_protocol;
+
         /* Socket options */
         bool keep_alive;
         bool no_delay;
index ee0838e6762faf7a3435c3b5b9824b9cf90d9409..5568898bd710eef17ca54e89148324f3b55e98ab 100644 (file)
@@ -202,7 +202,7 @@ static int swap_add_device_links(Swap *s) {
                 return 0;
 
         if (is_device_path(s->what))
-                return unit_add_node_link(UNIT(s), s->what, UNIT(s)->manager->running_as == MANAGER_SYSTEM);
+                return unit_add_node_link(UNIT(s), s->what, UNIT(s)->manager->running_as == MANAGER_SYSTEM, UNIT_BINDS_TO);
         else
                 /* File based swap devices need to be ordered after
                  * systemd-remount-fs.service, since they might need a
@@ -211,6 +211,8 @@ static int swap_add_device_links(Swap *s) {
 }
 
 static int swap_add_default_dependencies(Swap *s) {
+        int r;
+
         assert(s);
 
         if (!UNIT(s)->default_dependencies)
@@ -222,6 +224,12 @@ static int swap_add_default_dependencies(Swap *s) {
         if (detect_container() > 0)
                 return 0;
 
+        /* swap units generated for the swap dev links are missing the
+         * ordering dep against the swap target. */
+        r = unit_add_dependency_by_name(UNIT(s), UNIT_BEFORE, SPECIAL_SWAP_TARGET, NULL, true);
+        if (r < 0)
+                return r;
+
         return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true);
 }
 
index 51b1d875beee1a977665ba1e5e447de235676de7..6b0f8e861698ed9a6433a5b500159103fd443500 100644 (file)
@@ -27,6 +27,7 @@
 #include "dbus-timer.h"
 #include "fs-util.h"
 #include "parse-util.h"
+#include "random-util.h"
 #include "special.h"
 #include "string-table.h"
 #include "string-util.h"
@@ -55,6 +56,7 @@ static void timer_init(Unit *u) {
         t->next_elapse_monotonic_or_boottime = USEC_INFINITY;
         t->next_elapse_realtime = USEC_INFINITY;
         t->accuracy_usec = u->manager->default_timer_accuracy_usec;
+        t->remain_after_elapse = true;
 }
 
 void timer_free_values(Timer *t) {
@@ -217,13 +219,15 @@ static void timer_dump(Unit *u, FILE *f, const char *prefix) {
                 "%sUnit: %s\n"
                 "%sPersistent: %s\n"
                 "%sWakeSystem: %s\n"
-                "%sAccuracy: %s\n",
+                "%sAccuracy: %s\n"
+                "%sRemainAfterElapse: %s\n",
                 prefix, timer_state_to_string(t->state),
                 prefix, timer_result_to_string(t->result),
                 prefix, trigger ? trigger->id : "n/a",
                 prefix, yes_no(t->persistent),
                 prefix, yes_no(t->wake_system),
-                prefix, format_timespan(buf, sizeof(buf), t->accuracy_usec, 1));
+                prefix, format_timespan(buf, sizeof(buf), t->accuracy_usec, 1),
+                prefix, yes_no(t->remain_after_elapse));
 
         LIST_FOREACH(value, v, t->values) {
 
@@ -275,13 +279,13 @@ static int timer_coldplug(Unit *u) {
         assert(t);
         assert(t->state == TIMER_DEAD);
 
-        if (t->deserialized_state != t->state) {
+        if (t->deserialized_state == t->state)
+                return 0;
 
-                if (t->deserialized_state == TIMER_WAITING)
-                        timer_enter_waiting(t, false);
-                else
-                        timer_set_state(t, t->deserialized_state);
-        }
+        if (t->deserialized_state == TIMER_WAITING)
+                timer_enter_waiting(t, false);
+        else
+                timer_set_state(t, t->deserialized_state);
 
         return 0;
 }
@@ -295,6 +299,23 @@ static void timer_enter_dead(Timer *t, TimerResult f) {
         timer_set_state(t, t->result != TIMER_SUCCESS ? TIMER_FAILED : TIMER_DEAD);
 }
 
+static void timer_enter_elapsed(Timer *t, bool leave_around) {
+        assert(t);
+
+        /* If a unit is marked with RemainAfterElapse=yes we leave it
+         * around even after it elapsed once, so that starting it
+         * later again does not necessarily mean immediate
+         * retriggering. We unconditionally leave units with
+         * TIMER_UNIT_ACTIVE or TIMER_UNIT_INACTIVE triggers around,
+         * since they might be restarted automatically at any time
+         * later on. */
+
+        if (t->remain_after_elapse || leave_around)
+                timer_set_state(t, TIMER_ELAPSED);
+        else
+                timer_enter_dead(t, TIMER_SUCCESS);
+}
+
 static usec_t monotonic_to_boottime(usec_t t) {
         usec_t a, b;
 
@@ -310,10 +331,33 @@ static usec_t monotonic_to_boottime(usec_t t) {
                 return 0;
 }
 
+static void add_random(Timer *t, usec_t *v) {
+        char s[FORMAT_TIMESPAN_MAX];
+        usec_t add;
+
+        assert(t);
+        assert(*v);
+
+        if (t->random_usec == 0)
+                return;
+        if (*v == USEC_INFINITY)
+                return;
+
+        add = random_u64() % t->random_usec;
+
+        if (*v + add < *v) /* overflow */
+                *v = (usec_t) -2; /* Highest possible value, that is not USEC_INFINITY */
+        else
+                *v += add;
+
+        log_unit_info(UNIT(t), "Adding %s random time.", format_timespan(s, sizeof(s), add, 0));
+}
+
 static void timer_enter_waiting(Timer *t, bool initial) {
         bool found_monotonic = false, found_realtime = false;
         usec_t ts_realtime, ts_monotonic;
         usec_t base = 0;
+        bool leave_around = false;
         TimerValue *v;
         int r;
 
@@ -374,7 +418,7 @@ static void timer_enter_waiting(Timer *t, bool initial) {
                                 break;
 
                         case TIMER_UNIT_ACTIVE:
-
+                                leave_around = true;
                                 base = UNIT_TRIGGER(UNIT(t))->inactive_exit_timestamp.monotonic;
 
                                 if (base <= 0)
@@ -386,7 +430,7 @@ static void timer_enter_waiting(Timer *t, bool initial) {
                                 break;
 
                         case TIMER_UNIT_INACTIVE:
-
+                                leave_around = true;
                                 base = UNIT_TRIGGER(UNIT(t))->inactive_enter_timestamp.monotonic;
 
                                 if (base <= 0)
@@ -423,14 +467,18 @@ static void timer_enter_waiting(Timer *t, bool initial) {
 
         if (!found_monotonic && !found_realtime) {
                 log_unit_debug(UNIT(t), "Timer is elapsed.");
-                timer_set_state(t, TIMER_ELAPSED);
+                timer_enter_elapsed(t, leave_around);
                 return;
         }
 
         if (found_monotonic) {
                 char buf[FORMAT_TIMESPAN_MAX];
+                usec_t left;
 
-                log_unit_debug(UNIT(t), "Monotonic timer elapses in %s.", format_timespan(buf, sizeof(buf), t->next_elapse_monotonic_or_boottime > ts_monotonic ? t->next_elapse_monotonic_or_boottime - ts_monotonic : 0, 0));
+                add_random(t, &t->next_elapse_monotonic_or_boottime);
+
+                left = t->next_elapse_monotonic_or_boottime > ts_monotonic ? t->next_elapse_monotonic_or_boottime - ts_monotonic : 0;
+                log_unit_debug(UNIT(t), "Monotonic timer elapses in %s.", format_timespan(buf, sizeof(buf), left, 0));
 
                 if (t->monotonic_event_source) {
                         r = sd_event_source_set_time(t->monotonic_event_source, t->next_elapse_monotonic_or_boottime);
@@ -463,6 +511,9 @@ static void timer_enter_waiting(Timer *t, bool initial) {
 
         if (found_realtime) {
                 char buf[FORMAT_TIMESTAMP_MAX];
+
+                add_random(t, &t->next_elapse_realtime);
+
                 log_unit_debug(UNIT(t), "Realtime timer elapses at %s.", format_timestamp(buf, sizeof(buf), t->next_elapse_realtime));
 
                 if (t->realtime_event_source) {
index ac5af6a93c3dacc32707d19fe2385985e3a8d675..0599f07818589a6c7fa228bd5c721aa681632edc 100644 (file)
@@ -58,6 +58,7 @@ struct Timer {
         Unit meta;
 
         usec_t accuracy_usec;
+        usec_t random_usec;
 
         LIST_HEAD(TimerValue, values);
         usec_t next_elapse_realtime;
@@ -73,6 +74,7 @@ struct Timer {
 
         bool persistent;
         bool wake_system;
+        bool remain_after_elapse;
 
         char *stamp_path;
 };
index f7aa3df085e2008f9b7fd498ebb0769977fc44b3..5c4a13edab3898da66ed8ff349ad462b1b0ce80a 100644 (file)
 
 typedef struct Transaction Transaction;
 
-#include "unit.h"
-#include "manager.h"
-#include "job.h"
 #include "hashmap.h"
+#include "job.h"
+#include "manager.h"
+#include "unit.h"
 
 struct Transaction {
         /* Jobs to be added */
index 141f42dbcfd0c818bd65894f1b29e4da8adc0632..9e18a39a674877ec52f001f46e4db24a69589e84 100644 (file)
 
 # The contents of this are an example to be copied into systemd.spec.
 
-%transfiletriggerin -- @systemunitdir@ /etc/systemd/system
-systemctl daemon-reload &>/dev/null || :
+%transfiletriggerin -P 900900 -p <lua> -- @systemunitdir@ /etc/systemd/system
+-- This script will run after any package is initially installed or
+-- upgraded. We care about the case where a package is initially
+-- installed, because other cases are covered by the *un scriptlets,
+-- so sometimes we will reload needlessly.
 
-%transfiletriggerun -- @systemunitdir@ /etc/systemd/system
-systemctl daemon-reload &>/dev/null || :
+pid = posix.fork()
+if pid == 0 then
+    assert(posix.exec("%{_bindir}/systemctl", "daemon-reload"))
+elseif pid > 0 then
+    posix.wait(pid)
+end
+
+%transfiletriggerun -p <lua> -- @systemunitdir@ /etc/systemd/system
+-- On removal, we need to run daemon-reload after any units have been
+-- removed. %transfiletriggerpostun would be ideal, but it does not get
+-- executed for some reason.
+-- On upgrade, we need to run daemon-reload after any new unit files
+-- have been installed, but before %postun scripts in packages get
+-- executed. %transfiletriggerun gets the right list of files
+-- but it is invoked too early (before changes happen).
+-- %filetriggerpostun happens at the right time, but it fires for
+-- every package.
+-- To execute the reload at the right time, we create a state
+-- file in %transfiletriggerun and execute the daemon-reload in
+-- the first %filetriggerpostun.
+
+posix.mkdir("%{_localstatedir}/lib")
+posix.mkdir("%{_localstatedir}/lib/rpm-state")
+posix.mkdir("%{_localstatedir}/lib/rpm-state/systemd")
+io.open("%{_localstatedir}/lib/rpm-state/systemd/needs-reload", "w")
+
+%filetriggerpostun -P 1000100 -p <lua> -- @systemunitdir@ /etc/systemd/system
+if posix.access("%{_localstatedir}/lib/rpm-state/systemd/needs-reload") then
+    posix.unlink("%{_localstatedir}/lib/rpm-state/systemd/needs-reload")
+    posix.rmdir("%{_localstatedir}/lib/rpm-state/systemd")
+    pid = posix.fork()
+    if pid == 0 then
+        assert(posix.exec("%{_bindir}/systemctl", "daemon-reload"))
+    elseif pid > 0 then
+        posix.wait(pid)
+    end
+end
index d199d87bf80bdb42668e029a5fb88bbbc3717850..e6e67d27c88c48e9cfefb9e04e9fe10d85da9a22 100644 (file)
@@ -1347,12 +1347,18 @@ static bool unit_assert_test(Unit *u) {
         return u->assert_result;
 }
 
+void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg_format) {
+        DISABLE_WARNING_FORMAT_NONLITERAL;
+        manager_status_printf(u->manager, STATUS_TYPE_NORMAL, status, unit_status_msg_format, unit_description(u));
+        REENABLE_WARNING;
+}
+
 _pure_ static const char* unit_get_status_message_format(Unit *u, JobType t) {
         const char *format;
         const UnitStatusMessageFormats *format_table;
 
         assert(u);
-        assert(t == JOB_START || t == JOB_STOP || t == JOB_RELOAD);
+        assert(IN_SET(t, JOB_START, JOB_STOP, JOB_RELOAD));
 
         if (t != JOB_RELOAD) {
                 format_table = &UNIT_VTABLE(u)->status_message_formats;
@@ -1377,6 +1383,10 @@ static void unit_status_print_starting_stopping(Unit *u, JobType t) {
 
         assert(u);
 
+        /* Reload status messages have traditionally not been printed to console. */
+        if (!IN_SET(t, JOB_START, JOB_STOP))
+                return;
+
         format = unit_get_status_message_format(u, t);
 
         DISABLE_WARNING_FORMAT_NONLITERAL;
@@ -1391,7 +1401,7 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) {
 
         assert(u);
 
-        if (t != JOB_START && t != JOB_STOP && t != JOB_RELOAD)
+        if (!IN_SET(t, JOB_START, JOB_STOP, JOB_RELOAD))
                 return;
 
         if (log_on_console())
@@ -1423,12 +1433,12 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) {
 }
 
 void unit_status_emit_starting_stopping_reloading(Unit *u, JobType t) {
+        assert(u);
+        assert(t >= 0);
+        assert(t < _JOB_TYPE_MAX);
 
         unit_status_log_starting_stopping_reloading(u, t);
-
-        /* Reload status messages have traditionally not been printed to console. */
-        if (t != JOB_RELOAD)
-                unit_status_print_starting_stopping(u, t);
+        unit_status_print_starting_stopping(u, t);
 }
 
 /* Errors:
@@ -2830,7 +2840,7 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
         }
 }
 
-int unit_add_node_link(Unit *u, const char *what, bool wants) {
+int unit_add_node_link(Unit *u, const char *what, bool wants, UnitDependency dep) {
         Unit *device;
         _cleanup_free_ char *e = NULL;
         int r;
@@ -2857,7 +2867,9 @@ int unit_add_node_link(Unit *u, const char *what, bool wants) {
         if (r < 0)
                 return r;
 
-        r = unit_add_two_dependencies(u, UNIT_AFTER, u->manager->running_as == MANAGER_SYSTEM ? UNIT_BINDS_TO : UNIT_WANTS, device, true);
+        r = unit_add_two_dependencies(u, UNIT_AFTER,
+                                      u->manager->running_as == MANAGER_SYSTEM ? dep : UNIT_WANTS,
+                                      device, true);
         if (r < 0)
                 return r;
 
@@ -2896,13 +2908,6 @@ int unit_coldplug(Unit *u) {
         return 0;
 }
 
-void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg_format) {
-        DISABLE_WARNING_FORMAT_NONLITERAL;
-        manager_status_printf(u->manager, STATUS_TYPE_NORMAL,
-                              status, unit_status_msg_format, unit_description(u));
-        REENABLE_WARNING;
-}
-
 bool unit_need_daemon_reload(Unit *u) {
         _cleanup_strv_free_ char **t = NULL;
         char **path;
@@ -3428,7 +3433,15 @@ int unit_make_transient(Unit *u) {
         u->load_state = UNIT_STUB;
         u->load_error = 0;
         u->transient = true;
+
         u->fragment_path = mfree(u->fragment_path);
+        u->source_path = mfree(u->source_path);
+        u->dropin_paths = strv_free(u->dropin_paths);
+        u->fragment_mtime = u->source_mtime = u->dropin_mtime = 0;
+
+        unit_add_to_dbus_queue(u);
+        unit_add_to_gc_queue(u);
+        unit_add_to_load_queue(u);
 
         return 0;
 }
@@ -3704,3 +3717,21 @@ int unit_fail_if_symlink(Unit *u, const char* where) {
 
         return -ELOOP;
 }
+
+bool unit_is_pristine(Unit *u) {
+        assert(u);
+
+        /* Check if the unit already exists or is already around,
+         * in a number of different ways. Note that to cater for unit
+         * types such as slice, we are generally fine with units that
+         * are marked UNIT_LOADED even even though nothing was
+         * actually loaded, as those unit types don't require a file
+         * on disk to validly load. */
+
+        return !(!IN_SET(u->load_state, UNIT_NOT_FOUND, UNIT_LOADED) ||
+                 u->fragment_path ||
+                 u->source_path ||
+                 !strv_isempty(u->dropin_paths) ||
+                 u->job ||
+                 u->merged_into);
+}
index bcf41d2348d9053c2b4a45c2a26ff435b94f78d1..3eb3484fb714d812d0ff884d6cc791287aeaa530 100644 (file)
@@ -30,11 +30,11 @@ typedef struct UnitVTable UnitVTable;
 typedef struct UnitRef UnitRef;
 typedef struct UnitStatusMessageFormats UnitStatusMessageFormats;
 
-#include "list.h"
 #include "condition.h"
+#include "failure-action.h"
 #include "install.h"
+#include "list.h"
 #include "unit-name.h"
-#include "failure-action.h"
 
 typedef enum KillOperation {
         KILL_TERMINATE,
@@ -242,16 +242,16 @@ typedef enum UnitSetPropertiesMode {
         UNIT_PERSISTENT = 2,
 } UnitSetPropertiesMode;
 
-#include "socket.h"
+#include "automount.h"
 #include "busname.h"
-#include "target.h"
 #include "device.h"
-#include "automount.h"
-#include "swap.h"
-#include "timer.h"
-#include "slice.h"
 #include "path.h"
 #include "scope.h"
+#include "slice.h"
+#include "socket.h"
+#include "swap.h"
+#include "target.h"
+#include "timer.h"
 
 struct UnitVTable {
         /* How much memory does an object of this unit type need */
@@ -528,7 +528,7 @@ int unit_serialize_item_escaped(Unit *u, FILE *f, const char *key, const char *v
 int unit_serialize_item_fd(Unit *u, FILE *f, FDSet *fds, const char *key, int fd);
 void unit_serialize_item_format(Unit *u, FILE *f, const char *key, const char *value, ...) _printf_(4,5);
 
-int unit_add_node_link(Unit *u, const char *what, bool wants);
+int unit_add_node_link(Unit *u, const char *what, bool wants, UnitDependency d);
 
 int unit_coldplug(Unit *u);
 
@@ -586,6 +586,8 @@ int unit_require_mounts_for(Unit *u, const char *path);
 
 bool unit_type_supported(UnitType t);
 
+bool unit_is_pristine(Unit *u);
+
 static inline bool unit_supported(Unit *u) {
         return unit_type_supported(u->type);
 }
index f7c8d11ace19b3a05e244de34425a8d24d80ba1f..87b8b77f222f0432aa75da93e5194c78b51f103f 100644 (file)
@@ -248,6 +248,7 @@ static int add_mount(
         assert(what);
         assert(where);
         assert(opts);
+        assert(post);
         assert(source);
 
         if (streq_ptr(fstype, "autofs"))
@@ -297,7 +298,7 @@ static int add_mount(
                 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n",
                 source);
 
-        if (post && !noauto && !nofail && !automount)
+        if (!noauto && !nofail && !automount)
                 fprintf(f, "Before=%s\n", post);
 
         if (!automount && opts) {
@@ -337,7 +338,7 @@ static int add_mount(
         if (r < 0)
                 return log_error_errno(r, "Failed to write unit file %s: %m", unit);
 
-        if (!noauto && post) {
+        if (!noauto) {
                 lnk = strjoin(arg_dest, "/", post, nofail || automount ? ".wants/" : ".requires/", name, NULL);
                 if (!lnk)
                         return log_oom();
@@ -368,10 +369,7 @@ static int add_mount(
                         "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n",
                         source);
 
-                if (post)
-                        fprintf(f,
-                                "Before=%s\n",
-                                post);
+                fprintf(f, "Before=%s\n", post);
 
                 if (opts) {
                         r = write_requires_after(f, opts);
index 6a2aa81c76c056ec7344c4ea670d3974b3d07d68..eec53c92664517c9258e998271b0325b27bda432 100644 (file)
@@ -21,8 +21,8 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <sys/types.h>
 #include <curl/curl.h>
+#include <sys/types.h>
 
 #include "sd-event.h"
 
index b71de6cb82fca356453aa589be6c312384146eec..e5e298f6abf211ca6dc22ed3fbb2b571f25a7c20 100644 (file)
@@ -22,8 +22,9 @@
 ***/
 
 #include "sd-event.h"
-#include "macro.h"
+
 #include "import-compress.h"
+#include "macro.h"
 
 typedef struct RawExport RawExport;
 
index ce27a9fc1e0401001803a0e31fdbfb315adeb5b0..9061e7515deb88b3cd15b2eb638daf53068a7c5d 100644 (file)
@@ -22,8 +22,9 @@
 ***/
 
 #include "sd-event.h"
-#include "macro.h"
+
 #include "import-compress.h"
+#include "macro.h"
 
 typedef struct TarExport TarExport;
 
index 50d91f732c79563973a716e43c758a36e02a7bba..0a1323255408e7c6af455cb2e2bdf1286870d834 100644 (file)
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <sys/types.h>
-
+#include <bzlib.h>
 #include <lzma.h>
+#include <sys/types.h>
 #include <zlib.h>
-#include <bzlib.h>
 
 #include "macro.h"
 
index bf7c7703408f0623e7930cf186cabc00a85ff873..626d965cf8bcce4da0a223d325ca83d52da5bc9b 100644 (file)
@@ -22,8 +22,9 @@
 ***/
 
 #include "sd-event.h"
-#include "macro.h"
+
 #include "import-util.h"
+#include "macro.h"
 
 typedef struct RawImport RawImport;
 
index aaecb51398e2c867645a382af8ee3752ea05d831..d12391572d74dffd40db054995680e71252e6f40 100644 (file)
@@ -22,8 +22,9 @@
 ***/
 
 #include "sd-event.h"
-#include "macro.h"
+
 #include "import-util.h"
+#include "macro.h"
 
 typedef struct TarImport TarImport;
 
index 7e6db1862ca62d7c0b3cc4e98b5deaaefdded63c..ea228bb5c80e4a9138dc54d0c2ea69cd5a2e8879 100644 (file)
@@ -23,8 +23,8 @@
 
 #include <stdbool.h>
 
-#include "pull-job.h"
 #include "import-util.h"
+#include "pull-job.h"
 
 int pull_make_local_copy(const char *final, const char *root, const char *local, bool force_local);
 
index 33d18cb394ea95be8696138dbd01fd61b35fa64b..a95d91205bbea29bd371c749923d5a03c22c2924 100644 (file)
@@ -22,6 +22,7 @@
 #pragma once
 
 #include "sd-event.h"
+
 #include "util.h"
 
 typedef enum { DKR_PULL_V1, DKR_PULL_V2 } DkrPullVersion;
index 1777bf1c337c25bda9607d71f399b256a8122079..56a74a34ef497d42829a3859675e1e0240b15e84 100644 (file)
@@ -23,9 +23,9 @@
 
 #include <gcrypt.h>
 
-#include "macro.h"
 #include "curl-util.h"
 #include "import-compress.h"
+#include "macro.h"
 
 typedef struct PullJob PullJob;
 
index b03b4f5c92de9e61298b0b141b4f8657da2dd8bd..0e4e1daf0e46a216649d00a70d658e23297ebb49 100644 (file)
@@ -22,8 +22,9 @@
 ***/
 
 #include "sd-event.h"
-#include "macro.h"
+
 #include "import-util.h"
+#include "macro.h"
 
 typedef struct RawPull RawPull;
 
index 420845ae508e7f8f4e61b7b2d53ac18b09a9bd7d..9f02f1ec718b4b4941fde4448c537ccbf96059c0 100644 (file)
@@ -22,8 +22,9 @@
 ***/
 
 #include "sd-event.h"
-#include "macro.h"
+
 #include "import-util.h"
+#include "macro.h"
 
 typedef struct TarPull TarPull;
 
index 14bfadc1328ec578d7f10e3d3cadaa386c7e2e0b..58cb5e70dfbf7b9c2d3c8cf7c6ee6f1994389c59 100644 (file)
@@ -22,6 +22,7 @@
 #pragma once
 
 #include "sd-event.h"
+
 #include "journal-remote-write.h"
 
 typedef enum {
index 6c2ccb9735cb6cdf31ffd085457302fab49efb11..fd81a1c592c0ec8b602ac62e4d84758b18931b0f 100644 (file)
 
 
 #include "sd-event.h"
-#include "hashmap.h"
-#include "microhttpd-util.h"
 
+#include "hashmap.h"
 #include "journal-remote-parse.h"
 #include "journal-remote-write.h"
+#include "microhttpd-util.h"
 
 typedef struct MHDDaemonWrapper MHDDaemonWrapper;
 
index 3b46fa8cbf0d34e52ddfb6353c33686fc7a01fd5..b8cd04d5275f6e5b42572445a97ae157e8b18e47 100644 (file)
@@ -2,8 +2,8 @@
 
 #include <inttypes.h>
 
-#include "sd-journal.h"
 #include "sd-event.h"
+#include "sd-journal.h"
 
 typedef enum {
         ENTRY_CURSOR = 0,           /* Nothing actually written yet. */
index b2feb9180a6578b5644f38bbfdda402438759e2c..3e8c4fa6d15533affb61610fb969a9d7ea8564a2 100644 (file)
@@ -21,8 +21,8 @@
 
 #pragma once
 
-#include <stdarg.h>
 #include <microhttpd.h>
+#include <stdarg.h>
 
 #include "macro.h"
 
index a72ecf6de7d04a630d8805976989041b5585bbbf..bcc73c263194780fe63326dbc2bb674d592a52ef 100644 (file)
@@ -24,6 +24,7 @@
 #include <stdbool.h>
 
 #include "sd-id128.h"
+
 #include "hashmap.h"
 #include "strbuf.h"
 
index 5959b1fed2f8e6a83d21567faf7afc26325801b4..b79221fc2e1ea66f62fad53cbfb22aa7e66f53ac 100644 (file)
@@ -25,8 +25,8 @@
  *
  */
 
-#include <sys/types.h>
 #include <inttypes.h>
+#include <sys/types.h>
 
 #include "macro.h"
 #include "util.h"
index 898d12d9924d331a0f70b4c5eebbb9d17727252f..46c1f3278ea2beeed1b05bed590da0809ea9b460 100644 (file)
 
 #include "sd-id128.h"
 
-#include "sparse-endian.h"
+#include "hashmap.h"
 #include "journal-def.h"
 #include "macro.h"
 #include "mmap-cache.h"
-#include "hashmap.h"
+#include "sparse-endian.h"
 
 typedef struct JournalMetrics {
         /* For all these: -1 means "pick automatically", and 0 means "no limit enforced" */
index 06847402e0c79216b2d9c35fc374e026f9b7afd0..c3e75ad240f982025c3c616fc37dc8c95f104bcb 100644 (file)
@@ -21,9 +21,9 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <sys/types.h>
 #include <inttypes.h>
 #include <stdbool.h>
+#include <sys/types.h>
 
 #include "sd-id128.h"
 #include "sd-journal.h"
index 68cdfb3410a91ce5416feadc599e73197c66cc83..5c88bb638345718da42daeda6bb65a76810694d7 100644 (file)
@@ -21,8 +21,8 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include "socket-util.h"
 #include "journald-server.h"
+#include "socket-util.h"
 
 void server_process_audit_message(Server *s, const void *buffer, size_t buffer_size, const struct ucred *ucred, const union sockaddr_union *sa, socklen_t salen);
 
index 44e5c893a7bc2aa66468bb8ef2f95bf1e24e5b41..63c559f8dd5cd1db44bba3435fec408842b9ff38 100644 (file)
@@ -23,8 +23,8 @@
 
 #include <netinet/if_ether.h>
 
-#include "sparse-endian.h"
 #include "socket-util.h"
+#include "sparse-endian.h"
 
 int arp_network_bind_raw_socket(int index, be32_t address, const struct ether_addr *eth_mac);
 
index a5daaa543a8ee8040cd3ea8308ebbc1e3cfcaa4f..7038212bcf753c322eafde00ce9996853c2c0fa4 100644 (file)
@@ -47,8 +47,7 @@ int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset, uint8_
 typedef int (*dhcp_option_cb_t)(uint8_t code, uint8_t len,
                                 const void *option, void *userdata);
 
-int dhcp_option_parse(DHCPMessage *message, size_t len,
-                      dhcp_option_cb_t cb, void *userdata);
+int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_cb_t cb, void *userdata, char **error_message);
 
 int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid,
                       uint8_t type, uint16_t arp_type, size_t optlen,
index c6b97ca8f7924e6acf097c25873e36774f224a00..138bdd9691d4cd01f8c256b929a5df762003a17e 100644 (file)
 #include <stdint.h>
 #include <linux/if_packet.h>
 
-#include "util.h"
-#include "list.h"
+#include "sd-dhcp-client.h"
 
 #include "dhcp-protocol.h"
-
-#include "sd-dhcp-client.h"
+#include "list.h"
+#include "util.h"
 
 struct sd_dhcp_route {
         struct in_addr dst_addr;
index a6c410ba9118bac4d65428fa2acc713c8e42220a..1de7f3639cbca8e73ed849b43a157f8e54def2d6 100644 (file)
@@ -24,6 +24,9 @@
 #include <stdio.h>
 #include <string.h>
 
+#include "alloc-util.h"
+#include "utf8.h"
+
 #include "dhcp-internal.h"
 
 static int option_append(uint8_t options[], size_t size, size_t *offset,
@@ -139,72 +142,84 @@ int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset,
 }
 
 static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overload,
-                         uint8_t *message_type, dhcp_option_cb_t cb,
+                         uint8_t *message_type, char **error_message, dhcp_option_cb_t cb,
                          void *userdata) {
         uint8_t code, len;
+        const uint8_t *option;
         size_t offset = 0;
 
         while (offset < buflen) {
-                switch (options[offset]) {
-                case DHCP_OPTION_PAD:
-                        offset++;
+                code = options[offset ++];
 
-                        break;
+                switch (code) {
+                case DHCP_OPTION_PAD:
+                        continue;
 
                 case DHCP_OPTION_END:
                         return 0;
+                }
 
-                case DHCP_OPTION_MESSAGE_TYPE:
-                        if (buflen < offset + 3)
-                                return -ENOBUFS;
+                if (buflen < offset + 1)
+                        return -ENOBUFS;
+
+                len = options[offset ++];
 
-                        len = options[++offset];
+                if (buflen < offset + len)
+                        return -EINVAL;
+
+                option = &options[offset];
+
+                switch (code) {
+                case DHCP_OPTION_MESSAGE_TYPE:
                         if (len != 1)
                                 return -EINVAL;
 
                         if (message_type)
-                                *message_type = options[++offset];
-                        else
-                                offset++;
-
-                        offset++;
+                                *message_type = *option;
 
                         break;
 
-                case DHCP_OPTION_OVERLOAD:
-                        if (buflen < offset + 3)
-                                return -ENOBUFS;
-
-                        len = options[++offset];
-                        if (len != 1)
+                case DHCP_OPTION_ERROR_MESSAGE:
+                        if (len == 0)
                                 return -EINVAL;
 
-                        if (overload)
-                                *overload = options[++offset];
-                        else
-                                offset++;
+                        if (error_message) {
+                                _cleanup_free_ char *string = NULL;
 
-                        offset++;
+                                /* Accept a trailing NUL byte */
+                                if (memchr(option, 0, len - 1))
+                                        return -EINVAL;
 
-                        break;
+                                string = strndup((const char *) option, len);
+                                if (!string)
+                                        return -ENOMEM;
 
-                default:
-                        if (buflen < offset + 3)
-                                return -ENOBUFS;
+                                if (!ascii_is_valid(string))
+                                        return -EINVAL;
 
-                        code = options[offset];
-                        len = options[++offset];
+                                free(*error_message);
+                                *error_message = string;
+                                string = NULL;
+                        }
 
-                        if (buflen < ++offset + len)
+                        break;
+                case DHCP_OPTION_OVERLOAD:
+                        if (len != 1)
                                 return -EINVAL;
 
-                        if (cb)
-                                cb(code, len, &options[offset], userdata);
+                        if (overload)
+                                *overload = *option;
 
-                        offset += len;
+                        break;
+
+                default:
+                        if (cb)
+                                cb(code, len, option, userdata);
 
                         break;
                 }
+
+                offset += len;
         }
 
         if (offset < buflen)
@@ -213,8 +228,8 @@ static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overlo
         return 0;
 }
 
-int dhcp_option_parse(DHCPMessage *message, size_t len,
-                      dhcp_option_cb_t cb, void *userdata) {
+int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_cb_t cb, void *userdata, char **_error_message) {
+        _cleanup_free_ char *error_message = NULL;
         uint8_t overload = 0;
         uint8_t message_type = 0;
         int r;
@@ -227,27 +242,29 @@ int dhcp_option_parse(DHCPMessage *message, size_t len,
 
         len -= sizeof(DHCPMessage);
 
-        r = parse_options(message->options, len, &overload, &message_type,
-                          cb, userdata);
+        r = parse_options(message->options, len, &overload, &message_type, &error_message, cb, userdata);
         if (r < 0)
                 return r;
 
         if (overload & DHCP_OVERLOAD_FILE) {
-                r = parse_options(message->file, sizeof(message->file),
-                                NULL, &message_type, cb, userdata);
+                r = parse_options(message->file, sizeof(message->file), NULL, &message_type, &error_message, cb, userdata);
                 if (r < 0)
                         return r;
         }
 
         if (overload & DHCP_OVERLOAD_SNAME) {
-                r = parse_options(message->sname, sizeof(message->sname),
-                                NULL, &message_type, cb, userdata);
+                r = parse_options(message->sname, sizeof(message->sname), NULL, &message_type, &error_message, cb, userdata);
                 if (r < 0)
                         return r;
         }
 
-        if (message_type)
-                return message_type;
+        if (message_type == 0)
+                return -ENOMSG;
+
+        if (_error_message && IN_SET(message_type, DHCP_NAK, DHCP_DECLINE)) {
+                *_error_message = error_message;
+                error_message = NULL;
+        }
 
-        return -ENOMSG;
+        return message_type;
 }
index 88a81d2866a7312d04198b9f23342aff47c5c586..f65529a00eb277d724e828907edecb0187b8a3a1 100644 (file)
@@ -21,8 +21,8 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <netinet/udp.h>
 #include <netinet/ip.h>
+#include <netinet/udp.h>
 #include <stdint.h>
 
 #include "macro.h"
@@ -132,11 +132,13 @@ enum {
         DHCP_OPTION_MESSAGE_TYPE                = 53,
         DHCP_OPTION_SERVER_IDENTIFIER           = 54,
         DHCP_OPTION_PARAMETER_REQUEST_LIST      = 55,
+        DHCP_OPTION_ERROR_MESSAGE               = 56,
         DHCP_OPTION_MAXIMUM_MESSAGE_SIZE        = 57,
         DHCP_OPTION_RENEWAL_T1_TIME             = 58,
         DHCP_OPTION_REBINDING_T2_TIME           = 59,
         DHCP_OPTION_VENDOR_CLASS_IDENTIFIER     = 60,
         DHCP_OPTION_CLIENT_IDENTIFIER           = 61,
+        DHCP_OPTION_FQDN                        = 81,
         DHCP_OPTION_NEW_POSIX_TIMEZONE          = 100,
         DHCP_OPTION_NEW_TZDB_TIMEZONE           = 101,
         DHCP_OPTION_CLASSLESS_STATIC_ROUTE      = 121,
@@ -144,3 +146,12 @@ enum {
         DHCP_OPTION_PRIVATE_LAST                = 254,
         DHCP_OPTION_END                         = 255,
 };
+
+#define DHCP_MAX_FQDN_LENGTH 255
+
+enum {
+        DHCP_FQDN_FLAG_S = (1 << 0),
+        DHCP_FQDN_FLAG_O = (1 << 1),
+        DHCP_FQDN_FLAG_E = (1 << 2),
+        DHCP_FQDN_FLAG_N = (1 << 3),
+};
index 3b88b93d9a5b1ab3a8db7ee259fe56647f465915..a42f622c37afe3f287620f6474762e53294ee739 100644 (file)
 
 #pragma once
 
-#include "sd-event.h"
 #include "sd-dhcp-server.h"
+#include "sd-event.h"
 
+#include "dhcp-internal.h"
 #include "hashmap.h"
-#include "util.h"
 #include "log.h"
-
-#include "dhcp-internal.h"
+#include "util.h"
 
 typedef struct DHCPClientId {
         size_t length;
index 4edecf7711b8fa56bf8f32f7fc23022a229f839e..f6cf0b30d331d77d7c988653366e9d2286169873 100644 (file)
@@ -25,6 +25,7 @@
 #include <stdint.h>
 
 #include "sd-dhcp6-lease.h"
+
 #include "dhcp6-internal.h"
 
 struct sd_dhcp6_lease {
index 62023a9e490354b7f6d3704603aae8f82268547f..850212aea1e3f1621ecdccf2d1321e291b151986 100644 (file)
@@ -360,7 +360,6 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char *
                                 /* End of name */
                                 break;
                         else if (c <= 63) {
-                                _cleanup_free_ char *t = NULL;
                                 const char *label;
 
                                 /* Literal label */
@@ -369,21 +368,20 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char *
                                 if (pos > optlen)
                                         return -EMSGSIZE;
 
-                                r = dns_label_escape(label, c, &t);
-                                if (r < 0)
-                                        goto fail;
-
-                                if (!GREEDY_REALLOC0(ret, allocated, n + !first + strlen(t) + 1)) {
+                                if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) {
                                         r = -ENOMEM;
                                         goto fail;
                                 }
 
-                                if (!first)
-                                        ret[n++] = '.';
-                                else
+                                if (first)
                                         first = false;
+                                else
+                                        ret[n++] = '.';
+
+                                r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX);
+                                if (r < 0)
+                                        goto fail;
 
-                                memcpy(ret + n, t, r);
                                 n += r;
                                 continue;
                         } else {
index 91308bf6c33ccadf0e14e3346cd6a1ce9cef1295..acad9d7d6a89a7aa442750a1114584e8f212fcc7 100644 (file)
@@ -47,17 +47,15 @@ int icmp6_bind_router_solicitation(int index) {
                 .ipv6mr_interface = index,
         };
         _cleanup_close_ int s = -1;
-        int r, zero = 0, hops = 255;
+        int r, zero = 0, one = 1, hops = 255;
 
-        s = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK,
-                   IPPROTO_ICMPV6);
+        s = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_ICMPV6);
         if (s < 0)
                 return -errno;
 
         ICMP6_FILTER_SETBLOCKALL(&filter);
         ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter);
-        r = setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filter,
-                       sizeof(filter));
+        r = setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof(filter));
         if (r < 0)
                 return -errno;
 
@@ -65,23 +63,23 @@ int icmp6_bind_router_solicitation(int index) {
            IPV6_PKTINFO socket option also applies for ICMPv6 multicast.
            Empirical experiments indicates otherwise and therefore an
            IPV6_MULTICAST_IF socket option is used here instead */
-        r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &index,
-                       sizeof(index));
+        r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &index, sizeof(index));
         if (r < 0)
                 return -errno;
 
-        r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &zero,
-                       sizeof(zero));
+        r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &zero, sizeof(zero));
         if (r < 0)
                 return -errno;
 
-        r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops,
-                       sizeof(hops));
+        r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, sizeof(hops));
         if (r < 0)
                 return -errno;
 
-        r = setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq,
-                       sizeof(mreq));
+        r = setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
+        if (r < 0)
+                return -errno;
+
+        r = setsockopt(s, SOL_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one));
         if (r < 0)
                 return -errno;
 
@@ -101,25 +99,25 @@ int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) {
                 struct ether_addr rs_opt_mac;
         } _packed_ rs = {
                 .rs.nd_rs_type = ND_ROUTER_SOLICIT,
+                .rs_opt.nd_opt_type = ND_OPT_SOURCE_LINKADDR,
+                .rs_opt.nd_opt_len = 1,
         };
-        struct iovec iov[1] = {
-                { &rs, },
+        struct iovec iov = {
+                .iov_base = &rs,
+                .iov_len = sizeof(rs),
         };
         struct msghdr msg = {
                 .msg_name = &dst,
                 .msg_namelen = sizeof(dst),
-                .msg_iov = iov,
+                .msg_iov = &iov,
                 .msg_iovlen = 1,
         };
         int r;
 
-        if (ether_addr) {
-                memcpy(&rs.rs_opt_mac, ether_addr, ETH_ALEN);
-                rs.rs_opt.nd_opt_type = ND_OPT_SOURCE_LINKADDR;
-                rs.rs_opt.nd_opt_len = 1;
-                iov[0].iov_len = sizeof(rs);
-        } else
-                iov[0].iov_len = sizeof(rs.rs);
+        assert(s >= 0);
+        assert(ether_addr);
+
+        rs.rs_opt_mac = *ether_addr;
 
         r = sendmsg(s, &msg, 0);
         if (r < 0)
index ca1da113d59c44620261fd32b674cd97a0c22e4c..f5cd77477fd7d365b5828734fbf821ec2bd1277e 100644 (file)
 
 #include <net/ethernet.h>
 
-#include "util.h"
-#include "lldp.h"
-#include "list.h"
-
 #include "sd-lldp.h"
 
+#include "list.h"
+#include "lldp.h"
+#include "util.h"
+
 typedef struct sd_lldp_packet tlv_packet;
 typedef struct sd_lldp_section tlv_section;
 
index d516f2dafd55218ae0e61e87902b03ee2054e256..8a30921966bd1275bdcf53b1c29c4ec6e3902571 100644 (file)
@@ -23,8 +23,8 @@
 
 #include <stdbool.h>
 
-#include "udev.h"
 #include "condition.h"
+#include "udev.h"
 
 bool net_match_config(const struct ether_addr *match_mac,
                       char * const *match_path,
index 137537253a4aad9a9cfb034c31a11050204f429d..7deb00af9c27d805a781f258d3b7e1d79e4ed9f8 100644 (file)
@@ -34,6 +34,8 @@
 #include "dhcp-internal.h"
 #include "dhcp-lease-internal.h"
 #include "dhcp-protocol.h"
+#include "dns-domain.h"
+#include "hostname-util.h"
 #include "random-util.h"
 #include "string-util.h"
 #include "util.h"
@@ -298,6 +300,9 @@ int sd_dhcp_client_set_hostname(sd_dhcp_client *client,
 
         assert_return(client, -EINVAL);
 
+        if (!hostname_is_valid(hostname, false) && !dns_name_is_valid(hostname))
+                return -EINVAL;
+
         if (streq_ptr(client->hostname, hostname))
                 return 0;
 
@@ -539,6 +544,24 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
         return 0;
 }
 
+static int client_append_fqdn_option(DHCPMessage *message, size_t optlen, size_t *optoffset,
+                                     const char *fqdn) {
+        uint8_t buffer[3 + DHCP_MAX_FQDN_LENGTH];
+        int r;
+
+        buffer[0] = DHCP_FQDN_FLAG_S | /* Request server to perform A RR DNS updates */
+                    DHCP_FQDN_FLAG_E;  /* Canonical wire format */
+        buffer[1] = 0;                 /* RCODE1 (deprecated) */
+        buffer[2] = 0;                 /* RCODE2 (deprecated) */
+
+        r = dns_name_to_wire_format(fqdn, buffer + 3, sizeof(buffer) - 3);
+        if (r > 0)
+                r = dhcp_option_append(message, optlen, optoffset, 0,
+                                       DHCP_OPTION_FQDN, 3 + r, buffer);
+
+        return r;
+}
+
 static int dhcp_client_send_raw(sd_dhcp_client *client, DHCPPacket *packet,
                                 size_t len) {
         dhcp_packet_append_ip_headers(packet, INADDR_ANY, DHCP_PORT_CLIENT,
@@ -576,13 +599,21 @@ static int client_send_discover(sd_dhcp_client *client) {
                         return r;
         }
 
-        /* it is unclear from RFC 2131 if client should send hostname in
-           DHCPDISCOVER but dhclient does and so we do as well
-        */
         if (client->hostname) {
-                r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
-                                       DHCP_OPTION_HOST_NAME,
-                                       strlen(client->hostname), client->hostname);
+                /* According to RFC 4702 "clients that send the Client FQDN option in
+                   their messages MUST NOT also send the Host Name option". Just send
+                   one of the two depending on the hostname type.
+                */
+                if (dns_name_is_single_label(client->hostname)) {
+                        /* it is unclear from RFC 2131 if client should send hostname in
+                           DHCPDISCOVER but dhclient does and so we do as well
+                        */
+                        r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
+                                               DHCP_OPTION_HOST_NAME,
+                                               strlen(client->hostname), client->hostname);
+                } else
+                        r = client_append_fqdn_option(&discover->dhcp, optlen, &optoffset,
+                                                      client->hostname);
                 if (r < 0)
                         return r;
         }
@@ -688,9 +719,13 @@ static int client_send_request(sd_dhcp_client *client) {
         }
 
         if (client->hostname) {
-                r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
-                                       DHCP_OPTION_HOST_NAME,
-                                       strlen(client->hostname), client->hostname);
+                if (dns_name_is_single_label(client->hostname))
+                        r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
+                                               DHCP_OPTION_HOST_NAME,
+                                               strlen(client->hostname), client->hostname);
+                else
+                        r = client_append_fqdn_option(&request->dhcp, optlen, &optoffset,
+                                                      client->hostname);
                 if (r < 0)
                         return r;
         }
@@ -1047,7 +1082,7 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer,
                         return r;
         }
 
-        r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease);
+        r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease, NULL);
         if (r != DHCP_OFFER) {
                 log_dhcp_client(client, "received message was not an OFFER, ignoring");
                 return -ENOMSG;
@@ -1086,7 +1121,7 @@ static int client_handle_forcerenew(sd_dhcp_client *client, DHCPMessage *force,
                                     size_t len) {
         int r;
 
-        r = dhcp_option_parse(force, len, NULL, NULL);
+        r = dhcp_option_parse(force, len, NULL, NULL, NULL);
         if (r != DHCP_FORCERENEW)
                 return -ENOMSG;
 
@@ -1098,6 +1133,7 @@ static int client_handle_forcerenew(sd_dhcp_client *client, DHCPMessage *force,
 static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
                              size_t len) {
         _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
+        _cleanup_free_ char *error_message = NULL;
         int r;
 
         r = dhcp_lease_new(&lease);
@@ -1112,9 +1148,9 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
                         return r;
         }
 
-        r = dhcp_option_parse(ack, len, dhcp_lease_parse_options, lease);
+        r = dhcp_option_parse(ack, len, dhcp_lease_parse_options, lease, &error_message);
         if (r == DHCP_NAK) {
-                log_dhcp_client(client, "NAK");
+                log_dhcp_client(client, "NAK: %s", strna(error_message));
                 return -EADDRNOTAVAIL;
         }
 
@@ -1478,9 +1514,8 @@ static int client_receive_message_udp(sd_event_source *s, int fd,
 
         r = ioctl(fd, FIONREAD, &buflen);
         if (r < 0)
-                return r;
-
-        if (buflen < 0)
+                return -errno;
+        else if (buflen < 0)
                 /* this can't be right */
                 return -EIO;
 
@@ -1490,26 +1525,28 @@ static int client_receive_message_udp(sd_event_source *s, int fd,
 
         len = read(fd, message, buflen);
         if (len < 0) {
-                log_dhcp_client(client, "could not receive message from UDP "
-                                "socket: %m");
-                return 0;
+                if (errno == EAGAIN || errno == EINTR)
+                        return 0;
+
+                log_dhcp_client(client, "Could not receive message from UDP socket: %m");
+                return -errno;
         } else if ((size_t)len < sizeof(DHCPMessage)) {
-                log_dhcp_client(client, "too small to be a DHCP message: ignoring");
+                log_dhcp_client(client, "Too small to be a DHCP message: ignoring");
                 return 0;
         }
 
         if (be32toh(message->magic) != DHCP_MAGIC_COOKIE) {
-                log_dhcp_client(client, "not a DHCP message: ignoring");
+                log_dhcp_client(client, "Not a DHCP message: ignoring");
                 return 0;
         }
 
         if (message->op != BOOTREPLY) {
-                log_dhcp_client(client, "not a BOOTREPLY message: ignoring");
+                log_dhcp_client(client, "Not a BOOTREPLY message: ignoring");
                 return 0;
         }
 
         if (message->htype != client->arp_type) {
-                log_dhcp_client(client, "packet type does not match client type");
+                log_dhcp_client(client, "Packet type does not match client type");
                 return 0;
         }
 
@@ -1523,13 +1560,12 @@ static int client_receive_message_udp(sd_event_source *s, int fd,
         }
 
         if (message->hlen != expected_hlen) {
-                log_dhcp_client(client, "unexpected packet hlen %d", message->hlen);
+                log_dhcp_client(client, "Unexpected packet hlen %d", message->hlen);
                 return 0;
         }
 
         if (memcmp(&message->chaddr[0], expected_chaddr, ETH_ALEN)) {
-                log_dhcp_client(client, "received chaddr does not match "
-                                "expected: ignoring");
+                log_dhcp_client(client, "Received chaddr does not match expected: ignoring");
                 return 0;
         }
 
@@ -1537,8 +1573,7 @@ static int client_receive_message_udp(sd_event_source *s, int fd,
             be32toh(message->xid) != client->xid) {
                 /* in BOUND state, we may receive FORCERENEW with xid set by server,
                    so ignore the xid in this case */
-                log_dhcp_client(client, "received xid (%u) does not match "
-                                "expected (%u): ignoring",
+                log_dhcp_client(client, "Received xid (%u) does not match expected (%u): ignoring",
                                 be32toh(message->xid), client->xid);
                 return 0;
         }
@@ -1567,9 +1602,8 @@ static int client_receive_message_raw(sd_event_source *s, int fd,
 
         r = ioctl(fd, FIONREAD, &buflen);
         if (r < 0)
-                return r;
-
-        if (buflen < 0)
+                return -errno;
+        else if (buflen < 0)
                 /* this can't be right */
                 return -EIO;
 
@@ -1582,9 +1616,12 @@ static int client_receive_message_raw(sd_event_source *s, int fd,
 
         len = recvmsg(fd, &msg, 0);
         if (len < 0) {
-                log_dhcp_client(client, "could not receive message from raw "
-                                "socket: %m");
-                return 0;
+                if (errno == EAGAIN || errno == EINTR)
+                        return 0;
+
+                log_dhcp_client(client, "Could not receive message from raw socket: %m");
+
+                return -errno;
         } else if ((size_t)len < sizeof(DHCPPacket))
                 return 0;
 
index 8befedc5002cacc57e9ef792912211da9201f08f..fccdc01bc30e602c9c64b5d593ed46204c8e23eb 100644 (file)
@@ -661,7 +661,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
                 break;
 
         default:
-                log_debug("Ignoring option DHCP option %i while parsing.", code);
+                log_debug("Ignoring option DHCP option %"PRIu8" while parsing.", code);
                 break;
         }
 
index 277c88e2b98360f91ef28b9ec31acdfece3b6073..587ff936ba0dc513fd40894707709f354ad87dff 100644 (file)
@@ -699,6 +699,7 @@ static int get_pool_offset(sd_dhcp_server *server, be32_t requested_ip) {
 int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
                                size_t length) {
         _cleanup_dhcp_request_free_ DHCPRequest *req = NULL;
+        _cleanup_free_ char *error_message = NULL;
         DHCPLease *existing_lease;
         int type, r;
 
@@ -714,7 +715,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
         if (!req)
                 return -ENOMEM;
 
-        type = dhcp_option_parse(message, length, parse_request, req);
+        type = dhcp_option_parse(message, length, parse_request, req, &error_message);
         if (type < 0)
                 return 0;
 
@@ -784,8 +785,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
                 break;
         }
         case DHCP_DECLINE:
-                log_dhcp_server(server, "DECLINE (0x%x)",
-                                be32toh(req->message->xid));
+                log_dhcp_server(server, "DECLINE (0x%x): %s", be32toh(req->message->xid), strna(error_message));
 
                 /* TODO: make sure we don't offer this address again */
 
@@ -963,10 +963,10 @@ static int server_receive_message(sd_event_source *s, int fd,
 
         if (ioctl(fd, FIONREAD, &buflen) < 0)
                 return -errno;
-        if (buflen < 0)
+        else if (buflen < 0)
                 return -EIO;
 
-        message = malloc0(buflen);
+        message = malloc(buflen);
         if (!message)
                 return -ENOMEM;
 
@@ -974,9 +974,12 @@ static int server_receive_message(sd_event_source *s, int fd,
         iov.iov_len = buflen;
 
         len = recvmsg(fd, &msg, 0);
-        if (len < buflen)
-                return 0;
-        else if ((size_t)len < sizeof(DHCPMessage))
+        if (len < 0) {
+                if (errno == EAGAIN || errno == EINTR)
+                        return 0;
+
+                return -errno;
+        } else if ((size_t)len < sizeof(DHCPMessage))
                 return 0;
 
         CMSG_FOREACH(cmsg, &msg) {
index 801331d270ba61824b75c8b6fdc71d9d8e85d0f5..36d909a4c5e56dd7cf3e7138d52b83a24becb05c 100644 (file)
@@ -895,7 +895,7 @@ static int client_receive_advertise(sd_dhcp6_client *client, DHCP6Message *adver
 static int client_receive_message(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
         sd_dhcp6_client *client = userdata;
         DHCP6_CLIENT_DONT_DESTROY(client);
-        _cleanup_free_ DHCP6Message *message;
+        _cleanup_free_ DHCP6Message *message = NULL;
         int r, buflen, len;
 
         assert(s);
@@ -903,18 +903,26 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
         assert(client->event);
 
         r = ioctl(fd, FIONREAD, &buflen);
-        if (r < 0 || buflen <= 0)
-                buflen = DHCP6_MIN_OPTIONS_SIZE;
+        if (r < 0)
+                return -errno;
+        else if (buflen < 0)
+                /* This really should not happen */
+                return -EIO;
 
-        message = malloc0(buflen);
+        message = malloc(buflen);
         if (!message)
                 return -ENOMEM;
 
         len = read(fd, message, buflen);
-        if ((size_t)len < sizeof(DHCP6Message)) {
-                log_dhcp6_client(client, "could not receive message from UDP socket: %m");
+        if (len < 0) {
+                if (errno == EAGAIN || errno == EINTR)
+                        return 0;
+
+                log_dhcp6_client(client, "Could not receive message from UDP socket: %m");
+
+                return -errno;
+        } else if ((size_t)len < sizeof(DHCP6Message))
                 return 0;
-        }
 
         switch(message->type) {
         case DHCP6_SOLICIT:
index 6703d87bc4173da04e56e65dab0daa75b175ba7b..f2bce3b99f56fca768a1689b75ce15b5a041ed9d 100644 (file)
@@ -32,6 +32,7 @@
 #include "in-addr-util.h"
 #include "list.h"
 #include "socket-util.h"
+#include "string-util.h"
 
 #define NDISC_ROUTER_SOLICITATION_INTERVAL      4 * USEC_PER_SEC
 #define NDISC_MAX_ROUTER_SOLICITATIONS          3
@@ -417,8 +418,7 @@ static int ndisc_prefix_update(sd_ndisc *nd, ssize_t len,
         return 0;
 }
 
-static int ndisc_ra_parse(sd_ndisc *nd, struct nd_router_advert *ra,
-                          ssize_t len) {
+static int ndisc_ra_parse(sd_ndisc *nd, struct nd_router_advert *ra, ssize_t len) {
         void *opt;
         struct nd_opt_hdr *opt_hdr;
 
@@ -481,30 +481,86 @@ static int ndisc_ra_parse(sd_ndisc *nd, struct nd_router_advert *ra,
 static int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
         _cleanup_free_ struct nd_router_advert *ra = NULL;
         sd_ndisc *nd = userdata;
-        int r, buflen = 0, pref, stateful;
-        union sockaddr_union router = {};
-        socklen_t router_len = sizeof(router);
+        union {
+                struct cmsghdr cmsghdr;
+                uint8_t buf[CMSG_LEN(sizeof(int))];
+        } control = {};
+        struct iovec iov = {};
+        union sockaddr_union sa = {};
+        struct msghdr msg = {
+                .msg_name = &sa.sa,
+                .msg_namelen = sizeof(sa),
+                .msg_iov = &iov,
+                .msg_iovlen = 1,
+                .msg_control = &control,
+                .msg_controllen = sizeof(control),
+        };
+        struct cmsghdr *cmsg;
+        struct in6_addr *gw;
         unsigned lifetime;
         ssize_t len;
+        int r, pref, stateful, buflen = 0;
 
         assert(s);
         assert(nd);
         assert(nd->event);
 
         r = ioctl(fd, FIONREAD, &buflen);
-        if (r < 0 || buflen <= 0)
-                buflen = ICMP6_RECV_SIZE;
+        if (r < 0)
+                return -errno;
+        else if (buflen < 0)
+                /* This really should not happen */
+                return -EIO;
+
+        iov.iov_len = buflen;
 
-        ra = malloc(buflen);
+        ra = malloc(iov.iov_len);
         if (!ra)
                 return -ENOMEM;
 
-        len = recvfrom(fd, ra, buflen, 0, &router.sa, &router_len);
+        iov.iov_base = ra;
+
+        len = recvmsg(fd, &msg, 0);
         if (len < 0) {
+                if (errno == EAGAIN || errno == EINTR)
+                        return 0;
+
                 log_ndisc(nd, "Could not receive message from ICMPv6 socket: %m");
+                return -errno;
+        } else if ((size_t)len < sizeof(struct nd_router_advert)) {
                 return 0;
-        } else if (router_len != sizeof(router.in6) && router_len != 0) {
-                log_ndisc(nd, "Received invalid source address size from ICMPv6 socket: %zu bytes", (size_t)router_len);
+        } else if (msg.msg_namelen == 0)
+                gw = NULL; /* only happens when running the test-suite over a socketpair */
+        else if (msg.msg_namelen != sizeof(sa.in6)) {
+                log_ndisc(nd, "Received invalid source address size from ICMPv6 socket: %zu bytes", (size_t)msg.msg_namelen);
+                return 0;
+        } else
+                gw = &sa.in6.sin6_addr;
+
+        assert(!(msg.msg_flags & MSG_CTRUNC));
+        assert(!(msg.msg_flags & MSG_TRUNC));
+
+        CMSG_FOREACH(cmsg, &msg) {
+                if (cmsg->cmsg_level == SOL_IPV6 &&
+                    cmsg->cmsg_type == IPV6_HOPLIMIT &&
+                    cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
+                        int hops = *(int*)CMSG_DATA(cmsg);
+
+                        if (hops != 255) {
+                                log_ndisc(nd, "Received RA with invalid hop limit %d. Ignoring.", hops);
+                                return 0;
+                        }
+
+                        break;
+                }
+        }
+
+        if (gw && !in_addr_is_link_local(AF_INET6, (const union in_addr_union*) gw)) {
+                _cleanup_free_ char *addr = NULL;
+
+                (void)in_addr_to_string(AF_INET6, (const union in_addr_union*) gw, &addr);
+
+                log_ndisc(nd, "Received RA from non-link-local address %s. Ignoring.", strna(addr));
                 return 0;
         }
 
@@ -544,7 +600,7 @@ static int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t r
         }
 
         if (nd->router_callback)
-                nd->router_callback(nd, stateful, router_len != 0 ? &router.in6.sin6_addr : NULL, lifetime, pref, nd->userdata);
+                nd->router_callback(nd, stateful, gw, lifetime, pref, nd->userdata);
 
         return 0;
 }
@@ -552,8 +608,6 @@ static int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t r
 static int ndisc_router_solicitation_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
         sd_ndisc *nd = userdata;
         uint64_t time_now, next_timeout;
-        struct ether_addr unset = { };
-        struct ether_addr *addr = NULL;
         int r;
 
         assert(s);
@@ -567,10 +621,7 @@ static int ndisc_router_solicitation_timeout(sd_event_source *s, uint64_t usec,
                         nd->callback(nd, SD_NDISC_EVENT_TIMEOUT, nd->userdata);
                 nd->state = NDISC_STATE_ADVERTISMENT_LISTEN;
         } else {
-                if (memcmp(&nd->mac_addr, &unset, sizeof(struct ether_addr)))
-                        addr = &nd->mac_addr;
-
-                r = icmp6_send_router_solicitation(nd->fd, addr);
+                r = icmp6_send_router_solicitation(nd->fd, &nd->mac_addr);
                 if (r < 0)
                         log_ndisc(nd, "Error sending Router Solicitation");
                 else {
index 1200a7c251a526aef0189051dfd94a238847e5e5..4478147a830b41116f3e3d582908e5dc1e07f986 100644 (file)
@@ -223,7 +223,7 @@ int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port, const voi
 static int test_discover_message_verify(size_t size, struct DHCPMessage *dhcp) {
         int res;
 
-        res = dhcp_option_parse(dhcp, size, check_options, NULL);
+        res = dhcp_option_parse(dhcp, size, check_options, NULL, NULL);
         assert_se(res == DHCP_DISCOVER);
 
         if (verbose)
@@ -390,7 +390,7 @@ static int test_addr_acq_recv_request(size_t size, DHCPMessage *request) {
         uint8_t *msg_bytes = (uint8_t *)request;
         int res;
 
-        res = dhcp_option_parse(request, size, check_options, NULL);
+        res = dhcp_option_parse(request, size, check_options, NULL, NULL);
         assert_se(res == DHCP_REQUEST);
         assert_se(xid == request->xid);
 
@@ -420,7 +420,7 @@ static int test_addr_acq_recv_discover(size_t size, DHCPMessage *discover) {
         uint8_t *msg_bytes = (uint8_t *)discover;
         int res;
 
-        res = dhcp_option_parse(discover, size, check_options, NULL);
+        res = dhcp_option_parse(discover, size, check_options, NULL, NULL);
         assert_se(res == DHCP_DISCOVER);
 
         assert_se(msg_bytes[size - 1] == DHCP_OPTION_END);
index 3607df63af99814cc793deebe67bdb6f8ded179c..75d22c4df358a99572b8dfc2d8f3eebd4e119f2d 100644 (file)
@@ -75,9 +75,8 @@ static const char *dhcp_type(int type) {
 static void test_invalid_buffer_length(void) {
         DHCPMessage message;
 
-        assert_se(dhcp_option_parse(&message, 0, NULL, NULL) == -EINVAL);
-        assert_se(dhcp_option_parse(&message, sizeof(DHCPMessage) - 1, NULL, NULL)
-               == -EINVAL);
+        assert_se(dhcp_option_parse(&message, 0, NULL, NULL, NULL) == -EINVAL);
+        assert_se(dhcp_option_parse(&message, sizeof(DHCPMessage) - 1, NULL, NULL, NULL) == -EINVAL);
 }
 
 static void test_message_init(void) {
@@ -101,7 +100,7 @@ static void test_message_init(void) {
         assert_se(magic[2] == 83);
         assert_se(magic[3] == 99);
 
-        assert_se(dhcp_option_parse(message, len, NULL, NULL) >= 0);
+        assert_se(dhcp_option_parse(message, len, NULL, NULL, NULL) >= 0);
 }
 
 static DHCPMessage *create_message(uint8_t *options, uint16_t optlen,
@@ -264,19 +263,12 @@ static void test_options(struct option_desc *desc) {
         buflen = sizeof(DHCPMessage) + optlen;
 
         if (!desc) {
-                assert_se((res = dhcp_option_parse(message, buflen,
-                                                test_options_cb,
-                                                NULL)) == -ENOMSG);
+                assert_se((res = dhcp_option_parse(message, buflen, test_options_cb, NULL, NULL)) == -ENOMSG);
         } else if (desc->success) {
-                assert_se((res = dhcp_option_parse(message, buflen,
-                                                test_options_cb,
-                                                desc)) >= 0);
-                assert_se(desc->pos == -1 && desc->filepos == -1 &&
-                                desc->snamepos == -1);
+                assert_se((res = dhcp_option_parse(message, buflen, test_options_cb, desc, NULL)) >= 0);
+                assert_se(desc->pos == -1 && desc->filepos == -1 && desc->snamepos == -1);
         } else
-                assert_se((res = dhcp_option_parse(message, buflen,
-                                                test_options_cb,
-                                                desc)) < 0);
+                assert_se((res = dhcp_option_parse(message, buflen, test_options_cb, desc, NULL)) < 0);
 
         if (verbose)
                 printf("DHCP type %s\n", dhcp_type(res));
index 5009ca8e619567011cb6ee31255c2b4047411784..e01b0758327efe0f4837ad64ebfbbef800b5e36c 100644 (file)
@@ -22,6 +22,7 @@
 ***/
 
 #include "sd-bus.h"
+
 #include "bus-match.h"
 
 int bus_add_match_internal(sd_bus *bus, const char *match, struct bus_match_component *components, unsigned n_components, uint64_t cookie);
index d2522edebad8848dfa7d41c7963e747fc56258f7..71e56991fa8bd6be117315247750a004df52df2f 100644 (file)
@@ -21,8 +21,8 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <stdio.h>
 #include <stdbool.h>
+#include <stdio.h>
 
 #include "sd-bus.h"
 
index fb0199c948f677ba4e13bc4207c0d2b9936bee29..d7fd8612d00dc0da8826983746961db0ca99c640 100644 (file)
@@ -24,6 +24,7 @@
 #include <stdbool.h>
 
 #include "sd-bus.h"
+
 #include "macro.h"
 
 bool bus_error_is_dirty(sd_bus_error *e);
index 1914e6cb8b35c04c509147f0a35eb3e39d1495b0..57c2430ee8d13915c94d6ac4242659146c620019 100644 (file)
@@ -24,6 +24,7 @@
 #include <stdio.h>
 
 #include "sd-bus.h"
+
 #include "set.h"
 
 struct introspect {
index 23a15e4d024ee7d0f45fb34adf6b1f69a9ec36d5..c997e58f9abccfc68e1fde1dcfe0a5c9a4e9ba22 100644 (file)
@@ -22,6 +22,7 @@
 ***/
 
 #include "sd-bus.h"
+
 #include "bus-internal.h"
 
 sd_bus_slot *bus_slot_allocate(sd_bus *bus, bool floating, BusSlotType type, size_t extra, void *userdata);
index 700ac691b5ba0710a3e67ba2fed4220c62b0c558..3191b458d15e1bfc9d9112c91ab7ef90a2e66099 100644 (file)
@@ -416,11 +416,9 @@ _public_ int sd_event_new(sd_event** ret) {
         e->original_pid = getpid();
         e->perturb = USEC_INFINITY;
 
-        e->pending = prioq_new(pending_prioq_compare);
-        if (!e->pending) {
-                r = -ENOMEM;
+        r = prioq_ensure_allocated(&e->pending, pending_prioq_compare);
+        if (r < 0)
                 goto fail;
-        }
 
         e->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
         if (e->epoll_fd < 0) {
@@ -437,7 +435,9 @@ fail:
 }
 
 _public_ sd_event* sd_event_ref(sd_event *e) {
-        assert_return(e, NULL);
+
+        if (!e)
+                return NULL;
 
         assert(e->n_ref >= 1);
         e->n_ref++;
@@ -1050,17 +1050,13 @@ _public_ int sd_event_add_time(
         d = event_get_clock_data(e, type);
         assert(d);
 
-        if (!d->earliest) {
-                d->earliest = prioq_new(earliest_time_prioq_compare);
-                if (!d->earliest)
-                        return -ENOMEM;
-        }
+        r = prioq_ensure_allocated(&d->earliest, earliest_time_prioq_compare);
+        if (r < 0)
+                return r;
 
-        if (!d->latest) {
-                d->latest = prioq_new(latest_time_prioq_compare);
-                if (!d->latest)
-                        return -ENOMEM;
-        }
+        r = prioq_ensure_allocated(&d->latest, latest_time_prioq_compare);
+        if (r < 0)
+                return r;
 
         if (d->fd < 0) {
                 r = event_setup_timer_fd(e, d, clock);
@@ -1311,11 +1307,9 @@ _public_ int sd_event_add_exit(
         assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
         assert_return(!event_pid_changed(e), -ECHILD);
 
-        if (!e->exit) {
-                e->exit = prioq_new(exit_prioq_compare);
-                if (!e->exit)
-                        return -ENOMEM;
-        }
+        r = prioq_ensure_allocated(&e->exit, exit_prioq_compare);
+        if (r < 0)
+                return r;
 
         s = source_new(e, !ret, SOURCE_EXIT);
         if (!s)
@@ -1339,7 +1333,9 @@ _public_ int sd_event_add_exit(
 }
 
 _public_ sd_event_source* sd_event_source_ref(sd_event_source *s) {
-        assert_return(s, NULL);
+
+        if (!s)
+                return NULL;
 
         assert(s->n_ref >= 1);
         s->n_ref++;
@@ -2433,7 +2429,9 @@ _public_ int sd_event_prepare(sd_event *e) {
 
         e->iteration++;
 
+        e->state = SD_EVENT_PREPARING;
         r = event_prepare(e);
+        e->state = SD_EVENT_INITIAL;
         if (r < 0)
                 return r;
 
index c1a3b494836e7dc658a8f96fd35798797b5336b8..9417a8d1d15c0890e862560baeec57871bc30097 100644 (file)
@@ -158,11 +158,22 @@ static int exit_handler(sd_event_source *s, void *userdata) {
         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;
 
         assert_se(pipe(a) >= 0);
         assert_se(pipe(b) >= 0);
@@ -170,6 +181,7 @@ static void test_basic(void) {
         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);
 
@@ -230,10 +242,14 @@ static void test_basic(void) {
         sd_event_source_unref(y);
 
         do_quit = true;
-        assert_se(sd_event_source_set_time(z, now(CLOCK_MONOTONIC) + 200 * USEC_PER_MSEC) >= 0);
+        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);
index 5d0f11a2c1807e2ac3a5f4a1ebfac79f168349e8..74d4f2553414f89317f9f65bba1b184a3cc2ed89 100644 (file)
@@ -23,6 +23,7 @@
 
 
 #include "sd-netlink.h"
+
 #include "in-addr-util.h"
 
 struct local_address {
index 5f504962919582cf926fbb2c6a1c8af899a11314..52c50751107f32f17969e94f4eb485fd5637ed2d 100644 (file)
@@ -21,8 +21,8 @@
 #define _LIBUDEV_PRIVATE_H_
 
 #include <signal.h>
-#include <stdint.h>
 #include <stdbool.h>
+#include <stdint.h>
 
 #include "libudev.h"
 
index 93e9ed02eb6db461c5168059bbe3af6eab39482c..1f55759798c812d9d633a14787c3bd1863df2638 100644 (file)
@@ -21,8 +21,8 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <sys/types.h>
 #include <stdbool.h>
+#include <sys/types.h>
 
 #include "libudev.h"
 
index e9b424b5f64b949bb93bea032af863b8c76e02c7..63c279cde789e315058945488c046e3fa052ac2f 100644 (file)
@@ -35,8 +35,8 @@ typedef enum HandleAction {
         _HANDLE_ACTION_INVALID = -1
 } HandleAction;
 
-#include "logind.h"
 #include "logind-inhibit.h"
+#include "logind.h"
 
 int manager_handle_action(
                 Manager *m,
index d054c33ceca6b5a671b4352943914d99056ca04a..d27407fc920b003317f4c196f712b6be2ace3ff2 100644 (file)
@@ -25,8 +25,8 @@ typedef struct Session Session;
 typedef enum KillWho KillWho;
 
 #include "list.h"
-#include "logind-user.h"
 #include "login-util.h"
+#include "logind-user.h"
 
 typedef enum SessionState {
         SESSION_OPENING,  /* Session scope is being created */
index dac7a29ed193da0c39d2c7c925d1247341a73339..bc5d4abb80636d52ca96fff19c06982430164f62 100644 (file)
@@ -31,9 +31,9 @@
 
 typedef struct Manager Manager;
 
-#include "machine.h"
-#include "machine-dbus.h"
 #include "image-dbus.h"
+#include "machine-dbus.h"
+#include "machine.h"
 
 struct Manager {
         sd_event *event;
index e6207ccce638a75c08885957c87f7587bddb83f0..7f5bdf1d2f456e2040f9768880c2b71eb6bd58d6 100644 (file)
@@ -23,6 +23,7 @@
 
 typedef struct AddressPool AddressPool;
 
+#include "in-addr-util.h"
 #include "networkd.h"
 
 struct AddressPool {
index 4049a23bdc4a1c923b11b38583b8fc3badb5be86..accd0a027d98724a70521272f848dc4da2820ffe 100644 (file)
@@ -28,9 +28,9 @@
 
 typedef struct Address Address;
 
-#include "networkd.h"
-#include "networkd-network.h"
 #include "networkd-link.h"
+#include "networkd-network.h"
+#include "networkd.h"
 
 #define CACHE_INFO_INFINITY_LIFE_TIME 0xFFFFFFFFU
 
index b9c60a3c77871a765eb6087c83c28ec9cf9c0e79..48e3d840556d588ee45654ee42fa2ca260bb9b67 100644 (file)
@@ -255,6 +255,7 @@ static int dhcp_lease_lost(Link *link) {
         }
 
         link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease);
+        link_dirty(link);
         link->dhcp4_configured = false;
 
         return 0;
@@ -331,6 +332,7 @@ static int dhcp_lease_renew(sd_dhcp_client *client, Link *link) {
         sd_dhcp_lease_unref(link->dhcp_lease);
         link->dhcp4_configured = false;
         link->dhcp_lease = sd_dhcp_lease_ref(lease);
+        link_dirty(link);
 
         r = sd_dhcp_lease_get_address(lease, &address);
         if (r < 0)
@@ -408,6 +410,7 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
                            NULL);
 
         link->dhcp_lease = sd_dhcp_lease_ref(lease);
+        link_dirty(link);
 
         if (link->network->dhcp_mtu) {
                 uint16_t mtu;
index f0efb902d0da5ff187dd395c1d0b1288b4f04598..c8e3f2ce56763b77ff23fcbe49d840844f341863 100644 (file)
@@ -23,8 +23,8 @@
 
 typedef struct FdbEntry FdbEntry;
 
-#include "networkd.h"
 #include "networkd-network.h"
+#include "networkd.h"
 
 struct FdbEntry {
         Network *network;
index 01d5942ce5b6b1bde69f6d353f76bbec8c336a9a..a9d91b07f60a979675ac322aec346dd871043093 100644 (file)
@@ -2040,9 +2040,13 @@ static int link_configure(Link *link) {
         assert(link->network);
         assert(link->state == LINK_STATE_PENDING);
 
-        r = link_drop_foreign_config(link);
-        if (r < 0)
-                return r;
+        /* Drop foreign config, but ignore loopback device.
+         * We do not want to remove loopback address. */
+        if (!(link->flags & IFF_LOOPBACK)) {
+                r = link_drop_foreign_config(link);
+                if (r < 0)
+                        return r;
+        }
 
         r = link_set_bridge_fdb(link);
         if (r < 0)
@@ -2296,7 +2300,8 @@ network_file_fail:
                         if (r < 0) {
                                 log_link_debug_errno(link, r, "Failed to extract next address string: %m");
                                 continue;
-                        } if (r == 0)
+                        }
+                        if (r == 0)
                                 break;
 
                         prefixlen_str = strchr(address_str, '/');
@@ -2326,6 +2331,8 @@ network_file_fail:
         }
 
         if (routes) {
+                p = routes;
+
                 for (;;) {
                         Route *route;
                         _cleanup_free_ char *route_str = NULL;
@@ -2340,7 +2347,8 @@ network_file_fail:
                         if (r < 0) {
                                 log_link_debug_errno(link, r, "Failed to extract next route string: %m");
                                 continue;
-                        } if (r == 0)
+                        }
+                        if (r == 0)
                                 break;
 
                         prefixlen_str = strchr(route_str, '/');
index aa2235b11dc3f273b35a11cb9b099dd39c524927..3964a12f37e6ffd6b0ad5fe9a18df16454f4ceef 100644 (file)
@@ -56,9 +56,9 @@ typedef enum LinkOperationalState {
         _LINK_OPERSTATE_INVALID = -1
 } LinkOperationalState;
 
-#include "networkd.h"
-#include "networkd-network.h"
 #include "networkd-address.h"
+#include "networkd-network.h"
+#include "networkd.h"
 
 struct Link {
         Manager *manager;
index d21f355f5dce4a82618e424507185ad8bc588dcd..16977ea6a9ca78e81c393121e5a112184d2e4663 100644 (file)
@@ -23,9 +23,8 @@
 
 typedef struct VxLan VxLan;
 
-#include "networkd-netdev.h"
-
 #include "in-addr-util.h"
+#include "networkd-netdev.h"
 
 #define VXLAN_VID_MAX (1u << 24) - 1
 
index 3b9ab27b675186ef759aa7551cac78ff70249643..3ab39efd57435a708de604ce811fcf67778b36ad 100644 (file)
@@ -26,8 +26,8 @@
 typedef struct NetDev NetDev;
 typedef struct NetDevVTable NetDevVTable;
 
-#include "networkd.h"
 #include "networkd-link.h"
+#include "networkd.h"
 
 typedef struct netdev_join_callback netdev_join_callback;
 
@@ -103,16 +103,16 @@ struct NetDev {
         LIST_HEAD(netdev_join_callback, callbacks);
 };
 
-#include "networkd-netdev-bridge.h"
 #include "networkd-netdev-bond.h"
-#include "networkd-netdev-vlan.h"
-#include "networkd-netdev-macvlan.h"
+#include "networkd-netdev-bridge.h"
+#include "networkd-netdev-dummy.h"
 #include "networkd-netdev-ipvlan.h"
-#include "networkd-netdev-vxlan.h"
-#include "networkd-netdev-veth.h"
+#include "networkd-netdev-macvlan.h"
 #include "networkd-netdev-tunnel.h"
-#include "networkd-netdev-dummy.h"
 #include "networkd-netdev-tuntap.h"
+#include "networkd-netdev-veth.h"
+#include "networkd-netdev-vlan.h"
+#include "networkd-netdev-vxlan.h"
 
 struct NetDevVTable {
         /* How much memory does an object of this unit type need */
index a27c67eea58e7f3bf998af8daf9c163385d3a5dc..cb3a50d9badb59cbb48e448a41386f02736c0d8c 100644 (file)
 
 typedef struct Network Network;
 
-#include "networkd.h"
-#include "networkd-netdev.h"
 #include "networkd-address.h"
-#include "networkd-route.h"
 #include "networkd-fdb.h"
+#include "networkd-netdev.h"
+#include "networkd-route.h"
 #include "networkd-util.h"
+#include "networkd.h"
 
 #define DHCP_ROUTE_METRIC 1024
 #define IPV4LL_ROUTE_METRIC 2048
index b2767566745c47dc226abe0d67bfb57c03e898b8..37c12907d76f41226a1e9c4504199743de350f65 100644 (file)
@@ -23,8 +23,8 @@
 
 typedef struct Route Route;
 
-#include "networkd.h"
 #include "networkd-network.h"
+#include "networkd.h"
 
 struct Route {
         Network *network;
index 97665fac7aa4e2fc4e108268fef09e44d5d6e85c..8086e528bf6393772f02d438ce2ceb8db5628d1a 100644 (file)
 
 #include <arpa/inet.h>
 
+#include "sd-bus.h"
 #include "sd-event.h"
 #include "sd-netlink.h"
-#include "sd-bus.h"
-#include "udev.h"
 
 #include "hashmap.h"
 #include "list.h"
+#include "udev.h"
 
 typedef struct Manager Manager;
 
-#include "networkd-network.h"
 #include "networkd-address-pool.h"
 #include "networkd-link.h"
+#include "networkd-network.h"
 #include "networkd-util.h"
 
 struct Manager {
index 985fdfaad53425e99f6bceffbd36c1eb6514fe95..4e8db637503c4986bb3112743c11bf9fb75d0d7e 100644 (file)
@@ -21,8 +21,8 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <sys/types.h>
 #include <stdbool.h>
+#include <sys/types.h>
 
 int chown_cgroup(pid_t pid, uid_t uid_shift);
 int sync_cgroup(pid_t pid, bool unified_requested);
index 39cec286955e0e219ef7c9a24b356f1919735320..cb7340bad7972d0c1d30bdc46e1944b145f4ffe9 100644 (file)
@@ -25,8 +25,9 @@
 
 #include "sd-event.h"
 #include "sd-netlink.h"
-#include "list.h"
+
 #include "in-addr-util.h"
+#include "list.h"
 
 typedef struct ExposePort {
         int protocol;
index b86effef47cb14e9b076ae47946227572100da2a..c91fc79c429f57768ec262273fb177d0ffd9fecd 100644 (file)
@@ -22,9 +22,8 @@
 ***/
 
 #include <net/if.h>
-
-#include <sys/types.h>
 #include <stdbool.h>
+#include <sys/types.h>
 
 int setup_veth(const char *machine_name, pid_t pid, char iface_name[IFNAMSIZ], bool bridge);
 int setup_veth_extra(const char *machine_name, pid_t pid, char **pairs);
index dde0d8bd450fa1a1630bd0b612654a7b359e4cd4..10230a5b83f2ca82e4e063c44ea2f217e4f4dc09 100644 (file)
@@ -24,9 +24,8 @@
 #include <stdio.h>
 
 #include "macro.h"
-
-#include "nspawn-mount.h"
 #include "nspawn-expose-ports.h"
+#include "nspawn-mount.h"
 
 typedef enum SettingsMask {
         SETTING_BOOT          = 1 << 0,
index 969fa9619e17454070a4ed6335eeef38f8dc85ac..c98a959b3b6d8af3c2df6a90e60cb9df64c94b74 100644 (file)
@@ -416,6 +416,9 @@ enum nss_status _nss_mymachines_getpwnam_r(
         if (!e || e == p)
                 goto not_found;
 
+        if (e - p > HOST_NAME_MAX - 1) /* -1 for the last dash */
+                goto not_found;
+
         r = parse_uid(e + 1, &uid);
         if (r < 0)
                 goto not_found;
@@ -573,6 +576,9 @@ enum nss_status _nss_mymachines_getgrnam_r(
         if (!e || e == p)
                 goto not_found;
 
+        if (e - p > HOST_NAME_MAX - 1)  /* -1 for the last dash */
+                goto not_found;
+
         r = parse_gid(e + 1, &gid);
         if (r < 0)
                 goto not_found;
index eb4e646846bd081fa493f652baad126b35769353..f68751a2e52b32a27af6e685f0ba5d70af2e65dd 100644 (file)
@@ -28,6 +28,7 @@
 #include "alloc-util.h"
 #include "bus-error.h"
 #include "bus-util.h"
+#include "escape.h"
 #include "in-addr-util.h"
 #include "parse-util.h"
 #include "resolved-def.h"
@@ -41,6 +42,7 @@ static int arg_type = 0;
 static uint16_t arg_class = 0;
 static bool arg_legend = true;
 static uint64_t arg_flags = 0;
+static bool arg_resolve_service = false;
 
 static void print_source(uint64_t flags, usec_t rtt) {
         char rtt_str[FORMAT_TIMESTAMP_MAX];
@@ -102,10 +104,8 @@ static int resolve_host(sd_bus *bus, const char *name) {
         ts = now(CLOCK_MONOTONIC);
 
         r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
-        if (r < 0) {
-                log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r));
-                return r;
-        }
+        if (r < 0)
+                return log_error_errno(r, "%s: resolve call failed: %s", name, bus_error_message(&error, r));
 
         ts = now(CLOCK_MONOTONIC) - ts;
 
@@ -114,10 +114,10 @@ static int resolve_host(sd_bus *bus, const char *name) {
                 return bus_log_parse_error(r);
 
         while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) {
-                const void *a;
-                size_t sz;
                 _cleanup_free_ char *pretty = NULL;
                 int ifindex, family;
+                const void *a;
+                size_t sz;
 
                 assert_cc(sizeof(int) == sizeof(int32_t));
 
@@ -140,7 +140,7 @@ static int resolve_host(sd_bus *bus, const char *name) {
 
                 if (sz != FAMILY_ADDRESS_SIZE(family)) {
                         log_error("%s: systemd-resolved returned address of invalid size %zu for family %s", name, sz, af_to_name(family) ?: "unknown");
-                        continue;
+                        return -EINVAL;
                 }
 
                 ifname[0] = 0;
@@ -437,6 +437,207 @@ static int resolve_record(sd_bus *bus, const char *name) {
         return 0;
 }
 
+static int resolve_service(sd_bus *bus, const char *name, const char *type, const char *domain) {
+        const char *canonical_name, *canonical_type, *canonical_domain;
+        _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
+        _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+        char ifname[IF_NAMESIZE] = "";
+        size_t indent, sz;
+        uint64_t flags;
+        const char *p;
+        unsigned c;
+        usec_t ts;
+        int r;
+
+        assert(bus);
+        assert(domain);
+
+        if (isempty(name))
+                name = NULL;
+        if (isempty(type))
+                type = NULL;
+
+        if (arg_ifindex > 0 && !if_indextoname(arg_ifindex, ifname))
+                return log_error_errno(errno, "Failed to resolve interface name for index %i: %m", arg_ifindex);
+
+        if (name)
+                log_debug("Resolving service \"%s\" of type %s in %s (family %s, interface %s).", name, type, domain, af_to_name(arg_family) ?: "*", isempty(ifname) ? "*" : ifname);
+        else if (type)
+                log_debug("Resolving service type %s of %s (family %s, interface %s).", type, domain, af_to_name(arg_family) ?: "*", isempty(ifname) ? "*" : ifname);
+        else
+                log_debug("Resolving service type %s (family %s, interface %s).", domain, af_to_name(arg_family) ?: "*", isempty(ifname) ? "*" : ifname);
+
+        r = sd_bus_message_new_method_call(
+                        bus,
+                        &req,
+                        "org.freedesktop.resolve1",
+                        "/org/freedesktop/resolve1",
+                        "org.freedesktop.resolve1.Manager",
+                        "ResolveService");
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_append(req, "isssit", arg_ifindex, name, type, domain, arg_family, arg_flags);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        ts = now(CLOCK_MONOTONIC);
+
+        r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
+        if (r < 0)
+                return log_error_errno(r, "Resolve call failed: %s", bus_error_message(&error, r));
+
+        ts = now(CLOCK_MONOTONIC) - ts;
+
+        r = sd_bus_message_enter_container(reply, 'a', "(qqqsa(iiay)s)");
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        indent =
+                (name ? strlen(name) + 1 : 0) +
+                (type ? strlen(type) + 1 : 0) +
+                strlen(domain) + 2;
+
+        c = 0;
+        while ((r = sd_bus_message_enter_container(reply, 'r', "qqqsa(iiay)s")) > 0) {
+                uint16_t priority, weight, port;
+                const char *hostname, *canonical;
+
+                r = sd_bus_message_read(reply, "qqqs", &priority, &weight, &port, &hostname);
+                if (r < 0)
+                        return bus_log_parse_error(r);
+
+                if (name)
+                        printf("%*s%s", (int) strlen(name), c == 0 ? name : "", c == 0 ? "/" : " ");
+                if (type)
+                        printf("%*s%s", (int) strlen(type), c == 0 ? type : "", c == 0 ? "/" : " ");
+
+                printf("%*s%s %s:%u [priority=%u, weight=%u]\n",
+                       (int) strlen(domain), c == 0 ? domain : "",
+                       c == 0 ? ":" : " ",
+                       hostname, port,
+                       priority, weight);
+
+                r = sd_bus_message_enter_container(reply, 'a', "(iiay)");
+                if (r < 0)
+                        return bus_log_parse_error(r);
+
+                while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) {
+                        _cleanup_free_ char *pretty = NULL;
+                        int ifindex, family;
+                        const void *a;
+
+                        assert_cc(sizeof(int) == sizeof(int32_t));
+
+                        r = sd_bus_message_read(reply, "ii", &ifindex, &family);
+                        if (r < 0)
+                                return bus_log_parse_error(r);
+
+                        r = sd_bus_message_read_array(reply, 'y', &a, &sz);
+                        if (r < 0)
+                                return bus_log_parse_error(r);
+
+                        r = sd_bus_message_exit_container(reply);
+                        if (r < 0)
+                                return bus_log_parse_error(r);
+
+                        if (!IN_SET(family, AF_INET, AF_INET6)) {
+                                log_debug("%s: skipping entry with family %d (%s)", name, family, af_to_name(family) ?: "unknown");
+                                continue;
+                        }
+
+                        if (sz != FAMILY_ADDRESS_SIZE(family)) {
+                                log_error("%s: systemd-resolved returned address of invalid size %zu for family %s", name, sz, af_to_name(family) ?: "unknown");
+                                return -EINVAL;
+                        }
+
+                        ifname[0] = 0;
+                        if (ifindex > 0 && !if_indextoname(ifindex, ifname))
+                                log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex);
+
+                        r = in_addr_to_string(family, a, &pretty);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to print address for %s: %m", name);
+
+                        printf("%*s%s%s%s\n", (int) indent, "", pretty, isempty(ifname) ? "" : "%s", ifname);
+                }
+                if (r < 0)
+                        return bus_log_parse_error(r);
+
+                r = sd_bus_message_exit_container(reply);
+                if (r < 0)
+                        return bus_log_parse_error(r);
+
+                r = sd_bus_message_read(reply, "s", &canonical);
+                if (r < 0)
+                        return bus_log_parse_error(r);
+
+                if (!streq(hostname, canonical))
+                        printf("%*s(%s)\n", (int) indent, "", canonical);
+
+                r = sd_bus_message_exit_container(reply);
+                if (r < 0)
+                        return bus_log_parse_error(r);
+
+                c++;
+        }
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        r = sd_bus_message_exit_container(reply);
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        r = sd_bus_message_enter_container(reply, 'a', "ay");
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        c = 0;
+        while ((r = sd_bus_message_read_array(reply, 'y', (const void**) &p, &sz)) > 0) {
+                _cleanup_free_ char *escaped = NULL;
+
+                escaped = cescape_length(p, sz);
+                if (!escaped)
+                        return log_oom();
+
+                printf("%*s%s\n", (int) indent, "", escaped);
+                c++;
+        }
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        r = sd_bus_message_exit_container(reply);
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        r = sd_bus_message_read(reply, "ssst", &canonical_name, &canonical_type, &canonical_domain, &flags);
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        if (isempty(canonical_name))
+                canonical_name = NULL;
+        if (isempty(canonical_type))
+                canonical_type = NULL;
+
+        if (!streq_ptr(name, canonical_name) ||
+            !streq_ptr(type, canonical_type) ||
+            !streq_ptr(domain, canonical_domain)) {
+
+                printf("%*s(", (int) indent, "");
+
+                if (canonical_name)
+                        printf("%s/", canonical_name);
+                if (canonical_type)
+                        printf("%s/", canonical_type);
+
+                printf("%s)\n", canonical_domain);
+        }
+
+        print_source(flags, ts);
+
+        return 0;
+}
+
 static void help_dns_types(void) {
         int i;
         const char *t;
@@ -464,33 +665,49 @@ static void help_dns_classes(void) {
 }
 
 static void help(void) {
-        printf("%s [OPTIONS...]\n\n"
-               "Resolve IPv4 or IPv6 addresses.\n\n"
-               "  -h --help               Show this help\n"
-               "     --version            Show package version\n"
-               "  -4                      Resolve IPv4 addresses\n"
-               "  -6                      Resolve IPv6 addresses\n"
-               "  -i INTERFACE            Look on interface\n"
-               "  -p --protocol=PROTOCOL  Look via protocol\n"
-               "  -t --type=TYPE          Query RR with DNS type\n"
-               "  -c --class=CLASS        Query RR with DNS class\n"
-               "     --legend[=BOOL]      Do [not] print column headers\n"
-               , program_invocation_short_name);
+        printf("%s [OPTIONS...] NAME...\n"
+               "%s [OPTIONS...] --service [[NAME] TYPE] DOMAIN\n\n"
+               "Resolve domain names, IPv4 or IPv6 addresses, resource records, and services.\n\n"
+               "  -h --help                 Show this help\n"
+               "     --version              Show package version\n"
+               "  -4                        Resolve IPv4 addresses\n"
+               "  -6                        Resolve IPv6 addresses\n"
+               "  -i INTERFACE              Look on interface\n"
+               "  -p --protocol=PROTOCOL    Look via protocol\n"
+               "  -t --type=TYPE            Query RR with DNS type\n"
+               "  -c --class=CLASS          Query RR with DNS class\n"
+               "     --service              Resolve service (SRV)\n"
+               "     --service-address=BOOL Do [not] resolve address for services\n"
+               "     --service-txt=BOOL     Do [not] resolve TXT records for services\n"
+               "     --cname=BOOL           Do [not] follow CNAME redirects\n"
+               "     --search=BOOL          Do [not] use search domains\n"
+               "     --legend=BOOL          Do [not] print column headers\n"
+               , program_invocation_short_name, program_invocation_short_name);
 }
 
 static int parse_argv(int argc, char *argv[]) {
         enum {
                 ARG_VERSION = 0x100,
                 ARG_LEGEND,
+                ARG_SERVICE,
+                ARG_CNAME,
+                ARG_SERVICE_ADDRESS,
+                ARG_SERVICE_TXT,
+                ARG_SEARCH,
         };
 
         static const struct option options[] = {
-                { "help",      no_argument,       NULL, 'h'           },
-                { "version",   no_argument,       NULL, ARG_VERSION   },
-                { "type",      required_argument, NULL, 't'           },
-                { "class",     required_argument, NULL, 'c'           },
-                { "legend",    optional_argument, NULL, ARG_LEGEND    },
-                { "protocol",  required_argument, NULL, 'p'           },
+                { "help",            no_argument,       NULL, 'h'                 },
+                { "version",         no_argument,       NULL, ARG_VERSION         },
+                { "type",            required_argument, NULL, 't'                 },
+                { "class",           required_argument, NULL, 'c'                 },
+                { "legend",          required_argument, NULL, ARG_LEGEND          },
+                { "protocol",        required_argument, NULL, 'p'                 },
+                { "cname",           required_argument, NULL, ARG_CNAME           },
+                { "service",         no_argument,       NULL, ARG_SERVICE         },
+                { "service-address", required_argument, NULL, ARG_SERVICE_ADDRESS },
+                { "service-txt",     required_argument, NULL, ARG_SERVICE_TXT     },
+                { "search",          required_argument, NULL, ARG_SEARCH          },
                 {}
         };
 
@@ -563,16 +780,11 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_LEGEND:
-                        if (optarg) {
-                                r = parse_boolean(optarg);
-                                if (r < 0) {
-                                        log_error("Failed to parse --legend= argument");
-                                        return r;
-                                }
-
-                                arg_legend = !!r;
-                        } else
-                                arg_legend = false;
+                        r = parse_boolean(optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse --legend= argument");
+
+                        arg_legend = r;
                         break;
 
                 case 'p':
@@ -591,6 +803,50 @@ static int parse_argv(int argc, char *argv[]) {
 
                         break;
 
+                case ARG_SERVICE:
+                        arg_resolve_service = true;
+                        break;
+
+                case ARG_CNAME:
+                        r = parse_boolean(optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse --cname= argument.");
+                        if (r == 0)
+                                arg_flags |= SD_RESOLVED_NO_CNAME;
+                        else
+                                arg_flags &= ~SD_RESOLVED_NO_CNAME;
+                        break;
+
+                case ARG_SERVICE_ADDRESS:
+                        r = parse_boolean(optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse --service-address= argument.");
+                        if (r == 0)
+                                arg_flags |= SD_RESOLVED_NO_ADDRESS;
+                        else
+                                arg_flags &= ~SD_RESOLVED_NO_ADDRESS;
+                        break;
+
+                case ARG_SERVICE_TXT:
+                        r = parse_boolean(optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse --service-txt= argument.");
+                        if (r == 0)
+                                arg_flags |= SD_RESOLVED_NO_TXT;
+                        else
+                                arg_flags &= ~SD_RESOLVED_NO_TXT;
+                        break;
+
+                case ARG_SEARCH:
+                        r = parse_boolean(optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse --search argument.");
+                        if (r == 0)
+                                arg_flags |= SD_RESOLVED_NO_SEARCH;
+                        else
+                                arg_flags &= ~SD_RESOLVED_NO_SEARCH;
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -599,7 +855,12 @@ static int parse_argv(int argc, char *argv[]) {
                 }
 
         if (arg_type == 0 && arg_class != 0) {
-                log_error("--class= may only be used in conjunction with --type=");
+                log_error("--class= may only be used in conjunction with --type=.");
+                return -EINVAL;
+        }
+
+        if (arg_type != 0 && arg_resolve_service) {
+                log_error("--service and --type= may not be combined.");
                 return -EINVAL;
         }
 
@@ -632,6 +893,28 @@ int main(int argc, char **argv) {
                 goto finish;
         }
 
+        if (arg_resolve_service) {
+
+                if (argc < optind + 1) {
+                        log_error("Domain specification required.");
+                        r = -EINVAL;
+                        goto finish;
+
+                } else if (argc == optind + 1)
+                        r = resolve_service(bus, NULL, NULL, argv[optind]);
+                else if (argc == optind + 2)
+                        r = resolve_service(bus, NULL, argv[optind], argv[optind+1]);
+                else if (argc == optind + 3)
+                        r = resolve_service(bus, argv[optind], argv[optind+1], argv[optind+2]);
+                else {
+                        log_error("Too many arguments");
+                        r = -EINVAL;
+                        goto finish;
+                }
+
+                goto finish;
+        }
+
         while (argv[optind]) {
                 int family, ifindex, k;
                 union in_addr_union a;
index f0a3b607d4a85d7716e7a088b4f31bcde7098059..62bb08a2e8a8b5997b42cb99d33782ff3199aee1 100644 (file)
@@ -31,15 +31,14 @@ static int reply_query_state(DnsQuery *q) {
         const char *name;
         int r;
 
-        if (q->request_hostname)
-                name = q->request_hostname;
-        else {
+        if (q->request_address_valid) {
                 r = in_addr_to_string(q->request_family, &q->request_address, &ip);
                 if (r < 0)
                         return r;
 
                 name = ip;
-        }
+        } else
+                name = dns_question_first_name(q->question);
 
         switch (q->state) {
 
@@ -132,10 +131,9 @@ static int append_address(sd_bus_message *reply, DnsResourceRecord *rr, int ifin
 }
 
 static void bus_method_resolve_hostname_complete(DnsQuery *q) {
-        _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *cname = NULL, *canonical = NULL;
+        _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *canonical = NULL;
         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
-        _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
-        unsigned added = 0, i;
+        unsigned added = 0;
         int r;
 
         assert(q);
@@ -145,6 +143,16 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) {
                 goto finish;
         }
 
+        r = dns_query_process_cname(q);
+        if (r == -ELOOP) {
+                r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_question_first_name(q->question));
+                goto finish;
+        }
+        if (r < 0)
+                goto finish;
+        if (r > 0) /* This was a cname, and the query was restarted. */
+                return;
+
         r = sd_bus_message_new_method_return(q->request, &reply);
         if (r < 0)
                 goto finish;
@@ -154,92 +162,42 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) {
                 goto finish;
 
         if (q->answer) {
-                answer = dns_answer_ref(q->answer);
+                DnsResourceRecord *rr;
+                int ifindex;
 
-                for (i = 0; i < answer->n_rrs; i++) {
-                        r = dns_question_matches_rr(q->question, answer->items[i].rr);
+                DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
+                        r = dns_question_matches_rr(q->question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
                         if (r < 0)
                                 goto finish;
-                        if (r == 0) {
-                                /* Hmm, if this is not an address record,
-                                   maybe it's a cname? If so, remember this */
-                                r = dns_question_matches_cname(q->question, answer->items[i].rr);
-                                if (r < 0)
-                                        goto finish;
-                                if (r > 0)
-                                        cname = dns_resource_record_ref(answer->items[i].rr);
-
+                        if (r == 0)
                                 continue;
-                        }
 
-                        r = append_address(reply, answer->items[i].rr, answer->items[i].ifindex);
+                        r = append_address(reply, rr, ifindex);
                         if (r < 0)
                                 goto finish;
 
                         if (!canonical)
-                                canonical = dns_resource_record_ref(answer->items[i].rr);
+                                canonical = dns_resource_record_ref(rr);
 
                         added ++;
                 }
         }
 
-        if (added == 0) {
-                if (!cname) {
-                        r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of requested type", q->request_hostname);
-                        goto finish;
-                }
-
-                /* This has a cname? Then update the query with the
-                 * new cname. */
-                r = dns_query_cname_redirect(q, cname);
-                if (r < 0) {
-                        if (r == -ELOOP)
-                                r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop on '%s'", q->request_hostname);
-                        else
-                                r = sd_bus_reply_method_errno(q->request, -r, NULL);
-
-                        goto finish;
-                }
-
-                /* Before we restart the query, let's see if any of
-                 * the RRs we already got already answers our query */
-                for (i = 0; i < answer->n_rrs; i++) {
-                        r = dns_question_matches_rr(q->question, answer->items[i].rr);
-                        if (r < 0)
-                                goto finish;
-                        if (r == 0)
-                                continue;
-
-                        r = append_address(reply, answer->items[i].rr, answer->items[i].ifindex);
-                        if (r < 0)
-                                goto finish;
-
-                        if (!canonical)
-                                canonical = dns_resource_record_ref(answer->items[i].rr);
-
-                        added++;
-                }
-
-                /* If we didn't find anything, then let's restart the
-                 * query, this time with the cname */
-                if (added <= 0) {
-                        r = dns_query_go(q);
-                        if (r < 0) {
-                                r = sd_bus_reply_method_errno(q->request, -r, NULL);
-                                goto finish;
-                        }
-
-                        return;
-                }
+        if (added <= 0) {
+                r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_question_first_name(q->question));
+                goto finish;
         }
 
         r = sd_bus_message_close_container(reply);
         if (r < 0)
                 goto finish;
 
-        /* Return the precise spelling and uppercasing reported by the server */
+        /* Return the precise spelling and uppercasing and CNAME target reported by the server */
         assert(canonical);
-        r = sd_bus_message_append(reply, "st", DNS_RESOURCE_KEY_NAME(canonical->key), SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family));
+        r = sd_bus_message_append(
+                        reply, "st",
+                        DNS_RESOURCE_KEY_NAME(canonical->key),
+                        SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family));
         if (r < 0)
                 goto finish;
 
@@ -248,23 +206,23 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) {
 finish:
         if (r < 0) {
                 log_error_errno(r, "Failed to send hostname reply: %m");
-                sd_bus_reply_method_errno(q->request, -r, NULL);
+                sd_bus_reply_method_errno(q->request, r, NULL);
         }
 
         dns_query_free(q);
 }
 
-static int check_ifindex_flags(int ifindex, uint64_t *flags, sd_bus_error *error) {
+static int check_ifindex_flags(int ifindex, uint64_t *flags, uint64_t ok, sd_bus_error *error) {
         assert(flags);
 
         if (ifindex < 0)
                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
 
-        if (*flags & ~SD_RESOLVED_FLAGS_ALL)
+        if (*flags & ~(SD_RESOLVED_PROTOCOLS_ALL|SD_RESOLVED_NO_CNAME|ok))
                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid flags parameter");
 
-        if (*flags == 0)
-                *flags = SD_RESOLVED_FLAGS_DEFAULT;
+        if ((*flags & SD_RESOLVED_PROTOCOLS_ALL) == 0) /* If no protocol is enabled, enable all */
+                *flags |= SD_RESOLVED_PROTOCOLS_ALL;
 
         return 0;
 }
@@ -281,6 +239,8 @@ static int bus_method_resolve_hostname(sd_bus_message *message, void *userdata,
         assert(message);
         assert(m);
 
+        assert_cc(sizeof(int) == sizeof(int32_t));
+
         r = sd_bus_message_read(message, "isit", &ifindex, &hostname, &family, &flags);
         if (r < 0)
                 return r;
@@ -288,41 +248,19 @@ static int bus_method_resolve_hostname(sd_bus_message *message, void *userdata,
         if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
 
-        r = dns_name_normalize(hostname, NULL);
+        r = dns_name_is_valid(hostname);
         if (r < 0)
+                return r;
+        if (r == 0)
                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", hostname);
 
-        r = check_ifindex_flags(ifindex, &flags, error);
+        r = check_ifindex_flags(ifindex, &flags, SD_RESOLVED_NO_SEARCH, error);
         if (r < 0)
                 return r;
 
-        question = dns_question_new(family == AF_UNSPEC ? 2 : 1);
-        if (!question)
-                return -ENOMEM;
-
-        if (family != AF_INET6) {
-                _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
-
-                key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, hostname);
-                if (!key)
-                        return -ENOMEM;
-
-                r = dns_question_add(question, key);
-                if (r < 0)
-                        return r;
-        }
-
-        if (family != AF_INET) {
-                _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
-
-                key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, hostname);
-                if (!key)
-                        return -ENOMEM;
-
-                r = dns_question_add(question, key);
-                if (r < 0)
-                        return r;
-        }
+        r = dns_question_new_address(&question, family, hostname);
+        if (r < 0)
+                return r;
 
         r = dns_query_new(m, &q, question, ifindex, flags);
         if (r < 0)
@@ -330,27 +268,28 @@ static int bus_method_resolve_hostname(sd_bus_message *message, void *userdata,
 
         q->request = sd_bus_message_ref(message);
         q->request_family = family;
-        q->request_hostname = hostname;
         q->complete = bus_method_resolve_hostname_complete;
 
         r = dns_query_bus_track(q, message);
         if (r < 0)
-                return r;
+                goto fail;
 
         r = dns_query_go(q);
-        if (r < 0) {
-                dns_query_free(q);
-                return r;
-        }
+        if (r < 0)
+                goto fail;
 
         return 1;
+
+fail:
+        dns_query_free(q);
+        return r;
 }
 
 static void bus_method_resolve_address_complete(DnsQuery *q) {
         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
-        _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
-        unsigned added = 0, i;
-        int r;
+        DnsResourceRecord *rr;
+        unsigned added = 0;
+        int ifindex, r;
 
         assert(q);
 
@@ -359,6 +298,16 @@ static void bus_method_resolve_address_complete(DnsQuery *q) {
                 goto finish;
         }
 
+        r = dns_query_process_cname(q);
+        if (r == -ELOOP) {
+                r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_question_first_name(q->question));
+                goto finish;
+        }
+        if (r < 0)
+                goto finish;
+        if (r > 0) /* This was a cname, and the query was restarted. */
+                return;
+
         r = sd_bus_message_new_method_return(q->request, &reply);
         if (r < 0)
                 goto finish;
@@ -368,16 +317,14 @@ static void bus_method_resolve_address_complete(DnsQuery *q) {
                 goto finish;
 
         if (q->answer) {
-                answer = dns_answer_ref(q->answer);
-
-                for (i = 0; i < answer->n_rrs; i++) {
-                        r = dns_question_matches_rr(q->question, answer->items[i].rr);
+                DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
+                        r = dns_question_matches_rr(q->question, rr, NULL);
                         if (r < 0)
                                 goto finish;
                         if (r == 0)
                                 continue;
 
-                        r = sd_bus_message_append(reply, "(is)", answer->items[i].ifindex, answer->items[i].rr->ptr.name);
+                        r = sd_bus_message_append(reply, "(is)", ifindex, rr->ptr.name);
                         if (r < 0)
                                 goto finish;
 
@@ -385,12 +332,11 @@ static void bus_method_resolve_address_complete(DnsQuery *q) {
                 }
         }
 
-        if (added == 0) {
+        if (added <= 0) {
                 _cleanup_free_ char *ip = NULL;
 
                 in_addr_to_string(q->request_family, &q->request_address, &ip);
-
-                r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Address '%s' does not have any RR of requested type", ip);
+                r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Address '%s' does not have any RR of requested type", strna(ip));
                 goto finish;
         }
 
@@ -407,16 +353,14 @@ static void bus_method_resolve_address_complete(DnsQuery *q) {
 finish:
         if (r < 0) {
                 log_error_errno(r, "Failed to send address reply: %m");
-                sd_bus_reply_method_errno(q->request, -r, NULL);
+                sd_bus_reply_method_errno(q->request, r, NULL);
         }
 
         dns_query_free(q);
 }
 
 static int bus_method_resolve_address(sd_bus_message *message, void *userdata, sd_bus_error *error) {
-        _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
         _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
-        _cleanup_free_ char *reverse = NULL;
         Manager *m = userdata;
         int family, ifindex;
         uint64_t flags;
@@ -428,6 +372,8 @@ static int bus_method_resolve_address(sd_bus_message *message, void *userdata, s
         assert(message);
         assert(m);
 
+        assert_cc(sizeof(int) == sizeof(int32_t));
+
         r = sd_bus_message_read(message, "ii", &ifindex, &family);
         if (r < 0)
                 return r;
@@ -446,54 +392,77 @@ static int bus_method_resolve_address(sd_bus_message *message, void *userdata, s
         if (r < 0)
                 return r;
 
-        r = check_ifindex_flags(ifindex, &flags, error);
+        r = check_ifindex_flags(ifindex, &flags, 0, error);
         if (r < 0)
                 return r;
 
-        r = dns_name_reverse(family, d, &reverse);
+        r = dns_question_new_reverse(&question, family, d);
         if (r < 0)
                 return r;
 
-        question = dns_question_new(1);
-        if (!question)
-                return -ENOMEM;
+        r = dns_query_new(m, &q, question, ifindex, flags|SD_RESOLVED_NO_SEARCH);
+        if (r < 0)
+                return r;
 
-        key = dns_resource_key_new_consume(DNS_CLASS_IN, DNS_TYPE_PTR, reverse);
-        if (!key)
-                return -ENOMEM;
+        q->request = sd_bus_message_ref(message);
+        q->request_family = family;
+        memcpy(&q->request_address, d, sz);
+        q->complete = bus_method_resolve_address_complete;
+
+        r = dns_query_bus_track(q, message);
+        if (r < 0)
+                goto fail;
 
-        reverse = NULL;
+        r = dns_query_go(q);
+        if (r < 0)
+                goto fail;
 
-        r = dns_question_add(question, key);
+        return 1;
+
+fail:
+        dns_query_free(q);
+        return r;
+}
+
+static int bus_message_append_rr(sd_bus_message *m, DnsResourceRecord *rr, int ifindex) {
+        _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
+        size_t start;
+        int r;
+
+        assert(m);
+        assert(rr);
+
+        r = sd_bus_message_open_container(m, 'r', "iqqay");
         if (r < 0)
                 return r;
 
-        r = dns_query_new(m, &q, question, ifindex, flags);
+        r = sd_bus_message_append(m, "iqq",
+                                  ifindex,
+                                  rr->key->class,
+                                  rr->key->type);
         if (r < 0)
                 return r;
 
-        q->request = sd_bus_message_ref(message);
-        q->request_family = family;
-        memcpy(&q->request_address, d, sz);
-        q->complete = bus_method_resolve_address_complete;
+        r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0);
+        if (r < 0)
+                return r;
 
-        r = dns_query_bus_track(q, message);
+        p->refuse_compression = true;
+
+        r = dns_packet_append_rr(p, rr, &start);
         if (r < 0)
                 return r;
 
-        r = dns_query_go(q);
-        if (r < 0) {
-                dns_query_free(q);
+        r = sd_bus_message_append_array(m, 'y', DNS_PACKET_DATA(p) + start, p->size - start);
+        if (r < 0)
                 return r;
-        }
 
-        return 1;
+        return sd_bus_message_close_container(m);
 }
 
 static void bus_method_resolve_record_complete(DnsQuery *q) {
         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
-        _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
-        unsigned added = 0, i;
+        unsigned added = 0;
         int r;
 
         assert(q);
@@ -503,6 +472,16 @@ static void bus_method_resolve_record_complete(DnsQuery *q) {
                 goto finish;
         }
 
+        r = dns_query_process_cname(q);
+        if (r == -ELOOP) {
+                r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_question_first_name(q->question));
+                goto finish;
+        }
+        if (r < 0)
+                goto finish;
+        if (r > 0) /* Following a CNAME */
+                return;
+
         r = sd_bus_message_new_method_return(q->request, &reply);
         if (r < 0)
                 goto finish;
@@ -512,44 +491,17 @@ static void bus_method_resolve_record_complete(DnsQuery *q) {
                 goto finish;
 
         if (q->answer) {
-                answer = dns_answer_ref(q->answer);
-
-                for (i = 0; i < answer->n_rrs; i++) {
-                        _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
-                        size_t start;
+                DnsResourceRecord *rr;
+                int ifindex;
 
-                        r = dns_question_matches_rr(q->question, answer->items[i].rr);
+                DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
+                        r = dns_question_matches_rr(q->question, rr, NULL);
                         if (r < 0)
                                 goto finish;
                         if (r == 0)
                                 continue;
 
-                        r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0);
-                        if (r < 0)
-                                goto finish;
-
-                        p->refuse_compression = true;
-
-                        r = dns_packet_append_rr(p, answer->items[i].rr, &start);
-                        if (r < 0)
-                                goto finish;
-
-                        r = sd_bus_message_open_container(reply, 'r', "iqqay");
-                        if (r < 0)
-                                goto finish;
-
-                        r = sd_bus_message_append(reply, "iqq",
-                                                  answer->items[i].ifindex,
-                                                  answer->items[i].rr->key->class,
-                                                  answer->items[i].rr->key->type);
-                        if (r < 0)
-                                goto finish;
-
-                        r = sd_bus_message_append_array(reply, 'y', DNS_PACKET_DATA(p) + start, p->size - start);
-                        if (r < 0)
-                                goto finish;
-
-                        r = sd_bus_message_close_container(reply);
+                        r = bus_message_append_rr(reply, rr, ifindex);
                         if (r < 0)
                                 goto finish;
 
@@ -558,7 +510,7 @@ static void bus_method_resolve_record_complete(DnsQuery *q) {
         }
 
         if (added <= 0) {
-                r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Name '%s' does not have any RR of the requested type", q->request_hostname);
+                r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Name '%s' does not have any RR of the requested type", dns_question_first_name(q->question));
                 goto finish;
         }
 
@@ -575,7 +527,7 @@ static void bus_method_resolve_record_complete(DnsQuery *q) {
 finish:
         if (r < 0) {
                 log_error_errno(r, "Failed to send record reply: %m");
-                sd_bus_reply_method_errno(q->request, -r, NULL);
+                sd_bus_reply_method_errno(q->request, r, NULL);
         }
 
         dns_query_free(q);
@@ -594,15 +546,19 @@ static int bus_method_resolve_record(sd_bus_message *message, void *userdata, sd
         assert(message);
         assert(m);
 
+        assert_cc(sizeof(int) == sizeof(int32_t));
+
         r = sd_bus_message_read(message, "isqqt", &ifindex, &name, &class, &type, &flags);
         if (r < 0)
                 return r;
 
-        r = dns_name_normalize(name, NULL);
+        r = dns_name_is_valid(name);
         if (r < 0)
+                return r;
+        if (r == 0)
                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid name '%s'", name);
 
-        r = check_ifindex_flags(ifindex, &flags, error);
+        r = check_ifindex_flags(ifindex, &flags, 0, error);
         if (r < 0)
                 return r;
 
@@ -618,32 +574,657 @@ static int bus_method_resolve_record(sd_bus_message *message, void *userdata, sd
         if (r < 0)
                 return r;
 
-        r = dns_query_new(m, &q, question, ifindex, flags);
+        r = dns_query_new(m, &q, question, ifindex, flags|SD_RESOLVED_NO_SEARCH);
         if (r < 0)
                 return r;
 
         q->request = sd_bus_message_ref(message);
-        q->request_hostname = name;
         q->complete = bus_method_resolve_record_complete;
 
         r = dns_query_bus_track(q, message);
         if (r < 0)
-                return r;
+                goto fail;
 
         r = dns_query_go(q);
+        if (r < 0)
+                goto fail;
+
+        return 1;
+
+fail:
+        dns_query_free(q);
+        return r;
+}
+
+static int append_srv(DnsQuery *q, sd_bus_message *reply, DnsResourceRecord *rr) {
+        _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *canonical = NULL;
+        DnsQuery *aux;
+        int r;
+
+        assert(q);
+        assert(reply);
+        assert(rr);
+        assert(rr->key);
+
+        if (rr->key->type != DNS_TYPE_SRV)
+                return 0;
+
+        if ((q->flags & SD_RESOLVED_NO_ADDRESS) == 0) {
+                /* First, let's see if we could find an appropriate A or AAAA
+                 * record for the SRV record */
+                LIST_FOREACH(auxiliary_queries, aux, q->auxiliary_queries) {
+                        DnsResourceRecord *zz;
+
+                        if (aux->state != DNS_TRANSACTION_SUCCESS)
+                                continue;
+                        if (aux->auxiliary_result != 0)
+                                continue;
+
+                        r = dns_name_equal(dns_question_first_name(aux->question), rr->srv.name);
+                        if (r < 0)
+                                return r;
+                        if (r == 0)
+                                continue;
+
+                        DNS_ANSWER_FOREACH(zz, aux->answer) {
+
+                                r = dns_question_matches_rr(aux->question, zz, NULL);
+                                if (r < 0)
+                                        return r;
+                                if (r == 0)
+                                        continue;
+
+                                canonical = dns_resource_record_ref(zz);
+                                break;
+                        }
+
+                        if (canonical)
+                                break;
+                }
+
+                /* Is there are successful A/AAAA lookup for this SRV RR? If not, don't add it */
+                if (!canonical)
+                        return 0;
+        }
+
+        r = sd_bus_message_open_container(reply, 'r', "qqqsa(iiay)s");
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_append(
+                        reply,
+                        "qqqs",
+                        rr->srv.priority, rr->srv.weight, rr->srv.port, rr->srv.name);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_open_container(reply, 'a', "(iiay)");
+        if (r < 0)
+                return r;
+
+        if ((q->flags & SD_RESOLVED_NO_ADDRESS) == 0) {
+                LIST_FOREACH(auxiliary_queries, aux, q->auxiliary_queries) {
+                        DnsResourceRecord *zz;
+                        int ifindex;
+
+                        if (aux->state != DNS_TRANSACTION_SUCCESS)
+                                continue;
+                        if (aux->auxiliary_result != 0)
+                                continue;
+
+                        r = dns_name_equal(dns_question_first_name(aux->question), rr->srv.name);
+                        if (r < 0)
+                                return r;
+                        if (r == 0)
+                                continue;
+
+                        DNS_ANSWER_FOREACH_IFINDEX(zz, ifindex, aux->answer) {
+
+                                r = dns_question_matches_rr(aux->question, zz, NULL);
+                                if (r < 0)
+                                        return r;
+                                if (r == 0)
+                                        continue;
+
+                                r = append_address(reply, zz, ifindex);
+                                if (r < 0)
+                                        return r;
+                        }
+                }
+        }
+
+        r = sd_bus_message_close_container(reply);
+        if (r < 0)
+                return r;
+
+        /* Note that above we appended the hostname as encoded in the
+         * SRV, and here the canonical hostname this maps to. */
+        r = sd_bus_message_append(reply, "s", canonical ? DNS_RESOURCE_KEY_NAME(canonical->key) : rr->srv.name);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_close_container(reply);
+        if (r < 0)
+                return r;
+
+        return 1;
+}
+
+static int append_txt(sd_bus_message *reply, DnsResourceRecord *rr) {
+        DnsTxtItem *i;
+        int r;
+
+        assert(reply);
+        assert(rr);
+        assert(rr->key);
+
+        if (rr->key->type != DNS_TYPE_TXT)
+                return 0;
+
+        LIST_FOREACH(items, i, rr->txt.items) {
+
+                if (i->length <= 0)
+                        continue;
+
+                r = sd_bus_message_append_array(reply, 'y', i->data, i->length);
+                if (r < 0)
+                        return r;
+        }
+
+        return 1;
+}
+
+static void resolve_service_all_complete(DnsQuery *q) {
+        _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *canonical = NULL;
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+        _cleanup_free_ char *name = NULL, *type = NULL, *domain = NULL;
+        DnsQuery *aux;
+        unsigned added = false;
+        int r;
+
+        assert(q);
+
+        if (q->block_all_complete > 0)
+                return;
+
+        if ((q->flags & SD_RESOLVED_NO_ADDRESS) == 0) {
+                DnsQuery *bad = NULL;
+                bool have_success = false;
+
+                LIST_FOREACH(auxiliary_queries, aux, q->auxiliary_queries) {
+
+                        switch (aux->state) {
+
+                        case DNS_TRANSACTION_PENDING:
+                                /* If an auxiliary query is still pending, let's wait */
+                                return;
+
+                        case DNS_TRANSACTION_SUCCESS:
+                                if (aux->auxiliary_result == 0)
+                                        have_success = true;
+                                else
+                                        bad = aux;
+                                break;
+
+                        default:
+                                bad = aux;
+                                break;
+                        }
+                }
+
+                if (!have_success) {
+                        /* We can only return one error, hence pick the last error we encountered */
+
+                        assert(bad);
+
+                        if (bad->state == DNS_TRANSACTION_SUCCESS) {
+                                assert(bad->auxiliary_result != 0);
+
+                                if (bad->auxiliary_result == -ELOOP) {
+                                        r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_question_first_name(bad->question));
+                                        goto finish;
+                                }
+
+                                r = bad->auxiliary_result;
+                                goto finish;
+                        }
+
+                        r = reply_query_state(bad);
+                        goto finish;
+                }
+        }
+
+        r = sd_bus_message_new_method_return(q->request, &reply);
+        if (r < 0)
+                goto finish;
+
+        r = sd_bus_message_open_container(reply, 'a', "(qqqsa(iiay)s)");
+        if (r < 0)
+                goto finish;
+
+        if (q->answer) {
+                DnsResourceRecord *rr;
+
+                DNS_ANSWER_FOREACH(rr, q->answer) {
+                        r = dns_question_matches_rr(q->question, rr, NULL);
+                        if (r < 0)
+                                goto finish;
+                        if (r == 0)
+                                continue;
+
+                        r = append_srv(q, reply, rr);
+                        if (r < 0)
+                                goto finish;
+                        if (r == 0) /* not an SRV record */
+                                continue;
+
+                        if (!canonical)
+                                canonical = dns_resource_record_ref(rr);
+
+                        added++;
+                }
+        }
+
+        if (added <= 0) {
+                r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_question_first_name(q->question));
+                goto finish;
+        }
+
+        r = sd_bus_message_close_container(reply);
+        if (r < 0)
+                goto finish;
+
+        r = sd_bus_message_open_container(reply, 'a', "ay");
+        if (r < 0)
+                goto finish;
+
+        if (q->answer) {
+                DnsResourceRecord *rr;
+
+                DNS_ANSWER_FOREACH(rr, q->answer) {
+                        r = dns_question_matches_rr(q->question, rr, NULL);
+                        if (r < 0)
+                                goto finish;
+                        if (r == 0)
+                                continue;
+
+                        r = append_txt(reply, rr);
+                        if (r < 0)
+                                goto finish;
+                }
+        }
+
+        r = sd_bus_message_close_container(reply);
+        if (r < 0)
+                goto finish;
+
+        assert(canonical);
+        r = dns_service_split(DNS_RESOURCE_KEY_NAME(canonical->key), &name, &type, &domain);
+        if (r < 0)
+                goto finish;
+
+        r = sd_bus_message_append(
+                        reply,
+                        "ssst",
+                        name, type, domain,
+                        SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family));
+        if (r < 0)
+                goto finish;
+
+        r = sd_bus_send(q->manager->bus, reply, NULL);
+
+finish:
         if (r < 0) {
-                dns_query_free(q);
+                log_error_errno(r, "Failed to send service reply: %m");
+                sd_bus_reply_method_errno(q->request, r, NULL);
+        }
+
+        dns_query_free(q);
+}
+
+static void resolve_service_hostname_complete(DnsQuery *q) {
+        int r;
+
+        assert(q);
+        assert(q->auxiliary_for);
+
+        if (q->state != DNS_TRANSACTION_SUCCESS) {
+                resolve_service_all_complete(q->auxiliary_for);
+                return;
+        }
+
+        r = dns_query_process_cname(q);
+        if (r > 0) /* This was a cname, and the query was restarted. */
+                return;
+
+        /* This auxiliary lookup is finished or failed, let's see if all are finished now. */
+        q->auxiliary_result = r;
+        resolve_service_all_complete(q->auxiliary_for);
+}
+
+static int resolve_service_hostname(DnsQuery *q, DnsResourceRecord *rr, int ifindex) {
+        _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
+        DnsQuery *aux;
+        int r;
+
+        assert(q);
+        assert(rr);
+        assert(rr->key);
+        assert(rr->key->type == DNS_TYPE_SRV);
+
+        /* OK, we found an SRV record for the service. Let's resolve
+         * the hostname included in it */
+
+        r = dns_question_new_address(&question, q->request_family, rr->srv.name);
+        if (r < 0)
+                return r;
+
+        r = dns_query_new(q->manager, &aux, question, ifindex, q->flags|SD_RESOLVED_NO_SEARCH);
+        if (r < 0)
                 return r;
+
+        aux->request_family = q->request_family;
+        aux->complete = resolve_service_hostname_complete;
+
+        r = dns_query_make_auxiliary(aux, q);
+        if (r == -EAGAIN) {
+                /* Too many auxiliary lookups? If so, don't complain,
+                 * let's just not add this one, we already have more
+                 * than enough */
+
+                dns_query_free(aux);
+                return 0;
         }
+        if (r < 0)
+                goto fail;
+
+        /* Note that auxiliary queries do not track the original bus
+         * client, only the primary request does that. */
+
+        r = dns_query_go(aux);
+        if (r < 0)
+                goto fail;
 
         return 1;
+
+fail:
+        dns_query_free(aux);
+        return r;
+}
+
+static void bus_method_resolve_service_complete(DnsQuery *q) {
+        unsigned found = 0;
+        int r;
+
+        assert(q);
+
+        if (q->state != DNS_TRANSACTION_SUCCESS) {
+                r = reply_query_state(q);
+                goto finish;
+        }
+
+        r = dns_query_process_cname(q);
+        if (r == -ELOOP) {
+                r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_question_first_name(q->question));
+                goto finish;
+        }
+        if (r < 0)
+                goto finish;
+        if (r > 0) /* This was a cname, and the query was restarted. */
+                return;
+
+        if (q->answer) {
+                DnsResourceRecord *rr;
+                int ifindex;
+
+                DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
+                        r = dns_question_matches_rr(q->question, rr, NULL);
+                        if (r < 0)
+                                goto finish;
+                        if (r == 0)
+                                continue;
+
+                        if (rr->key->type != DNS_TYPE_SRV)
+                                continue;
+
+                        if ((q->flags & SD_RESOLVED_NO_ADDRESS) == 0) {
+                                q->block_all_complete ++;
+                                r = resolve_service_hostname(q, rr, ifindex);
+                                q->block_all_complete --;
+
+                                if (r < 0)
+                                        goto finish;
+                        }
+
+                        found++;
+                }
+        }
+
+        if (found <= 0) {
+                r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_question_first_name(q->question));
+                goto finish;
+        }
+
+        /* Maybe we are already finished? check now... */
+        resolve_service_all_complete(q);
+        return;
+
+finish:
+        if (r < 0) {
+                log_error_errno(r, "Failed to send service reply: %m");
+                sd_bus_reply_method_errno(q->request, r, NULL);
+        }
+
+        dns_query_free(q);
+}
+
+static int bus_method_resolve_service(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
+        const char *name, *type, *domain, *joined;
+        _cleanup_free_ char *n = NULL;
+        Manager *m = userdata;
+        int family, ifindex;
+        uint64_t flags;
+        DnsQuery *q;
+        int r;
+
+        assert(message);
+        assert(m);
+
+        assert_cc(sizeof(int) == sizeof(int32_t));
+
+        r = sd_bus_message_read(message, "isssit", &ifindex, &name, &type, &domain, &family, &flags);
+        if (r < 0)
+                return r;
+
+        if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
+
+        if (isempty(name))
+                name = NULL;
+        else {
+                if (!dns_service_name_is_valid(name))
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid service name '%s'", name);
+        }
+
+        if (isempty(type))
+                type = NULL;
+        else if (!dns_srv_type_is_valid(type))
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid SRV service type '%s'", type);
+
+        r = dns_name_is_valid(domain);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid domain '%s'", domain);
+
+        if (name && !type)
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Service name cannot be specified without service type.");
+
+        r = check_ifindex_flags(ifindex, &flags, SD_RESOLVED_NO_TXT|SD_RESOLVED_NO_ADDRESS, error);
+        if (r < 0)
+                return r;
+
+        if (type) {
+                /* If the type is specified, we generate the full domain name to look up ourselves */
+                r = dns_service_join(name, type, domain, &n);
+                if (r < 0)
+                        return r;
+
+                joined = n;
+        } else
+                /* If no type is specified, we assume the domain
+                 * contains the full domain name to lookup already */
+                joined = domain;
+
+        r = dns_question_new_service(&question, joined, !(flags & SD_RESOLVED_NO_TXT));
+        if (r < 0)
+                return r;
+
+        r = dns_query_new(m, &q, question, ifindex, flags|SD_RESOLVED_NO_SEARCH);
+        if (r < 0)
+                return r;
+
+        q->request = sd_bus_message_ref(message);
+        q->request_family = family;
+        q->complete = bus_method_resolve_service_complete;
+
+        r = dns_query_bus_track(q, message);
+        if (r < 0)
+                goto fail;
+
+        r = dns_query_go(q);
+        if (r < 0)
+                goto fail;
+
+        return 1;
+
+fail:
+        dns_query_free(q);
+        return r;
+}
+
+static int append_dns_server(sd_bus_message *reply, DnsServer *s) {
+        int r;
+
+        assert(reply);
+        assert(s);
+
+        r = sd_bus_message_open_container(reply, 'r', "iiay");
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_append(reply, "ii", s->link ? s->link->ifindex : 0, s->family);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_append_array(reply, 'y', &s->address, FAMILY_ADDRESS_SIZE(s->family));
+        if (r < 0)
+                return r;
+
+        return sd_bus_message_close_container(reply);
+}
+
+static int bus_property_get_dns_servers(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        Manager *m = userdata;
+        unsigned c = 0;
+        DnsServer *s;
+        Iterator i;
+        Link *l;
+        int r;
+
+        assert(reply);
+        assert(m);
+
+        r = sd_bus_message_open_container(reply, 'a', "(iiay)");
+        if (r < 0)
+                return r;
+
+        LIST_FOREACH(servers, s, m->dns_servers) {
+                r = append_dns_server(reply, s);
+                if (r < 0)
+                        return r;
+
+                c++;
+        }
+
+        HASHMAP_FOREACH(l, m->links, i) {
+                LIST_FOREACH(servers, s, l->dns_servers) {
+                        r = append_dns_server(reply, s);
+                        if (r < 0)
+                                return r;
+                        c++;
+                }
+        }
+
+        if (c == 0) {
+                LIST_FOREACH(servers, s, m->fallback_dns_servers) {
+                        r = append_dns_server(reply, s);
+                        if (r < 0)
+                                return r;
+                }
+        }
+
+        return sd_bus_message_close_container(reply);
+}
+
+static int bus_property_get_search_domains(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        Manager *m = userdata;
+        DnsSearchDomain *d;
+        Iterator i;
+        Link *l;
+        int r;
+
+        assert(reply);
+        assert(m);
+
+        r = sd_bus_message_open_container(reply, 'a', "(is)");
+        if (r < 0)
+                return r;
+
+        LIST_FOREACH(domains, d, m->search_domains) {
+                r = sd_bus_message_append(reply, "(is)", 0, d->name);
+                if (r < 0)
+                        return r;
+        }
+
+        HASHMAP_FOREACH(l, m->links, i) {
+                LIST_FOREACH(domains, d, l->search_domains) {
+                        r = sd_bus_message_append(reply, "is", l->ifindex, d->name);
+                        if (r < 0)
+                                return r;
+                }
+        }
+
+        return sd_bus_message_close_container(reply);
 }
 
 static const sd_bus_vtable resolve_vtable[] = {
         SD_BUS_VTABLE_START(0),
+        SD_BUS_PROPERTY("LLMNRHostname", "s", NULL, offsetof(Manager, llmnr_hostname), 0),
+        SD_BUS_PROPERTY("DNSServers", "a(iiay)", bus_property_get_dns_servers, 0, 0),
+        SD_BUS_PROPERTY("SearchDomains", "a(is)", bus_property_get_search_domains, 0, 0),
+
         SD_BUS_METHOD("ResolveHostname", "isit", "a(iiay)st", bus_method_resolve_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("ResolveAddress", "iiayt", "a(is)t", bus_method_resolve_address, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("ResolveRecord", "isqqt", "a(iqqay)t", bus_method_resolve_record, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("ResolveService", "isssit", "a(qqqsa(iiay)s)aayssst", bus_method_resolve_service, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_VTABLE_END,
 };
 
index 920771955136d3ab56658578271b158520003b4c..3fc7d9ae3d2b889f09f5a91af2c13e6a2c142f6e 100644 (file)
 #include "resolved-conf.h"
 #include "string-util.h"
 
-int manager_parse_dns_server(Manager *m, DnsServerType type, const char *string) {
-        DnsServer *first;
+int manager_add_dns_server_by_string(Manager *m, DnsServerType type, const char *word) {
+        union in_addr_union address;
+        int family, r;
+        DnsServer *s;
+
+        assert(m);
+        assert(word);
+
+        r = in_addr_from_string_auto(word, &family, &address);
+        if (r < 0)
+                return r;
+
+        /* Filter out duplicates */
+        s = dns_server_find(manager_get_first_dns_server(m, type), family, &address);
+        if (s) {
+                /*
+                 * Drop the marker. This is used to find the servers
+                 * that ceased to exist, see
+                 * manager_mark_dns_servers() and
+                 * manager_flush_marked_dns_servers().
+                 */
+                dns_server_move_back_and_unmark(s);
+                return 0;
+        }
+
+        return dns_server_new(m, NULL, type, NULL, family, &address);
+}
+
+int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, const char *string) {
         int r;
 
         assert(m);
         assert(string);
 
-        first = type == DNS_SERVER_FALLBACK ? m->fallback_dns_servers : m->dns_servers;
-
         for(;;) {
                 _cleanup_free_ char *word = NULL;
-                union in_addr_union addr;
-                bool found = false;
-                DnsServer *s;
-                int family;
 
                 r = extract_first_word(&string, &word, NULL, 0);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to parse resolved dns server syntax \"%s\": %m", string);
+                        return r;
                 if (r == 0)
                         break;
 
-                r = in_addr_from_string_auto(word, &family, &addr);
-                if (r < 0) {
-                        log_warning("Ignoring invalid DNS address '%s'", word);
-                        continue;
-                }
+                r = manager_add_dns_server_by_string(m, type, word);
+                if (r < 0)
+                        log_warning_errno(r, "Failed to add DNS server address '%s', ignoring.", word);
+        }
+
+        return 0;
+}
 
-                /* Filter out duplicates */
-                LIST_FOREACH(servers, s, first)
-                        if (s->family == family && in_addr_equal(family, &s->address, &addr)) {
-                                found = true;
-                                break;
-                        }
+int manager_add_search_domain_by_string(Manager *m, const char *domain) {
+        DnsSearchDomain *d;
+        int r;
 
-                if (found)
-                        continue;
+        assert(m);
+        assert(domain);
 
-                r = dns_server_new(m, NULL, type, NULL, family, &addr);
+        r = dns_search_domain_find(m->search_domains, domain, &d);
+        if (r < 0)
+                return r;
+        if (r > 0) {
+                dns_search_domain_move_back_and_unmark(d);
+                return 0;
+        }
+
+        return dns_search_domain_new(m, NULL, DNS_SEARCH_DOMAIN_SYSTEM, NULL, domain);
+}
+
+int manager_parse_search_domains_and_warn(Manager *m, const char *string) {
+        int r;
+
+        assert(m);
+        assert(string);
+
+        for(;;) {
+                _cleanup_free_ char *word = NULL;
+
+                r = extract_first_word(&string, &word, NULL, EXTRACT_QUOTES);
                 if (r < 0)
                         return r;
+                if (r == 0)
+                        break;
+
+                r = manager_add_search_domain_by_string(m, word);
+                if (r < 0)
+                        log_warning_errno(r, "Failed to add search domain '%s', ignoring.", word);
         }
 
         return 0;
 }
 
-int config_parse_dnsv(
+int config_parse_dns_servers(
                 const char *unit,
                 const char *filename,
                 unsigned line,
@@ -95,10 +141,10 @@ int config_parse_dnsv(
 
         if (isempty(rvalue))
                 /* Empty assignment means clear the list */
-                manager_flush_dns_servers(m, ltype);
+                dns_server_unlink_all(manager_get_first_dns_server(m, ltype));
         else {
                 /* Otherwise, add to the list */
-                r = manager_parse_dns_server(m, ltype, rvalue);
+                r = manager_parse_dns_server_string_and_warn(m, ltype, rvalue);
                 if (r < 0) {
                         log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse DNS server string '%s'. Ignoring.", rvalue);
                         return 0;
@@ -109,6 +155,47 @@ int config_parse_dnsv(
          * /etc/resolv.conf */
         if (ltype == DNS_SERVER_SYSTEM)
                 m->read_resolv_conf = false;
+        if (ltype == DNS_SERVER_FALLBACK)
+                m->need_builtin_fallbacks = false;
+
+        return 0;
+}
+
+int config_parse_search_domains(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        Manager *m = userdata;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(m);
+
+        if (isempty(rvalue))
+                /* Empty assignment means clear the list */
+                dns_search_domain_unlink_all(m->search_domains);
+        else {
+                /* Otherwise, add to the list */
+                r = manager_parse_search_domains_and_warn(m, rvalue);
+                if (r < 0) {
+                        log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse search domains string '%s'. Ignoring.", rvalue);
+                        return 0;
+                }
+        }
+
+        /* If we have a manual setting, then we stop reading
+         * /etc/resolv.conf */
+        m->read_resolv_conf = false;
 
         return 0;
 }
@@ -148,11 +235,24 @@ int config_parse_support(
 }
 
 int manager_parse_config_file(Manager *m) {
+        int r;
+
         assert(m);
 
-        return config_parse_many(PKGSYSCONFDIR "/resolved.conf",
-                                 CONF_PATHS_NULSTR("systemd/resolved.conf.d"),
-                                 "Resolve\0",
-                                 config_item_perf_lookup, resolved_gperf_lookup,
-                                 false, m);
+        r = config_parse_many(PKGSYSCONFDIR "/resolved.conf",
+                              CONF_PATHS_NULSTR("systemd/resolved.conf.d"),
+                              "Resolve\0",
+                              config_item_perf_lookup, resolved_gperf_lookup,
+                              false, m);
+        if (r < 0)
+                return r;
+
+        if (m->need_builtin_fallbacks) {
+                r = manager_parse_dns_server_string_and_warn(m, DNS_SERVER_FALLBACK, DNS_SERVERS);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+
 }
index b3dbea7b6b3c7aefea90283b6e33156ce4883ecf..28d2549d35df8cb2174e71f15b7234facfdc0e37 100644 (file)
 
 #include "resolved-manager.h"
 
-int manager_parse_dns_server(Manager *m, DnsServerType type, const char *string);
 int manager_parse_config_file(Manager *m);
 
+int manager_add_search_domain_by_string(Manager *m, const char *domain);
+int manager_parse_search_domains_and_warn(Manager *m, const char *string);
+
+int manager_add_dns_server_by_string(Manager *m, DnsServerType type, const char *word);
+int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, const char *string);
+
 const struct ConfigPerfItem* resolved_gperf_lookup(const char *key, unsigned length);
 
-int config_parse_dnsv(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_dns_servers(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_search_domains(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 int config_parse_support(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
index 086d111205b3f47a44bf978ea1d57bf19a918acd..be29f516631cad22fb66fcbf2780a8b8e90292fb 100644 (file)
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#define SD_RESOLVED_DNS           ((uint64_t) 1)
-#define SD_RESOLVED_LLMNR_IPV4    ((uint64_t) 2)
-#define SD_RESOLVED_LLMNR_IPV6    ((uint64_t) 4)
-#define SD_RESOLVED_LLMNR         (SD_RESOLVED_LLMNR_IPV4|SD_RESOLVED_LLMNR_IPV6)
+#define SD_RESOLVED_DNS           (UINT64_C(1) << 0)
+#define SD_RESOLVED_LLMNR_IPV4    (UINT64_C(1) << 1)
+#define SD_RESOLVED_LLMNR_IPV6    (UINT64_C(1) << 2)
+#define SD_RESOLVED_NO_CNAME      (UINT64_C(1) << 5)
+#define SD_RESOLVED_NO_TXT        (UINT64_C(1) << 6)
+#define SD_RESOLVED_NO_ADDRESS    (UINT64_C(1) << 7)
+#define SD_RESOLVED_NO_SEARCH     (UINT64_C(1) << 8)
 
-#define SD_RESOLVED_FLAGS_ALL     (SD_RESOLVED_DNS|SD_RESOLVED_LLMNR_IPV4|SD_RESOLVED_LLMNR_IPV6)
-#define SD_RESOLVED_FLAGS_DEFAULT SD_RESOLVED_FLAGS_ALL
+#define SD_RESOLVED_LLMNR         (SD_RESOLVED_LLMNR_IPV4|SD_RESOLVED_LLMNR_IPV6)
+#define SD_RESOLVED_PROTOCOLS_ALL (SD_RESOLVED_LLMNR|SD_RESOLVED_DNS)
index 3cf9c6807477f815e6929ef6854d4291bee51413..4db67f7278f4f7ac9a8806e7b7f91f2a9a5509f9 100644 (file)
@@ -141,7 +141,7 @@ int dns_answer_contains(DnsAnswer *a, DnsResourceKey *key) {
                 return 0;
 
         for (i = 0; i < a->n_rrs; i++) {
-                r = dns_resource_key_match_rr(key, a->items[i].rr);
+                r = dns_resource_key_match_rr(key, a->items[i].rr, NULL);
                 if (r < 0)
                         return r;
                 if (r > 0)
index 044d73b19c3173fc9e718f712fe7a714f796d651..8814919deb365fdbe5a6557aa8fa22f18839fbfc 100644 (file)
@@ -58,3 +58,20 @@ void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local);
 int dns_answer_reserve(DnsAnswer **a, unsigned n_free);
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsAnswer*, dns_answer_unref);
+
+#define DNS_ANSWER_FOREACH(kk, a)                                       \
+        for (unsigned _i = ({                                           \
+                                (kk) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].rr : NULL; \
+                                0;                                      \
+                           });                                          \
+             (a) && ((_i) < (a)->n_rrs);                                \
+             _i++, (kk) = (_i < (a)->n_rrs ? (a)->items[_i].rr : NULL))
+
+#define DNS_ANSWER_FOREACH_IFINDEX(kk, ifindex, a)                      \
+        for (unsigned _i = ({                                           \
+                                (kk) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].rr : NULL; \
+                                (ifindex) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].ifindex : 0; \
+                                0;                                      \
+                           });                                          \
+             (a) && ((_i) < (a)->n_rrs);                                \
+             _i++, (kk) = ((_i < (a)->n_rrs) ? (a)->items[_i].rr : NULL), (ifindex) = ((_i < (a)->n_rrs) ? (a)->items[_i].ifindex : 0))
index 04f64022e01c4444e404e8f6e6f9c598afff04a5..d963ce6e00357574f9e4f146884439a1bc54cb6d 100644 (file)
 ***/
 
 #include "alloc-util.h"
+#include "dns-domain.h"
 #include "resolved-dns-cache.h"
 #include "resolved-dns-packet.h"
+#include "string-util.h"
 
 /* Never cache more than 1K entries */
 #define CACHE_MAX 1024
@@ -521,25 +523,53 @@ fail:
 
 static DnsCacheItem *dns_cache_get_by_key_follow_cname(DnsCache *c, DnsResourceKey *k) {
         _cleanup_(dns_resource_key_unrefp) DnsResourceKey *cname_key = NULL;
-        DnsCacheItem *i, *j;
+        DnsCacheItem *i;
+        const char *n;
+        int r;
 
         assert(c);
         assert(k);
 
+        /* If we hit some OOM error, or suchlike, we don't care too
+         * much, after all this is just a cache */
+
         i = hashmap_get(c->by_key, k);
-        if (i || k->type == DNS_TYPE_CNAME)
+        if (i || k->type == DNS_TYPE_CNAME || k->type == DNS_TYPE_DNAME)
                 return i;
 
-        /* check if we have a CNAME record instead */
+        /* Check if we have a CNAME record instead */
         cname_key = dns_resource_key_new_cname(k);
         if (!cname_key)
                 return NULL;
 
-        j = hashmap_get(c->by_key, cname_key);
-        if (j)
-                return j;
+        i = hashmap_get(c->by_key, cname_key);
+        if (i)
+                return i;
+
+        /* OK, let's look for cached DNAME records. */
+        n = DNS_RESOURCE_KEY_NAME(k);
+        for (;;) {
+                _cleanup_(dns_resource_key_unrefp) DnsResourceKey *dname_key = NULL;
+                char label[DNS_LABEL_MAX];
+
+                if (isempty(n))
+                        return NULL;
 
-        return i;
+                dname_key = dns_resource_key_new(k->class, DNS_TYPE_DNAME, n);
+                if (!dname_key)
+                        return NULL;
+
+                i = hashmap_get(c->by_key, dname_key);
+                if (i)
+                        return i;
+
+                /* Jump one label ahead */
+                r = dns_label_unescape(&n, label, sizeof(label));
+                if (r <= 0)
+                        return NULL;
+        }
+
+        return NULL;
 }
 
 int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **ret) {
index 60cf6a47848d22986c54a2599332a0bc780174b4..164435b4fb08ea71b53372a5b3e67460ff23c3fb 100644 (file)
 
 
 #include "hashmap.h"
+#include "list.h"
 #include "prioq.h"
 #include "time-util.h"
-#include "list.h"
 
 typedef struct DnsCache {
         Hashmap *by_key;
         Prioq *by_expiry;
 } DnsCache;
 
-#include "resolved-dns-rr.h"
-#include "resolved-dns-question.h"
 #include "resolved-dns-answer.h"
+#include "resolved-dns-question.h"
+#include "resolved-dns-rr.h"
 
 void dns_cache_flush(DnsCache *c);
 void dns_cache_prune(DnsCache *c);
index f23b3cf89340d120119ed20c53900250611e66e6..40b662246f89a444de2a86a96b409ff7bb27bb96 100644 (file)
@@ -370,6 +370,28 @@ int dns_packet_append_string(DnsPacket *p, const char *s, size_t *start) {
         return 0;
 }
 
+int dns_packet_append_raw_string(DnsPacket *p, const void *s, size_t size, size_t *start) {
+        void *d;
+        int r;
+
+        assert(p);
+        assert(s || size == 0);
+
+        if (size > 255)
+                return -E2BIG;
+
+        r = dns_packet_extend(p, 1 + size, &d, start);
+        if (r < 0)
+                return r;
+
+        ((uint8_t*) d)[0] = (uint8_t) size;
+
+        if (size > 0)
+                memcpy(((uint8_t*) d) + 1, s, size);
+
+        return 0;
+}
+
 int dns_packet_append_label(DnsPacket *p, const char *d, size_t l, size_t *start) {
         void *w;
         int r;
@@ -643,19 +665,20 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star
                 break;
 
         case DNS_TYPE_SPF: /* exactly the same as TXT */
-        case DNS_TYPE_TXT: {
-                char **s;
+        case DNS_TYPE_TXT:
 
-                if (strv_isempty(rr->txt.strings)) {
+                if (!rr->txt.items) {
                         /* RFC 6763, section 6.1 suggests to generate
                          * single empty string for an empty array. */
 
-                        r = dns_packet_append_string(p, "", NULL);
+                        r = dns_packet_append_raw_string(p, NULL, 0, NULL);
                         if (r < 0)
                                 goto fail;
                 } else {
-                        STRV_FOREACH(s, rr->txt.strings) {
-                                r = dns_packet_append_string(p, *s, NULL);
+                        DnsTxtItem *i;
+
+                        LIST_FOREACH(items, i, rr->txt.items) {
+                                r = dns_packet_append_raw_string(p, i->data, i->length, NULL);
                                 if (r < 0)
                                         goto fail;
                         }
@@ -663,7 +686,6 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star
 
                 r = 0;
                 break;
-        }
 
         case DNS_TYPE_A:
                 r = dns_packet_append_blob(p, &rr->a.in_addr, sizeof(struct in_addr), NULL);
@@ -1062,6 +1084,35 @@ fail:
         return r;
 }
 
+int dns_packet_read_raw_string(DnsPacket *p, const void **ret, size_t *size, size_t *start) {
+        size_t saved_rindex;
+        uint8_t c;
+        int r;
+
+        assert(p);
+
+        saved_rindex = p->rindex;
+
+        r = dns_packet_read_uint8(p, &c, NULL);
+        if (r < 0)
+                goto fail;
+
+        r = dns_packet_read(p, c, ret, NULL);
+        if (r < 0)
+                goto fail;
+
+        if (size)
+                *size = c;
+        if (start)
+                *start = saved_rindex;
+
+        return 0;
+
+fail:
+        dns_packet_rewind(p, saved_rindex);
+        return r;
+}
+
 int dns_packet_read_name(
                 DnsPacket *p,
                 char **_ret,
@@ -1094,7 +1145,6 @@ int dns_packet_read_name(
                         /* End of name */
                         break;
                 else if (c <= 63) {
-                        _cleanup_free_ char *t = NULL;
                         const char *label;
 
                         /* Literal label */
@@ -1102,21 +1152,20 @@ int dns_packet_read_name(
                         if (r < 0)
                                 goto fail;
 
-                        r = dns_label_escape(label, c, &t);
-                        if (r < 0)
-                                goto fail;
-
-                        if (!GREEDY_REALLOC(ret, allocated, n + !first + strlen(t) + 1)) {
+                        if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) {
                                 r = -ENOMEM;
                                 goto fail;
                         }
 
-                        if (!first)
-                                ret[n++] = '.';
-                        else
+                        if (first)
                                 first = false;
+                        else
+                                ret[n++] = '.';
+
+                        r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX);
+                        if (r < 0)
+                                goto fail;
 
-                        memcpy(ret + n, t, r);
                         n += r;
                         continue;
                 } else if (allow_compression && (c & 0xc0) == 0xc0) {
@@ -1412,24 +1461,37 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
         case DNS_TYPE_SPF: /* exactly the same as TXT */
         case DNS_TYPE_TXT:
                 if (rdlength <= 0) {
+                        DnsTxtItem *i;
                         /* RFC 6763, section 6.1 suggests to treat
                          * empty TXT RRs as equivalent to a TXT record
                          * with a single empty string. */
 
-                        r = strv_extend(&rr->txt.strings, "");
-                        if (r < 0)
-                                goto fail;
+                        i = malloc0(offsetof(DnsTxtItem, data) + 1); /* for safety reasons we add an extra NUL byte */
+                        if (!i)
+                                return -ENOMEM;
+
+                        rr->txt.items = i;
                 } else {
+                        DnsTxtItem *last = NULL;
+
                         while (p->rindex < offset + rdlength) {
-                                char *s;
+                                DnsTxtItem *i;
+                                const void *data;
+                                size_t sz;
 
-                                r = dns_packet_read_string(p, &s, NULL);
+                                r = dns_packet_read_raw_string(p, &data, &sz, NULL);
                                 if (r < 0)
-                                        goto fail;
+                                        return r;
 
-                                r = strv_consume(&rr->txt.strings, s);
-                                if (r < 0)
-                                        goto fail;
+                                i = malloc0(offsetof(DnsTxtItem, data) + sz + 1); /* extra NUL byte at the end */
+                                if (!i)
+                                        return -ENOMEM;
+
+                                memcpy(i->data, data, sz);
+                                i->length = sz;
+
+                                LIST_INSERT_AFTER(items, rr->txt.items, last, i);
+                                last = i;
                         }
                 }
 
@@ -1682,12 +1744,9 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
                 if (r < 0)
                         goto fail;
 
-                /* The types bitmap must contain at least the NSEC record itself, so an empty bitmap means
-                   something went wrong */
-                if (bitmap_isclear(rr->nsec.types)) {
-                        r = -EBADMSG;
-                        goto fail;
-                }
+                /* We accept empty NSEC bitmaps. The bit indicating the presence of the NSEC record itself
+                 * is redundant and in e.g., RFC4956 this fact is used to define a use for NSEC records
+                 * without the NSEC bit set. */
 
                 break;
 
index fbbabaf232d7d47e767866bbf9b35317c8c8ebaf..90b5a7c8bd81a130fb83f1f9212c80101a400b90 100644 (file)
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
  ***/
 
-#include <netinet/udp.h>
 #include <netinet/ip.h>
+#include <netinet/udp.h>
 
-#include "macro.h"
-#include "sparse-endian.h"
 #include "hashmap.h"
 #include "in-addr-util.h"
+#include "macro.h"
+#include "sparse-endian.h"
 
 typedef struct DnsPacketHeader DnsPacketHeader;
 typedef struct DnsPacket DnsPacket;
 
-#include "resolved-dns-rr.h"
-#include "resolved-dns-question.h"
-#include "resolved-dns-answer.h"
 #include "resolved-def.h"
+#include "resolved-dns-answer.h"
+#include "resolved-dns-question.h"
+#include "resolved-dns-rr.h"
 
 typedef enum DnsProtocol {
         DNS_PROTOCOL_DNS,
@@ -155,9 +155,9 @@ int dns_packet_append_uint8(DnsPacket *p, uint8_t v, size_t *start);
 int dns_packet_append_uint16(DnsPacket *p, uint16_t v, size_t *start);
 int dns_packet_append_uint32(DnsPacket *p, uint32_t v, size_t *start);
 int dns_packet_append_string(DnsPacket *p, const char *s, size_t *start);
+int dns_packet_append_raw_string(DnsPacket *p, const void *s, size_t size, size_t *start);
 int dns_packet_append_label(DnsPacket *p, const char *s, size_t l, size_t *start);
-int dns_packet_append_name(DnsPacket *p, const char *name,
-                           bool allow_compression, size_t *start);
+int dns_packet_append_name(DnsPacket *p, const char *name, bool allow_compression, size_t *start);
 int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *key, size_t *start);
 int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *start);
 
@@ -167,8 +167,8 @@ int dns_packet_read_uint8(DnsPacket *p, uint8_t *ret, size_t *start);
 int dns_packet_read_uint16(DnsPacket *p, uint16_t *ret, size_t *start);
 int dns_packet_read_uint32(DnsPacket *p, uint32_t *ret, size_t *start);
 int dns_packet_read_string(DnsPacket *p, char **ret, size_t *start);
-int dns_packet_read_name(DnsPacket *p, char **ret,
-                         bool allow_compression, size_t *start);
+int dns_packet_read_raw_string(DnsPacket *p, const void **ret, size_t *size, size_t *start);
+int dns_packet_read_name(DnsPacket *p, char **ret, bool allow_compression, size_t *start);
 int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, size_t *start);
 int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start);
 
@@ -177,6 +177,14 @@ void dns_packet_rewind(DnsPacket *p, size_t idx);
 int dns_packet_skip_question(DnsPacket *p);
 int dns_packet_extract(DnsPacket *p);
 
+static inline bool DNS_PACKET_SHALL_CACHE(DnsPacket *p) {
+        /* Never cache data originating from localhost, under the
+         * assumption, that it's coming from a locally DNS forwarder
+         * or server, that is caching on its own. */
+
+        return in_addr_is_localhost(p->family, &p->sender) == 0;
+}
+
 enum {
         DNS_RCODE_SUCCESS = 0,
         DNS_RCODE_FORMERR = 1,
index f7cb84e2a62fed26af548d5b06f2d41ad7405c32..a96cf439adc60af72de25a3a09983b0222914f4c 100644 (file)
 
 #define CNAME_MAX 8
 #define QUERIES_MAX 2048
+#define AUXILIARY_QUERIES_MAX 64
 
-static void dns_query_stop(DnsQuery *q) {
-        DnsTransaction *t;
+static int dns_query_candidate_new(DnsQueryCandidate **ret, DnsQuery *q, DnsScope *s) {
+        DnsQueryCandidate *c;
 
+        assert(ret);
         assert(q);
+        assert(s);
 
-        q->timeout_event_source = sd_event_source_unref(q->timeout_event_source);
+        c = new0(DnsQueryCandidate, 1);
+        if (!c)
+                return -ENOMEM;
+
+        c->query = q;
+        c->scope = s;
+
+        LIST_PREPEND(candidates_by_query, q->candidates, c);
+        LIST_PREPEND(candidates_by_scope, s->query_candidates, c);
+
+        *ret = c;
+        return 0;
+}
 
-        while ((t = set_steal_first(q->transactions))) {
-                set_remove(t->queries, q);
+static void dns_query_candidate_stop(DnsQueryCandidate *c) {
+        DnsTransaction *t;
+
+        assert(c);
+
+        while ((t = set_steal_first(c->transactions))) {
+                set_remove(t->query_candidates, c);
                 dns_transaction_gc(t);
         }
 }
 
+DnsQueryCandidate* dns_query_candidate_free(DnsQueryCandidate *c) {
+
+        if (!c)
+                return NULL;
+
+        dns_query_candidate_stop(c);
+
+        set_free(c->transactions);
+        dns_search_domain_unref(c->search_domain);
+
+        if (c->query)
+                LIST_REMOVE(candidates_by_query, c->query->candidates, c);
+
+        if (c->scope)
+                LIST_REMOVE(candidates_by_scope, c->scope->query_candidates, c);
+
+        free(c);
+
+        return NULL;
+}
+
+static int dns_query_candidate_next_search_domain(DnsQueryCandidate *c) {
+        _cleanup_(dns_search_domain_unrefp) DnsSearchDomain *previous = NULL;
+        DnsSearchDomain *next = NULL;
+
+        assert(c);
+
+        if (c->search_domain && c->search_domain->linked) {
+                next = c->search_domain->domains_next;
+
+                if (!next) /* We hit the end of the list */
+                        return 0;
+
+        } else {
+                next = dns_scope_get_search_domains(c->scope);
+
+                if (!next) /* OK, there's nothing. */
+                        return 0;
+        }
+
+        dns_search_domain_unref(c->search_domain);
+        c->search_domain = dns_search_domain_ref(next);
+
+        return 1;
+}
+
+static int dns_query_candidate_add_transaction(DnsQueryCandidate *c, DnsResourceKey *key) {
+        DnsTransaction *t;
+        int r;
+
+        assert(c);
+        assert(key);
+
+        r = set_ensure_allocated(&c->transactions, NULL);
+        if (r < 0)
+                return r;
+
+        t = dns_scope_find_transaction(c->scope, key, true);
+        if (!t) {
+                r = dns_transaction_new(&t, c->scope, key);
+                if (r < 0)
+                        return r;
+        }
+
+        r = set_ensure_allocated(&t->query_candidates, NULL);
+        if (r < 0)
+                goto gc;
+
+        r = set_put(t->query_candidates, c);
+        if (r < 0)
+                goto gc;
+
+        r = set_put(c->transactions, t);
+        if (r < 0) {
+                set_remove(t->query_candidates, c);
+                goto gc;
+        }
+
+        return 0;
+
+gc:
+        dns_transaction_gc(t);
+        return r;
+}
+
+static int dns_query_candidate_go(DnsQueryCandidate *c) {
+        DnsTransaction *t;
+        Iterator i;
+        int r;
+
+        assert(c);
+
+        /* Start the transactions that are not started yet */
+        SET_FOREACH(t, c->transactions, i) {
+                if (t->state != DNS_TRANSACTION_NULL)
+                        continue;
+
+                r = dns_transaction_go(t);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+static DnsTransactionState dns_query_candidate_state(DnsQueryCandidate *c) {
+        DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
+        DnsTransaction *t;
+        Iterator i;
+
+        assert(c);
+
+        if (c->error_code != 0)
+                return DNS_TRANSACTION_RESOURCES;
+
+        SET_FOREACH(t, c->transactions, i) {
+
+                switch (t->state) {
+
+                case DNS_TRANSACTION_PENDING:
+                case DNS_TRANSACTION_NULL:
+                        return t->state;
+
+                case DNS_TRANSACTION_SUCCESS:
+                        state = t->state;
+                        break;
+
+                default:
+                        if (state != DNS_TRANSACTION_SUCCESS)
+                                state = t->state;
+
+                        break;
+                }
+        }
+
+        return state;
+}
+
+static int dns_query_candidate_setup_transactions(DnsQueryCandidate *c) {
+        DnsResourceKey *key;
+        int n = 0, r;
+
+        assert(c);
+
+        dns_query_candidate_stop(c);
+
+        /* Create one transaction per question key */
+        DNS_QUESTION_FOREACH(key, c->query->question) {
+                _cleanup_(dns_resource_key_unrefp) DnsResourceKey *new_key = NULL;
+
+                if (c->search_domain) {
+                        r = dns_resource_key_new_append_suffix(&new_key, key, c->search_domain->name);
+                        if (r < 0)
+                                goto fail;
+                }
+
+                r = dns_query_candidate_add_transaction(c, new_key ?: key);
+                if (r < 0)
+                        goto fail;
+
+                n++;
+        }
+
+        return n;
+
+fail:
+        dns_query_candidate_stop(c);
+        return r;
+}
+
+void dns_query_candidate_ready(DnsQueryCandidate *c) {
+        DnsTransactionState state;
+        int r;
+
+        assert(c);
+
+        state = dns_query_candidate_state(c);
+
+        if (IN_SET(state, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_NULL))
+                return;
+
+        if (state != DNS_TRANSACTION_SUCCESS && c->search_domain) {
+
+                r = dns_query_candidate_next_search_domain(c);
+                if (r < 0)
+                        goto fail;
+
+                if (r > 0) {
+                        /* OK, there's another search domain to try, let's do so. */
+
+                        r = dns_query_candidate_setup_transactions(c);
+                        if (r < 0)
+                                goto fail;
+
+                        if (r > 0) {
+                                /* New transactions where queued. Start them and wait */
+
+                                r = dns_query_candidate_go(c);
+                                if (r < 0)
+                                        goto fail;
+
+                                return;
+                        }
+                }
+
+        }
+
+        dns_query_ready(c->query);
+        return;
+
+fail:
+        log_warning_errno(r, "Failed to follow search domains: %m");
+        c->error_code = r;
+        dns_query_ready(c->query);
+}
+
+static void dns_query_stop(DnsQuery *q) {
+        DnsQueryCandidate *c;
+
+        assert(q);
+
+        q->timeout_event_source = sd_event_source_unref(q->timeout_event_source);
+
+        LIST_FOREACH(candidates_by_query, c, q->candidates)
+                dns_query_candidate_stop(c);
+}
+
 DnsQuery *dns_query_free(DnsQuery *q) {
         if (!q)
                 return NULL;
 
-        dns_query_stop(q);
-        set_free(q->transactions);
+        while (q->auxiliary_queries)
+                dns_query_free(q->auxiliary_queries);
+
+        if (q->auxiliary_for) {
+                assert(q->auxiliary_for->n_auxiliary_queries > 0);
+                q->auxiliary_for->n_auxiliary_queries--;
+                LIST_REMOVE(auxiliary_queries, q->auxiliary_for->auxiliary_queries, q);
+        }
+
+        while (q->candidates)
+                dns_query_candidate_free(q->candidates);
 
         dns_question_unref(q->question);
         dns_answer_unref(q->answer);
+        dns_search_domain_unref(q->answer_search_domain);
 
         sd_bus_message_unref(q->request);
         sd_bus_track_unref(q->bus_track);
@@ -75,7 +332,7 @@ int dns_query_new(Manager *m, DnsQuery **ret, DnsQuestion *question, int ifindex
         assert(m);
         assert(question);
 
-        r = dns_question_is_valid(question);
+        r = dns_question_is_valid_for_query(question);
         if (r < 0)
                 return r;
 
@@ -89,6 +346,8 @@ int dns_query_new(Manager *m, DnsQuery **ret, DnsQuestion *question, int ifindex
         q->question = dns_question_ref(question);
         q->ifindex = ifindex;
         q->flags = flags;
+        q->answer_family = AF_UNSPEC;
+        q->answer_protocol = _DNS_PROTOCOL_INVALID;
 
         for (i = 0; i < question->n_keys; i++) {
                 _cleanup_free_ char *p;
@@ -111,6 +370,29 @@ int dns_query_new(Manager *m, DnsQuery **ret, DnsQuestion *question, int ifindex
         return 0;
 }
 
+int dns_query_make_auxiliary(DnsQuery *q, DnsQuery *auxiliary_for) {
+        assert(q);
+        assert(auxiliary_for);
+
+        /* Ensure that that the query is not auxiliary yet, and
+         * nothing else is auxiliary to it either */
+        assert(!q->auxiliary_for);
+        assert(!q->auxiliary_queries);
+
+        /* Ensure that the unit we shall be made auxiliary for isn't
+         * auxiliary itself */
+        assert(!auxiliary_for->auxiliary_for);
+
+        if (auxiliary_for->n_auxiliary_queries >= AUXILIARY_QUERIES_MAX)
+                return -EAGAIN;
+
+        LIST_PREPEND(auxiliary_queries, auxiliary_for->auxiliary_queries, q);
+        q->auxiliary_for = auxiliary_for;
+
+        auxiliary_for->n_auxiliary_queries++;
+        return 0;
+}
+
 static void dns_query_complete(DnsQuery *q, DnsTransactionState state) {
         assert(q);
         assert(!IN_SET(state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
@@ -137,64 +419,40 @@ static int on_query_timeout(sd_event_source *s, usec_t usec, void *userdata) {
         return 0;
 }
 
-static int dns_query_add_transaction(DnsQuery *q, DnsScope *s, DnsResourceKey *key) {
-        DnsTransaction *t;
+static int dns_query_add_candidate(DnsQuery *q, DnsScope *s) {
+        DnsQueryCandidate *c;
         int r;
 
         assert(q);
         assert(s);
-        assert(key);
 
-        r = set_ensure_allocated(&q->transactions, NULL);
+        r = dns_query_candidate_new(&c, q, s);
         if (r < 0)
                 return r;
 
-        t = dns_scope_find_transaction(s, key, true);
-        if (!t) {
-                r = dns_transaction_new(&t, s, key);
-                if (r < 0)
-                        return r;
-        }
-
-        r = set_ensure_allocated(&t->queries, NULL);
+        /* If this a single-label domain on DNS, we might append a suitable search domain first. */
+        r = dns_scope_name_needs_search_domain(s, dns_question_first_name(q->question));
         if (r < 0)
-                goto gc;
-
-        r = set_put(t->queries, q);
-        if (r < 0)
-                goto gc;
+                goto fail;
+        if (r > 0) {
+                /* OK, we need a search domain now. Let's find one for this scope */
 
-        r = set_put(q->transactions, t);
-        if (r < 0) {
-                set_remove(t->queries, q);
-                goto gc;
+                r = dns_query_candidate_next_search_domain(c);
+                if (r <= 0) /* if there's no search domain, then we won't add any transaction. */
+                        goto fail;
         }
 
+        r = dns_query_candidate_setup_transactions(c);
+        if (r < 0)
+                goto fail;
+
         return 0;
 
-gc:
-        dns_transaction_gc(t);
+fail:
+        dns_query_candidate_free(c);
         return r;
 }
 
-static int dns_query_add_transaction_split(DnsQuery *q, DnsScope *s) {
-        unsigned i;
-        int r;
-
-        assert(q);
-        assert(s);
-
-        /* Create one transaction per question key */
-
-        for (i = 0; i < q->question->n_keys; i++) {
-                r = dns_query_add_transaction(q, s, q->question->keys[i]);
-                if (r < 0)
-                        return r;
-        }
-
-        return 0;
-}
-
 static int SYNTHESIZE_IFINDEX(int ifindex) {
 
         /* When the caller asked for resolving on a specific
@@ -597,9 +855,9 @@ static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) {
         q->answer = answer;
         answer = NULL;
 
-        q->answer_family = SYNTHESIZE_FAMILY(q->flags);
-        q->answer_protocol = SYNTHESIZE_PROTOCOL(q->flags);
         q->answer_rcode = DNS_RCODE_SUCCESS;
+        q->answer_protocol = SYNTHESIZE_PROTOCOL(q->flags);
+        q->answer_family = SYNTHESIZE_FAMILY(q->flags);
 
         *state = DNS_TRANSACTION_SUCCESS;
 
@@ -609,9 +867,8 @@ static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) {
 int dns_query_go(DnsQuery *q) {
         DnsScopeMatch found = DNS_SCOPE_NO;
         DnsScope *s, *first = NULL;
-        DnsTransaction *t;
+        DnsQueryCandidate *c;
         const char *name;
-        Iterator i;
         int r;
 
         assert(q);
@@ -622,7 +879,7 @@ int dns_query_go(DnsQuery *q) {
         assert(q->question);
         assert(q->question->n_keys > 0);
 
-        name = DNS_RESOURCE_KEY_NAME(q->question->keys[0]);
+        name = dns_question_first_name(q->question);
 
         LIST_FOREACH(scopes, s, q->manager->dns_scopes) {
                 DnsScopeMatch match;
@@ -655,7 +912,7 @@ int dns_query_go(DnsQuery *q) {
                 return 1;
         }
 
-        r = dns_query_add_transaction_split(q, first);
+        r = dns_query_add_candidate(q, first);
         if (r < 0)
                 goto fail;
 
@@ -669,7 +926,7 @@ int dns_query_go(DnsQuery *q) {
                 if (match != found)
                         continue;
 
-                r = dns_query_add_transaction_split(q, s);
+                r = dns_query_add_candidate(q, s);
                 if (r < 0)
                         goto fail;
         }
@@ -691,14 +948,13 @@ int dns_query_go(DnsQuery *q) {
         q->state = DNS_TRANSACTION_PENDING;
         q->block_ready++;
 
-        /* Start the transactions that are not started yet */
-        SET_FOREACH(t, q->transactions, i) {
-                if (t->state != DNS_TRANSACTION_NULL)
-                        continue;
-
-                r = dns_transaction_go(t);
-                if (r < 0)
+        /* Start the transactions */
+        LIST_FOREACH(candidates_by_query, c, q->candidates) {
+                r = dns_query_candidate_go(c);
+                if (r < 0) {
+                        q->block_ready--;
                         goto fail;
+                }
         }
 
         q->block_ready--;
@@ -711,132 +967,128 @@ fail:
         return r;
 }
 
-void dns_query_ready(DnsQuery *q) {
-        DnsTransaction *t;
+static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
         DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
-        _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
-        int rcode = 0;
-        DnsScope *scope = NULL;
-        bool pending = false;
+        DnsTransaction *t;
         Iterator i;
 
         assert(q);
-        assert(IN_SET(q->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
 
-        /* Note that this call might invalidate the query. Callers
-         * should hence not attempt to access the query or transaction
-         * after calling this function, unless the block_ready
-         * counter was explicitly bumped before doing so. */
-
-        if (q->block_ready > 0)
+        if (!c) {
+                dns_query_synthesize_reply(q, &state);
+                dns_query_complete(q, state);
                 return;
+        }
 
-        SET_FOREACH(t, q->transactions, i) {
+        SET_FOREACH(t, c->transactions, i) {
 
-                /* If we found a successful answer, ignore all answers from other scopes */
-                if (state == DNS_TRANSACTION_SUCCESS && t->scope != scope)
-                        continue;
+                switch (t->state) {
 
-                /* One of the transactions is still going on, let's maybe wait for it */
-                if (IN_SET(t->state, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_NULL)) {
-                        pending = true;
-                        continue;
-                }
+                case DNS_TRANSACTION_SUCCESS: {
+                        /* We found a successfuly reply, merge it into the answer */
+                        DnsAnswer *merged;
 
-                /* One of the transactions is successful, let's use
-                 * it, and copy its data out */
-                if (t->state == DNS_TRANSACTION_SUCCESS) {
-                        DnsAnswer *a;
-
-                        if (t->received) {
-                                rcode = DNS_PACKET_RCODE(t->received);
-                                a = t->received->answer;
-                        } else {
-                                rcode = t->cached_rcode;
-                                a = t->cached;
+                        merged = dns_answer_merge(q->answer, t->answer);
+                        if (!merged) {
+                                dns_query_complete(q, DNS_TRANSACTION_RESOURCES);
+                                return;
                         }
 
-                        if (state == DNS_TRANSACTION_SUCCESS) {
-                                DnsAnswer *merged;
+                        dns_answer_unref(q->answer);
+                        q->answer = merged;
+                        q->answer_rcode = t->answer_rcode;
+
+                        state = DNS_TRANSACTION_SUCCESS;
+                        break;
+                }
+
+                case DNS_TRANSACTION_PENDING:
+                case DNS_TRANSACTION_NULL:
+                case DNS_TRANSACTION_ABORTED:
+                        /* Ignore transactions that didn't complete */
+                        continue;
+
+                default:
+                        /* Any kind of failure? Store the data away,
+                         * if there's nothing stored yet. */
 
-                                merged = dns_answer_merge(answer, a);
-                                if (!merged) {
-                                        dns_query_complete(q, DNS_TRANSACTION_RESOURCES);
-                                        return;
-                                }
+                        if (state != DNS_TRANSACTION_SUCCESS) {
 
-                                dns_answer_unref(answer);
-                                answer = merged;
-                        } else {
-                                dns_answer_unref(answer);
-                                answer = dns_answer_ref(a);
+                                dns_answer_unref(q->answer);
+                                q->answer = dns_answer_ref(t->answer);
+                                q->answer_rcode = t->answer_rcode;
+
+                                state = t->state;
                         }
 
-                        scope = t->scope;
-                        state = DNS_TRANSACTION_SUCCESS;
-                        continue;
+                        break;
                 }
+        }
 
-                /* One of the transactions has failed, let's see
-                 * whether we find anything better, but if not, return
-                 * its response data */
-                if (state != DNS_TRANSACTION_SUCCESS && t->state == DNS_TRANSACTION_FAILURE) {
-                        DnsAnswer *a;
-
-                        if (t->received) {
-                                rcode = DNS_PACKET_RCODE(t->received);
-                                a = t->received->answer;
-                        } else {
-                                rcode = t->cached_rcode;
-                                a = t->cached;
-                        }
+        q->answer_protocol = c->scope->protocol;
+        q->answer_family = c->scope->family;
 
-                        dns_answer_unref(answer);
-                        answer = dns_answer_ref(a);
+        dns_search_domain_unref(q->answer_search_domain);
+        q->answer_search_domain = dns_search_domain_ref(c->search_domain);
 
-                        scope = t->scope;
-                        state = DNS_TRANSACTION_FAILURE;
-                        continue;
-                }
+        dns_query_synthesize_reply(q, &state);
+        dns_query_complete(q, state);
+}
 
-                if (state == DNS_TRANSACTION_NO_SERVERS && t->state != DNS_TRANSACTION_NO_SERVERS)
-                        state = t->state;
-        }
+void dns_query_ready(DnsQuery *q) {
+
+        DnsQueryCandidate *bad = NULL, *c;
+        bool pending = false;
 
-        if (pending) {
+        assert(q);
+        assert(IN_SET(q->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
 
-                /* If so far we weren't successful, and there's
-                 * something still pending, then wait for it */
-                if (state != DNS_TRANSACTION_SUCCESS)
+        /* Note that this call might invalidate the query. Callers
+         * should hence not attempt to access the query or transaction
+         * after calling this function, unless the block_ready
+         * counter was explicitly bumped before doing so. */
+
+        if (q->block_ready > 0)
+                return;
+
+        LIST_FOREACH(candidates_by_query, c, q->candidates) {
+                DnsTransactionState state;
+
+                state = dns_query_candidate_state(c);
+                switch (state) {
+
+                case DNS_TRANSACTION_SUCCESS:
+                        /* One of the transactions is successful,
+                         * let's use it, and copy its data out */
+                        dns_query_accept(q, c);
                         return;
 
-                /* If we already were successful, then only wait for
-                 * other transactions on the same scope to finish. */
-                SET_FOREACH(t, q->transactions, i) {
-                        if (t->scope == scope && IN_SET(t->state, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_NULL))
-                                return;
-                }
-        }
+                case DNS_TRANSACTION_PENDING:
+                case DNS_TRANSACTION_NULL:
+                        /* One of the transactions is still going on, let's maybe wait for it */
+                        pending = true;
+                        break;
 
-        if (IN_SET(state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_FAILURE)) {
-                q->answer = dns_answer_ref(answer);
-                q->answer_rcode = rcode;
-                q->answer_protocol = scope ? scope->protocol : _DNS_PROTOCOL_INVALID;
-                q->answer_family = scope ? scope->family : AF_UNSPEC;
+                default:
+                        /* Any kind of failure */
+                        bad = c;
+                        break;
+                }
         }
 
-        /* Try to synthesize a reply if we couldn't resolve something. */
-        dns_query_synthesize_reply(q, &state);
+        if (pending)
+                return;
 
-        dns_query_complete(q, state);
+        dns_query_accept(q, bad);
 }
 
-int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname) {
+static int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname) {
         _cleanup_(dns_question_unrefp) DnsQuestion *nq = NULL;
         int r;
 
         assert(q);
 
+        q->n_cname_redirects ++;
         if (q->n_cname_redirects > CNAME_MAX)
                 return -ELOOP;
 
@@ -848,14 +1100,66 @@ int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname) {
         q->question = nq;
         nq = NULL;
 
-        q->n_cname_redirects++;
-
         dns_query_stop(q);
         q->state = DNS_TRANSACTION_NULL;
 
         return 0;
 }
 
+int dns_query_process_cname(DnsQuery *q) {
+        _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *cname = NULL;
+        DnsResourceRecord *rr;
+        int r;
+
+        assert(q);
+
+        if (q->state != DNS_TRANSACTION_SUCCESS)
+                return 0;
+
+        DNS_ANSWER_FOREACH(rr, q->answer) {
+
+                r = dns_question_matches_rr(q->question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
+                if (r < 0)
+                        return r;
+                if (r > 0)
+                        return 0; /* The answer matches directly, no need to follow cnames */
+
+                r = dns_question_matches_cname(q->question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
+                if (r < 0)
+                        return r;
+                if (r > 0 && !cname)
+                        cname = dns_resource_record_ref(rr);
+        }
+
+        if (!cname)
+                return 0; /* No cname to follow */
+
+        if (q->flags & SD_RESOLVED_NO_CNAME)
+                return -ELOOP;
+
+        /* OK, let's actually follow the CNAME */
+        r = dns_query_cname_redirect(q, cname);
+        if (r < 0)
+                return r;
+
+        /* Let's see if the answer can already answer the new
+         * redirected question */
+        DNS_ANSWER_FOREACH(rr, q->answer) {
+                r = dns_question_matches_rr(q->question, rr, NULL);
+                if (r < 0)
+                        return r;
+                if (r > 0)
+                        return 0; /* It can answer it, yay! */
+        }
+
+        /* OK, it cannot, let's begin with the new query */
+        r = dns_query_go(q);
+        if (r < 0)
+                return r;
+
+        return 1; /* We return > 0, if we restarted the query for a new cname */
+}
+
 static int on_bus_track(sd_bus_track *t, void *userdata) {
         DnsQuery *q = userdata;
 
index e7063d967885f0478d536bbd09aeb0ca2a49777a..a9d7904a8dc9b2eb1583a84ac867718bdf630094 100644 (file)
 
 
 #include "sd-bus.h"
+
 #include "set.h"
 
+typedef struct DnsQueryCandidate DnsQueryCandidate;
 typedef struct DnsQuery DnsQuery;
 
-#include "resolved-dns-question.h"
 #include "resolved-dns-answer.h"
+#include "resolved-dns-question.h"
 #include "resolved-dns-stream.h"
+#include "resolved-dns-search-domain.h"
+
+struct DnsQueryCandidate {
+        DnsQuery *query;
+        DnsScope *scope;
+
+        DnsSearchDomain *search_domain;
+
+        int error_code;
+        Set *transactions;
+
+        LIST_FIELDS(DnsQueryCandidate, candidates_by_query);
+        LIST_FIELDS(DnsQueryCandidate, candidates_by_scope);
+};
 
 struct DnsQuery {
         Manager *manager;
-        DnsQuestion *question;
 
+        /* When resolving a service, we first create a TXT+SRV query,
+         * and then for the hostnames we discover auxiliary A+AAAA
+         * queries. This pointer always points from the auxiliary
+         * queries back to the TXT+SRV query. */
+        DnsQuery *auxiliary_for;
+        LIST_HEAD(DnsQuery, auxiliary_queries);
+        unsigned n_auxiliary_queries;
+        int auxiliary_result;
+
+        DnsQuestion *question;
         uint64_t flags;
         int ifindex;
 
         DnsTransactionState state;
         unsigned n_cname_redirects;
 
+        LIST_HEAD(DnsQueryCandidate, candidates);
         sd_event_source *timeout_event_source;
 
         /* Discovered data */
         DnsAnswer *answer;
-        int answer_family;
-        DnsProtocol answer_protocol;
         int answer_rcode;
+        DnsProtocol answer_protocol;
+        int answer_family;
+        DnsSearchDomain *answer_search_domain;
 
         /* Bus client information */
         sd_bus_message *request;
         int request_family;
-        const char *request_hostname;
+        bool request_address_valid;
         union in_addr_union request_address;
+        unsigned block_all_complete;
 
         /* Completion callback */
         void (*complete)(DnsQuery* q);
         unsigned block_ready;
 
-        Set *transactions;
-
         sd_bus_track *bus_track;
 
         LIST_FIELDS(DnsQuery, queries);
+        LIST_FIELDS(DnsQuery, auxiliary_queries);
 };
 
+DnsQueryCandidate* dns_query_candidate_free(DnsQueryCandidate *c);
+void dns_query_candidate_ready(DnsQueryCandidate *c);
+
 int dns_query_new(Manager *m, DnsQuery **q, DnsQuestion *question, int family, uint64_t flags);
 DnsQuery *dns_query_free(DnsQuery *q);
 
+int dns_query_make_auxiliary(DnsQuery *q, DnsQuery *auxiliary_for);
+
 int dns_query_go(DnsQuery *q);
 void dns_query_ready(DnsQuery *q);
 
-int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname);
+int dns_query_process_cname(DnsQuery *q);
 
 int dns_query_bus_track(DnsQuery *q, sd_bus_message *m);
 
index 48951221dc9ccbd090c3573d469aa40e9888968a..3249448d3b73ba145badcabd850c41c6beba8002 100644 (file)
@@ -89,7 +89,7 @@ int dns_question_add(DnsQuestion *q, DnsResourceKey *key) {
         return 0;
 }
 
-int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr) {
+int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain) {
         unsigned i;
         int r;
 
@@ -99,7 +99,7 @@ int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr) {
                 return 0;
 
         for (i = 0; i < q->n_keys; i++) {
-                r = dns_resource_key_match_rr(q->keys[i], rr);
+                r = dns_resource_key_match_rr(q->keys[i], rr, search_domain);
                 if (r != 0)
                         return r;
         }
@@ -107,7 +107,7 @@ int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr) {
         return 0;
 }
 
-int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr) {
+int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain) {
         unsigned i;
         int r;
 
@@ -117,7 +117,7 @@ int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr) {
                 return 0;
 
         for (i = 0; i < q->n_keys; i++) {
-                r = dns_resource_key_match_cname(q->keys[i], rr);
+                r = dns_resource_key_match_cname(q->keys[i], rr, search_domain);
                 if (r != 0)
                         return r;
         }
@@ -125,7 +125,7 @@ int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr) {
         return 0;
 }
 
-int dns_question_is_valid(DnsQuestion *q) {
+int dns_question_is_valid_for_query(DnsQuestion *q) {
         const char *name;
         unsigned i;
         int r;
@@ -155,50 +155,6 @@ int dns_question_is_valid(DnsQuestion *q) {
         return 1;
 }
 
-int dns_question_is_superset(DnsQuestion *q, DnsQuestion *other) {
-        unsigned j;
-        int r;
-
-        /* Checks if all keys in "other" are also contained in "q" */
-
-        if (!other)
-                return 1;
-
-        for (j = 0; j < other->n_keys; j++) {
-                DnsResourceKey *b = other->keys[j];
-                bool found = false;
-                unsigned i;
-
-                if (!q)
-                        return 0;
-
-                for (i = 0; i < q->n_keys; i++) {
-                        DnsResourceKey *a = q->keys[i];
-
-                        r = dns_name_equal(DNS_RESOURCE_KEY_NAME(a), DNS_RESOURCE_KEY_NAME(b));
-                        if (r < 0)
-                                return r;
-
-                        if (r == 0)
-                                continue;
-
-                        if (a->class != b->class && a->class != DNS_CLASS_ANY)
-                                continue;
-
-                        if (a->type != b->type && a->type != DNS_TYPE_ANY)
-                                continue;
-
-                        found = true;
-                        break;
-                }
-
-                if (!found)
-                        return 0;
-        }
-
-        return 1;
-}
-
 int dns_question_contains(DnsQuestion *a, DnsResourceKey *k) {
         unsigned j;
         int r;
@@ -251,6 +207,7 @@ int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname,
 
         assert(cname);
         assert(ret);
+        assert(IN_SET(cname->key->type, DNS_TYPE_CNAME, DNS_TYPE_DNAME));
 
         if (!q) {
                 n = dns_question_new(0);
@@ -263,7 +220,22 @@ int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname,
         }
 
         for (i = 0; i < q->n_keys; i++) {
-                r = dns_name_equal(DNS_RESOURCE_KEY_NAME(q->keys[i]), cname->cname.name);
+                _cleanup_free_ char *destination = NULL;
+                const char *d;
+
+                if (cname->key->type == DNS_TYPE_CNAME)
+                        d = cname->cname.name;
+                else {
+                        r = dns_name_change_suffix(DNS_RESOURCE_KEY_NAME(q->keys[i]), DNS_RESOURCE_KEY_NAME(cname->key), cname->dname.name, &destination);
+                        if (r < 0)
+                                return r;
+                        if (r == 0)
+                                continue;
+
+                        d = destination;
+                }
+
+                r = dns_name_equal(DNS_RESOURCE_KEY_NAME(q->keys[i]), d);
                 if (r < 0)
                         return r;
 
@@ -301,3 +273,131 @@ int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname,
 
         return 1;
 }
+
+const char *dns_question_first_name(DnsQuestion *q) {
+
+        if (!q)
+                return NULL;
+
+        if (q->n_keys < 1)
+                return NULL;
+
+        return DNS_RESOURCE_KEY_NAME(q->keys[0]);
+}
+
+int dns_question_new_address(DnsQuestion **ret, int family, const char *name) {
+        _cleanup_(dns_question_unrefp) DnsQuestion *q = NULL;
+        int r;
+
+        assert(ret);
+        assert(name);
+
+        if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
+                return -EAFNOSUPPORT;
+
+        q = dns_question_new(family == AF_UNSPEC ? 2 : 1);
+        if (!q)
+                return -ENOMEM;
+
+        if (family != AF_INET6) {
+                _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
+
+                key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, name);
+                if (!key)
+                        return -ENOMEM;
+
+                r = dns_question_add(q, key);
+                if (r < 0)
+                        return r;
+        }
+
+        if (family != AF_INET) {
+                _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
+
+                key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, name);
+                if (!key)
+                        return -ENOMEM;
+
+                r = dns_question_add(q, key);
+                if (r < 0)
+                        return r;
+        }
+
+        *ret = q;
+        q = NULL;
+
+        return 0;
+}
+
+int dns_question_new_reverse(DnsQuestion **ret, int family, const union in_addr_union *a) {
+        _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
+        _cleanup_(dns_question_unrefp) DnsQuestion *q = NULL;
+        _cleanup_free_ char *reverse = NULL;
+        int r;
+
+        assert(ret);
+        assert(a);
+
+        if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
+                return -EAFNOSUPPORT;
+
+        r = dns_name_reverse(family, a, &reverse);
+        if (r < 0)
+                return r;
+
+        q = dns_question_new(1);
+        if (!q)
+                return -ENOMEM;
+
+        key = dns_resource_key_new_consume(DNS_CLASS_IN, DNS_TYPE_PTR, reverse);
+        if (!key)
+                return -ENOMEM;
+
+        reverse = NULL;
+
+        r = dns_question_add(q, key);
+        if (r < 0)
+                return r;
+
+        *ret = q;
+        q = NULL;
+
+        return 0;
+}
+
+int dns_question_new_service(DnsQuestion **ret, const char *name, bool with_txt) {
+        _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
+        _cleanup_(dns_question_unrefp) DnsQuestion *q = NULL;
+        int r;
+
+        assert(ret);
+        assert(name);
+
+        q = dns_question_new(1 + with_txt);
+        if (!q)
+                return -ENOMEM;
+
+        key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_SRV, name);
+        if (!key)
+                return -ENOMEM;
+
+        r = dns_question_add(q, key);
+        if (r < 0)
+                return r;
+
+        if (with_txt) {
+                dns_resource_key_unref(key);
+                key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_TXT, name);
+                if (!key)
+                        return -ENOMEM;
+
+                r = dns_question_add(q, key);
+                if (r < 0)
+                        return r;
+        }
+
+        *ret = q;
+        q = NULL;
+
+        return 0;
+}
index 13cd1f20f31ea893afbd6ede50e62008deec1094..e77116c03a51e571f98e39ae7f0bc8d7b45a1d46 100644 (file)
@@ -37,15 +37,28 @@ DnsQuestion *dns_question_new(unsigned n);
 DnsQuestion *dns_question_ref(DnsQuestion *q);
 DnsQuestion *dns_question_unref(DnsQuestion *q);
 
+int dns_question_new_address(DnsQuestion **ret, int family, const char *name);
+int dns_question_new_reverse(DnsQuestion **ret, int family, const union in_addr_union *a);
+int dns_question_new_service(DnsQuestion **ret, const char *name, bool with_txt);
+
 int dns_question_add(DnsQuestion *q, DnsResourceKey *key);
 
-int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr);
-int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr);
-int dns_question_is_valid(DnsQuestion *q);
-int dns_question_is_superset(DnsQuestion *q, DnsQuestion *other);
+int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain);
+int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr, const char* search_domain);
+int dns_question_is_valid_for_query(DnsQuestion *q);
 int dns_question_contains(DnsQuestion *a, DnsResourceKey *k);
 int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b);
 
 int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname, DnsQuestion **ret);
 
+const char *dns_question_first_name(DnsQuestion *q);
+
 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQuestion*, dns_question_unref);
+
+#define DNS_QUESTION_FOREACH(key, q)                                    \
+        for (unsigned _i = ({                                           \
+                                (key) = ((q) && (q)->n_keys > 0) ? (q)->keys[0] : NULL; \
+                                0;                                      \
+                        });                                             \
+             (q) && ((_i) < (q)->n_keys);                               \
+             _i++, (key) = (_i < (q)->n_keys ? (q)->keys[_i] : NULL))
index ba2ea686f33ec13d75841f624590fbde71d1ca7e..4a1abb0cdca477b8365ee8ac5868588eccb18b3d 100644 (file)
@@ -57,10 +57,61 @@ DnsResourceKey* dns_resource_key_new_cname(const DnsResourceKey *key) {
 }
 
 DnsResourceKey* dns_resource_key_new_redirect(const DnsResourceKey *key, const DnsResourceRecord *cname) {
+        int r;
+
         assert(key);
         assert(cname);
 
-        return dns_resource_key_new(key->class, key->type, cname->cname.name);
+        assert(IN_SET(cname->key->type, DNS_TYPE_CNAME, DNS_TYPE_DNAME));
+
+        if (cname->key->type == DNS_TYPE_CNAME)
+                return dns_resource_key_new(key->class, key->type, cname->cname.name);
+        else {
+                DnsResourceKey *k;
+                char *destination = NULL;
+
+                r = dns_name_change_suffix(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(cname->key), cname->dname.name, &destination);
+                if (r < 0)
+                        return NULL;
+                if (r == 0)
+                        return dns_resource_key_ref((DnsResourceKey*) key);
+
+                k = dns_resource_key_new_consume(key->class, key->type, destination);
+                if (!k) {
+                        free(destination);
+                        return NULL;
+                }
+
+                return k;
+        }
+}
+
+int dns_resource_key_new_append_suffix(DnsResourceKey **ret, DnsResourceKey *key, char *name) {
+        DnsResourceKey *new_key;
+        char *joined;
+        int r;
+
+        assert(ret);
+        assert(key);
+        assert(name);
+
+        if (dns_name_is_root(name)) {
+                *ret = dns_resource_key_ref(key);
+                return 0;
+        }
+
+        r = dns_name_concat(DNS_RESOURCE_KEY_NAME(key), name, &joined);
+        if (r < 0)
+                return r;
+
+        new_key = dns_resource_key_new_consume(key->class, key->type, joined);
+        if (!new_key) {
+                free(joined);
+                return -ENOMEM;
+        }
+
+        *ret = new_key;
+        return 0;
 }
 
 DnsResourceKey* dns_resource_key_new_consume(uint16_t class, uint16_t type, char *name) {
@@ -122,30 +173,73 @@ int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b) {
         return 1;
 }
 
-int dns_resource_key_match_rr(const DnsResourceKey *key, const DnsResourceRecord *rr) {
+int dns_resource_key_match_rr(const DnsResourceKey *key, const DnsResourceRecord *rr, const char *search_domain) {
+        int r;
+
         assert(key);
         assert(rr);
 
+        /* Checks if an rr matches the specified key. If a search
+         * domain is specified, it will also be checked if the key
+         * with the search domain suffixed might match the RR. */
+
         if (rr->key->class != key->class && key->class != DNS_CLASS_ANY)
                 return 0;
 
         if (rr->key->type != key->type && key->type != DNS_TYPE_ANY)
                 return 0;
 
-        return dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key));
+        r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key));
+        if (r != 0)
+                return r;
+
+        if (search_domain) {
+                _cleanup_free_ char *joined = NULL;
+
+                r = dns_name_concat(DNS_RESOURCE_KEY_NAME(key), search_domain, &joined);
+                if (r < 0)
+                        return r;
+
+                return dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), joined);
+        }
+
+        return 0;
 }
 
-int dns_resource_key_match_cname(const DnsResourceKey *key, const DnsResourceRecord *rr) {
+int dns_resource_key_match_cname(const DnsResourceKey *key, const DnsResourceRecord *rr, const char *search_domain) {
+        int r;
+
         assert(key);
         assert(rr);
 
         if (rr->key->class != key->class && key->class != DNS_CLASS_ANY)
                 return 0;
 
-        if (rr->key->type != DNS_TYPE_CNAME)
+        if (rr->key->type == DNS_TYPE_CNAME)
+                r = dns_name_equal(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(rr->key));
+        else if (rr->key->type == DNS_TYPE_DNAME)
+                r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(rr->key));
+        else
                 return 0;
 
-        return dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key));
+        if (r != 0)
+                return r;
+
+        if (search_domain) {
+                _cleanup_free_ char *joined = NULL;
+
+                r = dns_name_concat(DNS_RESOURCE_KEY_NAME(key), search_domain, &joined);
+                if (r < 0)
+                        return r;
+
+                if (rr->key->type == DNS_TYPE_CNAME)
+                        return dns_name_equal(joined, DNS_RESOURCE_KEY_NAME(rr->key));
+                else if (rr->key->type == DNS_TYPE_DNAME)
+                        return dns_name_endswith(joined, DNS_RESOURCE_KEY_NAME(rr->key));
+        }
+
+        return 0;
+
 }
 
 static void dns_resource_key_hash_func(const void *i, struct siphash *state) {
@@ -273,7 +367,7 @@ DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr) {
 
                 case DNS_TYPE_TXT:
                 case DNS_TYPE_SPF:
-                        strv_free(rr->txt.strings);
+                        dns_txt_item_free_all(rr->txt.items);
                         break;
 
                 case DNS_TYPE_SOA:
@@ -430,7 +524,7 @@ int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecor
 
         case DNS_TYPE_SPF: /* exactly the same as TXT */
         case DNS_TYPE_TXT:
-                return strv_equal(a->txt.strings, b->txt.strings);
+                return dns_txt_item_equal(a->txt.items, b->txt.items);
 
         case DNS_TYPE_A:
                 return memcmp(&a->a.in_addr, &b->a.in_addr, sizeof(struct in_addr)) == 0;
@@ -600,6 +694,43 @@ static char *format_types(Bitmap *types) {
         return strjoin("( ", str, " )", NULL);
 }
 
+static char *format_txt(DnsTxtItem *first) {
+        DnsTxtItem *i;
+        size_t c = 1;
+        char *p, *s;
+
+        LIST_FOREACH(items, i, first)
+                c += i->length * 4 + 3;
+
+        p = s = new(char, c);
+        if (!s)
+                return NULL;
+
+        LIST_FOREACH(items, i, first) {
+                size_t j;
+
+                if (i != first)
+                        *(p++) = ' ';
+
+                *(p++) = '"';
+
+                for (j = 0; j < i->length; j++) {
+                        if (i->data[j] < ' ' || i->data[j] == '"' || i->data[j] >= 127) {
+                                *(p++) = '\\';
+                                *(p++) = '0' + (i->data[j] / 100);
+                                *(p++) = '0' + ((i->data[j] / 10) % 10);
+                                *(p++) = '0' + (i->data[j] % 10);
+                        } else
+                                *(p++) = i->data[j];
+                }
+
+                *(p++) = '"';
+        }
+
+        *p = 0;
+        return s;
+}
+
 int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
         _cleanup_free_ char *k = NULL, *t = NULL;
         char *s;
@@ -642,14 +773,13 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
 
         case DNS_TYPE_SPF: /* exactly the same as TXT */
         case DNS_TYPE_TXT:
-                t = strv_join_quoted(rr->txt.strings);
+                t = format_txt(rr->txt.items);
                 if (!t)
                         return -ENOMEM;
 
                 s = strjoin(k, " ", t, NULL);
                 if (!s)
                         return -ENOMEM;
-
                 break;
 
         case DNS_TYPE_A: {
@@ -890,3 +1020,32 @@ int dns_class_from_string(const char *s, uint16_t *class) {
 
         return 0;
 }
+
+DnsTxtItem *dns_txt_item_free_all(DnsTxtItem *i) {
+        DnsTxtItem *n;
+
+        if (!i)
+                return NULL;
+
+        n = i->items_next;
+
+        free(i);
+        return dns_txt_item_free_all(n);
+}
+
+bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b) {
+
+        if (!a != !b)
+                return false;
+
+        if (!a)
+                return true;
+
+        if (a->length != b->length)
+                return false;
+
+        if (memcmp(a->data, b->data, a->length) != 0)
+                return false;
+
+        return dns_txt_item_equal(a->items_next, b->items_next);
+}
index 9e2207c0aac128992cd6ec00664563f81fd5f21c..f8066c06a6db726cb0ff36965a9bf1cac4a0458f 100644 (file)
 #include <netinet/in.h>
 
 #include "bitmap.h"
+#include "dns-type.h"
 #include "hashmap.h"
 #include "in-addr-util.h"
-#include "dns-type.h"
+#include "list.h"
 
 typedef struct DnsResourceKey DnsResourceKey;
 typedef struct DnsResourceRecord DnsResourceRecord;
+typedef struct DnsTxtItem DnsTxtItem;
 
 /* DNS record classes, see RFC 1035 */
 enum {
@@ -45,6 +47,12 @@ struct DnsResourceKey {
         char *_name; /* don't access directy, use DNS_RESOURCE_KEY_NAME()! */
 };
 
+struct DnsTxtItem {
+        size_t length;
+        LIST_FIELDS(DnsTxtItem, items);
+        uint8_t data[];
+};
+
 struct DnsResourceRecord {
         unsigned n_ref;
         DnsResourceKey *key;
@@ -73,7 +81,7 @@ struct DnsResourceRecord {
                 } hinfo;
 
                 struct {
-                        char **strings;
+                        DnsTxtItem *items;
                 } txt, spf;
 
                 struct {
@@ -178,13 +186,15 @@ static inline const char* DNS_RESOURCE_KEY_NAME(const DnsResourceKey *key) {
 
 DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char *name);
 DnsResourceKey* dns_resource_key_new_cname(const DnsResourceKey *key);
+DnsResourceKey* dns_resource_key_new_dname(const DnsResourceKey *key);
 DnsResourceKey* dns_resource_key_new_redirect(const DnsResourceKey *key, const DnsResourceRecord *cname);
+int dns_resource_key_new_append_suffix(DnsResourceKey **ret, DnsResourceKey *key, char *name);
 DnsResourceKey* dns_resource_key_new_consume(uint16_t class, uint16_t type, char *name);
 DnsResourceKey* dns_resource_key_ref(DnsResourceKey *key);
 DnsResourceKey* dns_resource_key_unref(DnsResourceKey *key);
 int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b);
-int dns_resource_key_match_rr(const DnsResourceKey *key, const DnsResourceRecord *rr);
-int dns_resource_key_match_cname(const DnsResourceKey *key, const DnsResourceRecord *rr);
+int dns_resource_key_match_rr(const DnsResourceKey *key, const DnsResourceRecord *rr, const char *search_domain);
+int dns_resource_key_match_cname(const DnsResourceKey *key, const DnsResourceRecord *rr, const char *search_domain);
 int dns_resource_key_to_string(const DnsResourceKey *key, char **ret);
 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsResourceKey*, dns_resource_key_unref);
 
@@ -198,6 +208,9 @@ int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecor
 int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret);
 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsResourceRecord*, dns_resource_record_unref);
 
+DnsTxtItem *dns_txt_item_free_all(DnsTxtItem *i);
+bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b);
+
 const char *dns_class_to_string(uint16_t type);
 int dns_class_from_string(const char *name, uint16_t *class);
 
index b15370b017b8e9d5c06c74ddbf369608364c7233..fc4ae57ce0805149bacbb80bfccecdd9542d5726 100644 (file)
@@ -69,18 +69,12 @@ int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int
         return 0;
 }
 
-DnsScope* dns_scope_free(DnsScope *s) {
-        DnsTransaction *t;
-        DnsResourceRecord *rr;
-
-        if (!s)
-                return NULL;
-
-        log_debug("Removing scope on link %s, protocol %s, family %s", s->link ? s->link->name : "*", dns_protocol_to_string(s->protocol), s->family == AF_UNSPEC ? "*" : af_to_name(s->family));
+static void dns_scope_abort_transactions(DnsScope *s) {
+        assert(s);
 
-        dns_scope_llmnr_membership(s, false);
+        while (s->transactions) {
+                DnsTransaction *t = s->transactions;
 
-        while ((t = hashmap_steal_first(s->transactions))) {
                 /* Abort the transaction, but make sure it is not
                  * freed while we still look at it */
 
@@ -90,8 +84,23 @@ DnsScope* dns_scope_free(DnsScope *s) {
 
                 dns_transaction_free(t);
         }
+}
 
-        hashmap_free(s->transactions);
+DnsScope* dns_scope_free(DnsScope *s) {
+        DnsResourceRecord *rr;
+
+        if (!s)
+                return NULL;
+
+        log_debug("Removing scope on link %s, protocol %s, family %s", s->link ? s->link->name : "*", dns_protocol_to_string(s->protocol), s->family == AF_UNSPEC ? "*" : af_to_name(s->family));
+
+        dns_scope_llmnr_membership(s, false);
+        dns_scope_abort_transactions(s);
+
+        while (s->query_candidates)
+                dns_query_candidate_free(s->query_candidates);
+
+        hashmap_free(s->transactions_by_key);
 
         while ((rr = ordered_hashmap_steal_first(s->conflict_queue)))
                 dns_resource_record_unref(rr);
@@ -103,7 +112,6 @@ DnsScope* dns_scope_free(DnsScope *s) {
         dns_zone_flush(&s->zone);
 
         LIST_REMOVE(scopes, s->manager->dns_scopes, s);
-        strv_free(s->domains);
         free(s);
 
         return NULL;
@@ -136,11 +144,11 @@ void dns_scope_next_dns_server(DnsScope *s) {
 void dns_scope_packet_received(DnsScope *s, usec_t rtt) {
         assert(s);
 
-        if (rtt > s->max_rtt) {
-                s->max_rtt = rtt;
-                s->resend_timeout = MIN(MAX(MULTICAST_RESEND_TIMEOUT_MIN_USEC, s->max_rtt * 2),
-                                        MULTICAST_RESEND_TIMEOUT_MAX_USEC);
-        }
+        if (rtt <= s->max_rtt)
+                return;
+
+        s->max_rtt = rtt;
+        s->resend_timeout = MIN(MAX(MULTICAST_RESEND_TIMEOUT_MIN_USEC, s->max_rtt * 2), MULTICAST_RESEND_TIMEOUT_MAX_USEC);
 }
 
 void dns_scope_packet_lost(DnsScope *s, usec_t usec) {
@@ -323,7 +331,7 @@ int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *add
 }
 
 DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain) {
-        char **i;
+        DnsSearchDomain *d;
 
         assert(s);
         assert(domain);
@@ -334,7 +342,7 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co
         if ((SD_RESOLVED_FLAGS_MAKE(s->protocol, s->family) & flags) == 0)
                 return DNS_SCOPE_NO;
 
-        if (dns_name_root(domain) != 0)
+        if (dns_name_is_root(domain))
                 return DNS_SCOPE_NO;
 
         /* Never resolve any loopback hostname or IP address via DNS,
@@ -345,15 +353,22 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co
             dns_name_equal(domain, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0)
                 return DNS_SCOPE_NO;
 
-        STRV_FOREACH(i, s->domains)
-                if (dns_name_endswith(domain, *i) > 0)
+        /* Always honour search domains for routing queries. Note that
+         * we return DNS_SCOPE_YES here, rather than just
+         * DNS_SCOPE_MAYBE, which means wildcard scopes won't be
+         * considered anymore. */
+        LIST_FOREACH(domains, d, dns_scope_get_search_domains(s))
+                if (dns_name_endswith(domain, d->name) > 0)
                         return DNS_SCOPE_YES;
 
         switch (s->protocol) {
+
         case DNS_PROTOCOL_DNS:
-                if (dns_name_endswith(domain, "254.169.in-addr.arpa") == 0 &&
-                    dns_name_endswith(domain, "0.8.e.f.ip6.arpa") == 0 &&
-                    dns_name_single_label(domain) == 0)
+
+                if ((!dns_name_is_single_label(domain) ||
+                     (!(flags & SD_RESOLVED_NO_SEARCH) && dns_scope_has_search_domains(s))) &&
+                    dns_name_endswith(domain, "254.169.in-addr.arpa") == 0 &&
+                    dns_name_endswith(domain, "0.8.e.f.ip6.arpa") == 0)
                         return DNS_SCOPE_MAYBE;
 
                 return DNS_SCOPE_NO;
@@ -371,7 +386,7 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co
         case DNS_PROTOCOL_LLMNR:
                 if ((s->family == AF_INET && dns_name_endswith(domain, "in-addr.arpa") > 0) ||
                     (s->family == AF_INET6 && dns_name_endswith(domain, "ip6.arpa") > 0) ||
-                    (dns_name_single_label(domain) > 0 && /* only resolve single label names via LLMNR */
+                    (dns_name_is_single_label(domain) && /* only resolve single label names via LLMNR */
                      !is_gateway_hostname(domain) && /* don't resolve "gateway" with LLMNR, let nss-myhostname handle this */
                      manager_is_own_hostname(s->manager, domain) <= 0))  /* never resolve the local hostname via LLMNR */
                         return DNS_SCOPE_MAYBE;
@@ -543,6 +558,7 @@ static void dns_scope_verify_conflicts(DnsScope *s, DnsPacket *p) {
 void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
         _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
         _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
+        DnsResourceKey *key = NULL;
         bool tentative = false;
         int r, fd;
 
@@ -576,7 +592,10 @@ void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
                 return;
         }
 
-        r = dns_zone_lookup(&s->zone, p->question, &answer, &soa, &tentative);
+        assert(p->question->n_keys == 1);
+        key = p->question->keys[0];
+
+        r = dns_zone_lookup(&s->zone, key, &answer, &soa, &tentative);
         if (r < 0) {
                 log_debug_errno(r, "Failed to lookup key: %m");
                 return;
@@ -634,7 +653,7 @@ DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsResourceKey *key,
 
         /* Try to find an ongoing transaction that is a equal to the
          * specified question */
-        t = hashmap_get(scope->transactions, key);
+        t = hashmap_get(scope->transactions_by_key, key);
         if (!t)
                 return NULL;
 
@@ -642,7 +661,7 @@ DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsResourceKey *key,
          * data instead of a real packet, if that's requested. */
         if (!cache_ok &&
             IN_SET(t->state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_FAILURE) &&
-            !t->received)
+            t->answer_source != DNS_TRANSACTION_NETWORK)
                 return NULL;
 
         return t;
@@ -846,3 +865,45 @@ void dns_scope_dump(DnsScope *s, FILE *f) {
                 dns_cache_dump(&s->cache, f);
         }
 }
+
+DnsSearchDomain *dns_scope_get_search_domains(DnsScope *s) {
+        assert(s);
+
+        /* Returns the list of *local* search domains -- not the
+         * global ones. */
+
+        if (s->protocol != DNS_PROTOCOL_DNS)
+                return NULL;
+
+        if (s->link)
+                return s->link->search_domains;
+
+        return NULL;
+}
+
+bool dns_scope_has_search_domains(DnsScope *s) {
+        assert(s);
+
+        /* Tests if there are *any* search domains suitable for this
+         * scope. This means either local or global ones */
+
+        if (s->protocol != DNS_PROTOCOL_DNS)
+                return false;
+
+        if (s->manager->search_domains)
+                return true;
+
+        if (s->link && s->link->search_domains)
+                return true;
+
+        return false;
+}
+
+bool dns_scope_name_needs_search_domain(DnsScope *s, const char *name) {
+        assert(s);
+
+        if (s->protocol != DNS_PROTOCOL_DNS)
+                return false;
+
+        return dns_name_is_single_label(name);
+}
index b75f212897cdaf913e2db617a32750c81807168f..7876410b7d6be7cbcd7449efefbac16bc0b7645c 100644 (file)
@@ -25,9 +25,9 @@
 
 typedef struct DnsScope DnsScope;
 
-#include "resolved-dns-server.h"
-#include "resolved-dns-packet.h"
 #include "resolved-dns-cache.h"
+#include "resolved-dns-packet.h"
+#include "resolved-dns-server.h"
 #include "resolved-dns-zone.h"
 #include "resolved-link.h"
 
@@ -47,8 +47,6 @@ struct DnsScope {
 
         Link *link;
 
-        char **domains;
-
         DnsCache cache;
         DnsZone zone;
 
@@ -60,7 +58,18 @@ struct DnsScope {
         usec_t resend_timeout;
         usec_t max_rtt;
 
-        Hashmap *transactions;
+        LIST_HEAD(DnsQueryCandidate, query_candidates);
+
+        /* Note that we keep track of ongoing transactions in two
+         * ways: once in a hashmap, indexed by the rr key, and once in
+         * a linked list. We use the hashmap to quickly find
+         * transactions we can reuse for a key. But note that there
+         * might be multiple transactions for the same key (because
+         * the zone probing can't reuse a transaction answered from
+         * the zone or the cache), and the hashmap only tracks the
+         * most recent entry. */
+        Hashmap *transactions_by_key;
+        LIST_HEAD(DnsTransaction, transactions);
 
         LIST_FIELDS(DnsScope, scopes);
 };
@@ -91,3 +100,8 @@ int dns_scope_notify_conflict(DnsScope *scope, DnsResourceRecord *rr);
 void dns_scope_check_conflicts(DnsScope *scope, DnsPacket *p);
 
 void dns_scope_dump(DnsScope *s, FILE *f);
+
+DnsSearchDomain *dns_scope_get_search_domains(DnsScope *s);
+bool dns_scope_has_search_domains(DnsScope *s);
+
+bool dns_scope_name_needs_search_domain(DnsScope *s, const char *name);
diff --git a/src/resolve/resolved-dns-search-domain.c b/src/resolve/resolved-dns-search-domain.c
new file mode 100644 (file)
index 0000000..f9d966a
--- /dev/null
@@ -0,0 +1,232 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  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 "dns-domain.h"
+#include "resolved-dns-search-domain.h"
+
+int dns_search_domain_new(
+                Manager *m,
+                DnsSearchDomain **ret,
+                DnsSearchDomainType type,
+                Link *l,
+                const char *name) {
+
+        _cleanup_free_ char *normalized = NULL;
+        DnsSearchDomain *d;
+        int r;
+
+        assert(m);
+        assert((type == DNS_SEARCH_DOMAIN_LINK) == !!l);
+        assert(name);
+
+        r = dns_name_normalize(name, &normalized);
+        if (r < 0)
+                return r;
+
+        if (dns_name_is_root(normalized))
+                return -EINVAL;
+
+        if (l) {
+                if (l->n_search_domains >= LINK_SEARCH_DOMAINS_MAX)
+                        return -E2BIG;
+        } else {
+                if (m->n_search_domains >= MANAGER_SEARCH_DOMAINS_MAX)
+                        return -E2BIG;
+        }
+
+        d = new0(DnsSearchDomain, 1);
+        if (!d)
+                return -ENOMEM;
+
+        d->n_ref = 1;
+        d->manager = m;
+        d->type = type;
+        d->name = normalized;
+        normalized = NULL;
+
+        switch (type) {
+
+        case DNS_SEARCH_DOMAIN_LINK:
+                d->link = l;
+                LIST_APPEND(domains, l->search_domains, d);
+                l->n_search_domains++;
+                break;
+
+        case DNS_SERVER_SYSTEM:
+                LIST_APPEND(domains, m->search_domains, d);
+                m->n_search_domains++;
+                break;
+
+        default:
+                assert_not_reached("Unknown search domain type");
+        }
+
+        d->linked = true;
+
+        if (ret)
+                *ret = d;
+
+        return 0;
+}
+
+DnsSearchDomain* dns_search_domain_ref(DnsSearchDomain *d) {
+        if (!d)
+                return NULL;
+
+        assert(d->n_ref > 0);
+        d->n_ref++;
+
+        return d;
+}
+
+DnsSearchDomain* dns_search_domain_unref(DnsSearchDomain *d) {
+        if (!d)
+                return NULL;
+
+        assert(d->n_ref > 0);
+        d->n_ref--;
+
+        if (d->n_ref > 0)
+                return NULL;
+
+        free(d->name);
+        free(d);
+
+        return NULL;
+}
+
+void dns_search_domain_unlink(DnsSearchDomain *d) {
+        assert(d);
+        assert(d->manager);
+
+        if (!d->linked)
+                return;
+
+        switch (d->type) {
+
+        case DNS_SEARCH_DOMAIN_LINK:
+                assert(d->link);
+                assert(d->link->n_search_domains > 0);
+                LIST_REMOVE(domains, d->link->search_domains, d);
+                d->link->n_search_domains--;
+                break;
+
+        case DNS_SEARCH_DOMAIN_SYSTEM:
+                assert(d->manager->n_search_domains > 0);
+                LIST_REMOVE(domains, d->manager->search_domains, d);
+                d->manager->n_search_domains--;
+                break;
+        }
+
+        d->linked = false;
+
+        dns_search_domain_unref(d);
+}
+
+void dns_search_domain_move_back_and_unmark(DnsSearchDomain *d) {
+        DnsSearchDomain *tail;
+
+        assert(d);
+
+        if (!d->marked)
+                return;
+
+        d->marked = false;
+
+        if (!d->linked || !d->domains_next)
+                return;
+
+        switch (d->type) {
+
+        case DNS_SEARCH_DOMAIN_LINK:
+                assert(d->link);
+                LIST_FIND_TAIL(domains, d, tail);
+                LIST_REMOVE(domains, d->link->search_domains, d);
+                LIST_INSERT_AFTER(domains, d->link->search_domains, tail, d);
+                break;
+
+        case DNS_SEARCH_DOMAIN_SYSTEM:
+                LIST_FIND_TAIL(domains, d, tail);
+                LIST_REMOVE(domains, d->manager->search_domains, d);
+                LIST_INSERT_AFTER(domains, d->manager->search_domains, tail, d);
+                break;
+
+        default:
+                assert_not_reached("Unknown search domain type");
+        }
+}
+
+void dns_search_domain_unlink_all(DnsSearchDomain *first) {
+        DnsSearchDomain *next;
+
+        if (!first)
+                return;
+
+        next = first->domains_next;
+        dns_search_domain_unlink(first);
+
+        dns_search_domain_unlink_all(next);
+}
+
+void dns_search_domain_unlink_marked(DnsSearchDomain *first) {
+        DnsSearchDomain *next;
+
+        if (!first)
+                return;
+
+        next = first->domains_next;
+
+        if (first->marked)
+                dns_search_domain_unlink(first);
+
+        dns_search_domain_unlink_marked(next);
+}
+
+void dns_search_domain_mark_all(DnsSearchDomain *first) {
+        if (!first)
+                return;
+
+        first->marked = true;
+        dns_search_domain_mark_all(first->domains_next);
+}
+
+int dns_search_domain_find(DnsSearchDomain *first, const char *name, DnsSearchDomain **ret) {
+        DnsSearchDomain *d;
+        int r;
+
+        assert(name);
+        assert(ret);
+
+        LIST_FOREACH(domains, d, first) {
+
+                r = dns_name_equal(name, d->name);
+                if (r < 0)
+                        return r;
+                if (r > 0) {
+                        *ret = d;
+                        return 1;
+                }
+        }
+
+        *ret = NULL;
+        return 0;
+}
diff --git a/src/resolve/resolved-dns-search-domain.h b/src/resolve/resolved-dns-search-domain.h
new file mode 100644 (file)
index 0000000..2e0af31
--- /dev/null
@@ -0,0 +1,75 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  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 "macro.h"
+
+typedef struct DnsSearchDomain DnsSearchDomain;
+
+typedef enum DnsSearchDomainType {
+        DNS_SEARCH_DOMAIN_SYSTEM,
+        DNS_SEARCH_DOMAIN_LINK,
+} DnsSearchDomainType;
+
+#include "resolved-link.h"
+#include "resolved-manager.h"
+
+struct DnsSearchDomain {
+        Manager *manager;
+
+        unsigned n_ref;
+
+        DnsSearchDomainType type;
+        Link *link;
+
+        char *name;
+
+        bool marked:1;
+
+        bool linked:1;
+        LIST_FIELDS(DnsSearchDomain, domains);
+};
+
+int dns_search_domain_new(
+                Manager *m,
+                DnsSearchDomain **ret,
+                DnsSearchDomainType type,
+                Link *link,
+                const char *name);
+
+DnsSearchDomain* dns_search_domain_ref(DnsSearchDomain *d);
+DnsSearchDomain* dns_search_domain_unref(DnsSearchDomain *d);
+
+void dns_search_domain_unlink(DnsSearchDomain *d);
+void dns_search_domain_move_back_and_unmark(DnsSearchDomain *d);
+
+void dns_search_domain_unlink_all(DnsSearchDomain *first);
+void dns_search_domain_unlink_marked(DnsSearchDomain *first);
+void dns_search_domain_mark_all(DnsSearchDomain *first);
+
+int dns_search_domain_find(DnsSearchDomain *first, const char *name, DnsSearchDomain **ret);
+
+static inline const char* DNS_SEARCH_DOMAIN_NAME(DnsSearchDomain *d) {
+        return d ? d->name : NULL;
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(DnsSearchDomain*, dns_search_domain_unref);
index e803f635abda8835e93e0eba1638ac4383fc9756..0ebd22fe2208d9d28a0dba6179c900f9a65712b5 100644 (file)
@@ -21,7 +21,9 @@
 
 #include "alloc-util.h"
 #include "resolved-dns-server.h"
+#include "resolved-resolv-conf.h"
 #include "siphash24.h"
+#include "string-util.h"
 
 /* After how much time to repeat classic DNS requests */
 #define DNS_TIMEOUT_MIN_USEC (500 * USEC_PER_MSEC)
@@ -35,36 +37,57 @@ int dns_server_new(
                 int family,
                 const union in_addr_union *in_addr) {
 
-        DnsServer *s, *tail;
+        DnsServer *s;
 
         assert(m);
         assert((type == DNS_SERVER_LINK) == !!l);
         assert(in_addr);
 
+        if (!IN_SET(family, AF_INET, AF_INET6))
+                return -EAFNOSUPPORT;
+
+        if (l) {
+                if (l->n_dns_servers >= LINK_DNS_SERVERS_MAX)
+                        return -E2BIG;
+        } else {
+                if (m->n_dns_servers >= MANAGER_DNS_SERVERS_MAX)
+                        return -E2BIG;
+        }
+
         s = new0(DnsServer, 1);
         if (!s)
                 return -ENOMEM;
 
         s->n_ref = 1;
+        s->manager = m;
         s->type = type;
         s->family = family;
         s->address = *in_addr;
         s->resend_timeout = DNS_TIMEOUT_MIN_USEC;
 
-        if (type == DNS_SERVER_LINK) {
-                LIST_FIND_TAIL(servers, l->dns_servers, tail);
-                LIST_INSERT_AFTER(servers, l->dns_servers, tail, s);
+        switch (type) {
+
+        case DNS_SERVER_LINK:
                 s->link = l;
-        } else if (type == DNS_SERVER_SYSTEM) {
-                LIST_FIND_TAIL(servers, m->dns_servers, tail);
-                LIST_INSERT_AFTER(servers, m->dns_servers, tail, s);
-        } else if (type == DNS_SERVER_FALLBACK) {
-                LIST_FIND_TAIL(servers, m->fallback_dns_servers, tail);
-                LIST_INSERT_AFTER(servers, m->fallback_dns_servers, tail, s);
-        } else
+                LIST_APPEND(servers, l->dns_servers, s);
+                l->n_dns_servers++;
+                break;
+
+        case DNS_SERVER_SYSTEM:
+                LIST_APPEND(servers, m->dns_servers, s);
+                m->n_dns_servers++;
+                break;
+
+        case DNS_SERVER_FALLBACK:
+                LIST_APPEND(servers, m->fallback_dns_servers, s);
+                m->n_dns_servers++;
+                break;
+
+        default:
                 assert_not_reached("Unknown server type");
+        }
 
-        s->manager = m;
+        s->linked = true;
 
         /* A new DNS server that isn't fallback is added and the one
          * we used so far was a fallback one? Then let's try to pick
@@ -85,56 +108,127 @@ DnsServer* dns_server_ref(DnsServer *s)  {
                 return NULL;
 
         assert(s->n_ref > 0);
-
         s->n_ref ++;
 
         return s;
 }
 
-static DnsServer* dns_server_free(DnsServer *s)  {
+DnsServer* dns_server_unref(DnsServer *s)  {
         if (!s)
                 return NULL;
 
+        assert(s->n_ref > 0);
+        s->n_ref --;
+
+        if (s->n_ref > 0)
+                return NULL;
+
+        free(s);
+        return NULL;
+}
+
+void dns_server_unlink(DnsServer *s) {
+        assert(s);
+        assert(s->manager);
+
+        /* This removes the specified server from the linked list of
+         * servers, but any server might still stay around if it has
+         * refs, for example from an ongoing transaction. */
+
+        if (!s->linked)
+                return;
+
+        switch (s->type) {
+
+        case DNS_SERVER_LINK:
+                assert(s->link);
+                assert(s->link->n_dns_servers > 0);
+                LIST_REMOVE(servers, s->link->dns_servers, s);
+                break;
+
+        case DNS_SERVER_SYSTEM:
+                assert(s->manager->n_dns_servers > 0);
+                LIST_REMOVE(servers, s->manager->dns_servers, s);
+                s->manager->n_dns_servers--;
+                break;
+
+        case DNS_SERVER_FALLBACK:
+                assert(s->manager->n_dns_servers > 0);
+                LIST_REMOVE(servers, s->manager->fallback_dns_servers, s);
+                s->manager->n_dns_servers--;
+                break;
+        }
+
+        s->linked = false;
+
         if (s->link && s->link->current_dns_server == s)
                 link_set_dns_server(s->link, NULL);
 
-        if (s->manager && s->manager->current_dns_server == s)
+        if (s->manager->current_dns_server == s)
                 manager_set_dns_server(s->manager, NULL);
 
-        free(s);
-
-        return NULL;
+        dns_server_unref(s);
 }
 
-DnsServer* dns_server_unref(DnsServer *s)  {
-        if (!s)
-                return NULL;
+void dns_server_move_back_and_unmark(DnsServer *s) {
+        DnsServer *tail;
 
-        assert(s->n_ref > 0);
+        assert(s);
 
-        if (s->n_ref == 1)
-                dns_server_free(s);
-        else
-                s->n_ref --;
+        if (!s->marked)
+                return;
 
-        return NULL;
+        s->marked = false;
+
+        if (!s->linked || !s->servers_next)
+                return;
+
+        /* Move us to the end of the list, so that the order is
+         * strictly kept, if we are not at the end anyway. */
+
+        switch (s->type) {
+
+        case DNS_SERVER_LINK:
+                assert(s->link);
+                LIST_FIND_TAIL(servers, s, tail);
+                LIST_REMOVE(servers, s->link->dns_servers, s);
+                LIST_INSERT_AFTER(servers, s->link->dns_servers, tail, s);
+                break;
+
+        case DNS_SERVER_SYSTEM:
+                LIST_FIND_TAIL(servers, s, tail);
+                LIST_REMOVE(servers, s->manager->dns_servers, s);
+                LIST_INSERT_AFTER(servers, s->manager->dns_servers, tail, s);
+                break;
+
+        case DNS_SERVER_FALLBACK:
+                LIST_FIND_TAIL(servers, s, tail);
+                LIST_REMOVE(servers, s->manager->fallback_dns_servers, s);
+                LIST_INSERT_AFTER(servers, s->manager->fallback_dns_servers, tail, s);
+                break;
+
+        default:
+                assert_not_reached("Unknown server type");
+        }
 }
 
 void dns_server_packet_received(DnsServer *s, usec_t rtt) {
         assert(s);
 
-        if (rtt > s->max_rtt) {
-                s->max_rtt = rtt;
-                s->resend_timeout = MIN(MAX(DNS_TIMEOUT_MIN_USEC, s->max_rtt * 2),
-                                        DNS_TIMEOUT_MAX_USEC);
-        }
+        if (rtt <= s->max_rtt)
+                return;
+
+        s->max_rtt = rtt;
+        s->resend_timeout = MIN(MAX(DNS_TIMEOUT_MIN_USEC, s->max_rtt * 2), DNS_TIMEOUT_MAX_USEC);
 }
 
 void dns_server_packet_lost(DnsServer *s, usec_t usec) {
         assert(s);
 
-        if (s->resend_timeout <= usec)
-                s->resend_timeout = MIN(s->resend_timeout * 2, DNS_TIMEOUT_MAX_USEC);
+        if (s->resend_timeout > usec)
+                return;
+
+        s->resend_timeout = MIN(s->resend_timeout * 2, DNS_TIMEOUT_MAX_USEC);
 }
 
 static void dns_server_hash_func(const void *p, struct siphash *state) {
@@ -161,3 +255,140 @@ const struct hash_ops dns_server_hash_ops = {
         .hash = dns_server_hash_func,
         .compare = dns_server_compare_func
 };
+
+void dns_server_unlink_all(DnsServer *first) {
+        DnsServer *next;
+
+        if (!first)
+                return;
+
+        next = first->servers_next;
+        dns_server_unlink(first);
+
+        dns_server_unlink_all(next);
+}
+
+void dns_server_unlink_marked(DnsServer *first) {
+        DnsServer *next;
+
+        if (!first)
+                return;
+
+        next = first->servers_next;
+
+        if (first->marked)
+                dns_server_unlink(first);
+
+        dns_server_unlink_marked(next);
+}
+
+void dns_server_mark_all(DnsServer *first) {
+        if (!first)
+                return;
+
+        first->marked = true;
+        dns_server_mark_all(first->servers_next);
+}
+
+DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr) {
+        DnsServer *s;
+
+        LIST_FOREACH(servers, s, first)
+                if (s->family == family && in_addr_equal(family, &s->address, in_addr) > 0)
+                        return s;
+
+        return NULL;
+}
+
+DnsServer *manager_get_first_dns_server(Manager *m, DnsServerType t) {
+        assert(m);
+
+        switch (t) {
+
+        case DNS_SERVER_SYSTEM:
+                return m->dns_servers;
+
+        case DNS_SERVER_FALLBACK:
+                return m->fallback_dns_servers;
+
+        default:
+                return NULL;
+        }
+}
+
+DnsServer *manager_set_dns_server(Manager *m, DnsServer *s) {
+        assert(m);
+
+        if (m->current_dns_server == s)
+                return s;
+
+        if (s) {
+                _cleanup_free_ char *ip = NULL;
+
+                in_addr_to_string(s->family, &s->address, &ip);
+                log_info("Switching to system DNS server %s.", strna(ip));
+        }
+
+        dns_server_unref(m->current_dns_server);
+        m->current_dns_server = dns_server_ref(s);
+
+        if (m->unicast_scope)
+                dns_cache_flush(&m->unicast_scope->cache);
+
+        return s;
+}
+
+DnsServer *manager_get_dns_server(Manager *m) {
+        Link *l;
+        assert(m);
+
+        /* Try to read updates resolv.conf */
+        manager_read_resolv_conf(m);
+
+        /* If no DNS server was chose so far, pick the first one */
+        if (!m->current_dns_server)
+                manager_set_dns_server(m, m->dns_servers);
+
+        if (!m->current_dns_server) {
+                bool found = false;
+                Iterator i;
+
+                /* No DNS servers configured, let's see if there are
+                 * any on any links. If not, we use the fallback
+                 * servers */
+
+                HASHMAP_FOREACH(l, m->links, i)
+                        if (l->dns_servers) {
+                                found = true;
+                                break;
+                        }
+
+                if (!found)
+                        manager_set_dns_server(m, m->fallback_dns_servers);
+        }
+
+        return m->current_dns_server;
+}
+
+void manager_next_dns_server(Manager *m) {
+        assert(m);
+
+        /* If there's currently no DNS server set, then the next
+         * manager_get_dns_server() will find one */
+        if (!m->current_dns_server)
+                return;
+
+        /* Change to the next one, but make sure to follow the linked
+         * list only if the server is still linked. */
+        if (m->current_dns_server->linked && m->current_dns_server->servers_next) {
+                manager_set_dns_server(m, m->current_dns_server->servers_next);
+                return;
+        }
+
+        /* If there was no next one, then start from the beginning of
+         * the list */
+        if (m->current_dns_server->type == DNS_SERVER_FALLBACK)
+                manager_set_dns_server(m, m->fallback_dns_servers);
+        else
+                manager_set_dns_server(m, m->dns_servers);
+}
index 10111fd6bde39f5ed5974c4548fcc5827ac1d151..3a78d4a3b5b0053c721808823fc9838a6d456eb9 100644 (file)
@@ -24,7 +24,6 @@
 #include "in-addr-util.h"
 
 typedef struct DnsServer DnsServer;
-typedef enum DnsServerSource DnsServerSource;
 
 typedef enum DnsServerType {
         DNS_SERVER_SYSTEM,
@@ -32,6 +31,7 @@ typedef enum DnsServerType {
         DNS_SERVER_LINK,
 } DnsServerType;
 
+#include "resolved-manager.h"
 #include "resolved-link.h"
 
 struct DnsServer {
@@ -40,7 +40,6 @@ struct DnsServer {
         unsigned n_ref;
 
         DnsServerType type;
-
         Link *link;
 
         int family;
@@ -51,23 +50,40 @@ struct DnsServer {
 
         bool marked:1;
 
+        /* If linked is set, then this server appears in the servers linked list */
+        bool linked:1;
         LIST_FIELDS(DnsServer, servers);
 };
 
 int dns_server_new(
                 Manager *m,
-                DnsServer **s,
+                DnsServer **ret,
                 DnsServerType type,
-                Link *l,
+                Link *link,
                 int family,
                 const union in_addr_union *address);
 
 DnsServer* dns_server_ref(DnsServer *s);
 DnsServer* dns_server_unref(DnsServer *s);
 
+void dns_server_unlink(DnsServer *s);
+void dns_server_move_back_and_unmark(DnsServer *s);
+
 void dns_server_packet_received(DnsServer *s, usec_t rtt);
 void dns_server_packet_lost(DnsServer *s, usec_t usec);
 
+DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr);
+
+void dns_server_unlink_all(DnsServer *first);
+void dns_server_unlink_marked(DnsServer *first);
+void dns_server_mark_all(DnsServer *first);
+
+DnsServer *manager_get_first_dns_server(Manager *m, DnsServerType t);
+
+DnsServer *manager_set_dns_server(Manager *m, DnsServer *s);
+DnsServer *manager_get_dns_server(Manager *m);
+void manager_next_dns_server(Manager *m);
+
 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsServer*, dns_server_unref);
 
 extern const struct hash_ops dns_server_hash_ops;
index 6545f6cd8a7ed39a37b9fd4d945e2257f1b608c9..8c4f23a4dae36014db106f98370db6e0850cd451 100644 (file)
@@ -29,7 +29,7 @@
 #include "string-table.h"
 
 DnsTransaction* dns_transaction_free(DnsTransaction *t) {
-        DnsQuery *q;
+        DnsQueryCandidate *c;
         DnsZoneItem *i;
 
         if (!t)
@@ -39,7 +39,8 @@ DnsTransaction* dns_transaction_free(DnsTransaction *t) {
 
         dns_packet_unref(t->sent);
         dns_packet_unref(t->received);
-        dns_answer_unref(t->cached);
+
+        dns_answer_unref(t->answer);
 
         sd_event_source_unref(t->dns_udp_event_source);
         safe_close(t->dns_udp_fd);
@@ -48,7 +49,8 @@ DnsTransaction* dns_transaction_free(DnsTransaction *t) {
         dns_stream_free(t->stream);
 
         if (t->scope) {
-                hashmap_remove(t->scope->transactions, t->key);
+                hashmap_remove_value(t->scope->transactions_by_key, t->key, t);
+                LIST_REMOVE(transactions_by_scope, t->scope->transactions, t);
 
                 if (t->id != 0)
                         hashmap_remove(t->scope->manager->dns_transactions, UINT_TO_PTR(t->id));
@@ -56,9 +58,10 @@ DnsTransaction* dns_transaction_free(DnsTransaction *t) {
 
         dns_resource_key_unref(t->key);
 
-        while ((q = set_steal_first(t->queries)))
-                set_remove(q->transactions, t);
-        set_free(t->queries);
+        while ((c = set_steal_first(t->query_candidates)))
+                set_remove(c->transactions, t);
+
+        set_free(t->query_candidates);
 
         while ((i = set_steal_first(t->zone_items)))
                 i->probe_transaction = NULL;
@@ -76,7 +79,7 @@ void dns_transaction_gc(DnsTransaction *t) {
         if (t->block_gc > 0)
                 return;
 
-        if (set_isempty(t->queries) && set_isempty(t->zone_items))
+        if (set_isempty(t->query_candidates) && set_isempty(t->zone_items))
                 dns_transaction_free(t);
 }
 
@@ -92,7 +95,7 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key)
         if (r < 0)
                 return r;
 
-        r = hashmap_ensure_allocated(&s->transactions, &dns_resource_key_hash_ops);
+        r = hashmap_ensure_allocated(&s->transactions_by_key, &dns_resource_key_hash_ops);
         if (r < 0)
                 return r;
 
@@ -101,6 +104,7 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key)
                 return -ENOMEM;
 
         t->dns_udp_fd = -1;
+        t->answer_source = _DNS_TRANSACTION_SOURCE_INVALID;
         t->key = dns_resource_key_ref(key);
 
         /* Find a fresh, unused transaction id */
@@ -115,12 +119,13 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key)
                 return r;
         }
 
-        r = hashmap_put(s->transactions, t->key, t);
+        r = hashmap_replace(s->transactions_by_key, t->key, t);
         if (r < 0) {
                 hashmap_remove(s->manager->dns_transactions, UINT_TO_PTR(t->id));
                 return r;
         }
 
+        LIST_PREPEND(transactions_by_scope, s->transactions, t);
         t->scope = s;
 
         if (ret)
@@ -136,6 +141,9 @@ static void dns_transaction_stop(DnsTransaction *t) {
 
         t->timeout_event_source = sd_event_source_unref(t->timeout_event_source);
         t->stream = dns_stream_free(t->stream);
+
+        /* Note that we do not drop the UDP socket here, as we want to
+         * reuse it to repeat the interaction. */
 }
 
 static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) {
@@ -181,7 +189,7 @@ static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) {
 }
 
 void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) {
-        DnsQuery *q;
+        DnsQueryCandidate *c;
         DnsZoneItem *z;
         Iterator i;
 
@@ -192,11 +200,12 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) {
          * should hence not attempt to access the query or transaction
          * after calling this function. */
 
-        log_debug("Transaction on scope %s on %s/%s now complete with <%s>",
+        log_debug("Transaction on scope %s on %s/%s now complete with <%s> from %s",
                   dns_protocol_to_string(t->scope->protocol),
                   t->scope->link ? t->scope->link->name : "*",
                   t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family),
-                  dns_transaction_state_to_string(state));
+                  dns_transaction_state_to_string(state),
+                  t->answer_source < 0 ? "none" : dns_transaction_source_to_string(t->answer_source));
 
         t->state = state;
 
@@ -205,8 +214,8 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) {
         /* Notify all queries that are interested, but make sure the
          * transaction isn't freed while we are still looking at it */
         t->block_gc++;
-        SET_FOREACH(q, t->queries, i)
-                dns_query_ready(q);
+        SET_FOREACH(c, t->query_candidates, i)
+                dns_query_candidate_ready(c);
         SET_FOREACH(z, t->zone_items, i)
                 dns_zone_item_ready(z);
         t->block_gc--;
@@ -314,6 +323,8 @@ static int dns_transaction_open_tcp(DnsTransaction *t) {
         dns_server_unref(t->server);
         t->server = dns_server_ref(server);
         t->received = dns_packet_unref(t->received);
+        t->answer = dns_answer_unref(t->answer);
+        t->answer_rcode = 0;
         t->stream->complete = on_stream_complete;
         t->stream->transaction = t;
 
@@ -385,6 +396,8 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
                 t->received = dns_packet_ref(p);
         }
 
+        t->answer_source = DNS_TRANSACTION_NETWORK;
+
         if (p->ipproto == IPPROTO_TCP) {
                 if (DNS_PACKET_TC(p)) {
                         /* Truncated via TCP? Somebody must be fucking with us */
@@ -453,6 +466,11 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
                 return;
         }
 
+        /* Install the answer as answer to the transaction */
+        dns_answer_unref(t->answer);
+        t->answer = dns_answer_ref(p->answer);
+        t->answer_rcode = DNS_PACKET_RCODE(p);
+
         /* Only consider responses with equivalent query section to the request */
         if (p->question->n_keys != 1 || dns_resource_key_equal(p->question->keys[0], t->key) <= 0) {
                 dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
@@ -460,7 +478,8 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
         }
 
         /* According to RFC 4795, section 2.9. only the RRs from the answer section shall be cached */
-        dns_cache_put(&t->scope->cache, t->key, DNS_PACKET_RCODE(p), p->answer, DNS_PACKET_ANCOUNT(p), 0, p->family, &p->sender);
+        if (DNS_PACKET_SHALL_CACHE(p))
+                dns_cache_put(&t->scope->cache, t->key, DNS_PACKET_RCODE(p), p->answer, DNS_PACKET_ANCOUNT(p), 0, p->family, &p->sender);
 
         if (DNS_PACKET_RCODE(p) == DNS_RCODE_SUCCESS)
                 dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
@@ -623,8 +642,24 @@ int dns_transaction_go(DnsTransaction *t) {
         t->n_attempts++;
         t->start_usec = ts;
         t->received = dns_packet_unref(t->received);
-        t->cached = dns_answer_unref(t->cached);
-        t->cached_rcode = 0;
+        t->answer = dns_answer_unref(t->answer);
+        t->answer_rcode = 0;
+        t->answer_source = _DNS_TRANSACTION_SOURCE_INVALID;
+
+        /* Check the zone, but obly if this transaction is not used
+         * for probing or verifying a zone item. */
+        if (set_isempty(t->zone_items)) {
+
+                r = dns_zone_lookup(&t->scope->zone, t->key, &t->answer, NULL, NULL);
+                if (r < 0)
+                        return r;
+                if (r > 0) {
+                        t->answer_rcode = DNS_RCODE_SUCCESS;
+                        t->answer_source = DNS_TRANSACTION_ZONE;
+                        dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
+                        return 0;
+                }
+        }
 
         /* Check the cache, but only if this transaction is not used
          * for probing or verifying a zone item. */
@@ -638,11 +673,12 @@ int dns_transaction_go(DnsTransaction *t) {
                 /* Let's then prune all outdated entries */
                 dns_cache_prune(&t->scope->cache);
 
-                r = dns_cache_lookup(&t->scope->cache, t->key, &t->cached_rcode, &t->cached);
+                r = dns_cache_lookup(&t->scope->cache, t->key, &t->answer_rcode, &t->answer);
                 if (r < 0)
                         return r;
                 if (r > 0) {
-                        if (t->cached_rcode == DNS_RCODE_SUCCESS)
+                        t->answer_source = DNS_TRANSACTION_CACHE;
+                        if (t->answer_rcode == DNS_RCODE_SUCCESS)
                                 dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
                         else
                                 dns_transaction_complete(t, DNS_TRANSACTION_FAILURE);
@@ -745,3 +781,10 @@ static const char* const dns_transaction_state_table[_DNS_TRANSACTION_STATE_MAX]
         [DNS_TRANSACTION_ABORTED] = "aborted",
 };
 DEFINE_STRING_TABLE_LOOKUP(dns_transaction_state, DnsTransactionState);
+
+static const char* const dns_transaction_source_table[_DNS_TRANSACTION_SOURCE_MAX] = {
+        [DNS_TRANSACTION_NETWORK] = "network",
+        [DNS_TRANSACTION_CACHE] = "cache",
+        [DNS_TRANSACTION_ZONE] = "zone",
+};
+DEFINE_STRING_TABLE_LOOKUP(dns_transaction_source, DnsTransactionSource);
index acf6a6f651a509323030bc554e02908f8dc61612..ee80dcf5a9f5d203b8689f620a3566e786b2a74d 100644 (file)
@@ -23,6 +23,7 @@
 
 typedef struct DnsTransaction DnsTransaction;
 typedef enum DnsTransactionState DnsTransactionState;
+typedef enum DnsTransactionSource DnsTransactionSource;
 
 enum DnsTransactionState {
         DNS_TRANSACTION_NULL,
@@ -39,10 +40,18 @@ enum DnsTransactionState {
         _DNS_TRANSACTION_STATE_INVALID = -1
 };
 
-#include "resolved-dns-scope.h"
+enum DnsTransactionSource {
+        DNS_TRANSACTION_NETWORK,
+        DNS_TRANSACTION_CACHE,
+        DNS_TRANSACTION_ZONE,
+        _DNS_TRANSACTION_SOURCE_MAX,
+        _DNS_TRANSACTION_SOURCE_INVALID = -1
+};
+
+#include "resolved-dns-answer.h"
 #include "resolved-dns-packet.h"
 #include "resolved-dns-question.h"
-#include "resolved-dns-answer.h"
+#include "resolved-dns-scope.h"
 
 struct DnsTransaction {
         DnsScope *scope;
@@ -55,8 +64,10 @@ struct DnsTransaction {
         bool initial_jitter;
 
         DnsPacket *sent, *received;
-        DnsAnswer *cached;
-        int cached_rcode;
+
+        DnsAnswer *answer;
+        int answer_rcode;
+        DnsTransactionSource answer_source;
 
         usec_t start_usec;
         sd_event_source *timeout_event_source;
@@ -71,9 +82,10 @@ struct DnsTransaction {
         /* TCP connection logic, if we need it */
         DnsStream *stream;
 
-        /* Queries this transaction is referenced by and that shall be
-         * notified about this specific transaction completing. */
-        Set *queries;
+        /* Query candidates this transaction is referenced by and that
+         * shall be notified about this specific transaction
+         * completing. */
+        Set *query_candidates;
 
         /* Zone items this transaction is referenced by and that shall
          * be notified about completion. */
@@ -96,6 +108,9 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state);
 const char* dns_transaction_state_to_string(DnsTransactionState p) _const_;
 DnsTransactionState dns_transaction_state_from_string(const char *s) _pure_;
 
+const char* dns_transaction_source_to_string(DnsTransactionSource p) _const_;
+DnsTransactionSource dns_transaction_source_from_string(const char *s) _pure_;
+
 /* LLMNR Jitter interval, see RFC 4795 Section 7 */
 #define LLMNR_JITTER_INTERVAL_USEC (100 * USEC_PER_MSEC)
 
@@ -105,4 +120,4 @@ DnsTransactionState dns_transaction_state_from_string(const char *s) _pure_;
 /* Maximum attempts to send LLMNR requests, see RFC 4795 Section 2.7 */
 #define LLMNR_TRANSACTION_ATTEMPTS_MAX 3
 
-#define TRANSACTION_ATTEMPTS_MAX(p) (p == DNS_PROTOCOL_LLMNR ? LLMNR_TRANSACTION_ATTEMPTS_MAX : DNS_TRANSACTION_ATTEMPTS_MAX)
+#define TRANSACTION_ATTEMPTS_MAX(p) ((p) == DNS_PROTOCOL_LLMNR ? LLMNR_TRANSACTION_ATTEMPTS_MAX : DNS_TRANSACTION_ATTEMPTS_MAX)
index 48dcf76daac9bf40e252c2c56516467176ad3aea..493d11dd14169a9a234bb3c9d1c0da6a5eef2a01 100644 (file)
@@ -283,97 +283,76 @@ int dns_zone_put(DnsZone *z, DnsScope *s, DnsResourceRecord *rr, bool probe) {
         return 0;
 }
 
-int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **ret_answer, DnsAnswer **ret_soa, bool *ret_tentative) {
+int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, DnsAnswer **ret_answer, DnsAnswer **ret_soa, bool *ret_tentative) {
         _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
-        unsigned i, n_answer = 0, n_soa = 0;
-        bool tentative = true;
+        unsigned n_answer = 0;
+        DnsZoneItem *j, *first;
+        bool tentative = true, need_soa = false;
         int r;
 
         assert(z);
-        assert(q);
+        assert(key);
         assert(ret_answer);
-        assert(ret_soa);
 
-        if (q->n_keys <= 0) {
-                *ret_answer = NULL;
-                *ret_soa = NULL;
-
-                if (ret_tentative)
-                        *ret_tentative = false;
+        /* First iteration, count what we have */
 
-                return 0;
-        }
+        if (key->type == DNS_TYPE_ANY || key->class == DNS_CLASS_ANY) {
+                bool found = false, added = false;
+                int k;
 
-        /* First iteration, count what we have */
-        for (i = 0; i < q->n_keys; i++) {
-                DnsZoneItem *j, *first;
+                /* If this is a generic match, then we have to
+                 * go through the list by the name and look
+                 * for everything manually */
 
-                if (q->keys[i]->type == DNS_TYPE_ANY ||
-                    q->keys[i]->class == DNS_CLASS_ANY) {
-                        bool found = false, added = false;
-                        int k;
+                first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(key));
+                LIST_FOREACH(by_name, j, first) {
+                        if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
+                                continue;
 
-                        /* If this is a generic match, then we have to
-                         * go through the list by the name and look
-                         * for everything manually */
+                        found = true;
 
-                        first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i]));
-                        LIST_FOREACH(by_name, j, first) {
-                                if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
-                                        continue;
+                        k = dns_resource_key_match_rr(key, j->rr, NULL);
+                        if (k < 0)
+                                return k;
+                        if (k > 0) {
+                                n_answer++;
+                                added = true;
+                        }
 
-                                found = true;
+                }
 
-                                k = dns_resource_key_match_rr(q->keys[i], j->rr);
-                                if (k < 0)
-                                        return k;
-                                if (k > 0) {
-                                        n_answer++;
-                                        added = true;
-                                }
+                if (found && !added)
+                        need_soa = true;
 
-                        }
+        } else {
+                bool found = false;
 
-                        if (found && !added)
-                                n_soa++;
+                /* If this is a specific match, then look for
+                 * the right key immediately */
 
-                } else {
-                        bool found = false;
+                first = hashmap_get(z->by_key, key);
+                LIST_FOREACH(by_key, j, first) {
+                        if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
+                                continue;
 
-                        /* If this is a specific match, then look for
-                         * the right key immediately */
+                        found = true;
+                        n_answer++;
+                }
 
-                        first = hashmap_get(z->by_key, q->keys[i]);
-                        LIST_FOREACH(by_key, j, first) {
+                if (!found) {
+                        first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(key));
+                        LIST_FOREACH(by_name, j, first) {
                                 if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
                                         continue;
 
-                                found = true;
-                                n_answer++;
-                        }
-
-                        if (!found) {
-                                first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i]));
-                                LIST_FOREACH(by_name, j, first) {
-                                        if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
-                                                continue;
-
-                                        n_soa++;
-                                        break;
-                                }
+                                need_soa = true;
+                                break;
                         }
                 }
         }
 
-        if (n_answer <= 0 && n_soa <= 0) {
-                *ret_answer = NULL;
-                *ret_soa = NULL;
-
-                if (ret_tentative)
-                        *ret_tentative = false;
-
-                return 0;
-        }
+        if (n_answer <= 0 && !need_soa)
+                goto return_empty;
 
         if (n_answer > 0) {
                 answer = dns_answer_new(n_answer);
@@ -381,99 +360,113 @@ int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **ret_answer, DnsAnswe
                         return -ENOMEM;
         }
 
-        if (n_soa > 0) {
-                soa = dns_answer_new(n_soa);
+        if (need_soa) {
+                soa = dns_answer_new(1);
                 if (!soa)
                         return -ENOMEM;
         }
 
         /* Second iteration, actually add the RRs to the answers */
-        for (i = 0; i < q->n_keys; i++) {
-                DnsZoneItem *j, *first;
-
-                if (q->keys[i]->type == DNS_TYPE_ANY ||
-                    q->keys[i]->class == DNS_CLASS_ANY) {
-                        bool found = false, added = false;
-                        int k;
+        if (key->type == DNS_TYPE_ANY || key->class == DNS_CLASS_ANY) {
+                bool found = false, added = false;
+                int k;
 
-                        first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i]));
-                        LIST_FOREACH(by_name, j, first) {
-                                if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
-                                        continue;
-
-                                found = true;
+                first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(key));
+                LIST_FOREACH(by_name, j, first) {
+                        if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
+                                continue;
 
-                                if (j->state != DNS_ZONE_ITEM_PROBING)
-                                        tentative = false;
+                        found = true;
 
-                                k = dns_resource_key_match_rr(q->keys[i], j->rr);
-                                if (k < 0)
-                                        return k;
-                                if (k > 0) {
-                                        r = dns_answer_add(answer, j->rr, 0);
-                                        if (r < 0)
-                                                return r;
+                        if (j->state != DNS_ZONE_ITEM_PROBING)
+                                tentative = false;
 
-                                        added = true;
-                                }
-                        }
-
-                        if (found && !added) {
-                                r = dns_answer_add_soa(soa, DNS_RESOURCE_KEY_NAME(q->keys[i]), LLMNR_DEFAULT_TTL);
+                        k = dns_resource_key_match_rr(key, j->rr, NULL);
+                        if (k < 0)
+                                return k;
+                        if (k > 0) {
+                                r = dns_answer_add(answer, j->rr, 0);
                                 if (r < 0)
                                         return r;
+
+                                added = true;
                         }
-                } else {
-                        bool found = false;
+                }
 
-                        first = hashmap_get(z->by_key, q->keys[i]);
-                        LIST_FOREACH(by_key, j, first) {
-                                if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
-                                        continue;
+                if (found && !added) {
+                        r = dns_answer_add_soa(soa, DNS_RESOURCE_KEY_NAME(key), LLMNR_DEFAULT_TTL);
+                        if (r < 0)
+                                return r;
+                }
+        } else {
+                bool found = false;
 
-                                found = true;
+                first = hashmap_get(z->by_key, key);
+                LIST_FOREACH(by_key, j, first) {
+                        if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
+                                continue;
 
-                                if (j->state != DNS_ZONE_ITEM_PROBING)
-                                        tentative = false;
+                        found = true;
 
-                                r = dns_answer_add(answer, j->rr, 0);
-                                if (r < 0)
-                                        return r;
-                        }
+                        if (j->state != DNS_ZONE_ITEM_PROBING)
+                                tentative = false;
+
+                        r = dns_answer_add(answer, j->rr, 0);
+                        if (r < 0)
+                                return r;
+                }
 
-                        if (!found) {
-                                bool add_soa = false;
+                if (!found) {
+                        bool add_soa = false;
 
-                                first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i]));
-                                LIST_FOREACH(by_name, j, first) {
-                                        if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
-                                                continue;
+                        first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(key));
+                        LIST_FOREACH(by_name, j, first) {
+                                if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
+                                        continue;
 
-                                        if (j->state != DNS_ZONE_ITEM_PROBING)
-                                                tentative = false;
+                                if (j->state != DNS_ZONE_ITEM_PROBING)
+                                        tentative = false;
 
-                                        add_soa = true;
-                                }
+                                add_soa = true;
+                        }
 
-                                if (add_soa) {
-                                        r = dns_answer_add_soa(soa, DNS_RESOURCE_KEY_NAME(q->keys[i]), LLMNR_DEFAULT_TTL);
-                                        if (r < 0)
-                                                return r;
-                                }
+                        if (add_soa) {
+                                r = dns_answer_add_soa(soa, DNS_RESOURCE_KEY_NAME(key), LLMNR_DEFAULT_TTL);
+                                if (r < 0)
+                                        return r;
                         }
                 }
         }
 
+        /* If the caller sets ret_tentative to NULL, then use this as
+         * indication to not return tentative entries */
+
+        if (!ret_tentative && tentative)
+                goto return_empty;
+
         *ret_answer = answer;
         answer = NULL;
 
-        *ret_soa = soa;
-        soa = NULL;
+        if (ret_soa) {
+                *ret_soa = soa;
+                soa = NULL;
+        }
 
         if (ret_tentative)
                 *ret_tentative = tentative;
 
         return 1;
+
+return_empty:
+        *ret_answer = NULL;
+
+        if (ret_soa)
+                *ret_soa = NULL;
+
+        if (ret_tentative)
+                *ret_tentative = false;
+
+        return 0;
 }
 
 void dns_zone_item_conflict(DnsZoneItem *i) {
index 495d17cdb1214391d691797abffadbec94edfd50..44a8624c3064e123c3e254d537ca1801a5fa851b 100644 (file)
@@ -31,9 +31,9 @@ typedef struct DnsZone {
 typedef struct DnsZoneItem DnsZoneItem;
 typedef enum DnsZoneItemState DnsZoneItemState;
 
-#include "resolved-dns-rr.h"
-#include "resolved-dns-question.h"
 #include "resolved-dns-answer.h"
+#include "resolved-dns-question.h"
+#include "resolved-dns-rr.h"
 #include "resolved-dns-transaction.h"
 
 /* RFC 4795 Section 2.8. suggests a TTL of 30s by default */
@@ -67,7 +67,7 @@ void dns_zone_flush(DnsZone *z);
 int dns_zone_put(DnsZone *z, DnsScope *s, DnsResourceRecord *rr, bool probe);
 void dns_zone_remove_rr(DnsZone *z, DnsResourceRecord *rr);
 
-int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **answer, DnsAnswer **soa, bool *tentative);
+int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, DnsAnswer **answer, DnsAnswer **soa, bool *tentative);
 
 void dns_zone_item_conflict(DnsZoneItem *i);
 void dns_zone_item_ready(DnsZoneItem *i);
index 8e78fbf06abe02e5215dad3825f83189f3db4226..50662656d567f1e7e7d6df358602d703f64bd0de 100644 (file)
@@ -14,6 +14,7 @@ struct ConfigPerfItem;
 %struct-type
 %includes
 %%
-Resolve.DNS,          config_parse_dnsv,    DNS_SERVER_SYSTEM,   0
-Resolve.FallbackDNS,  config_parse_dnsv,    DNS_SERVER_FALLBACK, 0
-Resolve.LLMNR,        config_parse_support, 0,                   offsetof(Manager, llmnr_support)
+Resolve.DNS,          config_parse_dns_servers,    DNS_SERVER_SYSTEM,   0
+Resolve.FallbackDNS,  config_parse_dns_servers,    DNS_SERVER_FALLBACK, 0
+Resolve.Domains,      config_parse_search_domains, 0,                   0
+Resolve.LLMNR,        config_parse_support,        0,                   offsetof(Manager, llmnr_support)
index 28926410753ffae5925bd94e7731808cab5ef640..ddd9427dabdc2e37b2d6fce2af1ab422e97275ec 100644 (file)
@@ -65,19 +65,15 @@ Link *link_free(Link *l) {
         if (!l)
                 return NULL;
 
+        dns_server_unlink_marked(l->dns_servers);
+        dns_search_domain_unlink_all(l->search_domains);
+
         while (l->addresses)
                 link_address_free(l->addresses);
 
         if (l->manager)
                 hashmap_remove(l->manager->links, INT_TO_PTR(l->ifindex));
 
-        while (l->dns_servers) {
-                DnsServer *s = l->dns_servers;
-
-                LIST_REMOVE(servers, l->dns_servers, s);
-                dns_server_unref(s);
-        }
-
         dns_scope_free(l->unicast_scope);
         dns_scope_free(l->llmnr_ipv4_scope);
         dns_scope_free(l->llmnr_ipv6_scope);
@@ -158,7 +154,6 @@ int link_update_rtnl(Link *l, sd_netlink_message *m) {
 static int link_update_dns_servers(Link *l) {
         _cleanup_strv_free_ char **nameservers = NULL;
         char **nameserver;
-        DnsServer *s, *nx;
         int r;
 
         assert(l);
@@ -167,20 +162,20 @@ static int link_update_dns_servers(Link *l) {
         if (r < 0)
                 goto clear;
 
-        LIST_FOREACH(servers, s, l->dns_servers)
-                s->marked = true;
+        dns_server_mark_all(l->dns_servers);
 
         STRV_FOREACH(nameserver, nameservers) {
                 union in_addr_union a;
+                DnsServer *s;
                 int family;
 
                 r = in_addr_from_string_auto(*nameserver, &family, &a);
                 if (r < 0)
                         goto clear;
 
-                s = link_find_dns_server(l, family, &a);
+                s = dns_server_find(l->dns_servers, family, &a);
                 if (s)
-                        s->marked = false;
+                        dns_server_move_back_and_unmark(s);
                 else {
                         r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, family, &a);
                         if (r < 0)
@@ -188,22 +183,11 @@ static int link_update_dns_servers(Link *l) {
                 }
         }
 
-        LIST_FOREACH_SAFE(servers, s, nx, l->dns_servers)
-                if (s->marked) {
-                        LIST_REMOVE(servers, l->dns_servers, s);
-                        dns_server_unref(s);
-                }
-
+        dns_server_unlink_marked(l->dns_servers);
         return 0;
 
 clear:
-        while (l->dns_servers) {
-                s = l->dns_servers;
-
-                LIST_REMOVE(servers, l->dns_servers, s);
-                dns_server_unref(s);
-        }
-
+        dns_server_unlink_all(l->dns_servers);
         return r;
 }
 
@@ -236,29 +220,56 @@ clear:
         return r;
 }
 
-static int link_update_domains(Link *l) {
+static int link_update_search_domains(Link *l) {
+        _cleanup_strv_free_ char **domains = NULL;
+        char **i;
         int r;
 
-        if (!l->unicast_scope)
-                return 0;
-
-        l->unicast_scope->domains = strv_free(l->unicast_scope->domains);
+        assert(l);
 
-        r = sd_network_link_get_domains(l->ifindex,
-                                        &l->unicast_scope->domains);
+        r = sd_network_link_get_domains(l->ifindex, &domains);
         if (r < 0)
-                return r;
+                goto clear;
+
+        dns_search_domain_mark_all(l->search_domains);
+
+        STRV_FOREACH(i, domains) {
+                DnsSearchDomain *d;
+
+                r = dns_search_domain_find(l->search_domains, *i, &d);
+                if (r < 0)
+                        goto clear;
+
+                if (r > 0)
+                        dns_search_domain_move_back_and_unmark(d);
+                else {
+                        r = dns_search_domain_new(l->manager, NULL, DNS_SEARCH_DOMAIN_LINK, l, *i);
+                        if (r < 0)
+                                goto clear;
+                }
+        }
 
+        dns_search_domain_unlink_marked(l->search_domains);
         return 0;
+
+clear:
+        dns_search_domain_unlink_all(l->search_domains);
+        return r;
 }
 
 int link_update_monitor(Link *l) {
+        int r;
+
         assert(l);
 
         link_update_dns_servers(l);
         link_update_llmnr_support(l);
         link_allocate_scopes(l);
-        link_update_domains(l);
+
+        r = link_update_search_domains(l);
+        if (r < 0)
+                log_warning_errno(r, "Failed to read search domains for interface %s, ignoring: %m", l->name);
+
         link_add_rrs(l, false);
 
         return 0;
@@ -303,17 +314,6 @@ LinkAddress *link_find_address(Link *l, int family, const union in_addr_union *i
         return NULL;
 }
 
-DnsServer* link_find_dns_server(Link *l, int family, const union in_addr_union *in_addr) {
-        DnsServer *s;
-
-        assert(l);
-
-        LIST_FOREACH(servers, s, l->dns_servers)
-                if (s->family == family && in_addr_equal(family, &s->address, in_addr))
-                        return s;
-        return NULL;
-}
-
 DnsServer* link_set_dns_server(Link *l, DnsServer *s) {
         assert(l);
 
@@ -327,7 +327,8 @@ DnsServer* link_set_dns_server(Link *l, DnsServer *s) {
                 log_info("Switching to DNS server %s for interface %s.", strna(ip), l->name);
         }
 
-        l->current_dns_server = s;
+        dns_server_unref(l->current_dns_server);
+        l->current_dns_server = dns_server_ref(s);
 
         if (l->unicast_scope)
                 dns_cache_flush(&l->unicast_scope->cache);
@@ -350,7 +351,9 @@ void link_next_dns_server(Link *l) {
         if (!l->current_dns_server)
                 return;
 
-        if (l->current_dns_server->servers_next) {
+        /* Change to the next one, but make sure to follow the linked
+         * list only if this server is actually still linked. */
+        if (l->current_dns_server->linked && l->current_dns_server->servers_next) {
                 link_set_dns_server(l, l->current_dns_server->servers_next);
                 return;
         }
index e3ab27c2497329b3dc757c73a50b6e49bb229c37..eb00015bd55ad3e1cd91aade9bf75797ebd56e17 100644 (file)
@@ -30,8 +30,13 @@ typedef struct Link Link;
 typedef struct LinkAddress LinkAddress;
 
 #include "resolved-dns-rr.h"
+#include "resolved-dns-search-domain.h"
+#include "resolved-dns-server.h"
 #include "resolved-manager.h"
 
+#define LINK_SEARCH_DOMAINS_MAX 32
+#define LINK_DNS_SERVERS_MAX 32
+
 struct LinkAddress {
         Link *link;
 
@@ -56,6 +61,10 @@ struct Link {
 
         LIST_HEAD(DnsServer, dns_servers);
         DnsServer *current_dns_server;
+        unsigned n_dns_servers;
+
+        LIST_HEAD(DnsSearchDomain, search_domains);
+        unsigned n_search_domains;
 
         Support llmnr_support;
 
@@ -76,7 +85,6 @@ LinkAddress* link_find_address(Link *l, int family, const union in_addr_union *i
 void link_add_rrs(Link *l, bool force_remove);
 
 DnsServer* link_set_dns_server(Link *l, DnsServer *s);
-DnsServer* link_find_dns_server(Link *l, int family, const union in_addr_union *in_addr);
 DnsServer* link_get_dns_server(Link *l);
 void link_next_dns_server(Link *l);
 
index a588538b52afa2eb892804444ba4cbcd1b212531..f1f454c7868b56eed077ab060ac1b135946436fe 100644 (file)
@@ -21,7 +21,6 @@
 
 #include <netinet/in.h>
 #include <poll.h>
-#include <resolv.h>
 #include <sys/ioctl.h>
 
 #include "af-list.h"
@@ -40,6 +39,7 @@
 #include "resolved-conf.h"
 #include "resolved-llmnr.h"
 #include "resolved-manager.h"
+#include "resolved-resolv-conf.h"
 #include "socket-util.h"
 #include "string-table.h"
 #include "string-util.h"
@@ -351,7 +351,7 @@ static int determine_hostname(char **llmnr_hostname, char **mdns_hostname) {
                 return -EINVAL;
         }
 
-        r = dns_label_escape(label, r, &n);
+        r = dns_label_escape_new(label, r, &n);
         if (r < 0)
                 return log_error_errno(r, "Failed to escape host name: %m");
 
@@ -476,10 +476,7 @@ int manager_new(Manager **ret) {
 
         m->llmnr_support = SUPPORT_YES;
         m->read_resolv_conf = true;
-
-        r = manager_parse_dns_server(m, DNS_SERVER_FALLBACK, DNS_SERVERS);
-        if (r < 0)
-                return r;
+        m->need_builtin_fallbacks = true;
 
         r = sd_event_default(&m->event);
         if (r < 0)
@@ -536,15 +533,16 @@ Manager *manager_free(Manager *m) {
         if (!m)
                 return NULL;
 
+        dns_server_unlink_all(m->dns_servers);
+        dns_server_unlink_all(m->fallback_dns_servers);
+        dns_search_domain_unlink_all(m->search_domains);
+
         while ((l = hashmap_first(m->links)))
                link_free(l);
 
         while (m->dns_queries)
                 dns_query_free(m->dns_queries);
 
-        manager_flush_dns_servers(m, DNS_SERVER_SYSTEM);
-        manager_flush_dns_servers(m, DNS_SERVER_FALLBACK);
-
         dns_scope_free(m->unicast_scope);
 
         hashmap_free(m->links);
@@ -553,6 +551,9 @@ Manager *manager_free(Manager *m) {
         sd_event_source_unref(m->network_event_source);
         sd_network_monitor_unref(m->network_monitor);
 
+        sd_netlink_unref(m->rtnl);
+        sd_event_source_unref(m->rtnl_event_source);
+
         manager_llmnr_stop(m);
 
         sd_bus_slot_unref(m->prepare_for_sleep_slot);
@@ -576,294 +577,6 @@ Manager *manager_free(Manager *m) {
         return NULL;
 }
 
-int manager_read_resolv_conf(Manager *m) {
-        _cleanup_fclose_ FILE *f = NULL;
-        struct stat st, own;
-        char line[LINE_MAX];
-        DnsServer *s, *nx;
-        usec_t t;
-        int r;
-
-        assert(m);
-
-        /* Reads the system /etc/resolv.conf, if it exists and is not
-         * symlinked to our own resolv.conf instance */
-
-        if (!m->read_resolv_conf)
-                return 0;
-
-        r = stat("/etc/resolv.conf", &st);
-        if (r < 0) {
-                if (errno != ENOENT)
-                        log_warning_errno(errno, "Failed to open /etc/resolv.conf: %m");
-                r = -errno;
-                goto clear;
-        }
-
-        /* Have we already seen the file? */
-        t = timespec_load(&st.st_mtim);
-        if (t == m->resolv_conf_mtime)
-                return 0;
-
-        m->resolv_conf_mtime = t;
-
-        /* Is it symlinked to our own file? */
-        if (stat("/run/systemd/resolve/resolv.conf", &own) >= 0 &&
-            st.st_dev == own.st_dev &&
-            st.st_ino == own.st_ino) {
-                r = 0;
-                goto clear;
-        }
-
-        f = fopen("/etc/resolv.conf", "re");
-        if (!f) {
-                if (errno != ENOENT)
-                        log_warning_errno(errno, "Failed to open /etc/resolv.conf: %m");
-                r = -errno;
-                goto clear;
-        }
-
-        if (fstat(fileno(f), &st) < 0) {
-                r = log_error_errno(errno, "Failed to stat open file: %m");
-                goto clear;
-        }
-
-        LIST_FOREACH(servers, s, m->dns_servers)
-                s->marked = true;
-
-        FOREACH_LINE(line, f, r = -errno; goto clear) {
-                union in_addr_union address;
-                int family;
-                char *l;
-                const char *a;
-
-                truncate_nl(line);
-
-                l = strstrip(line);
-                if (*l == '#' || *l == ';')
-                        continue;
-
-                a = first_word(l, "nameserver");
-                if (!a)
-                        continue;
-
-                r = in_addr_from_string_auto(a, &family, &address);
-                if (r < 0) {
-                        log_warning("Failed to parse name server %s.", a);
-                        continue;
-                }
-
-                LIST_FOREACH(servers, s, m->dns_servers)
-                        if (s->family == family && in_addr_equal(family, &s->address, &address) > 0)
-                                break;
-
-                if (s)
-                        s->marked = false;
-                else {
-                        r = dns_server_new(m, NULL, DNS_SERVER_SYSTEM, NULL, family, &address);
-                        if (r < 0)
-                                goto clear;
-                }
-        }
-
-        LIST_FOREACH_SAFE(servers, s, nx, m->dns_servers)
-                if (s->marked) {
-                        LIST_REMOVE(servers, m->dns_servers, s);
-                        dns_server_unref(s);
-                }
-
-        /* Whenever /etc/resolv.conf changes, start using the first
-         * DNS server of it. This is useful to deal with broken
-         * network managing implementations (like NetworkManager),
-         * that when connecting to a VPN place both the VPN DNS
-         * servers and the local ones in /etc/resolv.conf. Without
-         * resetting the DNS server to use back to the first entry we
-         * will continue to use the local one thus being unable to
-         * resolve VPN domains. */
-        manager_set_dns_server(m, m->dns_servers);
-
-        return 0;
-
-clear:
-        while (m->dns_servers) {
-                s = m->dns_servers;
-
-                LIST_REMOVE(servers, m->dns_servers, s);
-                dns_server_unref(s);
-        }
-
-        return r;
-}
-
-static void write_resolv_conf_server(DnsServer *s, FILE *f, unsigned *count) {
-        _cleanup_free_ char *t  = NULL;
-        int r;
-
-        assert(s);
-        assert(f);
-        assert(count);
-
-        r = in_addr_to_string(s->family, &s->address, &t);
-        if (r < 0) {
-                log_warning_errno(r, "Invalid DNS address. Ignoring: %m");
-                return;
-        }
-
-        if (*count == MAXNS)
-                fputs("# Too many DNS servers configured, the following entries may be ignored.\n", f);
-
-        fprintf(f, "nameserver %s\n", t);
-        (*count) ++;
-}
-
-static void write_resolv_conf_search(
-                const char *domain, FILE *f,
-                unsigned *count,
-                unsigned *length) {
-
-        assert(domain);
-        assert(f);
-        assert(length);
-
-        if (*count >= MAXDNSRCH ||
-            *length + strlen(domain) > 256) {
-                if (*count == MAXDNSRCH)
-                        fputs(" # Too many search domains configured, remaining ones ignored.", f);
-                if (*length <= 256)
-                        fputs(" # Total length of all search domains is too long, remaining ones ignored.", f);
-
-                return;
-        }
-
-        fprintf(f, " %s", domain);
-
-        (*length) += strlen(domain);
-        (*count) ++;
-}
-
-static int write_resolv_conf_contents(FILE *f, OrderedSet *dns, OrderedSet *domains) {
-        Iterator i;
-
-        fputs("# This file is managed by systemd-resolved(8). Do not edit.\n#\n"
-              "# Third party programs must not access this file directly, but\n"
-              "# only through the symlink at /etc/resolv.conf. To manage\n"
-              "# resolv.conf(5) in a different way, replace the symlink by a\n"
-              "# static file or a different symlink.\n\n", f);
-
-        if (ordered_set_isempty(dns))
-                fputs("# No DNS servers known.\n", f);
-        else {
-                DnsServer *s;
-                unsigned count = 0;
-
-                ORDERED_SET_FOREACH(s, dns, i)
-                        write_resolv_conf_server(s, f, &count);
-        }
-
-        if (!ordered_set_isempty(domains)) {
-                unsigned length = 0, count = 0;
-                char *domain;
-
-                fputs("search", f);
-                ORDERED_SET_FOREACH(domain, domains, i)
-                        write_resolv_conf_search(domain, f, &count, &length);
-                fputs("\n", f);
-        }
-
-        return fflush_and_check(f);
-}
-
-int manager_write_resolv_conf(Manager *m) {
-        static const char path[] = "/run/systemd/resolve/resolv.conf";
-        _cleanup_free_ char *temp_path = NULL;
-        _cleanup_fclose_ FILE *f = NULL;
-        _cleanup_ordered_set_free_ OrderedSet *dns = NULL, *domains = NULL;
-        DnsServer *s;
-        Iterator i;
-        Link *l;
-        int r;
-
-        assert(m);
-
-        /* Read the system /etc/resolv.conf first */
-        manager_read_resolv_conf(m);
-
-        /* Add the full list to a set, to filter out duplicates */
-        dns = ordered_set_new(&dns_server_hash_ops);
-        if (!dns)
-                return -ENOMEM;
-
-        domains = ordered_set_new(&dns_name_hash_ops);
-        if (!domains)
-                return -ENOMEM;
-
-        /* First add the system-wide servers */
-        LIST_FOREACH(servers, s, m->dns_servers) {
-                r = ordered_set_put(dns, s);
-                if (r == -EEXIST)
-                        continue;
-                if (r < 0)
-                        return r;
-        }
-
-        /* Then, add the per-link servers and domains */
-        HASHMAP_FOREACH(l, m->links, i) {
-                char **domain;
-
-                LIST_FOREACH(servers, s, l->dns_servers) {
-                        r = ordered_set_put(dns, s);
-                        if (r == -EEXIST)
-                                continue;
-                        if (r < 0)
-                                return r;
-                }
-
-                if (!l->unicast_scope)
-                        continue;
-
-                STRV_FOREACH(domain, l->unicast_scope->domains) {
-                        r = ordered_set_put(domains, *domain);
-                        if (r == -EEXIST)
-                                continue;
-                        if (r < 0)
-                                return r;
-                }
-        }
-
-        /* If we found nothing, add the fallback servers */
-        if (ordered_set_isempty(dns)) {
-                LIST_FOREACH(servers, s, m->fallback_dns_servers) {
-                        r = ordered_set_put(dns, s);
-                        if (r == -EEXIST)
-                                continue;
-                        if (r < 0)
-                                return r;
-                }
-        }
-
-        r = fopen_temporary_label(path, path, &f, &temp_path);
-        if (r < 0)
-                return r;
-
-        fchmod(fileno(f), 0644);
-
-        r = write_resolv_conf_contents(f, dns, domains);
-        if (r < 0)
-                goto fail;
-
-        if (rename(temp_path, path) < 0) {
-                r = -errno;
-                goto fail;
-        }
-
-        return 0;
-
-fail:
-        (void) unlink(path);
-        (void) unlink(temp_path);
-        return r;
-}
-
 int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) {
         _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
         union {
@@ -1171,97 +884,6 @@ int manager_send(Manager *m, int fd, int ifindex, int family, const union in_add
         return -EAFNOSUPPORT;
 }
 
-DnsServer* manager_find_dns_server(Manager *m, int family, const union in_addr_union *in_addr) {
-        DnsServer *s;
-
-        assert(m);
-        assert(in_addr);
-
-        LIST_FOREACH(servers, s, m->dns_servers)
-                if (s->family == family && in_addr_equal(family, &s->address, in_addr) > 0)
-                        return s;
-
-        LIST_FOREACH(servers, s, m->fallback_dns_servers)
-                if (s->family == family && in_addr_equal(family, &s->address, in_addr) > 0)
-                        return s;
-
-        return NULL;
-}
-
-DnsServer *manager_set_dns_server(Manager *m, DnsServer *s) {
-        assert(m);
-
-        if (m->current_dns_server == s)
-                return s;
-
-        if (s) {
-                _cleanup_free_ char *ip = NULL;
-
-                in_addr_to_string(s->family, &s->address, &ip);
-                log_info("Switching to system DNS server %s.", strna(ip));
-        }
-
-        m->current_dns_server = s;
-
-        if (m->unicast_scope)
-                dns_cache_flush(&m->unicast_scope->cache);
-
-        return s;
-}
-
-DnsServer *manager_get_dns_server(Manager *m) {
-        Link *l;
-        assert(m);
-
-        /* Try to read updates resolv.conf */
-        manager_read_resolv_conf(m);
-
-        if (!m->current_dns_server)
-                manager_set_dns_server(m, m->dns_servers);
-
-        if (!m->current_dns_server) {
-                bool found = false;
-                Iterator i;
-
-                /* No DNS servers configured, let's see if there are
-                 * any on any links. If not, we use the fallback
-                 * servers */
-
-                HASHMAP_FOREACH(l, m->links, i)
-                        if (l->dns_servers) {
-                                found = true;
-                                break;
-                        }
-
-                if (!found)
-                        manager_set_dns_server(m, m->fallback_dns_servers);
-        }
-
-        return m->current_dns_server;
-}
-
-void manager_next_dns_server(Manager *m) {
-        assert(m);
-
-        /* If there's currently no DNS server set, then the next
-         * manager_get_dns_server() will find one */
-        if (!m->current_dns_server)
-                return;
-
-        /* Change to the next one */
-        if (m->current_dns_server->servers_next) {
-                manager_set_dns_server(m, m->current_dns_server->servers_next);
-                return;
-        }
-
-        /* If there was no next one, then start from the beginning of
-         * the list */
-        if (m->current_dns_server->type == DNS_SERVER_FALLBACK)
-                manager_set_dns_server(m, m->fallback_dns_servers);
-        else
-                manager_set_dns_server(m, m->dns_servers);
-}
-
 uint32_t manager_find_mtu(Manager *m) {
         uint32_t mtu = 0;
         Link *l;
@@ -1415,42 +1037,102 @@ void manager_verify_all(Manager *m) {
                 dns_zone_verify_all(&s->zone);
 }
 
-void manager_flush_dns_servers(Manager *m, DnsServerType t) {
+int manager_is_own_hostname(Manager *m, const char *name) {
+        int r;
+
+        assert(m);
+        assert(name);
+
+        if (m->llmnr_hostname) {
+                r = dns_name_equal(name, m->llmnr_hostname);
+                if (r != 0)
+                        return r;
+        }
+
+        if (m->mdns_hostname)
+                return dns_name_equal(name, m->mdns_hostname);
+
+        return 0;
+}
+
+int manager_compile_dns_servers(Manager *m, OrderedSet **dns) {
         DnsServer *s;
+        Iterator i;
+        Link *l;
+        int r;
 
         assert(m);
+        assert(dns);
+
+        r = ordered_set_ensure_allocated(dns, &dns_server_hash_ops);
+        if (r < 0)
+                return r;
 
-        if (t == DNS_SERVER_SYSTEM)
-                while (m->dns_servers) {
-                        s = m->dns_servers;
+        /* First add the system-wide servers and domains */
+        LIST_FOREACH(servers, s, m->dns_servers) {
+                r = ordered_set_put(*dns, s);
+                if (r == -EEXIST)
+                        continue;
+                if (r < 0)
+                        return r;
+        }
 
-                        LIST_REMOVE(servers, m->dns_servers, s);
-                        dns_server_unref(s);
+        /* Then, add the per-link servers */
+        HASHMAP_FOREACH(l, m->links, i) {
+                LIST_FOREACH(servers, s, l->dns_servers) {
+                        r = ordered_set_put(*dns, s);
+                        if (r == -EEXIST)
+                                continue;
+                        if (r < 0)
+                                return r;
                 }
+        }
 
-        if (t == DNS_SERVER_FALLBACK)
-                while (m->fallback_dns_servers) {
-                        s = m->fallback_dns_servers;
-
-                        LIST_REMOVE(servers, m->fallback_dns_servers, s);
-                        dns_server_unref(s);
+        /* If we found nothing, add the fallback servers */
+        if (ordered_set_isempty(*dns)) {
+                LIST_FOREACH(servers, s, m->fallback_dns_servers) {
+                        r = ordered_set_put(*dns, s);
+                        if (r == -EEXIST)
+                                continue;
+                        if (r < 0)
+                                return r;
                 }
+        }
+
+        return 0;
 }
 
-int manager_is_own_hostname(Manager *m, const char *name) {
+int manager_compile_search_domains(Manager *m, OrderedSet **domains) {
+        DnsSearchDomain *d;
+        Iterator i;
+        Link *l;
         int r;
 
         assert(m);
-        assert(name);
+        assert(domains);
 
-        if (m->llmnr_hostname) {
-                r = dns_name_equal(name, m->llmnr_hostname);
-                if (r != 0)
+        r = ordered_set_ensure_allocated(domains, &dns_name_hash_ops);
+        if (r < 0)
+                return r;
+
+        LIST_FOREACH(domains, d, m->search_domains) {
+                r = ordered_set_put(*domains, d->name);
+                if (r == -EEXIST)
+                        continue;
+                if (r < 0)
                         return r;
         }
 
-        if (m->mdns_hostname)
-                return dns_name_equal(name, m->mdns_hostname);
+        HASHMAP_FOREACH(l, m->links, i) {
+
+                LIST_FOREACH(domains, d, l->search_domains) {
+                        r = ordered_set_put(*domains, d->name);
+                        if (r == -EEXIST)
+                                continue;
+                        if (r < 0)
+                                return r;
+                }
+        }
 
         return 0;
 }
index fe7fe99505817e0736c3c5085ece4fad8f65d3bf..d00c4445839ace26e89d7c90e8f9539b06c9601f 100644 (file)
 ***/
 
 #include "sd-event.h"
-#include "sd-network.h"
 #include "sd-netlink.h"
-#include "list.h"
+#include "sd-network.h"
+
 #include "hashmap.h"
+#include "list.h"
+#include "ordered-set.h"
 
 typedef struct Manager Manager;
 typedef enum Support Support;
@@ -39,9 +41,14 @@ enum Support {
 };
 
 #include "resolved-dns-query.h"
+#include "resolved-dns-search-domain.h"
+#include "resolved-dns-server.h"
 #include "resolved-dns-stream.h"
 #include "resolved-link.h"
 
+#define MANAGER_SEARCH_DOMAINS_MAX 32
+#define MANAGER_DNS_SERVERS_MAX 32
+
 struct Manager {
         sd_event *event;
 
@@ -67,9 +74,15 @@ struct Manager {
         /* Unicast dns */
         LIST_HEAD(DnsServer, dns_servers);
         LIST_HEAD(DnsServer, fallback_dns_servers);
+        unsigned n_dns_servers; /* counts both main and fallback */
         DnsServer *current_dns_server;
 
-        bool read_resolv_conf;
+        LIST_HEAD(DnsSearchDomain, search_domains);
+        unsigned n_search_domains;
+
+        bool need_builtin_fallbacks:1;
+
+        bool read_resolv_conf:1;
         usec_t resolv_conf_mtime;
 
         LIST_HEAD(DnsScope, dns_scopes);
@@ -112,13 +125,6 @@ int manager_new(Manager **ret);
 Manager* manager_free(Manager *m);
 
 int manager_start(Manager *m);
-int manager_read_resolv_conf(Manager *m);
-int manager_write_resolv_conf(Manager *m);
-
-DnsServer *manager_set_dns_server(Manager *m, DnsServer *s);
-DnsServer *manager_find_dns_server(Manager *m, int family, const union in_addr_union *in_addr);
-DnsServer *manager_get_dns_server(Manager *m);
-void manager_next_dns_server(Manager *m);
 
 uint32_t manager_find_mtu(Manager *m);
 
@@ -137,13 +143,14 @@ DnsScope* manager_find_scope(Manager *m, DnsPacket *p);
 
 void manager_verify_all(Manager *m);
 
-void manager_flush_dns_servers(Manager *m, DnsServerType t);
-
 DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
 
 #define EXTRA_CMSG_SPACE 1024
 
 int manager_is_own_hostname(Manager *m, const char *name);
 
+int manager_compile_dns_servers(Manager *m, OrderedSet **servers);
+int manager_compile_search_domains(Manager *m, OrderedSet **domains);
+
 const char* support_to_string(Support p) _const_;
 int support_from_string(const char *s) _pure_;
diff --git a/src/resolve/resolved-resolv-conf.c b/src/resolve/resolved-resolv-conf.c
new file mode 100644 (file)
index 0000000..956f380
--- /dev/null
@@ -0,0 +1,273 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2014 Tom Gundersen <teg@jklm.no>
+
+  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 <resolv.h>
+
+#include "alloc-util.h"
+#include "dns-domain.h"
+#include "fd-util.h"
+#include "fileio-label.h"
+#include "fileio.h"
+#include "ordered-set.h"
+#include "resolved-conf.h"
+#include "resolved-resolv-conf.h"
+#include "string-util.h"
+#include "strv.h"
+
+int manager_read_resolv_conf(Manager *m) {
+        _cleanup_fclose_ FILE *f = NULL;
+        struct stat st, own;
+        char line[LINE_MAX];
+        usec_t t;
+        int r;
+
+        assert(m);
+
+        /* Reads the system /etc/resolv.conf, if it exists and is not
+         * symlinked to our own resolv.conf instance */
+
+        if (!m->read_resolv_conf)
+                return 0;
+
+        r = stat("/etc/resolv.conf", &st);
+        if (r < 0) {
+                if (errno == ENOENT)
+                        return 0;
+
+                r = log_warning_errno(errno, "Failed to stat /etc/resolv.conf: %m");
+                goto clear;
+        }
+
+        /* Have we already seen the file? */
+        t = timespec_load(&st.st_mtim);
+        if (t == m->resolv_conf_mtime)
+                return 0;
+
+        /* Is it symlinked to our own file? */
+        if (stat("/run/systemd/resolve/resolv.conf", &own) >= 0 &&
+            st.st_dev == own.st_dev &&
+            st.st_ino == own.st_ino)
+                return 0;
+
+        f = fopen("/etc/resolv.conf", "re");
+        if (!f) {
+                if (errno == ENOENT)
+                        return 0;
+
+                r = log_warning_errno(errno, "Failed to open /etc/resolv.conf: %m");
+                goto clear;
+        }
+
+        if (fstat(fileno(f), &st) < 0) {
+                r = log_error_errno(errno, "Failed to stat open file: %m");
+                goto clear;
+        }
+
+        dns_server_mark_all(m->dns_servers);
+        dns_search_domain_mark_all(m->search_domains);
+
+        FOREACH_LINE(line, f, r = -errno; goto clear) {
+                const char *a;
+                char *l;
+
+                l = strstrip(line);
+                if (*l == '#' || *l == ';')
+                        continue;
+
+                a = first_word(l, "nameserver");
+                if (a) {
+                        r = manager_add_dns_server_by_string(m, DNS_SERVER_SYSTEM, a);
+                        if (r < 0)
+                                log_warning_errno(r, "Failed to parse DNS server address '%s', ignoring.", a);
+
+                        continue;
+                }
+
+                a = first_word(l, "domain");
+                if (!a) /* We treat "domain" lines, and "search" lines as equivalent, and add both to our list. */
+                        a = first_word(l, "search");
+                if (a) {
+                        r = manager_parse_search_domains_and_warn(m, a);
+                        if (r < 0)
+                                log_warning_errno(r, "Failed to parse search domain string '%s', ignoring.", a);
+                }
+        }
+
+        m->resolv_conf_mtime = t;
+
+        /* Flush out all servers and search domains that are still
+         * marked. Those are then ones that didn't appear in the new
+         * /etc/resolv.conf */
+        dns_server_unlink_marked(m->dns_servers);
+        dns_search_domain_unlink_marked(m->search_domains);
+
+        /* Whenever /etc/resolv.conf changes, start using the first
+         * DNS server of it. This is useful to deal with broken
+         * network managing implementations (like NetworkManager),
+         * that when connecting to a VPN place both the VPN DNS
+         * servers and the local ones in /etc/resolv.conf. Without
+         * resetting the DNS server to use back to the first entry we
+         * will continue to use the local one thus being unable to
+         * resolve VPN domains. */
+        manager_set_dns_server(m, m->dns_servers);
+
+        /* Unconditionally flush the cache when /etc/resolv.conf is
+         * modified, even if the data it contained was completely
+         * identical to the previous version we used. We do this
+         * because altering /etc/resolv.conf is typically done when
+         * the network configuration changes, and that should be
+         * enough to flush the global unicast DNS cache. */
+        if (m->unicast_scope)
+                dns_cache_flush(&m->unicast_scope->cache);
+
+        return 0;
+
+clear:
+        dns_server_unlink_all(m->dns_servers);
+        dns_search_domain_unlink_all(m->search_domains);
+        return r;
+}
+
+static void write_resolv_conf_server(DnsServer *s, FILE *f, unsigned *count) {
+        _cleanup_free_ char *t  = NULL;
+        int r;
+
+        assert(s);
+        assert(f);
+        assert(count);
+
+        r = in_addr_to_string(s->family, &s->address, &t);
+        if (r < 0) {
+                log_warning_errno(r, "Invalid DNS address. Ignoring: %m");
+                return;
+        }
+
+        if (*count == MAXNS)
+                fputs("# Too many DNS servers configured, the following entries may be ignored.\n", f);
+        (*count) ++;
+
+        fprintf(f, "nameserver %s\n", t);
+}
+
+static void write_resolv_conf_search(
+                const char *domain,
+                FILE *f,
+                unsigned *count,
+                unsigned *length) {
+
+        assert(domain);
+        assert(f);
+        assert(length);
+
+        if (*count >= MAXDNSRCH ||
+            *length + strlen(domain) > 256) {
+                if (*count == MAXDNSRCH)
+                        fputs(" # Too many search domains configured, remaining ones ignored.", f);
+                if (*length <= 256)
+                        fputs(" # Total length of all search domains is too long, remaining ones ignored.", f);
+
+                return;
+        }
+
+        (*length) += strlen(domain);
+        (*count) ++;
+
+        fputc(' ', f);
+        fputs(domain, f);
+}
+
+static int write_resolv_conf_contents(FILE *f, OrderedSet *dns, OrderedSet *domains) {
+        Iterator i;
+
+        fputs("# This file is managed by systemd-resolved(8). Do not edit.\n#\n"
+              "# Third party programs must not access this file directly, but\n"
+              "# only through the symlink at /etc/resolv.conf. To manage\n"
+              "# resolv.conf(5) in a different way, replace the symlink by a\n"
+              "# static file or a different symlink.\n\n", f);
+
+        if (ordered_set_isempty(dns))
+                fputs("# No DNS servers known.\n", f);
+        else {
+                unsigned count = 0;
+                DnsServer *s;
+
+                ORDERED_SET_FOREACH(s, dns, i)
+                        write_resolv_conf_server(s, f, &count);
+        }
+
+        if (!ordered_set_isempty(domains)) {
+                unsigned length = 0, count = 0;
+                char *domain;
+
+                fputs("search", f);
+                ORDERED_SET_FOREACH(domain, domains, i)
+                        write_resolv_conf_search(domain, f, &count, &length);
+                fputs("\n", f);
+        }
+
+        return fflush_and_check(f);
+}
+
+int manager_write_resolv_conf(Manager *m) {
+
+        #define PRIVATE_RESOLV_CONF "/run/systemd/resolve/resolv.conf"
+
+        _cleanup_ordered_set_free_ OrderedSet *dns = NULL, *domains = NULL;
+        _cleanup_free_ char *temp_path = NULL;
+        _cleanup_fclose_ FILE *f = NULL;
+        int r;
+
+        assert(m);
+
+        /* Read the system /etc/resolv.conf first */
+        manager_read_resolv_conf(m);
+
+        /* Add the full list to a set, to filter out duplicates */
+        r = manager_compile_dns_servers(m, &dns);
+        if (r < 0)
+                return r;
+
+        r = manager_compile_search_domains(m, &domains);
+        if (r < 0)
+                return r;
+
+        r = fopen_temporary_label(PRIVATE_RESOLV_CONF, PRIVATE_RESOLV_CONF, &f, &temp_path);
+        if (r < 0)
+                return r;
+
+        fchmod(fileno(f), 0644);
+
+        r = write_resolv_conf_contents(f, dns, domains);
+        if (r < 0)
+                goto fail;
+
+        if (rename(temp_path, PRIVATE_RESOLV_CONF) < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        return 0;
+
+fail:
+        (void) unlink(PRIVATE_RESOLV_CONF);
+        (void) unlink(temp_path);
+        return r;
+}
diff --git a/src/resolve/resolved-resolv-conf.h b/src/resolve/resolved-resolv-conf.h
new file mode 100644 (file)
index 0000000..a3355e9
--- /dev/null
@@ -0,0 +1,27 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2014 Tom Gundersen <teg@jklm.no>
+
+  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 "resolved-manager.h"
+
+int manager_read_resolv_conf(Manager *m);
+int manager_write_resolv_conf(Manager *m);
index 7ba0546f4a8b698ba80d87079f32d33dd79be682..be406b71fe7e09ce23702c0709ef2fcbd8c25c96 100644 (file)
@@ -26,6 +26,7 @@
 #include "mkdir.h"
 #include "resolved-conf.h"
 #include "resolved-manager.h"
+#include "resolved-resolv-conf.h"
 #include "selinux-util.h"
 #include "signal-util.h"
 #include "user-util.h"
@@ -81,8 +82,10 @@ int main(int argc, char *argv[]) {
         }
 
         r = manager_parse_config_file(m);
-        if (r < 0)
-                log_warning_errno(r, "Failed to parse configuration file: %m");
+        if (r < 0) {
+                log_error_errno(r, "Failed to parse configuration file: %m");
+                goto finish;
+        }
 
         r = manager_start(m);
         if (r < 0) {
index 3eb19e42b742e60cadee813a98ce3be2fbe697ce..39ecf832177257f207575d98cc0d1a1741dcaa93 100644 (file)
@@ -14,4 +14,5 @@
 [Resolve]
 #DNS=
 #FallbackDNS=@DNS_SERVERS@
+#Domains=
 #LLMNR=yes
index df6a4f0074a147de12318d2f6a9dc1c1fa56f543..e1accc467ba01a26ad4b1456bcdbf97673e6a160 100644 (file)
@@ -648,6 +648,11 @@ static int transient_timer_set_properties(sd_bus_message *m) {
         if (r < 0)
                 return r;
 
+        /* Automatically clean up our transient timers */
+        r = sd_bus_message_append(m, "(sv)", "RemainAfterElapse", "b", false);
+        if (r < 0)
+                return r;
+
         if (arg_on_active) {
                 r = sd_bus_message_append(m, "(sv)", "OnActiveSec", "t", arg_on_active);
                 if (r < 0)
@@ -687,6 +692,51 @@ static int transient_timer_set_properties(sd_bus_message *m) {
         return 0;
 }
 
+static int make_unit_name(sd_bus *bus, UnitType t, char **ret) {
+        const char *unique, *id;
+        char *p;
+        int r;
+
+        assert(bus);
+        assert(t >= 0);
+        assert(t < _UNIT_TYPE_MAX);
+
+        r = sd_bus_get_unique_name(bus, &unique);
+        if (r < 0) {
+                sd_id128_t rnd;
+
+                /* We couldn't get the unique name, which is a pretty
+                 * common case if we are connected to systemd
+                 * directly. In that case, just pick a random uuid as
+                 * name */
+
+                r = sd_id128_randomize(&rnd);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to generate random run unit name: %m");
+
+                if (asprintf(ret, "run-r" SD_ID128_FORMAT_STR ".%s", SD_ID128_FORMAT_VAL(rnd), unit_type_to_string(t)) < 0)
+                        return log_oom();
+
+                return 0;
+        }
+
+        /* We managed to get the unique name, then let's use that to
+         * name our transient units. */
+
+        id = startswith(unique, ":1.");
+        if (!id) {
+                log_error("Unique name %s has unexpected format.", unique);
+                return -EINVAL;
+        }
+
+        p = strjoin("run-u", id, ".", unit_type_to_string(t), NULL);
+        if (!p)
+                return log_oom();
+
+        *ret = p;
+        return 0;
+}
+
 static int start_transient_service(
                 sd_bus *bus,
                 char **argv) {
@@ -763,8 +813,11 @@ static int start_transient_service(
                 r = unit_name_mangle_with_suffix(arg_unit, UNIT_NAME_NOGLOB, ".service", &service);
                 if (r < 0)
                         return log_error_errno(r, "Failed to mangle unit name: %m");
-        } else if (asprintf(&service, "run-"PID_FMT".service", getpid()) < 0)
-                return log_oom();
+        } else {
+                r = make_unit_name(bus, UNIT_SERVICE, &service);
+                if (r < 0)
+                        return r;
+        }
 
         r = sd_bus_message_new_method_call(
                         bus,
@@ -882,8 +935,11 @@ static int start_transient_scope(
                 r = unit_name_mangle_with_suffix(arg_unit, UNIT_NAME_NOGLOB, ".scope", &scope);
                 if (r < 0)
                         return log_error_errno(r, "Failed to mangle scope name: %m");
-        } else if (asprintf(&scope, "run-"PID_FMT".scope", getpid()) < 0)
-                return log_oom();
+        } else {
+                r = make_unit_name(bus, UNIT_SCOPE, &scope);
+                if (r < 0)
+                        return r;
+        }
 
         r = sd_bus_message_new_method_call(
                         bus,
@@ -1052,9 +1108,15 @@ static int start_transient_timer(
 
                         break;
                 }
-        } else if ((asprintf(&service, "run-"PID_FMT".service", getpid()) < 0) ||
-                   (asprintf(&timer, "run-"PID_FMT".timer", getpid()) < 0))
-                return log_oom();
+        } else {
+                r = make_unit_name(bus, UNIT_SERVICE, &service);
+                if (r < 0)
+                        return r;
+
+                r = unit_name_change_suffix(service, ".timer", &timer);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to change unit suffix: %m");
+        }
 
         r = sd_bus_message_new_method_call(
                         bus,
index cf612e87225038221021a55e260bc1e12797b8e9..256a6a5900e3811d8d2522b450b89d7fa69a6ca2 100644 (file)
@@ -23,9 +23,9 @@
 
 #ifdef HAVE_ACL
 
+#include <acl/libacl.h>
 #include <stdbool.h>
 #include <sys/acl.h>
-#include <acl/libacl.h>
 
 #include "macro.h"
 
index 73ceeba18f986289698cecc7cb1f83e8183950a1..8775808da4057398dbe4f63390c736051047d1dd 100644 (file)
@@ -1428,16 +1428,36 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
                         return bus_log_create_error(r);
 
                 return 0;
+
         } else if (streq(field, "EnvironmentFile")) {
+
                 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, "EnvironmentFiles");
                 if (r < 0)
-                        return r;
+                        return bus_log_create_error(r);
 
                 r = sd_bus_message_append(m, "v", "a(sb)", 1,
                                           eq[0] == '-' ? eq + 1 : eq,
                                           eq[0] == '-');
                 if (r < 0)
-                        return r;
+                        return bus_log_create_error(r);
+
+                return 0;
+
+        } else if (streq(field, "RandomizedDelaySec")) {
+                usec_t t;
+
+                r = parse_sec(eq, &t);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to parse RandomizedDelaySec= parameter: %s", eq);
+
+                r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, "RandomizedDelayUSec");
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_message_append(m, "v", "t", t);
+                if (r < 0)
+                        return bus_log_create_error(r);
+
                 return 0;
         }
 
@@ -1450,13 +1470,11 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
                        "SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies",
                        "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit",
                        "PrivateTmp", "PrivateDevices", "PrivateNetwork", "NoNewPrivileges",
-                       "SyslogLevelPrefix", "Delegate")) {
+                       "SyslogLevelPrefix", "Delegate", "RemainAfterElapse")) {
 
                 r = parse_boolean(eq);
-                if (r < 0) {
-                        log_error("Failed to parse boolean assignment %s.", assignment);
-                        return -EINVAL;
-                }
+                if (r < 0)
+                        return log_error_errno(r, "Failed to parse boolean assignment %s.", assignment);
 
                 r = sd_bus_message_append(m, "v", "b", r);
 
index aa832454b5c4d5a6b95bf37e9e859b5056f21e84..5842bdd15ea1620a627b1fe2dbb4bcd3e7e6743a 100644 (file)
@@ -23,6 +23,7 @@
 
 #include <stdbool.h>
 #include <sys/types.h>
+
 #include "logs-show.h"
 
 int show_cgroup_by_path(const char *path, const char *prefix, unsigned columns, bool kernel_threads, OutputFlags flags);
index fb0234baae1039eebf19e60145ff999ca4e8aed2..2872b22d9d18b1896e31a6db8202c6918dea749d 100644 (file)
@@ -21,8 +21,8 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <stdio.h>
 #include <stdbool.h>
+#include <stdio.h>
 
 #include "macro.h"
 
index 7af15e00984e077f3a27cb35912b98749f2973ea..4cf6355b7155f16d4f48e254c59e6443529463c5 100644 (file)
@@ -29,6 +29,8 @@
 #include "hexdecoct.h"
 #include "parse-util.h"
 #include "string-util.h"
+#include "strv.h"
+#include "utf8.h"
 
 int dns_label_unescape(const char **name, char *dest, size_t sz) {
         const char *n;
@@ -180,30 +182,31 @@ int dns_label_unescape_suffix(const char *name, const char **label_terminal, cha
         return r;
 }
 
-int dns_label_escape(const char *p, size_t l, char **ret) {
-        _cleanup_free_ char *s = NULL;
+int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) {
         char *q;
-        int r;
-
-        assert(p);
-        assert(ret);
 
         if (l > DNS_LABEL_MAX)
                 return -EINVAL;
+        if (sz < 1)
+                return -ENOSPC;
 
-        s = malloc(l * 4 + 1);
-        if (!s)
-                return -ENOMEM;
+        assert(p);
+        assert(dest);
 
-        q = s;
+        q = dest;
         while (l > 0) {
 
                 if (*p == '.' || *p == '\\') {
 
+                        if (sz < 3)
+                                return -ENOSPC;
+
                         /* Dot or backslash */
                         *(q++) = '\\';
                         *(q++) = *p;
 
+                        sz -= 2;
+
                 } else if (*p == '_' ||
                            *p == '-' ||
                            (*p >= '0' && *p <= '9') ||
@@ -211,15 +214,27 @@ int dns_label_escape(const char *p, size_t l, char **ret) {
                            (*p >= 'A' && *p <= 'Z')) {
 
                         /* Proper character */
+
+                        if (sz < 2)
+                                return -ENOSPC;
+
                         *(q++) = *p;
+                        sz -= 1;
+
                 } else if ((uint8_t) *p >= (uint8_t) ' ' && *p != 127) {
 
                         /* Everything else */
+
+                        if (sz < 5)
+                                return -ENOSPC;
+
                         *(q++) = '\\';
                         *(q++) = '0' + (char) ((uint8_t) *p / 100);
                         *(q++) = '0' + (char) (((uint8_t) *p / 10) % 10);
                         *(q++) = '0' + (char) ((uint8_t) *p % 10);
 
+                        sz -= 4;
+
                 } else
                         return -EINVAL;
 
@@ -228,8 +243,28 @@ int dns_label_escape(const char *p, size_t l, char **ret) {
         }
 
         *q = 0;
+        return (int) (q - dest);
+}
+
+int dns_label_escape_new(const char *p, size_t l, char **ret) {
+        _cleanup_free_ char *s = NULL;
+        int r;
+
+        assert(p);
+        assert(ret);
+
+        if (l > DNS_LABEL_MAX)
+                return -EINVAL;
+
+        s = new(char, DNS_LABEL_ESCAPED_MAX);
+        if (!s)
+                return -ENOMEM;
+
+        r = dns_label_escape(p, l, s, DNS_LABEL_ESCAPED_MAX);
+        if (r < 0)
+                return r;
+
         *ret = s;
-        r = q - s;
         s = NULL;
 
         return r;
@@ -349,28 +384,32 @@ int dns_name_concat(const char *a, const char *b, char **_ret) {
                 if (k > 0)
                         r = k;
 
-                r = dns_label_escape(label, r, &t);
-                if (r < 0)
-                        return r;
-
                 if (_ret) {
-                        if (!GREEDY_REALLOC(ret, allocated, n + !first + strlen(t) + 1))
+                        if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX))
                                 return -ENOMEM;
 
+                        r = dns_label_escape(label, r, ret + n + !first, DNS_LABEL_ESCAPED_MAX);
+                        if (r < 0)
+                                return r;
+
                         if (!first)
-                                ret[n++] = '.';
-                        else
-                                first = false;
+                                ret[n] = '.';
+                } else {
+                        char escaped[DNS_LABEL_ESCAPED_MAX];
 
-                        memcpy(ret + n, t, r);
+                        r = dns_label_escape(label, r, escaped, sizeof(escaped));
+                        if (r < 0)
+                                return r;
                 }
 
+                if (!first)
+                        n++;
+                else
+                        first = false;
+
                 n += r;
         }
 
-        if (n > DNS_NAME_MAX)
-                return -EINVAL;
-
         if (_ret) {
                 if (!GREEDY_REALLOC(ret, allocated, n + 1))
                         return -ENOMEM;
@@ -546,6 +585,73 @@ int dns_name_endswith(const char *name, const char *suffix) {
         }
 }
 
+int dns_name_change_suffix(const char *name, const char *old_suffix, const char *new_suffix, char **ret) {
+        const char *n, *s, *saved_before = NULL, *saved_after = NULL, *prefix;
+        int r, q, k, w;
+
+        assert(name);
+        assert(old_suffix);
+        assert(new_suffix);
+        assert(ret);
+
+        n = name;
+        s = old_suffix;
+
+        for (;;) {
+                char ln[DNS_LABEL_MAX+1], ls[DNS_LABEL_MAX+1];
+
+                if (!saved_before)
+                        saved_before = n;
+
+                r = dns_label_unescape(&n, ln, sizeof(ln));
+                if (r < 0)
+                        return r;
+                k = dns_label_undo_idna(ln, r, ln, sizeof(ln));
+                if (k < 0)
+                        return k;
+                if (k > 0)
+                        r = k;
+
+                if (!saved_after)
+                        saved_after = n;
+
+                q = dns_label_unescape(&s, ls, sizeof(ls));
+                if (q < 0)
+                        return q;
+                w = dns_label_undo_idna(ls, q, ls, sizeof(ls));
+                if (w < 0)
+                        return w;
+                if (w > 0)
+                        q = w;
+
+                if (r == 0 && q == 0)
+                        break;
+                if (r == 0 && saved_after == n) {
+                        *ret = NULL; /* doesn't match */
+                        return 0;
+                }
+
+                ln[r] = ls[q] = 0;
+
+                if (r != q || strcasecmp(ln, ls)) {
+
+                        /* Not the same, let's jump back, and try with the next label again */
+                        s = old_suffix;
+                        n = saved_after;
+                        saved_after = saved_before = NULL;
+                }
+        }
+
+        /* Found it! Now generate the new name */
+        prefix = strndupa(name, saved_before - name);
+
+        r = dns_name_concat(prefix, new_suffix, ret);
+        if (r < 0)
+                return r;
+
+        return 1;
+}
+
 int dns_name_between(const char *a, const char *b, const char *c) {
         int n;
 
@@ -684,34 +790,283 @@ int dns_name_address(const char *p, int *family, union in_addr_union *address) {
         return 0;
 }
 
-int dns_name_root(const char *name) {
-        char label[DNS_LABEL_MAX+1];
-        int r;
+bool dns_name_is_root(const char *name) {
 
         assert(name);
 
-        r = dns_label_unescape(&name, label, sizeof(label));
-        if (r < 0)
-                return r;
+        /* There are exactly two ways to encode the root domain name:
+         * as empty string, or with a single dot. */
 
-        return r == 0 && *name == 0;
+        return STR_IN_SET(name, "", ".");
 }
 
-int dns_name_single_label(const char *name) {
+bool dns_name_is_single_label(const char *name) {
         char label[DNS_LABEL_MAX+1];
         int r;
 
         assert(name);
 
         r = dns_label_unescape(&name, label, sizeof(label));
+        if (r <= 0)
+                return false;
+
+        return dns_name_is_root(name);
+}
+
+/* Encode a domain name according to RFC 1035 Section 3.1 */
+int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len) {
+        uint8_t *label_length;
+        uint8_t *out;
+        int r;
+
+        assert_return(buffer, -EINVAL);
+        assert_return(domain, -EINVAL);
+        assert_return(domain[0], -EINVAL);
+
+        out = buffer;
+
+        do {
+                /* reserve a byte for label length */
+                if (len == 0)
+                        return -ENOBUFS;
+                len--;
+                label_length = out;
+                out++;
+
+                /* convert and copy a single label */
+                r = dns_label_unescape(&domain, (char *) out, len);
+                if (r < 0)
+                        return r;
+
+                /* fill label length, move forward */
+                *label_length = r;
+                out += r;
+                len -= r;
+        } while (r != 0);
+
+        return out - buffer;
+}
+
+static bool srv_type_label_is_valid(const char *label, size_t n) {
+        size_t k;
+
+        assert(label);
+
+        if (n < 2) /* Label needs to be at least 2 chars long */
+                return false;
+
+        if (label[0] != '_') /* First label char needs to be underscore */
+                return false;
+
+        /* Second char must be a letter */
+        if (!(label[1] >= 'A' && label[1] <= 'Z') &&
+            !(label[1] >= 'a' && label[1] <= 'z'))
+                return false;
+
+        /* Third and further chars must be alphanumeric or a hyphen */
+        for (k = 2; k < n; k++) {
+                if (!(label[k] >= 'A' && label[k] <= 'Z') &&
+                    !(label[k] >= 'a' && label[k] <= 'z') &&
+                    !(label[k] >= '0' && label[k] <= '9') &&
+                    label[k] != '-')
+                        return false;
+        }
+
+        return true;
+}
+
+bool dns_srv_type_is_valid(const char *name) {
+        unsigned c = 0;
+        int r;
+
+        if (!name)
+                return false;
+
+        for (;;) {
+                char label[DNS_LABEL_MAX];
+
+                /* This more or less implements RFC 6335, Section 5.1 */
+
+                r = dns_label_unescape(&name, label, sizeof(label));
+                if (r < 0)
+                        return false;
+                if (r == 0)
+                        break;
+
+                if (c >= 2)
+                        return false;
+
+                if (!srv_type_label_is_valid(label, r))
+                        return false;
+
+                c++;
+        }
+
+        return c == 2; /* exactly two labels */
+}
+
+bool dns_service_name_is_valid(const char *name) {
+        size_t l;
+
+        /* This more or less implements RFC 6763, Section 4.1.1 */
+
+        if (!name)
+                return false;
+
+        if (!utf8_is_valid(name))
+                return false;
+
+        if (string_has_cc(name, NULL))
+                return false;
+
+        l = strlen(name);
+        if (l <= 0)
+                return false;
+        if (l > 63)
+                return false;
+
+        return true;
+}
+
+int dns_service_join(const char *name, const char *type, const char *domain, char **ret) {
+        char escaped[DNS_LABEL_ESCAPED_MAX];
+        _cleanup_free_ char *n = NULL;
+        int r;
+
+        assert(type);
+        assert(domain);
+        assert(ret);
+
+        if (!dns_srv_type_is_valid(type))
+                return -EINVAL;
+
+        if (!name)
+                return dns_name_concat(type, domain, ret);
+
+        if (!dns_service_name_is_valid(name))
+                return -EINVAL;
+
+        r = dns_label_escape(name, strlen(name), escaped, sizeof(escaped));
         if (r < 0)
                 return r;
-        if (r == 0)
-                return 0;
 
-        r = dns_label_unescape(&name, label, sizeof(label));
+        r = dns_name_concat(type, domain, &n);
         if (r < 0)
                 return r;
 
-        return r == 0 && *name == 0;
+        return dns_name_concat(escaped, n, ret);
+}
+
+static bool dns_service_name_label_is_valid(const char *label, size_t n) {
+        char *s;
+
+        assert(label);
+
+        if (memchr(label, 0, n))
+                return false;
+
+        s = strndupa(label, n);
+        return dns_service_name_is_valid(s);
+}
+
+int dns_service_split(const char *joined, char **_name, char **_type, char **_domain) {
+        _cleanup_free_ char *name = NULL, *type = NULL, *domain = NULL;
+        const char *p = joined, *q = NULL, *d = NULL;
+        char a[DNS_LABEL_MAX], b[DNS_LABEL_MAX], c[DNS_LABEL_MAX];
+        int an, bn, cn, r;
+        unsigned x = 0;
+
+        assert(joined);
+
+        /* Get first label from the full name */
+        an = dns_label_unescape(&p, a, sizeof(a));
+        if (an < 0)
+                return an;
+
+        if (an > 0) {
+                x++;
+
+                /* If there was a first label, try to get the second one */
+                bn = dns_label_unescape(&p, b, sizeof(b));
+                if (bn < 0)
+                        return bn;
+
+                if (bn > 0) {
+                        x++;
+
+                        /* If there was a second label, try to get the third one */
+                        q = p;
+                        cn = dns_label_unescape(&p, c, sizeof(c));
+                        if (cn < 0)
+                                return cn;
+
+                        if (cn > 0)
+                                x++;
+                } else
+                        cn = 0;
+        } else
+                an = 0;
+
+        if (x >= 2 && srv_type_label_is_valid(b, bn)) {
+
+                if (x >= 3 && srv_type_label_is_valid(c, cn)) {
+
+                        if (dns_service_name_label_is_valid(a, an)) {
+
+                                /* OK, got <name> . <type> . <type2> . <domain> */
+
+                                name = strndup(a, an);
+                                if (!name)
+                                        return -ENOMEM;
+
+                                type = new(char, bn+1+cn+1);
+                                if (!type)
+                                        return -ENOMEM;
+                                strcpy(stpcpy(stpcpy(type, b), "."), c);
+
+                                d = p;
+                                goto finish;
+                        }
+
+                } else if (srv_type_label_is_valid(a, an)) {
+
+                        /* OK, got <type> . <type2> . <domain> */
+
+                        name = NULL;
+
+                        type = new(char, an+1+bn+1);
+                        if (!type)
+                                return -ENOMEM;
+                        strcpy(stpcpy(stpcpy(type, a), "."), b);
+
+                        d = q;
+                        goto finish;
+                }
+        }
+
+        name = NULL;
+        type = NULL;
+        d = joined;
+
+finish:
+        r = dns_name_normalize(d, &domain);
+        if (r < 0)
+                return r;
+
+        if (_domain) {
+                *_domain = domain;
+                domain = NULL;
+        }
+
+        if (_type) {
+                *_type = type;
+                type = NULL;
+        }
+
+        if (_name) {
+                *_name = name;
+                name = NULL;
+        }
+
+        return 0;
 }
index 1f0d242c18bb0e5aecde3aba12a4e2a5f571ac26..99c72574dbb9ef8bfbe4e11ce88c0eec50f7f9e9 100644 (file)
 #include "in-addr-util.h"
 
 #define DNS_LABEL_MAX 63
-#define DNS_NAME_MAX 255
+#define DNS_LABEL_ESCAPED_MAX (DNS_LABEL_MAX*4+1)
 
 int dns_label_unescape(const char **name, char *dest, size_t sz);
 int dns_label_unescape_suffix(const char *name, const char **label_end, char *dest, size_t sz);
-int dns_label_escape(const char *p, size_t l, char **ret);
+int dns_label_escape(const char *p, size_t l, char *dest, size_t sz);
+int dns_label_escape_new(const char *p, size_t l, char **ret);
 
 int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max);
 int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max);
@@ -62,8 +63,18 @@ int dns_name_between(const char *a, const char *b, const char *c);
 int dns_name_equal(const char *x, const char *y);
 int dns_name_endswith(const char *name, const char *suffix);
 
+int dns_name_change_suffix(const char *name, const char *old_suffix, const char *new_suffix, char **ret);
+
 int dns_name_reverse(int family, const union in_addr_union *a, char **ret);
 int dns_name_address(const char *p, int *family, union in_addr_union *a);
 
-int dns_name_root(const char *name);
-int dns_name_single_label(const char *name);
+bool dns_name_is_root(const char *name);
+bool dns_name_is_single_label(const char *name);
+
+int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len);
+
+bool dns_srv_type_is_valid(const char *name);
+bool dns_service_name_is_valid(const char *name);
+
+int dns_service_join(const char *name, const char *type, const char *domain, char **ret);
+int dns_service_split(const char *joined, char **name, char **type, char **domain);
index e953a12737e066caf02eb7749bc80e6f2e031e16..5cb4c3af4e55531184c85fe732d00ee9ceaae09d 100644 (file)
@@ -24,6 +24,7 @@
 #include <stdbool.h>
 
 #include "sd-id128.h"
+
 #include "time-util.h"
 
 #define EFI_VENDOR_LOADER SD_ID128_MAKE(4a,67,b0,82,0a,4c,41,cf,b6,c7,44,0b,29,bb,8c,4f)
index 569e1faa55acea78237ee278179f09e472c197dc..98927bbc5982b6d2088ebe682c2ab385a9681cab 100644 (file)
@@ -26,8 +26,8 @@
 
 #include "sd-journal.h"
 
-#include "util.h"
 #include "output-mode.h"
+#include "util.h"
 
 int output_journal(
                 FILE *f,
index f041600fbfb6d1ea9257e000bc5c676b2dcc3251..038db7453c3567afe1dfb5f8d8c6e1667c2ae540 100644 (file)
@@ -21,9 +21,9 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include "time-util.h"
-#include "lockfile-util.h"
 #include "hashmap.h"
+#include "lockfile-util.h"
+#include "time-util.h"
 
 typedef enum ImageType {
         IMAGE_DIRECTORY,
index 3657aa5d9c5693d5747236fddabdafba6f6fd259..a7b51a91dacecb9ba1394f4bd8b0d60b8dcc9f77 100644 (file)
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <nss.h>
+#include <grp.h>
 #include <netdb.h>
-#include <resolv.h>
+#include <nss.h>
 #include <pwd.h>
-#include <grp.h>
+#include <resolv.h>
 
 
 #define NSS_GETHOSTBYNAME_PROTOTYPES(module)            \
index 51b82d57dbf7b44f2e9ec8725deeba82c9496be7..f478d809c24fbd72596176034e1d78ffff2ffd62 100644 (file)
@@ -3360,6 +3360,7 @@ typedef struct UnitStatusInfo {
         usec_t inactive_enter_timestamp;
 
         bool need_daemon_reload;
+        bool transient;
 
         /* Service */
         pid_t main_pid;
@@ -3459,7 +3460,7 @@ static void print_status_info(
 
         path = i->source_path ? i->source_path : i->fragment_path;
 
-        if (i->load_error)
+        if (i->load_error != 0)
                 printf("   Loaded: %s%s%s (Reason: %s)\n",
                        on, strna(i->load_state), off, i->load_error);
         else if (path && !isempty(i->unit_file_state) && !isempty(i->unit_file_preset))
@@ -3475,6 +3476,9 @@ static void print_status_info(
                 printf("   Loaded: %s%s%s\n",
                        on, strna(i->load_state), off);
 
+        if (i->transient)
+                printf("Transient: yes\n");
+
         if (!strv_isempty(i->dropin_paths)) {
                 _cleanup_free_ char *dir = NULL;
                 bool last = false;
@@ -3839,6 +3843,8 @@ static int status_property(const char *name, sd_bus_message *m, UnitStatusInfo *
                         i->condition_result = b;
                 else if (streq(name, "AssertResult"))
                         i->assert_result = b;
+                else if (streq(name, "Transient"))
+                        i->transient = b;
 
                 break;
         }
@@ -4646,8 +4652,7 @@ static int show(int argc, char *argv[], void *userdata) {
                 return -EINVAL;
         }
 
-        if (show_properties)
-                pager_open_if_enabled();
+        pager_open_if_enabled();
 
         if (show_status)
                 /* Increase max number of open files to 16K if we can, we
@@ -6545,8 +6550,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                         return version();
 
                 case 't': {
-                        if (isempty(optarg))
-                                return log_error_errno(r, "--type requires arguments.");
+                        if (isempty(optarg)) {
+                                log_error("--type requires arguments.");
+                                return -EINVAL;
+                        }
 
                         p = optarg;
                         for(;;) {
@@ -6778,8 +6785,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_STATE: {
-                        if (isempty(optarg))
-                                return log_error_errno(r, "--signal requires arguments.");
+                        if (isempty(optarg)) {
+                                log_error("--signal requires arguments.");
+                                return -EINVAL;
+                        }
 
                         p = optarg;
                         for(;;) {
index 43cf247cdfdd29260f45daf88801f5f6b49ad11f..d8adf59acab4b379f9df19f14153a43e74e8989a 100644 (file)
@@ -27,8 +27,9 @@
 #include <sys/types.h>
 #include <sys/uio.h>
 
-#include "sd-id128.h"
 #include "sd-event.h"
+#include "sd-id128.h"
+
 #include "_sd-common.h"
 
 _SD_BEGIN_DECLARATIONS;
index 214e77cab14e95c43c12b0485b66bc8302f6c054..c26cd1be3a617938af45b8d2728cafa6f6ece2b6 100644 (file)
@@ -22,8 +22,8 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <sys/types.h>
 #include <inttypes.h>
+#include <sys/types.h>
 
 #include "_sd-common.h"
 
index fc117258213499350c0ebcce6d3888c980e3ef17..edf80563ac547db50debd94491adacccd79cc775 100644 (file)
@@ -23,8 +23,8 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <sys/types.h>
 #include <inttypes.h>
+#include <sys/types.h>
 
 #include "_sd-common.h"
 
index c0146158f3798fa1909652993bfb57d3466232d7..fc1d70e738348dc61b4d0bace3c93cebed507286 100644 (file)
@@ -27,8 +27,8 @@
 #include <netinet/in.h>
 #include <sys/types.h>
 
-#include "sd-event.h"
 #include "sd-dhcp-lease.h"
+#include "sd-event.h"
 
 #include "_sd-common.h"
 
index 55bceb1ea59a8d57ee957f89fa1c92fe2b8a383e..56b63c38da48094dc19e44047498a6d3db040e14 100644 (file)
@@ -27,6 +27,7 @@
 #include <netinet/in.h>
 
 #include "sd-event.h"
+
 #include "_sd-common.h"
 
 _SD_BEGIN_DECLARATIONS;
index 0ca6c07de4fae2ed4f80f616753130f897c06faf..29e95e2492dab6a472be2e956d2c644140fd3876 100644 (file)
@@ -26,8 +26,8 @@
 #include <net/ethernet.h>
 #include <sys/types.h>
 
-#include "sd-event.h"
 #include "sd-dhcp6-lease.h"
+#include "sd-event.h"
 
 #include "_sd-common.h"
 
index 565de5495aebdfc768cfc369cc1ea8205393ead7..fb97f7f28dc48d4a57be99832aa9790c67942f3d 100644 (file)
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <sys/types.h>
-#include <sys/signalfd.h>
-#include <sys/epoll.h>
 #include <inttypes.h>
 #include <signal.h>
+#include <sys/epoll.h>
+#include <sys/signalfd.h>
+#include <sys/types.h>
 
 #include "_sd-common.h"
 
@@ -56,7 +56,8 @@ enum {
         SD_EVENT_PENDING,
         SD_EVENT_RUNNING,
         SD_EVENT_EXITING,
-        SD_EVENT_FINISHED
+        SD_EVENT_FINISHED,
+        SD_EVENT_PREPARING,
 };
 
 enum {
@@ -87,9 +88,9 @@ int sd_event_add_post(sd_event *e, sd_event_source **s, sd_event_handler_t callb
 int sd_event_add_exit(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata);
 
 int sd_event_prepare(sd_event *e);
-int sd_event_wait(sd_event *e, uint64_t timeout);
+int sd_event_wait(sd_event *e, uint64_t usec);
 int sd_event_dispatch(sd_event *e);
-int sd_event_run(sd_event *e, uint64_t timeout);
+int sd_event_run(sd_event *e, uint64_t usec);
 int sd_event_loop(sd_event *e);
 int sd_event_exit(sd_event *e, int code);
 
index 6337d614525046d624736e0c4e2ae5421d75bc8a..c1e79640eb84c88f26bb661d18e180446577451e 100644 (file)
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <netinet/in.h>
 #include <net/ethernet.h>
+#include <netinet/in.h>
 
 #include "sd-event.h"
+
 #include "_sd-common.h"
 
 _SD_BEGIN_DECLARATIONS;
index 2949f1dfb284de3ce7ba61ac10f11d2cb7a1cdf1..1d25f02bd0807bb0b62c77cad5d1405482cc2c17 100644 (file)
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <netinet/in.h>
 #include <net/ethernet.h>
+#include <netinet/in.h>
 
 #include "sd-event.h"
+
 #include "_sd-common.h"
 
 _SD_BEGIN_DECLARATIONS;
index 00237a21589f9d7715aa1117666ba0f281db6368..facb6d8a952996bc0fec93409ad089c19160eb94 100644 (file)
 ***/
 
 #include <inttypes.h>
-#include <sys/types.h>
 #include <stdarg.h>
+#include <sys/types.h>
 #include <sys/uio.h>
 #include <syslog.h>
 
 #include "sd-id128.h"
+
 #include "_sd-common.h"
 
 /* Journal APIs. See sd-journal(3) for more information. */
index 31651ce132f2af0c7dcb77b70ed0a62eea119551..16d297a52deafb5f329fd43856b17a3eca281007 100644 (file)
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <net/ethernet.h>
 #include <inttypes.h>
+#include <net/ethernet.h>
 
 #include "sd-event.h"
+
 #include "_sd-common.h"
 
 _SD_BEGIN_DECLARATIONS;
index 59c6eedcda5f6702dca85b3e8d8e6dd78aec979d..2ad6bcb3578969a149c316780e583613c4481587 100644 (file)
@@ -22,8 +22,8 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <sys/types.h>
 #include <inttypes.h>
+#include <sys/types.h>
 
 #include "_sd-common.h"
 
index 8aedaec6d1b69101fbb70e7cfd7d919f8b539f59..072832a91619dd67e39e4703a1fbe9ee25128af2 100644 (file)
@@ -23,6 +23,7 @@
 ***/
 
 #include "sd-id128.h"
+
 #include "_sd-common.h"
 
 _SD_BEGIN_DECLARATIONS;
index 80e24325f7dc263712d9099bf843f6f8c42158da..71e65d44259ade9cc2a1a55dd600533704b5e9b8 100644 (file)
@@ -26,6 +26,7 @@
 #include <net/ethernet.h>
 
 #include "sd-event.h"
+
 #include "_sd-common.h"
 
 _SD_BEGIN_DECLARATIONS;
index 2960deda0a4a8b107644b057ac241a129aab0a1a..dd5cc04ca64384b9efc291cd2127567cb2ead771 100644 (file)
 ***/
 
 #include <inttypes.h>
-#include <netinet/in.h>
 #include <netinet/ether.h>
+#include <netinet/in.h>
 #include <linux/rtnetlink.h>
 #include <linux/neighbour.h>
 
 #include "sd-event.h"
+
 #include "_sd-common.h"
 
 _SD_BEGIN_DECLARATIONS;
index 4179015fbfd068f3e52a48929e8c752d0b0e750e..076f45745d78ec2b0defdae38d6d7f1af4314a9b 100644 (file)
@@ -23,8 +23,8 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <sys/types.h>
 #include <inttypes.h>
+#include <sys/types.h>
 
 #include "_sd-common.h"
 
index 82c4b39efe1ab3434b5143f9955737f075456772..bfe32102f8f608d6ee70e6f6e4ebeb464b216154 100644 (file)
@@ -28,6 +28,7 @@
 #include <sys/types.h>
 
 #include "sd-event.h"
+
 #include "_sd-common.h"
 
 _SD_BEGIN_DECLARATIONS;
index 70819b0371c0043458a3e7c7d31c016a3fb9d10d..9cef7154c61fff8fc7bb27204a26836f08cb2879 100644 (file)
@@ -75,7 +75,7 @@ static void test_next(const char *input, const char *new_tz, usec_t after, usec_
 
         u = after;
         r = calendar_spec_next_usec(c, after, &u);
-        printf("At: %s\n", r < 0 ? strerror(-r) : format_timestamp(buf, sizeof(buf), u));
+        printf("At: %s\n", r < 0 ? strerror(-r) : format_timestamp_us(buf, sizeof(buf), u));
         if (expect != (usec_t)-1)
                 assert_se(r >= 0 && u == expect);
         else
@@ -123,6 +123,9 @@ int main(int argc, char* argv[]) {
         test_one("annually", "*-01-01 00:00:00");
         test_one("*:2/3", "*-*-* *:02/3:00");
         test_one("2015-10-25 01:00:00 uTc", "2015-10-25 01:00:00 UTC");
+        test_one("2016-03-27 03:17:00.4200005", "2016-03-27 03:17:00.420001");
+        test_one("2016-03-27 03:17:00/0.42", "2016-03-27 03:17:00/0.420000");
+        test_one("2016-03-27 03:17:00/0.42", "2016-03-27 03:17:00/0.420000");
 
         test_next("2016-03-27 03:17:00", "", 12345, 1459048620000000);
         test_next("2016-03-27 03:17:00", "CET", 12345, 1459041420000000);
@@ -131,11 +134,19 @@ int main(int argc, char* argv[]) {
         test_next("2016-03-27 03:17:00 UTC", "", 12345, 1459048620000000);
         test_next("2016-03-27 03:17:00 UTC", "CET", 12345, 1459048620000000);
         test_next("2016-03-27 03:17:00 UTC", "EET", 12345, 1459048620000000);
+        test_next("2016-03-27 03:17:00.420000001 UTC", "EET", 12345, 1459048620420000);
+        test_next("2016-03-27 03:17:00.4200005 UTC", "EET", 12345, 1459048620420001);
+        test_next("2015-11-13 09:11:23.42", "EET", 12345, 1447398683420000);
+        test_next("2015-11-13 09:11:23.42/1.77", "EET", 1447398683420000, 1447398685190000);
+        test_next("2015-11-13 09:11:23.42/1.77", "EET", 1447398683419999, 1447398683420000);
 
         assert_se(calendar_spec_from_string("test", &c) < 0);
         assert_se(calendar_spec_from_string("", &c) < 0);
         assert_se(calendar_spec_from_string("7", &c) < 0);
         assert_se(calendar_spec_from_string("121212:1:2", &c) < 0);
+        assert_se(calendar_spec_from_string("2000-03-05.23 00:00:00", &c) < 0);
+        assert_se(calendar_spec_from_string("2000-03-05 00:00.1:00", &c) < 0);
+        assert_se(calendar_spec_from_string("00:00:00/0.00000001", &c) < 0);
 
         return 0;
 }
index d5778748a05036722f3a8bb1a0197880d310adc4..f010e4e19ac4a26ba4e46e5c4b427bf61821dbda 100644 (file)
@@ -52,6 +52,36 @@ static void test_dns_label_unescape(void) {
         test_dns_label_unescape_one("foobar.", "foobar", 20, 6);
 }
 
+static void test_dns_name_to_wire_format_one(const char *what, const char *expect, size_t buffer_sz, int ret) {
+        uint8_t buffer[buffer_sz];
+        int r;
+
+        r = dns_name_to_wire_format(what, buffer, buffer_sz);
+        assert_se(r == ret);
+
+        if (r < 0)
+                return;
+
+        assert_se(!memcmp(buffer, expect, r));
+}
+
+static void test_dns_name_to_wire_format(void) {
+        const char out1[] = { 3, 'f', 'o', 'o', 0 };
+        const char out2[] = { 5, 'h', 'a', 'l', 'l', 'o', 3, 'f', 'o', 'o', 3, 'b', 'a', 'r', 0 };
+        const char out3[] = { 4, ' ', 'f', 'o', 'o', 3, 'b', 'a', 'r', 0 };
+
+        test_dns_name_to_wire_format_one("", NULL, 0, -EINVAL);
+
+        test_dns_name_to_wire_format_one("foo", out1, sizeof(out1), sizeof(out1));
+        test_dns_name_to_wire_format_one("foo", out1, sizeof(out1) + 1, sizeof(out1));
+        test_dns_name_to_wire_format_one("foo", out1, sizeof(out1) - 1, -ENOBUFS);
+
+        test_dns_name_to_wire_format_one("hallo.foo.bar", out2, sizeof(out2), sizeof(out2));
+        test_dns_name_to_wire_format_one("hallo.foo..bar", NULL, 32, -EINVAL);
+
+        test_dns_name_to_wire_format_one("\\032foo.bar", out3, sizeof(out3), sizeof(out3));
+}
+
 static void test_dns_label_unescape_suffix_one(const char *what, const char *expect1, const char *expect2, size_t buffer_sz, int ret1, int ret2) {
         char buffer[buffer_sz];
         const char *label;
@@ -96,7 +126,7 @@ static void test_dns_label_escape_one(const char *what, size_t l, const char *ex
         _cleanup_free_ char *t = NULL;
         int r;
 
-        r = dns_label_escape(what, l, &t);
+        r = dns_label_escape_new(what, l, &t);
         assert_se(r == ret);
 
         if (r < 0)
@@ -216,21 +246,21 @@ static void test_dns_name_endswith(void) {
         test_dns_name_endswith_one("x.y\001.z", "waldo", -EINVAL);
 }
 
-static void test_dns_name_root(void) {
-        assert_se(dns_name_root("") == true);
-        assert_se(dns_name_root(".") == true);
-        assert_se(dns_name_root("xxx") == false);
-        assert_se(dns_name_root("xxx.") == false);
-        assert_se(dns_name_root("..") == -EINVAL);
+static void test_dns_name_is_root(void) {
+        assert_se(dns_name_is_root(""));
+        assert_se(dns_name_is_root("."));
+        assert_se(!dns_name_is_root("xxx"));
+        assert_se(!dns_name_is_root("xxx."));
+        assert_se(!dns_name_is_root(".."));
 }
 
-static void test_dns_name_single_label(void) {
-        assert_se(dns_name_single_label("") == false);
-        assert_se(dns_name_single_label(".") == false);
-        assert_se(dns_name_single_label("..") == -EINVAL);
-        assert_se(dns_name_single_label("x") == true);
-        assert_se(dns_name_single_label("x.") == true);
-        assert_se(dns_name_single_label("xx.yy") == false);
+static void test_dns_name_is_single_label(void) {
+        assert_se(!dns_name_is_single_label(""));
+        assert_se(!dns_name_is_single_label("."));
+        assert_se(!dns_name_is_single_label(".."));
+        assert_se(dns_name_is_single_label("x"));
+        assert_se(dns_name_is_single_label("x."));
+        assert_se(!dns_name_is_single_label("xx.yy"));
 }
 
 static void test_dns_name_reverse_one(const char *address, const char *name) {
@@ -286,6 +316,117 @@ static void test_dns_name_is_valid(void) {
         test_dns_name_is_valid_one("\n", 0);
 }
 
+static void test_dns_service_name_is_valid(void) {
+        assert_se(dns_service_name_is_valid("Lennart's Compüter"));
+        assert_se(dns_service_name_is_valid("piff.paff"));
+
+        assert_se(!dns_service_name_is_valid(NULL));
+        assert_se(!dns_service_name_is_valid(""));
+        assert_se(!dns_service_name_is_valid("foo\nbar"));
+        assert_se(!dns_service_name_is_valid("foo\201bar"));
+        assert_se(!dns_service_name_is_valid("this is an overly long string that is certainly longer than 63 characters"));
+}
+
+static void test_dns_srv_type_is_valid(void) {
+
+        assert_se(dns_srv_type_is_valid("_http._tcp"));
+        assert_se(dns_srv_type_is_valid("_foo-bar._tcp"));
+        assert_se(dns_srv_type_is_valid("_w._udp"));
+        assert_se(dns_srv_type_is_valid("_a800._tcp"));
+        assert_se(dns_srv_type_is_valid("_a-800._tcp"));
+
+        assert_se(!dns_srv_type_is_valid(NULL));
+        assert_se(!dns_srv_type_is_valid(""));
+        assert_se(!dns_srv_type_is_valid("x"));
+        assert_se(!dns_srv_type_is_valid("_foo"));
+        assert_se(!dns_srv_type_is_valid("_tcp"));
+        assert_se(!dns_srv_type_is_valid("_"));
+        assert_se(!dns_srv_type_is_valid("_foo."));
+        assert_se(!dns_srv_type_is_valid("_föo._tcp"));
+        assert_se(!dns_srv_type_is_valid("_f\no._tcp"));
+        assert_se(!dns_srv_type_is_valid("_800._tcp"));
+        assert_se(!dns_srv_type_is_valid("_-800._tcp"));
+        assert_se(!dns_srv_type_is_valid("_-foo._tcp"));
+        assert_se(!dns_srv_type_is_valid("_piep._foo._udp"));
+}
+
+static void test_dns_service_join_one(const char *a, const char *b, const char *c, int r, const char *d) {
+        _cleanup_free_ char *x = NULL, *y = NULL, *z = NULL, *t = NULL;
+
+        assert_se(dns_service_join(a, b, c, &t) == r);
+        assert_se(streq_ptr(t, d));
+
+        if (r < 0)
+                return;
+
+        assert_se(dns_service_split(t, &x, &y, &z) >= 0);
+        assert_se(streq_ptr(a, x));
+        assert_se(streq_ptr(b, y));
+        assert_se(streq_ptr(c, z));
+}
+
+static void test_dns_service_join(void) {
+        test_dns_service_join_one("", "", "", -EINVAL, NULL);
+        test_dns_service_join_one("", "_http._tcp", "", -EINVAL, NULL);
+        test_dns_service_join_one("", "_http._tcp", "foo", -EINVAL, NULL);
+        test_dns_service_join_one("foo", "", "foo", -EINVAL, NULL);
+        test_dns_service_join_one("foo", "foo", "foo", -EINVAL, NULL);
+
+        test_dns_service_join_one("foo", "_http._tcp", "", 0, "foo._http._tcp");
+        test_dns_service_join_one(NULL, "_http._tcp", "", 0, "_http._tcp");
+        test_dns_service_join_one("foo", "_http._tcp", "foo", 0, "foo._http._tcp.foo");
+        test_dns_service_join_one(NULL, "_http._tcp", "foo", 0, "_http._tcp.foo");
+        test_dns_service_join_one("Lennart's PC", "_pc._tcp", "foo.bar.com", 0, "Lennart\\039s\\032PC._pc._tcp.foo.bar.com");
+        test_dns_service_join_one(NULL, "_pc._tcp", "foo.bar.com", 0, "_pc._tcp.foo.bar.com");
+}
+
+static void test_dns_service_split_one(const char *joined, const char *a, const char *b, const char *c, int r) {
+        _cleanup_free_ char *x = NULL, *y = NULL, *z = NULL, *t = NULL;
+
+        assert_se(dns_service_split(joined, &x, &y, &z) == r);
+        assert_se(streq_ptr(x, a));
+        assert_se(streq_ptr(y, b));
+        assert_se(streq_ptr(z, c));
+
+        if (r < 0)
+                return;
+
+        if (y) {
+                assert_se(dns_service_join(x, y, z, &t) == 0);
+                assert_se(streq_ptr(joined, t));
+        } else
+                assert_se(!x && streq_ptr(z, joined));
+}
+
+static void test_dns_service_split(void) {
+        test_dns_service_split_one("", NULL, NULL, "", 0);
+        test_dns_service_split_one("foo", NULL, NULL, "foo", 0);
+        test_dns_service_split_one("foo.bar", NULL, NULL, "foo.bar", 0);
+        test_dns_service_split_one("_foo.bar", NULL, NULL, "_foo.bar", 0);
+        test_dns_service_split_one("_foo._bar", NULL, "_foo._bar", "", 0);
+        test_dns_service_split_one("_meh._foo._bar", "_meh", "_foo._bar", "", 0);
+        test_dns_service_split_one("Wuff\\032Wuff._foo._bar.waldo.com", "Wuff Wuff", "_foo._bar", "waldo.com", 0);
+}
+
+static void test_dns_name_change_suffix_one(const char *name, const char *old_suffix, const char *new_suffix, int r, const char *result) {
+        _cleanup_free_ char *s = NULL;
+
+        assert_se(dns_name_change_suffix(name, old_suffix, new_suffix, &s) == r);
+        assert_se(streq_ptr(s, result));
+}
+
+static void test_dns_name_change_suffix(void) {
+        test_dns_name_change_suffix_one("foo.bar", "bar", "waldo", 1, "foo.waldo");
+        test_dns_name_change_suffix_one("foo.bar.waldi.quux", "foo.bar.waldi.quux", "piff.paff", 1, "piff.paff");
+        test_dns_name_change_suffix_one("foo.bar.waldi.quux", "bar.waldi.quux", "piff.paff", 1, "foo.piff.paff");
+        test_dns_name_change_suffix_one("foo.bar.waldi.quux", "waldi.quux", "piff.paff", 1, "foo.bar.piff.paff");
+        test_dns_name_change_suffix_one("foo.bar.waldi.quux", "quux", "piff.paff", 1, "foo.bar.waldi.piff.paff");
+        test_dns_name_change_suffix_one("foo.bar.waldi.quux", "", "piff.paff", 1, "foo.bar.waldi.quux.piff.paff");
+        test_dns_name_change_suffix_one("", "", "piff.paff", 1, "piff.paff");
+        test_dns_name_change_suffix_one("", "", "", 1, "");
+        test_dns_name_change_suffix_one("a", "b", "c", 0, NULL);
+}
+
 int main(int argc, char *argv[]) {
 
         test_dns_label_unescape();
@@ -295,11 +436,17 @@ int main(int argc, char *argv[]) {
         test_dns_name_equal();
         test_dns_name_endswith();
         test_dns_name_between();
-        test_dns_name_root();
-        test_dns_name_single_label();
+        test_dns_name_is_root();
+        test_dns_name_is_single_label();
         test_dns_name_reverse();
         test_dns_name_concat();
         test_dns_name_is_valid();
+        test_dns_name_to_wire_format();
+        test_dns_service_name_is_valid();
+        test_dns_srv_type_is_valid();
+        test_dns_service_join();
+        test_dns_service_split();
+        test_dns_name_change_suffix();
 
         return 0;
 }
index a571a95a70c442afd78455d6a99d52354f3a296c..c20be993507b5d6bd0dde4d54725b707e41ed6e4 100644 (file)
@@ -30,7 +30,7 @@ static int do_test(const uint8_t *in, size_t len, const uint8_t *key) {
         unsigned i, j;
 
         out = siphash24(in, len, key);
-        assert_se(out == htole64(0xa129ca6149be45e5));
+        assert_se(out == 0xa129ca6149be45e5);
 
         /* verify the internal state as given in the above paper */
         siphash24_init(&state, key);
@@ -44,7 +44,7 @@ static int do_test(const uint8_t *in, size_t len, const uint8_t *key) {
         assert_se(state.v2 == 0x634cb3577b01fd3d);
         assert_se(state.v3 == 0xa5224d6f55c7d9c8);
         out = siphash24_finalize(&state);
-        assert_se(out == htole64(0xa129ca6149be45e5));
+        assert_se(out == 0xa129ca6149be45e5);
         assert_se(state.v0 == 0xf6bcd53893fecff1);
         assert_se(state.v1 == 0x54b9964c7ea0d937);
         assert_se(state.v2 == 0x1b38329c099bb55a);
@@ -59,7 +59,7 @@ static int do_test(const uint8_t *in, size_t len, const uint8_t *key) {
                         siphash24_compress(&in[i], j - i, &state);
                         siphash24_compress(&in[j], len - j, &state);
                         out = siphash24_finalize(&state);
-                        assert_se(out == htole64(0xa129ca6149be45e5));
+                        assert_se(out == 0xa129ca6149be45e5);
                 }
         }
         return 0;
index c3973a316e08a596e0a32c75153e226c90733028..0b3630f77c7dd8eb9221ec4d0ddd3414e9a096b4 100644 (file)
@@ -680,11 +680,42 @@ static void test_config_parse_rlimit(void) {
         assert_se(rl[RLIMIT_NOFILE]->rlim_cur == 55);
         assert_se(rl[RLIMIT_NOFILE]->rlim_cur == rl[RLIMIT_NOFILE]->rlim_max);
 
+        assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "55:66", rl, NULL) >= 0);
+        assert_se(rl[RLIMIT_NOFILE]);
+        assert_se(rl[RLIMIT_NOFILE]->rlim_cur == 55);
+        assert_se(rl[RLIMIT_NOFILE]->rlim_max == 66);
+
         assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "infinity", rl, NULL) >= 0);
         assert_se(rl[RLIMIT_NOFILE]);
         assert_se(rl[RLIMIT_NOFILE]->rlim_cur == RLIM_INFINITY);
         assert_se(rl[RLIMIT_NOFILE]->rlim_cur == rl[RLIMIT_NOFILE]->rlim_max);
 
+        assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "infinity:infinity", rl, NULL) >= 0);
+        assert_se(rl[RLIMIT_NOFILE]);
+        assert_se(rl[RLIMIT_NOFILE]->rlim_cur == RLIM_INFINITY);
+        assert_se(rl[RLIMIT_NOFILE]->rlim_cur == rl[RLIMIT_NOFILE]->rlim_max);
+
+        assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "10:20:30", rl, NULL) >= 0);
+        assert_se(rl[RLIMIT_NOFILE]);
+        assert_se(rl[RLIMIT_NOFILE]->rlim_cur == 10);
+        assert_se(rl[RLIMIT_NOFILE]->rlim_max == 20);
+
+        /* Invalid values don't change rl */
+        assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "wat:wat", rl, NULL) >= 0);
+        assert_se(rl[RLIMIT_NOFILE]);
+        assert_se(rl[RLIMIT_NOFILE]->rlim_cur == 10);
+        assert_se(rl[RLIMIT_NOFILE]->rlim_max == 20);
+
+        assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "66:wat", rl, NULL) >= 0);
+        assert_se(rl[RLIMIT_NOFILE]);
+        assert_se(rl[RLIMIT_NOFILE]->rlim_cur == 10);
+        assert_se(rl[RLIMIT_NOFILE]->rlim_max == 20);
+
+        assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "200:100", rl, NULL) >= 0);
+        assert_se(rl[RLIMIT_NOFILE]);
+        assert_se(rl[RLIMIT_NOFILE]->rlim_cur == 10);
+        assert_se(rl[RLIMIT_NOFILE]->rlim_max == 20);
+
         rl[RLIMIT_NOFILE] = mfree(rl[RLIMIT_NOFILE]);
 
         assert_se(config_parse_sec_limit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "56", rl, NULL) >= 0);
@@ -697,6 +728,11 @@ static void test_config_parse_rlimit(void) {
         assert_se(rl[RLIMIT_CPU]->rlim_cur == 57);
         assert_se(rl[RLIMIT_CPU]->rlim_cur == rl[RLIMIT_CPU]->rlim_max);
 
+        assert_se(config_parse_sec_limit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "40s:1m", rl, NULL) >= 0);
+        assert_se(rl[RLIMIT_CPU]);
+        assert_se(rl[RLIMIT_CPU]->rlim_cur == 40);
+        assert_se(rl[RLIMIT_CPU]->rlim_max == 60);
+
         assert_se(config_parse_sec_limit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "infinity", rl, NULL) >= 0);
         assert_se(rl[RLIMIT_CPU]);
         assert_se(rl[RLIMIT_CPU]->rlim_cur == RLIM_INFINITY);
@@ -714,16 +750,31 @@ static void test_config_parse_rlimit(void) {
         assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 58);
         assert_se(rl[RLIMIT_RTTIME]->rlim_cur == rl[RLIMIT_RTTIME]->rlim_max);
 
+        assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "58:60", rl, NULL) >= 0);
+        assert_se(rl[RLIMIT_RTTIME]);
+        assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 58);
+        assert_se(rl[RLIMIT_RTTIME]->rlim_max == 60);
+
         assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "59s", rl, NULL) >= 0);
         assert_se(rl[RLIMIT_RTTIME]);
         assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 59 * USEC_PER_SEC);
         assert_se(rl[RLIMIT_RTTIME]->rlim_cur == rl[RLIMIT_RTTIME]->rlim_max);
 
+        assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "59s:123s", rl, NULL) >= 0);
+        assert_se(rl[RLIMIT_RTTIME]);
+        assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 59 * USEC_PER_SEC);
+        assert_se(rl[RLIMIT_RTTIME]->rlim_max == 123 * USEC_PER_SEC);
+
         assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "infinity", rl, NULL) >= 0);
         assert_se(rl[RLIMIT_RTTIME]);
         assert_se(rl[RLIMIT_RTTIME]->rlim_cur == RLIM_INFINITY);
         assert_se(rl[RLIMIT_RTTIME]->rlim_cur == rl[RLIMIT_RTTIME]->rlim_max);
 
+        assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "infinity:infinity", rl, NULL) >= 0);
+        assert_se(rl[RLIMIT_RTTIME]);
+        assert_se(rl[RLIMIT_RTTIME]->rlim_cur == RLIM_INFINITY);
+        assert_se(rl[RLIMIT_RTTIME]->rlim_cur == rl[RLIMIT_RTTIME]->rlim_max);
+
         assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "2345ms", rl, NULL) >= 0);
         assert_se(rl[RLIMIT_RTTIME]);
         assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 2345 * USEC_PER_MSEC);
index 56466fe462a9c518d69efff3cb366f4b992915b9..cbc19c4054dd946cc9c9ee7d22bb44bef81f0917 100644 (file)
@@ -22,7 +22,6 @@
 ***/
 
 #include "conf-parser.h"
-
 #include "timesyncd-manager.h"
 
 const struct ConfigPerfItem* timesyncd_gperf_lookup(const char *key, unsigned length);
index 090b2fcba88433c71ee46a719878e93110ba82df..fab22cfe84b9f4b48ac6e582ebb36ff27b9077e0 100644 (file)
@@ -22,8 +22,9 @@
 ***/
 
 #include "sd-event.h"
-#include "sd-resolve.h"
 #include "sd-network.h"
+#include "sd-resolve.h"
+
 #include "list.h"
 #include "ratelimit.h"
 
index 18c44445e1e53aed4e73d6d4d65e9e45beff43a6..f764d0737b401f54f82553a18bd4c5f417f7c9a2 100644 (file)
@@ -21,8 +21,8 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include "socket-util.h"
 #include "list.h"
+#include "socket-util.h"
 
 typedef struct ServerAddress ServerAddress;
 typedef struct ServerName ServerName;
index d5f600ef4570afdcc1a79a8827cf3a5149e1bd7d..5e04c821b60df686791ab53b50abdb390281eb8d 100644 (file)
@@ -9,4 +9,4 @@
 # and systemd-coredump(8) and core(5) for the explanation of the
 # setting below.
 
-kernel.core_pattern=|@rootlibexecdir@/systemd-coredump %p %u %g %s %t %e
+kernel.core_pattern=|@rootlibexecdir@/systemd-coredump %P %u %g %s %t %e
index 5c7aca43a8e6a5e4557e783e4a4cade1fbe1ad3a..fa7e73ce3ad17343c638bef03a5a891b97e028e3 100644 (file)
@@ -36,7 +36,7 @@ you can even skip the "clean" and "setup" if you want to run the machine again.
 $ sudo make KERNEL_APPEND="systemd.unit=multi-user.target" run
 
 You can specify a different kernel and initramfs with $KERNEL_BIN and $INITRD.
-(Fedora's default kernel path and initramfs are used by default)
+(Fedora's or Debian's default kernel path and initramfs are used by default)
 
 $ sudo make KERNEL_BIN=/boot/vmlinuz-foo INITRD=/boot/initramfs-bar clean check
 
index b6b474393daa8c7c049d9f093a9f17523b88ea6b..6963d8c88df5c6c486ab272de11658f6b9b07bbc 100755 (executable)
@@ -11,7 +11,7 @@ check_result_qemu() {
     mount ${LOOPDEV}p1 $TESTDIR/root
     [[ -e $TESTDIR/root/testok ]] && ret=0
     [[ -f $TESTDIR/root/failed ]] && cp -a $TESTDIR/root/failed $TESTDIR
-    [[ -f $TESTDIR/root/var/log/journal ]] && cp -a $TESTDIR/root/var/log/journal $TESTDIR
+    cp -a $TESTDIR/root/var/log/journal $TESTDIR
     umount $TESTDIR/root
     [[ -f $TESTDIR/failed ]] && cat $TESTDIR/failed
     ls -l $TESTDIR/journal/*/*.journal
@@ -58,9 +58,16 @@ Type=oneshot
 EOF
 
         setup_testsuite
-    )
+    ) || return 1
     setup_nspawn_root
 
+    # mask some services that we do not want to run in these tests
+    ln -s /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service
+    ln -s /dev/null $initdir/etc/systemd/system/systemd-journal-catalog-update.service
+    ln -s /dev/null $initdir/etc/systemd/system/systemd-networkd.service
+    ln -s /dev/null $initdir/etc/systemd/system/systemd-networkd.socket
+    ln -s /dev/null $initdir/etc/systemd/system/systemd-resolved.service
+
     ddebug "umount $TESTDIR/root"
     umount $TESTDIR/root
 }
index 2997da06ff1e42855376e841dee147044181a9f0..dada99df59a1715c3869a7db5639682224ad861b 100755 (executable)
@@ -13,7 +13,7 @@ check_result_qemu() {
     [[ -f $TESTDIR/root/failed ]] && cp -a $TESTDIR/root/failed $TESTDIR
     cryptsetup luksOpen ${LOOPDEV}p2 varcrypt <$TESTDIR/keyfile
     mount /dev/mapper/varcrypt $TESTDIR/root/var
-    [[ -f $TESTDIR/root/var/log/journal ]] && cp -a $TESTDIR/root/var/log/journal $TESTDIR
+    cp -a $TESTDIR/root/var/log/journal $TESTDIR
     umount $TESTDIR/root/var
     umount $TESTDIR/root
     cryptsetup luksClose /dev/mapper/varcrypt
@@ -76,7 +76,7 @@ EOF
         cat >>$initdir/etc/fstab <<EOF
 /dev/mapper/varcrypt    /var    ext3    defaults 0 1
 EOF
-    )
+    ) || return 1
     setup_nspawn_root
 
     ddebug "umount $TESTDIR/root/var"
index 41e02e2c8acc67ba7b99a4c6060e04946bec029d..83393435f0a48126443cd0f11d28281f8569c6f5 100755 (executable)
@@ -63,7 +63,7 @@ EOF
         cp test-jobs.sh $initdir/
 
         setup_testsuite
-    )
+    ) || return 1
     setup_nspawn_root
 
     ddebug "umount $TESTDIR/root"
index a873ba7986ce79b4b1a16bcdbe3e672e7c72a2bf..9288200717d20c00fd0880bc604816abd41b801b 100644 (file)
@@ -51,8 +51,11 @@ run_qemu() {
             && KERNEL_BIN="/boot/$MACHINE_ID/$KERNEL_VER/linux"
     fi
 
+    default_fedora_initrd=/boot/initramfs-${KERNEL_VER}.img
+    default_debian_initrd=/boot/initrd.img-${KERNEL_VER}
     [ "$KERNEL_BIN" ] || KERNEL_BIN=/boot/vmlinuz-$KERNEL_VER
-    [ "$INITRD" ]     || INITRD=/boot/initramfs-${KERNEL_VER}.img
+    [ "$INITRD" ]     || { [ -e "$default_fedora_initrd" ] && INITRD=$default_fedora_initrd; }
+    [ "$INITRD" ]     || { [ "$LOOKS_LIKE_DEBIAN" ] && [ -e "$default_debian_initrd" ] && INITRD=$default_debian_initrd; }
     [ "$QEMU_SMP" ]   || QEMU_SMP=1
 
     find_qemu_bin || return 1
@@ -68,8 +71,7 @@ selinux=0 \
 $KERNEL_APPEND \
 "
 
-    QEMU_OPTIONS="-machine accel=kvm:tcg \
--smp $QEMU_SMP \
+    QEMU_OPTIONS="-smp $QEMU_SMP \
 -net none \
 -m 512M \
 -nographic \
@@ -80,13 +82,17 @@ $KERNEL_APPEND \
         QEMU_OPTIONS="$QEMU_OPTIONS -initrd $INITRD"
     fi
 
+    if [ -c /dev/kvm ]; then
+        QEMU_OPTIONS="$QEMU_OPTIONS -machine accel=kvm -enable-kvm -cpu host"
+    fi
+
     ( set -x
       $QEMU_BIN $QEMU_OPTIONS -append "$KERNEL_APPEND" $TESTDIR/rootdisk.img ) || return 1
 }
 
 run_nspawn() {
     set -x
-    ../../systemd-nspawn --boot --directory=$TESTDIR/nspawn-root $ROOTLIBDIR/systemd $KERNEL_APPEND
+    ../../systemd-nspawn --register=no --boot --directory=$TESTDIR/nspawn-root $ROOTLIBDIR/systemd $KERNEL_APPEND
 }
 
 setup_basic_environment() {
@@ -142,12 +148,12 @@ install_missing_libraries() {
 create_empty_image() {
     rm -f "$TESTDIR/rootdisk.img"
     # Create the blank file to use as a root filesystem
-    dd if=/dev/null of="$TESTDIR/rootdisk.img" bs=1M seek=300
+    dd if=/dev/null of="$TESTDIR/rootdisk.img" bs=1M seek=400
     LOOPDEV=$(losetup --show -P -f $TESTDIR/rootdisk.img)
     [ -b "$LOOPDEV" ] || return 1
     echo "LOOPDEV=$LOOPDEV" >> $STATEFILE
     sfdisk "$LOOPDEV" <<EOF
-,290M
+,390M
 ,
 EOF
 
@@ -1128,7 +1134,7 @@ inst_libdir_file() {
 }
 
 check_nspawn() {
-    [[ -d /sys/fs/cgroup/systemd ]]
+    [[ -d /run/systemd/system ]]
 }
 
 
index 17b1325bba62647e4e2b74d74415dcfa8a7e1c12..8091683feed9a572dcf1ec5adc1c9b6d4f539b4d 100755 (executable)
@@ -268,6 +268,7 @@ def _make_section(template, name, directives, formatting):
             b = tree.SubElement(para, 'citerefentry')
             c = tree.SubElement(b, 'refentrytitle')
             c.text = manpage
+            c.attrib['target'] = varname
             d = tree.SubElement(b, 'manvolnum')
             d.text = manvolume
         entry.tail = '\n\n'
index 1e21d51aaef5b1833577b6bd2db6c2536bc87898..66aba4f98508e4f69b6b1c973647b7cd6b22e278 100644 (file)
@@ -17,3 +17,4 @@ ExecStart=-@rootlibexecdir@/systemd --user
 Slice=user-%i.slice
 KillMode=mixed
 Delegate=yes
+TasksMax=infinity