waveterm/waveshell/main-waveshell.go
Sylvie Crowe 167277ec11
Rename Waveshell First Pass (#632)
This begins the process of renaming mshell to waveshell everywhere by
making the most simple changes. There will need to be additional changes
in the future, but the hope is to merge simple changes in now to reduce
the number of future merge conflicts.
2024-05-02 14:16:00 -07:00

153 lines
4.2 KiB
Go

// Copyright 2023, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
package main
import (
"fmt"
"os"
"strings"
"syscall"
"time"
"github.com/wavetermdev/waveterm/waveshell/pkg/base"
"github.com/wavetermdev/waveterm/waveshell/pkg/packet"
"github.com/wavetermdev/waveterm/waveshell/pkg/server"
"github.com/wavetermdev/waveterm/waveshell/pkg/shexec"
"github.com/wavetermdev/waveterm/waveshell/pkg/wlog"
)
// this is set from build/linker flags
var BuildTime = "0"
func readFullRunPacket(packetParser *packet.PacketParser) (*packet.RunPacketType, error) {
rpb := packet.MakeRunPacketBuilder()
for pk := range packetParser.MainCh {
ok, runPacket := rpb.ProcessPacket(pk)
if runPacket != nil {
return runPacket, nil
}
if !ok {
return nil, fmt.Errorf("invalid packet '%s' sent to waveshell", pk.GetType())
}
}
return nil, fmt.Errorf("no run packet received")
}
func handleSingle() {
packetParser := packet.MakePacketParser(os.Stdin, nil)
sender := packet.MakePacketSender(os.Stdout, nil)
defer func() {
sender.Close()
sender.WaitForDone()
}()
wlog.LogConsumer = sender.SendLogPacket
initPacket := shexec.MakeInitPacket()
sender.SendPacket(initPacket)
if len(os.Args) >= 3 && os.Args[2] == "--version" {
return
}
runPacket, err := readFullRunPacket(packetParser)
if err != nil {
sender.SendErrorResponse(runPacket.ReqId, err)
return
}
err = shexec.ValidateRunPacket(runPacket)
if err != nil {
sender.SendErrorResponse(runPacket.ReqId, err)
return
}
err = runPacket.CK.Validate("run packet")
if err != nil {
sender.SendErrorResponse(runPacket.ReqId, fmt.Errorf("run packets from server must have a CK: %v", err))
}
if runPacket.Detached {
sender.SendErrorResponse(runPacket.ReqId, fmt.Errorf("detached mode not supported"))
return
} else {
shexec.IgnoreSigPipe()
ticker := time.NewTicker(1 * time.Minute)
go func() {
for range ticker.C {
// this will let the command detect when the server has gone away
// that will then trigger cmd.SendHup() to send SIGHUP to the exec'ed process
sender.SendPacket(packet.MakePingPacket())
}
}()
defer ticker.Stop()
cmd, err := shexec.RunCommandSimple(runPacket, sender, true)
if err != nil {
sender.SendErrorResponse(runPacket.ReqId, fmt.Errorf("error running command: %w", err))
return
}
defer cmd.Close()
startPacket := cmd.MakeCmdStartPacket(runPacket.ReqId)
sender.SendPacket(startPacket)
go func() {
exitErr := sender.WaitForDone()
if exitErr != nil {
base.Logf("I/O error talking to server, sending SIGHUP to children\n")
cmd.SendSignal(syscall.SIGHUP)
}
}()
cmd.RunRemoteIOAndWait(packetParser, sender)
return
}
}
func handleUsage() {
usage := `
waveshell is a helper program for wave terminal. it is used to execute commands
Options:
--help - prints this message
--version - print version
--server - multiplexer to run multiple commands
--single - run a single command (connected to multiplexer)
--single --version - return an init packet with version info
waveshell does not open any external ports and does not require any additional permissions.
it communicates exclusively through stdin/stdout with an attached process
via a JSON packet format.
`
fmt.Printf("%s\n\n", strings.TrimSpace(usage))
}
func main() {
base.SetBuildTime(BuildTime)
if len(os.Args) == 1 {
handleUsage()
return
}
firstArg := os.Args[1]
if firstArg == "--help" {
handleUsage()
return
} else if firstArg == "--version" {
fmt.Printf("waveshell %s+%s\n", base.WaveshellVersion, base.BuildTime)
return
} else if firstArg == "--single" || firstArg == "--single-from-server" {
base.ProcessType = base.ProcessType_WaveShellSingle
wlog.GlobalSubsystem = base.ProcessType_WaveShellSingle
base.InitDebugLog("single")
handleSingle()
return
} else if firstArg == "--server" {
base.ProcessType = base.ProcessType_WaveShellServer
wlog.GlobalSubsystem = base.ProcessType_WaveShellServer
base.InitDebugLog("server")
base.EnsureRcFilesDir()
rtnCode, err := server.RunServer()
if err != nil {
fmt.Fprintf(os.Stderr, "[error] %v\n", err)
}
if rtnCode != 0 {
os.Exit(rtnCode)
}
return
} else {
handleUsage()
return
}
}