]> Cypherpunks.ru repositories - nncp.git/blob - src/dirwatch.go
Unify copyright comment format
[nncp.git] / src / dirwatch.go
1 //go:build !nofsnotify
2 // +build !nofsnotify
3
4 // NNCP -- Node to Node copy, utilities for store-and-forward data exchange
5 // Copyright (C) 2016-2024 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 package nncp
20
21 import (
22         "fmt"
23         "time"
24
25         "github.com/fsnotify/fsnotify"
26 )
27
28 type DirWatcher struct {
29         w      *fsnotify.Watcher
30         C      chan struct{}
31         isDead chan struct{}
32 }
33
34 func (ctx *Ctx) NewDirWatcher(dir string, d time.Duration) (*DirWatcher, error) {
35         w, err := fsnotify.NewWatcher()
36         if err != nil {
37                 return nil, err
38         }
39         err = ensureDir(dir)
40         if err != nil {
41                 return nil, err
42         }
43         err = w.Add(dir)
44         if err != nil {
45                 w.Close()
46                 return nil, err
47         }
48         dw := DirWatcher{
49                 w:      w,
50                 C:      make(chan struct{}),
51                 isDead: make(chan struct{}),
52         }
53         go func() {
54                 ticker := time.NewTicker(d)
55                 dw.C <- struct{}{}
56                 hasEvents := false
57                 for {
58                         select {
59                         case err := <-w.Errors:
60                                 ctx.LogE("dir-watch", LEs{{"Dir", dir}}, err, func(les LEs) string {
61                                         return "fsnotify error: " + err.Error()
62                                 })
63                         case e := <-w.Events:
64                                 ctx.LogD("dir-watch-event", LEs{{"Dir", dir}}, func(les LEs) string {
65                                         return fmt.Sprintf("fsnotify event: %v", e)
66                                 })
67                                 if e.Op&(fsnotify.Create|fsnotify.Rename) > 0 {
68                                         hasEvents = true
69                                 }
70                         case <-ticker.C:
71                                 if hasEvents {
72                                         dw.C <- struct{}{}
73                                         hasEvents = false
74                                 }
75                         case <-dw.isDead:
76                                 w.Close()
77                                 ticker.Stop()
78                                 close(dw.C)
79                                 return
80                         }
81                 }
82         }()
83         return &dw, err
84 }
85
86 func (dw *DirWatcher) Close() {
87         close(dw.isDead)
88         for range dw.C {
89         }
90 }