| 
									
										
										
										
											2019-09-27 11:10:47 +01:00
										 |  |  | // Package reload periodically checks if the Corefile has changed, and reloads if so. | 
					
						
							| 
									
										
										
										
											2018-01-27 05:42:57 -05:00
										 |  |  | package reload | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2019-07-30 15:44:16 -07:00
										 |  |  | 	"bytes" | 
					
						
							| 
									
										
										
										
											2018-01-27 05:42:57 -05:00
										 |  |  | 	"crypto/md5" | 
					
						
							| 
									
										
										
										
											2019-07-30 15:44:16 -07:00
										 |  |  | 	"encoding/json" | 
					
						
							| 
									
										
										
										
											2019-02-17 14:57:36 +00:00
										 |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2018-01-27 05:42:57 -05:00
										 |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-03 09:04:47 +08:00
										 |  |  | 	"github.com/caddyserver/caddy" | 
					
						
							| 
									
										
										
										
											2019-07-30 15:44:16 -07:00
										 |  |  | 	"github.com/caddyserver/caddy/caddyfile" | 
					
						
							| 
									
										
										
										
											2018-01-27 05:42:57 -05:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 13:15:56 -05:00
										 |  |  | const ( | 
					
						
							|  |  |  | 	unused    = 0 | 
					
						
							|  |  |  | 	maybeUsed = 1 | 
					
						
							|  |  |  | 	used      = 2 | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2018-01-27 05:42:57 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | type reload struct { | 
					
						
							| 
									
										
										
										
											2019-02-17 14:57:36 +00:00
										 |  |  | 	dur  time.Duration | 
					
						
							|  |  |  | 	u    int | 
					
						
							|  |  |  | 	mtx  sync.RWMutex | 
					
						
							|  |  |  | 	quit chan bool | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (r *reload) setUsage(u int) { | 
					
						
							|  |  |  | 	r.mtx.Lock() | 
					
						
							|  |  |  | 	defer r.mtx.Unlock() | 
					
						
							|  |  |  | 	r.u = u | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (r *reload) usage() int { | 
					
						
							|  |  |  | 	r.mtx.RLock() | 
					
						
							|  |  |  | 	defer r.mtx.RUnlock() | 
					
						
							|  |  |  | 	return r.u | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (r *reload) setInterval(i time.Duration) { | 
					
						
							|  |  |  | 	r.mtx.Lock() | 
					
						
							|  |  |  | 	defer r.mtx.Unlock() | 
					
						
							|  |  |  | 	r.dur = i | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (r *reload) interval() time.Duration { | 
					
						
							|  |  |  | 	r.mtx.RLock() | 
					
						
							|  |  |  | 	defer r.mtx.RUnlock() | 
					
						
							|  |  |  | 	return r.dur | 
					
						
							| 
									
										
										
										
											2018-01-27 05:42:57 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-30 15:44:16 -07:00
										 |  |  | func parse(corefile caddy.Input) ([]byte, error) { | 
					
						
							|  |  |  | 	serverBlocks, err := caddyfile.Parse(corefile.Path(), bytes.NewReader(corefile.Body()), nil) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return json.Marshal(serverBlocks) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-27 05:42:57 -05:00
										 |  |  | func hook(event caddy.EventName, info interface{}) error { | 
					
						
							|  |  |  | 	if event != caddy.InstanceStartupEvent { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// if reload is removed from the Corefile, then the hook | 
					
						
							|  |  |  | 	// is still registered but setup is never called again | 
					
						
							|  |  |  | 	// so we need a flag to tell us not to reload | 
					
						
							| 
									
										
										
										
											2019-02-17 14:57:36 +00:00
										 |  |  | 	if r.usage() == unused { | 
					
						
							| 
									
										
										
										
											2018-01-27 05:42:57 -05:00
										 |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// this should be an instance. ok to panic if not | 
					
						
							| 
									
										
										
										
											2018-02-02 13:15:56 -05:00
										 |  |  | 	instance := info.(*caddy.Instance) | 
					
						
							| 
									
										
										
										
											2019-07-30 15:44:16 -07:00
										 |  |  | 	parsedCorefile, err := parse(instance.Caddyfile()) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	md5sum := md5.Sum(parsedCorefile) | 
					
						
							| 
									
										
										
										
											2018-04-19 07:41:56 +01:00
										 |  |  | 	log.Infof("Running configuration MD5 = %x\n", md5sum) | 
					
						
							| 
									
										
										
										
											2018-01-27 05:42:57 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	go func() { | 
					
						
							| 
									
										
										
										
											2019-02-17 14:57:36 +00:00
										 |  |  | 		tick := time.NewTicker(r.interval()) | 
					
						
							| 
									
										
										
										
											2018-01-27 05:42:57 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		for { | 
					
						
							|  |  |  | 			select { | 
					
						
							|  |  |  | 			case <-tick.C: | 
					
						
							| 
									
										
										
										
											2018-02-02 13:15:56 -05:00
										 |  |  | 				corefile, err := caddy.LoadCaddyfile(instance.Caddyfile().ServerType()) | 
					
						
							| 
									
										
										
										
											2018-01-27 05:42:57 -05:00
										 |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					continue | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-07-30 15:44:16 -07:00
										 |  |  | 				parsedCorefile, err := parse(corefile) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					log.Warningf("Corefile parse failed: %s", err) | 
					
						
							|  |  |  | 					continue | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				s := md5.Sum(parsedCorefile) | 
					
						
							| 
									
										
										
										
											2018-02-02 13:15:56 -05:00
										 |  |  | 				if s != md5sum { | 
					
						
							|  |  |  | 					// Let not try to restart with the same file, even though it is wrong. | 
					
						
							|  |  |  | 					md5sum = s | 
					
						
							|  |  |  | 					// now lets consider that plugin will not be reload, unless appear in next config file | 
					
						
							| 
									
										
										
										
											2019-08-23 14:29:17 +08:00
										 |  |  | 					// change status of usage will be reset in setup if the plugin appears in config file | 
					
						
							| 
									
										
										
										
											2019-02-17 14:57:36 +00:00
										 |  |  | 					r.setUsage(maybeUsed) | 
					
						
							| 
									
										
										
										
											2018-02-02 13:15:56 -05:00
										 |  |  | 					_, err := instance.Restart(corefile) | 
					
						
							| 
									
										
										
										
											2018-01-27 05:42:57 -05:00
										 |  |  | 					if err != nil { | 
					
						
							| 
									
										
										
										
											2019-01-19 11:23:13 +00:00
										 |  |  | 						log.Errorf("Corefile changed but reload failed: %s", err) | 
					
						
							| 
									
										
										
										
											2019-06-26 09:38:46 +03:00
										 |  |  | 						FailedCount.Add(1) | 
					
						
							| 
									
										
										
										
											2018-01-27 05:42:57 -05:00
										 |  |  | 						continue | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2018-02-02 13:15:56 -05:00
										 |  |  | 					// we are done, if the plugin was not set used, then it is not. | 
					
						
							| 
									
										
										
										
											2019-02-17 14:57:36 +00:00
										 |  |  | 					if r.usage() == maybeUsed { | 
					
						
							|  |  |  | 						r.setUsage(unused) | 
					
						
							| 
									
										
										
										
											2018-02-02 13:15:56 -05:00
										 |  |  | 					} | 
					
						
							| 
									
										
										
										
											2018-01-27 05:42:57 -05:00
										 |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			case <-r.quit: | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } |