aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2021-05-17 16:58:24 +0200
committerJason A. Donenfeld <Jason@zx2c4.com>2021-06-18 11:28:50 +0200
commita55011ccd2452183461658575dc3fa7c474ae5fe (patch)
tree6591a83d940f3f1a233dc167ff4da1d0490e5beb
parentmanager: rewrite service arguments when migrating configs (diff)
downloadwireguard-windows-a55011ccd2452183461658575dc3fa7c474ae5fe.tar.xz
wireguard-windows-a55011ccd2452183461658575dc3fa7c474ae5fe.zip
main: log CLI to stderr/stdout
If stderr is available, log to stderr. Otherwise, log to stdout. Otherwise, fallback to message box GUI prompts for error strings. Requested-by: Andrew Burkett <burkett.andrew@gmail.com> Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
-rw-r--r--docs/enterprise.md22
-rw-r--r--main.go78
2 files changed, 57 insertions, 43 deletions
diff --git a/docs/enterprise.md b/docs/enterprise.md
index 3f75ad14..37a0ca44 100644
--- a/docs/enterprise.md
+++ b/docs/enterprise.md
@@ -8,7 +8,7 @@ While consumer users are generally directed toward [wireguard-installer.exe](htt
### Tunnel Service versus Manager Service and UI
-The "manager service" is responsible for displaying a UI on select users' desktops (in the system tray), and responding to requests from the UI to do things like add, remove, start, or stop tunnels. The "tunnel service" is a separate Windows service for each tunnel. These two services may be used together, or separately, as described below.
+The "manager service" is responsible for displaying a UI on select users' desktops (in the system tray), and responding to requests from the UI to do things like add, remove, start, or stop tunnels. The "tunnel service" is a separate Windows service for each tunnel. These two services may be used together, or separately, as described below. The various commands below will log errors and status to standard error, or, if standard error does not exist, to standard output.
### Tunnel Service
@@ -63,10 +63,10 @@ By default, the manager stops existing tunnels when starting new tunnels, so tha
### Diagnostic Logs
-The manager and all tunnel services produce diagnostic logs in a shared ringbuffer-based log. This is shown in the UI, and also can be dumped to a file using the command:
+The manager and all tunnel services produce diagnostic logs in a shared ringbuffer-based log. This is shown in the UI, and also can be dumped to standard out using the command:
```text
-> wireguard /dumplog C:\path\to\diagnostic\log.txt
+> wireguard /dumplog > C:\path\to\diagnostic\log.txt
```
### Updates
@@ -74,7 +74,13 @@ The manager and all tunnel services produce diagnostic logs in a shared ringbuff
Administrators are notified of updates within the UI and can update from within the UI, but updates can also be invoked at the command line using the command:
```text
-> wireguard /update C:\path\to\update\log.txt
+> wireguard /update
+```
+
+Or, to log the status of that command:
+
+```text
+> wireguard /update 2> C:\path\to\update\log.txt
```
### Wintun Adapters
@@ -82,5 +88,11 @@ Administrators are notified of updates within the UI and can update from within
The tunnel service creates a Wintun adapter at startup and destroys it at shutdown. It may be desirable, however, to remove all Wintun adapters created in WireGuard's pool and uninstall the driver if no other applications are using Wintun. This can be accomplished using the command:
```text
-> wireguard /removealladapters C:\path\to\removal\log.txt
+> wireguard /removealladapters
+```
+
+Or, to log the status of that command:
+
+```text
+> wireguard /removealladapters 2> C:\path\to\removal\log.txt
```
diff --git a/main.go b/main.go
index 5862d4e3..7aa00643 100644
--- a/main.go
+++ b/main.go
@@ -9,6 +9,7 @@ import (
"debug/pe"
"errors"
"fmt"
+ "io"
"log"
"os"
"strconv"
@@ -27,9 +28,25 @@ import (
"golang.zx2c4.com/wireguard/windows/updater"
)
+func setLogFile() {
+ logHandle, err := windows.GetStdHandle(windows.STD_ERROR_HANDLE)
+ if logHandle == 0 || err != nil {
+ logHandle, err = windows.GetStdHandle(windows.STD_OUTPUT_HANDLE)
+ }
+ if logHandle == 0 || err != nil {
+ log.SetOutput(io.Discard)
+ } else {
+ log.SetOutput(os.NewFile(uintptr(logHandle), "stderr"))
+ }
+}
+
func fatal(v ...interface{}) {
- windows.MessageBox(0, windows.StringToUTF16Ptr(fmt.Sprint(v...)), windows.StringToUTF16Ptr(l18n.Sprintf("Error")), windows.MB_ICONERROR)
- os.Exit(1)
+ if log.Writer() == io.Discard {
+ windows.MessageBox(0, windows.StringToUTF16Ptr(fmt.Sprint(v...)), windows.StringToUTF16Ptr(l18n.Sprintf("Error")), windows.MB_ICONERROR)
+ os.Exit(1)
+ } else {
+ log.Fatal(append([]interface{}{l18n.Sprintf("Error: ")}, v...))
+ }
}
func fatalf(format string, v ...interface{}) {
@@ -37,7 +54,11 @@ func fatalf(format string, v ...interface{}) {
}
func info(title string, format string, v ...interface{}) {
- windows.MessageBox(0, windows.StringToUTF16Ptr(l18n.Sprintf(format, v...)), windows.StringToUTF16Ptr(title), windows.MB_ICONINFORMATION)
+ if log.Writer() == io.Discard {
+ windows.MessageBox(0, windows.StringToUTF16Ptr(l18n.Sprintf(format, v...)), windows.StringToUTF16Ptr(title), windows.MB_ICONINFORMATION)
+ } else {
+ log.Printf(title+":\n"+format, v...)
+ }
}
func usage() {
@@ -50,9 +71,9 @@ func usage() {
"/managerservice",
"/tunnelservice CONFIG_PATH",
"/ui CMD_READ_HANDLE CMD_WRITE_HANDLE CMD_EVENT_HANDLE LOG_MAPPING_HANDLE",
- "/dumplog OUTPUT_PATH",
- "/update [LOG_FILE]",
- "/removealladapters [LOG_FILE]",
+ "/dumplog",
+ "/update",
+ "/removealladapters",
}
builder := strings.Builder{}
for _, flag := range flags {
@@ -133,6 +154,7 @@ func main() {
panic("failed to restrict dll search path")
}
+ setLogFile()
checkForWow64()
if len(os.Args) <= 1 {
@@ -246,13 +268,17 @@ func main() {
ui.RunUI()
return
case "/dumplog":
- if len(os.Args) != 3 {
+ if len(os.Args) != 2 {
usage()
}
- file, err := os.Create(os.Args[2])
+ outputHandle, err := windows.GetStdHandle(windows.STD_OUTPUT_HANDLE)
if err != nil {
fatal(err)
}
+ if outputHandle == 0 {
+ fatal("Stdout must be set")
+ }
+ file := os.NewFile(uintptr(outputHandle), "stdout")
defer file.Close()
err = ringlogger.DumpTo(file, true)
if err != nil {
@@ -260,21 +286,9 @@ func main() {
}
return
case "/update":
- if len(os.Args) != 2 && len(os.Args) != 3 {
+ if len(os.Args) != 2 {
usage()
}
- var f *os.File
- var err error
- if len(os.Args) == 2 {
- f = os.Stdout
- } else {
- f, err = os.Create(os.Args[2])
- if err != nil {
- fatal(err)
- }
- defer f.Close()
- }
- l := log.New(f, "", log.LstdFlags)
for progress := range updater.DownloadVerifyAndExecute(0) {
if len(progress.Activity) > 0 {
if progress.BytesTotal > 0 || progress.BytesDownloaded > 0 {
@@ -282,13 +296,13 @@ func main() {
if progress.BytesTotal > 0 {
percent = float64(progress.BytesDownloaded) / float64(progress.BytesTotal) * 100.0
}
- l.Printf("%s: %d/%d (%.2f%%)\n", progress.Activity, progress.BytesDownloaded, progress.BytesTotal, percent)
+ log.Printf("%s: %d/%d (%.2f%%)\n", progress.Activity, progress.BytesDownloaded, progress.BytesTotal, percent)
} else {
- l.Println(progress.Activity)
+ log.Println(progress.Activity)
}
}
if progress.Error != nil {
- l.Printf("Error: %v\n", progress.Error)
+ log.Printf("Error: %v\n", progress.Error)
}
if progress.Complete || progress.Error != nil {
return
@@ -296,24 +310,12 @@ func main() {
}
return
case "/removealladapters":
- if len(os.Args) != 2 && len(os.Args) != 3 {
+ if len(os.Args) != 2 {
usage()
}
- var f *os.File
- var err error
- if len(os.Args) == 2 {
- f = os.Stdout
- } else {
- f, err = os.Create(os.Args[2])
- if err != nil {
- fatal(err)
- }
- defer f.Close()
- }
- log.SetOutput(f)
rebootRequired, err := tun.WintunPool.DeleteDriver()
if err != nil {
- log.Printf("Error: %v\n", err)
+ fatal(err)
} else if rebootRequired {
log.Println("A reboot may be required")
}