CreateCommand processes raw command input with OpenSSH-like behavior. If the script provided is empty, it will default to the users shell. This injects environment variables specified by the user at launch too. The final argument is an interface that allows the caller to provide alternative implemen
(ctx context.Context, script string, env []string, ei usershell.EnvInfoer)
| 960 | // This is useful when creating a command to be run in a separate environment |
| 961 | // (for example, a Docker container). Pass in nil to use the default. |
| 962 | func (s *Server) CreateCommand(ctx context.Context, script string, env []string, ei usershell.EnvInfoer) (*pty.Cmd, error) { |
| 963 | if ei == nil { |
| 964 | ei = &usershell.SystemEnvInfo{} |
| 965 | } |
| 966 | |
| 967 | shell, dir, env, err := s.CommandEnv(ei, env) |
| 968 | if err != nil { |
| 969 | return nil, xerrors.Errorf("prepare command env: %w", err) |
| 970 | } |
| 971 | |
| 972 | // OpenSSH executes all commands with the users current shell. |
| 973 | // We replicate that behavior for IDE support. |
| 974 | caller := "-c" |
| 975 | if runtime.GOOS == "windows" { |
| 976 | caller = "/c" |
| 977 | } |
| 978 | name := shell |
| 979 | args := []string{caller, script} |
| 980 | |
| 981 | // A preceding space is generally not idiomatic for a shebang, |
| 982 | // but in Terraform it's quite standard to use <<EOF for a multi-line |
| 983 | // string which would indent with spaces, so we accept it for user-ease. |
| 984 | if strings.HasPrefix(strings.TrimSpace(script), "#!") { |
| 985 | // If the script starts with a shebang, we should |
| 986 | // execute it directly. This is useful for running |
| 987 | // scripts that aren't executable. |
| 988 | shebang := strings.SplitN(strings.TrimSpace(script), "\n", 2)[0] |
| 989 | shebang = strings.TrimSpace(shebang) |
| 990 | shebang = strings.TrimPrefix(shebang, "#!") |
| 991 | words, err := shellquote.Split(shebang) |
| 992 | if err != nil { |
| 993 | return nil, xerrors.Errorf("split shebang: %w", err) |
| 994 | } |
| 995 | name = words[0] |
| 996 | if len(words) > 1 { |
| 997 | args = words[1:] |
| 998 | } else { |
| 999 | args = []string{} |
| 1000 | } |
| 1001 | args = append(args, caller, script) |
| 1002 | } |
| 1003 | |
| 1004 | // gliderlabs/ssh returns a command slice of zero |
| 1005 | // when a shell is requested. |
| 1006 | if len(script) == 0 { |
| 1007 | args = []string{} |
| 1008 | if runtime.GOOS != "windows" { |
| 1009 | // On Linux and macOS, we should start a login |
| 1010 | // shell to consume juicy environment variables! |
| 1011 | args = append(args, "-l") |
| 1012 | } |
| 1013 | } |
| 1014 | |
| 1015 | // Modify command prior to execution. This will usually be a no-op, but not |
| 1016 | // always. For example, to run a command in a Docker container, we need to |
| 1017 | // modify the command to be `docker exec -it <container> <command>`. |
| 1018 | modifiedName, modifiedArgs := ei.ModifyCommand(name, args...) |
| 1019 | // Log if the command was modified. |