/* ** Zabbix ** Copyright (C) 2001-2023 Zabbix SIA ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **/ package vfsfs import ( "fmt" "sync" "time" ) const timeout = 1 var stuckMounts map[string]int var stuckMux sync.Mutex type fsCaller struct { fsFunc func(path string) (stats *FsStats, err error) errChan chan error outChanStuckUnchecked chan *FsStats outChanStuckChecked chan interface{} p *Plugin } func (f *fsCaller) executeFunc(path string) { stats, err := f.fsFunc(path) if err != nil { f.errChan <- err return } f.outChanStuckUnchecked <- stats } func (f *fsCaller) checkNotStuckAndExecute(path string) { if isStuck(path) { f.outChanStuckChecked <- fmt.Errorf("mount '%s' is unavailable", path) return } defer func() { resetStuck(path) }() go f.executeFunc(path) select { case stat := <-f.outChanStuckUnchecked: f.outChanStuckChecked <- stat return case err := <-f.errChan: f.outChanStuckChecked <- err return case <-time.After(timeout * time.Second): f.outChanStuckChecked <- fmt.Errorf("operation on mount '%s' timed out", path) } incStuck(path) select { case <-f.outChanStuckUnchecked: return case <-f.errChan: return case <-time.After(timeout * 12 * 3600 * time.Second): return } } func (f *fsCaller) run(path string) (stat *FsStats, err error) { go f.checkNotStuckAndExecute(path) v := <-f.outChanStuckChecked switch d := v.(type) { case *FsStats: return d, nil case error: return nil, d default: return nil, fmt.Errorf("Unsupported return type %T", d) } } func isStuck(path string) bool { stuckMux.Lock() defer stuckMux.Unlock() return stuckMounts[path] > 0 } func resetStuck(path string) { stuckMux.Lock() defer stuckMux.Unlock() stuckMounts[path] = 0 } func incStuck(path string) { stuckMux.Lock() defer stuckMux.Unlock() stuckMounts[path]++ } func (p *Plugin) newFSCaller(fsFunc func(path string) (stats *FsStats, err error), fsLen int) *fsCaller { fc := fsCaller{} fc.fsFunc = fsFunc fc.errChan = make(chan error, fsLen) fc.outChanStuckUnchecked = make(chan *FsStats, fsLen) fc.outChanStuckChecked = make(chan interface{}) fc.p = p return &fc } func init() { stuckMounts = make(map[string]int) }