// This example shows how to work with EventInfo's underlying FSEvent struct. // Investigating notify.(*FSEvent).Flags field we are able to say whether // the event's path is a file or a directory and many more. func ExampleWatch_darwinDirFileSymlink() { var must = func(err error) { if err != nil { log.Fatal(err) } } var stop = func(c ...chan<- notify.EventInfo) { for _, c := range c { notify.Stop(c) } } // Make the channels buffered to ensure no event is dropped. Notify will drop // an event if the receiver is not able to keep up the sending pace. dir := make(chan notify.EventInfo, 1) file := make(chan notify.EventInfo, 1) symlink := make(chan notify.EventInfo, 1) all := make(chan notify.EventInfo, 1) // Set up a single watchpoint listening for FSEvents-specific events on // multiple user-provided channels. must(notify.Watch(".", dir, notify.FSEventsIsDir)) must(notify.Watch(".", file, notify.FSEventsIsFile)) must(notify.Watch(".", symlink, notify.FSEventsIsSymlink)) must(notify.Watch(".", all, notify.All)) defer stop(dir, file, symlink, all) // Block until an event is received. select { case ei := <-dir: log.Println("The directory", ei.Path(), "has changed") case ei := <-file: log.Println("The file", ei.Path(), "has changed") case ei := <-symlink: log.Println("The symlink", ei.Path(), "has changed") case ei := <-all: var kind string // Investigate underlying *notify.FSEvent struct to access more // information about the event. switch flags := ei.Sys().(*notify.FSEvent).Flags; { case flags¬ify.FSEventsIsFile != 0: kind = "file" case flags¬ify.FSEventsIsDir != 0: kind = "dir" case flags¬ify.FSEventsIsSymlink != 0: kind = "symlink" } log.Printf("The %s under path %s has been %sd\n", kind, ei.Path(), ei.Event()) } }
func newWatcher(path string, include, exclude *regexp.Regexp) (*watcher, error) { events := make(chan notify.EventInfo, 10) ops := make(chan Operation, 10) if err := notify.Watch(path+"/...", events, notify.All); err != nil { return nil, err } go func() { for ev := range events { if include.MatchString(ev.Path()) == false { debug(fmt.Sprintf("Skipping: does not match include path: %s", ev.Path())) continue } if exclude.MatchString(ev.Path()) == true { debug(fmt.Sprintf("Skipping: does match exclude path: %s", ev.Path())) continue } ops <- Operation{ Path: ev.Path(), EventInfo: ev, } } }() return &watcher{ops: ops, events: events}, nil }
// This example shows why it is important to not create leaks by stoping // a channel when it's no longer being used. func ExampleStop() { waitfor := func(path string, e notify.Event, timeout time.Duration) bool { dir, file := filepath.Split(path) c := make(chan notify.EventInfo, 1) if err := notify.Watch(dir, c, e); err != nil { log.Fatal(err) } // Clean up watchpoint associated with c. If Stop was not called upon // return the channel would be leaked as notify holds the only reference // to it and does not release it on its own. defer notify.Stop(c) t := time.After(timeout) for { select { case ei := <-c: if filepath.Base(ei.Path()) == file { return true } case <-t: return false } } } if waitfor("index.lock", notify.Create, 5*time.Second) { log.Println("The git repository was locked") } if waitfor("index.lock", notify.Remove, 5*time.Second) { log.Println("The git repository was unlocked") } }
// Watch watches a set of paths. Mod structs representing a changeset are sent // on the channel ch. // // Watch applies heuristics to cope with transient files and unreliable event // notifications. Modifications are batched up until there is a a lull in the // stream of changes of duration lullTime. This lets us represent processes // that progressively affect multiple files, like rendering, as a single // changeset. func Watch(paths []string, lullTime time.Duration, ch chan *Mod) (*Watcher, error) { evtch := make(chan notify.EventInfo, 4096) for _, p := range paths { stat, err := os.Stat(p) if err != nil { return nil, err } if stat.IsDir() { p = path.Join(p, "...") } err = notify.Watch(p, evtch, notify.All) if err != nil { return nil, err } } go func() { for { b := batch(lullTime, MaxLullWait, statExistenceChecker{}, evtch) if b != nil && !b.Empty() { ret, err := b.normPaths(paths) if err != nil { Logger.Shout("Error normalising paths: %s", err) } ch <- ret } } }() return &Watcher{evtch}, nil }
func (w *Watcher) Watch() error { notifyCh := make(chan notify.EventInfo, 10) if err := notify.Watch(w.Dir+"/...", notifyCh, notifyEvents...); err != nil { return err } go func() { for ei := range notifyCh { if !w.Matcher.Match(ei.Path()) { continue } ev := FileEvent{ Path: ei.Path(), Type: convertNotifyType(ei.Event()), Time: time.Now().UnixNano(), } log.Println(ev) w.Events <- ev } }() return nil }
func main() { // Make the channel buffered to ensure no event is dropped. Notify will drop // an event if the receiver is not able to keep up the sending pace. c := make(chan notify.EventInfo, 1) // Set up a watchpoint listening for inotify-specific events within a // current working directory. Dispatch each InCloseWrite and InMovedTo // events separately to c. if err := notify.Watch(".", c, notify.FSEventsModified, notify.FSEventsRemoved); err != nil { log.Fatal(err) } defer notify.Stop(c) // Block until an event is received. switch ei := <-c; ei.Event() { case notify.FSEventsChangeOwner: log.Println("The owner of", ei.Path(), "has changed.") case notify.FSEventsMount: log.Println("The path", ei.Path(), "has been mounted.") } // switch ei := <-c; ei.Event() { // case notify.FSEventsModified: // log.Println("Editing of", ei.Path(), "file is done.") // case notify.FSEventsRemoved: // log.Println("File", ei.Path(), "was swapped/moved into the watched directory.") // } }
// Watch watches a path p, batching events with duration batchTime. A list of // strings are written to chan, representing all files changed, added or // removed. We apply heuristics to cope with things like transient files and // unreliable event notifications. func Watch(p string, batchTime time.Duration, ch chan []string) error { stat, err := os.Stat(p) if err != nil { return err } if stat.IsDir() { p = path.Join(p, "...") } evtch := make(chan notify.EventInfo) err = notify.Watch(p, evtch, notify.All) if err == nil { go func() { for { ret := batch(batchTime, statChecker{}, evtch) if len(ret) > 0 { for i := range ret { norm, _ := normPath(p, ret[i]) ret[i] = norm } ch <- ret } } }() } return err }
// FSEvents may report multiple filesystem actions with one, coalesced event. // Notify unscoalesces such event and dispatches series of single events // back to the user. // // This example shows how to coalesce events by investigating notify.(*FSEvent).ID // field, for the science. func ExampleWatch_darwinCoalesce() { // Make the channels buffered to ensure no event is dropped. Notify will drop // an event if the receiver is not able to keep up the sending pace. c := make(chan notify.EventInfo, 4) // Set up a watchpoint listetning for events within current working directory. // Dispatch all platform-independent separately to c. if err := notify.Watch(".", c, notify.All); err != nil { log.Fatal(err) } defer notify.Stop(c) var id uint64 var coalesced []notify.EventInfo for ei := range c { switch n := ei.Sys().(*notify.FSEvent).ID; { case id == 0: id = n coalesced = []notify.EventInfo{ei} case id == n: coalesced = append(coalesced, ei) default: log.Printf("FSEvents reported a filesystem action with the following"+ " coalesced events %v groupped by %d ID\n", coalesced, id) return } } }
func setUpFolderListener(path string, stop <-chan bool) { os.RemoveAll(path) os.Mkdir(path, os.ModePerm) replacer := replacer.NewMakerRegistry(path) for _, format := range allFormats() { for _, class := range allClasses() { replacer.Add(format, class) } } c := make(chan notify.EventInfo, 1000) if err := notify.Watch(path, c, notify.Rename, notify.Remove); err != nil { log.Fatal(err) } defer notify.Stop(c) go func() { for { select { case event := <-c: replacer.Replace(event.Path()) } } }() <-stop }
func stalk(c chan notify.EventInfo) { pathTree := filepath.Join(*path, "...") err := notify.Watch(pathTree, c, notify.All) if err != nil { fmt.Printf("cannot watch %s, %s\n", *path, err.Error()) os.Exit(1) } else { log.Printf("Stalking path %s", *path) } for { ei := <-c if f := filterFiles(ei); f { log.Printf("%s\n", ei) execute(*cmd) } if *wait != 0 { time.Sleep(time.Duration(*wait) * time.Second) } } }
// This example shows how to watch directory-name changes in the working directory subtree. func ExampleWatch_windows() { // Make the channel buffered to ensure no event is dropped. Notify will drop // an event if the receiver is not able to keep up the sending pace. c := make(chan notify.EventInfo, 4) // Since notify package behaves exactly like ReadDirectoryChangesW function, // we must register notify.FileNotifyChangeDirName filter and wait for one // of FileAction* events. if err := notify.Watch("./...", c, notify.FileNotifyChangeDirName); err != nil { log.Fatal(err) } defer notify.Stop(c) // Wait for actions. for ei := range c { switch ei.Event() { case notify.FileActionAdded, notify.FileActionRenamedNewName: log.Println("Created:", ei.Path()) case notify.FileActionRemoved, notify.FileActionRenamedOldName: log.Println("Removed:", ei.Path()) case notify.FileActionModified: panic("notify: unexpected action") } } }
func (d *Dispatcher) watch() { for _, path := range d.Paths { recursivePath := filepath.Join(path.Name, "...") if err := notify.Watch(recursivePath, d.watcher, flags...); err != nil { d.log.Error(err) } else { d.log.WithFields(logrus.Fields{"path": path.Name}).Info("Watching recursively") } } }
func watch(p string, ch chan notify.EventInfo) error { stat, err := os.Stat(p) if err != nil { return err } if stat.IsDir() { p = path.Join(p, "...") } return notify.Watch(p, ch, notify.All) }
func (w *watcher) loop() { defer func() { w.ac.mu.Lock() w.running = false w.starting = false w.ac.mu.Unlock() }() err := notify.Watch(w.ac.keydir, w.ev, notify.All) if err != nil { glog.V(logger.Detail).Infof("can't watch %s: %v", w.ac.keydir, err) return } defer notify.Stop(w.ev) glog.V(logger.Detail).Infof("now watching %s", w.ac.keydir) defer glog.V(logger.Detail).Infof("no longer watching %s", w.ac.keydir) w.ac.mu.Lock() w.running = true w.ac.mu.Unlock() // Wait for file system events and reload. // When an event occurs, the reload call is delayed a bit so that // multiple events arriving quickly only cause a single reload. var ( debounce = time.NewTimer(0) debounceDuration = 500 * time.Millisecond inCycle, hadEvent bool ) defer debounce.Stop() for { select { case <-w.quit: return case <-w.ev: if !inCycle { debounce.Reset(debounceDuration) inCycle = true } else { hadEvent = true } case <-debounce.C: w.ac.mu.Lock() w.ac.reload() w.ac.mu.Unlock() if hadEvent { debounce.Reset(debounceDuration) inCycle, hadEvent = true, false } else { inCycle, hadEvent = false, false } } } }
// Listen registers for events within the given root directories (recursively). func (w *Watcher) Listen(listener Listener, roots ...string) { eventCh := make(chan notify.EventInfo, 100) // Walk through all files / directories under the root, adding each to watcher. for _, p := range roots { // is the directory / file a symlink? f, err := os.Lstat(p) if err == nil && f.Mode()&os.ModeSymlink == os.ModeSymlink { realPath, err := filepath.EvalSymlinks(p) if err != nil { panic(err) } p = realPath } fi, err := os.Stat(p) if err != nil { glog.Errorln("Failed to stat watched path", p, ":", err) continue } if fi.IsDir() { err = notify.Watch(p+string(filepath.Separator)+"...", eventCh, notify.All) } else { err = notify.Watch(p, eventCh, notify.All) } if err != nil { glog.Errorln("Failed to watch", p, ":", err) } } if w.eagerRebuildEnabled() { // Create goroutine to notify file changes in real time go w.NotifyWhenUpdated(listener, eventCh) } w.events = append(w.events, eventCh) w.listeners = append(w.listeners, listener) }
func watchFile() { c := make(chan notify.EventInfo, 1) if err := notify.Watch("./users.txt", c, 0x04000); err != nil { fmt.Println("error watching users file", err) } defer notify.Stop(c) // Block until an event is received. ei := <-c fmt.Println("Users - watchFile - users file has been changed", ei.Event()) parseAndSetUsers() go watchFile() }
func main() { // Make the channel buffered to ensure no event is dropped. Notify will drop // an event if the receiver is not able to keep up the sending pace. c := make(chan notify.EventInfo, 1) // Set up a watchpoint listening on events within current working directory. // Dispatch each create and remove events separately to c. if err := notify.Watch(".", c, notify.Create, notify.Remove); err != nil { log.Fatal(err) } defer notify.Stop(c) // Block until an event is received. ei := <-c log.Println("Got event:", ei) }
// This example shows how to set up a recursive watchpoint. func ExampleWatch_recursive() { // Make the channel buffered to ensure no event is dropped. Notify will drop // an event if the receiver is not able to keep up the sending pace. c := make(chan notify.EventInfo, 1) // Set up a watchpoint listening for events within a directory tree rooted // at current working directory. Dispatch remove events to c. if err := notify.Watch("./...", c, notify.Remove); err != nil { log.Fatal(err) } defer notify.Stop(c) // Block until an event is received. ei := <-c log.Println("Got event:", ei) }
func main() { repotmp := "/repo/PKGBUILD/" currentuser, _ := user.Current() homedirs, _ := getHomes("/home/*") logFile := StartLog("/var/log/pkgbuild.log", currentuser) defer logFile.Close() Info.Println("\n\n\t\tI don't know who you are\n\t\tI don't know what you are syncing\n\t\tIf you are syncing via rsync, I can tell you \n\t\tI don't have the condition to pick it\n\t\tBut what I do have are a very particular set of channels\n\t\tChannels that pick up debs and push it to the repo\n\t\tI will look for debs in", homedirs, ", \n\t\tI will find it, and I will add it to repo . . .\n") fmt.Println("\n\n\t\tI don't know who you are\n\t\tI don't know what you are syncing\n\t\tIf you are syncing via rsync, I can tell you \n\t\tI don't have the condition to pick it\n\t\tBut what I do have are a very particular set of channels\n\t\tChannels that pick up debs and push it to the repo\n\t\tI will look for debs in", homedirs, ", \n\t\tI will find it, and I will add it to repo . . .\n") Info.Println("Starting taken . . .\nUse scp to copy deb files.") fmt.Println("Starting taken . . .\nUse scp to copy deb files.") Info.Println("Deb files will be moved to ", repotmp, "before pushing to repo") c := make(chan notify.EventInfo, 20) for _, i := range homedirs { if err := notify.Watch(i, c, notify.InCloseWrite, notify.Create); err != nil { log.Fatal(err) } } /*if err := notify.Watch("/home/girishg/", c, notify.InCloseWrite, notify.All); err != nil { log.Fatal(err) } if err := notify.Watch("/home/anotheruser/", c, notify.InCloseWrite, notify.All); err != nil { log.Fatal(err) }*/ defer notify.Stop(c) for ei := range c { switch ei.Event() { case notify.Create: Info.Println(ei.Event(), ei.Path()) case notify.InCloseWrite: Info.Println(ei.Event(), ei.Path()) if ValidateDeb(ei.Path()) { debfile := moveDebs(ei.Path(), repotmp) CallAptlyAdd(debfile) CallAptlyShow() CallAptlyPublish() } } } }
// New - Creates a new watcher which flushes caches. func New(path string, cache CacheFlusher) (*Watcher, error) { events := make(chan notify.EventInfo, 1) path, err := filepath.Abs(path) if err != nil { return nil, err } path = filepath.Dir(path) path = filepath.Join(path, "...") err = notify.Watch(path, events, notify.All) if err != nil { return nil, fmt.Errorf("watching on %q: %v", path, err) } return &Watcher{ events: events, flusher: cache, }, nil }
// Monitor the given path and call monitor() on match. // // If a glob includes **, the monitor will be recursive. eg. // // ./src/thrift/**/*.thrift // // "id" is a unique identifier for the object doing the monitoring func (f *FileMonitor) Monitor(id string, globs []*glob.Glob, monitor func(path string)) { for _, g := range globs { watch := g.Dir() entries, ok := f.monitors[watch] if !ok { entries = map[string]*fileMonitorEntry{} f.monitors[watch] = entries } entry, ok := entries[id] if !ok { entry = &fileMonitorEntry{ ch: make(chan notify.EventInfo, 128), globs: glob.Set{}, } entries[id] = entry if g.IsRecursive() { watch = filepath.Join(watch, "...") } f.log.Debugf("Watching %s", watch) err := notify.Watch(watch, entry.ch, notify.All) if err != nil { f.log.Printf("Failed to watch %s", g) continue } go func() { for event := range entry.ch { for _, g := range entry.globs { if g.Match(event.Path()) { f.log.Debugf("%s -> %s", event, g) go monitor(event.Path()) break } } } }() } if entry.globs.Contains(g) { continue } f.log.Debugf(" +%s", g) entry.globs.Add(g) } }
func watch(p string, ch chan string) error { stat, err := os.Stat(p) if err != nil { return err } if stat.IsDir() { p = path.Join(p, "...") } evtch := make(chan notify.EventInfo) err = notify.Watch(p, evtch, notify.All) if err == nil { go func() { for e := range evtch { ch <- e.Path() } }() } return err }
func main() { var handlers []*handler if command != "" { h, err := newHandler(command) if err != nil { die(err) } handlers = append(handlers, h) } if file != "" { p, err := ioutil.ReadFile(file) if err != nil { die(err) } h, err := newHandler(string(p)) if err != nil { die(err) } handlers = append(handlers, h) } var run []chan<- Event for _, h := range handlers { run = append(run, h.Daemon()) } c := make(chan notify.EventInfo, 1) for _, path := range paths { if err := notify.Watch(path, c, notify.All); err != nil { die(err) } } for ei := range c { log.Println("received", ei) e := newEvent(ei) for _, run := range run { select { case run <- e: default: log.Println("event dropped due to slow handler") } } } }
// Create new asset manager. func New(cfg Config) (*Manager, error) { m := &Manager{ cfg: cfg, stopChan: make(chan struct{}), eventsChan: make(chan notify.EventInfo, 20), files: make(map[string]*file), } _, err := os.Stat(m.cfg.Path) if err == nil { err := notify.Watch(filepath.Join(m.cfg.Path, "..."), m.eventsChan, notify.InCloseWrite) if err != nil { return nil, err } } go m.notifyLoop() return m, nil }
// This example shows how to use Sys() method from EventInfo interface to tie // two separate events generated by rename(2) function. func ExampleWatch_linuxMove() { // Make the channel buffered to ensure no event is dropped. Notify will drop // an event if the receiver is not able to keep up the sending pace. c := make(chan notify.EventInfo, 2) // Set up a watchpoint listening for inotify-specific events within a // current working directory. Dispatch each InMovedFrom and InMovedTo // events separately to c. if err := notify.Watch(".", c, notify.InMovedFrom, notify.InMovedTo); err != nil { log.Fatal(err) } defer notify.Stop(c) // Inotify reports move filesystem action by sending two events tied with // unique cookie value (uint32): one of the events is of InMovedFrom type // carrying move source path, while the second one is of InMoveTo type // carrying move destination path. moves := make(map[uint32]struct { From string To string }) // Wait for moves. for ei := range c { cookie := ei.Sys().(*syscall.InotifyEvent).Cookie info := moves[cookie] switch ei.Event() { case notify.InMovedFrom: info.From = ei.Path() case notify.InMovedTo: info.To = ei.Path() } moves[cookie] = info if cookie != 0 && info.From != "" && info.To != "" { log.Println("File:", info.From, "was renamed to", info.To) delete(moves, cookie) } } }
// This example shows how to use FSEvents-specifc event values. func ExampleWatch_darwin() { // Make the channel buffered to ensure no event is dropped. Notify will drop // an event if the receiver is not able to keep up the sending pace. c := make(chan notify.EventInfo, 1) // Set up a watchpoint listening for FSEvents-specific events within a // current working directory. Dispatch each FSEventsChangeOwner and FSEventsMount // events separately to c. if err := notify.Watch(".", c, notify.FSEventsChangeOwner, notify.FSEventsMount); err != nil { log.Fatal(err) } defer notify.Stop(c) // Block until an event is received. switch ei := <-c; ei.Event() { case notify.FSEventsChangeOwner: log.Println("The owner of", ei.Path(), "has changed.") case notify.FSEventsMount: log.Println("The path", ei.Path(), "has been mounted.") } }
// This example shows how to watch changes made on file-system by text editor // when saving a file. Usually, either InCloseWrite or InMovedTo (when swapping // with a temporary file) event is created. func ExampleWatch_linux() { // Make the channel buffered to ensure no event is dropped. Notify will drop // an event if the receiver is not able to keep up the sending pace. c := make(chan notify.EventInfo, 1) // Set up a watchpoint listening for inotify-specific events within a // current working directory. Dispatch each InCloseWrite and InMovedTo // events separately to c. if err := notify.Watch(".", c, notify.InCloseWrite, notify.InMovedTo); err != nil { log.Fatal(err) } defer notify.Stop(c) // Block until an event is received. switch ei := <-c; ei.Event() { case notify.InCloseWrite: log.Println("Editing of", ei.Path(), "file is done.") case notify.InMovedTo: log.Println("File", ei.Path(), "was swapped/moved into the watched directory.") } }
/* Monitoring changes in file system. It designed for run in separate goroutine. */ func fileMon(path string, bus chan fileChangeEvent) { // Make the channel buffered to ensure no event is dropped. Notify will drop // an event if the receiver is not able to keep up the sending pace. c := make(chan notify.EventInfo, 1) // Set up a watchpoint listening on events within current working directory. // Dispatch each create and remove events separately to c. if err := notify.Watch(path+"/...", c, notify.All); err != nil { log.Fatal(err) } defer notify.Stop(c) // Block until an event is received. for { event := <-c fstat, err := os.Lstat(event.Path()) if os.IsNotExist(err) { bus <- fileChangeEvent{Path: event.Path(), IsRemoved: true} continue } if err != nil { log.Println(err) continue } if fstat.IsDir() { bus <- fileChangeEvent{Path: event.Path(), IsDir: true} continue } content, err := ioutil.ReadFile(event.Path()) if err != nil { log.Println(err) } bus <- fileChangeEvent{Path: event.Path(), Content: content} } }
// Watches for all fs events on an input path. func (f *fsClient) Watch(params watchParams) (*watchObject, *probe.Error) { eventChan := make(chan Event) errorChan := make(chan *probe.Error) doneChan := make(chan bool) // Make the channel buffered to ensure no event is dropped. Notify will drop // an event if the receiver is not able to keep up the sending pace. neventChan := make(chan notify.EventInfo, 1) var fsEvents []notify.Event for _, event := range params.events { switch event { case "put": fsEvents = append(fsEvents, EventTypePut...) case "delete": fsEvents = append(fsEvents, EventTypeDelete...) default: return nil, errInvalidArgument().Trace(event) } } // Set up a watchpoint listening for events within a directory tree rooted // at current working directory. Dispatch remove events to c. recursivePath := f.PathURL.Path if params.recursive { recursivePath = f.PathURL.Path + "..." } if e := notify.Watch(recursivePath, neventChan, fsEvents...); e != nil { return nil, probe.NewError(e) } // wait for doneChan to close the watcher, eventChan and errorChan go func() { <-doneChan close(eventChan) close(errorChan) notify.Stop(neventChan) }() timeFormatFS := "2006-01-02T15:04:05.000Z" // Get fsnotify notifications for events and errors, and sent them // using eventChan and errorChan go func() { for { select { case event, ok := <-neventChan: if !ok { return } if isIgnoredFile(event.Path()) { continue } var i os.FileInfo if IsPutEvent(event.Event()) { // Look for any writes, send a response to indicate a full copy. var e error i, e = os.Stat(event.Path()) if e != nil { if os.IsNotExist(e) { continue } errorChan <- probe.NewError(e) continue } if i.IsDir() { // we want files continue } eventChan <- Event{ Time: time.Now().Format(timeFormatFS), Size: i.Size(), Path: event.Path(), Client: f, Type: EventCreate, } } else if IsDeleteEvent(event.Event()) { eventChan <- Event{ Time: time.Now().Format(timeFormatFS), Path: event.Path(), Client: f, Type: EventRemove, } } } } }() return &watchObject{ events: eventChan, errors: errorChan, done: doneChan, }, nil }
func main() { setVerbosity() // Get port or die. port := getEnv("PORT") // Get content or die. getContent() root := getRoot() log.WithField("Root", root).Info("Our root directory") // We'll parse and store the responses ahead of time. resps, err := pages.Load(root) if err != nil { log.Fatalf("pages.Load: unexpected error: %s", err) } log.WithField("Resps", resps).Debug("The parsed responses") responses = Atomic{Resps: resps} log.Info("Starting server.") log.Info("Listening on port: ", port) // If jumpfile exists. if _, err := os.Stat(root + "/jumpfile.json"); err == nil { buf, err := ioutil.ReadFile(root + "/jumpfile.json") if err != nil { log.Warningln("jumpfile readfile: unexpected error: %s", err) } err = json.Unmarshal(buf, &jumpfile) if err != nil { log.Warningln("jumpfile unmarshal: unexpected error: %s", err) } log.Debugln(jumpfile) } else { log.Infoln("No jumpfile found") } // Our request handler. http.HandleFunc("/", handler) if watch { events := make(chan notify.EventInfo, 5) if err := notify.Watch(fmt.Sprintf("%s/...", root), events, notify.Create, notify.Remove, notify.Write, notify.Rename); err != nil { log.Warningln("notify.Watch:", err) } defer notify.Stop(events) go func() { for event := range events { log.Info("event:", event) // Lacks error handling as this should not run in production. responses.Resps, _ = pages.Load(getRoot()) } }() } // Listen on port and serve with our handler. err = http.ListenAndServe(":"+port, nil) if err != nil { panic(err) } }