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.

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

报告相同问题?

悬赏问题

  • ¥100 任意维数的K均值聚类
  • ¥15 stamps做sbas-insar,时序沉降图怎么画
  • ¥15 unity第一人称射击小游戏,有demo,在原脚本的基础上进行修改以达到要求
  • ¥15 买了个传感器,根据商家发的代码和步骤使用但是代码报错了不会改,有没有人可以看看
  • ¥15 关于#Java#的问题,如何解决?
  • ¥15 加热介质是液体,换热器壳侧导热系数和总的导热系数怎么算
  • ¥100 嵌入式系统基于PIC16F882和热敏电阻的数字温度计
  • ¥15 cmd cl 0x000007b
  • ¥20 BAPI_PR_CHANGE how to add account assignment information for service line
  • ¥500 火焰左右视图、视差(基于双目相机)