]> Cypherpunks.ru repositories - nncp.git/blob - src/dirwatch.go
go fmt
[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-2021 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         "os"
26         "time"
27
28         "github.com/fsnotify/fsnotify"
29 )
30
31 type DirWatcher struct {
32         w      *fsnotify.Watcher
33         C      chan struct{}
34         isDead chan struct{}
35 }
36
37 func (ctx *Ctx) NewDirWatcher(dir string, d time.Duration) (*DirWatcher, error) {
38         w, err := fsnotify.NewWatcher()
39         if err != nil {
40                 return nil, err
41         }
42         err = w.Add(dir)
43         if err != nil {
44                 if !os.IsNotExist(err) {
45                         w.Close()
46                         return nil, err
47                 }
48                 if err = os.MkdirAll(dir, os.FileMode(0777)); err != nil {
49                         w.Close()
50                         return nil, err
51                 }
52                 err = w.Add(dir)
53                 if err != nil {
54                         w.Close()
55                         return nil, err
56                 }
57         }
58         dw := DirWatcher{
59                 w:      w,
60                 C:      make(chan struct{}),
61                 isDead: make(chan struct{}),
62         }
63         go func() {
64                 ticker := time.NewTicker(d)
65                 dw.C <- struct{}{}
66                 hasEvents := false
67                 for {
68                         select {
69                         case err := <-w.Errors:
70                                 ctx.LogE("dir-watch", LEs{{"Dir", dir}}, err, func(les LEs) string {
71                                         return "fsnotify error: " + err.Error()
72                                 })
73                         case e := <-w.Events:
74                                 ctx.LogD("dir-watch-event", LEs{{"Dir", dir}}, func(les LEs) string {
75                                         return fmt.Sprintf("fsnotify event: %v", e)
76                                 })
77                                 if e.Op&(fsnotify.Create|fsnotify.Rename) > 0 {
78                                         hasEvents = true
79                                 }
80                         case <-ticker.C:
81                                 if hasEvents {
82                                         dw.C <- struct{}{}
83                                         hasEvents = false
84                                 }
85                         case <-dw.isDead:
86                                 w.Close()
87                                 ticker.Stop()
88                                 close(dw.C)
89                                 return
90                         }
91                 }
92         }()
93         return &dw, err
94 }
95
96 func (dw *DirWatcher) Close() {
97         close(dw.isDead)
98         for range dw.C {
99         }
100 }