From 076bce8727d3f02cc3cbf6cdfb560a758185aaf3 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Wed, 20 Mar 2019 02:18:05 -0600 Subject: ringlogger: introduce basic file ring logging Signed-off-by: Jason A. Donenfeld --- ringlogger/cli_test.go | 80 +++++++++++++++ ringlogger/global.go | 31 ++++++ ringlogger/ringlogger_windows.go | 207 +++++++++++++++++++++++++++++++++++++++ service/errors.go | 6 +- service/ipc_client.go | 5 + service/ipc_server.go | 6 ++ service/service_manager.go | 51 +++------- service/service_tunnel.go | 47 ++------- ui/logview.go | 128 ++++++++++++++++++++++++ ui/ui.go | 16 ++- 10 files changed, 498 insertions(+), 79 deletions(-) create mode 100644 ringlogger/cli_test.go create mode 100644 ringlogger/global.go create mode 100644 ringlogger/ringlogger_windows.go create mode 100644 ui/logview.go diff --git a/ringlogger/cli_test.go b/ringlogger/cli_test.go new file mode 100644 index 00000000..64057b34 --- /dev/null +++ b/ringlogger/cli_test.go @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +package ringlogger + +import ( + "fmt" + "os" + "sync" + "testing" + "time" +) + +func TestThreads(t *testing.T) { + wg := sync.WaitGroup{} + wg.Add(2) + go func() { + rl, err := NewRinglogger("ringlogger_test.bin", "ONE") + if err != nil { + t.Fatal(err) + } + for i := 0; i < 1024; i++ { + fmt.Fprintf(rl, "bla bla bla %d", i) + } + rl.Close() + wg.Done() + }() + go func() { + rl, err := NewRinglogger("ringlogger_test.bin", "TWO") + if err != nil { + t.Fatal(err) + } + for i := 1024; i < 2047; i++ { + fmt.Fprintf(rl, "bla bla bla %d", i) + } + rl.Close() + wg.Done() + }() + wg.Wait() +} + +func TestWriteText(t *testing.T) { + rl, err := NewRinglogger("ringlogger_test.bin", "TXT") + if err != nil { + t.Fatal(err) + } + if len(os.Args) != 3 { + t.Fatal("Should pass exactly one argument") + } + fmt.Fprintf(rl, os.Args[2]) + rl.Close() +} + +func TestDump(t *testing.T) { + rl, err := NewRinglogger("ringlogger_test.bin", "DMP") + if err != nil { + t.Fatal(err) + } + _, err = rl.WriteTo(os.Stdout) + if err != nil { + t.Fatal(err) + } + rl.Close() +} + +func TestFollow(t *testing.T) { + rl, err := NewRinglogger("ringlogger_test.bin", "FOL") + if err != nil { + t.Fatal(err) + } + cursor := CursorAll + for { + cursor = rl.FollowFromCursor(cursor, func(line string, stamp time.Time) { + fmt.Printf("%v: %s\n", stamp, line) + }) + time.Sleep(300 * time.Millisecond) + } +} diff --git a/ringlogger/global.go b/ringlogger/global.go new file mode 100644 index 00000000..da79b6de --- /dev/null +++ b/ringlogger/global.go @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +package ringlogger + +import ( + "golang.zx2c4.com/wireguard/windows/conf" + "log" + "path" +) + +var Global *Ringlogger + +func InitGlobalLogger(tag string) error { + if Global != nil { + return nil + } + root, err := conf.RootDirectory() + if err != nil { + return err + } + Global, err = NewRinglogger(path.Join(root, "log.bin"), tag) + if err != nil { + return err + } + log.SetOutput(Global) + log.SetFlags(0) + return nil +} diff --git a/ringlogger/ringlogger_windows.go b/ringlogger/ringlogger_windows.go new file mode 100644 index 00000000..1a49570c --- /dev/null +++ b/ringlogger/ringlogger_windows.go @@ -0,0 +1,207 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +package ringlogger + +import ( + "bytes" + "fmt" + "golang.org/x/sys/windows" + "io" + "os" + "runtime" + "sync/atomic" + "time" + "unsafe" +) + +const ( + maxLogLineLength = 512 + maxLines = 2048 + magic = 0xbadbabe +) + +type logLine struct { + timeNs int64 + line [maxLogLineLength]byte +} + +type logMem struct { + nextIndex uint32 + lines [maxLines]logLine + magic uint32 +} + +type Ringlogger struct { + tag string + file *os.File + mapping windows.Handle + log *logMem +} + +func NewRinglogger(filename string, tag string) (*Ringlogger, error) { + file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, 0600) + if err != nil { + return nil, err + } + err = file.Truncate(int64(unsafe.Sizeof(logMem{}))) + if err != nil { + return nil, err + } + mapping, err := windows.CreateFileMapping(windows.Handle(file.Fd()), nil, windows.PAGE_READWRITE, 0, 0, nil) + if err != nil { + return nil, err + } + view, err := windows.MapViewOfFile(mapping, windows.FILE_MAP_WRITE, 0, 0, 0) + if err != nil { + return nil, err + } + if err != nil { + windows.CloseHandle(mapping) + return nil, err + } + log := (*logMem)(unsafe.Pointer(view)) + if log.magic != magic { + bytes := (*[unsafe.Sizeof(logMem{})]byte)(unsafe.Pointer(log)) + for i := range bytes { + bytes[i] = 0 + } + log.magic = magic + windows.FlushViewOfFile(view, uintptr(len(bytes))) + } + + rl := &Ringlogger{ + tag: tag, + file: file, + mapping: mapping, + log: log, + } + runtime.SetFinalizer(rl, (*Ringlogger).Close) + return rl, nil +} + +func (rl *Ringlogger) Write(p []byte) (n int, err error) { + // Race: This isn't synchronized with the fetch_add below, so items might be slightly out of order. + ts := time.Now().UnixNano() + + if rl.log == nil { + return 0, io.EOF + } + + // Race: More than maxLines writers and this will clash. + index := atomic.AddUint32(&rl.log.nextIndex, 1) - 1 + line := &rl.log.lines[index%maxLines] + + // Race: Before this line executes, we'll display old data after new data. + atomic.StoreInt64(&line.timeNs, 0) + for i := range line.line { + line.line[i] = 0 + } + + text := []byte(fmt.Sprintf("[%s] %s", rl.tag, bytes.TrimSpace(p))) + if len(text) > maxLogLineLength-1 { + text = text[:maxLogLineLength-1] + } + line.line[len(text)] = 0 + copy(line.line[:], text[:]) + atomic.StoreInt64(&line.timeNs, ts) + + windows.FlushViewOfFile((uintptr)(unsafe.Pointer(&rl.log.nextIndex)), unsafe.Sizeof(rl.log.nextIndex)) + windows.FlushViewOfFile((uintptr)(unsafe.Pointer(line)), unsafe.Sizeof(*line)) + + return len(p), nil +} + +func (rl *Ringlogger) WriteTo(out io.Writer) (n int64, err error) { + if rl.log == nil { + return 0, io.EOF + } + log := *rl.log + i := log.nextIndex + for l := 0; l < maxLines; l++ { + line := &log.lines[i%maxLines] + if line.timeNs == 0 { + i++ + continue + } + index := bytes.IndexByte(line.line[:], 0) + if index < 1 { + i++ + continue + } + var bytes int + bytes, err = fmt.Fprintf(out, "%s: %s\n", time.Unix(0, line.timeNs).Format("2006-01-02 15:04:05.000000"), line.line[:index]) + if err != nil { + return + } + n += int64(bytes) + i++ + } + return +} + +const CursorAll = ^uint32(0) + +type FollowLine struct { + Line string + Stamp time.Time +} + +func (rl *Ringlogger) FollowFromCursor(cursor uint32) ([]FollowLine, uint32) { + followLines := make([]FollowLine, 0, maxLines) + + if rl.log == nil { + return followLines, cursor + } + + log := *rl.log + i := cursor + + if i == CursorAll { + i = log.nextIndex + } + + for l := 0; l < maxLines; l++ { + line := &log.lines[i%maxLines] + if cursor != CursorAll && i%maxLines == log.nextIndex%maxLines { + break + } + if line.timeNs == 0 { + if cursor == CursorAll { + i++ + continue + } else { + break + } + } + index := bytes.IndexByte(line.line[:], 0) + if index > 0 { + followLines = append(followLines, FollowLine{string(line.line[:index]), time.Unix(0, line.timeNs)}) + } + i++ + cursor = i % maxLines + } + return followLines, cursor +} + +func (rl *Ringlogger) Close() error { + if rl.file != nil { + rl.file.Close() + rl.file = nil + } + if rl.log != nil { + windows.UnmapViewOfFile((uintptr)(unsafe.Pointer(rl.log))) + rl.log = nil + } + if rl.mapping != 0 { + windows.CloseHandle(rl.mapping) + rl.mapping = 0 + } + return nil +} + +func (rl *Ringlogger) Filename() string { + return rl.file.Name() +} diff --git a/service/errors.go b/service/errors.go index fd6bc6ab..02ca33dc 100644 --- a/service/errors.go +++ b/service/errors.go @@ -15,7 +15,7 @@ type Error uint32 const ( ErrorSuccess Error = iota - ErrorEventlogOpen + ErrorRingloggerOpen ErrorLoadConfiguration ErrorCreateWintun ErrorDetermineWintunName @@ -36,8 +36,8 @@ func (e Error) Error() string { switch e { case ErrorSuccess: return "No error." - case ErrorEventlogOpen: - return "Unable to open Windows event log." + case ErrorRingloggerOpen: + return "Unable to open log file." case ErrorDetermineExecutablePath: return "Unable to determine path of running executable." case ErrorLoadConfiguration: diff --git a/service/ipc_client.go b/service/ipc_client.go index e6295b91..7e260ea7 100644 --- a/service/ipc_client.go +++ b/service/ipc_client.go @@ -141,6 +141,11 @@ func IPCClientQuit(stopTunnelsOnQuit bool) (bool, error) { return alreadyQuit, rpcClient.Call("ManagerService.Quit", stopTunnelsOnQuit, &alreadyQuit) } +func IPCClientLogFilePath() (string, error) { + var path string + return path, rpcClient.Call("ManagerService.LogFilePath", uintptr(0), &path) +} + func IPCClientRegisterTunnelChange(cb func(tunnel *Tunnel, state TunnelState, err error)) *TunnelChangeCallback { s := &TunnelChangeCallback{cb} tunnelChangeCallbacks[s] = true diff --git a/service/ipc_server.go b/service/ipc_server.go index 17ea67c2..079dc85c 100644 --- a/service/ipc_server.go +++ b/service/ipc_server.go @@ -11,6 +11,7 @@ import ( "github.com/Microsoft/go-winio" "golang.org/x/sys/windows/svc" "golang.zx2c4.com/wireguard/windows/conf" + "golang.zx2c4.com/wireguard/windows/ringlogger" "io/ioutil" "net/rpc" "os" @@ -206,6 +207,11 @@ func (s *ManagerService) Quit(stopTunnelsOnQuit bool, alreadyQuit *bool) error { return nil } +func (s *ManagerService) LogFilePath(unused uintptr, filepath *string) error { + *filepath = ringlogger.Global.Filename() + return nil +} + func IPCServerListen(reader *os.File, writer *os.File, events *os.File) error { service := &ManagerService{events: events} diff --git a/service/service_manager.go b/service/service_manager.go index 5f9758ec..9fffbfe3 100644 --- a/service/service_manager.go +++ b/service/service_manager.go @@ -6,15 +6,13 @@ package service import ( - "fmt" "golang.org/x/sys/windows" "golang.org/x/sys/windows/svc" - "golang.org/x/sys/windows/svc/eventlog" "golang.zx2c4.com/wireguard/windows/conf" + "golang.zx2c4.com/wireguard/windows/ringlogger" "log" "os" "runtime/debug" - "strconv" "sync" "syscall" "unsafe" @@ -59,21 +57,9 @@ type wellKnownSidType uint32 type managerService struct{} -type elogger struct { - *eventlog.Log -} - -func (elog elogger) Write(p []byte) (n int, err error) { - msg := string(p) - n = len(msg) - err = elog.Warning(1, msg) - return -} - func (service *managerService) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (svcSpecificEC bool, exitCode uint32) { changes <- svc.Status{State: svc.StartPending} - var elog *eventlog.Log var err error serviceError := ErrorSuccess @@ -81,26 +67,19 @@ func (service *managerService) Execute(args []string, r <-chan svc.ChangeRequest svcSpecificEC, exitCode = determineErrorCode(err, serviceError) logErr := combineErrors(err, serviceError) if logErr != nil { - if elog != nil { - elog.Error(1, logErr.Error()) - } else { - fmt.Println(logErr.Error()) - } + log.Print(logErr) } changes <- svc.Status{State: svc.StopPending} }() - //TODO: remember to clean this up in the msi uninstaller - eventlog.InstallAsEventCreate("WireGuard", eventlog.Info|eventlog.Warning|eventlog.Error) - elog, err = eventlog.Open("WireGuard") + err = ringlogger.InitGlobalLogger("MGR") if err != nil { - serviceError = ErrorEventlogOpen + serviceError = ErrorRingloggerOpen return } - log.SetOutput(elogger{elog}) defer func() { if x := recover(); x != nil { - elog.Error(1, fmt.Sprintf("%v:\n%s", x, string(debug.Stack()))) + log.Printf("%v:\n%s", x, string(debug.Stack())) panic(x) } }() @@ -148,7 +127,7 @@ func (service *managerService) Execute(args []string, r <-chan svc.ChangeRequest //TODO: Isn't it better to use an impersonation token and userToken.IsMember instead? gs, err := userToken.GetTokenGroups() if err != nil { - elog.Error(1, "Unable to lookup user groups from token: "+err.Error()) + log.Printf("Unable to lookup user groups from token: %v", err) return } p := unsafe.Pointer(&gs.Groups[0]) @@ -167,12 +146,12 @@ func (service *managerService) Execute(args []string, r <-chan svc.ChangeRequest user, err := userToken.GetTokenUser() if err != nil { - elog.Error(1, "Unable to lookup user from token: "+err.Error()) + log.Printf("Unable to lookup user from token: %v", err) return } username, domain, accType, err := user.User.Sid.LookupAccount("") if err != nil { - elog.Error(1, "Unable to lookup username from sid: "+err.Error()) + log.Printf("Unable to lookup username from sid: %v", err) return } if accType != windows.SidTypeUser { @@ -181,17 +160,17 @@ func (service *managerService) Execute(args []string, r <-chan svc.ChangeRequest ourReader, theirReader, theirReaderStr, ourWriter, theirWriter, theirWriterStr, err := inheritableSocketpairEmulation() if err != nil { - elog.Error(1, "Unable to create two inheritable pipes: "+err.Error()) + log.Printf("Unable to create two inheritable pipes: %v", err) return } ourEvents, theirEvents, theirEventStr, err := inheritableEvents() err = IPCServerListen(ourReader, ourWriter, ourEvents) if err != nil { - elog.Error(1, "Unable to listen on IPC pipes: "+err.Error()) + log.Printf("Unable to listen on IPC pipes: %v", err) return } - elog.Info(1, "Starting UI process for user: "+username+", domain: "+domain) + log.Printf("Starting UI process for user: '%s@%s'", username, domain) attr := &os.ProcAttr{ Sys: &syscall.SysProcAttr{ Token: syscall.Token(userToken), @@ -203,7 +182,7 @@ func (service *managerService) Execute(args []string, r <-chan svc.ChangeRequest theirWriter.Close() theirEvents.Close() if err != nil { - elog.Error(1, "Unable to start manager UI process: "+err.Error()) + log.Printf("Unable to start manager UI process: %v", err) return } @@ -260,7 +239,7 @@ loop: } sessionNotification := (*wtsSessionNotification)(unsafe.Pointer(c.EventData)) if uintptr(sessionNotification.size) != unsafe.Sizeof(*sessionNotification) { - elog.Error(1, "Unexpected size of WTSSESSION_NOTIFICATION: "+strconv.Itoa(int(sessionNotification.size))) + log.Printf("Unexpected size of WTSSESSION_NOTIFICATION: %d", sessionNotification.size) continue } if c.EventType == wtsSessionLogoff { @@ -278,7 +257,7 @@ loop: } default: - elog.Info(1, fmt.Sprintf("Unexpected service control request #%d\n", c)) + log.Printf("Unexpected service control request #%d", c) } } } @@ -292,7 +271,7 @@ loop: if uninstall { err = UninstallManager() if err != nil { - elog.Error(1, "Unable to uninstaller manager when quitting: "+err.Error()) + log.Printf("Unable to uninstaller manager when quitting: %v", err) } } return diff --git a/service/service_tunnel.go b/service/service_tunnel.go index 2e615932..f8c843f6 100644 --- a/service/service_tunnel.go +++ b/service/service_tunnel.go @@ -9,36 +9,18 @@ import ( "bufio" "fmt" "golang.org/x/sys/windows/svc" - "golang.org/x/sys/windows/svc/eventlog" "golang.zx2c4.com/winipcfg" "golang.zx2c4.com/wireguard/device" "golang.zx2c4.com/wireguard/ipc" "golang.zx2c4.com/wireguard/tun" "golang.zx2c4.com/wireguard/windows/conf" + "golang.zx2c4.com/wireguard/windows/ringlogger" "log" "net" "runtime/debug" "strings" ) -type confElogger struct { - elog *eventlog.Log - conf *conf.Config - level int -} - -func (elog confElogger) Write(p []byte) (n int, err error) { - msg := elog.conf.Name + ": " + string(p) - n = len(msg) - switch elog.level { - case 1, 2: - err = elog.elog.Info(1, msg) - case 3: - err = elog.elog.Error(1, msg) - } - return -} - type tunnelService struct { path string } @@ -49,7 +31,6 @@ func (service *tunnelService) Execute(args []string, r <-chan svc.ChangeRequest, var dev *device.Device var uapi net.Listener var routeChangeCallback *winipcfg.RouteChangeCallback - var elog *eventlog.Log var logger *device.Logger var err error serviceError := ErrorSuccess @@ -59,11 +40,9 @@ func (service *tunnelService) Execute(args []string, r <-chan svc.ChangeRequest, logErr := combineErrors(err, serviceError) if logErr != nil { if logger != nil { - logger.Error.Println(logErr.Error()) - } else if elog != nil { - elog.Error(1, logErr.Error()) + logger.Error.Print(logErr) } else { - fmt.Println(logErr.Error()) + log.Print(logErr) } } changes <- svc.Status{State: svc.StopPending} @@ -76,22 +55,17 @@ func (service *tunnelService) Execute(args []string, r <-chan svc.ChangeRequest, if dev != nil { dev.Close() } - if elog != nil { - elog.Info(1, "Shutting down") - } + log.Print("Shutting down") }() - //TODO: remember to clean this up in the msi uninstaller - eventlog.InstallAsEventCreate("WireGuard", eventlog.Info|eventlog.Warning|eventlog.Error) - elog, err = eventlog.Open("WireGuard") + err = ringlogger.InitGlobalLogger("TUN") if err != nil { - serviceError = ErrorEventlogOpen + serviceError = ErrorRingloggerOpen return } - log.SetOutput(elogger{elog}) defer func() { if x := recover(); x != nil { - elog.Error(1, fmt.Sprintf("%v:\n%s", x, string(debug.Stack()))) + log.Printf("%v:\n%s", x, string(debug.Stack())) panic(x) } }() @@ -102,11 +76,8 @@ func (service *tunnelService) Execute(args []string, r <-chan svc.ChangeRequest, return } - logger = &device.Logger{ - Debug: log.New(&confElogger{elog: elog, conf: conf, level: 1}, "", 0), - Info: log.New(&confElogger{elog: elog, conf: conf, level: 2}, "", 0), - Error: log.New(&confElogger{elog: elog, conf: conf, level: 3}, "", 0), - } + stdLog := log.New(ringlogger.Global, fmt.Sprintf("[%s] ", conf.Name), 0) + logger = &device.Logger{stdLog, stdLog, stdLog} logger.Info.Println("Starting wireguard-go version", device.WireGuardGoVersion) logger.Debug.Println("Debug log enabled") diff --git a/ui/logview.go b/ui/logview.go new file mode 100644 index 00000000..e7a8a237 --- /dev/null +++ b/ui/logview.go @@ -0,0 +1,128 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +package ui + +import ( + "errors" + "fmt" + "github.com/lxn/walk" + "github.com/lxn/win" + "golang.zx2c4.com/wireguard/windows/ringlogger" + "strings" + "syscall" + "time" + "unsafe" +) + +type LogView struct { + walk.WidgetBase + logChan chan string +} + +const ( + TEM_APPENDTEXT = win.WM_USER + 6 +) + +func NewLogView(parent walk.Container, rl *ringlogger.Ringlogger) (*LogView, error) { + lc := make(chan string, 1024) + lv := &LogView{logChan: lc} + + if err := walk.InitWidget( + lv, + parent, + "EDIT", + win.WS_TABSTOP|win.WS_VISIBLE|win.WS_VSCROLL|win.ES_MULTILINE|win.ES_WANTRETURN, + win.WS_EX_CLIENTEDGE); err != nil { + return nil, err + } + lv.setReadOnly(true) + lv.SendMessage(win.EM_SETLIMITTEXT, 4294967295, 0) + + go func() { + var lines []ringlogger.FollowLine + cursor := ringlogger.CursorAll + for { + if lv.IsDisposed() { + return + } + lines, cursor = rl.FollowFromCursor(cursor) + sb := strings.Builder{} + for _, line := range lines { + sb.WriteString(fmt.Sprintf("%s: %s\r\n", line.Stamp.Format("2006-01-02 15:04:05.000000"), strings.ReplaceAll(line.Line, "\n", "\r\n"))) + } + newLines := sb.String() + if len(newLines) > 0 { + lv.AppendText(newLines) + } + time.Sleep(300 * time.Millisecond) + } + }() + return lv, nil +} + +func (*LogView) LayoutFlags() walk.LayoutFlags { + return walk.ShrinkableHorz | walk.ShrinkableVert | walk.GrowableHorz | walk.GrowableVert | walk.GreedyHorz | walk.GreedyVert +} + +func (*LogView) MinSizeHint() walk.Size { + return walk.Size{20, 12} +} + +func (*LogView) SizeHint() walk.Size { + return walk.Size{100, 100} +} + +func (lv *LogView) setTextSelection(start, end int) { + lv.SendMessage(win.EM_SETSEL, uintptr(start), uintptr(end)) +} + +func (lv *LogView) textLength() int { + return int(lv.SendMessage(0x000E, uintptr(0), uintptr(0))) +} + +func (lv *LogView) AppendText(value string) { + textLength := lv.textLength() + lv.setTextSelection(textLength, textLength) + lv.SendMessage(win.EM_REPLACESEL, 0, uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(value)))) +} + +func (lv *LogView) setReadOnly(readOnly bool) error { + if 0 == lv.SendMessage(win.EM_SETREADONLY, uintptr(win.BoolToBOOL(readOnly)), 0) { + return errors.New("fail to call EM_SETREADONLY") + } + + return nil +} + +func (lv *LogView) PostAppendText(value string) { + lv.logChan <- value + win.PostMessage(lv.Handle(), TEM_APPENDTEXT, 0, 0) +} + +func (lv *LogView) Write(p []byte) (int, error) { + lv.PostAppendText(string(p) + "\r\n") + return len(p), nil +} + +func (lv *LogView) WndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) uintptr { + switch msg { + case win.WM_GETDLGCODE: + if wParam == win.VK_RETURN { + return win.DLGC_WANTALLKEYS + } + + return win.DLGC_HASSETSEL | win.DLGC_WANTARROWS | win.DLGC_WANTCHARS + case TEM_APPENDTEXT: + select { + case value := <-lv.logChan: + lv.AppendText(value) + default: + return 0 + } + } + + return lv.WidgetBase.WndProc(hwnd, msg, wParam, lParam) +} diff --git a/ui/ui.go b/ui/ui.go index 7bc49489..bbae9980 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -10,6 +10,7 @@ import ( "github.com/lxn/walk" "github.com/lxn/win" "golang.zx2c4.com/wireguard/windows/conf" + "golang.zx2c4.com/wireguard/windows/ringlogger" "golang.zx2c4.com/wireguard/windows/service" "golang.zx2c4.com/wireguard/windows/ui/syntax" "os" @@ -54,7 +55,7 @@ func RunUI() { tray.SetToolTip("WireGuard: Deactivated") tray.SetVisible(true) - mw.SetSize(walk.Size{900, 800}) + mw.SetSize(walk.Size{900, 1400}) mw.SetLayout(walk.NewVBoxLayout()) mw.SetIcon(icon) mw.Closing().Attach(func(canceled *bool, reason walk.CloseReason) { @@ -178,6 +179,17 @@ func RunUI() { restoreState = false }) + logfile, err := service.IPCClientLogFilePath() + var logger *ringlogger.Ringlogger + if err == nil { + logger, err = ringlogger.NewRinglogger(logfile, "GUI") + } + if err != nil { + walk.MsgBox(nil, "Unable to initialize logging", fmt.Sprintf("%v\n\nFile: %s", err, logfile), walk.MsgBoxIconError) + return + } + NewLogView(mw, logger) + quitAction := walk.NewAction() quitAction.SetText("Exit") quit = func() { @@ -249,7 +261,7 @@ func RunUI() { setServiceState(tunnel, state, err == nil) if err != nil { if mw.Visible() { - walk.MsgBox(mw, "Tunnel Error", err.Error()+"\n\nPlease consult the Windows Event Log for more information.", walk.MsgBoxIconWarning) + walk.MsgBox(mw, "Tunnel Error", err.Error()+"\n\nPlease consult the log for more information.", walk.MsgBoxIconWarning) } else { tray.ShowError("WireGuard Tunnel Error", err.Error()) } -- cgit v1.2.3-59-g8ed1b