Fixed review comments
Fixed running as a windows service Added logging to windows evenlog
This commit is contained in:
parent
a4dc4c61d8
commit
448a6caeb8
|
@ -17,6 +17,11 @@
|
||||||
"WarnUnmatchedDirective": true,
|
"WarnUnmatchedDirective": true,
|
||||||
|
|
||||||
"EnableAll": true,
|
"EnableAll": true,
|
||||||
|
"DisableAll": false,
|
||||||
|
"Disable": [
|
||||||
|
"maligned",
|
||||||
|
"goconst"
|
||||||
|
],
|
||||||
|
|
||||||
"Cyclo": 20,
|
"Cyclo": 20,
|
||||||
"LineLength": 200
|
"LineLength": 200
|
||||||
|
|
22
README.md
22
README.md
|
@ -99,6 +99,17 @@ sudo ./AdGuardHome
|
||||||
|
|
||||||
Now open the browser and navigate to http://localhost:3000/ to control your AdGuard Home service.
|
Now open the browser and navigate to http://localhost:3000/ to control your AdGuard Home service.
|
||||||
|
|
||||||
|
### Running as a service
|
||||||
|
|
||||||
|
You can register AdGuard Home as a system service on Windows, Linux/(systemd | Upstart | SysV), and OSX/Launchd.
|
||||||
|
|
||||||
|
* `AdGuardHome -s install` - install AdGuard Home as a system service.
|
||||||
|
* `AdGuardHome -s uninstall` - uninstalls the AdGuard Home service.
|
||||||
|
* `AdGuardHome -s start` - starts the service.
|
||||||
|
* `AdGuardHome -s stop` - stops the service.
|
||||||
|
* `AdGuardHome -s restart` - restarts the service.
|
||||||
|
* `AdGuardHome -s status` - shows the current service status.
|
||||||
|
|
||||||
### Command-line arguments
|
### Command-line arguments
|
||||||
|
|
||||||
Here is a list of all available command-line arguments.
|
Here is a list of all available command-line arguments.
|
||||||
|
@ -119,15 +130,6 @@ Options:
|
||||||
-h, --help print this help
|
-h, --help print this help
|
||||||
```
|
```
|
||||||
|
|
||||||
Please note, that you can register AdGuard Home as a system service on Windows, Linux/(systemd | Upstart | SysV), and OSX/Launchd.
|
|
||||||
|
|
||||||
* `AdGuardHome -s install` - install as a system service.
|
|
||||||
* `AdGuardHome -s uninstall` - uninstall's AdGuard Home service.
|
|
||||||
* `AdGuardHome -s start` - starts the service.
|
|
||||||
* `AdGuardHome -s stop` - stops the service.
|
|
||||||
* `AdGuardHome -s restart` - restarts the service.
|
|
||||||
* `AdGuardHome -s status` - shows the current service status.
|
|
||||||
|
|
||||||
### Running without superuser
|
### Running without superuser
|
||||||
|
|
||||||
You can run AdGuard Home without superuser privileges, but you need to either grant the binary a capability (on Linux) or instruct it to use a different port (all platforms).
|
You can run AdGuard Home without superuser privileges, but you need to either grant the binary a capability (on Linux) or instruct it to use a different port (all platforms).
|
||||||
|
@ -198,7 +200,7 @@ Settings are stored in [YAML format](https://en.wikipedia.org/wiki/YAML), possib
|
||||||
* `range_end` - end IP address of the controlled range.
|
* `range_end` - end IP address of the controlled range.
|
||||||
* `lease_duration` - lease duration in seconds. If 0, using default duration (2 hours).
|
* `lease_duration` - lease duration in seconds. If 0, using default duration (2 hours).
|
||||||
* `user_rules` — User-specified filtering rules.
|
* `user_rules` — User-specified filtering rules.
|
||||||
* `log_file` — Path to the log file. If empty, writes to stdout, if 'syslog' -- system log.
|
* `log_file` — Path to the log file. If empty, writes to stdout, if `syslog` -- system log (or eventlog on Windows).
|
||||||
* `verbose` — Enable our disables debug verbose output.
|
* `verbose` — Enable our disables debug verbose output.
|
||||||
|
|
||||||
Removing an entry from settings file will reset it to the default value. Deleting the file will reset all settings to the default values.
|
Removing an entry from settings file will reset it to the default value. Deleting the file will reset all settings to the default values.
|
||||||
|
|
111
app.go
111
app.go
|
@ -4,17 +4,20 @@ import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
stdlog "log"
|
stdlog "log"
|
||||||
"log/syslog"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gobuffalo/packr"
|
"github.com/gobuffalo/packr"
|
||||||
|
|
||||||
"github.com/hmage/golibs/log"
|
"github.com/hmage/golibs/log"
|
||||||
"golang.org/x/crypto/ssh/terminal"
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
)
|
)
|
||||||
|
@ -22,6 +25,14 @@ import (
|
||||||
// VersionString will be set through ldflags, contains current version
|
// VersionString will be set through ldflags, contains current version
|
||||||
var VersionString = "undefined"
|
var VersionString = "undefined"
|
||||||
|
|
||||||
|
const (
|
||||||
|
// We use it to detect the working dir
|
||||||
|
executableName = "AdGuardHome"
|
||||||
|
|
||||||
|
// Used in config to indicate that syslog or eventlog (win) should be used for logger output
|
||||||
|
configSyslog = "syslog"
|
||||||
|
)
|
||||||
|
|
||||||
// main is the entry point
|
// main is the entry point
|
||||||
func main() {
|
func main() {
|
||||||
// config can be specified, which reads options from there, but other command line flags have to override config values
|
// config can be specified, which reads options from there, but other command line flags have to override config values
|
||||||
|
@ -33,9 +44,6 @@ func main() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// run the protection
|
|
||||||
run(args)
|
|
||||||
|
|
||||||
signalChannel := make(chan os.Signal)
|
signalChannel := make(chan os.Signal)
|
||||||
signal.Notify(signalChannel, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGQUIT)
|
signal.Notify(signalChannel, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGQUIT)
|
||||||
go func() {
|
go func() {
|
||||||
|
@ -44,23 +52,30 @@ func main() {
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
address := net.JoinHostPort(config.BindHost, strconv.Itoa(config.BindPort))
|
// run the protection
|
||||||
URL := fmt.Sprintf("http://%s", address)
|
run(args)
|
||||||
log.Println("Go to " + URL)
|
|
||||||
log.Fatal(http.ListenAndServe(address, nil))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// run initializes configuration and runs the AdGuard Home
|
// run initializes configuration and runs the AdGuard Home
|
||||||
|
// run is a blocking method and it won't exit until the service is stopped!
|
||||||
func run(args options) {
|
func run(args options) {
|
||||||
|
// config file path can be overridden by command-line arguments:
|
||||||
if args.configFilename != "" {
|
if args.configFilename != "" {
|
||||||
config.ourConfigFilename = args.configFilename
|
config.ourConfigFilename = args.configFilename
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// configure working dir and config path
|
||||||
|
initWorkingDir()
|
||||||
|
|
||||||
// configure log level and output
|
// configure log level and output
|
||||||
configureLogger(args)
|
configureLogger(args)
|
||||||
|
|
||||||
// print the first message after logger is configured
|
// print the first message after logger is configured
|
||||||
log.Printf("AdGuard Home, version %s\n", VersionString)
|
log.Printf("AdGuard Home, version %s\n", VersionString)
|
||||||
|
log.Printf("Current working directory is %s", config.ourBinaryDir)
|
||||||
|
if args.runningAsService {
|
||||||
|
log.Printf("AdGuard Home is running as a service")
|
||||||
|
}
|
||||||
|
|
||||||
err := askUsernamePasswordIfPossible()
|
err := askUsernamePasswordIfPossible()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -111,27 +126,6 @@ func run(args options) {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
box := packr.NewBox("build/static")
|
|
||||||
{
|
|
||||||
executable, osErr := os.Executable()
|
|
||||||
if osErr != nil {
|
|
||||||
panic(osErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
executableName := filepath.Base(executable)
|
|
||||||
if executableName == "AdGuardHome" {
|
|
||||||
// Binary build
|
|
||||||
config.ourBinaryDir = filepath.Dir(executable)
|
|
||||||
} else {
|
|
||||||
// Most likely we're debugging -- using current working directory in this case
|
|
||||||
workDir, _ := os.Getwd()
|
|
||||||
config.ourBinaryDir = workDir
|
|
||||||
}
|
|
||||||
log.Printf("Current working directory is %s", config.ourBinaryDir)
|
|
||||||
}
|
|
||||||
http.Handle("/", optionalAuthHandler(http.FileServer(box)))
|
|
||||||
registerControlHandlers()
|
|
||||||
|
|
||||||
err = startDNSServer()
|
err = startDNSServer()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
@ -153,6 +147,35 @@ func run(args options) {
|
||||||
}()
|
}()
|
||||||
// Schedule automatic filters updates
|
// Schedule automatic filters updates
|
||||||
go periodicallyRefreshFilters()
|
go periodicallyRefreshFilters()
|
||||||
|
|
||||||
|
// Initialize and run the admin Web interface
|
||||||
|
box := packr.NewBox("build/static")
|
||||||
|
http.Handle("/", optionalAuthHandler(http.FileServer(box)))
|
||||||
|
registerControlHandlers()
|
||||||
|
|
||||||
|
address := net.JoinHostPort(config.BindHost, strconv.Itoa(config.BindPort))
|
||||||
|
URL := fmt.Sprintf("http://%s", address)
|
||||||
|
log.Println("Go to " + URL)
|
||||||
|
log.Fatal(http.ListenAndServe(address, nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
// initWorkingDir initializes the ourBinaryDir (basically, we use it as a working dir)
|
||||||
|
func initWorkingDir() {
|
||||||
|
exec, err := os.Executable()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
currentExecutableName := filepath.Base(exec)
|
||||||
|
currentExecutableName = strings.TrimSuffix(currentExecutableName, path.Ext(currentExecutableName))
|
||||||
|
if currentExecutableName == executableName {
|
||||||
|
// Binary build
|
||||||
|
config.ourBinaryDir = filepath.Dir(exec)
|
||||||
|
} else {
|
||||||
|
// Most likely we're debugging -- using current working directory in this case
|
||||||
|
workDir, _ := os.Getwd()
|
||||||
|
config.ourBinaryDir = workDir
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// configureLogger configures logger level and output
|
// configureLogger configures logger level and output
|
||||||
|
@ -169,25 +192,30 @@ func configureLogger(args options) {
|
||||||
|
|
||||||
log.Verbose = ls.Verbose
|
log.Verbose = ls.Verbose
|
||||||
|
|
||||||
|
if args.runningAsService && ls.LogFile == "" && runtime.GOOS == "windows" {
|
||||||
|
// When running as a Windows service, use eventlog by default if nothing else is configured
|
||||||
|
// Otherwise, we'll simply loose the log output
|
||||||
|
ls.LogFile = configSyslog
|
||||||
|
}
|
||||||
|
|
||||||
if ls.LogFile == "" {
|
if ls.LogFile == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add windows eventlog support
|
if ls.LogFile == configSyslog {
|
||||||
if ls.LogFile == "syslog" {
|
// Use syslog where it is possible and eventlog on Windows
|
||||||
w, err := syslog.New(syslog.LOG_INFO, "AdGuard Home")
|
err := configureSyslog()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("cannot initialize syslog: %s", err)
|
log.Fatalf("cannot initialize syslog: %s", err)
|
||||||
}
|
}
|
||||||
stdlog.SetOutput(w)
|
} else {
|
||||||
|
logFilePath := filepath.Join(config.ourBinaryDir, ls.LogFile)
|
||||||
|
file, err := os.OpenFile(logFilePath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0755)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("cannot create a log file: %s", err)
|
||||||
|
}
|
||||||
|
stdlog.SetOutput(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
logFilePath := filepath.Join(config.ourBinaryDir, ls.LogFile)
|
|
||||||
file, err := os.OpenFile(logFilePath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0755)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("cannot create a log file: %s", err)
|
|
||||||
}
|
|
||||||
stdlog.SetOutput(file)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func cleanup() {
|
func cleanup() {
|
||||||
|
@ -221,6 +249,9 @@ type options struct {
|
||||||
|
|
||||||
// service control action (see service.ControlAction array + "status" command)
|
// service control action (see service.ControlAction array + "status" command)
|
||||||
serviceControlAction string
|
serviceControlAction string
|
||||||
|
|
||||||
|
// runningAsService flag is set to true when options are passed from the service runner
|
||||||
|
runningAsService bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadOptions reads command line arguments and initializes configuration
|
// loadOptions reads command line arguments and initializes configuration
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -9,6 +9,7 @@ require (
|
||||||
github.com/gobuffalo/packr v1.19.0
|
github.com/gobuffalo/packr v1.19.0
|
||||||
github.com/hmage/golibs v0.0.0-20181229160906-c8491df0bfc4
|
github.com/hmage/golibs v0.0.0-20181229160906-c8491df0bfc4
|
||||||
github.com/joomcode/errorx v0.1.0
|
github.com/joomcode/errorx v0.1.0
|
||||||
|
github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1 // indirect
|
||||||
github.com/kardianos/service v0.0.0-20181115005516-4c239ee84e7b
|
github.com/kardianos/service v0.0.0-20181115005516-4c239ee84e7b
|
||||||
github.com/krolaw/dhcp4 v0.0.0-20180925202202-7cead472c414
|
github.com/krolaw/dhcp4 v0.0.0-20180925202202-7cead472c414
|
||||||
github.com/miekg/dns v1.1.1
|
github.com/miekg/dns v1.1.1
|
||||||
|
@ -17,6 +18,7 @@ require (
|
||||||
go.uber.org/goleak v0.10.0
|
go.uber.org/goleak v0.10.0
|
||||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9
|
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9
|
||||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3
|
golang.org/x/net v0.0.0-20181220203305-927f97764cc3
|
||||||
|
golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb
|
||||||
gopkg.in/asaskevich/govalidator.v4 v4.0.0-20160518190739-766470278477
|
gopkg.in/asaskevich/govalidator.v4 v4.0.0-20160518190739-766470278477
|
||||||
gopkg.in/yaml.v2 v2.2.1
|
gopkg.in/yaml.v2 v2.2.1
|
||||||
)
|
)
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -37,6 +37,8 @@ github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||||
github.com/joomcode/errorx v0.1.0 h1:QmJMiI1DE1UFje2aI1ZWO/VMT5a32qBoXUclGOt8vsc=
|
github.com/joomcode/errorx v0.1.0 h1:QmJMiI1DE1UFje2aI1ZWO/VMT5a32qBoXUclGOt8vsc=
|
||||||
github.com/joomcode/errorx v0.1.0/go.mod h1:kgco15ekB6cs+4Xjzo7SPeXzx38PbJzBwbnu9qfVNHQ=
|
github.com/joomcode/errorx v0.1.0/go.mod h1:kgco15ekB6cs+4Xjzo7SPeXzx38PbJzBwbnu9qfVNHQ=
|
||||||
|
github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1 h1:PJPDf8OUfOK1bb/NeTKd4f1QXZItOX389VN3B6qC8ro=
|
||||||
|
github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
|
||||||
github.com/kardianos/service v0.0.0-20181115005516-4c239ee84e7b h1:vfiqKno48aUndBMjTeWFpCExNnTf2Xnd6d228L4EfTQ=
|
github.com/kardianos/service v0.0.0-20181115005516-4c239ee84e7b h1:vfiqKno48aUndBMjTeWFpCExNnTf2Xnd6d228L4EfTQ=
|
||||||
github.com/kardianos/service v0.0.0-20181115005516-4c239ee84e7b/go.mod h1:10UU/bEkzh2iEN6aYzbevY7J6p03KO5siTxQWXMEerg=
|
github.com/kardianos/service v0.0.0-20181115005516-4c239ee84e7b/go.mod h1:10UU/bEkzh2iEN6aYzbevY7J6p03KO5siTxQWXMEerg=
|
||||||
github.com/krolaw/dhcp4 v0.0.0-20180925202202-7cead472c414 h1:6wnYc2S/lVM7BvR32BM74ph7bPgqMztWopMYKgVyEho=
|
github.com/krolaw/dhcp4 v0.0.0-20180925202202-7cead472c414 h1:6wnYc2S/lVM7BvR32BM74ph7bPgqMztWopMYKgVyEho=
|
||||||
|
|
105
service.go
105
service.go
|
@ -2,11 +2,17 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
"github.com/hmage/golibs/log"
|
"github.com/hmage/golibs/log"
|
||||||
"github.com/kardianos/service"
|
"github.com/kardianos/service"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
launchdStdoutPath = "/var/log/AdGuardHome.stdout.log"
|
||||||
|
launchdStderrPath = "/var/log/AdGuardHome.stderr.log"
|
||||||
|
)
|
||||||
|
|
||||||
// Represents the program that will be launched by a service or daemon
|
// Represents the program that will be launched by a service or daemon
|
||||||
type program struct {
|
type program struct {
|
||||||
}
|
}
|
||||||
|
@ -14,7 +20,7 @@ type program struct {
|
||||||
// Start should quickly start the program
|
// Start should quickly start the program
|
||||||
func (p *program) Start(s service.Service) error {
|
func (p *program) Start(s service.Service) error {
|
||||||
// Start should not block. Do the actual work async.
|
// Start should not block. Do the actual work async.
|
||||||
args := options{}
|
args := options{runningAsService: true}
|
||||||
go run(args)
|
go run(args)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -23,6 +29,7 @@ func (p *program) Start(s service.Service) error {
|
||||||
func (p *program) Stop(s service.Service) error {
|
func (p *program) Stop(s service.Service) error {
|
||||||
// Stop should not block. Return with a few seconds.
|
// Stop should not block. Return with a few seconds.
|
||||||
cleanup()
|
cleanup()
|
||||||
|
os.Exit(0)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,6 +40,9 @@ func (p *program) Stop(s service.Service) error {
|
||||||
// start -- starts the previously installed service
|
// start -- starts the previously installed service
|
||||||
// stop -- stops the previously installed service
|
// stop -- stops the previously installed service
|
||||||
// restart - restarts the previously installed service
|
// restart - restarts the previously installed service
|
||||||
|
// run - this is a special command that is not supposed to be used directly
|
||||||
|
// it is specified when we register a service, and it indicates to the app
|
||||||
|
// that it is being run as a service/daemon.
|
||||||
func handleServiceControlAction(action string) {
|
func handleServiceControlAction(action string) {
|
||||||
log.Printf("Service control action: %s", action)
|
log.Printf("Service control action: %s", action)
|
||||||
|
|
||||||
|
@ -45,7 +55,9 @@ func handleServiceControlAction(action string) {
|
||||||
DisplayName: "AdGuard Home service",
|
DisplayName: "AdGuard Home service",
|
||||||
Description: "AdGuard Home: Network-level blocker",
|
Description: "AdGuard Home: Network-level blocker",
|
||||||
WorkingDirectory: pwd,
|
WorkingDirectory: pwd,
|
||||||
|
Arguments: []string{"-s", "run"},
|
||||||
}
|
}
|
||||||
|
configureService(svcConfig)
|
||||||
prg := &program{}
|
prg := &program{}
|
||||||
s, err := service.New(prg, svcConfig)
|
s, err := service.New(prg, svcConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -66,11 +78,100 @@ func handleServiceControlAction(action string) {
|
||||||
case service.StatusRunning:
|
case service.StatusRunning:
|
||||||
log.Printf("Service is running")
|
log.Printf("Service is running")
|
||||||
}
|
}
|
||||||
|
} else if action == "run" {
|
||||||
|
err = s.Run()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to run service: %s", err)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if action == "uninstall" {
|
||||||
|
// In case of Windows and Linux when a running service is being uninstalled,
|
||||||
|
// it is just marked for deletion but not stopped
|
||||||
|
// So we explicitly stop it here
|
||||||
|
_ = s.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
err = service.Control(s, action)
|
err = service.Control(s, action)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
log.Printf("Action %s has been done successfully", action)
|
log.Printf("Action %s has been done successfully on %s", action, service.ChosenSystem().String())
|
||||||
|
|
||||||
|
if action == "install" {
|
||||||
|
// Start automatically after install
|
||||||
|
err = service.Control(s, "start")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to start the service: %s", err)
|
||||||
|
}
|
||||||
|
log.Printf("Service has been started")
|
||||||
|
} else if action == "uninstall" {
|
||||||
|
cleanupService()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// configureService defines additional settings of the service
|
||||||
|
func configureService(c *service.Config) {
|
||||||
|
c.Option = service.KeyValue{}
|
||||||
|
|
||||||
|
// OS X
|
||||||
|
// Redefines the launchd config file template
|
||||||
|
// The purpose is to enable stdout/stderr redirect by default
|
||||||
|
c.Option["LaunchdConfig"] = launchdConfig
|
||||||
|
// This key is used to start the job as soon as it has been loaded. For daemons this means execution at boot time, for agents execution at login.
|
||||||
|
c.Option["RunAtLoad"] = true
|
||||||
|
|
||||||
|
// POSIX
|
||||||
|
// Redirect StdErr & StdOut to files.
|
||||||
|
c.Option["LogOutput"] = true
|
||||||
|
|
||||||
|
// Windows
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
c.UserName = "NT AUTHORITY\\NetworkService"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanupService called on the service uninstall, cleans up additional files if needed
|
||||||
|
func cleanupService() {
|
||||||
|
if runtime.GOOS == "darwin" {
|
||||||
|
// Removing log files on cleanup and ignore errors
|
||||||
|
err := os.Remove(launchdStdoutPath)
|
||||||
|
if err != nil && !os.IsNotExist(err) {
|
||||||
|
log.Printf("cannot remove %s", launchdStdoutPath)
|
||||||
|
}
|
||||||
|
err = os.Remove(launchdStderrPath)
|
||||||
|
if err != nil && !os.IsNotExist(err) {
|
||||||
|
log.Printf("cannot remove %s", launchdStderrPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basically the same template as the one defined in github.com/kardianos/service
|
||||||
|
// but with two additional keys - StandardOutPath and StandardErrorPath
|
||||||
|
var launchdConfig = `<?xml version='1.0' encoding='UTF-8'?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"
|
||||||
|
"http://www.apple.com/DTDs/PropertyList-1.0.dtd" >
|
||||||
|
<plist version='1.0'>
|
||||||
|
<dict>
|
||||||
|
<key>Label</key><string>{{html .Name}}</string>
|
||||||
|
<key>ProgramArguments</key>
|
||||||
|
<array>
|
||||||
|
<string>{{html .Path}}</string>
|
||||||
|
{{range .Config.Arguments}}
|
||||||
|
<string>{{html .}}</string>
|
||||||
|
{{end}}
|
||||||
|
</array>
|
||||||
|
{{if .UserName}}<key>UserName</key><string>{{html .UserName}}</string>{{end}}
|
||||||
|
{{if .ChRoot}}<key>RootDirectory</key><string>{{html .ChRoot}}</string>{{end}}
|
||||||
|
{{if .WorkingDirectory}}<key>WorkingDirectory</key><string>{{html .WorkingDirectory}}</string>{{end}}
|
||||||
|
<key>SessionCreate</key><{{bool .SessionCreate}}/>
|
||||||
|
<key>KeepAlive</key><{{bool .KeepAlive}}/>
|
||||||
|
<key>RunAtLoad</key><{{bool .RunAtLoad}}/>
|
||||||
|
<key>Disabled</key><false/>
|
||||||
|
<key>StandardOutPath</key>
|
||||||
|
<string>` + launchdStdoutPath + `</string>
|
||||||
|
<key>StandardErrorPath</key>
|
||||||
|
<string>` + launchdStderrPath + `</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
`
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
// +build !windows,!nacl,!plan9
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"log/syslog"
|
||||||
|
)
|
||||||
|
|
||||||
|
// configureSyslog reroutes standard logger output to syslog
|
||||||
|
func configureSyslog() error {
|
||||||
|
w, err := syslog.New(syslog.LOG_NOTICE|syslog.LOG_USER, "AdGuard Home")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.SetOutput(w)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
"golang.org/x/sys/windows/svc/eventlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
// should be the same as the service name!
|
||||||
|
const eventLogSrc = "AdGuardHome"
|
||||||
|
|
||||||
|
type eventLogWriter struct {
|
||||||
|
el *eventlog.Log
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write sends a log message to the Event Log.
|
||||||
|
func (w *eventLogWriter) Write(b []byte) (int, error) {
|
||||||
|
return len(b), w.el.Info(1, string(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
func configureSyslog() error {
|
||||||
|
// Continue if we receive "registry key already exists" or if we get
|
||||||
|
// ERROR_ACCESS_DENIED so that we can log without administrative permissions
|
||||||
|
// for pre-existing eventlog sources.
|
||||||
|
if err := eventlog.InstallAsEventCreate(eventLogSrc, eventlog.Info|eventlog.Warning|eventlog.Error); err != nil {
|
||||||
|
if !strings.Contains(err.Error(), "registry key already exists") && err != windows.ERROR_ACCESS_DENIED {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
el, err := eventlog.Open(eventLogSrc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.SetOutput(&eventLogWriter{el: el})
|
||||||
|
return nil
|
||||||
|
}
|
Loading…
Reference in New Issue