github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/overlord/configstate/configcore/watchdog.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2018 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 "sort" 26 "strings" 27 "time" 28 29 "github.com/snapcore/snapd/dirs" 30 "github.com/snapcore/snapd/osutil" 31 "github.com/snapcore/snapd/overlord/configstate/config" 32 "github.com/snapcore/snapd/systemd" 33 ) 34 35 func init() { 36 // add supported configuration of this module 37 supportedConfigurations["core.watchdog.runtime-timeout"] = true 38 supportedConfigurations["core.watchdog.shutdown-timeout"] = true 39 } 40 41 func updateWatchdogConfig(config map[string]uint, opts *fsOnlyContext) error { 42 var sysd systemd.Systemd 43 44 dir := dirs.SnapSystemdConfDir 45 if opts != nil { 46 dir = dirs.SnapSystemdConfDirUnder(opts.RootDir) 47 } else { 48 sysd = systemd.New(dirs.GlobalRootDir, systemd.SystemMode, &sysdLogger{}) 49 } 50 51 name := "10-snapd-watchdog.conf" 52 dirContent := make(map[string]osutil.FileState, 1) 53 54 configStr := []string{} 55 for k, v := range config { 56 if v > 0 { 57 configStr = append(configStr, fmt.Sprintf("%s=%d\n", k, v)) 58 } 59 } 60 if len(configStr) > 0 { 61 if err := os.MkdirAll(dir, 0755); err != nil { 62 return err 63 } 64 65 // We order the variables to have predictable output 66 sort.Strings(configStr) 67 content := "[Manager]\n" + strings.Join(configStr, "") 68 dirContent[name] = &osutil.MemoryFileState{ 69 Content: []byte(content), 70 Mode: 0644, 71 } 72 } 73 74 glob := name 75 changed, removed, err := osutil.EnsureDirState(dir, glob, dirContent) 76 if err != nil { 77 return err 78 } 79 80 // something was changed, reexec systemd manager 81 if sysd != nil && (len(changed) > 0 || len(removed) > 0) { 82 return sysd.DaemonReexec() 83 } 84 85 return nil 86 } 87 88 func handleWatchdogConfiguration(tr config.ConfGetter, opts *fsOnlyContext) error { 89 config := map[string]uint{} 90 91 for _, key := range []string{"runtime-timeout", "shutdown-timeout"} { 92 output, err := coreCfg(tr, "watchdog."+key) 93 if err != nil { 94 return err 95 } 96 secs, err := getSystemdConfSeconds(output) 97 if err != nil { 98 return fmt.Errorf("cannot set timer to %q: %v", output, err) 99 } 100 switch key { 101 case "runtime-timeout": 102 config["RuntimeWatchdogSec"] = secs 103 case "shutdown-timeout": 104 config["ShutdownWatchdogSec"] = secs 105 } 106 } 107 108 if err := updateWatchdogConfig(config, opts); err != nil { 109 return err 110 } 111 112 return nil 113 } 114 115 func getSystemdConfSeconds(timeStr string) (uint, error) { 116 if timeStr == "" { 117 return 0, nil 118 } 119 120 dur, err := time.ParseDuration(timeStr) 121 if err != nil { 122 return 0, fmt.Errorf("cannot parse %q: %v", timeStr, err) 123 } 124 if dur < 0 { 125 return 0, fmt.Errorf("cannot use negative duration %q: %v", timeStr, err) 126 } 127 128 return uint(dur.Seconds()), nil 129 } 130 131 func validateWatchdogOptions(tr config.ConfGetter) error { 132 for _, key := range []string{"runtime-timeout", "shutdown-timeout"} { 133 option, err := coreCfg(tr, "watchdog."+key) 134 if err != nil { 135 return err 136 } 137 if _, err = getSystemdConfSeconds(option); err != nil { 138 return err 139 } 140 } 141 142 return nil 143 }