diff options
276 files changed, 9803 insertions, 2637 deletions
diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..70b6c0f139 --- /dev/null +++ b/.editorconfig @@ -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 diff --git a/.gitignore b/.gitignore index b0abc0d4e9..98eb29d657 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ *.a *.cache -*.html *.la *.lo *.log @@ -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 diff --git a/Makefile-man.am b/Makefile-man.am index c792c89324..db0416f9d1 100644 --- a/Makefile-man.am +++ b/Makefile-man.am @@ -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 \ diff --git a/Makefile.am b/Makefile.am index 336c977e5e..6597d305d1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 @@ -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: @@ -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 diff --git a/configure.ac b/configure.ac index c96b9fb1d9..ec30ff12ae 100644 --- a/configure.ac +++ b/configure.ac @@ -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]) diff --git a/man/.gitignore b/man/.gitignore index bf5eeab938..d928e5a83f 100644 --- a/man/.gitignore +++ b/man/.gitignore @@ -1,4 +1,5 @@ /systemd.directives.xml /systemd.index.xml /*.[13578] +/*.html /custom-entities.ent diff --git a/man/custom-html.xsl b/man/custom-html.xsl index 84c23014e4..e89d73e7f1 100644 --- a/man/custom-html.xsl +++ b/man/custom-html.xsl @@ -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> diff --git a/man/resolved.conf.xml b/man/resolved.conf.xml index 811e33f4fa..4680b6a4e5 100644 --- a/man/resolved.conf.xml +++ b/man/resolved.conf.xml @@ -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"> @@ -77,10 +77,11 @@ 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> @@ -98,6 +99,16 @@ </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 <literal>resolve</literal>. Controls Link-Local Multicast Name diff --git a/man/sd-daemon.xml b/man/sd-daemon.xml index b7ba363656..b06d99f346 100644 --- a/man/sd-daemon.xml +++ b/man/sd-daemon.xml @@ -71,10 +71,10 @@ <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 index 0000000000..47989f4421 --- /dev/null +++ b/man/sd-event.xml @@ -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 <systemd/sd-event.h></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> diff --git a/man/sd_event_add_child.xml b/man/sd_event_add_child.xml index 77bec4e706..d4b180cf03 100644 --- a/man/sd_event_add_child.xml +++ b/man/sd_event_add_child.xml @@ -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> @@ -45,13 +45,23 @@ <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 <systemd/sd-bus.h></funcsynopsisinfo> + <funcsynopsisinfo>#include <systemd/sd-event.h></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> @@ -64,13 +74,6 @@ </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> <paramdef>pid_t *<parameter>pid</parameter></paramdef> @@ -83,42 +86,75 @@ <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> @@ -158,7 +194,7 @@ <term><constant>-EBUSY</constant></term> <listitem><para>A handler is already installed for this - child.</para></listitem> + child process.</para></listitem> </varlistentry> @@ -176,19 +212,17 @@ </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> @@ -196,10 +230,16 @@ <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> diff --git a/man/sd_event_add_defer.xml b/man/sd_event_add_defer.xml index 826f2fd224..6a13ede76e 100644 --- a/man/sd_event_add_defer.xml +++ b/man/sd_event_add_defer.xml @@ -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> @@ -46,13 +46,22 @@ <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 <systemd/sd-bus.h></funcsynopsisinfo> + <funcsynopsisinfo>#include <systemd/sd-event.h></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> @@ -78,49 +87,67 @@ <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> @@ -164,15 +191,7 @@ </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> @@ -181,10 +200,16 @@ <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 index 0000000000..4cc0428e29 --- /dev/null +++ b/man/sd_event_add_io.xml @@ -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 <systemd/sd-event.h></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> diff --git a/man/sd_event_add_signal.xml b/man/sd_event_add_signal.xml index 0923fe0ae7..b5312735d2 100644 --- a/man/sd_event_add_signal.xml +++ b/man/sd_event_add_signal.xml @@ -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> @@ -45,13 +45,24 @@ <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 <systemd/sd-bus.h></funcsynopsisinfo> + <funcsynopsisinfo>#include <systemd/sd-event.h></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> @@ -63,13 +74,6 @@ </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> </funcprototype> @@ -80,41 +84,61 @@ <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> @@ -168,19 +192,17 @@ </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> @@ -188,10 +210,16 @@ <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> diff --git a/man/sd_event_add_time.xml b/man/sd_event_add_time.xml index c5f7aee19d..df38f52fc9 100644 --- a/man/sd_event_add_time.xml +++ b/man/sd_event_add_time.xml @@ -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> @@ -49,13 +49,23 @@ <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 <systemd/sd-bus.h></funcsynopsisinfo> + <funcsynopsisinfo>#include <systemd/sd-event.h></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> @@ -69,34 +79,27 @@ </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> @@ -112,73 +115,130 @@ <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> @@ -228,19 +288,17 @@ <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> @@ -248,11 +306,18 @@ <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 index 0000000000..4f34f3b122 --- /dev/null +++ b/man/sd_event_exit.xml @@ -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 <systemd/sd-event.h></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> diff --git a/man/sd_event_get_fd.xml b/man/sd_event_get_fd.xml index ecdbe76ec4..f68752dd0e 100644 --- a/man/sd_event_get_fd.xml +++ b/man/sd_event_get_fd.xml @@ -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> @@ -51,11 +50,11 @@ <refsynopsisdiv> <funcsynopsis> - <funcsynopsisinfo>#include <systemd/sd-bus.h></funcsynopsisinfo> + <funcsynopsisinfo>#include <systemd/sd-event.h></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> @@ -65,19 +64,29 @@ <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> @@ -108,21 +117,13 @@ <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> @@ -130,7 +131,9 @@ <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> diff --git a/man/sd_event_new.xml b/man/sd_event_new.xml index f6c5d39814..3356faa899 100644 --- a/man/sd_event_new.xml +++ b/man/sd_event_new.xml @@ -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> @@ -47,32 +47,42 @@ <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 <systemd/sd-bus.h></funcsynopsisinfo> + <funcsynopsisinfo>#include <systemd/sd-event.h></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> @@ -103,6 +113,17 @@ 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> @@ -117,6 +138,19 @@ 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> @@ -149,19 +183,20 @@ <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> @@ -174,7 +209,9 @@ <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 index 0000000000..f577e44c0e --- /dev/null +++ b/man/sd_event_now.xml @@ -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 <systemd/sd-event.h></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> diff --git a/man/sd_event_run.xml b/man/sd_event_run.xml index 06236fcd1a..5b68959165 100644 --- a/man/sd_event_run.xml +++ b/man/sd_event_run.xml @@ -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> @@ -69,47 +69,60 @@ <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> @@ -122,7 +135,7 @@ <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> @@ -153,15 +166,7 @@ <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> @@ -169,13 +174,15 @@ <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 index 1471e12e59..0000000000 --- a/man/sd_event_set_name.xml +++ /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 <systemd/sd-bus.h></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 index 0000000000..cbc5bc0836 --- /dev/null +++ b/man/sd_event_set_watchdog.xml @@ -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 <systemd/sd-event.h></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 index 0000000000..2fdbd411bd --- /dev/null +++ b/man/sd_event_source_get_event.xml @@ -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 <systemd/sd-event.h></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 index 0000000000..1c06e81fe0 --- /dev/null +++ b/man/sd_event_source_get_pending.xml @@ -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 <systemd/sd-event.h></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 index 0000000000..b9488a622f --- /dev/null +++ b/man/sd_event_source_set_description.xml @@ -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 <systemd/sd-event.h></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 index 0000000000..74c02e87bb --- /dev/null +++ b/man/sd_event_source_set_enabled.xml @@ -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 <systemd/sd-event.h></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 index 0000000000..7066a55306 --- /dev/null +++ b/man/sd_event_source_set_prepare.xml @@ -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 <systemd/sd-event.h></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 index 0000000000..cc0f5a0103 --- /dev/null +++ b/man/sd_event_source_set_priority.xml @@ -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 <systemd/sd-event.h></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 index 0000000000..533d491b13 --- /dev/null +++ b/man/sd_event_source_set_userdata.xml @@ -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 <systemd/sd-event.h></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 index 0000000000..579ec47e8e --- /dev/null +++ b/man/sd_event_source_unref.xml @@ -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 <systemd/sd-event.h></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> diff --git a/man/sd_event_wait.xml b/man/sd_event_wait.xml index 7ca50aedf9..1eefa80700 100644 --- a/man/sd_event_wait.xml +++ b/man/sd_event_wait.xml @@ -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> @@ -46,14 +46,32 @@ <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 <systemd/sd-event.h></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> @@ -70,66 +88,184 @@ <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 -<---<---<---<---<---<---<---<---<---<---<---<---\ + | | + | ^ + | | + v ret == 0 | + sd_event_prepare() >--->--->--->--->- ARMED | + | | ^ + | ret > 0 | | + | | | + v v ret == 0 | + PENDING <---<---<---<---<---< sd_event_wait() >--->--->--+ + | ret > 0 ^ + | | + | | + v | + sd_event_dispatch() >--->--->--->--->--->--->--->--->--->--->/ + | 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> @@ -137,13 +273,15 @@ <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> @@ -156,7 +294,7 @@ <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> @@ -185,14 +323,7 @@ <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> @@ -200,13 +331,15 @@ <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> diff --git a/man/sd_watchdog_enabled.xml b/man/sd_watchdog_enabled.xml index 144ab1db61..6e27528a71 100644 --- a/man/sd_watchdog_enabled.xml +++ b/man/sd_watchdog_enabled.xml @@ -98,6 +98,11 @@ <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> @@ -168,7 +173,8 @@ <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> diff --git a/man/systemctl.xml b/man/systemctl.xml index 755a74f987..1fb056874c 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -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 diff --git a/man/systemd-journald.service.xml b/man/systemd-journald.service.xml index f1054b03bb..2810638bc2 100644 --- a/man/systemd-journald.service.xml +++ b/man/systemd-journald.service.xml @@ -106,13 +106,6 @@ <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> diff --git a/man/systemd-system.conf.xml b/man/systemd-system.conf.xml index ead52951da..edc6df914a 100644 --- a/man/systemd-system.conf.xml +++ b/man/systemd-system.conf.xml @@ -354,13 +354,26 @@ <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> diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index 6dda6c5e69..5f98ef163c 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -630,7 +630,10 @@ <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 @@ -1381,6 +1384,7 @@ <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 @@ -1389,6 +1393,24 @@ </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> <listitem><para>Terminal type, set only for units connected to @@ -1402,8 +1424,8 @@ <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>) diff --git a/man/systemd.mount.xml b/man/systemd.mount.xml index a724d88584..4a8d265fed 100644 --- a/man/systemd.mount.xml +++ b/man/systemd.mount.xml @@ -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 diff --git a/man/systemd.service.xml b/man/systemd.service.xml index 20a71afe59..b998a1f81f 100644 --- a/man/systemd.service.xml +++ b/man/systemd.service.xml @@ -506,7 +506,8 @@ 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 @@ -521,7 +522,9 @@ 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> @@ -678,8 +681,10 @@ <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> @@ -711,14 +716,16 @@ 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> diff --git a/man/systemd.socket.xml b/man/systemd.socket.xml index beac053bf0..43841c2399 100644 --- a/man/systemd.socket.xml +++ b/man/systemd.socket.xml @@ -311,6 +311,15 @@ </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>, <option>both</option> or <option>ipv6-only</option>. Controls diff --git a/man/systemd.time.xml b/man/systemd.time.xml index a6fcc95e4c..ffcac82263 100644 --- a/man/systemd.time.xml +++ b/man/systemd.time.xml @@ -229,6 +229,10 @@ 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 diff --git a/man/systemd.timer.xml b/man/systemd.timer.xml index 8cf6c4683b..cfa13015b0 100644 --- a/man/systemd.timer.xml +++ b/man/systemd.timer.xml @@ -190,13 +190,12 @@ <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> @@ -204,6 +203,38 @@ 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> @@ -243,6 +274,24 @@ 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> @@ -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 "Απαιτείται πιστοποίηση για να ορίσετε την ώρα του συστήματος." @@ -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 " @@ -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." @@ -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)'." diff --git a/po/pt_BR.po b/po/pt_BR.po index 1dd5900e2f..2a11371f97 100644 --- a/po/pt_BR.po +++ b/po/pt_BR.po @@ -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." @@ -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" @@ -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." @@ -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 "Засвідчення потрібно, щоб вказати системний час." diff --git a/po/zh_TW.po b/po/zh_TW.po index fb276a1577..5a214a3c48 100644 --- a/po/zh_TW.po +++ b/po/zh_TW.po @@ -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 "設定系統時間需要驗證。" diff --git a/shell-completion/bash/loginctl b/shell-completion/bash/loginctl index 7a083d2875..776eca4e62 100644 --- a/shell-completion/bash/loginctl +++ b/shell-completion/bash/loginctl @@ -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' diff --git a/shell-completion/bash/machinectl b/shell-completion/bash/machinectl index 140465d316..61c5402786 100644 --- a/shell-completion/bash/machinectl +++ b/shell-completion/bash/machinectl @@ -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' diff --git a/shell-completion/bash/systemctl.in b/shell-completion/bash/systemctl.in index d80d8f02a8..6ffab33e45 100644 --- a/shell-completion/bash/systemctl.in +++ b/shell-completion/bash/systemctl.in @@ -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) diff --git a/shell-completion/bash/systemd-nspawn b/shell-completion/bash/systemd-nspawn index f9b740380c..429e712eb3 100644 --- a/shell-completion/bash/systemd-nspawn +++ b/shell-completion/bash/systemd-nspawn @@ -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 diff --git a/src/basic/audit-util.h b/src/basic/audit-util.h index 6de331c73e..026d3cd9b1 100644 --- a/src/basic/audit-util.h +++ b/src/basic/audit-util.h @@ -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) diff --git a/src/basic/bitmap.h b/src/basic/bitmap.h index 2874bc99f7..9ce7b42d00 100644 --- a/src/basic/bitmap.h +++ b/src/basic/bitmap.h @@ -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; diff --git a/src/basic/calendarspec.c b/src/basic/calendarspec.c index 157ae1fb74..8f60561ede 100644 --- a/src/basic/calendarspec.c +++ b/src/basic/calendarspec.c @@ -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; } diff --git a/src/basic/calendarspec.h b/src/basic/calendarspec.h index 56dc02f391..75b699682a 100644 --- a/src/basic/calendarspec.h +++ b/src/basic/calendarspec.h @@ -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); diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h index 01359fa7cb..a80ee60bd3 100644 --- a/src/basic/cgroup-util.h +++ b/src/basic/cgroup-util.h @@ -21,12 +21,12 @@ 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 { diff --git a/src/basic/escape.c b/src/basic/escape.c index 4815161b09..42a84c9317 100644 --- a/src/basic/escape.c +++ b/src/basic/escape.c @@ -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; diff --git a/src/basic/escape.h b/src/basic/escape.h index 85ba909081..52ebf11c4a 100644 --- a/src/basic/escape.h +++ b/src/basic/escape.h @@ -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); diff --git a/src/basic/fd-util.h b/src/basic/fd-util.h index 0e9182d75b..5ce1592eeb 100644 --- a/src/basic/fd-util.h +++ b/src/basic/fd-util.h @@ -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" diff --git a/src/basic/fileio-label.h b/src/basic/fileio-label.h index 25fa351be2..9feb3cccb5 100644 --- a/src/basic/fileio-label.h +++ b/src/basic/fileio-label.h @@ -23,6 +23,7 @@ ***/ #include <stdio.h> + #include "fileio.h" int write_string_file_atomic_label(const char *fn, const char *line); diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h index 902c7e295b..5fbb7bc4c3 100644 --- a/src/basic/fs-util.h +++ b/src/basic/fs-util.h @@ -22,10 +22,10 @@ ***/ #include <fcntl.h> +#include <limits.h> #include <sys/inotify.h> #include <sys/types.h> #include <unistd.h> -#include <limits.h> #include "time-util.h" diff --git a/src/basic/gunicode.h b/src/basic/gunicode.h index e70818fdd7..b03aa43160 100644 --- a/src/basic/gunicode.h +++ b/src/basic/gunicode.h @@ -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); diff --git a/src/basic/hostname-util.c b/src/basic/hostname-util.c index ea0528c6fc..c57a3cbd60 100644 --- a/src/basic/hostname-util.c +++ b/src/basic/hostname-util.c @@ -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; diff --git a/src/basic/in-addr-util.c b/src/basic/in-addr-util.c index f4e24121e7..b75c39aac7 100644 --- a/src/basic/in-addr-util.c +++ b/src/basic/in-addr-util.c @@ -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); diff --git a/src/basic/in-addr-util.h b/src/basic/in-addr-util.h index 51af08868c..58f55b3418 100644 --- a/src/basic/in-addr-util.h +++ b/src/basic/in-addr-util.h @@ -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); diff --git a/src/basic/ioprio.h b/src/basic/ioprio.h index e5c71d0043..d8bb6eb497 100644 --- a/src/basic/ioprio.h +++ b/src/basic/ioprio.h @@ -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 diff --git a/src/basic/json.h b/src/basic/json.h index e0b4d810b5..8a7d79cb17 100644 --- a/src/basic/json.h +++ b/src/basic/json.h @@ -22,6 +22,7 @@ ***/ #include <stdbool.h> + #include "util.h" enum { diff --git a/src/basic/memfd-util.h b/src/basic/memfd-util.h index 2cb404ea81..3e4de008a4 100644 --- a/src/basic/memfd-util.h +++ b/src/basic/memfd-util.h @@ -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); diff --git a/src/basic/ordered-set.h b/src/basic/ordered-set.h index 6c617ab305..da10e90ff2 100644 --- a/src/basic/ordered-set.h +++ b/src/basic/ordered-set.h @@ -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; diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c index 151067e916..3ae99d9334 100644 --- a/src/basic/parse-util.c +++ b/src/basic/parse-util.c @@ -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; +} diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h index 408690d0b3..125de53d7a 100644 --- a/src/basic/parse-util.h +++ b/src/basic/parse-util.h @@ -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); diff --git a/src/basic/process-util.h b/src/basic/process-util.h index 72633ebf70..fdc7e1bdef 100644 --- a/src/basic/process-util.h +++ b/src/basic/process-util.h @@ -19,12 +19,12 @@ 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" diff --git a/src/basic/selinux-util.h b/src/basic/selinux-util.h index 2afcaec183..d19984c5fe 100644 --- a/src/basic/selinux-util.h +++ b/src/basic/selinux-util.h @@ -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" diff --git a/src/basic/set.h b/src/basic/set.h index 4554ef2d49..5fd7de08f9 100644 --- a/src/basic/set.h +++ b/src/basic/set.h @@ -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; diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h index c60f2556af..129ffa811c 100644 --- a/src/basic/socket-util.h +++ b/src/basic/socket-util.h @@ -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> diff --git a/src/basic/stat-util.h b/src/basic/stat-util.h index 909b220a24..fb92464274 100644 --- a/src/basic/stat-util.h +++ b/src/basic/stat-util.h @@ -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); diff --git a/src/basic/terminal-util.h b/src/basic/terminal-util.h index f2185c1c11..b2c7a297ae 100644 --- a/src/basic/terminal-util.h +++ b/src/basic/terminal-util.h @@ -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" diff --git a/src/basic/time-util.c b/src/basic/time-util.c index 647763a230..b9da6991da 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -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: diff --git a/src/basic/user-util.h b/src/basic/user-util.h index 11ff6674cf..6106e138be 100644 --- a/src/basic/user-util.h +++ b/src/basic/user-util.h @@ -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); diff --git a/src/basic/virt.c b/src/basic/virt.c index 1e5d6eea6e..b82680a54b 100644 --- a/src/basic/virt.c +++ b/src/basic/virt.c @@ -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) diff --git a/src/bootchart/bootchart.h b/src/bootchart/bootchart.h index bdb4b00199..8432a2a119 100644 --- a/src/bootchart/bootchart.h +++ b/src/bootchart/bootchart.h @@ -25,6 +25,7 @@ ***/ #include <stdbool.h> + #include "list.h" #define MAXCPUS 16 diff --git a/src/bootchart/store.h b/src/bootchart/store.h index bbb4796efd..4d2e0d439f 100644 --- a/src/bootchart/store.h +++ b/src/bootchart/store.h @@ -25,6 +25,7 @@ ***/ #include <dirent.h> + #include "bootchart.h" double gettime_ns(void); diff --git a/src/bus-proxyd/bus-xml-policy.h b/src/bus-proxyd/bus-xml-policy.h index 8f0ab8f17f..8dde0cb868 100644 --- a/src/bus-proxyd/bus-xml-policy.h +++ b/src/bus-proxyd/bus-xml-policy.h @@ -23,8 +23,8 @@ #include <pthread.h> -#include "list.h" #include "hashmap.h" +#include "list.h" typedef enum PolicyItemType { _POLICY_ITEM_TYPE_UNSET = 0, diff --git a/src/bus-proxyd/driver.h b/src/bus-proxyd/driver.h index da3834f8b0..9f68902441 100644 --- a/src/bus-proxyd/driver.h +++ b/src/bus-proxyd/driver.h @@ -22,6 +22,7 @@ ***/ #include "sd-bus.h" + #include "bus-xml-policy.h" #include "proxy.h" diff --git a/src/core/bus-endpoint.h b/src/core/bus-endpoint.h index 4a31f4c4be..f6c5f7c5af 100644 --- a/src/core/bus-endpoint.h +++ b/src/core/bus-endpoint.h @@ -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; diff --git a/src/core/bus-policy.h b/src/core/bus-policy.h index 3b04f5457a..2f61289185 100644 --- a/src/core/bus-policy.h +++ b/src/core/bus-policy.h @@ -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; diff --git a/src/core/cgroup.h b/src/core/cgroup.h index 457544b49f..1b18d06652 100644 --- a/src/core/cgroup.h +++ b/src/core/cgroup.h @@ -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); diff --git a/src/core/dbus-kill.h b/src/core/dbus-kill.h index 794c402048..1d32fca547 100644 --- a/src/core/dbus-kill.h +++ b/src/core/dbus-kill.h @@ -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[]; diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index 67e4e8b218..2562396180 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -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); } diff --git a/src/core/dbus-scope.h b/src/core/dbus-scope.h index 33beda47b7..4fb0b25e09 100644 --- a/src/core/dbus-scope.h +++ b/src/core/dbus-scope.h @@ -22,6 +22,7 @@ ***/ #include "sd-bus.h" + #include "unit.h" extern const sd_bus_vtable bus_scope_vtable[]; diff --git a/src/core/dbus-service.h b/src/core/dbus-service.h index aab9f7aa26..a67b64ab5b 100644 --- a/src/core/dbus-service.h +++ b/src/core/dbus-service.h @@ -22,6 +22,7 @@ ***/ #include "sd-bus.h" + #include "unit.h" extern const sd_bus_vtable bus_service_vtable[]; diff --git a/src/core/dbus-slice.h b/src/core/dbus-slice.h index eadc3b1a9c..117d11471b 100644 --- a/src/core/dbus-slice.h +++ b/src/core/dbus-slice.h @@ -22,6 +22,7 @@ ***/ #include "sd-bus.h" + #include "unit.h" extern const sd_bus_vtable bus_slice_vtable[]; diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c index be5ef261a6..895dd07753 100644 --- a/src/core/dbus-socket.c +++ b/src/core/dbus-socket.c @@ -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), diff --git a/src/core/dbus-socket.h b/src/core/dbus-socket.h index 17164d9871..8dad6ea2e9 100644 --- a/src/core/dbus-socket.h +++ b/src/core/dbus-socket.h @@ -22,6 +22,7 @@ ***/ #include "sd-bus.h" + #include "unit.h" extern const sd_bus_vtable bus_socket_vtable[]; diff --git a/src/core/dbus-swap.h b/src/core/dbus-swap.h index 9469f68ab8..a414ca7f75 100644 --- a/src/core/dbus-swap.h +++ b/src/core/dbus-swap.h @@ -23,6 +23,7 @@ ***/ #include "sd-bus.h" + #include "unit.h" extern const sd_bus_vtable bus_swap_vtable[]; diff --git a/src/core/dbus-timer.c b/src/core/dbus-timer.c index a8a280d961..ec301df6d7 100644 --- a/src/core/dbus-timer.c +++ b/src/core/dbus-timer.c @@ -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; diff --git a/src/core/dbus-timer.h b/src/core/dbus-timer.h index 103172f055..ca35c4b8c1 100644 --- a/src/core/dbus-timer.h +++ b/src/core/dbus-timer.h @@ -22,6 +22,7 @@ ***/ #include "sd-bus.h" + #include "unit.h" extern const sd_bus_vtable bus_timer_vtable[]; diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index d9b7382c82..66b465a0b7 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -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."); +} diff --git a/src/core/dbus-unit.h b/src/core/dbus-unit.h index b622e0ae8d..ac9ee2d6b8 100644 --- a/src/core/dbus-unit.h +++ b/src/core/dbus-unit.h @@ -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); diff --git a/src/core/execute.c b/src/core/execute.c index 07979bf8b3..677480cbe1 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -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; diff --git a/src/core/execute.h b/src/core/execute.h index 1faff160cb..be5be9f531 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -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; diff --git a/src/core/job.c b/src/core/job.c index 53e0947215..9654590635 100644 --- a/src/core/job.c +++ b/src/core/job.c @@ -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) { diff --git a/src/core/job.h b/src/core/job.h index 60d8bd4f3e..118b24e5b7 100644 --- a/src/core/job.h +++ b/src/core/job.h @@ -26,6 +26,7 @@ #include "sd-event.h" #include "list.h" +#include "unit-name.h" typedef struct Job Job; typedef struct JobDependency JobDependency; diff --git a/src/core/load-dropin.h b/src/core/load-dropin.h index 1e018c4525..93ffcc4a72 100644 --- a/src/core/load-dropin.h +++ b/src/core/load-dropin.h @@ -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 */ diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index 799418033d..0408b9a829 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -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 diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index dda79267f7..3c124495b6 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -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; } diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h index 62300c10f9..a451fc164a 100644 --- a/src/core/load-fragment.h +++ b/src/core/load-fragment.h @@ -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); diff --git a/src/core/macros.systemd.in b/src/core/macros.systemd.in index 8a0e44b58c..2cace3d3ba 100644 --- a/src/core/macros.systemd.in +++ b/src/core/macros.systemd.in @@ -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 \ diff --git a/src/core/manager.h b/src/core/manager.h index b5b258f909..f6903a5c34 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -21,12 +21,13 @@ 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" diff --git a/src/core/mount.c b/src/core/mount.c index 9b44357e90..2ad4ad4f42 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -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; diff --git a/src/core/mount.h b/src/core/mount.h index 83d14ae713..9f78aa9075 100644 --- a/src/core/mount.h +++ b/src/core/mount.h @@ -23,8 +23,8 @@ typedef struct Mount Mount; -#include "kill.h" #include "execute.h" +#include "kill.h" typedef enum MountExecCommand { MOUNT_EXEC_MOUNT, diff --git a/src/core/selinux-access.h b/src/core/selinux-access.h index 30725521cb..3566ba529f 100644 --- a/src/core/selinux-access.h +++ b/src/core/selinux-access.h @@ -22,6 +22,7 @@ ***/ #include "sd-bus.h" + #include "bus-util.h" #include "manager.h" diff --git a/src/core/service.h b/src/core/service.h index e765668247..d0faad88e0 100644 --- a/src/core/service.h +++ b/src/core/service.h @@ -24,10 +24,10 @@ 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, diff --git a/src/core/socket.c b/src/core/socket.c index 5b9e32ce9d..860a1e3051 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -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, diff --git a/src/core/socket.h b/src/core/socket.h index 94cda8a90d..08033287a6 100644 --- a/src/core/socket.h +++ b/src/core/socket.h @@ -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; diff --git a/src/core/swap.c b/src/core/swap.c index ee0838e676..5568898bd7 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -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); } diff --git a/src/core/timer.c b/src/core/timer.c index 51b1d875be..6b0f8e8616 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -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) { diff --git a/src/core/timer.h b/src/core/timer.h index ac5af6a93c..0599f07818 100644 --- a/src/core/timer.h +++ b/src/core/timer.h @@ -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; }; diff --git a/src/core/transaction.h b/src/core/transaction.h index f7aa3df085..5c4a13edab 100644 --- a/src/core/transaction.h +++ b/src/core/transaction.h @@ -23,10 +23,10 @@ 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 */ diff --git a/src/core/triggers.systemd.in b/src/core/triggers.systemd.in index 141f42dbcf..9e18a39a67 100644 --- a/src/core/triggers.systemd.in +++ b/src/core/triggers.systemd.in @@ -19,8 +19,46 @@ # 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 diff --git a/src/core/unit.c b/src/core/unit.c index d199d87bf8..e6e67d27c8 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -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); +} diff --git a/src/core/unit.h b/src/core/unit.h index bcf41d2348..3eb3484fb7 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -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); } diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c index f7c8d11ace..87b8b77f22 100644 --- a/src/fstab-generator/fstab-generator.c +++ b/src/fstab-generator/fstab-generator.c @@ -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); diff --git a/src/import/curl-util.h b/src/import/curl-util.h index 6a2aa81c76..eec53c9266 100644 --- a/src/import/curl-util.h +++ b/src/import/curl-util.h @@ -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" diff --git a/src/import/export-raw.h b/src/import/export-raw.h index b71de6cb82..e5e298f6ab 100644 --- a/src/import/export-raw.h +++ b/src/import/export-raw.h @@ -22,8 +22,9 @@ ***/ #include "sd-event.h" -#include "macro.h" + #include "import-compress.h" +#include "macro.h" typedef struct RawExport RawExport; diff --git a/src/import/export-tar.h b/src/import/export-tar.h index ce27a9fc1e..9061e7515d 100644 --- a/src/import/export-tar.h +++ b/src/import/export-tar.h @@ -22,8 +22,9 @@ ***/ #include "sd-event.h" -#include "macro.h" + #include "import-compress.h" +#include "macro.h" typedef struct TarExport TarExport; diff --git a/src/import/import-compress.h b/src/import/import-compress.h index 50d91f732c..0a13232554 100644 --- a/src/import/import-compress.h +++ b/src/import/import-compress.h @@ -21,11 +21,10 @@ 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" diff --git a/src/import/import-raw.h b/src/import/import-raw.h index bf7c770340..626d965cf8 100644 --- a/src/import/import-raw.h +++ b/src/import/import-raw.h @@ -22,8 +22,9 @@ ***/ #include "sd-event.h" -#include "macro.h" + #include "import-util.h" +#include "macro.h" typedef struct RawImport RawImport; diff --git a/src/import/import-tar.h b/src/import/import-tar.h index aaecb51398..d12391572d 100644 --- a/src/import/import-tar.h +++ b/src/import/import-tar.h @@ -22,8 +22,9 @@ ***/ #include "sd-event.h" -#include "macro.h" + #include "import-util.h" +#include "macro.h" typedef struct TarImport TarImport; diff --git a/src/import/pull-common.h b/src/import/pull-common.h index 7e6db1862c..ea228bb5c8 100644 --- a/src/import/pull-common.h +++ b/src/import/pull-common.h @@ -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); diff --git a/src/import/pull-dkr.h b/src/import/pull-dkr.h index 33d18cb394..a95d91205b 100644 --- a/src/import/pull-dkr.h +++ b/src/import/pull-dkr.h @@ -22,6 +22,7 @@ #pragma once #include "sd-event.h" + #include "util.h" typedef enum { DKR_PULL_V1, DKR_PULL_V2 } DkrPullVersion; diff --git a/src/import/pull-job.h b/src/import/pull-job.h index 1777bf1c33..56a74a34ef 100644 --- a/src/import/pull-job.h +++ b/src/import/pull-job.h @@ -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; diff --git a/src/import/pull-raw.h b/src/import/pull-raw.h index b03b4f5c92..0e4e1daf0e 100644 --- a/src/import/pull-raw.h +++ b/src/import/pull-raw.h @@ -22,8 +22,9 @@ ***/ #include "sd-event.h" -#include "macro.h" + #include "import-util.h" +#include "macro.h" typedef struct RawPull RawPull; diff --git a/src/import/pull-tar.h b/src/import/pull-tar.h index 420845ae50..9f02f1ec71 100644 --- a/src/import/pull-tar.h +++ b/src/import/pull-tar.h @@ -22,8 +22,9 @@ ***/ #include "sd-event.h" -#include "macro.h" + #include "import-util.h" +#include "macro.h" typedef struct TarPull TarPull; diff --git a/src/journal-remote/journal-remote-parse.h b/src/journal-remote/journal-remote-parse.h index 14bfadc132..58cb5e70df 100644 --- a/src/journal-remote/journal-remote-parse.h +++ b/src/journal-remote/journal-remote-parse.h @@ -22,6 +22,7 @@ #pragma once #include "sd-event.h" + #include "journal-remote-write.h" typedef enum { diff --git a/src/journal-remote/journal-remote.h b/src/journal-remote/journal-remote.h index 6c2ccb9735..fd81a1c592 100644 --- a/src/journal-remote/journal-remote.h +++ b/src/journal-remote/journal-remote.h @@ -23,11 +23,11 @@ #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; diff --git a/src/journal-remote/journal-upload.h b/src/journal-remote/journal-upload.h index 3b46fa8cbf..b8cd04d527 100644 --- a/src/journal-remote/journal-upload.h +++ b/src/journal-remote/journal-upload.h @@ -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. */ diff --git a/src/journal-remote/microhttpd-util.h b/src/journal-remote/microhttpd-util.h index b2feb9180a..3e8c4fa6d1 100644 --- a/src/journal-remote/microhttpd-util.h +++ b/src/journal-remote/microhttpd-util.h @@ -21,8 +21,8 @@ #pragma once -#include <stdarg.h> #include <microhttpd.h> +#include <stdarg.h> #include "macro.h" diff --git a/src/journal/catalog.h b/src/journal/catalog.h index a72ecf6de7..bcc73c2631 100644 --- a/src/journal/catalog.h +++ b/src/journal/catalog.h @@ -24,6 +24,7 @@ #include <stdbool.h> #include "sd-id128.h" + #include "hashmap.h" #include "strbuf.h" diff --git a/src/journal/fsprg.h b/src/journal/fsprg.h index 5959b1fed2..b79221fc2e 100644 --- a/src/journal/fsprg.h +++ b/src/journal/fsprg.h @@ -25,8 +25,8 @@ * */ -#include <sys/types.h> #include <inttypes.h> +#include <sys/types.h> #include "macro.h" #include "util.h" diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h index 898d12d992..46c1f3278e 100644 --- a/src/journal/journal-file.h +++ b/src/journal/journal-file.h @@ -29,11 +29,11 @@ #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" */ diff --git a/src/journal/journal-internal.h b/src/journal/journal-internal.h index 06847402e0..c3e75ad240 100644 --- a/src/journal/journal-internal.h +++ b/src/journal/journal-internal.h @@ -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" diff --git a/src/journal/journald-audit.h b/src/journal/journald-audit.h index 68cdfb3410..5c88bb6383 100644 --- a/src/journal/journald-audit.h +++ b/src/journal/journald-audit.h @@ -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); diff --git a/src/libsystemd-network/arp-util.h b/src/libsystemd-network/arp-util.h index 44e5c893a7..63c559f8dd 100644 --- a/src/libsystemd-network/arp-util.h +++ b/src/libsystemd-network/arp-util.h @@ -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); diff --git a/src/libsystemd-network/dhcp-internal.h b/src/libsystemd-network/dhcp-internal.h index a5daaa543a..7038212bcf 100644 --- a/src/libsystemd-network/dhcp-internal.h +++ b/src/libsystemd-network/dhcp-internal.h @@ -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, diff --git a/src/libsystemd-network/dhcp-lease-internal.h b/src/libsystemd-network/dhcp-lease-internal.h index c6b97ca8f7..138bdd9691 100644 --- a/src/libsystemd-network/dhcp-lease-internal.h +++ b/src/libsystemd-network/dhcp-lease-internal.h @@ -25,12 +25,11 @@ #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; diff --git a/src/libsystemd-network/dhcp-option.c b/src/libsystemd-network/dhcp-option.c index a6c410ba91..1de7f3639c 100644 --- a/src/libsystemd-network/dhcp-option.c +++ b/src/libsystemd-network/dhcp-option.c @@ -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; } diff --git a/src/libsystemd-network/dhcp-protocol.h b/src/libsystemd-network/dhcp-protocol.h index 88a81d2866..f65529a00e 100644 --- a/src/libsystemd-network/dhcp-protocol.h +++ b/src/libsystemd-network/dhcp-protocol.h @@ -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), +}; diff --git a/src/libsystemd-network/dhcp-server-internal.h b/src/libsystemd-network/dhcp-server-internal.h index 3b88b93d9a..a42f622c37 100644 --- a/src/libsystemd-network/dhcp-server-internal.h +++ b/src/libsystemd-network/dhcp-server-internal.h @@ -22,14 +22,13 @@ #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; diff --git a/src/libsystemd-network/dhcp6-lease-internal.h b/src/libsystemd-network/dhcp6-lease-internal.h index 4edecf7711..f6cf0b30d3 100644 --- a/src/libsystemd-network/dhcp6-lease-internal.h +++ b/src/libsystemd-network/dhcp6-lease-internal.h @@ -25,6 +25,7 @@ #include <stdint.h> #include "sd-dhcp6-lease.h" + #include "dhcp6-internal.h" struct sd_dhcp6_lease { diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index 62023a9e49..850212aea1 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -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 { diff --git a/src/libsystemd-network/icmp6-util.c b/src/libsystemd-network/icmp6-util.c index 91308bf6c3..acad9d7d6a 100644 --- a/src/libsystemd-network/icmp6-util.c +++ b/src/libsystemd-network/icmp6-util.c @@ -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) diff --git a/src/libsystemd-network/lldp-tlv.h b/src/libsystemd-network/lldp-tlv.h index ca1da113d5..f5cd77477f 100644 --- a/src/libsystemd-network/lldp-tlv.h +++ b/src/libsystemd-network/lldp-tlv.h @@ -24,12 +24,12 @@ #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; diff --git a/src/libsystemd-network/network-internal.h b/src/libsystemd-network/network-internal.h index d516f2dafd..8a30921966 100644 --- a/src/libsystemd-network/network-internal.h +++ b/src/libsystemd-network/network-internal.h @@ -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, diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index 137537253a..7deb00af9c 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -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; diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c index 8befedc500..fccdc01bc3 100644 --- a/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/libsystemd-network/sd-dhcp-lease.c @@ -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; } diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c index 277c88e2b9..587ff936ba 100644 --- a/src/libsystemd-network/sd-dhcp-server.c +++ b/src/libsystemd-network/sd-dhcp-server.c @@ -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) { diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 801331d270..36d909a4c5 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -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: diff --git a/src/libsystemd-network/sd-ndisc.c b/src/libsystemd-network/sd-ndisc.c index 6703d87bc4..f2bce3b99f 100644 --- a/src/libsystemd-network/sd-ndisc.c +++ b/src/libsystemd-network/sd-ndisc.c @@ -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 { diff --git a/src/libsystemd-network/test-dhcp-client.c b/src/libsystemd-network/test-dhcp-client.c index 1200a7c251..4478147a83 100644 --- a/src/libsystemd-network/test-dhcp-client.c +++ b/src/libsystemd-network/test-dhcp-client.c @@ -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); diff --git a/src/libsystemd-network/test-dhcp-option.c b/src/libsystemd-network/test-dhcp-option.c index 3607df63af..75d22c4df3 100644 --- a/src/libsystemd-network/test-dhcp-option.c +++ b/src/libsystemd-network/test-dhcp-option.c @@ -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)); diff --git a/src/libsystemd/sd-bus/bus-control.h b/src/libsystemd/sd-bus/bus-control.h index 5009ca8e61..e01b075832 100644 --- a/src/libsystemd/sd-bus/bus-control.h +++ b/src/libsystemd/sd-bus/bus-control.h @@ -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); diff --git a/src/libsystemd/sd-bus/bus-dump.h b/src/libsystemd/sd-bus/bus-dump.h index d2522edeba..71e56991fa 100644 --- a/src/libsystemd/sd-bus/bus-dump.h +++ b/src/libsystemd/sd-bus/bus-dump.h @@ -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" diff --git a/src/libsystemd/sd-bus/bus-error.h b/src/libsystemd/sd-bus/bus-error.h index fb0199c948..d7fd8612d0 100644 --- a/src/libsystemd/sd-bus/bus-error.h +++ b/src/libsystemd/sd-bus/bus-error.h @@ -24,6 +24,7 @@ #include <stdbool.h> #include "sd-bus.h" + #include "macro.h" bool bus_error_is_dirty(sd_bus_error *e); diff --git a/src/libsystemd/sd-bus/bus-introspect.h b/src/libsystemd/sd-bus/bus-introspect.h index 1914e6cb8b..57c2430ee8 100644 --- a/src/libsystemd/sd-bus/bus-introspect.h +++ b/src/libsystemd/sd-bus/bus-introspect.h @@ -24,6 +24,7 @@ #include <stdio.h> #include "sd-bus.h" + #include "set.h" struct introspect { diff --git a/src/libsystemd/sd-bus/bus-slot.h b/src/libsystemd/sd-bus/bus-slot.h index 23a15e4d02..c997e58f9a 100644 --- a/src/libsystemd/sd-bus/bus-slot.h +++ b/src/libsystemd/sd-bus/bus-slot.h @@ -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); diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c index 700ac691b5..3191b458d1 100644 --- a/src/libsystemd/sd-event/sd-event.c +++ b/src/libsystemd/sd-event/sd-event.c @@ -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; diff --git a/src/libsystemd/sd-event/test-event.c b/src/libsystemd/sd-event/test-event.c index c1a3b49483..9417a8d1d1 100644 --- a/src/libsystemd/sd-event/test-event.c +++ b/src/libsystemd/sd-event/test-event.c @@ -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); diff --git a/src/libsystemd/sd-netlink/local-addresses.h b/src/libsystemd/sd-netlink/local-addresses.h index 5d0f11a2c1..74d4f25534 100644 --- a/src/libsystemd/sd-netlink/local-addresses.h +++ b/src/libsystemd/sd-netlink/local-addresses.h @@ -23,6 +23,7 @@ #include "sd-netlink.h" + #include "in-addr-util.h" struct local_address { diff --git a/src/libudev/libudev-private.h b/src/libudev/libudev-private.h index 5f50496291..52c5075110 100644 --- a/src/libudev/libudev-private.h +++ b/src/libudev/libudev-private.h @@ -21,8 +21,8 @@ #define _LIBUDEV_PRIVATE_H_ #include <signal.h> -#include <stdint.h> #include <stdbool.h> +#include <stdint.h> #include "libudev.h" diff --git a/src/login/logind-acl.h b/src/login/logind-acl.h index 93e9ed02eb..1f55759798 100644 --- a/src/login/logind-acl.h +++ b/src/login/logind-acl.h @@ -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" diff --git a/src/login/logind-action.h b/src/login/logind-action.h index e9b424b5f6..63c279cde7 100644 --- a/src/login/logind-action.h +++ b/src/login/logind-action.h @@ -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, diff --git a/src/login/logind-session.h b/src/login/logind-session.h index d054c33cec..d27407fc92 100644 --- a/src/login/logind-session.h +++ b/src/login/logind-session.h @@ -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 */ diff --git a/src/machine/machined.h b/src/machine/machined.h index dac7a29ed1..bc5d4abb80 100644 --- a/src/machine/machined.h +++ b/src/machine/machined.h @@ -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; diff --git a/src/network/networkd-address-pool.h b/src/network/networkd-address-pool.h index e6207ccce6..7f5bdf1d2f 100644 --- a/src/network/networkd-address-pool.h +++ b/src/network/networkd-address-pool.h @@ -23,6 +23,7 @@ typedef struct AddressPool AddressPool; +#include "in-addr-util.h" #include "networkd.h" struct AddressPool { diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h index 4049a23bdc..accd0a027d 100644 --- a/src/network/networkd-address.h +++ b/src/network/networkd-address.h @@ -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 diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index b9c60a3c77..48e3d84055 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -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; diff --git a/src/network/networkd-fdb.h b/src/network/networkd-fdb.h index f0efb902d0..c8e3f2ce56 100644 --- a/src/network/networkd-fdb.h +++ b/src/network/networkd-fdb.h @@ -23,8 +23,8 @@ typedef struct FdbEntry FdbEntry; -#include "networkd.h" #include "networkd-network.h" +#include "networkd.h" struct FdbEntry { Network *network; diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 01d5942ce5..a9d91b07f6 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -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, '/'); diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index aa2235b11d..3964a12f37 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -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; diff --git a/src/network/networkd-netdev-vxlan.h b/src/network/networkd-netdev-vxlan.h index d21f355f5d..16977ea6a9 100644 --- a/src/network/networkd-netdev-vxlan.h +++ b/src/network/networkd-netdev-vxlan.h @@ -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 diff --git a/src/network/networkd-netdev.h b/src/network/networkd-netdev.h index 3b9ab27b67..3ab39efd57 100644 --- a/src/network/networkd-netdev.h +++ b/src/network/networkd-netdev.h @@ -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 */ diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index a27c67eea5..cb3a50d9ba 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -25,12 +25,12 @@ 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 diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h index b276756674..37c12907d7 100644 --- a/src/network/networkd-route.h +++ b/src/network/networkd-route.h @@ -23,8 +23,8 @@ typedef struct Route Route; -#include "networkd.h" #include "networkd-network.h" +#include "networkd.h" struct Route { Network *network; diff --git a/src/network/networkd.h b/src/network/networkd.h index 97665fac7a..8086e528bf 100644 --- a/src/network/networkd.h +++ b/src/network/networkd.h @@ -23,19 +23,19 @@ #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 { diff --git a/src/nspawn/nspawn-cgroup.h b/src/nspawn/nspawn-cgroup.h index 985fdfaad5..4e8db63750 100644 --- a/src/nspawn/nspawn-cgroup.h +++ b/src/nspawn/nspawn-cgroup.h @@ -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); diff --git a/src/nspawn/nspawn-expose-ports.h b/src/nspawn/nspawn-expose-ports.h index 39cec28695..cb7340bad7 100644 --- a/src/nspawn/nspawn-expose-ports.h +++ b/src/nspawn/nspawn-expose-ports.h @@ -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; diff --git a/src/nspawn/nspawn-network.h b/src/nspawn/nspawn-network.h index b86effef47..c91fc79c42 100644 --- a/src/nspawn/nspawn-network.h +++ b/src/nspawn/nspawn-network.h @@ -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); diff --git a/src/nspawn/nspawn-settings.h b/src/nspawn/nspawn-settings.h index dde0d8bd45..10230a5b83 100644 --- a/src/nspawn/nspawn-settings.h +++ b/src/nspawn/nspawn-settings.h @@ -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, diff --git a/src/nss-mymachines/nss-mymachines.c b/src/nss-mymachines/nss-mymachines.c index 969fa9619e..c98a959b3b 100644 --- a/src/nss-mymachines/nss-mymachines.c +++ b/src/nss-mymachines/nss-mymachines.c @@ -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; diff --git a/src/resolve-host/resolve-host.c b/src/resolve-host/resolve-host.c index eb4e646846..f68751a2e5 100644 --- a/src/resolve-host/resolve-host.c +++ b/src/resolve-host/resolve-host.c @@ -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; diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index f0a3b607d4..62bb08a2e8 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -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, }; diff --git a/src/resolve/resolved-conf.c b/src/resolve/resolved-conf.c index 9207719551..3fc7d9ae3d 100644 --- a/src/resolve/resolved-conf.c +++ b/src/resolve/resolved-conf.c @@ -27,53 +27,99 @@ #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; + } diff --git a/src/resolve/resolved-conf.h b/src/resolve/resolved-conf.h index b3dbea7b6b..28d2549d35 100644 --- a/src/resolve/resolved-conf.h +++ b/src/resolve/resolved-conf.h @@ -23,10 +23,16 @@ #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); diff --git a/src/resolve/resolved-def.h b/src/resolve/resolved-def.h index 086d111205..be29f51663 100644 --- a/src/resolve/resolved-def.h +++ b/src/resolve/resolved-def.h @@ -21,10 +21,13 @@ 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) diff --git a/src/resolve/resolved-dns-answer.c b/src/resolve/resolved-dns-answer.c index 3cf9c68074..4db67f7278 100644 --- a/src/resolve/resolved-dns-answer.c +++ b/src/resolve/resolved-dns-answer.c @@ -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) diff --git a/src/resolve/resolved-dns-answer.h b/src/resolve/resolved-dns-answer.h index 044d73b19c..8814919deb 100644 --- a/src/resolve/resolved-dns-answer.h +++ b/src/resolve/resolved-dns-answer.h @@ -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)) diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c index 04f64022e0..d963ce6e00 100644 --- a/src/resolve/resolved-dns-cache.c +++ b/src/resolve/resolved-dns-cache.c @@ -20,8 +20,10 @@ ***/ #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) { diff --git a/src/resolve/resolved-dns-cache.h b/src/resolve/resolved-dns-cache.h index 60cf6a4784..164435b4fb 100644 --- a/src/resolve/resolved-dns-cache.h +++ b/src/resolve/resolved-dns-cache.h @@ -23,18 +23,18 @@ #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); diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index f23b3cf893..40b662246f 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -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; diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h index fbbabaf232..90b5a7c8bd 100644 --- a/src/resolve/resolved-dns-packet.h +++ b/src/resolve/resolved-dns-packet.h @@ -21,21 +21,21 @@ 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, diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c index f7cb84e2a6..a96cf439ad 100644 --- a/src/resolve/resolved-dns-query.c +++ b/src/resolve/resolved-dns-query.c @@ -30,29 +30,286 @@ #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; diff --git a/src/resolve/resolved-dns-query.h b/src/resolve/resolved-dns-query.h index e7063d9678..a9d7904a8d 100644 --- a/src/resolve/resolved-dns-query.h +++ b/src/resolve/resolved-dns-query.h @@ -23,56 +23,88 @@ #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); diff --git a/src/resolve/resolved-dns-question.c b/src/resolve/resolved-dns-question.c index 48951221dc..3249448d3b 100644 --- a/src/resolve/resolved-dns-question.c +++ b/src/resolve/resolved-dns-question.c @@ -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; +} diff --git a/src/resolve/resolved-dns-question.h b/src/resolve/resolved-dns-question.h index 13cd1f20f3..e77116c03a 100644 --- a/src/resolve/resolved-dns-question.h +++ b/src/resolve/resolved-dns-question.h @@ -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)) diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c index ba2ea686f3..4a1abb0cdc 100644 --- a/src/resolve/resolved-dns-rr.c +++ b/src/resolve/resolved-dns-rr.c @@ -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); +} diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h index 9e2207c0aa..f8066c06a6 100644 --- a/src/resolve/resolved-dns-rr.h +++ b/src/resolve/resolved-dns-rr.h @@ -24,12 +24,14 @@ #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); diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index b15370b017..fc4ae57ce0 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -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); +} diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h index b75f212897..7876410b7d 100644 --- a/src/resolve/resolved-dns-scope.h +++ b/src/resolve/resolved-dns-scope.h @@ -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 index 0000000000..f9d966abb1 --- /dev/null +++ b/src/resolve/resolved-dns-search-domain.c @@ -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 index 0000000000..2e0af31dda --- /dev/null +++ b/src/resolve/resolved-dns-search-domain.h @@ -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); diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c index e803f635ab..0ebd22fe22 100644 --- a/src/resolve/resolved-dns-server.c +++ b/src/resolve/resolved-dns-server.c @@ -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); +} diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h index 10111fd6bd..3a78d4a3b5 100644 --- a/src/resolve/resolved-dns-server.h +++ b/src/resolve/resolved-dns-server.h @@ -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; diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 6545f6cd8a..8c4f23a4da 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -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); diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h index acf6a6f651..ee80dcf5a9 100644 --- a/src/resolve/resolved-dns-transaction.h +++ b/src/resolve/resolved-dns-transaction.h @@ -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) diff --git a/src/resolve/resolved-dns-zone.c b/src/resolve/resolved-dns-zone.c index 48dcf76daa..493d11dd14 100644 --- a/src/resolve/resolved-dns-zone.c +++ b/src/resolve/resolved-dns-zone.c @@ -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) { diff --git a/src/resolve/resolved-dns-zone.h b/src/resolve/resolved-dns-zone.h index 495d17cdb1..44a8624c30 100644 --- a/src/resolve/resolved-dns-zone.h +++ b/src/resolve/resolved-dns-zone.h @@ -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); diff --git a/src/resolve/resolved-gperf.gperf b/src/resolve/resolved-gperf.gperf index 8e78fbf06a..50662656d5 100644 --- a/src/resolve/resolved-gperf.gperf +++ b/src/resolve/resolved-gperf.gperf @@ -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) diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c index 2892641075..ddd9427dab 100644 --- a/src/resolve/resolved-link.c +++ b/src/resolve/resolved-link.c @@ -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; } diff --git a/src/resolve/resolved-link.h b/src/resolve/resolved-link.h index e3ab27c249..eb00015bd5 100644 --- a/src/resolve/resolved-link.h +++ b/src/resolve/resolved-link.h @@ -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); diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index a588538b52..f1f454c786 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -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; } diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h index fe7fe99505..d00c444583 100644 --- a/src/resolve/resolved-manager.h +++ b/src/resolve/resolved-manager.h @@ -22,10 +22,12 @@ ***/ #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 index 0000000000..956f380f3c --- /dev/null +++ b/src/resolve/resolved-resolv-conf.c @@ -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 index 0000000000..a3355e994b --- /dev/null +++ b/src/resolve/resolved-resolv-conf.h @@ -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); diff --git a/src/resolve/resolved.c b/src/resolve/resolved.c index 7ba0546f4a..be406b71fe 100644 --- a/src/resolve/resolved.c +++ b/src/resolve/resolved.c @@ -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) { diff --git a/src/resolve/resolved.conf.in b/src/resolve/resolved.conf.in index 3eb19e42b7..39ecf83217 100644 --- a/src/resolve/resolved.conf.in +++ b/src/resolve/resolved.conf.in @@ -14,4 +14,5 @@ [Resolve] #DNS= #FallbackDNS=@DNS_SERVERS@ +#Domains= #LLMNR=yes diff --git a/src/run/run.c b/src/run/run.c index df6a4f0074..e1accc467b 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -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, diff --git a/src/shared/acl-util.h b/src/shared/acl-util.h index cf612e8722..256a6a5900 100644 --- a/src/shared/acl-util.h +++ b/src/shared/acl-util.h @@ -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" diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c index 73ceeba18f..8775808da4 100644 --- a/src/shared/bus-util.c +++ b/src/shared/bus-util.c @@ -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); diff --git a/src/shared/cgroup-show.h b/src/shared/cgroup-show.h index aa832454b5..5842bdd15e 100644 --- a/src/shared/cgroup-show.h +++ b/src/shared/cgroup-show.h @@ -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); diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h index fb0234baae..2872b22d9d 100644 --- a/src/shared/conf-parser.h +++ b/src/shared/conf-parser.h @@ -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" diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c index 7af15e0098..4cf6355b71 100644 --- a/src/shared/dns-domain.c +++ b/src/shared/dns-domain.c @@ -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; } diff --git a/src/shared/dns-domain.h b/src/shared/dns-domain.h index 1f0d242c18..99c72574db 100644 --- a/src/shared/dns-domain.h +++ b/src/shared/dns-domain.h @@ -26,11 +26,12 @@ #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); diff --git a/src/shared/efivars.h b/src/shared/efivars.h index e953a12737..5cb4c3af4e 100644 --- a/src/shared/efivars.h +++ b/src/shared/efivars.h @@ -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) diff --git a/src/shared/logs-show.h b/src/shared/logs-show.h index 569e1faa55..98927bbc59 100644 --- a/src/shared/logs-show.h +++ b/src/shared/logs-show.h @@ -26,8 +26,8 @@ #include "sd-journal.h" -#include "util.h" #include "output-mode.h" +#include "util.h" int output_journal( FILE *f, diff --git a/src/shared/machine-image.h b/src/shared/machine-image.h index f041600fbf..038db7453c 100644 --- a/src/shared/machine-image.h +++ b/src/shared/machine-image.h @@ -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, diff --git a/src/shared/nss-util.h b/src/shared/nss-util.h index 3657aa5d9c..a7b51a91da 100644 --- a/src/shared/nss-util.h +++ b/src/shared/nss-util.h @@ -21,11 +21,11 @@ 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) \ diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 51b82d57db..f478d809c2 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -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(;;) { diff --git a/src/systemd/sd-bus.h b/src/systemd/sd-bus.h index 43cf247cdf..d8adf59aca 100644 --- a/src/systemd/sd-bus.h +++ b/src/systemd/sd-bus.h @@ -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; diff --git a/src/systemd/sd-daemon.h b/src/systemd/sd-daemon.h index 214e77cab1..c26cd1be3a 100644 --- a/src/systemd/sd-daemon.h +++ b/src/systemd/sd-daemon.h @@ -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" diff --git a/src/systemd/sd-device.h b/src/systemd/sd-device.h index fc11725821..edf80563ac 100644 --- a/src/systemd/sd-device.h +++ b/src/systemd/sd-device.h @@ -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" diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h index c0146158f3..fc1d70e738 100644 --- a/src/systemd/sd-dhcp-client.h +++ b/src/systemd/sd-dhcp-client.h @@ -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" diff --git a/src/systemd/sd-dhcp-server.h b/src/systemd/sd-dhcp-server.h index 55bceb1ea5..56b63c38da 100644 --- a/src/systemd/sd-dhcp-server.h +++ b/src/systemd/sd-dhcp-server.h @@ -27,6 +27,7 @@ #include <netinet/in.h> #include "sd-event.h" + #include "_sd-common.h" _SD_BEGIN_DECLARATIONS; diff --git a/src/systemd/sd-dhcp6-client.h b/src/systemd/sd-dhcp6-client.h index 0ca6c07de4..29e95e2492 100644 --- a/src/systemd/sd-dhcp6-client.h +++ b/src/systemd/sd-dhcp6-client.h @@ -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" diff --git a/src/systemd/sd-event.h b/src/systemd/sd-event.h index 565de5495a..fb97f7f28d 100644 --- a/src/systemd/sd-event.h +++ b/src/systemd/sd-event.h @@ -22,11 +22,11 @@ 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); diff --git a/src/systemd/sd-ipv4acd.h b/src/systemd/sd-ipv4acd.h index 6337d61452..c1e79640eb 100644 --- a/src/systemd/sd-ipv4acd.h +++ b/src/systemd/sd-ipv4acd.h @@ -23,10 +23,11 @@ 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; diff --git a/src/systemd/sd-ipv4ll.h b/src/systemd/sd-ipv4ll.h index 2949f1dfb2..1d25f02bd0 100644 --- a/src/systemd/sd-ipv4ll.h +++ b/src/systemd/sd-ipv4ll.h @@ -22,10 +22,11 @@ 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; diff --git a/src/systemd/sd-journal.h b/src/systemd/sd-journal.h index 00237a2158..facb6d8a95 100644 --- a/src/systemd/sd-journal.h +++ b/src/systemd/sd-journal.h @@ -23,12 +23,13 @@ ***/ #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. */ diff --git a/src/systemd/sd-lldp.h b/src/systemd/sd-lldp.h index 31651ce132..16d297a52d 100644 --- a/src/systemd/sd-lldp.h +++ b/src/systemd/sd-lldp.h @@ -23,10 +23,11 @@ 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; diff --git a/src/systemd/sd-login.h b/src/systemd/sd-login.h index 59c6eedcda..2ad6bcb357 100644 --- a/src/systemd/sd-login.h +++ b/src/systemd/sd-login.h @@ -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" diff --git a/src/systemd/sd-messages.h b/src/systemd/sd-messages.h index 8aedaec6d1..072832a916 100644 --- a/src/systemd/sd-messages.h +++ b/src/systemd/sd-messages.h @@ -23,6 +23,7 @@ ***/ #include "sd-id128.h" + #include "_sd-common.h" _SD_BEGIN_DECLARATIONS; diff --git a/src/systemd/sd-ndisc.h b/src/systemd/sd-ndisc.h index 80e24325f7..71e65d4425 100644 --- a/src/systemd/sd-ndisc.h +++ b/src/systemd/sd-ndisc.h @@ -26,6 +26,7 @@ #include <net/ethernet.h> #include "sd-event.h" + #include "_sd-common.h" _SD_BEGIN_DECLARATIONS; diff --git a/src/systemd/sd-netlink.h b/src/systemd/sd-netlink.h index 2960deda0a..dd5cc04ca6 100644 --- a/src/systemd/sd-netlink.h +++ b/src/systemd/sd-netlink.h @@ -23,12 +23,13 @@ ***/ #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; diff --git a/src/systemd/sd-network.h b/src/systemd/sd-network.h index 4179015fbf..076f45745d 100644 --- a/src/systemd/sd-network.h +++ b/src/systemd/sd-network.h @@ -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" diff --git a/src/systemd/sd-resolve.h b/src/systemd/sd-resolve.h index 82c4b39efe..bfe32102f8 100644 --- a/src/systemd/sd-resolve.h +++ b/src/systemd/sd-resolve.h @@ -28,6 +28,7 @@ #include <sys/types.h> #include "sd-event.h" + #include "_sd-common.h" _SD_BEGIN_DECLARATIONS; diff --git a/src/test/test-calendarspec.c b/src/test/test-calendarspec.c index 70819b0371..9cef7154c6 100644 --- a/src/test/test-calendarspec.c +++ b/src/test/test-calendarspec.c @@ -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; } diff --git a/src/test/test-dns-domain.c b/src/test/test-dns-domain.c index d5778748a0..f010e4e19a 100644 --- a/src/test/test-dns-domain.c +++ b/src/test/test-dns-domain.c @@ -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; } diff --git a/src/test/test-siphash24.c b/src/test/test-siphash24.c index a571a95a70..c20be99350 100644 --- a/src/test/test-siphash24.c +++ b/src/test/test-siphash24.c @@ -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; diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c index c3973a316e..0b3630f77c 100644 --- a/src/test/test-unit-file.c +++ b/src/test/test-unit-file.c @@ -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); diff --git a/src/timesync/timesyncd-conf.h b/src/timesync/timesyncd-conf.h index 56466fe462..cbc19c4054 100644 --- a/src/timesync/timesyncd-conf.h +++ b/src/timesync/timesyncd-conf.h @@ -22,7 +22,6 @@ ***/ #include "conf-parser.h" - #include "timesyncd-manager.h" const struct ConfigPerfItem* timesyncd_gperf_lookup(const char *key, unsigned length); diff --git a/src/timesync/timesyncd-manager.h b/src/timesync/timesyncd-manager.h index 090b2fcba8..fab22cfe84 100644 --- a/src/timesync/timesyncd-manager.h +++ b/src/timesync/timesyncd-manager.h @@ -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" diff --git a/src/timesync/timesyncd-server.h b/src/timesync/timesyncd-server.h index 18c44445e1..f764d0737b 100644 --- a/src/timesync/timesyncd-server.h +++ b/src/timesync/timesyncd-server.h @@ -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; diff --git a/sysctl.d/50-coredump.conf.in b/sysctl.d/50-coredump.conf.in index d5f600ef45..5e04c821b6 100644 --- a/sysctl.d/50-coredump.conf.in +++ b/sysctl.d/50-coredump.conf.in @@ -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 diff --git a/test/README.testsuite b/test/README.testsuite index 5c7aca43a8..fa7e73ce3a 100644 --- a/test/README.testsuite +++ b/test/README.testsuite @@ -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 diff --git a/test/TEST-01-BASIC/test.sh b/test/TEST-01-BASIC/test.sh index b6b474393d..6963d8c88d 100755 --- a/test/TEST-01-BASIC/test.sh +++ b/test/TEST-01-BASIC/test.sh @@ -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 } diff --git a/test/TEST-02-CRYPTSETUP/test.sh b/test/TEST-02-CRYPTSETUP/test.sh index 2997da06ff..dada99df59 100755 --- a/test/TEST-02-CRYPTSETUP/test.sh +++ b/test/TEST-02-CRYPTSETUP/test.sh @@ -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" diff --git a/test/TEST-03-JOBS/test.sh b/test/TEST-03-JOBS/test.sh index 41e02e2c8a..83393435f0 100755 --- a/test/TEST-03-JOBS/test.sh +++ b/test/TEST-03-JOBS/test.sh @@ -63,7 +63,7 @@ EOF cp test-jobs.sh $initdir/ setup_testsuite - ) + ) || return 1 setup_nspawn_root ddebug "umount $TESTDIR/root" diff --git a/test/test-functions b/test/test-functions index a873ba7986..9288200717 100644 --- a/test/test-functions +++ b/test/test-functions @@ -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 ]] } diff --git a/tools/make-directive-index.py b/tools/make-directive-index.py index 17b1325bba..8091683fee 100755 --- a/tools/make-directive-index.py +++ b/tools/make-directive-index.py @@ -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' diff --git a/units/user@.service.m4.in b/units/user@.service.m4.in index 1e21d51aae..66aba4f985 100644 --- a/units/user@.service.m4.in +++ b/units/user@.service.m4.in @@ -17,3 +17,4 @@ ExecStart=-@rootlibexecdir@/systemd --user Slice=user-%i.slice KillMode=mixed Delegate=yes +TasksMax=infinity |