mirror of
				https://github.com/coredns/coredns.git
				synced 2025-11-04 03:03:14 -05:00 
			
		
		
		
	
		
			
	
	
		
			103 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			103 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 
								 | 
							
								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 }
							 |