126 lines
2.9 KiB
Go
126 lines
2.9 KiB
Go
package cli
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"io"
|
|
"os/exec"
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
var re = regexp.MustCompile(`\w|\%|^(\+|,|\.|:|\/|\\|=|_|-|[a-z]|[A-Z]|[0-9]|\s)+$`)
|
|
|
|
func validateCliArgument(arg string) bool {
|
|
return re.Match([]byte(arg))
|
|
}
|
|
|
|
func validateCliArguments(args []string) bool {
|
|
for _, p := range args {
|
|
if !validateCliArgument(p) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func execNoCheck(ctx context.Context, command string, args []string) (string, string, error) {
|
|
cmd := exec.CommandContext(ctx, command, args...)
|
|
cmdStr := cmd.String()
|
|
|
|
pStdout, err := cmd.StdoutPipe()
|
|
if err != nil {
|
|
return "", cmdStr, err
|
|
}
|
|
|
|
pStderr, err := cmd.StderrPipe()
|
|
if err != nil {
|
|
return "", cmdStr, err
|
|
}
|
|
|
|
err = cmd.Start()
|
|
if err != nil {
|
|
return "", cmdStr, err
|
|
}
|
|
|
|
stdout, _ := io.ReadAll(pStdout)
|
|
stderr, _ := io.ReadAll(pStderr)
|
|
|
|
err = cmd.Wait()
|
|
if err != nil {
|
|
return "", cmdStr, errors.New(err.Error() + " | " + string(stderr) + " | " + string(stdout))
|
|
}
|
|
return string(stdout), cmdStr, nil
|
|
}
|
|
|
|
type Pipe struct {
|
|
Command string
|
|
SuffixSymbol string
|
|
Args []string
|
|
}
|
|
|
|
func (p *Pipe) IsSanatory() bool {
|
|
return validateCliArguments(p.Args) && validateCliArgument(p.Command)
|
|
}
|
|
|
|
func (p *Pipe) Execute(ctx context.Context) (string, string, error) {
|
|
if !p.IsSanatory() {
|
|
return "", "", errors.New("failed to execute cli command: " + p.Command + " " + strings.Join(p.Args, " ") + " some arguments are not allowed")
|
|
} else {
|
|
return execNoCheck(ctx, p.Command, p.Args)
|
|
}
|
|
}
|
|
|
|
// ExecuteCombined runs the command and returns combined stdout+stderr output.
|
|
// Useful when a tool writes useful output to either stream.
|
|
func (p *Pipe) ExecuteCombined(ctx context.Context) (string, string, error) {
|
|
if !p.IsSanatory() {
|
|
return "", "", errors.New("failed to execute cli command: " + p.Command + " " + strings.Join(p.Args, " ") + " some arguments are not allowed")
|
|
}
|
|
cmd := exec.CommandContext(ctx, p.Command, p.Args...)
|
|
|
|
cmdStr := cmd.String()
|
|
out, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
return "", cmdStr, errors.New(err.Error() + " | " + string(out))
|
|
}
|
|
return string(out), cmdStr, nil
|
|
}
|
|
|
|
func ExecuteCLIPipeLine(ctx context.Context, pipes []Pipe) (string, string, error) {
|
|
|
|
pipeLen := len(pipes)
|
|
if pipeLen == 0 {
|
|
return "", "", errors.New("failed to execute cli command: no pipes provided")
|
|
}
|
|
argLen := 0
|
|
for _, p := range pipes {
|
|
if !p.IsSanatory() {
|
|
return "", "", errors.New("failed to execute cli command: " + p.Command + " " + strings.Join(p.Args, " ") + " some arguments are not allowed")
|
|
}
|
|
argLen += len(p.Args) + 1
|
|
}
|
|
|
|
command := "/bin/sh"
|
|
var args []string = []string{}
|
|
args = append(args, "-c")
|
|
pipeLen -= 1
|
|
|
|
cmdArg := ""
|
|
|
|
for i, p := range pipes {
|
|
cmdArg += p.Command + " " + strings.Join(p.Args, " ")
|
|
if i != pipeLen {
|
|
if p.SuffixSymbol != "" {
|
|
cmdArg += " " + p.SuffixSymbol + " "
|
|
} else {
|
|
cmdArg += " | "
|
|
}
|
|
}
|
|
}
|
|
|
|
args = append(args, cmdArg)
|
|
|
|
return execNoCheck(ctx, command, args)
|
|
}
|