wireguard-go/main.go
Josh Bleecher Snyder 7139279cd0 device: change logging interface to use functions
This commit overhauls wireguard-go's logging.

The primary, motivating change is to use a function instead
of a *log.Logger as the basic unit of logging.
Using functions provides a lot more flexibility for
people to bring their own logging system.

It also introduces logging helper methods on Device.
These reduce line noise at the call site.
They also allow for log functions to be nil;
when nil, instead of generating a log line and throwing it away,
we don't bother generating it at all.
This spares allocation and pointless work.

This is a breaking change, although the fix required
of clients is fairly straightforward.

Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2021-01-26 22:40:20 +01:00

269 lines
6.0 KiB
Go

// +build !windows
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2020 WireGuard LLC. All Rights Reserved.
*/
package main
import (
"fmt"
"os"
"os/signal"
"runtime"
"strconv"
"syscall"
"golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/ipc"
"golang.zx2c4.com/wireguard/tun"
)
const (
ExitSetupSuccess = 0
ExitSetupFailed = 1
)
const (
ENV_WG_TUN_FD = "WG_TUN_FD"
ENV_WG_UAPI_FD = "WG_UAPI_FD"
ENV_WG_PROCESS_FOREGROUND = "WG_PROCESS_FOREGROUND"
)
func printUsage() {
fmt.Printf("usage:\n")
fmt.Printf("%s [-f/--foreground] INTERFACE-NAME\n", os.Args[0])
}
func warning() {
if runtime.GOOS != "linux" || os.Getenv(ENV_WG_PROCESS_FOREGROUND) == "1" {
return
}
fmt.Fprintln(os.Stderr, "┌───────────────────────────────────────────────────┐")
fmt.Fprintln(os.Stderr, "│ │")
fmt.Fprintln(os.Stderr, "│ Running this software on Linux is unnecessary, │")
fmt.Fprintln(os.Stderr, "│ because the Linux kernel has built-in first │")
fmt.Fprintln(os.Stderr, "│ class support for WireGuard, which will be │")
fmt.Fprintln(os.Stderr, "│ faster, slicker, and better integrated. For │")
fmt.Fprintln(os.Stderr, "│ information on installing the kernel module, │")
fmt.Fprintln(os.Stderr, "│ please visit: <https://wireguard.com/install>. │")
fmt.Fprintln(os.Stderr, "│ │")
fmt.Fprintln(os.Stderr, "└───────────────────────────────────────────────────┘")
}
func main() {
if len(os.Args) == 2 && os.Args[1] == "--version" {
fmt.Printf("wireguard-go v%s\n\nUserspace WireGuard daemon for %s-%s.\nInformation available at https://www.wireguard.com.\nCopyright (C) Jason A. Donenfeld <Jason@zx2c4.com>.\n", device.WireGuardGoVersion, runtime.GOOS, runtime.GOARCH)
return
}
warning()
var foreground bool
var interfaceName string
if len(os.Args) < 2 || len(os.Args) > 3 {
printUsage()
return
}
switch os.Args[1] {
case "-f", "--foreground":
foreground = true
if len(os.Args) != 3 {
printUsage()
return
}
interfaceName = os.Args[2]
default:
foreground = false
if len(os.Args) != 2 {
printUsage()
return
}
interfaceName = os.Args[1]
}
if !foreground {
foreground = os.Getenv(ENV_WG_PROCESS_FOREGROUND) == "1"
}
// get log level (default: info)
logLevel := func() int {
switch os.Getenv("LOG_LEVEL") {
case "debug":
return device.LogLevelDebug
case "info":
return device.LogLevelInfo
case "error":
return device.LogLevelError
case "silent":
return device.LogLevelSilent
}
return device.LogLevelInfo
}()
// open TUN device (or use supplied fd)
tun, err := func() (tun.Device, error) {
tunFdStr := os.Getenv(ENV_WG_TUN_FD)
if tunFdStr == "" {
return tun.CreateTUN(interfaceName, device.DefaultMTU)
}
// construct tun device from supplied fd
fd, err := strconv.ParseUint(tunFdStr, 10, 32)
if err != nil {
return nil, err
}
err = syscall.SetNonblock(int(fd), true)
if err != nil {
return nil, err
}
file := os.NewFile(uintptr(fd), "")
return tun.CreateTUNFromFile(file, device.DefaultMTU)
}()
if err == nil {
realInterfaceName, err2 := tun.Name()
if err2 == nil {
interfaceName = realInterfaceName
}
}
logger := device.NewLogger(
logLevel,
fmt.Sprintf("(%s) ", interfaceName),
)
logger.Infof("Starting wireguard-go version %v", device.WireGuardGoVersion)
logger.Debugf("Debug log enabled")
if err != nil {
logger.Errorf("Failed to create TUN device: %v", err)
os.Exit(ExitSetupFailed)
}
// open UAPI file (or use supplied fd)
fileUAPI, err := func() (*os.File, error) {
uapiFdStr := os.Getenv(ENV_WG_UAPI_FD)
if uapiFdStr == "" {
return ipc.UAPIOpen(interfaceName)
}
// use supplied fd
fd, err := strconv.ParseUint(uapiFdStr, 10, 32)
if err != nil {
return nil, err
}
return os.NewFile(uintptr(fd), ""), nil
}()
if err != nil {
logger.Errorf("UAPI listen error: %v", err)
os.Exit(ExitSetupFailed)
return
}
// daemonize the process
if !foreground {
env := os.Environ()
env = append(env, fmt.Sprintf("%s=3", ENV_WG_TUN_FD))
env = append(env, fmt.Sprintf("%s=4", ENV_WG_UAPI_FD))
env = append(env, fmt.Sprintf("%s=1", ENV_WG_PROCESS_FOREGROUND))
files := [3]*os.File{}
if os.Getenv("LOG_LEVEL") != "" && logLevel != device.LogLevelSilent {
files[0], _ = os.Open(os.DevNull)
files[1] = os.Stdout
files[2] = os.Stderr
} else {
files[0], _ = os.Open(os.DevNull)
files[1], _ = os.Open(os.DevNull)
files[2], _ = os.Open(os.DevNull)
}
attr := &os.ProcAttr{
Files: []*os.File{
files[0], // stdin
files[1], // stdout
files[2], // stderr
tun.File(),
fileUAPI,
},
Dir: ".",
Env: env,
}
path, err := os.Executable()
if err != nil {
logger.Errorf("Failed to determine executable: %v", err)
os.Exit(ExitSetupFailed)
}
process, err := os.StartProcess(
path,
os.Args,
attr,
)
if err != nil {
logger.Errorf("Failed to daemonize: %v", err)
os.Exit(ExitSetupFailed)
}
process.Release()
return
}
device := device.NewDevice(tun, logger)
logger.Infof("Device started")
errs := make(chan error)
term := make(chan os.Signal, 1)
uapi, err := ipc.UAPIListen(interfaceName, fileUAPI)
if err != nil {
logger.Errorf("Failed to listen on uapi socket: %v", err)
os.Exit(ExitSetupFailed)
}
go func() {
for {
conn, err := uapi.Accept()
if err != nil {
errs <- err
return
}
go device.IpcHandle(conn)
}
}()
logger.Infof("UAPI listener started")
// wait for program to terminate
signal.Notify(term, syscall.SIGTERM)
signal.Notify(term, os.Interrupt)
select {
case <-term:
case <-errs:
case <-device.Wait():
}
// clean up
uapi.Close()
device.Close()
logger.Infof("Shutting down")
}