1 package main 2 3 import ( 4 "errors" 5 "io" 6 "os" 7 "path" 8 "time" 9 10 "gopkg.in/fsnotify.v1" 11 12 "vimagination.zapto.org/byteio" 13 "golang.org/x/net/websocket" 14 ) 15 16 type Console struct { 17 c *Controller 18 } 19 20 func (c Console) Websocket(conn *websocket.Conn) { 21 conn.PayloadType = websocket.BinaryFrame 22 r := byteio.StickyLittleEndianReader{Reader: conn} 23 w := byteio.StickyLittleEndianWriter{Writer: conn} 24 25 err := c.handle(&r, &w) 26 if err != nil { 27 writeError(&w, err) 28 } 29 } 30 31 var logPaths = []string{ 32 "logs/server.log", 33 "logs/latest.log", 34 "server.log", 35 } 36 37 func (c Console) handle(r *byteio.StickyLittleEndianReader, w *byteio.StickyLittleEndianWriter) error { 38 id := int(r.ReadInt32()) 39 if r.Err != nil { 40 return r.Err 41 } 42 s := c.c.c.Server(id) 43 if s == nil { 44 return ErrUnknownServer 45 } 46 s.RLock() //Needed? Path never gets changed! 47 p := s.Path 48 s.RUnlock() 49 50 var ( 51 f *os.File 52 err error 53 logPath string 54 ) 55 56 for _, lp := range logPaths { 57 logPath = path.Join(p, lp) 58 f, err = os.Open(logPath) 59 if err == nil { 60 break 61 } 62 } 63 if f == nil { 64 return errors.New("unable to open log file") 65 } 66 defer func() { 67 f.Close() 68 }() 69 70 fsw, err := fsnotify.NewWatcher() 71 if err != nil { 72 return err 73 } 74 logDir := path.Dir(logPath) 75 fsw.Add(logDir) 76 defer fsw.Remove(logDir) 77 78 pw := partWriter{w} 79 80 io.Copy(pw, f) 81 82 if w.Err != nil { 83 return w.Err 84 } 85 t := time.NewTimer(time.Second * 10) 86 for { 87 select { 88 case ev := <-fsw.Events: 89 switch ev.Op { 90 case fsnotify.Create: 91 if ev.Name == logPath { 92 f, err = os.Open(logPath) 93 if err != nil { 94 return err 95 } 96 } 97 case fsnotify.Write: 98 if ev.Name == logPath { 99 io.Copy(pw, f) 100 if w.Err != nil { 101 return w.Err 102 } 103 } 104 case fsnotify.Remove: 105 if ev.Name == logPath { 106 f.Close() 107 } 108 } 109 case err = <-fsw.Errors: 110 case <-t.C: 111 w.WriteUint8(2) //ping 112 if w.Err != nil { 113 return w.Err 114 } 115 } 116 t.Reset(time.Second * 10) 117 } 118 } 119 120 type partWriter struct { 121 *byteio.StickyLittleEndianWriter 122 } 123 124 func (pw partWriter) Write(p []byte) (int, error) { 125 l := len(p) 126 for len(p) > 0 { 127 b := p 128 if len(b) > 65535 { 129 b = p[:65535] 130 131 } 132 p = p[len(b):] 133 pw.WriteUint8(1) 134 pw.WriteUint16(uint16(len(b))) 135 pw.StickyLittleEndianWriter.Write(b) 136 } 137 return l, nil 138 }