-
Notifications
You must be signed in to change notification settings - Fork 4
/
asciicast.go
102 lines (87 loc) · 2.08 KB
/
asciicast.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
package logger
import (
"encoding/json"
"os"
"path/filepath"
"time"
)
type AsciicastLogger struct {
SessionLogger
}
// func (tw TimingWriter) Write(p []byte) (n int, err error) {
// n, err = tw.log.Write(p)
// if err != nil {
// return
// }
// secDiff := float64(tw.timer.GetDiff()) / float64(time.Second)
// te := fmt.Sprintf("%d %.6f %d\n", tw.id, secDiff, n)
// _, err = tw.timing.Write([]byte(te))
// return
// }
type asciicastHeader struct {
Version int `json:"version"`
Width uint32 `json:"width"`
Height uint32 `json:"height"`
Timestamp int64 `json:"timestamp"`
Command string `json:"command,omitempty"`
Env map[string]string `json:"env,omitempty"`
}
type asciicastEvent [3]interface{}
type asciicastWriter struct {
Log *json.Encoder
Type string
Start time.Time
}
func (w asciicastWriter) Write(p []byte) (n int, err error) {
now := time.Now()
secDiff := float32(now.Sub(w.Start)) / float32(time.Second)
data := asciicastEvent{
secDiff,
w.Type,
string(p),
}
return len(p), w.Log.Encode(data)
}
func (w asciicastWriter) Close() error {
return nil
}
func (l *AsciicastLogger) Start() (err error) {
now := time.Now()
dir, _ := filepath.Split(l.folder())
err = os.MkdirAll(dir, 0700)
if err != nil {
return
}
f, err := os.Create(l.folder())
if err != nil {
return
}
defer f.Close()
meta := asciicastHeader{
Version: 2,
Width: l.PTYCols,
Height: l.PTYRows,
Timestamp: now.Unix(),
Command: l.Command,
Env: nil,
}
encoder := json.NewEncoder(f)
err = encoder.Encode(meta)
if err != nil {
return err
}
errs := make(chan error)
go l.startLog(l.ClientIn, l.ServerIn, asciicastWriter{encoder, "i", now}, errs)
go l.startLog(l.ServerOut, l.ClientOut, asciicastWriter{encoder, "o", now}, errs)
if !l.PTY {
// client stderr is extended channel and not needed to be closed
go l.startLog(l.ServerErr, writeDummyCloser{l.ClientErr}, asciicastWriter{encoder, "o", now}, errs)
}
// TODO: handle errors
<-errs
<-errs
if !l.PTY {
<-errs
}
return nil
}