dongshuobei1037 2018-05-02 02:08
浏览 28
已采纳

了解频道阅读行为

I am trying to write a program that uses inotify to watch a file, and if the file is removed, remove the watcher and set new watcher. The code I have tried for the same is

func main() {
    fsNotifyChan := make(chan fsnotify.Event)
    inotify.CreateWatcher() // code included below
    wg := new(sync.WaitGroup)
    wg.Add(1)
    go func() {
        for i := range fsNotifyChan {
            time.Sleep(time.Second * 5)
            fmt.Println(i)
            inotify.CreateWatcher()
            inotify.SetNewWatcher(i.Name, fsNotifyChan)
        }
    }()

    for k := range parsedConf{
        go inotify.SetNewWatcher(k, fsNotifyChan)
    }

    wg.Wait()
}

Where k is a map and the keys are paths to 2 files /var/log/syslog and /var/log/auth.log for example.

The function that I use to create inotify watcher is

package inotify
var Watcher *fsnotify.Watcher
var err error

func CreateWatcher () {
    Watcher, err = fsnotify.NewWatcher()
    if err != nil {
        log.Fatal(err)
    }
}

func SetNewWatcher(filepath string, c chan fsnotify.Event) {
    log.Infoln("Setting Watcher for ", filepath)

    defer Watcher.Close()
    wg := new(sync.WaitGroup)
    wg.Add(1)
    go func() {
        for {
            select {
            case event := <-Watcher.Events:
                log.Debugln("event:", event)
                if event.Op&fsnotify.Rename == fsnotify.Rename {
                    log.Infoln(event)
                    removeWatcher(filepath)
                    c <- event
                    wg.Done()
                    runtime.Goexit()
                } else if event.Op&fsnotify.Remove == fsnotify.Remove {
                    log.Infoln(event)
                    removeWatcher(filepath)
                    c <- event
                    wg.Done()
                    runtime.Goexit()

                }
            case err := <-Watcher.Errors:
                log.Errorln("error:", err)
                removeWatcher(filepath)
                wg.Done()
                runtime.Goexit()

            }
        }
        wg.Done()
    }()

    err = Watcher.Add(filepath)
    if err != nil {
        log.Fatal(err)
    }
    wg.Wait()
}

func removeWatcher(filename string) {
    err := Watcher.Remove(filename)
    log.Debugln("Removed watcher for", filename)
    if err != nil {
        log.Errorln(err)
    }
}

The problem I am seeing is when I start to run the program,

First output:

iNotifier.go:18: INFO: Setting Watcher for  /var/log/auth.log
iNotifier.go:18: INFO: Setting Watcher for  /var/log/syslog

Then after a sudo command like echo hi | sudo tee -a /var/log/syslog I can see

iNotifier.go:27: DEBUG: event: "/var/log/auth.log": WRITE
iNotifier.go:27: DEBUG: event: "/var/log/auth.log": WRITE
iNotifier.go:27: DEBUG: event: "/var/log/syslog": WRITE

which is perfectly fine now.

Now if I try to move the syslog and put it back as

➜  bin sudo mv /var/log/syslog /var/log/syslog.bak
➜  bin sudo mv /var/log/syslog.bak /var/log/syslog

or remove the file itself and touch a new one.

The output looks like

iNotifier.go:27: DEBUG: event: "/var/log/auth.log": WRITE
iNotifier.go:27: DEBUG: event: "/var/log/auth.log": WRITE
iNotifier.go:27: DEBUG: event: "/var/log/syslog": RENAME
iNotifier.go:29: INFO: "/var/log/syslog": RENAME
iNotifier.go:63: DEBUG: Removed watcher for /var/log/syslog
iNotifier.go:43: ERROR: error: <nil>
iNotifier.go:63: DEBUG: Removed watcher for /var/log/auth.log
iNotifier.go:65: ERROR: bad file descriptor
"/var/log/syslog": RENAME
iNotifier.go:18: INFO: Setting Watcher for  /var/log/syslog

