2023-08-26 11:27:38 +01:00
package cmd
import (
"context"
"log/slog"
"os"
2023-08-28 11:35:57 +02:00
"runtime"
2023-08-26 16:43:04 +01:00
"strings"
2023-08-26 11:27:38 +01:00
"github.com/AlexSSD7/linsk/vm"
"github.com/spf13/cobra"
"golang.org/x/crypto/ssh"
"golang.org/x/term"
)
var shellCmd = & cobra . Command {
Use : "shell" ,
// TODO: Fill this
// Short: "",
Args : cobra . RangeArgs ( 0 , 1 ) ,
2023-08-27 15:30:51 +01:00
Run : func ( cmd * cobra . Command , args [ ] string ) {
2023-08-26 11:27:38 +01:00
var passthroughArg string
if len ( args ) > 0 {
passthroughArg = args [ 0 ]
}
2023-08-27 13:44:57 +01:00
var forwardPortRules [ ] vm . PortForwardingRule
2023-08-26 16:43:04 +01:00
for i , fp := range strings . Split ( forwardPortsFlagStr , "," ) {
if fp == "" {
continue
}
2023-08-27 13:44:57 +01:00
fpr , err := vm . ParsePortForwardString ( fp )
2023-08-26 16:43:04 +01:00
if err != nil {
slog . Error ( "Failed to parse port forward string" , "index" , i , "value" , fp , "error" , err )
os . Exit ( 1 )
}
2023-08-27 13:44:57 +01:00
forwardPortRules = append ( forwardPortRules , fpr )
2023-08-26 16:43:04 +01:00
}
2023-08-27 13:44:57 +01:00
os . Exit ( runVM ( passthroughArg , func ( ctx context . Context , i * vm . VM , fm * vm . FileManager ) int {
2023-08-26 11:27:38 +01:00
sc , err := i . DialSSH ( )
if err != nil {
slog . Error ( "Failed to dial VM SSH" , "error" , err )
return 1
}
defer func ( ) { _ = sc . Close ( ) } ( )
sess , err := sc . NewSession ( )
if err != nil {
slog . Error ( "Failed to create new VM SSH session" , "error" , err )
return 1
}
defer func ( ) { _ = sess . Close ( ) } ( )
termFD := int ( os . Stdin . Fd ( ) )
termState , err := term . MakeRaw ( termFD )
if err != nil {
slog . Error ( "Failed to make raw terminal" , "error" , err )
return 1
}
defer func ( ) {
err := term . Restore ( termFD , termState )
if err != nil {
slog . Error ( "Failed to restore terminal" , "error" , err )
}
} ( )
2023-08-28 11:35:57 +02:00
termFDGetSize := termFD
if runtime . GOOS == "windows" {
// Another Windows workaround :/
termFDGetSize = int ( os . Stdout . Fd ( ) )
}
termWidth , termHeight , err := term . GetSize ( termFDGetSize )
2023-08-26 11:27:38 +01:00
if err != nil {
slog . Error ( "Failed to get terminal size" , "error" , err )
return 1
}
termModes := ssh . TerminalModes {
ssh . ECHO : 1 ,
ssh . TTY_OP_ISPEED : 14400 ,
ssh . TTY_OP_OSPEED : 14400 ,
}
term := os . Getenv ( "TERM" )
if term == "" {
term = "xterm-256color"
}
err = sess . RequestPty ( term , termHeight , termWidth , termModes )
if err != nil {
slog . Error ( "Failed to request VM SSH pty" , "error" , err )
return 1
}
sess . Stdin = os . Stdin
sess . Stdout = os . Stdout
sess . Stderr = os . Stderr
err = sess . Shell ( )
if err != nil {
slog . Error ( "Start VM SSH shell" , "error" , err )
return 1
}
doneCh := make ( chan struct { } , 1 )
go func ( ) {
err = sess . Wait ( )
if err != nil {
slog . Error ( "Failed to wait for VM SSH session to finish" , "error" , err )
}
doneCh <- struct { } { }
} ( )
select {
case <- ctx . Done ( ) :
case <- doneCh :
}
return 0
2023-08-27 13:44:57 +01:00
} , forwardPortRules , unrestrictedNetworkingFlag ) )
2023-08-26 11:27:38 +01:00
} ,
}
2023-08-26 11:57:12 +01:00
var forwardPortsFlagStr string
2023-08-26 16:43:04 +01:00
var unrestrictedNetworkingFlag bool
2023-08-26 11:57:12 +01:00
func init ( ) {
2023-08-26 16:43:04 +01:00
shellCmd . Flags ( ) . BoolVar ( & unrestrictedNetworkingFlag , "unsafe-unrestricted-networking" , false , "(UNSAFE) Enable unrestricted networking. This will allow the VM to connect to the internet." )
2023-08-26 11:57:12 +01:00
shellCmd . Flags ( ) . StringVar ( & forwardPortsFlagStr , "forward-ports" , "" , "Extra TCP port forwarding rules. Syntax: '<HOST PORT>:<VM PORT>' OR '<HOST BIND IP>:<HOST PORT>:<VM PORT>'. Multiple rules split by comma are accepted." )
}