package config import ( "encoding/base64" "fmt" "strconv" "strings" "github.com/mimecast/dtail/internal/lcontext" "github.com/mimecast/dtail/internal/omode" gossh "golang.org/x/crypto/ssh" ) // Args is a helper struct to summarize common client arguments. type Args struct { lcontext.LContext Arguments []string ConfigFile string ConnectionsPerCPU int ControlTTYPath string Discovery string InteractiveQuery bool LogDir string Logger string LogLevel string Mode omode.Mode NoAuthKey bool NoColor bool QueryStr string Quiet bool RegexInvert bool RegexStr string SSHAgentKeyIndex int SSHAuthMethods []gossh.AuthMethod SSHBindAddress string SSHHostKeyCallback gossh.HostKeyCallback SSHPort int SSHPrivateKeyFilePath string Serverless bool ServersStr string Plain bool Timeout int TrustAllHosts bool UserName string What string } func (a *Args) String() string { var sb strings.Builder sb.WriteString("Args(") sb.WriteString(fmt.Sprintf("%s:%v,", "Arguments", a.Arguments)) sb.WriteString(fmt.Sprintf("%s:%v,", "ConfigFile", a.ConfigFile)) sb.WriteString(fmt.Sprintf("%s:%v,", "ConnectionsPerCPU", a.ConnectionsPerCPU)) sb.WriteString(fmt.Sprintf("%s:%v,", "ControlTTYPath", a.ControlTTYPath)) sb.WriteString(fmt.Sprintf("%s:%v,", "Discovery", a.Discovery)) sb.WriteString(fmt.Sprintf("%s:%v,", "InteractiveQuery", a.InteractiveQuery)) sb.WriteString(fmt.Sprintf("%s:%v,", "LogDir", a.LogDir)) sb.WriteString(fmt.Sprintf("%s:%v,", "LogLevel", a.LogLevel)) sb.WriteString(fmt.Sprintf("%s:%v,", "Logger", a.Logger)) sb.WriteString(fmt.Sprintf("%s:%v,", "Mode", a.Mode)) sb.WriteString(fmt.Sprintf("%s:%v,", "NoAuthKey", a.NoAuthKey)) sb.WriteString(fmt.Sprintf("%s:%v,", "NoColor", a.NoColor)) sb.WriteString(fmt.Sprintf("%s:%v,", "QueryStr", a.QueryStr)) sb.WriteString(fmt.Sprintf("%s:%v,", "Quiet", a.Quiet)) sb.WriteString(fmt.Sprintf("%s:%v,", "RegexInvert", a.RegexInvert)) sb.WriteString(fmt.Sprintf("%s:%v,", "RegexStr", a.RegexStr)) sb.WriteString(fmt.Sprintf("%s:%v,", "SSHAgentKeyIndex", a.SSHAgentKeyIndex)) sb.WriteString(fmt.Sprintf("%s:%v,", "SSHAuthMethods", a.SSHAuthMethods)) sb.WriteString(fmt.Sprintf("%s:%v,", "SSHBindAddress", a.SSHBindAddress)) sb.WriteString(fmt.Sprintf("%s:%v,", "SSHHostKeyCallback", a.SSHHostKeyCallback)) sb.WriteString(fmt.Sprintf("%s:%v,", "SSHPrivateKeyFilePath", a.SSHPrivateKeyFilePath)) sb.WriteString(fmt.Sprintf("%s:%v,", "SSHPort", a.SSHPort)) sb.WriteString(fmt.Sprintf("%s:%v,", "Serverless", a.Serverless)) sb.WriteString(fmt.Sprintf("%s:%v,", "ServersStr", a.ServersStr)) sb.WriteString(fmt.Sprintf("%s:%v,", "Plain", a.Plain)) sb.WriteString(fmt.Sprintf("%s:%v,", "Timeout", a.Timeout)) sb.WriteString(fmt.Sprintf("%s:%v,", "TrustAllHosts", a.TrustAllHosts)) sb.WriteString(fmt.Sprintf("%s:%v,", "UserName", a.UserName)) sb.WriteString(fmt.Sprintf("%s:%v", "What", a.What)) sb.WriteString(")") return sb.String() } // SerializeOptions returns a string ready to be sent over the wire to the server. func (a *Args) SerializeOptions() string { options := make(map[string]string) if a.Quiet { options["quiet"] = fmt.Sprintf("%v", a.Quiet) } if a.Plain { options["plain"] = fmt.Sprintf("%v", a.Plain) } if a.Serverless { options["serverless"] = fmt.Sprintf("%v", a.Serverless) } if a.LContext.MaxCount != 0 { options["max"] = fmt.Sprintf("%d", a.LContext.MaxCount) } if a.LContext.BeforeContext != 0 { options["before"] = fmt.Sprintf("%d", a.LContext.BeforeContext) } if a.LContext.AfterContext != 0 { options["after"] = fmt.Sprintf("%d", a.LContext.AfterContext) } var sb strings.Builder var i int for k, v := range options { if i > 0 { sb.WriteString(":") } sb.WriteString(k) sb.WriteString("=") sb.WriteString(v) i++ } return sb.String() } // DeserializeOptions deserializes the options, but into a map. func DeserializeOptions(opts []string) (map[string]string, lcontext.LContext, error) { options := make(map[string]string, len(opts)) var ltx lcontext.LContext for _, o := range opts { kv := strings.SplitN(o, "=", 2) if len(kv) != 2 { return options, ltx, fmt.Errorf("Unable to parse options: %v", kv) } key := kv[0] val := kv[1] if strings.HasPrefix(val, "base64%") { s := strings.SplitN(val, "%", 2) decoded, err := base64.StdEncoding.DecodeString(s[1]) if err != nil { return options, ltx, err } val = string(decoded) } var err error if options, err = setOption(key, val, options, <x); err != nil { return options, ltx, err } } return options, ltx, nil } func setOption(key, val string, options map[string]string, ltx *lcontext.LContext) (map[string]string, error) { switch key { case "before": iVal, err := strconv.Atoi(val) if err != nil { return options, err } ltx.BeforeContext = iVal case "after": iVal, err := strconv.Atoi(val) if err != nil { return options, err } ltx.AfterContext = iVal case "max": iVal, err := strconv.Atoi(val) if err != nil { return options, err } ltx.MaxCount = iVal default: options[key] = val } return options, nil }