of which iNotifier.go:65: ERROR: bad file descriptor could be caused because the file has been moved already, and then the goroutine will exit with runtime.Goexit()

Now if I do the same sudo command echo hi | sudo tee -a /var/log/syslog, I can only see one inotify output from syslog file and not from the authlog file although things are written there.

iNotifier.go:27: DEBUG: event: "/var/log/syslog": WRITE

If I move the file again and move it back one more time, I stop getting any more notifications.

iNotifier.go:27: DEBUG: event: "/var/log/syslog": WRITE
iNotifier.go:27: DEBUG: event: "/var/log/syslog": RENAME
iNotifier.go:29: INFO: "/var/log/syslog": RENAME
iNotifier.go:63: DEBUG: Removed watcher for /var/log/syslog

and that's the last output. Any more file operations and I don't see any output. I know this could be a logical error on how I am using the way channels are supposed to be used. I am not explicitly closing the channel, and I am passing it again on further iterations. Can someone please help me understand what am I doing wrong here?

  • 写回答

1条回答 默认 最新

  • dsg41888 2018-05-05 06:28
    关注

    After trying a few options, I settled up with a different notify library. Apparently this library doesn't have option to individually close watcher based on path name, but can only do it by closing a channel. So I ended up creating a channel for every file that needs to be watched and a map to relate the file - to - channel, and when required, close the channel related to the file and re-open. The code snippet is

    var fileMapChan map[string]chan notify.EventInfo
    func main() {
    
    
        fileMapChan = make(map[string]chan notify.EventInfo)
        commonNotificationChan := make(chan string, 2048)
        for k := range parsedConf {
            c := make(chan notify.EventInfo, 2048)
            fileMapChan[k] = c
        }
        wg := new(sync.WaitGroup)
        //Create new watcher for every file
        for k, v := range fileMapChan {
            wg.Add(2)
            poller.SetNewNotifier(k, v)
            go poller.ReadChanAndFilter(v, commonNotificationChan, wg)
            go poller.FileStat(k, commonNotificationChan, wg)
        }
    
    ==============snip ✂ snip=============
    
    func SetNewNotifier(filepath string, c chan notify.EventInfo) error{
        log.Infoln("Setting new notifier to", filepath)
        if err := notify.Watch(filepath, c, notify.All); err != nil {
            log.Errorln(err)
            return err
        }
        return nil
    }
    
    func ReadChanAndFilter(r chan notify.EventInfo, w chan<- string, wg *sync.WaitGroup) {
        for i := range r {
            log.Debugln(i.Event(), "on", i.Path())
            if i.Event()&notify.Rename == notify.Rename {
                log.Infoln(i.Path(), "renamed")
                w <- i.Path()
                close(r)
                notify.Stop(r)
                runtime.Goexit()
            }
    
        }
        defer wg.Done()
    }
    

    I don't know if this is going to be the best approach, but this is working for me. Whenever a file is moved, it will re-add the watcher.

    Thank you.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 用windows做服务的同志有吗
  • ¥60 求一个简单的网页(标签-安全|关键词-上传)
  • ¥35 lstm时间序列共享单车预测,loss值优化,参数优化算法
  • ¥15 Python中的request,如何使用ssr节点,通过代理requests网页。本人在泰国,需要用大陆ip才能玩网页游戏,合法合规。
  • ¥100 为什么这个恒流源电路不能恒流?
  • ¥15 有偿求跨组件数据流路径图
  • ¥15 写一个方法checkPerson,入参实体类Person,出参布尔值
  • ¥15 我想咨询一下路面纹理三维点云数据处理的一些问题,上传的坐标文件里是怎么对无序点进行编号的,以及xy坐标在处理的时候是进行整体模型分片处理的吗
  • ¥15 一直显示正在等待HID—ISP
  • ¥15 Python turtle 画图