From 93d566d6b4a62bc13b1d8a4b7c82eb1355b0f404 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Alejandro=20Anderssen=20Gonz=C3=A1lez?= Date: Mon, 29 Apr 2019 10:59:41 +0100 Subject: [PATCH] (RFC) hostname reader --- .../lib/y2network/config_reader/hostname.rb | 130 ++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 library/network/src/lib/y2network/config_reader/hostname.rb diff --git a/library/network/src/lib/y2network/config_reader/hostname.rb b/library/network/src/lib/y2network/config_reader/hostname.rb new file mode 100644 index 000000000..efe24b3df --- /dev/null +++ b/library/network/src/lib/y2network/config_reader/hostname.rb @@ -0,0 +1,130 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "yast" +require "y2network/hostname" + +Yast.import "IP" +Yast.import "Mode" +Yast.import "Hostname" +Yast.import "String" + +module Y2Network + module ConfigReader + class Hostname + include Yast::Logger + + def self.from_system + # In installation (standard, or AutoYaST one), prefer /etc/install.inf + # (because HOSTNAME comes with netcfg.rpm already, #144687) + if (Yast::Mode.installation || Yast::Mode.autoinst) && File.exists?("/etc/install.inf") + fqhostname = read_hostname_from_install_inf + end + + # reads setup from /etc/HOSTNAME, returns a default if nothing found + fqhostname = Yast::Hostname.CurrentFQ if fqhostname.nil? || fqhostname.empty? + + Y2Network::Hostname.new(fqdn: fqhostname) + end + + private + + def read_hostname_from_install_inf + install_inf_hostname = Yast::SCR.Read(path(".etc.install_inf.Hostname")) || "" + log.info("Got #{install_inf_hostname} from install.inf") + + return "" if install_inf_hostname.empty? + + # if the name is actually IP, try to resolve it (bnc#556613, bnc#435649) + if Yast::IP.Check(install_inf_hostname) + fqhostname = ResolveIP(install_inf_hostname) + log.info("Got #{fqhostname} after resolving IP from install.inf") + else + fqhostname = install_inf_hostname + end + + fqhostname + end + + # Resolve IP to canonical hostname + # + # @param [String] ip given IP address + # @return resolved canonical hostname (FQDN) for given IP or empty string in case of failure. + def ResolveIP(ip) + getent = Yast::SCR.Execute(path(".target.bash_output"), "/usr/bin/getent hosts #{ip.shellescape}") + exit_code = getent.fetch("exit", -1) + + if exit_code != 0 + log.error("ResolveIP: getent call failed (#{getent})") + + return "" + end + + GetHostnameFromGetent(getent.fetch("stdout", "")) + end + + # Handles input as one line of getent output. Returns first hostname found + # on the line (= canonical hostname). + # + # @param [String] line in /etc/hosts format + # @return canonical hostname from given line + def GetHostnameFromGetent(line) + # line is expected same format as is used in /etc/hosts without additional + # comments (getent removes comments from the end). + # + # /etc/hosts line is formatted this way (man 5 hosts): + # + # [ ...] + # + # - field separators are at least one space and/or tab. + # - , in generic it is "a computer's unique name". In case + # of DNS world, is FQDN ("A" record), then is + # without domain part. For example: + # + # foo.example.com. IN A 1.2.3.4 + # + # => foo.example.com + # => foo + # + canonical_hostname = Yast::Builtins.regexpsub( + line, + Yast::Builtins.sformat("^[%1]+[[:blank:]]+(.*)", Yast::IP.ValidChars), + "\\1" + ) + + canonical_hostname = Yast::String.FirstChunk(canonical_hostname, " \t\n") + canonical_hostname = Yast::String.CutBlanks(canonical_hostname) + + if !Yast::Hostname.CheckDomain(canonical_hostname) && + !Yast::Hostname.Check(canonical_hostname) + log.error("GetHostnameFromGetent: Invalid hostname detected (#{canonical_hostname})") + log.error("GetHostnameFromGetent: input params - begin") + log.error(line) + log.error("GetHostnameFromGetent: input params - end") + + return "" + end + + log.info("GetHostnameFromGetEnt: canonical hostname => (#{canonical_hostname})") + + canonical_hostname + end + end + end +end