command/command.go (view raw)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | package command import ( "errors" "fmt" "io" "os" "os/exec" "strings" "time" "alin.ovh/erl/output" ) type Command interface { Start() error Wait() error Stop() error } type Cmd struct { *exec.Cmd name string args []string out output.Output opts Options } type Options struct { Output io.Writer Stdout io.Writer Stderr io.Writer } const timeout = 1 * time.Second func New(name string, args []string, options Options) *Cmd { if options.Stdout == nil { options.Stdout = os.Stdout } if options.Stderr == nil { options.Stderr = os.Stderr } if options.Output == nil { options.Output = os.Stderr } return &Cmd{ name: name, args: args, opts: options, out: output.New(options.Output), } } func (cmd *Cmd) Stop() error { if cmd.Cmd != nil && cmd.Process != nil && cmd.ProcessState == nil { err := cmd.Process.Signal(os.Interrupt) if err != nil { return fmt.Errorf("error killing command: %v", err) } if cmd.ProcessState == nil { cmd.out.Info("[command not stopped, waiting %s seconds before killing]\n", timeout) time.Sleep(timeout) if cmd.ProcessState == nil { cmd.out.Info("[command not stopped, killing]\n") err := cmd.Process.Kill() if err != nil { return fmt.Errorf("error killing command: %v", err) } } } } return nil } func (cmd *Cmd) Exited() bool { return cmd.Cmd != nil && cmd.ProcessState != nil && cmd.ProcessState.Exited() } func (cmd *Cmd) Wait() error { err := cmd.Cmd.Wait() if err != nil { var exitErr *exec.ExitError if errors.As(err, &exitErr) { if cmd.Exited() { cmd.out.Fail("[command exited with code %d]\n", cmd.ProcessState.ExitCode()) } else { cmd.out.Fail("[command stopped]\n") } } else { return fmt.Errorf("error waiting for command: %v", err) } } if cmd.Exited() && cmd.ProcessState.Success() { cmd.out.Success("[command finished]\n") } return nil } func (cmd *Cmd) Start() error { cmd.makeCommand() cmd.out.Success("[running: %s %s]\n", cmd.name, strings.Join(cmd.args, " ")) err := cmd.Cmd.Start() if err != nil { return fmt.Errorf("error starting command: %v", err) } return err } func (cmd *Cmd) makeCommand() { cmd.Cmd = exec.Command(cmd.name, cmd.args...) cmd.Stdout = cmd.opts.Stdout cmd.Stderr = cmd.opts.Stderr } |