diff options
42 files changed, 2684 insertions, 333 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 @@ -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.am b/Makefile.am index dd4af12c89..132e592976 100644 --- a/Makefile.am +++ b/Makefile.am @@ -661,6 +661,7 @@ EXTRA_DIST += \ README.md \ autogen.sh \ .dir-locals.el \ + .editorconfig \ .vimrc \ .ycm_extra_conf.py \ .travis.yml \ 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/systemd.service.xml b/man/systemd.service.xml index 596bbcda36..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> @@ -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/systemd-nspawn b/shell-completion/bash/systemd-nspawn index f9b740380c..2636666b5d 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,9 @@ _systemd_nspawn() { compopt -o nospace comps=$( compgen -A file -- "$cur" ) ;; + --kill-signal) + comps=$(compgen -A signal) + ;; esac COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) return 0 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 30604c58f9..52ebf11c4a 100644 --- a/src/basic/escape.h +++ b/src/basic/escape.h @@ -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/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/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/swap.c b/src/core/swap.c index ee0838e676..b6e4372fc0 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -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/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c index e8e3d7306f..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) { @@ -1052,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); @@ -1313,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) 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/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..2c17ad6ede 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,46 @@ 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" + " --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, }; 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 }, {} }; @@ -563,16 +777,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 +800,40 @@ 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 '?': return -EINVAL; @@ -599,7 +842,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 +880,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..1908cae2b7 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_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_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); 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_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, 0, 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,8 @@ static void bus_method_resolve_address_complete(DnsQuery *q) { goto finish; } + /* We don't process CNAME for PTR lookups. */ + r = sd_bus_message_new_method_return(q->request, &reply); if (r < 0) goto finish; @@ -368,16 +309,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); 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 +324,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 +345,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 +364,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,25 +384,11 @@ 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); - if (r < 0) - return r; - - r = dns_name_reverse(family, d, &reverse); + r = check_ifindex_flags(ifindex, &flags, 0, error); if (r < 0) return r; - question = dns_question_new(1); - if (!question) - 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(question, key); + r = dns_question_new_reverse(&question, family, d); if (r < 0) return r; @@ -479,21 +403,58 @@ static int bus_method_resolve_address(sd_bus_message *message, void *userdata, s 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 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 = sd_bus_message_append(m, "iqq", + ifindex, + rr->key->class, + rr->key->type); + if (r < 0) + return r; + + r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0); + if (r < 0) + return r; + + p->refuse_compression = true; + + r = dns_packet_append_rr(p, rr, &start); + if (r < 0) + return r; + + r = sd_bus_message_append_array(m, 'y', DNS_PACKET_DATA(p) + start, p->size - start); + if (r < 0) + return r; + + 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 +464,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_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 +483,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); 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 +502,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_name(q->question)); goto finish; } @@ -575,7 +519,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 +538,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; @@ -623,20 +571,535 @@ static int bus_method_resolve_record(sd_bus_message *message, void *userdata, sd 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_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); + 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_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); + 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_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); + 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_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); + 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) { + 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); + 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_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); + 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_name(q->question)); + goto finish; + } + + /* Maybe we are already finished? check now... */ + resolve_service_all_complete(q); + return; + +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 int bus_method_resolve_service(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; + 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 { + r = dns_srv_type_verify(type); + if (r < 0) + return r; + if (r == 0) + 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); + 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 const sd_bus_vtable resolve_vtable[] = { @@ -644,6 +1107,7 @@ static const sd_bus_vtable resolve_vtable[] = { 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-def.h b/src/resolve/resolved-def.h index 086d111205..85af1923ff 100644 --- a/src/resolve/resolved-def.h +++ b/src/resolve/resolved-def.h @@ -24,7 +24,9 @@ #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_NO_CNAME ((uint64_t) 8) +#define SD_RESOLVED_NO_TXT ((uint64_t) 16) +#define SD_RESOLVED_NO_ADDRESS ((uint64_t) 32) -#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.h b/src/resolve/resolved-dns-answer.h index 044d73b19c..b5b1ad56ba 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-packet.c b/src/resolve/resolved-dns-packet.c index f23b3cf893..472486777c 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, @@ -1412,24 +1463,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; } } diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h index 73803e4e82..48df5dfc53 100644 --- a/src/resolve/resolved-dns-packet.h +++ b/src/resolve/resolved-dns-packet.h @@ -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); diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c index f7cb84e2a6..c1cd650651 100644 --- a/src/resolve/resolved-dns-query.c +++ b/src/resolve/resolved-dns-query.c @@ -30,6 +30,7 @@ #define CNAME_MAX 8 #define QUERIES_MAX 2048 +#define AUXILIARY_QUERIES_MAX 64 static void dns_query_stop(DnsQuery *q) { DnsTransaction *t; @@ -48,6 +49,15 @@ DnsQuery *dns_query_free(DnsQuery *q) { if (!q) return NULL; + 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); + } + dns_query_stop(q); set_free(q->transactions); @@ -111,6 +121,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)); @@ -622,7 +655,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_name(q->question); LIST_FOREACH(scopes, s, q->manager->dns_scopes) { DnsScopeMatch match; @@ -831,12 +864,13 @@ void dns_query_ready(DnsQuery *q) { dns_query_complete(q, state); } -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 +882,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); + 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); + 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); + 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 2c28a7e284..256dddc00b 100644 --- a/src/resolve/resolved-dns-query.h +++ b/src/resolve/resolved-dns-query.h @@ -34,6 +34,16 @@ typedef struct DnsQuery DnsQuery; struct DnsQuery { Manager *manager; + + /* 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; @@ -53,8 +63,9 @@ struct DnsQuery { /* 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); @@ -65,15 +76,18 @@ struct DnsQuery { sd_bus_track *bus_track; LIST_FIELDS(DnsQuery, queries); + LIST_FIELDS(DnsQuery, auxiliary_queries); }; 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..868658652d 100644 --- a/src/resolve/resolved-dns-question.c +++ b/src/resolve/resolved-dns-question.c @@ -301,3 +301,129 @@ int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname, return 1; } + +const char *dns_question_name(DnsQuestion *q) { + assert(q); + + 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..7f65e47158 100644 --- a/src/resolve/resolved-dns-question.h +++ b/src/resolve/resolved-dns-question.h @@ -25,7 +25,7 @@ typedef struct DnsQuestion DnsQuestion; #include "resolved-dns-rr.h" -/* A simple array of resources keys */ +/* A simple array of resources keys, all sharing the same domain */ struct DnsQuestion { unsigned n_ref; @@ -37,6 +37,10 @@ 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); @@ -48,4 +52,6 @@ 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_name(DnsQuestion *q); + DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQuestion*, dns_question_unref); diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c index ba2ea686f3..636e07dea9 100644 --- a/src/resolve/resolved-dns-rr.c +++ b/src/resolve/resolved-dns-rr.c @@ -273,7 +273,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 +430,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 +600,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 +679,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 +926,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 a23546f757..c02f2ec00f 100644 --- a/src/resolve/resolved-dns-rr.h +++ b/src/resolve/resolved-dns-rr.h @@ -27,9 +27,11 @@ #include "dns-type.h" #include "hashmap.h" #include "in-addr-util.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 { @@ -198,6 +206,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-manager.c b/src/resolve/resolved-manager.c index a588538b52..a4ca7c89d3 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -553,6 +553,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); diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c index 423ccca9cc..e08143c3be 100644 --- a/src/shared/dns-domain.c +++ b/src/shared/dns-domain.c @@ -29,6 +29,7 @@ #include "hexdecoct.h" #include "parse-util.h" #include "string-util.h" +#include "utf8.h" int dns_label_unescape(const char **name, char *dest, size_t sz) { const char *n; @@ -749,3 +750,228 @@ int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len) { 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; +} + +int dns_srv_type_verify(const char *name) { + unsigned c = 0; + int r; + + if (!name) + return 0; + + 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 == -EINVAL) + return 0; + if (r < 0) + return r; + if (r == 0) + break; + + if (c >= 2) + return 0; + + if (!srv_type_label_is_valid(label, r)) + return 0; + + 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) { + _cleanup_free_ char *escaped = NULL, *n = NULL; + int r; + + assert(type); + assert(domain); + assert(ret); + + if (!dns_srv_type_verify(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); + if (r < 0) + return r; + + r = dns_name_concat(type, domain, &n); + if (r < 0) + return r; + + 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 b214897440..c6d7623e09 100644 --- a/src/shared/dns-domain.h +++ b/src/shared/dns-domain.h @@ -69,3 +69,10 @@ int dns_name_root(const char *name); int dns_name_single_label(const char *name); int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len); + +int dns_srv_type_verify(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/systemctl/systemctl.c b/src/systemctl/systemctl.c index 240fa2c551..f478d809c2 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -6550,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(;;) { @@ -6783,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/test/test-dns-domain.c b/src/test/test-dns-domain.c index 407c7d8ef7..a664e1c79c 100644 --- a/src/test/test-dns-domain.c +++ b/src/test/test-dns-domain.c @@ -316,6 +316,98 @@ 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_verify(void) { + + assert_se(dns_srv_type_verify("_http._tcp") > 0); + assert_se(dns_srv_type_verify("_foo-bar._tcp") > 0); + assert_se(dns_srv_type_verify("_w._udp") > 0); + assert_se(dns_srv_type_verify("_a800._tcp") > 0); + assert_se(dns_srv_type_verify("_a-800._tcp") > 0); + + assert_se(dns_srv_type_verify(NULL) == 0); + assert_se(dns_srv_type_verify("") == 0); + assert_se(dns_srv_type_verify("x") == 0); + assert_se(dns_srv_type_verify("_foo") == 0); + assert_se(dns_srv_type_verify("_tcp") == 0); + assert_se(dns_srv_type_verify("_") == 0); + assert_se(dns_srv_type_verify("_foo.") == 0); + assert_se(dns_srv_type_verify("_föo._tcp") == 0); + assert_se(dns_srv_type_verify("_f\no._tcp") == 0); + assert_se(dns_srv_type_verify("_800._tcp") == 0); + assert_se(dns_srv_type_verify("_-800._tcp") == 0); + assert_se(dns_srv_type_verify("_-foo._tcp") == 0); + assert_se(dns_srv_type_verify("_piep._foo._udp") == 0); +} + +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); +} + int main(int argc, char *argv[]) { test_dns_label_unescape(); @@ -331,6 +423,10 @@ int main(int argc, char *argv[]) { 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_verify(); + test_dns_service_join(); + test_dns_service_split(); return 0; } 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 |