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