From 1de6f3b4b986cb75c3791eadc7b5620bb1fcf5e8 Mon Sep 17 00:00:00 2001 From: Daniel Theophanes Date: Thu, 4 Feb 2016 22:15:58 -0800 Subject: [PATCH] caddy: support running as a service on windows and easy control Allows running caddy without a terminal open or user logged in. Use example "caddy -service install && caddy -service start". Fixes #293 --- main.go | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 63 insertions(+), 8 deletions(-) diff --git a/main.go b/main.go index 813423018..04e856ab0 100644 --- a/main.go +++ b/main.go @@ -7,11 +7,13 @@ import ( "io/ioutil" "log" "os" + "path/filepath" "runtime" "strconv" "strings" "time" + "github.com/kardianos/service" "github.com/mholt/caddy/caddy" "github.com/mholt/caddy/caddy/letsencrypt" "github.com/xenolf/lego/acme" @@ -23,6 +25,7 @@ var ( logfile string revoke string version bool + srvctl string ) const ( @@ -47,6 +50,7 @@ func init() { flag.StringVar(&revoke, "revoke", "", "Hostname for which to revoke the certificate") flag.StringVar(&caddy.Root, "root", caddy.DefaultRoot, "Root path to default site") flag.BoolVar(&version, "version", false, "Show version") + flag.StringVar(&srvctl, "service", "", "Control a system service of Caddy") } func main() { @@ -85,20 +89,39 @@ func main() { os.Exit(0) } - // Set CPU cap - err := setCPU(cpu) + // Create an absolute path to the caddy file for use + // in a service. + serviceCaddyPath := caddy.DefaultConfigFile + if len(conf) != 0 { + serviceCaddyPath = conf + } + if !filepath.IsAbs(serviceCaddyPath) { + wd, err := os.Getwd() + if err != nil { + mustLogFatal(err) + } + serviceCaddyPath = filepath.Join(wd, serviceCaddyPath) + } + sys, err := service.New(app{}, &service.Config{ + Name: caddy.AppName, + DisplayName: caddy.AppName, + + Arguments: []string{"-conf", serviceCaddyPath}, + }) if err != nil { mustLogFatal(err) } - // Get Caddyfile input - caddyfile, err := caddy.LoadCaddyfile(loadCaddyfile) - if err != nil { - mustLogFatal(err) + // Handle any service control commands. + if len(srvctl) != 0 { + err = service.Control(sys, srvctl) + if err != nil { + mustLogFatal(err, service.ControlAction) + } + return } - // Start your engines - err = caddy.Start(caddyfile) + err = sys.Run() if err != nil { mustLogFatal(err) } @@ -107,6 +130,38 @@ func main() { caddy.Wait() } +type app struct{} + +func (app) Start(s service.Service) error { + if !service.Interactive() && len(conf) != 0 { + // Set the WD here if running under a service. + // This is required as on windows there is no way to set the current working + // directory as a service. + dir, _ := filepath.Split(conf) + err := os.Chdir(dir) + if err != nil { + return err + } + } + // Set CPU cap + err := setCPU(cpu) + if err != nil { + return err + } + + // Get Caddyfile input + caddyfile, err := caddy.LoadCaddyfile(loadCaddyfile) + if err != nil { + return err + } + + // Start your engines + return caddy.Start(caddyfile) +} +func (app) Stop(s service.Service) error { + return caddy.Stop() +} + // mustLogFatal just wraps log.Fatal() in a way that ensures the // output is always printed to stderr so the user can see it // if the user is still there, even if the process log was not