aboutsummaryrefslogtreecommitdiff
path: root/core/helpers.go
diff options
context:
space:
mode:
Diffstat (limited to 'core/helpers.go')
-rw-r--r--core/helpers.go102
1 files changed, 102 insertions, 0 deletions
diff --git a/core/helpers.go b/core/helpers.go
new file mode 100644
index 000000000..8ef6a54cd
--- /dev/null
+++ b/core/helpers.go
@@ -0,0 +1,102 @@
+package core
+
+import (
+ "bytes"
+ "fmt"
+ "io/ioutil"
+ "log"
+ "os"
+ "os/exec"
+ "runtime"
+ "strconv"
+ "strings"
+ "sync"
+)
+
+// isLocalhost returns true if host looks explicitly like a localhost address.
+func isLocalhost(host string) bool {
+ return host == "localhost" || host == "::1" || strings.HasPrefix(host, "127.")
+}
+
+// checkFdlimit issues a warning if the OS max file descriptors is below a recommended minimum.
+func checkFdlimit() {
+ const min = 4096
+
+ // Warn if ulimit is too low for production sites
+ if runtime.GOOS == "linux" || runtime.GOOS == "darwin" {
+ out, err := exec.Command("sh", "-c", "ulimit -n").Output() // use sh because ulimit isn't in Linux $PATH
+ if err == nil {
+ // Note that an error here need not be reported
+ lim, err := strconv.Atoi(string(bytes.TrimSpace(out)))
+ if err == nil && lim < min {
+ fmt.Printf("Warning: File descriptor limit %d is too low for production sites. At least %d is recommended. Set with \"ulimit -n %d\".\n", lim, min, min)
+ }
+ }
+ }
+}
+
+// signalSuccessToParent tells the parent our status using pipe at index 3.
+// If this process is not a restart, this function does nothing.
+// Calling this function once this process has successfully initialized
+// is vital so that the parent process can unblock and kill itself.
+// This function is idempotent; it executes at most once per process.
+func signalSuccessToParent() {
+ signalParentOnce.Do(func() {
+ if IsRestart() {
+ ppipe := os.NewFile(3, "") // parent is reading from pipe at index 3
+ _, err := ppipe.Write([]byte("success")) // we must send some bytes to the parent
+ if err != nil {
+ log.Printf("[ERROR] Communicating successful init to parent: %v", err)
+ }
+ ppipe.Close()
+ }
+ })
+}
+
+// signalParentOnce is used to make sure that the parent is only
+// signaled once; doing so more than once breaks whatever socket is
+// at fd 4 (the reason for this is still unclear - to reproduce,
+// call Stop() and Start() in succession at least once after a
+// restart, then try loading first host of Caddyfile in the browser).
+// Do not use this directly - call signalSuccessToParent instead.
+var signalParentOnce sync.Once
+
+// caddyfileGob maps bind address to index of the file descriptor
+// in the Files array passed to the child process. It also contains
+// the caddyfile contents and other state needed by the new process.
+// Used only during graceful restarts where a new process is spawned.
+type caddyfileGob struct {
+ ListenerFds map[string]uintptr
+ Caddyfile Input
+ OnDemandTLSCertsIssued int32
+}
+
+// IsRestart returns whether this process is, according
+// to env variables, a fork as part of a graceful restart.
+func IsRestart() bool {
+ return os.Getenv("CADDY_RESTART") == "true"
+}
+
+// writePidFile writes the process ID to the file at PidFile, if specified.
+func writePidFile() error {
+ pid := []byte(strconv.Itoa(os.Getpid()) + "\n")
+ return ioutil.WriteFile(PidFile, pid, 0644)
+}
+
+// CaddyfileInput represents a Caddyfile as input
+// and is simply a convenient way to implement
+// the Input interface.
+type CaddyfileInput struct {
+ Filepath string
+ Contents []byte
+ RealFile bool
+}
+
+// Body returns c.Contents.
+func (c CaddyfileInput) Body() []byte { return c.Contents }
+
+// Path returns c.Filepath.
+func (c CaddyfileInput) Path() string { return c.Filepath }
+
+// IsFile returns true if the original input was a real file on the file system.
+func (c CaddyfileInput) IsFile() bool { return c.RealFile }