github.com/Lephar/snapd@v0.0.0-20210825215435-c7fba9cef4d2/overlord/configstate/configcore/hostname.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2020 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package configcore 21 22 import ( 23 "fmt" 24 "os" 25 "os/exec" 26 "path/filepath" 27 "regexp" 28 "strings" 29 30 "github.com/snapcore/snapd/osutil" 31 "github.com/snapcore/snapd/overlord/configstate/config" 32 "github.com/snapcore/snapd/sysconfig" 33 ) 34 35 func init() { 36 // add supported configuration of this module 37 supportedConfigurations["core.system.hostname"] = true 38 config.RegisterExternalConfig("core", "system.hostname", getHostnameFromSystemHelper) 39 } 40 41 // We are conservative here and follow hostname(7). The hostnamectl 42 // binary is more liberal but let's err on the side of caution for 43 // now. 44 var validHostname = regexp.MustCompile(`^[a-z0-9][a-z0-9-]{1,62}$`).MatchString 45 46 func validateHostnameSettings(tr config.ConfGetter) error { 47 hostname, err := coreCfg(tr, "system.hostname") 48 if err != nil { 49 return err 50 } 51 if hostname == "" { 52 return nil 53 } 54 if !validHostname(hostname) { 55 return fmt.Errorf("cannot set hostname %q: name not valid", hostname) 56 } 57 58 return nil 59 } 60 61 func handleHostnameConfiguration(_ sysconfig.Device, tr config.ConfGetter, opts *fsOnlyContext) error { 62 hostname, err := coreCfg(tr, "system.hostname") 63 if err != nil { 64 return nil 65 } 66 // nothing to do 67 if hostname == "" { 68 return nil 69 } 70 // runtime system 71 if opts == nil { 72 currentHostname, err := getHostnameFromSystem() 73 if err != nil { 74 return err 75 } 76 if hostname == currentHostname { 77 return nil 78 } 79 output, err := exec.Command("hostnamectl", "set-hostname", hostname).CombinedOutput() 80 if err != nil { 81 return fmt.Errorf("cannot set hostname: %v", osutil.OutputErr(output, err)) 82 } 83 } else { 84 // On the UC16/UC18/UC20 images the file /etc/hostname is a 85 // symlink to /etc/writable/hostname. The /etc/hostname is 86 // not part of the "writable-path" so we must set the file 87 // in /etc/writable here for this to work. 88 hostnamePath := filepath.Join(opts.RootDir, "/etc/writable/hostname") 89 if err := os.MkdirAll(filepath.Dir(hostnamePath), 0755); err != nil { 90 return err 91 } 92 if err := osutil.AtomicWriteFile(hostnamePath, []byte(hostname+"\n"), 0644, 0); err != nil { 93 return fmt.Errorf("cannot write hostname: %v", err) 94 } 95 } 96 97 return nil 98 } 99 100 func getHostnameFromSystemHelper(key string) (interface{}, error) { 101 // XXX: should we error for subkeys here? 102 return getHostnameFromSystem() 103 } 104 105 func getHostnameFromSystem() (string, error) { 106 output, err := exec.Command("hostname").CombinedOutput() 107 if err != nil { 108 return "", fmt.Errorf("cannot get hostname: %v", osutil.OutputErr(output, err)) 109 } 110 return strings.TrimSpace(string(output)), nil 111 }