]> Cypherpunks.ru repositories - nncp.git/blob - src/dirwatch.go
fsnotify usage
[nncp.git] / src / dirwatch.go
1 // +build !nofsnotify
2
3 /*
4 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
5 Copyright (C) 2016-2021 Sergey Matveev <stargrave@stargrave.org>
6
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, version 3 of the License.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 package nncp
21
22 import (
23         "fmt"
24         "os"
25         "time"
26
27         "github.com/fsnotify/fsnotify"
28 )
29
30 type DirWatcher struct {
31         w      *fsnotify.Watcher
32         C      chan struct{}
33         isDead chan struct{}
34 }
35
36 func (ctx *Ctx) NewDirWatcher(dir string, d time.Duration) (*DirWatcher, error) {
37         w, err := fsnotify.NewWatcher()
38         if err != nil {
39                 return nil, err
40         }
41         err = w.Add(dir)
42         if err != nil {
43                 if !os.IsNotExist(err) {
44                         w.Close()
45                         return nil, err
46                 }
47                 if err = os.MkdirAll(dir, os.FileMode(0777)); err != nil {
48                         w.Close()
49                         return nil, err
50                 }
51                 err = w.Add(dir)
52                 if err != nil {
53                         w.Close()
54                         return nil, err
55                 }
56         }
57         dw := DirWatcher{
58                 w:      w,
59                 C:      make(chan struct{}),
60                 isDead: make(chan struct{}),
61         }
62         go func() {
63                 ticker := time.NewTicker(d)
64                 dw.C <- struct{}{}
65                 hasEvents := false
66                 for {
67                         select {
68                         case err := <-w.Errors:
69                                 ctx.LogE("dir-watch", LEs{{"Dir", dir}}, err, func(les LEs) string {
70                                         return "fsnotify error: " + err.Error()
71                                 })
72                         case e := <-w.Events:
73                                 ctx.LogD("dir-watch-event", LEs{{"Dir", dir}}, func(les LEs) string {
74                                         return fmt.Sprintf("fsnotify event: %v", e)
75                                 })
76                                 if e.Op&(fsnotify.Create|fsnotify.Rename) > 0 {
77                                         hasEvents = true
78                                 }
79                         case <-ticker.C:
80                                 if hasEvents {
81                                         dw.C <- struct{}{}
82                                         hasEvents = false
83                                 }
84                         case <-dw.isDead:
85                                 w.Close()
86                                 ticker.Stop()
87                                 close(dw.C)
88                                 return
89                         }
90                 }
91         }()
92         return &dw, err
93 }
94
95 func (dw *DirWatcher) Close() {
96         close(dw.isDead)
97         for range dw.C {
98         }
99 }