mirror of
https://github.com/clearlinux/clr-installer.git
synced 2026-08-23 16:25:54 +00:00
59302346d6
Signed-off-by: Mark D Horn <mark.d.horn@intel.com>
195 lines
4.3 KiB
Go
195 lines
4.3 KiB
Go
// Copyright © 2020 Intel Corporation
|
|
//
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
package cmd
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
|
|
"github.com/clearlinux/clr-installer/log"
|
|
"github.com/clearlinux/clr-installer/proxy"
|
|
)
|
|
|
|
// Output interface allows implementors to process the output from a
|
|
// command according to their specific case
|
|
type Output interface {
|
|
Process(printPrefix, line string)
|
|
}
|
|
|
|
type runLogger struct{}
|
|
|
|
func (rl runLogger) Write(p []byte) (n int, err error) {
|
|
for _, curr := range strings.Split(string(p), "\n") {
|
|
if curr == "" {
|
|
continue
|
|
}
|
|
|
|
log.Debug(curr)
|
|
}
|
|
return len(p), nil
|
|
}
|
|
|
|
// RunAndLog executes a command (similar to Run) but takes care of writing
|
|
// the output to default logger
|
|
func RunAndLog(args ...string) error {
|
|
return Run(runLogger{}, args...)
|
|
}
|
|
|
|
// RunAndLogWithEnv does the same as RunAndLog but it changes the execution's environment
|
|
// variables adding the provided ones by the env argument
|
|
func RunAndLogWithEnv(env map[string]string, args ...string) error {
|
|
return run(nil, runLogger{}, env, args...)
|
|
}
|
|
|
|
// PipeRunAndLog is similar to RunAndLog runs a command and writes the output
|
|
// to default logger and also writes in to the process stdin
|
|
func PipeRunAndLog(in string, args ...string) error {
|
|
return run(func(cmd *exec.Cmd) error {
|
|
stdin, err := cmd.StdinPipe()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
go func() {
|
|
defer func() {
|
|
_ = stdin.Close()
|
|
}()
|
|
|
|
_, _ = io.WriteString(stdin, in)
|
|
}()
|
|
|
|
return nil
|
|
}, runLogger{}, nil, args...)
|
|
}
|
|
|
|
// PipeRunAndPipeOut is similar to PipeRunAndLog but runs a command by feeding
|
|
// a string to stdin of Cmd and output is written to a byte buffer instead of a log
|
|
func PipeRunAndPipeOut(in string, out *bytes.Buffer, args ...string) error {
|
|
return run(func(cmd *exec.Cmd) error {
|
|
stdin, err := cmd.StdinPipe()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
go func() {
|
|
defer func() {
|
|
_ = stdin.Close()
|
|
}()
|
|
|
|
_, _ = io.WriteString(stdin, in)
|
|
}()
|
|
|
|
return nil
|
|
}, out, nil, args...)
|
|
}
|
|
|
|
func run(sw func(cmd *exec.Cmd) error, writer io.Writer, env map[string]string, args ...string) error {
|
|
var exe string
|
|
var cmdArgs []string
|
|
|
|
log.Debug("%s", strings.Join(args, " "))
|
|
|
|
exe = args[0]
|
|
cmdArgs = args[1:]
|
|
|
|
cmd := exec.Command(exe, cmdArgs...)
|
|
|
|
// Add any proxy environment variables
|
|
for _, pvar := range proxy.GetProxyValues() {
|
|
cmd.Env = append(cmd.Env, pvar)
|
|
}
|
|
log.Debug("cmd.Env: %+v", cmd.Env)
|
|
|
|
if sw != nil {
|
|
if err := sw(cmd); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
cmd.Stdout = writer
|
|
cmd.Stderr = writer
|
|
|
|
if cmd.Stdin == nil {
|
|
cmd.Stdin = os.Stdin
|
|
}
|
|
|
|
for k, v := range env {
|
|
curr := fmt.Sprintf("%s=%s", k, v)
|
|
cmd.Args = append(cmd.Args, curr)
|
|
cmd.Env = append(cmd.Env, curr)
|
|
}
|
|
|
|
err := cmd.Run()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Run executes a command and uses writer to write both stdout and stderr
|
|
// args are the actual command and its arguments
|
|
func Run(writer io.Writer, args ...string) error {
|
|
return run(nil, writer, nil, args...)
|
|
}
|
|
|
|
// RunAndProcessOutput executes a command and process the output from
|
|
// Stdout and Stderr according to the implementor
|
|
// args are the actual command and its arguments
|
|
func RunAndProcessOutput(printPrefix string, output Output, args ...string) error {
|
|
var exe string
|
|
var cmdArgs []string
|
|
|
|
log.Debug(strings.Join(args, " "))
|
|
|
|
exe = args[0]
|
|
cmdArgs = args[1:]
|
|
|
|
cmd := exec.Command(exe, cmdArgs...)
|
|
|
|
// Add any proxy environment variables
|
|
for _, pvar := range proxy.GetProxyValues() {
|
|
cmd.Env = append(cmd.Env, pvar)
|
|
}
|
|
log.Debug("cmd.Env: %+v", cmd.Env)
|
|
|
|
stdout, err := cmd.StdoutPipe()
|
|
if err != nil {
|
|
log.Error("Could not connect a pipe to Stdout")
|
|
return err
|
|
}
|
|
|
|
// run the command but don't wait for it to finish
|
|
if err := cmd.Start(); err != nil {
|
|
log.Error("Failed to start command execution: %s", exe)
|
|
return err
|
|
}
|
|
|
|
// start scanning stdout for messages
|
|
scannerOut := bufio.NewScanner(stdout)
|
|
for scannerOut.Scan() {
|
|
// specific processing implementation
|
|
output.Process(printPrefix, scannerOut.Text())
|
|
}
|
|
|
|
if err := scannerOut.Err(); err != nil {
|
|
log.Error("An error occurred while reading stdout")
|
|
return err
|
|
}
|
|
|
|
// wait for the command to finish running
|
|
if err := cmd.Wait(); err != nil {
|
|
log.Error("An error occurred executing command: \"%s\". Error: %s", strings.Join(args, " "), err)
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|