caddy/config/directives.go
2015-03-26 09:52:03 -06:00

169 lines
3.6 KiB
Go

package config
import (
"os"
"os/exec"
"runtime"
"strconv"
"strings"
"github.com/mholt/caddy/middleware"
)
// dirFunc is a type of parsing function which processes
// a particular directive and populates the config.
type dirFunc func(*parser) error
// validDirectives is a map of valid, built-in directive names
// to their parsing function. Built-in directives cannot be
// ordered, so they should only be used for internal server
// configuration; not directly handling requests.
var validDirectives map[string]dirFunc
func init() {
// This has to be in the init function
// to avoid an initialization loop error because
// the 'import' directive (key) in this map
// invokes a method that uses this map.
validDirectives = map[string]dirFunc{
"root": func(p *parser) error {
if !p.nextArg() {
return p.argErr()
}
p.cfg.Root = p.tkn()
return nil
},
"import": func(p *parser) error {
if !p.nextArg() {
return p.argErr()
}
filename := p.tkn()
file, err := os.Open(filename)
if err != nil {
return p.err("Parse", err.Error())
}
defer file.Close()
p2, err := newParser(file)
if err != nil {
return p.err("Parse", "Could not import "+filename+"; "+err.Error())
}
p2.cfg = p.cfg
err = p2.directives()
if err != nil {
return err
}
p.cfg = p2.cfg
return nil
},
"tls": func(p *parser) error {
tls := TLSConfig{Enabled: true}
if !p.nextArg() {
return p.argErr()
}
tls.Certificate = p.tkn()
if !p.nextArg() {
return p.argErr()
}
tls.Key = p.tkn()
p.cfg.TLS = tls
return nil
},
"cpu": func(p *parser) error {
sysCores := runtime.NumCPU()
if !p.nextArg() {
return p.argErr()
}
strNum := p.tkn()
setCPU := func(val int) {
if val < 1 {
val = 1
}
if val > sysCores {
val = sysCores
}
if val > p.cfg.MaxCPU {
p.cfg.MaxCPU = val
}
}
if strings.HasSuffix(strNum, "%") {
// Percent
var percent float32
pctStr := strNum[:len(strNum)-1]
pctInt, err := strconv.Atoi(pctStr)
if err != nil || pctInt < 1 || pctInt > 100 {
return p.err("Parse", "Invalid number '"+strNum+"' (must be a positive percentage between 1 and 100)")
}
percent = float32(pctInt) / 100
setCPU(int(float32(sysCores) * percent))
} else {
// Number
num, err := strconv.Atoi(strNum)
if err != nil || num < 0 {
return p.err("Parse", "Invalid number '"+strNum+"' (requires positive integer or percent)")
}
setCPU(num)
}
return nil
},
"startup": func(p *parser) error {
// TODO: This code is duplicated with the shutdown directive below
if !p.nextArg() {
return p.argErr()
}
command, args, err := middleware.SplitCommandAndArgs(p.tkn())
if err != nil {
return p.err("Parse", err.Error())
}
startupfn := func() error {
cmd := exec.Command(command, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
return err
}
return nil
}
p.cfg.Startup = append(p.cfg.Startup, startupfn)
return nil
},
"shutdown": func(p *parser) error {
if !p.nextArg() {
return p.argErr()
}
command, args, err := middleware.SplitCommandAndArgs(p.tkn())
if err != nil {
return p.err("Parse", err.Error())
}
shutdownfn := func() error {
cmd := exec.Command(command, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
return err
}
return nil
}
p.cfg.Shutdown = append(p.cfg.Shutdown, shutdownfn)
return nil
},
}
}