diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 0d48f9b..0000000 --- a/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -# IDE -.vscode -.idea -.fleet - -# Build Directory -build diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..d7b24a1 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/shell.iml b/.idea/shell.iml new file mode 100644 index 0000000..5e764c4 --- /dev/null +++ b/.idea/shell.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file 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/go.mod b/go.mod index 37646fa..39d0fc5 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,5 @@ module git.shadowhosting.xyz/Eggactyl/shell -go 1.22.5 +go 1.22.4 require golang.org/x/sys v0.22.0 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 deleted file mode 100644 index 2e7950c..0000000 --- a/linux/interface.go +++ /dev/null @@ -1,36 +0,0 @@ -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 -} - -type CommandOptions struct { - Env map[string]string - Sources []string - Command string - Args []string - CustomErrors map[int8]error - 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") - ErrInvalidHandler = errors.New("invalid handler") - ErrRunningEvt = errors.New("error running event") -) diff --git a/linux/run.go b/linux/run.go deleted file mode 100644 index ad1c76d..0000000 --- a/linux/run.go +++ /dev/null @@ -1,235 +0,0 @@ -package linux - -import ( - "bufio" - "errors" - "fmt" - "golang.org/x/sys/unix" - "os" - "os/exec" - "os/signal" - "strings" - "syscall" -) - -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, - handlers: make(map[int]interface{}), - }, 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 { - - isCommandExecutable, err := cmd.isCommandExecutable(cmd.Options.Command) - if err != nil { - return err - } - - if !isCommandExecutable { - 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) { - 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 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 deleted file mode 100644 index 5d21199..0000000 --- a/linux/utils.go +++ /dev/null @@ -1,75 +0,0 @@ -package linux - -import ( - "errors" - "fmt" - cmd2 "git.shadowhosting.xyz/Eggactyl/shell/cmd" - "golang.org/x/sys/unix" - "io/fs" - "os" - "os/exec" -) - -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 len(whichOut) == 0 { - return false, nil - } - - if err := unix.Access(whichOut, 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 new file mode 100644 index 0000000..31c1b00 --- /dev/null +++ b/main.go @@ -0,0 +1,177 @@ +package shell + +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 + +}