Merge: app: add '--pidfile FILE' command-line parameter

Close #609

* commit '7746a3e6a9f204e7b7ab498c2f484cf426fb1a18':
  + app: add '--pidfile FILE' command-line parameter
This commit is contained in:
Simon Zolin 2019-04-15 15:36:22 +03:00
commit 7911a24dc8
2 changed files with 32 additions and 4 deletions

35
app.go
View File

@ -3,6 +3,7 @@ package main
import ( import (
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"io/ioutil"
"net" "net"
"net/http" "net/http"
"os" "os"
@ -25,6 +26,7 @@ var httpsServer struct {
cond *sync.Cond // reacts to config.TLS.Enabled, PortHTTPS, CertificateChain and PrivateKey cond *sync.Cond // reacts to config.TLS.Enabled, PortHTTPS, CertificateChain and PrivateKey
sync.Mutex // protects config.TLS sync.Mutex // protects config.TLS
} }
var pidFileName string // PID file name. Empty if no PID file was created.
const ( const (
// Used in config to indicate that syslog or eventlog (win) should be used for logger output // Used in config to indicate that syslog or eventlog (win) should be used for logger output
@ -47,6 +49,7 @@ func main() {
go func() { go func() {
<-signalChannel <-signalChannel
cleanup() cleanup()
cleanupAlways()
os.Exit(0) os.Exit(0)
}() }()
@ -124,6 +127,10 @@ func run(args options) {
} }
} }
if len(args.pidFile) != 0 && writePIDFile(args.pidFile) {
pidFileName = args.pidFile
}
// Update filters we've just loaded right away, don't wait for periodic update timer // Update filters we've just loaded right away, don't wait for periodic update timer
go func() { go func() {
refreshFiltersIfNecessary(false) refreshFiltersIfNecessary(false)
@ -158,8 +165,8 @@ func run(args options) {
// validate current TLS config and update warnings (it could have been loaded from file) // validate current TLS config and update warnings (it could have been loaded from file)
data := validateCertificates(config.TLS.CertificateChain, config.TLS.PrivateKey, config.TLS.ServerName) data := validateCertificates(config.TLS.CertificateChain, config.TLS.PrivateKey, config.TLS.ServerName)
if !data.ValidPair { if !data.ValidPair {
cleanupAlways()
log.Fatal(data.WarningValidation) log.Fatal(data.WarningValidation)
os.Exit(1)
} }
config.Lock() config.Lock()
config.TLS.tlsConfigStatus = data // update warnings config.TLS.tlsConfigStatus = data // update warnings
@ -173,8 +180,8 @@ func run(args options) {
copy(privatekey, []byte(config.TLS.PrivateKey)) copy(privatekey, []byte(config.TLS.PrivateKey))
cert, err := tls.X509KeyPair(certchain, privatekey) cert, err := tls.X509KeyPair(certchain, privatekey)
if err != nil { if err != nil {
cleanupAlways()
log.Fatal(err) log.Fatal(err)
os.Exit(1)
} }
httpsServer.cond.L.Unlock() httpsServer.cond.L.Unlock()
@ -189,8 +196,8 @@ func run(args options) {
printHTTPAddresses("https") printHTTPAddresses("https")
err = httpsServer.server.ListenAndServeTLS("", "") err = httpsServer.server.ListenAndServeTLS("", "")
if err != http.ErrServerClosed { if err != http.ErrServerClosed {
cleanupAlways()
log.Fatal(err) log.Fatal(err)
os.Exit(1)
} }
} }
}() }()
@ -206,13 +213,24 @@ func run(args options) {
} }
err := httpServer.ListenAndServe() err := httpServer.ListenAndServe()
if err != http.ErrServerClosed { if err != http.ErrServerClosed {
cleanupAlways()
log.Fatal(err) log.Fatal(err)
os.Exit(1)
} }
// We use ErrServerClosed as a sign that we need to rebind on new address, so go back to the start of the loop // We use ErrServerClosed as a sign that we need to rebind on new address, so go back to the start of the loop
} }
} }
// Write PID to a file
func writePIDFile(fn string) bool {
data := fmt.Sprintf("%d", os.Getpid())
err := ioutil.WriteFile(fn, []byte(data), 0644)
if err != nil {
log.Error("Couldn't write PID to file %s: %v", fn, err)
return false
}
return true
}
// initWorkingDir initializes the ourWorkingDir // initWorkingDir initializes the ourWorkingDir
// if no command-line arguments specified, we use the directory where our binary file is located // if no command-line arguments specified, we use the directory where our binary file is located
func initWorkingDir(args options) { func initWorkingDir(args options) {
@ -298,6 +316,13 @@ func cleanup() {
} }
} }
// This function is called before application exits
func cleanupAlways() {
if len(pidFileName) != 0 {
os.Remove(pidFileName)
}
}
// command-line arguments // command-line arguments
type options struct { type options struct {
verbose bool // is verbose logging enabled verbose bool // is verbose logging enabled
@ -306,6 +331,7 @@ type options struct {
bindHost string // host address to bind HTTP server on bindHost string // host address to bind HTTP server on
bindPort int // port to serve HTTP pages on bindPort int // port to serve HTTP pages on
logFile string // Path to the log file. If empty, write to stdout. If "syslog", writes to syslog logFile string // Path to the log file. If empty, write to stdout. If "syslog", writes to syslog
pidFile string // File name to save PID to
// service control action (see service.ControlAction array + "status" command) // service control action (see service.ControlAction array + "status" command)
serviceControlAction string serviceControlAction string
@ -342,6 +368,7 @@ func loadOptions() options {
{"logfile", "l", "path to the log file. If empty, writes to stdout, if 'syslog' -- system log", func(value string) { {"logfile", "l", "path to the log file. If empty, writes to stdout, if 'syslog' -- system log", func(value string) {
o.logFile = value o.logFile = value
}, nil}, }, nil},
{"pidfile", "", "File name to save PID to", func(value string) { o.pidFile = value }, nil},
{"verbose", "v", "enable verbose output", nil, func() { o.verbose = true }}, {"verbose", "v", "enable verbose output", nil, func() { o.verbose = true }},
{"help", "", "print this help", nil, func() { {"help", "", "print this help", nil, func() {
printHelp() printHelp()

View File

@ -32,6 +32,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()
cleanupAlways()
os.Exit(0) os.Exit(0)
return nil return nil
} }