github.com/swiftstack/proxyfs@v0.0.0-20201223034610-5434d919416e/proxyfsd/daemon.go (about) 1 package proxyfsd 2 3 import ( 4 "fmt" 5 "net/http" 6 _ "net/http/pprof" 7 "os" 8 "os/signal" 9 "strings" 10 "sync" 11 12 "golang.org/x/sys/unix" 13 14 "github.com/swiftstack/ProxyFS/conf" 15 "github.com/swiftstack/ProxyFS/logger" 16 "github.com/swiftstack/ProxyFS/transitions" 17 "github.com/swiftstack/ProxyFS/version" 18 19 // Force importing of the following "top-most" package 20 _ "github.com/swiftstack/ProxyFS/httpserver" 21 ) 22 23 // Daemon is launched as a GoRoutine that launches ProxyFS. During startup, the parent should read errChan 24 // to await Daemon getting to the point where it is ready to handle the specified signal set. Any errors 25 // encountered before or after this point will be sent to errChan (and be non-nil of course). 26 func Daemon(confFile string, confStrings []string, errChan chan error, wg *sync.WaitGroup, execArgs []string, signals ...os.Signal) { 27 var ( 28 confMap conf.ConfMap 29 err error 30 signalReceived os.Signal 31 ) 32 33 // Compute confMap 34 35 confMap, err = conf.MakeConfMapFromFile(confFile) 36 if nil != err { 37 errChan <- err 38 39 return 40 } 41 42 err = confMap.UpdateFromStrings(confStrings) 43 if nil != err { 44 errChan <- err 45 46 return 47 } 48 49 // Optionally launch an embedded HTTP Server for Golang runtime access; 50 // this should be done before transitions.Up() is called so it is 51 // available if transitions.Up() hangs (the embedded http server will 52 // return http.StatusServiceUnavailable (503) during the transition. 53 debugServerPortAsUint16, err := confMap.FetchOptionValueUint16("ProxyfsDebug", "DebugServerPort") 54 if nil != err && debugServerPortAsUint16 != 0 { 55 56 debugServerPortAsString := fmt.Sprintf("%d", debugServerPortAsUint16) 57 logger.Infof("proxyfsd.Daemon() starting debug HTTP Server on localhost:%s", debugServerPortAsString) 58 go http.ListenAndServe("localhost:"+debugServerPortAsString, nil) 59 } 60 61 // Arm signal handler used to catch signals 62 // 63 // Note: signalChan must be buffered to avoid race with window between 64 // arming handler and blocking on the chan read when signals might 65 // otherwise be lost. No signals will be processed until 66 // transitions.Up() finishes, but an incoming SIGHUP will not cause the 67 // process to immediately exit. 68 signalChan := make(chan os.Signal, 16) 69 70 // if signals is empty it means "catch all signals" it is possible to catch 71 signal.Notify(signalChan, signals...) 72 73 // Start up dæmon packages 74 75 err = transitions.Up(confMap) 76 if nil != err { 77 errChan <- err 78 return 79 } 80 wg.Add(1) 81 logger.Infof("proxyfsd is starting up (version %s) (PID %d); invoked as '%s'", 82 version.ProxyFSVersion, os.Getpid(), strings.Join(execArgs, "' '")) 83 defer func() { 84 logger.Infof("proxyfsd logger is shutting down (PID %d)", os.Getpid()) 85 err = transitions.Down(confMap) 86 if nil != err { 87 logger.Errorf("transitions.Down() failed: %v", err) // Oddly, if logger.Down() fails, will this work? 88 } 89 errChan <- err 90 wg.Done() 91 }() 92 93 // indicate transitions finished and signal handlers have been armed successfully 94 errChan <- nil 95 96 // Await a signal - reloading confFile each SIGHUP - exiting otherwise 97 for { 98 signalReceived = <-signalChan 99 logger.Infof("Received signal: '%v'", signalReceived) 100 101 // these signals are normally ignored, but if "signals..." above is empty 102 // they are delivered via the channel. we should simply ignore them. 103 if signalReceived == unix.SIGCHLD || signalReceived == unix.SIGURG || 104 signalReceived == unix.SIGWINCH || signalReceived == unix.SIGCONT { 105 logger.Infof("Ignored signal: '%v'", signalReceived) 106 continue 107 } 108 109 // we can get SIGPIPE whenever an HTTP or other client closes a 110 // socket on us, so ignore it 111 if signalReceived == unix.SIGPIPE { 112 logger.Infof("Ignored signal: '%v'", signalReceived) 113 continue 114 } 115 116 // SIGHUP means reconfig but any other signal means time to exit 117 if unix.SIGHUP != signalReceived { 118 logger.Infof("signal catcher is shutting down proxyfsd (PID %d)", os.Getpid()) 119 120 if signalReceived != unix.SIGTERM && signalReceived != unix.SIGINT { 121 logger.Errorf("proxyfsd received unexpected signal: %v", signalReceived) 122 } 123 124 return 125 } 126 127 // caught SIGHUP -- recompute confMap and re-apply 128 confMap, err = conf.MakeConfMapFromFile(confFile) 129 if nil != err { 130 err = fmt.Errorf("failed to load updated config: %v", err) 131 errChan <- err 132 return 133 } 134 135 err = confMap.UpdateFromStrings(confStrings) 136 if nil != err { 137 err = fmt.Errorf("failed to reapply config overrides: %v", err) 138 errChan <- err 139 return 140 } 141 142 err = transitions.Signaled(confMap) 143 if nil != err { 144 err = fmt.Errorf("transitions.Signaled() failed: %v", err) 145 errChan <- err 146 return 147 } 148 } 149 }