2015-01-13 14:43:45 -05:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2015-10-26 14:34:31 -05:00
|
|
|
"errors"
|
2015-01-13 18:14:00 -05:00
|
|
|
"flag"
|
2015-04-15 15:11:32 -05:00
|
|
|
"fmt"
|
2015-05-04 17:23:16 -05:00
|
|
|
"io/ioutil"
|
2015-01-13 14:43:45 -05:00
|
|
|
"log"
|
2015-05-04 07:53:54 -05:00
|
|
|
"os"
|
2015-04-24 21:08:14 -05:00
|
|
|
"runtime"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
2015-01-13 14:43:45 -05:00
|
|
|
|
2015-10-26 14:34:31 -05:00
|
|
|
"github.com/mholt/caddy/caddy"
|
|
|
|
"github.com/mholt/caddy/caddy/letsencrypt"
|
2015-01-13 14:43:45 -05:00
|
|
|
)
|
|
|
|
|
2015-04-09 11:08:22 -05:00
|
|
|
var (
|
2015-05-07 14:09:40 -05:00
|
|
|
conf string
|
|
|
|
cpu string
|
2015-11-05 16:07:34 -05:00
|
|
|
logfile string
|
2015-11-10 20:44:00 -05:00
|
|
|
pidfile string
|
|
|
|
revoke string
|
|
|
|
version bool
|
2015-04-09 11:08:22 -05:00
|
|
|
)
|
2015-01-19 01:11:21 -05:00
|
|
|
|
2015-10-26 14:34:31 -05:00
|
|
|
const (
|
|
|
|
appName = "Caddy"
|
2015-11-07 19:22:41 -05:00
|
|
|
appVersion = "0.8 beta 3"
|
2015-10-26 14:34:31 -05:00
|
|
|
)
|
|
|
|
|
2015-01-19 01:11:21 -05:00
|
|
|
func init() {
|
2015-10-26 14:34:31 -05:00
|
|
|
flag.StringVar(&conf, "conf", "", "Configuration file to use (default="+caddy.DefaultConfigFile+")")
|
2015-10-27 13:52:58 -05:00
|
|
|
flag.BoolVar(&caddy.HTTP2, "http2", true, "HTTP/2 support") // TODO: temporary flag until http2 merged into std lib
|
2015-10-26 14:34:31 -05:00
|
|
|
flag.BoolVar(&caddy.Quiet, "quiet", false, "Quiet mode (no initialization output)")
|
2015-04-24 21:08:14 -05:00
|
|
|
flag.StringVar(&cpu, "cpu", "100%", "CPU cap")
|
2015-10-26 14:34:31 -05:00
|
|
|
flag.StringVar(&caddy.Root, "root", caddy.DefaultRoot, "Root path to default site")
|
|
|
|
flag.StringVar(&caddy.Host, "host", caddy.DefaultHost, "Default host")
|
|
|
|
flag.StringVar(&caddy.Port, "port", caddy.DefaultPort, "Default port")
|
2015-11-10 20:44:00 -05:00
|
|
|
flag.StringVar(&pidfile, "pidfile", "", "Path to write pid file")
|
2015-05-07 14:09:40 -05:00
|
|
|
flag.BoolVar(&version, "version", false, "Show version")
|
2015-10-27 13:52:58 -05:00
|
|
|
// TODO: Boulder dev URL is: http://192.168.99.100:4000
|
|
|
|
// TODO: Staging API URL is: https://acme-staging.api.letsencrypt.org
|
|
|
|
// TODO: Production endpoint is: https://acme-v01.api.letsencrypt.org
|
|
|
|
flag.StringVar(&letsencrypt.CAUrl, "ca", "https://acme-staging.api.letsencrypt.org", "Certificate authority ACME server")
|
2015-10-17 21:17:24 -05:00
|
|
|
flag.BoolVar(&letsencrypt.Agreed, "agree", false, "Agree to Let's Encrypt Subscriber Agreement")
|
2015-10-30 16:55:59 -05:00
|
|
|
flag.StringVar(&letsencrypt.DefaultEmail, "email", "", "Default Let's Encrypt account email address")
|
2015-10-27 13:52:58 -05:00
|
|
|
flag.StringVar(&revoke, "revoke", "", "Hostname for which to revoke the certificate")
|
2015-11-05 16:07:34 -05:00
|
|
|
flag.StringVar(&logfile, "log", "", "Process log file")
|
2015-01-19 01:11:21 -05:00
|
|
|
}
|
|
|
|
|
2015-01-13 14:43:45 -05:00
|
|
|
func main() {
|
2015-11-05 16:07:34 -05:00
|
|
|
flag.Parse() // called here in main() to allow other packages to set flags in their inits
|
2015-05-06 15:57:32 -05:00
|
|
|
|
2015-10-26 14:34:31 -05:00
|
|
|
caddy.AppName = appName
|
|
|
|
caddy.AppVersion = appVersion
|
|
|
|
|
2015-11-05 16:07:34 -05:00
|
|
|
// set up process log before anything bad happens
|
|
|
|
switch logfile {
|
|
|
|
case "stdout":
|
|
|
|
log.SetOutput(os.Stdout)
|
|
|
|
case "stderr":
|
|
|
|
log.SetOutput(os.Stderr)
|
|
|
|
case "":
|
|
|
|
log.SetOutput(ioutil.Discard)
|
|
|
|
default:
|
2015-11-05 19:01:08 -05:00
|
|
|
file, err := os.OpenFile(logfile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
|
2015-11-05 16:07:34 -05:00
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("Error opening log file: %v", err)
|
|
|
|
}
|
|
|
|
log.SetOutput(file)
|
|
|
|
}
|
|
|
|
|
2015-05-07 14:09:40 -05:00
|
|
|
if version {
|
2015-10-26 14:34:31 -05:00
|
|
|
fmt.Printf("%s %s\n", caddy.AppName, caddy.AppVersion)
|
2015-05-07 14:09:40 -05:00
|
|
|
os.Exit(0)
|
|
|
|
}
|
2015-10-21 01:09:45 -05:00
|
|
|
if revoke != "" {
|
|
|
|
err := letsencrypt.Revoke(revoke)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
fmt.Printf("Revoked certificate for %s\n", revoke)
|
|
|
|
os.Exit(0)
|
|
|
|
}
|
2015-11-10 20:44:00 -05:00
|
|
|
if pidfile != "" {
|
|
|
|
pid := []byte(strconv.Itoa(os.Getpid()) + "\n")
|
|
|
|
err := ioutil.WriteFile(pidfile, pid, 0644)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
2015-05-07 14:09:40 -05:00
|
|
|
|
2015-04-24 21:08:14 -05:00
|
|
|
// Set CPU cap
|
2015-10-26 14:34:31 -05:00
|
|
|
err := setCPU(cpu)
|
2015-04-24 21:08:14 -05:00
|
|
|
if err != nil {
|
2015-11-05 16:07:34 -05:00
|
|
|
mustLogFatal(err)
|
2015-04-24 21:08:14 -05:00
|
|
|
}
|
|
|
|
|
2015-10-26 14:34:31 -05:00
|
|
|
// Get Caddyfile input
|
|
|
|
caddyfile, err := caddy.LoadCaddyfile(loadCaddyfile)
|
2015-04-15 15:11:32 -05:00
|
|
|
if err != nil {
|
2015-11-05 16:07:34 -05:00
|
|
|
mustLogFatal(err)
|
2015-04-15 15:11:32 -05:00
|
|
|
}
|
|
|
|
|
2015-10-26 14:34:31 -05:00
|
|
|
// Start your engines
|
|
|
|
err = caddy.Start(caddyfile)
|
|
|
|
if err != nil {
|
2015-11-03 14:01:54 -05:00
|
|
|
if caddy.IsRestart() {
|
2015-11-05 19:01:08 -05:00
|
|
|
log.Printf("[ERROR] Upon starting %s: %v", appName, err)
|
2015-11-03 14:01:54 -05:00
|
|
|
} else {
|
2015-11-05 16:07:34 -05:00
|
|
|
mustLogFatal(err)
|
2015-11-03 14:01:54 -05:00
|
|
|
}
|
2015-01-13 14:43:45 -05:00
|
|
|
}
|
|
|
|
|
2015-10-26 14:34:31 -05:00
|
|
|
// Twiddle your thumbs
|
|
|
|
caddy.Wait()
|
2015-05-20 21:06:30 -05:00
|
|
|
}
|
|
|
|
|
2015-11-05 16:07:34 -05:00
|
|
|
// mustLogFatal just wraps log.Fatal() in a way that ensures the
|
|
|
|
// output is always printed to stderr so the user can see it,
|
|
|
|
// even if the process log was not enabled.
|
|
|
|
func mustLogFatal(args ...interface{}) {
|
|
|
|
log.SetOutput(os.Stderr)
|
|
|
|
log.Fatal(args...)
|
|
|
|
}
|
|
|
|
|
2015-10-26 14:34:31 -05:00
|
|
|
func loadCaddyfile() (caddy.Input, error) {
|
2015-11-10 17:06:47 -05:00
|
|
|
// Try -conf flag
|
|
|
|
if conf != "" {
|
|
|
|
if conf == "stdin" {
|
|
|
|
return caddy.CaddyfileFromPipe(os.Stdin)
|
2015-11-06 15:22:22 -05:00
|
|
|
}
|
|
|
|
|
2015-10-26 14:34:31 -05:00
|
|
|
contents, err := ioutil.ReadFile(conf)
|
2015-05-04 07:53:54 -05:00
|
|
|
if err != nil {
|
2015-08-01 14:08:31 -05:00
|
|
|
return nil, err
|
2015-05-04 07:53:54 -05:00
|
|
|
}
|
2015-11-10 17:06:47 -05:00
|
|
|
|
2015-10-26 14:34:31 -05:00
|
|
|
return caddy.CaddyfileInput{
|
|
|
|
Contents: contents,
|
|
|
|
Filepath: conf,
|
2015-10-26 18:57:32 -05:00
|
|
|
RealFile: true,
|
2015-10-26 14:34:31 -05:00
|
|
|
}, nil
|
2015-05-04 07:53:54 -05:00
|
|
|
}
|
|
|
|
|
2015-10-26 14:34:31 -05:00
|
|
|
// command line args
|
2015-09-08 13:14:23 -05:00
|
|
|
if flag.NArg() > 0 {
|
2015-11-07 22:03:02 -05:00
|
|
|
confBody := caddy.Host + ":" + caddy.Port + "\n" + strings.Join(flag.Args(), "\n")
|
2015-10-26 14:34:31 -05:00
|
|
|
return caddy.CaddyfileInput{
|
|
|
|
Contents: []byte(confBody),
|
|
|
|
Filepath: "args",
|
|
|
|
}, nil
|
2015-09-08 13:14:23 -05:00
|
|
|
}
|
|
|
|
|
2015-10-26 14:34:31 -05:00
|
|
|
// Caddyfile in cwd
|
|
|
|
contents, err := ioutil.ReadFile(caddy.DefaultConfigFile)
|
2015-05-04 17:23:16 -05:00
|
|
|
if err != nil {
|
|
|
|
if os.IsNotExist(err) {
|
2015-11-03 10:10:16 -05:00
|
|
|
return caddy.DefaultInput(), nil
|
2015-05-04 17:23:16 -05:00
|
|
|
}
|
2015-08-01 14:08:31 -05:00
|
|
|
return nil, err
|
2015-05-04 17:23:16 -05:00
|
|
|
}
|
2015-10-26 14:34:31 -05:00
|
|
|
return caddy.CaddyfileInput{
|
|
|
|
Contents: contents,
|
|
|
|
Filepath: caddy.DefaultConfigFile,
|
2015-10-26 18:57:32 -05:00
|
|
|
RealFile: true,
|
2015-10-26 14:34:31 -05:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// setCPU parses string cpu and sets GOMAXPROCS
|
|
|
|
// according to its value. It accepts either
|
|
|
|
// a number (e.g. 3) or a percent (e.g. 50%).
|
|
|
|
func setCPU(cpu string) error {
|
|
|
|
var numCPU int
|
|
|
|
|
|
|
|
availCPU := runtime.NumCPU()
|
|
|
|
|
|
|
|
if strings.HasSuffix(cpu, "%") {
|
|
|
|
// Percent
|
|
|
|
var percent float32
|
|
|
|
pctStr := cpu[:len(cpu)-1]
|
|
|
|
pctInt, err := strconv.Atoi(pctStr)
|
|
|
|
if err != nil || pctInt < 1 || pctInt > 100 {
|
|
|
|
return errors.New("invalid CPU value: percentage must be between 1-100")
|
|
|
|
}
|
|
|
|
percent = float32(pctInt) / 100
|
|
|
|
numCPU = int(float32(availCPU) * percent)
|
|
|
|
} else {
|
|
|
|
// Number
|
|
|
|
num, err := strconv.Atoi(cpu)
|
|
|
|
if err != nil || num < 1 {
|
|
|
|
return errors.New("invalid CPU value: provide a number or percent greater than 0")
|
|
|
|
}
|
|
|
|
numCPU = num
|
|
|
|
}
|
|
|
|
|
|
|
|
if numCPU > availCPU {
|
|
|
|
numCPU = availCPU
|
|
|
|
}
|
2015-05-06 10:16:10 -05:00
|
|
|
|
2015-10-26 14:34:31 -05:00
|
|
|
runtime.GOMAXPROCS(numCPU)
|
|
|
|
return nil
|
2015-05-04 07:53:54 -05:00
|
|
|
}
|