// Copyright (c) 2014-2015 The Notify Authors. All rights reserved. // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. //go:build solaris || illumos // +build solaris illumos package notify import ( "fmt" "os" "syscall" ) // newTrigger returns implementation of trigger. func newTrigger(pthLkp map[string]*watched) trigger { return &fen{ pthLkp: pthLkp, cf: newCfen(), } } // fen is a structure implementing trigger for FEN. type fen struct { // p is a FEN port identifier p int // pthLkp is a structure mapping monitored files/dir with data about them, // shared with parent trg structure pthLkp map[string]*watched // cf wraps C operations for FEN cf cfen } // watched is a data structure representing watched file/directory. type watched struct { trgWatched } // Stop implements trigger. func (f *fen) Stop() error { return f.cf.portAlert(f.p) } // Close implements trigger. func (f *fen) Close() (err error) { return syscall.Close(f.p) } // NewWatched implements trigger. func (*fen) NewWatched(p string, fi os.FileInfo) (*watched, error) { return &watched{trgWatched{p: p, fi: fi}}, nil } // Record implements trigger. func (f *fen) Record(w *watched) { f.pthLkp[w.p] = w } // Del implements trigger. func (f *fen) Del(w *watched) { delete(f.pthLkp, w.p) } func inter2pe(n interface{}) PortEvent { pe, ok := n.(PortEvent) if !ok { panic(fmt.Sprintf("fen: type should be PortEvent, %T instead", n)) } return pe } // Watched implements trigger. func (f *fen) Watched(n interface{}) (*watched, int64, error) { pe := inter2pe(n) fo, ok := pe.PortevObject.(*FileObj) if !ok || fo == nil { panic(fmt.Sprintf("fen: type should be *FileObj, %T instead", fo)) } w, ok := f.pthLkp[fo.Name] if !ok { return nil, 0, errNotWatched } return w, int64(pe.PortevEvents), nil } // init initializes FEN. func (f *fen) Init() (err error) { f.p, err = f.cf.portCreate() return } func fi2fo(fi os.FileInfo, p string) FileObj { st, ok := fi.Sys().(*syscall.Stat_t) if !ok { panic(fmt.Sprintf("fen: type should be *syscall.Stat_t, %T instead", st)) } return FileObj{Name: p, Atim: st.Atim, Mtim: st.Mtim, Ctim: st.Ctim} } // Unwatch implements trigger. func (f *fen) Unwatch(w *watched) error { return f.cf.portDissociate(f.p, FileObj{Name: w.p}) } // Watch implements trigger. func (f *fen) Watch(fi os.FileInfo, w *watched, e int64) error { return f.cf.portAssociate(f.p, fi2fo(fi, w.p), int(e)) } // Wait implements trigger. func (f *fen) Wait() (interface{}, error) { var ( pe PortEvent err error ) err = f.cf.portGet(f.p, &pe) return pe, err } // IsStop implements trigger. func (f *fen) IsStop(n interface{}, err error) bool { return err == syscall.EBADF || inter2pe(n).PortevSource == srcAlert } func init() { encode = func(e Event, dir bool) (o int64) { // Create event is not supported by FEN. Instead FileModified event will // be registered. If this event will be reported on dir which is to be // monitored for Create, dir will be rescanned and Create events will // be generated and returned for new files. In case of files, // if not requested FileModified event is reported, it will be ignored. o = int64(e &^ Create) if (e&Create != 0 && dir) || e&Write != 0 { o = (o &^ int64(Write)) | int64(FileModified) } // Following events are 'exception events' and as such cannot be requested // explicitly for monitoring or filtered out. If the will be reported // by FEN and not subscribed with by user, they will be filtered out by // watcher's logic. o &= int64(^Rename & ^Remove &^ FileDelete &^ FileRenameTo &^ FileRenameFrom &^ Unmounted &^ MountedOver) return } nat2not = map[Event]Event{ FileModified: Write, FileRenameFrom: Rename, FileDelete: Remove, FileAccess: Event(0), FileAttrib: Event(0), FileRenameTo: Event(0), FileTrunc: Event(0), FileNoFollow: Event(0), Unmounted: Event(0), MountedOver: Event(0), } not2nat = map[Event]Event{ Write: FileModified, Rename: FileRenameFrom, Remove: FileDelete, } }