2014-01-15 22:17:40 +00:00
|
|
|
package agent
|
|
|
|
|
|
|
|
import (
|
2014-08-29 18:24:41 +00:00
|
|
|
"bytes"
|
2015-01-08 03:11:21 +00:00
|
|
|
"crypto/md5"
|
2014-08-27 23:49:12 +00:00
|
|
|
"fmt"
|
2014-08-22 19:47:58 +00:00
|
|
|
"os"
|
2017-10-04 23:48:00 +00:00
|
|
|
"os/exec"
|
|
|
|
"os/signal"
|
2017-09-25 18:40:42 +00:00
|
|
|
osuser "os/user"
|
2015-01-20 21:44:27 +00:00
|
|
|
"strconv"
|
2017-11-01 21:25:46 +00:00
|
|
|
"strings"
|
|
|
|
"time"
|
2014-08-29 18:24:41 +00:00
|
|
|
|
2016-06-06 20:19:31 +00:00
|
|
|
"github.com/hashicorp/consul/types"
|
2014-10-18 01:26:19 +00:00
|
|
|
"github.com/hashicorp/go-msgpack/codec"
|
2014-01-15 22:17:40 +00:00
|
|
|
)
|
|
|
|
|
2017-02-28 21:41:09 +00:00
|
|
|
// msgpackHandle is a shared handle for encoding/decoding of
|
|
|
|
// messages
|
|
|
|
var msgpackHandle = &codec.MsgpackHandle{
|
|
|
|
RawToString: true,
|
|
|
|
WriteExt: true,
|
|
|
|
}
|
|
|
|
|
2014-08-29 18:24:41 +00:00
|
|
|
// decodeMsgPack is used to decode a MsgPack encoded object
|
|
|
|
func decodeMsgPack(buf []byte, out interface{}) error {
|
|
|
|
return codec.NewDecoder(bytes.NewReader(buf), msgpackHandle).Decode(out)
|
|
|
|
}
|
|
|
|
|
|
|
|
// encodeMsgPack is used to encode an object with msgpack
|
|
|
|
func encodeMsgPack(msg interface{}) ([]byte, error) {
|
|
|
|
var buf bytes.Buffer
|
|
|
|
err := codec.NewEncoder(&buf, msgpackHandle).Encode(msg)
|
|
|
|
return buf.Bytes(), err
|
|
|
|
}
|
2015-01-08 03:11:21 +00:00
|
|
|
|
|
|
|
// stringHash returns a simple md5sum for a string.
|
|
|
|
func stringHash(s string) string {
|
|
|
|
return fmt.Sprintf("%x", md5.Sum([]byte(s)))
|
|
|
|
}
|
2015-01-20 21:44:27 +00:00
|
|
|
|
2016-06-06 20:19:31 +00:00
|
|
|
// checkIDHash returns a simple md5sum for a types.CheckID.
|
|
|
|
func checkIDHash(checkID types.CheckID) string {
|
|
|
|
return stringHash(string(checkID))
|
2016-06-06 08:53:30 +00:00
|
|
|
}
|
|
|
|
|
2017-09-25 18:40:42 +00:00
|
|
|
// setFilePermissions handles configuring ownership and permissions
|
|
|
|
// settings on a given file. All permission/ownership settings are
|
|
|
|
// optional. If no user or group is specified, the current user/group
|
|
|
|
// will be used. Mode is optional, and has no default (the operation is
|
|
|
|
// not performed if absent). User may be specified by name or ID, but
|
|
|
|
// group may only be specified by ID.
|
|
|
|
func setFilePermissions(path string, user, group, mode string) error {
|
2015-01-20 21:44:27 +00:00
|
|
|
var err error
|
|
|
|
uid, gid := os.Getuid(), os.Getgid()
|
2015-01-21 00:21:23 +00:00
|
|
|
|
2017-09-25 18:40:42 +00:00
|
|
|
if user != "" {
|
|
|
|
if uid, err = strconv.Atoi(user); err == nil {
|
2015-01-21 00:21:23 +00:00
|
|
|
goto GROUP
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try looking up the user by name
|
2018-01-26 15:55:43 +00:00
|
|
|
u, err := osuser.Lookup(user)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to look up user %s: %v", user, err)
|
2015-01-20 21:44:27 +00:00
|
|
|
}
|
2018-01-26 15:55:43 +00:00
|
|
|
uid, _ = strconv.Atoi(u.Uid)
|
2015-01-20 21:44:27 +00:00
|
|
|
}
|
2015-01-21 00:21:23 +00:00
|
|
|
|
|
|
|
GROUP:
|
2017-09-25 18:40:42 +00:00
|
|
|
if group != "" {
|
|
|
|
if gid, err = strconv.Atoi(group); err != nil {
|
|
|
|
return fmt.Errorf("invalid group specified: %v", group)
|
2015-01-20 21:44:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if err := os.Chown(path, uid, gid); err != nil {
|
2015-01-21 00:21:23 +00:00
|
|
|
return fmt.Errorf("failed setting ownership to %d:%d on %q: %s",
|
2015-01-20 21:44:27 +00:00
|
|
|
uid, gid, path, err)
|
|
|
|
}
|
|
|
|
|
2017-09-25 18:40:42 +00:00
|
|
|
if mode != "" {
|
|
|
|
mode, err := strconv.ParseUint(mode, 8, 32)
|
2015-01-20 21:44:27 +00:00
|
|
|
if err != nil {
|
2017-09-25 18:40:42 +00:00
|
|
|
return fmt.Errorf("invalid mode specified: %v", mode)
|
2015-01-20 21:44:27 +00:00
|
|
|
}
|
|
|
|
if err := os.Chmod(path, os.FileMode(mode)); err != nil {
|
|
|
|
return fmt.Errorf("failed setting permissions to %d on %q: %s",
|
|
|
|
mode, path, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2017-10-04 23:48:00 +00:00
|
|
|
|
|
|
|
// ForwardSignals will fire up a goroutine to forward signals to the given
|
|
|
|
// subprocess until the shutdown channel is closed.
|
|
|
|
func ForwardSignals(cmd *exec.Cmd, logFn func(error), shutdownCh <-chan struct{}) {
|
|
|
|
go func() {
|
|
|
|
signalCh := make(chan os.Signal, 10)
|
|
|
|
signal.Notify(signalCh, os.Interrupt, os.Kill)
|
|
|
|
defer signal.Stop(signalCh)
|
|
|
|
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case sig := <-signalCh:
|
|
|
|
if err := cmd.Process.Signal(sig); err != nil {
|
|
|
|
logFn(fmt.Errorf("failed to send signal %q: %v", sig, err))
|
|
|
|
}
|
|
|
|
|
|
|
|
case <-shutdownCh:
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
2017-11-01 21:25:46 +00:00
|
|
|
|
|
|
|
type durationFixer map[string]bool
|
|
|
|
|
|
|
|
func NewDurationFixer(fields ...string) durationFixer {
|
|
|
|
d := make(map[string]bool)
|
|
|
|
for _, field := range fields {
|
|
|
|
d[field] = true
|
|
|
|
}
|
|
|
|
return d
|
|
|
|
}
|
|
|
|
|
|
|
|
// FixupDurations is used to handle parsing any field names in the map to time.Durations
|
|
|
|
func (d durationFixer) FixupDurations(raw interface{}) error {
|
|
|
|
rawMap, ok := raw.(map[string]interface{})
|
|
|
|
if !ok {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
for key, val := range rawMap {
|
|
|
|
switch val.(type) {
|
|
|
|
case map[string]interface{}:
|
|
|
|
if err := d.FixupDurations(val); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
case []interface{}:
|
|
|
|
for _, v := range val.([]interface{}) {
|
|
|
|
if err := d.FixupDurations(v); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
case []map[string]interface{}:
|
|
|
|
for _, v := range val.([]map[string]interface{}) {
|
|
|
|
if err := d.FixupDurations(v); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
if d[strings.ToLower(key)] {
|
|
|
|
// Convert a string value into an integer
|
|
|
|
if vStr, ok := val.(string); ok {
|
|
|
|
dur, err := time.ParseDuration(vStr)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
rawMap[key] = dur
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|