github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/overlord/configstate/configcore/utils.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2017 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 "bufio" 24 "fmt" 25 "io" 26 "regexp" 27 ) 28 29 // first match is if it is comment, second is key, third value 30 var rx = regexp.MustCompile(`^[ \t]*(#?)[ \t#]*([a-z0-9_]+)=(.*)$`) 31 32 // updateKeyValueStream updates simple key=value files with comments. 33 // Example for such formats are: /etc/environment or /boot/uboot/config.txt 34 // 35 // An r io.Reader, map of supported config keys and a configuration 36 // "patch" is taken as input, the r is read line-by-line and any line 37 // and any required configuration change from the "config" input is 38 // applied. 39 // 40 // If changes need to be written a []string 41 // that contains the full file is returned. On error an error is returned. 42 func updateKeyValueStream(r io.Reader, supportedConfigKeys map[string]bool, newConfig map[string]string) (toWrite []string, err error) { 43 cfgKeys := make([]string, len(newConfig)) 44 i := 0 45 for k := range newConfig { 46 if !supportedConfigKeys[k] { 47 return nil, fmt.Errorf("cannot set unsupported configuration value %q", k) 48 } 49 cfgKeys[i] = k 50 i++ 51 } 52 53 // now go over the content 54 found := map[string]bool{} 55 needsWrite := false 56 57 scanner := bufio.NewScanner(r) 58 for scanner.Scan() { 59 line := scanner.Text() 60 matches := rx.FindStringSubmatch(line) 61 if len(matches) > 0 && supportedConfigKeys[matches[2]] { 62 wasComment := (matches[1] == "#") 63 key := matches[2] 64 oldValue := matches[3] 65 found[key] = true 66 if newConfig[key] != "" { 67 if wasComment || oldValue != newConfig[key] { 68 line = fmt.Sprintf("%s=%s", key, newConfig[key]) 69 needsWrite = true 70 } 71 } else { 72 if !wasComment { 73 line = fmt.Sprintf("#%s=%s", key, oldValue) 74 needsWrite = true 75 } 76 } 77 } 78 toWrite = append(toWrite, line) 79 } 80 if err := scanner.Err(); err != nil { 81 return nil, err 82 } 83 84 // write anything that is missing 85 for key := range newConfig { 86 if !found[key] && newConfig[key] != "" { 87 needsWrite = true 88 toWrite = append(toWrite, fmt.Sprintf("%s=%s", key, newConfig[key])) 89 } 90 } 91 92 if needsWrite { 93 return toWrite, nil 94 } 95 96 return nil, nil 97 }