github.com/kjdelisle/consul@v1.4.5/agent/util.go (about) 1 package agent 2 3 import ( 4 "bytes" 5 "crypto/md5" 6 "fmt" 7 "os" 8 "os/exec" 9 "os/signal" 10 osuser "os/user" 11 "strconv" 12 "strings" 13 "time" 14 15 "github.com/hashicorp/consul/types" 16 "github.com/hashicorp/go-msgpack/codec" 17 ) 18 19 // msgpackHandle is a shared handle for encoding/decoding of 20 // messages 21 var msgpackHandle = &codec.MsgpackHandle{ 22 RawToString: true, 23 WriteExt: true, 24 } 25 26 // decodeMsgPack is used to decode a MsgPack encoded object 27 func decodeMsgPack(buf []byte, out interface{}) error { 28 return codec.NewDecoder(bytes.NewReader(buf), msgpackHandle).Decode(out) 29 } 30 31 // encodeMsgPack is used to encode an object with msgpack 32 func encodeMsgPack(msg interface{}) ([]byte, error) { 33 var buf bytes.Buffer 34 err := codec.NewEncoder(&buf, msgpackHandle).Encode(msg) 35 return buf.Bytes(), err 36 } 37 38 // stringHash returns a simple md5sum for a string. 39 func stringHash(s string) string { 40 return fmt.Sprintf("%x", md5.Sum([]byte(s))) 41 } 42 43 // checkIDHash returns a simple md5sum for a types.CheckID. 44 func checkIDHash(checkID types.CheckID) string { 45 return stringHash(string(checkID)) 46 } 47 48 // setFilePermissions handles configuring ownership and permissions 49 // settings on a given file. All permission/ownership settings are 50 // optional. If no user or group is specified, the current user/group 51 // will be used. Mode is optional, and has no default (the operation is 52 // not performed if absent). User may be specified by name or ID, but 53 // group may only be specified by ID. 54 func setFilePermissions(path string, user, group, mode string) error { 55 var err error 56 uid, gid := os.Getuid(), os.Getgid() 57 58 if user != "" { 59 if uid, err = strconv.Atoi(user); err == nil { 60 goto GROUP 61 } 62 63 // Try looking up the user by name 64 u, err := osuser.Lookup(user) 65 if err != nil { 66 return fmt.Errorf("failed to look up user %s: %v", user, err) 67 } 68 uid, _ = strconv.Atoi(u.Uid) 69 } 70 71 GROUP: 72 if group != "" { 73 if gid, err = strconv.Atoi(group); err != nil { 74 return fmt.Errorf("invalid group specified: %v", group) 75 } 76 } 77 if err := os.Chown(path, uid, gid); err != nil { 78 return fmt.Errorf("failed setting ownership to %d:%d on %q: %s", 79 uid, gid, path, err) 80 } 81 82 if mode != "" { 83 mode, err := strconv.ParseUint(mode, 8, 32) 84 if err != nil { 85 return fmt.Errorf("invalid mode specified: %v", mode) 86 } 87 if err := os.Chmod(path, os.FileMode(mode)); err != nil { 88 return fmt.Errorf("failed setting permissions to %d on %q: %s", 89 mode, path, err) 90 } 91 } 92 93 return nil 94 } 95 96 // ForwardSignals will fire up a goroutine to forward signals to the given 97 // subprocess until the shutdown channel is closed. 98 func ForwardSignals(cmd *exec.Cmd, logFn func(error), shutdownCh <-chan struct{}) { 99 go func() { 100 signalCh := make(chan os.Signal, 10) 101 signal.Notify(signalCh, forwardSignals...) 102 defer signal.Stop(signalCh) 103 104 for { 105 select { 106 case sig := <-signalCh: 107 if err := cmd.Process.Signal(sig); err != nil { 108 logFn(fmt.Errorf("failed to send signal %q: %v", sig, err)) 109 } 110 111 case <-shutdownCh: 112 return 113 } 114 } 115 }() 116 } 117 118 type durationFixer map[string]bool 119 120 func NewDurationFixer(fields ...string) durationFixer { 121 d := make(map[string]bool) 122 for _, field := range fields { 123 d[field] = true 124 } 125 return d 126 } 127 128 // FixupDurations is used to handle parsing any field names in the map to time.Durations 129 func (d durationFixer) FixupDurations(raw interface{}) error { 130 rawMap, ok := raw.(map[string]interface{}) 131 if !ok { 132 return nil 133 } 134 for key, val := range rawMap { 135 switch val.(type) { 136 case map[string]interface{}: 137 if err := d.FixupDurations(val); err != nil { 138 return err 139 } 140 141 case []interface{}: 142 for _, v := range val.([]interface{}) { 143 if err := d.FixupDurations(v); err != nil { 144 return err 145 } 146 } 147 148 case []map[string]interface{}: 149 for _, v := range val.([]map[string]interface{}) { 150 if err := d.FixupDurations(v); err != nil { 151 return err 152 } 153 } 154 155 default: 156 if d[strings.ToLower(key)] { 157 // Convert a string value into an integer 158 if vStr, ok := val.(string); ok { 159 dur, err := time.ParseDuration(vStr) 160 if err != nil { 161 return err 162 } 163 rawMap[key] = dur 164 } 165 } 166 } 167 } 168 return nil 169 }