2022-06-10 09:35:24 +02:00
|
|
|
// Copyright 2022 Dashborg Inc
|
|
|
|
//
|
|
|
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
|
|
|
|
|
|
package base
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"io/fs"
|
|
|
|
"os"
|
|
|
|
"path"
|
|
|
|
"path/filepath"
|
|
|
|
)
|
|
|
|
|
|
|
|
const ScRunnerVarName = "SCRIPTHAUS_RUNNER"
|
|
|
|
const ScHomeVarName = "SCRIPTHAUS_HOME"
|
|
|
|
const HomeVarName = "HOME"
|
|
|
|
const ScShell = "bash"
|
|
|
|
const SessionsDirBaseName = ".sessions"
|
|
|
|
const RunnerBaseName = "runner"
|
|
|
|
const SessionDBName = "session.db"
|
|
|
|
const ScReadyString = "scripthaus runner ready"
|
|
|
|
|
|
|
|
const OSCEscError = "error"
|
|
|
|
|
|
|
|
type CommandFileNames struct {
|
2022-06-11 06:37:21 +02:00
|
|
|
PtyOutFile string
|
|
|
|
StdinFifo string
|
|
|
|
RunnerOutFile string
|
2022-06-10 09:35:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func GetScHomeDir() (string, error) {
|
|
|
|
scHome := os.Getenv(ScHomeVarName)
|
|
|
|
if scHome == "" {
|
|
|
|
homeVar := os.Getenv(HomeVarName)
|
|
|
|
if homeVar == "" {
|
|
|
|
return "", fmt.Errorf("Cannot resolve scripthaus home directory (SCRIPTHAUS_HOME and HOME not set)")
|
|
|
|
}
|
|
|
|
scHome = path.Join(homeVar, "scripthaus")
|
|
|
|
}
|
|
|
|
return scHome, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetCommandFileNames(sessionId string, cmdId string) (*CommandFileNames, error) {
|
|
|
|
if sessionId == "" || cmdId == "" {
|
|
|
|
return nil, fmt.Errorf("cannot get command-files when sessionid or cmdid is empty")
|
|
|
|
}
|
|
|
|
sdir, err := EnsureSessionDir(sessionId)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
base := path.Join(sdir, cmdId)
|
|
|
|
return &CommandFileNames{
|
2022-06-11 06:37:21 +02:00
|
|
|
PtyOutFile: base + ".ptyout",
|
|
|
|
StdinFifo: base + ".stdin",
|
|
|
|
RunnerOutFile: base + ".runout",
|
2022-06-10 09:35:24 +02:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func CleanUpCmdFiles(sessionId string, cmdId string) error {
|
|
|
|
if cmdId == "" {
|
|
|
|
return fmt.Errorf("bad cmdid, cannot clean up")
|
|
|
|
}
|
|
|
|
sdir, err := EnsureSessionDir(sessionId)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
cmdFileGlob := path.Join(sdir, cmdId+".*")
|
|
|
|
matches, err := filepath.Glob(cmdFileGlob)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
for _, file := range matches {
|
|
|
|
rmErr := os.Remove(file)
|
|
|
|
if err == nil && rmErr != nil {
|
|
|
|
err = rmErr
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func EnsureSessionDir(sessionId string) (string, error) {
|
|
|
|
if sessionId == "" {
|
|
|
|
return "", fmt.Errorf("Bad sessionid, cannot be empty")
|
|
|
|
}
|
|
|
|
shhome, err := GetScHomeDir()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
sdir := path.Join(shhome, ".sessions", sessionId)
|
|
|
|
info, err := os.Stat(sdir)
|
|
|
|
if errors.Is(err, fs.ErrNotExist) {
|
|
|
|
err = os.MkdirAll(sdir, 0777)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
info, err = os.Stat(sdir)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
if !info.IsDir() {
|
|
|
|
return "", fmt.Errorf("session dir '%s' must be a directory", sdir)
|
|
|
|
}
|
|
|
|
return sdir, nil
|
|
|
|
}
|
|
|
|
|
2022-06-11 06:37:21 +02:00
|
|
|
func GetScRunnerPath() (string, error) {
|
2022-06-10 09:35:24 +02:00
|
|
|
runnerPath := os.Getenv(ScRunnerVarName)
|
|
|
|
if runnerPath != "" {
|
2022-06-11 06:37:21 +02:00
|
|
|
return runnerPath, nil
|
2022-06-10 09:35:24 +02:00
|
|
|
}
|
|
|
|
scHome, err := GetScHomeDir()
|
|
|
|
if err != nil {
|
2022-06-11 06:37:21 +02:00
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return path.Join(scHome, RunnerBaseName), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func EnsureRunnerPath() error {
|
|
|
|
runnerPath, err := GetScRunnerPath()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2022-06-10 09:35:24 +02:00
|
|
|
}
|
2022-06-11 06:37:21 +02:00
|
|
|
info, err := os.Stat(runnerPath)
|
|
|
|
if err != nil {
|
|
|
|
if errors.Is(err, fs.ErrNotExist) {
|
|
|
|
return fmt.Errorf("cannot find scripthaus runner at path '%s'", runnerPath)
|
|
|
|
}
|
|
|
|
return fmt.Errorf("error stating scripthaus runner at path '%s'", runnerPath)
|
|
|
|
}
|
|
|
|
if info.Mode()&0100 == 0 {
|
|
|
|
return fmt.Errorf("scripthaus runner at path '%s' is not executable mode=%#o", runnerPath, info.Mode())
|
|
|
|
}
|
|
|
|
return nil
|
2022-06-10 09:35:24 +02:00
|
|
|
}
|
|
|
|
|
2022-06-11 06:37:21 +02:00
|
|
|
func GetScSessionsDir() (string, error) {
|
2022-06-10 09:35:24 +02:00
|
|
|
scHome, err := GetScHomeDir()
|
|
|
|
if err != nil {
|
2022-06-11 06:37:21 +02:00
|
|
|
return "", err
|
2022-06-10 09:35:24 +02:00
|
|
|
}
|
2022-06-11 06:37:21 +02:00
|
|
|
return path.Join(scHome, SessionsDirBaseName), nil
|
2022-06-10 09:35:24 +02:00
|
|
|
}
|
|
|
|
|
2022-06-11 06:37:21 +02:00
|
|
|
func GetSessionDBName(sessionId string) (string, error) {
|
2022-06-10 09:35:24 +02:00
|
|
|
scHome, err := GetScHomeDir()
|
|
|
|
if err != nil {
|
2022-06-11 06:37:21 +02:00
|
|
|
return "", err
|
2022-06-10 09:35:24 +02:00
|
|
|
}
|
2022-06-11 06:37:21 +02:00
|
|
|
return path.Join(scHome, SessionDBName), nil
|
2022-06-10 09:35:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// SH OSC Escapes (code 198, S=19, H=8)
|
|
|
|
// \e]198;cmdid;(cmd-id)BEL - return command-id to server
|
|
|
|
// \e]198;remote;0BEL - runner program not available
|
|
|
|
// \e]198;remote;1BEL - runner program is available
|
|
|
|
// \e]198;error;(error-str)BEL - communicate an internal error
|
|
|
|
func MakeSHOSCEsc(escName string, data string) string {
|
|
|
|
return fmt.Sprintf("\033]198;%s;%s\007", escName, data)
|
|
|
|
}
|
|
|
|
|
|
|
|
func WriteErrorMsg(fileName string, errVal string) error {
|
|
|
|
fd, err := os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY, 0600)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
oscEsc := MakeSHOSCEsc(OSCEscError, errVal)
|
|
|
|
_, writeErr := fd.Write([]byte(oscEsc))
|
|
|
|
return writeErr
|
|
|
|
}
|