mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-21 16:38:23 +01:00
160 lines
3.4 KiB
Go
160 lines
3.4 KiB
Go
// Copyright 2024, Command Line Inc.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package wavebase
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io/fs"
|
|
"log"
|
|
"os"
|
|
"os/exec"
|
|
"path"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
const WaveVersion = "v0.1.0"
|
|
const DefaultWaveHome = "~/.w2"
|
|
const WaveHomeVarName = "WAVETERM_HOME"
|
|
const WaveDevVarName = "WAVETERM_DEV"
|
|
const HomeVarName = "HOME"
|
|
const WaveLockFile = "waveterm.lock"
|
|
|
|
var baseLock = &sync.Mutex{}
|
|
var ensureDirCache = map[string]bool{}
|
|
|
|
func IsDevMode() bool {
|
|
pdev := os.Getenv(WaveDevVarName)
|
|
return pdev != ""
|
|
}
|
|
|
|
func GetHomeDir() string {
|
|
homeVar := os.Getenv(HomeVarName)
|
|
if homeVar == "" {
|
|
return "/"
|
|
}
|
|
return homeVar
|
|
}
|
|
|
|
func ExpandHomeDir(pathStr string) string {
|
|
if pathStr != "~" && !strings.HasPrefix(pathStr, "~/") {
|
|
return pathStr
|
|
}
|
|
homeDir := GetHomeDir()
|
|
if pathStr == "~" {
|
|
return homeDir
|
|
}
|
|
return path.Join(homeDir, pathStr[2:])
|
|
}
|
|
|
|
func ReplaceHomeDir(pathStr string) string {
|
|
homeDir := GetHomeDir()
|
|
if pathStr == homeDir {
|
|
return "~"
|
|
}
|
|
if strings.HasPrefix(pathStr, homeDir+"/") {
|
|
return "~" + pathStr[len(homeDir):]
|
|
}
|
|
return pathStr
|
|
}
|
|
|
|
func GetWaveHomeDir() string {
|
|
homeVar := os.Getenv(WaveHomeVarName)
|
|
if homeVar != "" {
|
|
return ExpandHomeDir(homeVar)
|
|
}
|
|
return ExpandHomeDir(DefaultWaveHome)
|
|
}
|
|
|
|
func EnsureWaveHomeDir() error {
|
|
return CacheEnsureDir(GetWaveHomeDir(), "wavehome", 0700, "wave home directory")
|
|
}
|
|
|
|
func CacheEnsureDir(dirName string, cacheKey string, perm os.FileMode, dirDesc string) error {
|
|
baseLock.Lock()
|
|
ok := ensureDirCache[cacheKey]
|
|
baseLock.Unlock()
|
|
if ok {
|
|
return nil
|
|
}
|
|
err := TryMkdirs(dirName, perm, dirDesc)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
baseLock.Lock()
|
|
ensureDirCache[cacheKey] = true
|
|
baseLock.Unlock()
|
|
return nil
|
|
}
|
|
|
|
func TryMkdirs(dirName string, perm os.FileMode, dirDesc string) error {
|
|
info, err := os.Stat(dirName)
|
|
if errors.Is(err, fs.ErrNotExist) {
|
|
err = os.MkdirAll(dirName, perm)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot make %s %q: %w", dirDesc, dirName, err)
|
|
}
|
|
info, err = os.Stat(dirName)
|
|
}
|
|
if err != nil {
|
|
return fmt.Errorf("error trying to stat %s: %w", dirDesc, err)
|
|
}
|
|
if !info.IsDir() {
|
|
return fmt.Errorf("%s %q must be a directory", dirDesc, dirName)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
var osLangOnce = &sync.Once{}
|
|
var osLang string
|
|
|
|
func determineLang() string {
|
|
ctx, cancelFn := context.WithTimeout(context.Background(), 2*time.Second)
|
|
defer cancelFn()
|
|
if runtime.GOOS == "darwin" {
|
|
out, err := exec.CommandContext(ctx, "defaults", "read", "-g", "AppleLocale").CombinedOutput()
|
|
if err != nil {
|
|
log.Printf("error executing 'defaults read -g AppleLocale': %v\n", err)
|
|
return ""
|
|
}
|
|
strOut := string(out)
|
|
truncOut := strings.Split(strOut, "@")[0]
|
|
return strings.TrimSpace(truncOut) + ".UTF-8"
|
|
} else {
|
|
// this is specifically to get the wavesrv LANG so waveshell
|
|
// on a remote uses the same LANG
|
|
return os.Getenv("LANG")
|
|
}
|
|
}
|
|
|
|
func DetermineLang() string {
|
|
osLangOnce.Do(func() {
|
|
osLang = determineLang()
|
|
})
|
|
return osLang
|
|
}
|
|
|
|
func AcquireWaveLock() (*os.File, error) {
|
|
homeDir := GetWaveHomeDir()
|
|
lockFileName := filepath.Join(homeDir, WaveLockFile)
|
|
log.Printf("[base] acquiring lock on %s\n", lockFileName)
|
|
fd, err := os.OpenFile(lockFileName, os.O_RDWR|os.O_CREATE, 0600)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = unix.Flock(int(fd.Fd()), unix.LOCK_EX|unix.LOCK_NB)
|
|
if err != nil {
|
|
fd.Close()
|
|
return nil, err
|
|
}
|
|
return fd, nil
|
|
}
|