| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | 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
 | 
					
						
							| 
									
										
										
										
											2016-04-28 11:07:44 -07:00
										 |  |  | // restart, then try loading first host of Corefile in the browser).
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | // Do not use this directly - call signalSuccessToParent instead.
 | 
					
						
							|  |  |  | var signalParentOnce sync.Once
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-28 11:07:44 -07:00
										 |  |  | // corefileGob maps bind address to index of the file descriptor
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | // in the Files array passed to the child process. It also contains
 | 
					
						
							| 
									
										
										
										
											2016-04-28 11:07:44 -07:00
										 |  |  | // the corefile contents and other state needed by the new process.
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | // Used only during graceful restarts where a new process is spawned.
 | 
					
						
							| 
									
										
										
										
											2016-04-28 11:07:44 -07:00
										 |  |  | type corefileGob struct {
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | 	ListenerFds            map[string]uintptr
 | 
					
						
							| 
									
										
										
										
											2016-04-28 11:07:44 -07:00
										 |  |  | 	Corefile               Input
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | 	OnDemandTLSCertsIssued int32
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // IsRestart returns whether this process is, according
 | 
					
						
							|  |  |  | // to env variables, a fork as part of a graceful restart.
 | 
					
						
							|  |  |  | func IsRestart() bool {
 | 
					
						
							| 
									
										
										
										
											2016-04-28 11:07:44 -07:00
										 |  |  | 	return os.Getenv("COREDNS_RESTART") == "true"
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // 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)
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-28 11:07:44 -07:00
										 |  |  | // CorefileInput represents a Corefile as input
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | // and is simply a convenient way to implement
 | 
					
						
							|  |  |  | // the Input interface.
 | 
					
						
							| 
									
										
										
										
											2016-04-28 11:07:44 -07:00
										 |  |  | type CorefileInput struct {
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | 	Filepath string
 | 
					
						
							|  |  |  | 	Contents []byte
 | 
					
						
							|  |  |  | 	RealFile bool
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Body returns c.Contents.
 | 
					
						
							| 
									
										
										
										
											2016-04-28 11:07:44 -07:00
										 |  |  | func (c CorefileInput) Body() []byte { return c.Contents }
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Path returns c.Filepath.
 | 
					
						
							| 
									
										
										
										
											2016-04-28 11:07:44 -07:00
										 |  |  | func (c CorefileInput) Path() string { return c.Filepath }
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | // IsFile returns true if the original input was a real file on the file system.
 | 
					
						
							| 
									
										
										
										
											2016-04-28 11:07:44 -07:00
										 |  |  | func (c CorefileInput) IsFile() bool { return c.RealFile }
 |