diff --git a/cmd/command.go b/cmd/command.go deleted file mode 100644 index 24c2920..0000000 --- a/cmd/command.go +++ /dev/null @@ -1,28 +0,0 @@ -package cmd - -import ( - "errors" - "fmt" - "os/exec" - "strings" -) - -func Command(cmd string) (string, error) { - - command := exec.Command("command", "-v", cmd) - - outputBytes, err := command.Output() - if err != nil { - var exitErr *exec.ExitError - if errors.As(err, &exitErr) { - if exitErr.ExitCode() == 1 { - return "", ErrNotFound - } else { - return "", fmt.Errorf("command error: %w", err) - } - } - } - - return strings.Trim(string(outputBytes), "\n"), nil - -} diff --git a/cmd/node.go b/cmd/node.go deleted file mode 100644 index 32bc6f2..0000000 --- a/cmd/node.go +++ /dev/null @@ -1,38 +0,0 @@ -package cmd - -import ( - "errors" - "fmt" - "os/exec" - "strings" -) - -var NodeNotFound = errors.New("nodejs not found") - -func Node(options BasicOptions, args ...string) (output string, err error) { - - if _, err := Which("node", BasicOptions{ - Env: options.Env, - Sources: options.Sources, - Cwd: options.Cwd, - }); err != nil { - if errors.Is(err, ErrNotFound) { - return "", NodeNotFound - } else { - return "", err - } - } - - command := exec.Command("node", args...) - - outputBytes, err := command.Output() - if err != nil { - var exitErr *exec.ExitError - if errors.As(err, &exitErr) { - return "", fmt.Errorf("command error: %w", err) - } - } - - return strings.Trim(string(outputBytes), "\n"), nil - -} diff --git a/cmd/pip.go b/cmd/pip.go deleted file mode 100644 index 974b3ee..0000000 --- a/cmd/pip.go +++ /dev/null @@ -1,37 +0,0 @@ -package cmd - -import ( - "errors" - "fmt" - "os/exec" - "strings" -) - -func Pip(options BasicOptions, args ...string) (output string, err error) { - - if _, err := Which("python3", BasicOptions{ - Env: options.Env, - Sources: options.Sources, - Cwd: options.Cwd, - }); err != nil { - if errors.Is(err, ErrNotFound) { - return "", PythonNotFound - } else { - return "", err - } - } - - command := exec.Command("python3", "-m", "pip") - command.Args = append(command.Args, args...) - - outputBytes, err := command.Output() - if err != nil { - var exitErr *exec.ExitError - if errors.As(err, &exitErr) { - return "", fmt.Errorf("command error: %w", err) - } - } - - return strings.Trim(string(outputBytes), "\n"), nil - -} diff --git a/cmd/python.go b/cmd/python.go deleted file mode 100644 index 2945854..0000000 --- a/cmd/python.go +++ /dev/null @@ -1,38 +0,0 @@ -package cmd - -import ( - "errors" - "fmt" - "os/exec" - "strings" -) - -var PythonNotFound = errors.New("python not found") - -func Python(options BasicOptions, args ...string) (output string, err error) { - - if _, err := Which("python3", BasicOptions{ - Env: options.Env, - Sources: options.Sources, - Cwd: options.Cwd, - }); err != nil { - if errors.Is(err, ErrNotFound) { - return "", PythonNotFound - } else { - return "", err - } - } - - command := exec.Command("python3", args...) - - outputBytes, err := command.Output() - if err != nil { - var exitErr *exec.ExitError - if errors.As(err, &exitErr) { - return "", fmt.Errorf("command error: %w", err) - } - } - - return strings.Trim(string(outputBytes), "\n"), nil - -} diff --git a/cmd/which.go b/cmd/which.go deleted file mode 100644 index 168abdd..0000000 --- a/cmd/which.go +++ /dev/null @@ -1,49 +0,0 @@ -package cmd - -import ( - "errors" - "fmt" - "os/exec" - "strings" -) - -var ErrNotFound = errors.New("which: command not found") - -type BasicOptions struct { - Env map[string]string - Sources []string - Cwd string -} - -func Which(cmd string, options BasicOptions) (dir string, err error) { - - var sourceCommand strings.Builder - for _, value := range options.Sources { - sourceCommand.WriteString(fmt.Sprintf("source %s && ", value)) - } - - command := exec.Command("which", cmd) - - if options.Cwd != "" { - command.Dir = options.Cwd - } - - for k, v := range options.Env { - command.Env = append(command.Env, fmt.Sprintf("%s=%s", k, v)) - } - - outputBytes, err := command.Output() - if err != nil { - var exitErr *exec.ExitError - if errors.As(err, &exitErr) { - if exitErr.ExitCode() == 1 { - return "", ErrNotFound - } else { - return "", fmt.Errorf("command error: %w", err) - } - } - } - - return strings.Trim(string(outputBytes), "\n"), nil - -} diff --git a/linux/events.go b/linux/events.go deleted file mode 100644 index f8168b5..0000000 --- a/linux/events.go +++ /dev/null @@ -1,17 +0,0 @@ -package linux - -const ( - EventOutput = iota - EventExit -) - -type EventOutputData struct { - Output string - CmdOptions CommandOptions -} - -type EventExitData struct { - HasSucceeded bool - ExitCode int - CmdOptions CommandOptions -} diff --git a/linux/interface.go b/linux/interface.go index 2e7950c..384ef94 100644 --- a/linux/interface.go +++ b/linux/interface.go @@ -2,27 +2,19 @@ package linux import ( "errors" - "io" - "sync" ) type LinuxCommand struct { - Options CommandOptions - handlers map[int]interface{} - wg sync.WaitGroup - stdout io.ReadCloser - stderr io.ReadCloser - stdin io.WriteCloser + Options CommandOptions } type CommandOptions struct { - Env map[string]string - Sources []string - Command string - Args []string - CustomErrors map[int8]error - Cwd string - Shell string + Env map[string]string + Sources map[string]string + Command string + Args []string + Cwd string + Shell string } // Errors @@ -31,6 +23,4 @@ var ( ErrRunningCmd = errors.New("error running command") ErrCommandNotFound = errors.New("error command not found") ErrCommandNotExecutable = errors.New("error command not executable") - ErrInvalidHandler = errors.New("invalid handler") - ErrRunningEvt = errors.New("error running event") ) diff --git a/linux/run.go b/linux/run.go index ad1c76d..8009d16 100644 --- a/linux/run.go +++ b/linux/run.go @@ -1,15 +1,10 @@ package linux import ( - "bufio" "errors" "fmt" - "golang.org/x/sys/unix" "os" "os/exec" - "os/signal" - "strings" - "syscall" ) func NewCommand(options CommandOptions) (*LinuxCommand, error) { @@ -27,30 +22,22 @@ func NewCommand(options CommandOptions) (*LinuxCommand, error) { } return &LinuxCommand{ - Options: options, - handlers: make(map[int]interface{}), + Options: options, }, nil } -func (cmd *LinuxCommand) AddHandler(handler interface{}) error { - - switch h := handler.(type) { - case func(data EventOutputData) error: - cmd.handlers[EventOutput] = h - break - case func(data EventExitData) error: - cmd.handlers[EventExit] = h - default: - return ErrInvalidHandler - } - - return 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 @@ -60,176 +47,30 @@ func (cmd *LinuxCommand) Run() error { return ErrCommandNotExecutable } - var sourceCommand strings.Builder - for _, value := range cmd.Options.Sources { - sourceCommand.WriteString(fmt.Sprintf("source %s && ", value)) - } - - var commandOptions strings.Builder - commandOptions.WriteString(" ") - for index, arg := range cmd.Options.Args { - if len(cmd.Options.Args)-1 == index { - commandOptions.WriteString(fmt.Sprintf("%s", arg)) - } else { - commandOptions.WriteString(fmt.Sprintf("%s ", arg)) - } - } - - command := exec.Command(cmd.Options.Shell, "-c", sourceCommand.String()+cmd.Options.Command+commandOptions.String()) - command.SysProcAttr = &unix.SysProcAttr{Setsid: true} - command.Dir = cmd.Options.Cwd - - for key, value := range cmd.Options.Env { - command.Env = append(command.Env, fmt.Sprintf("%s=%s", key, value)) - } - - var signalChannel chan os.Signal - signalChannel = make(chan os.Signal, 1) - signal.Notify(signalChannel, unix.SIGINT, unix.SIGTERM) - - if len(cmd.handlers) != 0 { - var err error - cmd.stdout, err = command.StdoutPipe() - if err != nil { - return err - } - - cmd.stdin, err = command.StdinPipe() - if err != nil { - return err - } - - cmd.stderr, err = command.StderrPipe() - if err != nil { - return err - } - } - 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 if _, ok := cmd.Options.CustomErrors[int8(exitErr.ExitCode())]; ok { - return cmd.Options.CustomErrors[int8(exitErr.ExitCode())] } else { return fmt.Errorf("%s: %w", ErrRunningCmd.Error(), err) } } } - if len(cmd.handlers) != 0 { - cmd.wg.Add(2) - - go func() { - defer cmd.wg.Done() - scanner := bufio.NewScanner(cmd.stderr) - for scanner.Scan() { - line := scanner.Text() - if h, ok := cmd.handlers[EventOutput]; ok { - if err := h.(func(data EventOutputData) error)(EventOutputData{ - Output: line, - CmdOptions: cmd.Options, - }); err != nil { - return - } - } - } - }() - - go func() { - defer cmd.wg.Done() - - scanner := bufio.NewScanner(cmd.stdout) - for scanner.Scan() { - line := scanner.Text() - if h, ok := cmd.handlers[EventOutput]; ok { - if err := h.(func(data EventOutputData) error)(EventOutputData{ - Output: line, - CmdOptions: cmd.Options, - }); err != nil { - return - } - } - } - }() - } - - cmd.wg.Add(1) - - go func() { - defer cmd.wg.Done() - - select { - case _, ok := <-signalChannel: - if !ok { - return - } - if err := unix.Kill(-command.Process.Pid, syscall.SIGINT); err != nil { - return - } - } - }() - - var exitInfo *EventExitData - - if _, ok := cmd.handlers[EventExit]; ok { - exitInfo = &EventExitData{ - HasSucceeded: true, - CmdOptions: cmd.Options, - } - } - if err := command.Wait(); err != nil { var exitErr *exec.ExitError - if errors.As(err, &exitErr) && exitErr.String() != "signal: interrupt" { + if errors.As(err, &exitErr) { + fmt.Println(exitErr.ExitCode()) if exitErr.ExitCode() == 127 { return ErrCommandNotFound - } else if _, ok := cmd.Options.CustomErrors[int8(exitErr.ExitCode())]; ok { - if h, ok := cmd.handlers[EventExit]; ok { - if exitInfo == nil { - return fmt.Errorf("%s: %w", ErrRunningCmd.Error(), err) - } - exitInfo.HasSucceeded = false - exitInfo.ExitCode = exitErr.ExitCode() - err := h.(func(data EventExitData) error)(*exitInfo) - if err != nil { - return fmt.Errorf("%s: %w", ErrRunningEvt.Error(), err) - } - } - return cmd.Options.CustomErrors[int8(exitErr.ExitCode())] } else { - if h, ok := cmd.handlers[EventExit]; ok { - if exitInfo == nil { - return fmt.Errorf("%s: %w", ErrRunningEvt.Error(), err) - } - exitInfo.HasSucceeded = false - exitInfo.ExitCode = exitErr.ExitCode() - err := h.(func(data EventExitData) error)(*exitInfo) - if err != nil { - return fmt.Errorf("%s: %w", ErrRunningEvt.Error(), err) - } - } return fmt.Errorf("%s: %w", ErrRunningCmd.Error(), err) } } } - if h, ok := cmd.handlers[EventExit]; ok { - if exitInfo == nil { - return nil - } - exitInfo.ExitCode = 0 - err := h.(func(data EventExitData) error)(*exitInfo) - if err != nil { - return fmt.Errorf("%s: %w", ErrRunningEvt.Error(), err) - } - } - - close(signalChannel) - signal.Stop(signalChannel) - cmd.wg.Wait() - return nil } diff --git a/linux/utils.go b/linux/utils.go index 5d21199..ba6a1d5 100644 --- a/linux/utils.go +++ b/linux/utils.go @@ -3,7 +3,6 @@ package linux import ( "errors" "fmt" - cmd2 "git.shadowhosting.xyz/Eggactyl/shell/cmd" "golang.org/x/sys/unix" "io/fs" "os" @@ -12,26 +11,11 @@ import ( func (cmd *LinuxCommand) isCommandExecutable(command string) (bool, error) { - whichOut, err := cmd2.Which(command, cmd2.BasicOptions{ - Env: cmd.Options.Env, - Sources: cmd.Options.Sources, - Cwd: cmd.Options.Cwd, - }) - if err != nil { - if errors.Is(err, cmd2.ErrNotFound) { - if _, err := os.Stat(command); errors.Is(err, fs.ErrNotExist) { - return false, err - } - } else { - return false, err - } + if _, err := os.Stat(command); errors.Is(err, fs.ErrNotExist) { + return false, err } - if len(whichOut) == 0 { - return false, nil - } - - if err := unix.Access(whichOut, unix.X_OK); err != nil { + if err := unix.Access(command, unix.X_OK); err != nil { if err == unix.EACCES { return false, nil } else { diff --git a/main.go b/main.go new file mode 100644 index 0000000..e5b9d97 --- /dev/null +++ b/main.go @@ -0,0 +1,177 @@ +package main + +import ( + "bytes" + "errors" + "fmt" + "io/fs" + "os" + "os/exec" + "strings" + + "golang.org/x/sys/unix" +) + +var LinuxType string + +var ErrCommandDoesNotExist = errors.New("command Doesn't Exist") +var ErrSourceDoesNotExist = errors.New("source Doesn't Exist") + +func RunCommand(command string, args ...string) error { + + if !DoesCommandExist(command) { + return ErrCommandDoesNotExist + } + + var stderr bytes.Buffer + + var mainCommand string + + mainCommand = "/bin/bash" + + bashCommand := exec.Command(mainCommand, "-c", command) + bashCommand.Args = append(bashCommand.Args, args...) + bashCommand.Stderr = &stderr + + if err := bashCommand.Start(); err != nil { + return errors.New(stderr.String() + " " + err.Error()) + } + + if err := bashCommand.Wait(); err != nil { + return errors.New(stderr.String() + " " + err.Error()) + } + + return nil + +} + +func RunCommandWithSource(source string, command string, args ...string) error { + + if !DoesSourceExist(source) { + return ErrSourceDoesNotExist + } + + if !DoesCommandExistWithSource(source, command) { + return ErrCommandDoesNotExist + } + + var stderr bytes.Buffer + + var mainCommand string + + mainCommand = "/bin/bash" + + bashCommand := exec.Command(mainCommand, "-c", fmt.Sprintf("source %s; %s", source, command)) + bashCommand.Args = append(bashCommand.Args, args...) + bashCommand.Stderr = &stderr + + if err := bashCommand.Start(); err != nil { + return errors.New(stderr.String()) + } + + if err := bashCommand.Wait(); err != nil { + return errors.New(stderr.String()) + } + + return nil + +} + +func DoesCommandExist(command string) bool { + + var stderr bytes.Buffer + + var mainCommand string + + mainCommand = "/bin/bash" + + bashCommand := exec.Command(mainCommand, "-c", fmt.Sprintf("command -v %s", command)) + bashCommand.Stderr = &stderr + + if err := bashCommand.Start(); err != nil { + if strings.Contains(err.Error(), "1") { + return false + } + } + + if err := bashCommand.Wait(); err != nil { + if strings.Contains(err.Error(), "1") { + return false + } + } + + return true + +} + +func DoesSourceExist(source string) bool { + + var stderr bytes.Buffer + + var mainCommand string + + mainCommand = "/bin/bash" + + bashCommand := exec.Command(mainCommand, "-c", fmt.Sprintf("source %s", source)) + bashCommand.Stderr = &stderr + + if err := bashCommand.Start(); err != nil { + if strings.Contains(err.Error(), "1") { + return false + } + } + + if err := bashCommand.Wait(); err != nil { + if strings.Contains(err.Error(), "1") { + return false + } + } + + return true + +} + +func DoesCommandExistWithSource(source string, command string) bool { + + if sourceCheck := DoesSourceExist(source); !sourceCheck { + return false + } + + var stderr bytes.Buffer + + var mainCommand string + + mainCommand = "/bin/bash" + + bashCommand := exec.Command(mainCommand, "-c", fmt.Sprintf("source %s && command -v %s", source, command)) + bashCommand.Stderr = &stderr + + if err := bashCommand.Start(); err != nil { + if strings.Contains(err.Error(), "1") { + return false + } + } + + if err := bashCommand.Wait(); err != nil { + if strings.Contains(err.Error(), "1") { + return false + } + } + + return true + +} + +func CanExecute(script string) bool { + + if _, err := os.Stat(script); errors.Is(err, fs.ErrNotExist) { + return false + } + + if err := unix.Access(script, unix.X_OK); err != nil { + return err == unix.EACCES + } + + return true + +}