mirror of
https://github.com/wavetermdev/backup.git
synced 2026-04-22 15:26:58 -07:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9c32f690c6 | |||
| 4b5eccab7c | |||
| a69ac24659 | |||
| fde141fce3 | |||
| 42656ecd71 | |||
| 291f7f61ed | |||
| da76719800 | |||
| 0d8b163b63 | |||
| 8656387329 |
Binary file not shown.
Binary file not shown.
@@ -3,7 +3,7 @@
|
||||
|
||||
:root {
|
||||
/*
|
||||
* term colors (16 + 6) form the base terminal theme
|
||||
* term colors (16 + 5) form the base terminal theme
|
||||
* for consistency these colors should be used by plugins/applications
|
||||
*/
|
||||
--term-black: #000000;
|
||||
@@ -27,6 +27,5 @@
|
||||
--term-cmdtext: #ffffff;
|
||||
--term-foreground: #d3d7cf;
|
||||
--term-background: #000000;
|
||||
--term-selection-background: #ffffff90;
|
||||
--term-cursor-accent: #000000;
|
||||
--term-selection-background: #ffffff40;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,5 @@
|
||||
--term-foreground: #000000;
|
||||
--term-background: #fefefe;
|
||||
--term-cmdtext: #000000;
|
||||
--term-selection-background: #00000040;
|
||||
--term-cursor-accent: #000000;
|
||||
--term-selection-background: #00000018;
|
||||
}
|
||||
|
||||
@@ -87,7 +87,6 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
|
||||
const availableFontFamilies: DropdownItem[] = [];
|
||||
availableFontFamilies.push({ label: "JetBrains Mono", value: "JetBrains Mono" });
|
||||
availableFontFamilies.push({ label: "Hack", value: "Hack" });
|
||||
availableFontFamilies.push({ label: "Fira Code", value: "Fira Code" });
|
||||
return availableFontFamilies;
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ class AlertModal extends React.Component<{}, {}> {
|
||||
<Markdown text={message?.message ?? ""} extraClassName="bottom-margin" />
|
||||
</If>
|
||||
<If condition={!message?.markdown}>{message?.message}</If>
|
||||
<If condition={message?.confirmflag}>
|
||||
<If condition={message.confirmflag}>
|
||||
<Checkbox
|
||||
onChange={this.handleDontShowAgain}
|
||||
label={"Don't show me this again"}
|
||||
|
||||
@@ -206,7 +206,7 @@ class BookmarksModel {
|
||||
}
|
||||
|
||||
handleDocKeyDown(e: any): void {
|
||||
const waveEvent = adaptFromReactOrNativeKeyEvent(e);
|
||||
let waveEvent = adaptFromReactOrNativeKeyEvent(e);
|
||||
if (checkKeyPressed(waveEvent, "Escape")) {
|
||||
e.preventDefault();
|
||||
if (this.editingBookmark.get() != null) {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
import * as mobx from "mobx";
|
||||
import { Model } from "./model";
|
||||
import { checkKeyPressed, adaptFromReactOrNativeKeyEvent } from "@/util/keyutil";
|
||||
|
||||
class ClientSettingsViewModel {
|
||||
globalModel: Model;
|
||||
@@ -22,15 +21,6 @@ class ClientSettingsViewModel {
|
||||
this.globalModel.activeMainView.set("clientsettings");
|
||||
})();
|
||||
}
|
||||
|
||||
handleDocKeyDown(e: any): void {
|
||||
const waveEvent = adaptFromReactOrNativeKeyEvent(e);
|
||||
if (checkKeyPressed(waveEvent, "Escape")) {
|
||||
e.preventDefault();
|
||||
this.closeView();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { ClientSettingsViewModel };
|
||||
|
||||
@@ -304,6 +304,10 @@ class CommandRunner {
|
||||
GlobalModel.clientSettingsViewModel.showClientSettingsView();
|
||||
}
|
||||
|
||||
syncShellState() {
|
||||
GlobalModel.submitCommand("sync", null, null, { nohist: "1" }, false);
|
||||
}
|
||||
|
||||
historyView(params: HistorySearchParams) {
|
||||
let kwargs = { nohist: "1" };
|
||||
kwargs["offset"] = String(params.offset);
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
import * as mobx from "mobx";
|
||||
import { Model } from "./model";
|
||||
import { checkKeyPressed, adaptFromReactOrNativeKeyEvent } from "@/util/keyutil";
|
||||
|
||||
class ConnectionsViewModel {
|
||||
globalModel: Model;
|
||||
@@ -22,15 +21,6 @@ class ConnectionsViewModel {
|
||||
this.globalModel.activeMainView.set("connections");
|
||||
})();
|
||||
}
|
||||
|
||||
handleDocKeyDown(e: any): void {
|
||||
const waveEvent = adaptFromReactOrNativeKeyEvent(e);
|
||||
if (checkKeyPressed(waveEvent, "Escape")) {
|
||||
e.preventDefault();
|
||||
this.closeView();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { ConnectionsViewModel };
|
||||
|
||||
@@ -291,7 +291,7 @@ class HistoryViewModel {
|
||||
}
|
||||
|
||||
handleDocKeyDown(e: any): void {
|
||||
const waveEvent = adaptFromReactOrNativeKeyEvent(e);
|
||||
let waveEvent = adaptFromReactOrNativeKeyEvent(e);
|
||||
if (checkKeyPressed(waveEvent, "Escape")) {
|
||||
e.preventDefault();
|
||||
this.closeView();
|
||||
|
||||
@@ -18,11 +18,10 @@ class ModalsModel {
|
||||
}
|
||||
}
|
||||
|
||||
popModal(callback?: () => void) {
|
||||
popModal() {
|
||||
mobx.action(() => {
|
||||
this.store.pop();
|
||||
})();
|
||||
callback && callback();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+37
-7
@@ -372,6 +372,7 @@ class Model {
|
||||
cancelAlert(): void {
|
||||
mobx.action(() => {
|
||||
this.alertMessage.set(null);
|
||||
this.modalsModel.popModal();
|
||||
})();
|
||||
if (this.alertPromiseResolver != null) {
|
||||
this.alertPromiseResolver(false);
|
||||
@@ -492,7 +493,7 @@ class Model {
|
||||
if (this.alertMessage.get() != null) {
|
||||
if (checkKeyPressed(waveEvent, "Escape")) {
|
||||
e.preventDefault();
|
||||
this.modalsModel.popModal(() => this.cancelAlert());
|
||||
this.cancelAlert();
|
||||
return;
|
||||
}
|
||||
if (checkKeyPressed(waveEvent, "Enter")) {
|
||||
@@ -502,10 +503,6 @@ class Model {
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (checkKeyPressed(waveEvent, "Escape") && this.modalsModel.store.length > 0) {
|
||||
this.modalsModel.popModal();
|
||||
return;
|
||||
}
|
||||
if (this.activeMainView.get() == "bookmarks") {
|
||||
this.bookmarksModel.handleDocKeyDown(e);
|
||||
}
|
||||
@@ -513,10 +510,10 @@ class Model {
|
||||
this.historyViewModel.handleDocKeyDown(e);
|
||||
}
|
||||
if (this.activeMainView.get() == "connections") {
|
||||
this.connectionViewModel.handleDocKeyDown(e);
|
||||
this.historyViewModel.handleDocKeyDown(e);
|
||||
}
|
||||
if (this.activeMainView.get() == "clientsettings") {
|
||||
this.clientSettingsViewModel.handleDocKeyDown(e);
|
||||
this.historyViewModel.handleDocKeyDown(e);
|
||||
} else {
|
||||
if (checkKeyPressed(waveEvent, "Escape")) {
|
||||
e.preventDefault();
|
||||
@@ -524,6 +521,9 @@ class Model {
|
||||
this.showSessionView();
|
||||
return;
|
||||
}
|
||||
if (this.clearModals()) {
|
||||
return;
|
||||
}
|
||||
const inputModel = this.inputModel;
|
||||
inputModel.toggleInfoMsg();
|
||||
if (inputModel.inputMode.get() != null) {
|
||||
@@ -643,6 +643,33 @@ class Model {
|
||||
return screen.getTermWrap(line.lineid);
|
||||
}
|
||||
|
||||
clearModals(): boolean {
|
||||
let didSomething = false;
|
||||
mobx.action(() => {
|
||||
if (this.screenSettingsModal.get()) {
|
||||
this.screenSettingsModal.set(null);
|
||||
didSomething = true;
|
||||
}
|
||||
if (this.sessionSettingsModal.get()) {
|
||||
this.sessionSettingsModal.set(null);
|
||||
didSomething = true;
|
||||
}
|
||||
if (this.screenSettingsModal.get()) {
|
||||
this.screenSettingsModal.set(null);
|
||||
didSomething = true;
|
||||
}
|
||||
if (this.clientSettingsModal.get()) {
|
||||
this.clientSettingsModal.set(false);
|
||||
didSomething = true;
|
||||
}
|
||||
if (this.lineSettingsModal.get()) {
|
||||
this.lineSettingsModal.set(null);
|
||||
didSomething = true;
|
||||
}
|
||||
})();
|
||||
return didSomething;
|
||||
}
|
||||
|
||||
restartWaveSrv(): void {
|
||||
getApi().restartWaveSrv();
|
||||
}
|
||||
@@ -1059,6 +1086,9 @@ class Model {
|
||||
this.activeMainView.set("session");
|
||||
this.deactivateScreenLines();
|
||||
this.ws.watchScreen(newActiveSessionId, newActiveScreenId);
|
||||
setTimeout(() => {
|
||||
GlobalCommandRunner.syncShellState();
|
||||
}, 100);
|
||||
}
|
||||
} else {
|
||||
console.warn("unknown update", genUpdate);
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
.image-renderer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding-top: var(--termpad);
|
||||
|
||||
img {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@@ -58,9 +58,6 @@ function getThemeFromCSSVars(): ITheme {
|
||||
theme.brightCyan = rootStyle.getPropertyValue("--term-bright-cyan");
|
||||
theme.brightWhite = rootStyle.getPropertyValue("--term-bright-white");
|
||||
theme.selectionBackground = rootStyle.getPropertyValue("--term-selection-background");
|
||||
theme.selectionInactiveBackground = rootStyle.getPropertyValue("--term-selection-background");
|
||||
theme.cursor = rootStyle.getPropertyValue("--term-selection-background");
|
||||
theme.cursorAccent = rootStyle.getPropertyValue("--term-cursor-accent");
|
||||
return theme;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ let isJetBrainsMonoLoaded = false;
|
||||
let isLatoFontLoaded = false;
|
||||
let isHackFontLoaded = false;
|
||||
let isBaseFontsLoaded = false;
|
||||
let isFiraCodeLoaded = false;
|
||||
|
||||
function addToFontFaceSet(fontFaceSet: FontFaceSet, fontFace: FontFace) {
|
||||
// any cast to work around typing issue
|
||||
@@ -56,25 +55,6 @@ function loadLatoFont() {
|
||||
latoFontBold.load();
|
||||
}
|
||||
|
||||
function loadFiraCodeFont() {
|
||||
if (isFiraCodeLoaded) {
|
||||
return;
|
||||
}
|
||||
isFiraCodeLoaded = true;
|
||||
let firaCodeRegular = new FontFace("Fira Code", "url('public/fonts/firacode-regular.woff2')", {
|
||||
style: "normal",
|
||||
weight: "400",
|
||||
});
|
||||
let firaCodeBold = new FontFace("Fira Code", "url('public/fonts/firacode-bold.woff2')", {
|
||||
style: "normal",
|
||||
weight: "700",
|
||||
});
|
||||
addToFontFaceSet(document.fonts, firaCodeRegular);
|
||||
addToFontFaceSet(document.fonts, firaCodeBold);
|
||||
firaCodeRegular.load();
|
||||
firaCodeBold.load();
|
||||
}
|
||||
|
||||
function loadHackFont() {
|
||||
if (isHackFontLoaded) {
|
||||
return;
|
||||
@@ -124,7 +104,6 @@ function loadFonts() {
|
||||
loadLatoFont();
|
||||
loadJetBrainsMonoFont();
|
||||
loadHackFont();
|
||||
loadFiraCodeFont();
|
||||
}
|
||||
|
||||
export { loadFonts };
|
||||
|
||||
@@ -61,15 +61,7 @@ func handleSingle() {
|
||||
sender.SendErrorResponse(runPacket.ReqId, fmt.Errorf("run packets from server must have a CK: %v", err))
|
||||
}
|
||||
if runPacket.Detached {
|
||||
cmd, startPk, err := shexec.RunCommandDetached(runPacket, sender)
|
||||
if err != nil {
|
||||
sender.SendErrorResponse(runPacket.ReqId, err)
|
||||
return
|
||||
}
|
||||
sender.SendPacket(startPk)
|
||||
sender.Close()
|
||||
sender.WaitForDone()
|
||||
cmd.DetachedWait(startPk)
|
||||
sender.SendErrorResponse(runPacket.ReqId, fmt.Errorf("detached mode not supported"))
|
||||
return
|
||||
} else {
|
||||
shexec.IgnoreSigPipe()
|
||||
|
||||
@@ -1,473 +0,0 @@
|
||||
// Copyright 2023, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package cmdtail
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"regexp"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/wavetermdev/waveterm/waveshell/pkg/base"
|
||||
"github.com/wavetermdev/waveterm/waveshell/pkg/packet"
|
||||
)
|
||||
|
||||
const MaxDataBytes = 4096
|
||||
const FileTypePty = "ptyout"
|
||||
const FileTypeRun = "runout"
|
||||
|
||||
type Tailer struct {
|
||||
Lock *sync.Mutex
|
||||
WatchList map[base.CommandKey]CmdWatchEntry
|
||||
Watcher *fsnotify.Watcher
|
||||
Sender *packet.PacketSender
|
||||
Gen FileNameGenerator
|
||||
Sessions map[string]bool
|
||||
}
|
||||
|
||||
type TailPos struct {
|
||||
ReqId string
|
||||
Running bool // an active tailer sending data
|
||||
TailPtyPos int64
|
||||
TailRunPos int64
|
||||
Follow bool
|
||||
}
|
||||
|
||||
type CmdWatchEntry struct {
|
||||
CmdKey base.CommandKey
|
||||
FilePtyLen int64
|
||||
FileRunLen int64
|
||||
Tails []TailPos
|
||||
Done bool
|
||||
}
|
||||
|
||||
type FileNameGenerator interface {
|
||||
PtyOutFile(ck base.CommandKey) string
|
||||
RunOutFile(ck base.CommandKey) string
|
||||
SessionDir(sessionId string) string
|
||||
}
|
||||
|
||||
func (w CmdWatchEntry) getTailPos(reqId string) (TailPos, bool) {
|
||||
for _, pos := range w.Tails {
|
||||
if pos.ReqId == reqId {
|
||||
return pos, true
|
||||
}
|
||||
}
|
||||
return TailPos{}, false
|
||||
}
|
||||
|
||||
func (w *CmdWatchEntry) updateTailPos(reqId string, newPos TailPos) {
|
||||
for idx, pos := range w.Tails {
|
||||
if pos.ReqId == reqId {
|
||||
w.Tails[idx] = newPos
|
||||
return
|
||||
}
|
||||
}
|
||||
w.Tails = append(w.Tails, newPos)
|
||||
}
|
||||
|
||||
func (w *CmdWatchEntry) removeTailPos(reqId string) {
|
||||
var newTails []TailPos
|
||||
for _, pos := range w.Tails {
|
||||
if pos.ReqId == reqId {
|
||||
continue
|
||||
}
|
||||
newTails = append(newTails, pos)
|
||||
}
|
||||
w.Tails = newTails
|
||||
}
|
||||
|
||||
func (pos TailPos) IsCurrent(entry CmdWatchEntry) bool {
|
||||
return pos.TailPtyPos >= entry.FilePtyLen && pos.TailRunPos >= entry.FileRunLen
|
||||
}
|
||||
|
||||
func (t *Tailer) updateTailPos_nolock(cmdKey base.CommandKey, reqId string, pos TailPos) {
|
||||
entry, found := t.WatchList[cmdKey]
|
||||
if !found {
|
||||
return
|
||||
}
|
||||
entry.updateTailPos(reqId, pos)
|
||||
t.WatchList[cmdKey] = entry
|
||||
}
|
||||
|
||||
func (t *Tailer) removeTailPos(cmdKey base.CommandKey, reqId string) {
|
||||
t.Lock.Lock()
|
||||
defer t.Lock.Unlock()
|
||||
t.removeTailPos_nolock(cmdKey, reqId)
|
||||
}
|
||||
|
||||
func (t *Tailer) removeTailPos_nolock(cmdKey base.CommandKey, reqId string) {
|
||||
entry, found := t.WatchList[cmdKey]
|
||||
if !found {
|
||||
return
|
||||
}
|
||||
entry.removeTailPos(reqId)
|
||||
t.WatchList[cmdKey] = entry
|
||||
if len(entry.Tails) == 0 {
|
||||
t.removeWatch_nolock(cmdKey)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Tailer) removeWatch_nolock(cmdKey base.CommandKey) {
|
||||
// delete from watchlist, remove watches
|
||||
delete(t.WatchList, cmdKey)
|
||||
t.Watcher.Remove(t.Gen.PtyOutFile(cmdKey))
|
||||
t.Watcher.Remove(t.Gen.RunOutFile(cmdKey))
|
||||
}
|
||||
|
||||
func (t *Tailer) getEntryAndPos_nolock(cmdKey base.CommandKey, reqId string) (CmdWatchEntry, TailPos, bool) {
|
||||
entry, found := t.WatchList[cmdKey]
|
||||
if !found {
|
||||
return CmdWatchEntry{}, TailPos{}, false
|
||||
}
|
||||
pos, found := entry.getTailPos(reqId)
|
||||
if !found {
|
||||
return CmdWatchEntry{}, TailPos{}, false
|
||||
}
|
||||
return entry, pos, true
|
||||
}
|
||||
|
||||
func (t *Tailer) addSessionWatcher(sessionId string) error {
|
||||
t.Lock.Lock()
|
||||
defer t.Lock.Unlock()
|
||||
|
||||
if t.Sessions[sessionId] {
|
||||
return nil
|
||||
}
|
||||
sdir := t.Gen.SessionDir(sessionId)
|
||||
err := t.Watcher.Add(sdir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.Sessions[sessionId] = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Tailer) removeSessionWatcher(sessionId string) {
|
||||
t.Lock.Lock()
|
||||
defer t.Lock.Unlock()
|
||||
|
||||
if !t.Sessions[sessionId] {
|
||||
return
|
||||
}
|
||||
sdir := t.Gen.SessionDir(sessionId)
|
||||
t.Watcher.Remove(sdir)
|
||||
}
|
||||
|
||||
func MakeTailer(sender *packet.PacketSender, gen FileNameGenerator) (*Tailer, error) {
|
||||
rtn := &Tailer{
|
||||
Lock: &sync.Mutex{},
|
||||
WatchList: make(map[base.CommandKey]CmdWatchEntry),
|
||||
Sessions: make(map[string]bool),
|
||||
Sender: sender,
|
||||
Gen: gen,
|
||||
}
|
||||
var err error
|
||||
rtn.Watcher, err = fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rtn, nil
|
||||
}
|
||||
|
||||
func (t *Tailer) readDataFromFile(fileName string, pos int64, maxBytes int) ([]byte, error) {
|
||||
fd, err := os.Open(fileName)
|
||||
defer fd.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf := make([]byte, maxBytes)
|
||||
nr, err := fd.ReadAt(buf, pos)
|
||||
if err != nil && err != io.EOF { // ignore EOF error
|
||||
return nil, err
|
||||
}
|
||||
return buf[0:nr], nil
|
||||
}
|
||||
|
||||
func (t *Tailer) makeCmdDataPacket(entry CmdWatchEntry, pos TailPos) (*packet.CmdDataPacketType, error) {
|
||||
dataPacket := packet.MakeCmdDataPacket(pos.ReqId)
|
||||
dataPacket.CK = entry.CmdKey
|
||||
dataPacket.PtyPos = pos.TailPtyPos
|
||||
dataPacket.RunPos = pos.TailRunPos
|
||||
if entry.FilePtyLen > pos.TailPtyPos {
|
||||
ptyData, err := t.readDataFromFile(t.Gen.PtyOutFile(entry.CmdKey), pos.TailPtyPos, MaxDataBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dataPacket.PtyData64 = base64.StdEncoding.EncodeToString(ptyData)
|
||||
dataPacket.PtyDataLen = len(ptyData)
|
||||
}
|
||||
if entry.FileRunLen > pos.TailRunPos {
|
||||
runData, err := t.readDataFromFile(t.Gen.RunOutFile(entry.CmdKey), pos.TailRunPos, MaxDataBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dataPacket.RunData64 = base64.StdEncoding.EncodeToString(runData)
|
||||
dataPacket.RunDataLen = len(runData)
|
||||
}
|
||||
return dataPacket, nil
|
||||
}
|
||||
|
||||
// returns (data-packet, keepRunning)
|
||||
func (t *Tailer) runSingleDataTransfer(key base.CommandKey, reqId string) (*packet.CmdDataPacketType, bool, error) {
|
||||
t.Lock.Lock()
|
||||
entry, pos, foundPos := t.getEntryAndPos_nolock(key, reqId)
|
||||
t.Lock.Unlock()
|
||||
if !foundPos {
|
||||
return nil, false, nil
|
||||
}
|
||||
dataPacket, dataErr := t.makeCmdDataPacket(entry, pos)
|
||||
|
||||
t.Lock.Lock()
|
||||
defer t.Lock.Unlock()
|
||||
entry, pos, foundPos = t.getEntryAndPos_nolock(key, reqId)
|
||||
if !foundPos {
|
||||
return nil, false, nil
|
||||
}
|
||||
// pos was updated between first and second get, throw out data-packet and re-run
|
||||
if pos.TailPtyPos != dataPacket.PtyPos || pos.TailRunPos != dataPacket.RunPos {
|
||||
return nil, true, nil
|
||||
}
|
||||
if dataErr != nil {
|
||||
// error, so return error packet, and stop running
|
||||
pos.Running = false
|
||||
t.updateTailPos_nolock(key, reqId, pos)
|
||||
return nil, false, dataErr
|
||||
}
|
||||
pos.TailPtyPos += int64(dataPacket.PtyDataLen)
|
||||
pos.TailRunPos += int64(dataPacket.RunDataLen)
|
||||
if pos.IsCurrent(entry) {
|
||||
// we caught up, tail position equals file length
|
||||
pos.Running = false
|
||||
}
|
||||
t.updateTailPos_nolock(key, reqId, pos)
|
||||
return dataPacket, pos.Running, nil
|
||||
}
|
||||
|
||||
// returns (removed)
|
||||
func (t *Tailer) checkRemove(cmdKey base.CommandKey, reqId string) bool {
|
||||
t.Lock.Lock()
|
||||
defer t.Lock.Unlock()
|
||||
entry, pos, foundPos := t.getEntryAndPos_nolock(cmdKey, reqId)
|
||||
if !foundPos {
|
||||
return false
|
||||
}
|
||||
if !pos.IsCurrent(entry) {
|
||||
return false
|
||||
}
|
||||
if !pos.Follow || entry.Done {
|
||||
t.removeTailPos_nolock(cmdKey, reqId)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *Tailer) RunDataTransfer(key base.CommandKey, reqId string) {
|
||||
for {
|
||||
dataPacket, keepRunning, err := t.runSingleDataTransfer(key, reqId)
|
||||
if dataPacket != nil {
|
||||
t.Sender.SendPacket(dataPacket)
|
||||
}
|
||||
if err != nil {
|
||||
t.removeTailPos(key, reqId)
|
||||
t.Sender.SendErrorResponse(reqId, err)
|
||||
break
|
||||
}
|
||||
if !keepRunning {
|
||||
removed := t.checkRemove(key, reqId)
|
||||
if removed {
|
||||
t.Sender.SendResponse(reqId, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Tailer) tryStartRun_nolock(entry CmdWatchEntry, pos TailPos) {
|
||||
if pos.Running {
|
||||
return
|
||||
}
|
||||
if pos.IsCurrent(entry) {
|
||||
return
|
||||
}
|
||||
pos.Running = true
|
||||
t.updateTailPos_nolock(entry.CmdKey, pos.ReqId, pos)
|
||||
go t.RunDataTransfer(entry.CmdKey, pos.ReqId)
|
||||
}
|
||||
|
||||
var updateFileRe = regexp.MustCompile("/([a-z0-9-]+)/([a-z0-9-]+)\\.(ptyout|runout)$")
|
||||
|
||||
func (t *Tailer) updateFile(relFileName string) {
|
||||
m := updateFileRe.FindStringSubmatch(relFileName)
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
finfo, err := os.Stat(relFileName)
|
||||
if err != nil {
|
||||
t.Sender.SendPacket(packet.FmtMessagePacket("error trying to stat file '%s': %v", relFileName, err))
|
||||
return
|
||||
}
|
||||
cmdKey := base.MakeCommandKey(m[1], m[2])
|
||||
t.Lock.Lock()
|
||||
defer t.Lock.Unlock()
|
||||
entry, foundEntry := t.WatchList[cmdKey]
|
||||
if !foundEntry {
|
||||
return
|
||||
}
|
||||
fileType := m[3]
|
||||
if fileType == FileTypePty {
|
||||
entry.FilePtyLen = finfo.Size()
|
||||
} else if fileType == FileTypeRun {
|
||||
entry.FileRunLen = finfo.Size()
|
||||
}
|
||||
t.WatchList[cmdKey] = entry
|
||||
for _, pos := range entry.Tails {
|
||||
t.tryStartRun_nolock(entry, pos)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Tailer) Run() {
|
||||
for {
|
||||
select {
|
||||
case event, ok := <-t.Watcher.Events:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if event.Op&fsnotify.Write == fsnotify.Write {
|
||||
t.updateFile(event.Name)
|
||||
}
|
||||
|
||||
case err, ok := <-t.Watcher.Errors:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
// what to do with this error? just send a message
|
||||
t.Sender.SendPacket(packet.FmtMessagePacket("error in tailer: %v", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Tailer) Close() error {
|
||||
return t.Watcher.Close()
|
||||
}
|
||||
|
||||
func max(v1 int64, v2 int64) int64 {
|
||||
if v1 > v2 {
|
||||
return v1
|
||||
}
|
||||
return v2
|
||||
}
|
||||
|
||||
func (entry *CmdWatchEntry) fillFilePos(gen FileNameGenerator) {
|
||||
ptyInfo, _ := os.Stat(gen.PtyOutFile(entry.CmdKey))
|
||||
if ptyInfo != nil {
|
||||
entry.FilePtyLen = ptyInfo.Size()
|
||||
}
|
||||
runoutInfo, _ := os.Stat(gen.RunOutFile(entry.CmdKey))
|
||||
if runoutInfo != nil {
|
||||
entry.FileRunLen = runoutInfo.Size()
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Tailer) KeyDone(key base.CommandKey) {
|
||||
t.Lock.Lock()
|
||||
defer t.Lock.Unlock()
|
||||
entry, foundEntry := t.WatchList[key]
|
||||
if !foundEntry {
|
||||
return
|
||||
}
|
||||
entry.Done = true
|
||||
var newTails []TailPos
|
||||
for _, pos := range entry.Tails {
|
||||
if pos.IsCurrent(entry) {
|
||||
continue
|
||||
}
|
||||
newTails = append(newTails, pos)
|
||||
}
|
||||
entry.Tails = newTails
|
||||
t.WatchList[key] = entry
|
||||
if len(entry.Tails) == 0 {
|
||||
t.removeWatch_nolock(key)
|
||||
}
|
||||
t.WatchList[key] = entry
|
||||
}
|
||||
|
||||
func (t *Tailer) RemoveWatch(pk *packet.UntailCmdPacketType) {
|
||||
t.Lock.Lock()
|
||||
defer t.Lock.Unlock()
|
||||
t.removeTailPos_nolock(pk.CK, pk.ReqId)
|
||||
}
|
||||
|
||||
func (t *Tailer) AddFileWatches_nolock(key base.CommandKey, ptyOnly bool) error {
|
||||
ptyName := t.Gen.PtyOutFile(key)
|
||||
runName := t.Gen.RunOutFile(key)
|
||||
fmt.Printf("WATCH> add %s\n", ptyName)
|
||||
err := t.Watcher.Add(ptyName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ptyOnly {
|
||||
return nil
|
||||
}
|
||||
err = t.Watcher.Add(runName)
|
||||
if err != nil {
|
||||
t.Watcher.Remove(ptyName) // best effort clean up
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// returns (up-to-date/done, error)
|
||||
func (t *Tailer) AddWatch(getPacket *packet.GetCmdPacketType) (bool, error) {
|
||||
if err := getPacket.CK.Validate("getcmd"); err != nil {
|
||||
return false, err
|
||||
}
|
||||
if getPacket.ReqId == "" {
|
||||
return false, fmt.Errorf("getcmd, no reqid specified")
|
||||
}
|
||||
t.Lock.Lock()
|
||||
defer t.Lock.Unlock()
|
||||
key := getPacket.CK
|
||||
entry, foundEntry := t.WatchList[key]
|
||||
if !foundEntry {
|
||||
// initialize entry, add watches
|
||||
entry = CmdWatchEntry{CmdKey: key}
|
||||
entry.fillFilePos(t.Gen)
|
||||
}
|
||||
pos, foundPos := entry.getTailPos(getPacket.ReqId)
|
||||
if !foundPos {
|
||||
// initialize a new tailpos
|
||||
pos = TailPos{ReqId: getPacket.ReqId}
|
||||
}
|
||||
// update tailpos with new values from getpacket
|
||||
pos.TailPtyPos = getPacket.PtyPos
|
||||
pos.TailRunPos = getPacket.RunPos
|
||||
pos.Follow = getPacket.Tail
|
||||
// convert negative pos to positive
|
||||
if pos.TailPtyPos < 0 {
|
||||
pos.TailPtyPos = max(0, entry.FilePtyLen+pos.TailPtyPos) // + because negative
|
||||
}
|
||||
if pos.TailRunPos < 0 {
|
||||
pos.TailRunPos = max(0, entry.FileRunLen+pos.TailRunPos) // + because negative
|
||||
}
|
||||
entry.updateTailPos(pos.ReqId, pos)
|
||||
if !pos.Follow && pos.IsCurrent(entry) {
|
||||
// don't add to t.WatchList, don't t.AddFileWatches_nolock, send rpc response
|
||||
return true, nil
|
||||
}
|
||||
if !foundEntry {
|
||||
err := t.AddFileWatches_nolock(key, getPacket.PtyOnly)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
t.WatchList[key] = entry
|
||||
t.tryStartRun_nolock(entry, pos)
|
||||
return false, nil
|
||||
}
|
||||
@@ -43,12 +43,10 @@ const (
|
||||
DataEndPacketStr = "dataend"
|
||||
ResponsePacketStr = "resp" // rpc-response
|
||||
DonePacketStr = "done"
|
||||
CmdErrorPacketStr = "cmderror" // command
|
||||
MessagePacketStr = "message"
|
||||
GetCmdPacketStr = "getcmd" // rpc
|
||||
UntailCmdPacketStr = "untailcmd" // rpc
|
||||
CdPacketStr = "cd" // rpc
|
||||
CmdDataPacketStr = "cmddata" // rpc-response
|
||||
RawPacketStr = "raw"
|
||||
SpecialInputPacketStr = "sinput" // command
|
||||
CompGenPacketStr = "compgen" // rpc
|
||||
@@ -90,7 +88,6 @@ func init() {
|
||||
TypeStrToFactory[PingPacketStr] = reflect.TypeOf(PingPacketType{})
|
||||
TypeStrToFactory[ResponsePacketStr] = reflect.TypeOf(ResponsePacketType{})
|
||||
TypeStrToFactory[DonePacketStr] = reflect.TypeOf(DonePacketType{})
|
||||
TypeStrToFactory[CmdErrorPacketStr] = reflect.TypeOf(CmdErrorPacketType{})
|
||||
TypeStrToFactory[MessagePacketStr] = reflect.TypeOf(MessagePacketType{})
|
||||
TypeStrToFactory[CmdStartPacketStr] = reflect.TypeOf(CmdStartPacketType{})
|
||||
TypeStrToFactory[CmdDonePacketStr] = reflect.TypeOf(CmdDonePacketType{})
|
||||
@@ -98,7 +95,6 @@ func init() {
|
||||
TypeStrToFactory[UntailCmdPacketStr] = reflect.TypeOf(UntailCmdPacketType{})
|
||||
TypeStrToFactory[InitPacketStr] = reflect.TypeOf(InitPacketType{})
|
||||
TypeStrToFactory[CdPacketStr] = reflect.TypeOf(CdPacketType{})
|
||||
TypeStrToFactory[CmdDataPacketStr] = reflect.TypeOf(CmdDataPacketType{})
|
||||
TypeStrToFactory[RawPacketStr] = reflect.TypeOf(RawPacketType{})
|
||||
TypeStrToFactory[SpecialInputPacketStr] = reflect.TypeOf(SpecialInputPacketType{})
|
||||
TypeStrToFactory[DataPacketStr] = reflect.TypeOf(DataPacketType{})
|
||||
@@ -128,7 +124,6 @@ func init() {
|
||||
|
||||
var _ RpcResponsePacketType = (*CmdStartPacketType)(nil)
|
||||
var _ RpcResponsePacketType = (*ResponsePacketType)(nil)
|
||||
var _ RpcResponsePacketType = (*CmdDataPacketType)(nil)
|
||||
var _ RpcResponsePacketType = (*StreamFileResponseType)(nil)
|
||||
var _ RpcResponsePacketType = (*FileDataPacketType)(nil)
|
||||
var _ RpcResponsePacketType = (*WriteFileReadyPacketType)(nil)
|
||||
@@ -155,36 +150,6 @@ func MakePacket(packetType string) (PacketType, error) {
|
||||
return rtn.Interface().(PacketType), nil
|
||||
}
|
||||
|
||||
type CmdDataPacketType struct {
|
||||
Type string `json:"type"`
|
||||
RespId string `json:"respid"`
|
||||
CK base.CommandKey `json:"ck"`
|
||||
PtyPos int64 `json:"ptypos"`
|
||||
PtyLen int64 `json:"ptylen"`
|
||||
RunPos int64 `json:"runpos"`
|
||||
RunLen int64 `json:"runlen"`
|
||||
PtyData64 string `json:"ptydata64"`
|
||||
PtyDataLen int `json:"ptydatalen"`
|
||||
RunData64 string `json:"rundata64"`
|
||||
RunDataLen int `json:"rundatalen"`
|
||||
}
|
||||
|
||||
func (*CmdDataPacketType) GetType() string {
|
||||
return CmdDataPacketStr
|
||||
}
|
||||
|
||||
func (p *CmdDataPacketType) GetResponseId() string {
|
||||
return p.RespId
|
||||
}
|
||||
|
||||
func (*CmdDataPacketType) GetResponseDone() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func MakeCmdDataPacket(reqId string) *CmdDataPacketType {
|
||||
return &CmdDataPacketType{Type: CmdDataPacketStr, RespId: reqId}
|
||||
}
|
||||
|
||||
type PingPacketType struct {
|
||||
Type string `json:"type"`
|
||||
}
|
||||
@@ -830,28 +795,6 @@ type BarePacketType struct {
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type CmdErrorPacketType struct {
|
||||
Type string `json:"type"`
|
||||
CK base.CommandKey `json:"ck"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
func (*CmdErrorPacketType) GetType() string {
|
||||
return CmdErrorPacketStr
|
||||
}
|
||||
|
||||
func (p *CmdErrorPacketType) GetCK() base.CommandKey {
|
||||
return p.CK
|
||||
}
|
||||
|
||||
func (p *CmdErrorPacketType) String() string {
|
||||
return fmt.Sprintf("error[%s]", p.Error)
|
||||
}
|
||||
|
||||
func MakeCmdErrorPacket(ck base.CommandKey, err error) *CmdErrorPacketType {
|
||||
return &CmdErrorPacketType{Type: CmdErrorPacketStr, CK: ck, Error: err.Error()}
|
||||
}
|
||||
|
||||
type WriteFilePacketType struct {
|
||||
Type string `json:"type"`
|
||||
ReqId string `json:"reqid"`
|
||||
@@ -1074,10 +1017,6 @@ func SendPacket(w io.Writer, packet PacketType) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func SendCmdError(w io.Writer, ck base.CommandKey, err error) error {
|
||||
return SendPacket(w, MakeCmdErrorPacket(ck, err))
|
||||
}
|
||||
|
||||
type PacketSender struct {
|
||||
Lock *sync.Mutex
|
||||
SendCh chan PacketType
|
||||
@@ -1197,10 +1136,6 @@ func (sender *PacketSender) SendPacket(pk PacketType) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sender *PacketSender) SendCmdError(ck base.CommandKey, err error) error {
|
||||
return sender.SendPacket(MakeCmdErrorPacket(ck, err))
|
||||
}
|
||||
|
||||
func (sender *PacketSender) SendErrorResponse(reqId string, err error) error {
|
||||
pk := MakeErrorResponsePacket(reqId, err)
|
||||
return sender.SendPacket(pk)
|
||||
@@ -1222,17 +1157,13 @@ type UnknownPacketReporter interface {
|
||||
type DefaultUPR struct{}
|
||||
|
||||
func (DefaultUPR) UnknownPacket(pk PacketType) {
|
||||
if pk.GetType() == CmdErrorPacketStr {
|
||||
errPacket := pk.(*CmdErrorPacketType)
|
||||
// at this point, just send the error packet to stderr rather than try to do something special
|
||||
fmt.Fprintf(os.Stderr, "[error] %s\n", errPacket.Error)
|
||||
} else if pk.GetType() == RawPacketStr {
|
||||
if pk.GetType() == RawPacketStr {
|
||||
rawPacket := pk.(*RawPacketType)
|
||||
fmt.Fprintf(os.Stderr, "%s\n", rawPacket.Data)
|
||||
} else if pk.GetType() == CmdStartPacketStr {
|
||||
return // do nothing
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "[error] invalid packet received '%s'", AsExtType(pk))
|
||||
wlog.Logf("[upr] invalid packet received '%s'", AsExtType(pk))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -151,7 +151,7 @@ func (m *MServer) ProcessCommandPacket(pk packet.CommandPacketType) {
|
||||
cproc := m.ClientMap[ck]
|
||||
m.Lock.Unlock()
|
||||
if cproc == nil {
|
||||
m.Sender.SendCmdError(ck, fmt.Errorf("no client proc for ck '%s', pk=%s", ck, packet.AsString(pk)))
|
||||
wlog.Logf("no client proc for ck %q, pk=%s", ck, packet.AsString(pk))
|
||||
return
|
||||
}
|
||||
cproc.Input.SendPacket(pk)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user