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 --- ui/logview.go | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ui/ui.go | 16 +++++++- 2 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 ui/logview.go (limited to 'ui') 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