forked from evcc-io/evcc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhelper.go
124 lines (107 loc) · 3.24 KB
/
helper.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package cmd
import (
"errors"
"fmt"
"net"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"github.com/evcc-io/evcc/cmd/shutdown"
"github.com/evcc-io/evcc/util"
"github.com/spf13/viper"
)
// parseLogLevels parses --log area:level[,...] switch into levels per log area
func parseLogLevels() {
levels := viper.GetStringMapString("levels")
var level string
for _, kv := range strings.Split(viper.GetString("log"), ",") {
areaLevel := strings.SplitN(kv, ":", 2)
if len(areaLevel) == 1 {
level = areaLevel[0]
} else {
levels[areaLevel[0]] = areaLevel[1]
}
}
util.LogLevel(level, levels)
}
// unwrap converts a wrapped error into slice of strings
func unwrap(err error) (res []string) {
for err != nil {
inner := errors.Unwrap(err)
if inner == nil {
res = append(res, err.Error())
} else {
cur := strings.TrimSuffix(err.Error(), ": "+inner.Error())
cur = strings.TrimSuffix(cur, inner.Error())
res = append(res, strings.TrimSpace(cur))
}
err = inner
}
return
}
// redact redacts a configuration string
func redact(src string) string {
secrets := []string{
"mac", // infrastructure
"sponsortoken", "plant", // global settings
"user", "password", "pin", // users
"token", "access", "refresh", "accesstoken", "refreshtoken", // tokens, including template variations
"ain", "secret", "serial", "deviceid", "machineid", "idtag", // devices
"app", "chats", "recipients", // push messaging
"vin"} // vehicles
return regexp.
MustCompile(fmt.Sprintf(`(?i)\b(%s)\b.*?:.*`, strings.Join(secrets, "|"))).
ReplaceAllString(src, "$1: *****")
}
func publishErrorInfo(valueChan chan<- util.Param, cfgFile string, err error) {
if cfgFile != "" {
file, pathErr := filepath.Abs(cfgFile)
if pathErr != nil {
file = cfgFile
}
valueChan <- util.Param{Key: "file", Val: file}
if src, fileErr := os.ReadFile(cfgFile); fileErr != nil {
log.ERROR.Println("could not open config file:", fileErr)
} else {
valueChan <- util.Param{Key: "config", Val: redact(string(src))}
// find line number
if match := regexp.MustCompile(`yaml: line (\d+):`).FindStringSubmatch(err.Error()); len(match) == 2 {
if line, err := strconv.Atoi(match[1]); err == nil {
valueChan <- util.Param{Key: "line", Val: line}
}
}
}
}
valueChan <- util.Param{Key: "fatal", Val: unwrap(err)}
}
// fatal logs a fatal error and runs shutdown functions before terminating
func fatal(err error) {
log.FATAL.Println(err)
<-shutdownDoneC()
os.Exit(1)
}
// shutdownDoneC returns a channel that closes when shutdown has completed
func shutdownDoneC() <-chan struct{} {
doneC := make(chan struct{})
go shutdown.Cleanup(doneC)
return doneC
}
func wrapErrors(err error) error {
if err != nil {
var opErr *net.OpError
var pathErr *os.PathError
switch {
case errors.As(err, &opErr):
if opErr.Op == "listen" && strings.Contains(opErr.Error(), "address already in use") {
err = fmt.Errorf("could not open port- check that evcc is not already running (%w)", err)
}
case errors.As(err, &pathErr):
if pathErr.Op == "remove" && strings.Contains(pathErr.Error(), "operation not permitted") {
err = fmt.Errorf("could not remove file- check that evcc is not already running (%w)", err)
}
}
}
return err
}