diff --git a/linux/interface.go b/linux/interface.go new file mode 100644 index 0000000..384ef94 --- /dev/null +++ b/linux/interface.go @@ -0,0 +1,26 @@ +package linux + +import ( + "errors" +) + +type LinuxCommand struct { + Options CommandOptions +} + +type CommandOptions struct { + Env map[string]string + Sources map[string]string + Command string + Args []string + Cwd string + Shell string +} + +// Errors +var ( + ErrFetchingCwd = errors.New("error fetching cwd") + ErrRunningCmd = errors.New("error running command") + ErrCommandNotFound = errors.New("error command not found") + ErrCommandNotExecutable = errors.New("error command not executable") +) diff --git a/linux/run.go b/linux/run.go new file mode 100644 index 0000000..8009d16 --- /dev/null +++ b/linux/run.go @@ -0,0 +1,76 @@ +package linux + +import ( + "errors" + "fmt" + "os" + "os/exec" +) + +func NewCommand(options CommandOptions) (*LinuxCommand, error) { + + if len(options.Shell) == 0 { + options.Shell = "/bin/bash" + } + + if len(options.Cwd) == 0 { + cwd, err := os.Getwd() + if err != nil { + return nil, ErrFetchingCwd + } + options.Cwd = cwd + } + + return &LinuxCommand{ + Options: options, + }, nil + +} + +func (cmd *LinuxCommand) Run() error { + + command := exec.Command(cmd.Options.Shell, "-c", cmd.Options.Command) + command.Dir = cmd.Options.Cwd + command.Args = append(command.Args, cmd.Options.Args...) + + // Loop through env to format and add them to the command. + for key, value := range cmd.Options.Env { + command.Env = append(command.Env, fmt.Sprintf("%s=%s", key, value)) + } + + isCommandExecutable, err := cmd.isCommandExecutable(cmd.Options.Command) + if err != nil { + return err + } + + if !isCommandExecutable { + return ErrCommandNotExecutable + } + + if err := command.Start(); err != nil { + var exitErr *exec.ExitError + if errors.As(err, &exitErr) { + fmt.Println(exitErr.ExitCode()) + if exitErr.ExitCode() == 127 { + return ErrCommandNotFound + } else { + return fmt.Errorf("%s: %w", ErrRunningCmd.Error(), err) + } + } + } + + if err := command.Wait(); err != nil { + var exitErr *exec.ExitError + if errors.As(err, &exitErr) { + fmt.Println(exitErr.ExitCode()) + if exitErr.ExitCode() == 127 { + return ErrCommandNotFound + } else { + return fmt.Errorf("%s: %w", ErrRunningCmd.Error(), err) + } + } + } + + return nil + +} diff --git a/linux/utils.go b/linux/utils.go new file mode 100644 index 0000000..ba6a1d5 --- /dev/null +++ b/linux/utils.go @@ -0,0 +1,59 @@ +package linux + +import ( + "errors" + "fmt" + "golang.org/x/sys/unix" + "io/fs" + "os" + "os/exec" +) + +func (cmd *LinuxCommand) isCommandExecutable(command string) (bool, error) { + + if _, err := os.Stat(command); errors.Is(err, fs.ErrNotExist) { + return false, err + } + + if err := unix.Access(command, unix.X_OK); err != nil { + if err == unix.EACCES { + return false, nil + } else { + fmt.Println(err) + return false, err + } + } + + return true, nil + +} + +func (cmd *LinuxCommand) doesCommandExist(command string) (bool, error) { + + shellCmd := exec.Command(cmd.Options.Shell, "-c", fmt.Sprintf("command -v %s", command)) + + if err := shellCmd.Start(); err != nil { + var exitErr *exec.ExitError + if errors.As(err, &exitErr) { + if exitErr.ExitCode() == 1 { + return false, nil + } else { + return false, ErrRunningCmd + } + } + } + + if err := shellCmd.Wait(); err != nil { + var exitErr *exec.ExitError + if errors.As(err, &exitErr) { + if exitErr.ExitCode() == 1 { + return false, nil + } else { + return false, ErrRunningCmd + } + } + } + + return true, nil + +} diff --git a/main.go b/main.go index 31c1b00..e5b9d97 100644 --- a/main.go +++ b/main.go @@ -1,4 +1,4 @@ -package shell +package main import ( "bytes"